# Discord Bot Usage Documentation ## Overview The Claude Coordinator Discord bot (`claude_coordinator/bot.py`) provides channel-based message routing to Claude CLI sessions. Each Discord channel maps to a specific project with isolated sessions and custom configurations. **Implementation**: 244 lines of Python code **Test Coverage**: 20 comprehensive test cases (455 lines), 100% passing ## Architecture ### Main Components - **ClaudeCoordinator** (discord.ext.commands.Bot subclass) - Message routing and filtering - Session lifecycle management - Integration with SessionManager, Config, ClaudeRunner - Error handling and Discord response formatting ### Key Features 1. **@Mention Trigger Mode**: Bot only responds when explicitly mentioned (safe for MVP) 2. **Channel-to-Project Mapping**: Each channel routes to a specific project directory 3. **Session Persistence**: Sessions maintained per-channel across restarts 4. **Typing Indicator**: Shows "Bot is typing..." while Claude processes 5. **Error Handling**: Graceful handling of timeouts, failures, and edge cases 6. **Response Chunking**: Automatically splits long responses at Discord's 2000 char limit ## Installation ### 1. Ensure Dependencies are Installed The bot requires: - discord.py >= 2.6.4 - aiosqlite >= 0.22.1 - pyyaml >= 6.0.3 These are already in `pyproject.toml` and should be installed via `uv sync`. ### 2. Configuration Create `~/.claude-coordinator/config.yaml`: ```yaml projects: my-project: name: "my-project" channel_id: "1234567890123456789" # Discord channel ID (as string) project_dir: "/path/to/project" allowed_tools: - "Bash" - "Read" - "Write" - "Edit" system_prompt: "You are a helpful coding assistant for this project." model: "sonnet" # or "opus", "haiku" ``` To get a Discord channel ID: 1. Enable Developer Mode in Discord (User Settings → Advanced → Developer Mode) 2. Right-click a channel → Copy Channel ID ### 3. Get Discord Bot Token 1. Go to https://discord.com/developers/applications 2. Create New Application 3. Navigate to Bot tab → Add Bot 4. Copy the Bot Token 5. Set environment variable: ```bash export DISCORD_TOKEN="your-token-here" ``` ### 4. Invite Bot to Server 1. In Discord Developer Portal → OAuth2 → URL Generator 2. Select scopes: `bot` 3. Select permissions: `Send Messages`, `Read Message History`, `View Channels` 4. Copy generated URL and open in browser 5. Select server and authorize ## Running the Bot ### Manual Execution ```bash cd /opt/projects/claude-coordinator export DISCORD_TOKEN="your-token-here" uv run python -m claude_coordinator.bot ``` ### As a Service (Recommended) Create `/etc/systemd/system/claude-coordinator.service`: ```ini [Unit] Description=Claude Discord Coordinator Bot After=network.target [Service] Type=simple User=discord-bot WorkingDirectory=/opt/projects/claude-coordinator Environment="DISCORD_TOKEN=your-token-here" ExecStart=/home/discord-bot/.local/bin/uv run python -m claude_coordinator.bot Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` Enable and start: ```bash sudo systemctl daemon-reload sudo systemctl enable claude-coordinator sudo systemctl start claude-coordinator sudo systemctl status claude-coordinator ``` View logs: ```bash sudo journalctl -u claude-coordinator -f ``` ## Usage ### Basic Usage 1. Go to a configured Discord channel 2. Mention the bot with your message: ``` @ClaudeCoordinator Can you help me debug this function? ``` 3. Bot shows typing indicator while processing 4. Claude's response appears in the channel ### Session Continuity Sessions are persistent per-channel: - First message creates a new session - Subsequent messages resume the same session - Session history maintained in SQLite database - Sessions persist across bot restarts ### Example Conversation ``` User: @ClaudeCoordinator Can you list the files in this project? Bot: I'll list the files in /path/to/project for you. [Claude's response with file listing] User: @ClaudeCoordinator Can you read setup.py? Bot: [Reads and displays setup.py contents, maintaining context from previous message] ``` ### Project Isolation - Each channel has its own isolated session - Sessions run in the configured `project_dir` - Only configured `allowed_tools` are available - Custom `system_prompt` sets project-specific behavior ## Message Filtering Logic The bot processes a message if ALL of the following are true: 1. ✅ Message author is NOT a bot 2. ✅ Bot was mentioned in the message (`@ClaudeCoordinator`) 3. ✅ Channel is configured in config.yaml Otherwise, the message is silently ignored. ## Error Handling ### Empty Message If you mention the bot with no content: ``` User: @ClaudeCoordinator Bot: ❌ Please provide a message after mentioning me. ``` ### Claude Failure If Claude CLI command fails: ``` Bot: ❌ **Error running Claude:** ``` Command failed: invalid syntax ``` ``` ### Timeout (>5 minutes) ``` Bot: ❌ **Timeout:** Claude took too long to respond (>5 minutes). ``` ### Unexpected Errors All exceptions are caught and reported: ``` Bot: ❌ **Unexpected error:** ``` [error details] ``` ``` ## Testing Run the test suite: ```bash cd /opt/projects/claude-coordinator uv run pytest tests/test_bot.py -v ``` **Test Coverage**: - Bot initialization and configuration - Message filtering (bot messages, mentions, unconfigured channels) - Message content extraction (removing mentions, whitespace) - Session management (creation, resumption, persistence) - Claude integration (config passing, response handling) - Error handling (empty messages, Claude failures) - Typing indicator verification **Results**: 20/20 tests passing (100%) ## Monitoring ### Database Location Session data stored in: ``` ~/.claude-coordinator/sessions.db ``` ### Inspect Sessions ```bash cd /opt/projects/claude-coordinator uv run python << 'EOF' import asyncio from claude_coordinator.session_manager import SessionManager async def main(): async with SessionManager() as sm: sessions = await sm.get_all_sessions() for s in sessions: print(f"Channel: {s['channel_id']}") print(f" Project: {s['project_name']}") print(f" Session: {s['session_id']}") print(f" Messages: {s['message_count']}") print(f" Last Active: {s['last_active']}") print() asyncio.run(main()) EOF ``` ### Logs With systemd service: ```bash # Follow logs in real-time sudo journalctl -u claude-coordinator -f # View last 100 lines sudo journalctl -u claude-coordinator -n 100 # View logs from today sudo journalctl -u claude-coordinator --since today ``` Manual execution logs go to stdout: ``` 2026-02-13 18:00:00 - claude_coordinator.bot - INFO - ClaudeCoordinator bot initialized 2026-02-13 18:00:01 - claude_coordinator.bot - INFO - Loaded configuration with 3 projects 2026-02-13 18:00:02 - discord.client - INFO - logging in using static token 2026-02-13 18:00:03 - claude_coordinator.bot - INFO - Logged in as ClaudeBot (ID: 123456789) 2026-02-13 18:00:03 - claude_coordinator.bot - INFO - Connected to 1 guilds ✓ Bot ready: ClaudeBot ``` ## Advanced Configuration ### Multiple Projects Configure different channels for different projects: ```yaml projects: web-app: name: "web-app" channel_id: "111111111111111111" project_dir: "/home/cal/projects/web-app" allowed_tools: ["Bash", "Read", "Write", "Edit", "Grep", "Glob"] model: "sonnet" data-pipeline: name: "data-pipeline" channel_id: "222222222222222222" project_dir: "/home/cal/projects/data-pipeline" allowed_tools: ["Bash", "Read", "Write"] # More restrictive system_prompt: "You are a data engineering assistant. Focus on Python and SQL." model: "opus" docs-site: channel_id: "333333333333333333" project_dir: "/home/cal/projects/docs" allowed_tools: ["Read", "Write", "Edit"] # No Bash execution system_prompt_file: "/home/cal/prompts/technical-writer.txt" model: "haiku" # Faster for documentation ``` ### External System Prompts Instead of inline `system_prompt`, use a file: ```yaml projects: my-project: # ... other config ... system_prompt_file: "/path/to/prompt.txt" ``` The file will be loaded and passed to Claude on each invocation. ## Security Considerations 1. **Tool Restrictions**: Use `allowed_tools` to limit what Claude can do - Production projects: Consider disabling `Bash` tool - Read-only access: Only allow `Read`, `Grep`, `Glob` 2. **Channel Access Control**: Use Discord role permissions to control who can access bot channels 3. **Environment Variables**: Never commit `DISCORD_TOKEN` to git - Use environment variables or systemd service config - Store in a secure secrets manager 4. **Database Permissions**: Session database should only be readable by bot user ```bash chmod 600 ~/.claude-coordinator/sessions.db ``` ## Troubleshooting ### Bot doesn't respond 1. **Check bot was mentioned**: Must use `@ClaudeCoordinator`, not just typing its name 2. **Check channel is configured**: Channel ID must be in config.yaml 3. **Check bot is running**: `systemctl status claude-coordinator` 4. **Check logs**: `journalctl -u claude-coordinator -f` ### "Timeout" errors - Claude CLI took >5 minutes to respond - This is configured in ClaudeRunner (default: 300 seconds) - Increase timeout in `claude_coordinator/claude_runner.py` if needed ### Bot crashes on startup 1. **Check DISCORD_TOKEN is set**: `echo $DISCORD_TOKEN` 2. **Check config file exists**: `ls ~/.claude-coordinator/config.yaml` 3. **Validate config syntax**: `uv run python -c "import yaml; yaml.safe_load(open('~/.claude-coordinator/config.yaml'))"` 4. **Check database permissions**: `ls -la ~/.claude-coordinator/sessions.db` ### Sessions not resuming - Check database exists: `ls ~/.claude-coordinator/sessions.db` - Verify session was saved (check logs for "Successfully processed message") - Inspect database to confirm session_id was stored ## Architecture Details ### Message Flow ``` Discord Message ↓ on_message() event handler ↓ [Filter: Is bot message?] → Yes → Ignore ↓ No [Filter: Bot mentioned?] → No → Ignore ↓ Yes [Filter: Channel configured?] → No → Ignore ↓ Yes _handle_claude_request() ↓ Extract message content (remove mentions) ↓ [Empty message?] → Yes → Send error ↓ No Start typing indicator ↓ Get session from SessionManager (None if new) ↓ Run ClaudeRunner with session_id + project config ↓ [Success?] → No → Send error message ↓ Yes Save/update session in database ↓ Format response (split at 2000 chars if needed) ↓ Send response to Discord channel ``` ### Session Lifecycle ``` User sends first message ↓ SessionManager.get_session(channel_id) → None ↓ ClaudeRunner.run(session_id=None) # New session ↓ Claude CLI creates new session, returns session_id ↓ SessionManager.save_session(channel_id, session_id) ↓ [Subsequent messages] ↓ SessionManager.get_session(channel_id) → session_data ↓ ClaudeRunner.run(session_id=session_data['session_id']) # Resume ↓ SessionManager.update_activity(channel_id) # Update timestamp ``` ## Development ### Running Tests ```bash # All tests uv run pytest tests/test_bot.py -v # Specific test class uv run pytest tests/test_bot.py::TestMessageFiltering -v # Single test uv run pytest tests/test_bot.py::TestMessageFiltering::test_ignores_bot_messages -v # With coverage uv run pytest tests/test_bot.py --cov=claude_coordinator.bot ``` ### Adding New Features 1. Update `claude_coordinator/bot.py` with new functionality 2. Add tests to `tests/test_bot.py` 3. Run full test suite: `uv run pytest tests/test_bot.py -v` 4. Update this documentation ### Code Structure - **`__init__`**: Initialize bot with intents and components - **`setup_hook`**: Async initialization (database, config loading) - **`on_ready`**: Log connection status - **`on_message`**: Main event handler with filtering logic - **`_handle_claude_request`**: Process message, call Claude, send response - **`_extract_message_content`**: Remove bot mentions from message - **`close`**: Clean shutdown of resources ## Future Enhancements Potential improvements for future versions: 1. **Slash Commands**: Support `/claude ` in addition to @mentions 2. **Thread Support**: Create threads for long conversations 3. **Reaction Controls**: React with ❌ to stop processing, ♻️ to retry 4. **Usage Tracking**: Track API costs per channel/user 5. **Admin Commands**: `/session reset`, `/session info`, `/config reload` 6. **Rate Limiting**: Prevent spam/abuse 7. **Multi-user Sessions**: Track per-user sessions instead of per-channel 8. **Attachment Support**: Process code files attached to messages ## Support For issues or questions: - Check logs: `journalctl -u claude-coordinator -f` - Review test output: `uv run pytest tests/test_bot.py -v` - Verify configuration: Ensure config.yaml is valid and channel IDs are correct - Test manually: Run `uv run python -m claude_coordinator.bot` to see startup errors