Overview
The Events module handles platform event logging and alerting for streaming events such as followers, subscribers, cheers, raids, channel point redemptions, and tips. Events are stored in a TimescaleDB hypertable with automatic compression for efficient time-series queries. Events are broadcast in real time via Redis pub/sub to WebSocket clients for overlay alerts and dashboard panels. The module supports paginated queries with filters by type, platform, date range, and full-text search.
Architecture
Backend
- GraphQL (
apps/api/src/graphql/events.rs) -- Queries for listing/fetching events with filters. Mutation for emitting test/manual events.
- Crate (
crates/lo-events/src/) -- Core event logic including EventFilter, EventInput, EventRow types, list_events, get_event, insert_event, and broadcast_event functions.
- Database -- Events are stored in TimescaleDB (
platform_events hypertable) with time-based partitioning and compression.
- Real-time -- Events are broadcast via
RedisPubSub using the GqlPubSub wrapper to all WebSocket subscribers for the account.
Frontend
- Next.js API proxy routes call GraphQL internally.
- Event panel displays events with filters by type and platform.
- Overlay alerts consume events via WebSocket for real-time display.
API
GraphQL Queries
| Query | Permission | Description |
|---|
events(filter: EventFilterInput) | events:read | Paginated event list with filters for types, platform, search, date range |
event(id: UUID) | events:read | Get a single event by ID (account-scoped) |
GraphQL Mutations
| Mutation | Permission | Description |
|---|
emitEvent(input: EmitEventInput) | events:create | Emit a test/manual event, insert into TimescaleDB and broadcast via pub/sub |
REST Endpoints
All paths live under /v1. Bodies are snake_case.
| Method | Path | Permission | Description |
|---|
GET | /v1/events | events:read | Paginated event list (accepts the same filters as EventFilterInput) |
GET | /v1/events/{id} | events:read | Fetch a single event by ID (account-scoped) |
POST | /v1/events/emit | events:create | Emit a manual event (insert + broadcast) |
POST | /v1/events/test | events:create | Emit a test event (broadcast only, not persisted in all paths) |
| Field | Type | Description |
|---|
types | [String] | Filter by event types (e.g., ["twitch:follower", "twitch:subscribe"]) |
platform | String | Filter by platform (comma-separated for multiple) |
search | String | Case-insensitive text search in raw JSON data |
from | String | ISO 8601 timestamp -- only events after this time |
to | String | ISO 8601 timestamp -- only events before this time |
page | u32 | Page number (1-based, default 1) |
limit | u32 | Items per page (default 25, max 100) |
Permissions
| Permission | Description |
|---|
events:read | Read/list events |
events:create | Create events (test events, emit) |
events:delete | Delete events |
events:userinfo | View user cards/profiles from event entries |
Database
| Table | Database | Description |
|---|
platform_events | TimescaleDB | Hypertable with compression. Fields: id (UUID), account_id, event_type (platform:action format), platform, external_id, raw (JSONB), created_at |
Event Types
Events follow the platform:action naming convention:
- Twitch:
twitch:follower, twitch:subscribe, twitch:gift_sub, twitch:resub, twitch:cheer, twitch:raid, twitch:reward, twitch:hype_train, twitch:hype_train_end, twitch:poll, twitch:poll_end, twitch:prediction, twitch:prediction_lock, twitch:prediction_end, twitch:goal, twitch:goal_end, twitch:ad_break, twitch:ban, twitch:unban, twitch:stream_online, twitch:stream_offline, twitch:stream_update
- YouTube:
youtube:subscribe, youtube:member, youtube:superchat, youtube:supersticker, youtube:gift_membership, youtube:gift_membership_received, youtube:poll
- Kick:
kick:follower, kick:subscribe, kick:gift, kick:stream_online, kick:stream_offline
- Trovo:
trovo:subscribe, trovo:spell
- Spotify:
spotify:track, spotify:play, spotify:pause, spotify:skip, spotify:volume, spotify:queue_add, spotify:device, spotify:playlist_add, spotify:playlist_remove, spotify:playlist_create, spotify:playlist_edit, spotify:playlist_delete
- StreamElements:
streamelements:tip
- Chat (per-platform):
twitch:chat, youtube:chat, kick:chat, trovo:chat
- Chat (cross-platform):
chat:message (fires for all platforms)
Data Flow
- Platform adapter detects an event (e.g., Twitch EventSub webhook receives a follow).
- Event is inserted into TimescaleDB via
lo_events::insert_event().
- Event is broadcast via
lo_events::broadcast_event() through Redis pub/sub.
- WebSocket server forwards the event to all connected clients for the account.
- Overlay alerts render the event. Dashboard event panel updates in real time.
- Users can query historical events with filters and pagination via
events query.
Key Files
| Path | Description |
|---|
apps/api/src/graphql/events.rs | GraphQL queries and mutations |
crates/lo-events/src/ | Core events crate (insert, list, broadcast, filter types) |
crates/lo-events-db/ | TimescaleDB event storage layer |