Cloudflare Worker Discord bot for rag tracking, direct mention replies, and thread-based /ask conversations.
- Runtime: Cloudflare Workers (
src/index.ts) - Language: TypeScript
- Database: Cloudflare D1 (
DB) - AI: Workers AI binding (
AI) and AI Gateway REST; model and prompt config live insrc/ai-config(@cf/...Workers AI models, Unified Billing partner chat models such asgrok/grok-4.3, and web-search models such asopenai/gpt-4o-search-preview) - Queue: Cloudflare Queues (
AI_JOBS,ai-jobs,ai-jobs-dlq) - Stateful connection: Durable Objects (
DiscordGateway) - Discord integration:
- Interactions webhook
- Discord REST calls for command registration, thread creation, and message posting
- Gateway WebSocket for mention-based AI
- Slash commands:
/rag user:<discord-user>/ragboard/ask prompt:<question>
- HTTP endpoints:
POST /discordDiscord interactionsPOST /gateway/startstart gateway connection (bot token auth)GET /gateway/healthgateway status (bot token auth)
- All other public paths, including
/and source-file-looking paths, return404.
flowchart LR
Discord[Discord Interactions] -->|POST /discord: signed interaction JSON| Worker
Worker -->|200 JSON: interaction response| Discord
Operator[Operator] -->|POST /gateway/start: Bearer DISCORD_BOT_TOKEN| Worker
Operator -->|GET /gateway/health: Bearer DISCORD_BOT_TOKEN| Worker
Worker -->|typed Durable Object RPC: start or health| GatewayDO[DiscordGateway Durable Object]
GatewayDO -->|JSON: start result or health state| Worker
Worker -->|JSON response| Operator
Unknown[Other public request] -->|any unconfigured path or method| Worker
Worker -->|404 Not found, or 405 on configured paths with the wrong method| Unknown
sequenceDiagram
actor User as Discord user
participant Discord as Discord Interactions API
participant Worker as Cloudflare Worker POST /discord
participant DB as D1 DB
participant AI as AI Gateway / Workers AI
User->>Discord: Slash command: /rag user, /ragboard, or /ask prompt
Discord->>Worker: POST /discord with interaction JSON + Ed25519 headers
Worker->>Worker: Verify signature and route interaction.data.name
alt /rag
Worker->>Discord: Immediate JSON: deferred interaction response
Worker->>DB: INSERT rag_events: target user, reporter, timestamp
Worker->>DB: UPSERT rag_totals: increment target count
Worker->>DB: SELECT rag total
Worker->>Discord: PATCH original response: mention, total, allowed_mentions
else /ragboard
Worker->>DB: SELECT top rag_totals: user, count, updated_at
DB-->>Worker: Leaderboard rows
Worker-->>Discord: JSON interaction response: leaderboard text
else /ask
Worker->>Discord: Immediate JSON: deferred interaction response
Worker->>AI: Chat request: concise thread title
AI-->>Worker: Thread title
Worker->>Discord: POST channel thread: title, public thread, 1 day archive
Worker->>DB: UPSERT rag_ai_threads: thread id, prompt, requester, title
Worker->>AI: Chat request or web-search Responses request: fresh user prompt
AI-->>Worker: Chat response or cited research response
Worker->>Discord: POST message inside created thread
Worker->>Discord: PATCH original response with thread link
end
sequenceDiagram
actor Operator
actor User as Discord user
participant Worker as Cloudflare Worker
participant GatewayDO as DiscordGateway Durable Object
participant DiscordGateway as Discord Gateway WebSocket
participant Queue as Cloudflare Queue ai-jobs
participant Consumer as Queue consumer
participant DiscordREST as Discord REST API
participant AI as AI Gateway / Workers AI
participant DB as D1 DB
Operator->>Worker: POST /gateway/start with Authorization: Bearer bot token
Worker->>GatewayDO: start() Durable Object RPC
GatewayDO->>GatewayDO: Store gatewayEnabled=true and set watchdog alarm
GatewayDO->>DiscordGateway: WebSocket IDENTIFY/RESUME with bot token and intents
DiscordGateway-->>GatewayDO: READY, heartbeat ACKs, MESSAGE_CREATE events
User->>DiscordGateway: Parent channel message mentioning bot
DiscordGateway-->>GatewayDO: MESSAGE_CREATE payload: author, channel_id, content, mentions
GatewayDO->>Queue: Enqueue channel_reply AiJob: channel, source message, requester, prompt, reply ids
Queue-->>Consumer: Deliver AiJob batch
Consumer->>DiscordREST: Optional GET explicit replied-to message
DiscordREST-->>Consumer: Replied-to message JSON: author, content, attachments
Consumer->>AI: Chat request: fresh user prompt
AI-->>Consumer: Chat response: generated text + optional usage
Consumer->>DB: INSERT rag_ai_interactions: prompt, response, model, status, token usage
Consumer->>DiscordREST: POST channel message: sanitized content, allowed_mentions parse=[]
DiscordREST-->>Consumer: Created message JSON or API error
Consumer->>Queue: ack on success/terminal 4xx, retry on transient errors
User->>DiscordGateway: Later message inside tracked thread, no @ required
DiscordGateway-->>GatewayDO: MESSAGE_CREATE payload for thread channel
GatewayDO->>DB: SELECT rag_ai_threads by thread id
GatewayDO->>Queue: Enqueue thread_reply AiJob
Consumer->>DiscordREST: GET thread messages before messageId, limit historyLimit
Consumer->>AI: Chat request: stored initial prompt + thread history + current message
Consumer->>DiscordREST: POST thread message
- Entry: interaction command routed in
src/index.ts - Handler:
src/commands/rag.ts - Data path:
- insert
rag_eventsrow - upsert/increment
rag_totals - read updated target total
- insert
- AI usage: none
- Response:
- target mention + updated rag total
- Entry: interaction command routed in
src/index.ts - Handler:
src/commands/ragboard.ts - Data path:
- select top 10 from
rag_totalsordered byrag_count
- select top 10 from
- Response:
- ranked leaderboard text or empty-state message
- Entry: interaction command routed in
src/index.ts - Handler:
src/commands/ask.ts - Behavior:
- defers the interaction
- generates a concise AI thread title
- creates a public Discord thread in the current channel
- stores the thread in
rag_ai_threads - posts the sanitized AI response inside the thread
- automatically uses neutral web-search research mode when the prompt asks for current information
- edits the original interaction response with a thread link
- Entry:
- authenticated
POST /gateway/startstarts Durable Object gateway client - gateway listens for Discord
MESSAGE_CREATE
- authenticated
- Handlers:
src/gateway.ts(connection) andsrc/mention.ts(logic) - Queue and worker:
- parent-channel mentions enqueue a
channel_replyjob inAI_JOBS - channel reply jobs answer in the same Discord channel and do not create or record a thread
/askcreates a Discord thread, records it inrag_ai_threads, and posts the answer inside that thread- later messages in a tracked thread enqueue
thread_replyjobs automatically without requiring an @ mention - reply jobs build context from the stored initial prompt plus recent messages in that thread only
- generated replies are sanitized for mentions/IDs
- parent-channel mentions enqueue a
- Delivery:
- direct mentions post in the same Discord channel
/askand tracked-thread follow-ups post inside the Discord thread
AI config is checked into src/ai-config:
discord-response.jsonanddiscord-response-system-prompt.mdcontrol mention replies.ask-web-search.jsonandask-web-search-system-prompt.mdcontrol/askresearch mode.
./deploy.sh