Discord Integration
Guild overview displays real-time Discord activity when a guild has a linked Discord server.
Architecture
flowchart LR
B1[Browser] -->|"REST poll (30s)"| A1["GET /discord/:id/activity"]
A1 --> D1["Discord.js cache + API"]
B2[Browser] -->|SSE stream| A2["GET /discord/:id/voice-stream"]
A2 --> D2[Discord gateway events]
Text channels use 30-second REST polling to show recent messages. Voice channels use Server-Sent Events (SSE) for real-time member join/leave updates, with the initial REST fetch as a baseline.
API Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /discord/bot-invite | Bot invite URL + required permissions list |
| GET | /discord/:guildId/channels | List text and voice channels |
| GET | /discord/:guildId/roles | List Discord roles |
| GET | /discord/:guildId/activity | Channel activity (messages + voice members) |
| SSE | /discord/:guildId/voice-stream | Real-time voice state events |
| PATCH | /organizations/:id/settings | Save discordOverviewChannelIds |
Activity Endpoint
Accepts optional channelIds query parameter (comma-separated) to filter which channels are returned. When omitted, all channels are returned.
Voice Stream (SSE)
Emits VoiceStateEvent objects when users join, leave, or move between voice channels:
interface VoiceStateEvent {
guildId: string;
channelId: string | null;
channelName: string | null;
channelType: 'voice' | 'stage';
userId: string;
displayName: string;
avatarUrl: string | null;
action: 'join' | 'leave' | 'move';
}
The backend listens to Discord gateway voiceStateUpdate events via an RxJS Subject and filters per-guild for each SSE connection. The Subject is completed on module destroy.
Frontend Hooks
useDiscordVoiceStream
Custom hook that subscribes to the SSE endpoint and merges real-time voice data with the REST baseline.
- On first SSE event for a channel, seeds from REST data then applies live updates
- Once SSE has tracked a channel, its member list becomes authoritative (replaces REST)
- Resets patches when REST data refreshes (React "store previous props" pattern)
- Cleans up EventSource on unmount or when server ID changes
Configurable Overview Channels
Guild admins can select which Discord channels appear on the overview tab via Settings > Integrations. Selected channel IDs are stored in guild.settings.discordOverviewChannelIds.
Event Announcements
When events are published, the bot posts a rich embed to the configured Discord channel with:
- Event title, type, date, and description
- Signup link back to the platform
- Live roster that updates as users sign up (character name, class, spec)
- Instance/difficulty metadata when applicable
Per-Event Discord Controls
The event form exposes a granular Discord section (only shown when the guild has a Discord server linked):
| Toggle | Default | Effect |
|---|---|---|
| Enable Discord integration | on | Master switch — when off, no Discord actions fire for this event |
| Create Discord scheduled event | on | Creates a native Discord Scheduled Event on publish |
| Send Discord announcement | on | Posts a rich embed to the announcement channel on publish |
All three flags are stored in event.metadata (discordAnnounce, discordScheduledEvent). Absence of a flag is treated as true (defaults to guild behaviour). Setting a flag to false explicitly opts out.
Announcement Channel Override
When "Send Discord announcement" is on, an optional channel selector appears. Leaving it empty uses the guild default (guild.settings.discordAnnouncementChannelId). Selecting a channel stores it on event.discordChannelId and takes precedence over the guild default.
The announcement embed is a "living" message — it updates automatically when signups change.
Voice Channel Override
When "Create Discord scheduled event" is on, an optional voice channel selector appears. This overrides the guild-level discordVoiceChannelId setting for this specific event. If no voice channel is chosen (event-level or guild-level), the scheduled event is created as an External type instead.
Voice channel override is stored in event.metadata.discordVoiceChannelId.
Discord Scheduled Events
Events can optionally create native Discord Scheduled Events, linked to voice channels when configured. See Event Lifecycle for details on auto-start, delay, and completion.
Voice Attendance Tracking
The bot tracks voice channel joins/leaves during active events. This data feeds into the attendance confirmation workflow. See Event Lifecycle for the full flow.
Bot Permissions
Required Discord bot permissions:
- Manage Roles
- Manage Events
- Send Messages
- View Channels
- Read Message History
- Connect (voice)
- View Voice Channel Members
When permissions change, the UI displays the updated requirements and prompts re-invitation.