# 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 ` | Generate a one-time auth link, DM it to the user | | `/jellyfin login` | Alias for `/login jellyfin` | | `/help` | Show available agents and commands | | `/` | Switch to a different agent for future messages | --- ## Auth Flow (Quick Connect) When a user types `/login jellyfin`: 1. Bot generates a one-time token via `auth_store` 2. Bot calls `auth_store.create_link_token(discord_id, "jellyfin")` 3. Bot DMs the user: `https:///api/v1/auth/login?service=jellyfin&token=...&discord_id=...` 4. User clicks the link → FastAPI serves the Jellyfin login form (or Quick Connect prompt) 5. User authenticates → credentials stored in `auth_store` 6. 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_HISTORY` env var (default: 7) - Oldest messages are silently dropped when the limit is exceeded - History is NOT persisted across restarts (future: could use SQLite)