Skip to main content

Chat

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

QueryPermissionDescription
chatHistory(filter: ChatFilterInput)chat:readPaginated chat message history with filters for platform, user, date range
chatMessageCountchat:readTotal message count for the account
platformUserProfile(platform, platformUserId)chat:userinfoUnified user profile with platform API enrichment via ProfileService
emotes(platform, channelId)chat:readEmote sets for a platform/channel (Twitch, 7TV, BTTV, FFZ, Trovo, Discord)
userEmoteschat:readTwitch emotes available to the authenticated user (subs, globals, follower)
platformUserNotes(platform, platformUserId)chat:notesList moderator notes for a platform user
moderationLog(platform, platformUserId, page, limit)chat:userinfoModeration action history for a user

GraphQL Mutations

MutationPermissionDescription
sendChatMessage(input: ChatMessageInputGql!)chat:writeBuffer a chat message in Redis and broadcast via pub/sub
sendChatToPlatform(input: SendToPlatformInput!)chat:writeSend a chat message to a platform (twitch, kick, trovo)
createPlatformUserNote(platform, platformUserId, note)chat:notesCreate a moderator note on a user
updatePlatformUserNote(noteId, note)chat:notesUpdate an existing moderator note
deletePlatformUserNote(noteId)chat:notesDelete a moderator note
updateUserTreatment(platform, platformUserId, treatment)chat:banUpdate chat user treatment (none, active_monitoring, restricted); Twitch syncs to Helix best-effort
moderateChat(input: ModerationInput!)chat:ban / chat:timeout / chat:deletePerform a moderation action (ban, timeout, delete). Permission checked per action type.
cancelRaidchat:raidCancel a pending Twitch raid
endPoll(pollId, status?)chat:pollEnd an active Twitch poll (status defaults to TERMINATED)
endPrediction(predictionId, status, winningOutcomeId?)chat:predictionEnd/cancel a Twitch prediction (RESOLVED requires winningOutcomeId)

REST Endpoints

All paths live under /v1. Bodies are snake_case and mirror the GraphQL inputs.

MethodPathPermissionDescription
GET/v1/chat/historychat:readPaginated chat history (filter by platform/user/date)
GET/v1/chat/history/countchat:readTotal message count for the account
POST/v1/chat/messagechat:writeIngest/buffer a message (bot-facing)
POST/v1/chat/sendchat:writeSend a chat message to a connected platform
POST/v1/chat/moderatechat:ban / chat:timeout / chat:deleteBan, timeout, or delete (permission resolved per action)
DELETE/v1/chat/raidchat:raidCancel the current Twitch raid
DELETE/v1/chat/pollchat:pollEnd the current Twitch poll
DELETE/v1/chat/predictionchat:predictionLock/resolve the current Twitch prediction
GET/v1/chat/users/{platform}/{platform_user_id}chat:userinfoUnified user profile (DB + enrichment)
GET/v1/chat/users/{platform}/{platform_user_id}/follow-statuschat:userinfoFollow relationship for the user
GET/v1/chat/users/{platform}/{platform_user_id}/moderation-logchat:userinfoModeration action history
GET/v1/chat/users/{platform}/{platform_user_id}/noteschat:notesList moderator notes
POST/v1/chat/users/{platform}/{platform_user_id}/noteschat:notesCreate a moderator note
PATCH/v1/chat/users/{platform}/{platform_user_id}/notes/{note_id}chat:notesUpdate a moderator note
DELETE/v1/chat/users/{platform}/{platform_user_id}/notes/{note_id}chat:notesDelete a moderator note
PUT/v1/chat/users/{platform}/{platform_user_id}/treatmentchat:banSet user treatment (none, active_monitoring, restricted)

Permissions

PermissionDescription
chat:readRead chat messages, view emotes
chat:writeSend/ingest chat messages
chat:userinfoView user profiles, follow status, moderation log
chat:deleteDelete chat messages
chat:banBan/unban chat users
chat:timeoutTimeout chat users
chat:notesManage moderator notes on platform users
chat:raidCancel raids
chat:pollEnd polls
chat:predictionEnd predictions

Database

TableDatabaseDescription
platform_messagesTimescaleDBChat 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_usersPostgreSQLPlatform user profiles with ban status, treatment, follower info, enrichment timestamps
platform_user_notesPostgreSQLModerator notes on platform users (created_by, created_by_name)
moderation_logPostgreSQLLog of moderation actions (ban, timeout, delete) with target user/message, moderator, reason, duration

Data Flow

  1. Platform adapter (Twitch IRC, YouTube live chat, etc.) receives a message.
  2. Message is sent to the API via sendChatMessage GraphQL mutation.
  3. Message is buffered in Redis via ChatBuffer::push().
  4. Message is broadcast via Redis pub/sub to all connected WebSocket clients.
  5. PendingChatAccounts tracks the account for the background flush worker.
  6. Flush worker periodically writes buffered messages from Redis to TimescaleDB in batches.
  7. Frontend receives the message via WebSocket and renders it with emotes and badges.

Key Files

PathDescription
apps/api/src/graphql/chat.rsGraphQL queries and mutations
crates/lo-chat/src/Core chat crate (buffer, filter, profile service, emotes, moderation)
apps/api/src/services/emotes.rsEmote fetching from Twitch, 7TV, BTTV, FFZ, Trovo, Discord
apps/api/src/state.rsPendingChatAccounts shared state