Overview
The Chat module provides a unified, multi-platform chat system that aggregates messages from Twitch, YouTube, Kick, Trovo, and Discord into a single real-time stream. It supports message history with pagination, moderation actions (bans, timeouts, message deletion), user profiles with enrichment data from a ProfileService, moderator notes, emote rendering (Twitch, 7TV, BTTV, FFZ), polls, predictions, and raid management. Messages are buffered in Redis, flushed to TimescaleDB in batches, and broadcast to all connected WebSocket clients in real time.
Architecture
Backend
- GraphQL (
apps/api/src/graphql/chat.rs) -- Queries for chat history, message count, user profiles, emotes, moderation log, and moderator notes. Mutations for sending messages, moderation actions, and CRUD on user notes.
- Crate (
crates/lo-chat/src/) -- Core chat logic including ChatBuffer (Redis-backed message buffering), ChatFilter/ChatMessageInput types, ProfileService (caching + circuit breaking for platform API enrichment), emote set types, moderation log, and platform user management.
- Database -- Chat messages are stored in TimescaleDB (
platform_messages hypertable). Platform users are tracked in PostgreSQL (platform_users). Moderator notes in platform_user_notes. Moderation log entries in moderation_log.
- Real-time -- Messages are broadcast via Redis pub/sub to all WebSocket subscribers for the account. The
PendingChatAccounts state tracks which accounts have buffered messages needing flush.
Frontend
- Next.js API proxy routes call GraphQL internally.
- The Multichat UI renders messages with emotes, badges, colors, and reply threading.
- User info modal fetches a unified profile combining DB data with live platform API enrichment.
API
GraphQL Queries
| Query | Permission | Description |
|---|
chatHistory(filter: ChatFilterInput) | chat:read | Paginated chat message history with filters for platform, user, date range |
chatMessageCount | chat:read | Total message count for the account |
platformUserProfile(platform, platformUserId) | chat:userinfo | Unified user profile with platform API enrichment via ProfileService |
emotes(platform, channelId) | chat:read | Emote sets for a platform/channel (Twitch, 7TV, BTTV, FFZ, Trovo, Discord) |
userEmotes | chat:read | Twitch emotes available to the authenticated user (subs, globals, follower) |
platformUserNotes(platform, platformUserId) | chat:notes | List moderator notes for a platform user |
moderationLog(platform, platformUserId, page, limit) | chat:userinfo | Moderation action history for a user |
GraphQL Mutations
| Mutation | Permission | Description |
|---|
sendChatMessage(input: ChatMessageInputGql!) | chat:write | Buffer a chat message in Redis and broadcast via pub/sub |
sendChatToPlatform(input: SendToPlatformInput!) | chat:write | Send a chat message to a platform (twitch, kick, trovo) |
createPlatformUserNote(platform, platformUserId, note) | chat:notes | Create a moderator note on a user |
updatePlatformUserNote(noteId, note) | chat:notes | Update an existing moderator note |
deletePlatformUserNote(noteId) | chat:notes | Delete a moderator note |
updateUserTreatment(platform, platformUserId, treatment) | chat:ban | Update chat user treatment (none, active_monitoring, restricted); Twitch syncs to Helix best-effort |
moderateChat(input: ModerationInput!) | chat:ban / chat:timeout / chat:delete | Perform a moderation action (ban, timeout, delete). Permission checked per action type. |
cancelRaid | chat:raid | Cancel a pending Twitch raid |
endPoll(pollId, status?) | chat:poll | End an active Twitch poll (status defaults to TERMINATED) |
endPrediction(predictionId, status, winningOutcomeId?) | chat:prediction | End/cancel a Twitch prediction (RESOLVED requires winningOutcomeId) |
REST Endpoints
All paths live under /v1. Bodies are snake_case and mirror the GraphQL inputs.
| Method | Path | Permission | Description |
|---|
GET | /v1/chat/history | chat:read | Paginated chat history (filter by platform/user/date) |
GET | /v1/chat/history/count | chat:read | Total message count for the account |
POST | /v1/chat/message | chat:write | Ingest/buffer a message (bot-facing) |
POST | /v1/chat/send | chat:write | Send a chat message to a connected platform |
POST | /v1/chat/moderate | chat:ban / chat:timeout / chat:delete | Ban, timeout, or delete (permission resolved per action) |
DELETE | /v1/chat/raid | chat:raid | Cancel the current Twitch raid |
DELETE | /v1/chat/poll | chat:poll | End the current Twitch poll |
DELETE | /v1/chat/prediction | chat:prediction | Lock/resolve the current Twitch prediction |
GET | /v1/chat/users/{platform}/{platform_user_id} | chat:userinfo | Unified user profile (DB + enrichment) |
GET | /v1/chat/users/{platform}/{platform_user_id}/follow-status | chat:userinfo | Follow relationship for the user |
GET | /v1/chat/users/{platform}/{platform_user_id}/moderation-log | chat:userinfo | Moderation action history |
GET | /v1/chat/users/{platform}/{platform_user_id}/notes | chat:notes | List moderator notes |
POST | /v1/chat/users/{platform}/{platform_user_id}/notes | chat:notes | Create a moderator note |
PATCH | /v1/chat/users/{platform}/{platform_user_id}/notes/{note_id} | chat:notes | Update a moderator note |
DELETE | /v1/chat/users/{platform}/{platform_user_id}/notes/{note_id} | chat:notes | Delete a moderator note |
PUT | /v1/chat/users/{platform}/{platform_user_id}/treatment | chat:ban | Set user treatment (none, active_monitoring, restricted) |
Permissions
| Permission | Description |
|---|
chat:read | Read chat messages, view emotes |
chat:write | Send/ingest chat messages |
chat:userinfo | View user profiles, follow status, moderation log |
chat:delete | Delete chat messages |
chat:ban | Ban/unban chat users |
chat:timeout | Timeout chat users |
chat:notes | Manage moderator notes on platform users |
chat:raid | Cancel raids |
chat:poll | End polls |
chat:prediction | End predictions |
Database
| Table | Database | Description |
|---|
platform_messages | TimescaleDB | Chat messages hypertable with compression. Fields: id, account_id, platform, channel_name, user_id, username, display_name, message, emotes (JSONB), badges (JSONB), color, is_mod, is_sub, is_vip, sub_tier, platform_message_id, reply fields, deleted_at, deleted_by |
platform_users | PostgreSQL | Platform user profiles with ban status, treatment, follower info, enrichment timestamps |
platform_user_notes | PostgreSQL | Moderator notes on platform users (created_by, created_by_name) |
moderation_log | PostgreSQL | Log of moderation actions (ban, timeout, delete) with target user/message, moderator, reason, duration |
Data Flow
- Platform adapter (Twitch IRC, YouTube live chat, etc.) receives a message.
- Message is sent to the API via
sendChatMessage GraphQL mutation.
- Message is buffered in Redis via
ChatBuffer::push().
- Message is broadcast via Redis pub/sub to all connected WebSocket clients.
PendingChatAccounts tracks the account for the background flush worker.
- Flush worker periodically writes buffered messages from Redis to TimescaleDB in batches.
- Frontend receives the message via WebSocket and renders it with emotes and badges.
Key Files
| Path | Description |
|---|
apps/api/src/graphql/chat.rs | GraphQL queries and mutations |
crates/lo-chat/src/ | Core chat crate (buffer, filter, profile service, emotes, moderation) |
apps/api/src/services/emotes.rs | Emote fetching from Twitch, 7TV, BTTV, FFZ, Trovo, Discord |
apps/api/src/state.rs | PendingChatAccounts shared state |