2.3 KiB
2.3 KiB
Discord — Connector
The Discord module embeds a Discord bot in-process alongside FastAPI. It uses the same LangGraph graphs and LLM client as the REST API — there is no HTTP loopback, no separate process, and no code duplication.
Files
| File | Purpose |
|---|---|
bot.py |
Discord Client subclass (AgentBot) — DM handler, command parser, graph invoker, Quick Connect orchestrator |
conversation.py |
In-memory conversation history store, keyed by Discord user ID |
Architecture
Discord Gateway (websocket)
│ DM: "What's trending?"
▼
discord.Client.on_message()
│ 1. Check: is this a DM? shares a guild? not a command?
│ 2. Build message history from ConversationStore
│ 3. Append user message
▼
_create_agent_graph(agent_id="media-agent")
│ Uses the exact same create_agent_graph() from src/graph.py
│ as the REST API — same LLM client, same tools, same cache.
▼
graph.ainvoke({"messages": [...]})
│ LangGraph runs agent_node → tool_node → agent_node → END
▼
Response text → split into ≤2000-char Discord messages → sent to user
Commands
Commands are DMs that start with /. The bot parses them before hitting the
LLM:
| Command | Action |
|---|---|
/login <service> |
Generate a one-time auth link, DM it to the user |
/jellyfin login |
Alias for /login jellyfin |
/help |
Show available agents and commands |
/<agent_id> |
Switch to a different agent for future messages |
Auth Flow (Quick Connect)
When a user types /login jellyfin:
- Bot generates a one-time token via
auth_store - Bot calls
auth_store.create_link_token(discord_id, "jellyfin") - Bot DMs the user:
https://<BASE_URL>/api/v1/auth/login?service=jellyfin&token=...&discord_id=... - User clicks the link → FastAPI serves the Jellyfin login form (or Quick Connect prompt)
- User authenticates → credentials stored in
auth_store - Future tool calls (e.g.
watch_history) automatically use the stored Jellyfin session
Conversation Persistence
- Per-user history stored in
ConversationStore(in-memory dict) - Max history length configurable via
DISCORD_MAX_HISTORYenv var (default: 7) - Oldest messages are silently dropped when the limit is exceeded
- History is NOT persisted across restarts (future: could use SQLite)