# ComfyUI Discord Bot + Web UI A Discord bot and web interface that integrates with [ComfyUI](https://github.com/comfyanonymous/ComfyUI) to generate AI images and videos. Requests can be submitted through Discord commands or a browser-based UI with real-time progress updates. ## Features **Discord Bot** - Image and video generation via simple prompts or full ComfyUI workflows - Runtime workflow parameter changes (prompt, negative prompt, input images, seeds) - Preset management — save and recall workflow configurations - Generation history with full output retrieval - Server control (start/stop ComfyUI via NSSM service) - Automatic image compression to fit Discord's 8 MiB limit **Web UI** - Invite-token authentication with JWT session cookies - Simple generate form and full dynamic workflow form (auto-discovers all node inputs) - Input image library — upload, browse, and select images for generation - Generation history with image preview - Preset management - Real-time status dashboard (ComfyUI connection, queue depth) - WebSocket-based live progress updates during generation - Admin panel for token and server management ## Architecture ``` the-third-rev/ ├── bot.py # Entry point — Discord bot + Uvicorn run via asyncio.gather ├── config.py # BotConfig dataclass, loads all env vars ├── comfy_client.py # ComfyUI REST + WebSocket client ├── workflow_inspector.py # Dynamic node discovery and override injection ├── workflow_manager.py # Workflow template storage (get/set) ├── workflow_state.py # Runtime override dict with file persistence ├── generation_db.py # SQLite generation history + file BLOBs ├── input_image_db.py # SQLite input image storage ├── token_store.py # SHA-256 hashed invite tokens ├── preset_manager.py # Workflow preset CRUD ├── user_state_registry.py # Per-user workflow state for web sessions ├── image_utils.py # PIL-based image compression ├── media_uploader.py # Optional external media upload ├── status_monitor.py # Background status polling and Discord log channel ├── discord_utils.py # Discord helpers, decorators, argument parsing ├── commands/ # Discord command handlers │ ├── __init__.py │ ├── generation.py # generate, workflow-gen │ ├── workflow.py # workflow-load │ ├── history.py # history, get-history │ ├── input_images.py # input image upload/management │ ├── presets.py # preset save/load/delete │ ├── server.py # ComfyUI server control │ ├── utility.py # test, info, misc │ ├── workflow_changes.py # get/set workflow overrides │ └── help_command.py # custom help ├── web/ # FastAPI application │ ├── app.py # App factory, middleware, static file serving │ ├── auth.py # JWT create/verify │ ├── deps.py # Shared FastAPI dependencies (bot reference) │ ├── login_guard.py # Auth decorators │ ├── ws_bus.py # Per-user WebSocket broadcast bus │ └── routers/ # API endpoints (generate, history, inputs, presets, ...) └── frontend/ # React + TypeScript + Vite + Tailwind source └── src/ ├── pages/ # GeneratePage, WorkflowPage, HistoryPage, ... ├── components/ # Layout, DynamicWorkflowForm, LazyImage ├── hooks/ # useAuth, useWebSocket, useStatus └── context/ # GenerationContext (pending count badge) ``` ComfyUI's own queue handles job ordering — no separate job queue exists in this bot. Generation callbacks are matched to requests via a `prompt_id → callback` map in `ComfyClient`. ## Requirements - Python 3.10+ - Node.js 18+ (for building the frontend) - ComfyUI running and accessible - Discord bot token with **Message Content Intent** enabled **Python dependencies:** ```bash pip install discord.py aiohttp websockets python-dotenv fastapi uvicorn pillow ``` ## Installation ### 1. Clone and install Python dependencies ```bash git clone cd the-third-rev pip install discord.py aiohttp websockets python-dotenv fastapi uvicorn pillow ``` ### 2. Build the frontend ```bash cd frontend npm install npm run build cd .. ``` The build output lands in `web-static/` and is served automatically by FastAPI. ### 3. Configure environment Create a `.env` file in the project root: ```bash # ── Required ──────────────────────────────────────── DISCORD_BOT_TOKEN=your_discord_bot_token COMFY_SERVER=localhost:8188 # ── ComfyUI paths ─────────────────────────────────── COMFY_OUTPUT_PATH=C:\path\to\ComfyUI\output COMFY_INPUT_PATH=C:\path\to\ComfyUI\input COMFY_HISTORY_LIMIT=10 # ── Startup workflow ──────────────────────────────── WORKFLOW_FILE=workflows/my_workflow.json # optional; loaded at startup # ── Web UI ────────────────────────────────────────── WEB_ENABLED=true WEB_HOST=0.0.0.0 WEB_PORT=8080 WEB_SECRET_KEY=change-me-to-a-random-secret WEB_JWT_EXPIRE_HOURS=720 WEB_SECURE_COOKIE=false # set true if serving over HTTPS # ── Admin ─────────────────────────────────────────── ADMIN_PASSWORD=your_admin_password # ── ComfyUI server control (optional) ─────────────── COMFY_SERVICE_NAME=ComfyUI # NSSM service name COMFY_START_BAT=C:\path\to\run_nvidia_gpu.bat COMFY_LOG_DIR=C:\path\to\logs COMFY_AUTOSTART=false # auto-start ComfyUI on bot launch # ── Discord status log channel (optional) ──────────── LOG_CHANNEL_ID=123456789012345678 # ── External media upload (optional) ──────────────── MEDIA_UPLOAD_USER= MEDIA_UPLOAD_PASS= ``` ### Full configuration reference | Variable | Required | Default | Description | |---|---|---|---| | `DISCORD_BOT_TOKEN` | Yes | — | Discord bot token | | `COMFY_SERVER` | Yes | — | ComfyUI address (`host:port`) | | `COMFY_OUTPUT_PATH` | No | `...\ComfyUI\output` | ComfyUI output directory | | `COMFY_INPUT_PATH` | No | `...\ComfyUI\input` | ComfyUI input directory | | `COMFY_HISTORY_LIMIT` | No | `10` | Generations kept in history | | `WORKFLOW_FILE` | No | — | Workflow JSON to load at startup | | `WEB_ENABLED` | No | `true` | Enable web UI | | `WEB_HOST` | No | `0.0.0.0` | Web server bind address | | `WEB_PORT` | No | `8080` | Web server port | | `WEB_SECRET_KEY` | No | — | JWT signing secret (set in production) | | `WEB_JWT_EXPIRE_HOURS` | No | `720` | Session expiry in hours | | `WEB_SECURE_COOKIE` | No | `false` | Mark session cookie as Secure (HTTPS only) | | `WEB_TOKEN_FILE` | No | `invite_tokens.json` | Invite token storage path | | `ADMIN_PASSWORD` | No | — | Admin panel password | | `COMFY_SERVICE_NAME` | No | — | NSSM service name for server control | | `COMFY_START_BAT` | No | — | ComfyUI launch script path | | `COMFY_LOG_DIR` | No | — | Directory for ComfyUI logs | | `COMFY_AUTOSTART` | No | `false` | Auto-start ComfyUI on bot launch | | `LOG_CHANNEL_ID` | No | — | Discord channel ID for status messages | ### 4. Create a web UI invite token The web UI requires an invite token to register an account: ```bash python -c "from token_store import create_token; print(create_token('username'))" ``` Copy the printed token — it is shown only once. Give it to the user who will register. ### 5. Run ```bash python bot.py ``` The bot starts the Discord client and the web server concurrently. Navigate to `http://localhost:8080` to access the web UI. ## Discord Commands All commands use the `ttr!` prefix. ### Generation | Command | Alias | Description | |---|---|---| | `ttr!generate prompt:` | `ttr!gen` | Generate using prompt mode | | `ttr!generate prompt: negative_prompt:` | | Generate with negative prompt | | `ttr!workflow-gen` | `ttr!wfg` | Execute loaded workflow with current overrides | | `ttr!workflow-gen queue:5` | | Queue 5 workflow runs | ### Workflow management | Command | Alias | Description | |---|---|---| | `ttr!workflow-load ` | `ttr!wfl` | Load workflow from file path | | `ttr!workflow-load` (+ attachment) | | Load workflow from attached JSON | | `ttr!get-current-workflow-changes type:all` | `ttr!gcwc` | Show current overrides | | `ttr!set-current-workflow-changes type:prompt ` | `ttr!scwc` | Set prompt override | | `ttr!set-current-workflow-changes type:negative_prompt ` | | Set negative prompt | ### History | Command | Alias | Description | |---|---|---| | `ttr!history` | | List recent generations | | `ttr!get-history ` | `ttr!gh` | Retrieve output from a past generation | ### Presets | Command | Description | |---|---| | `ttr!preset-save ` | Save current workflow overrides as a preset | | `ttr!preset-load ` | Apply a saved preset | | `ttr!preset-list` | List all presets | | `ttr!preset-delete ` | Delete a preset | ### Server control | Command | Description | |---|---| | `ttr!server-start` | Start the ComfyUI NSSM service | | `ttr!server-stop` | Stop the ComfyUI NSSM service | | `ttr!server-status` | Show ComfyUI service status | | `ttr!server-log` | Tail the ComfyUI log file | ### Utility | Command | Description | |---|---| | `ttr!test` | Verify bot is online | | `ttr!help` | Show command list | ## Workflow System ### How node injection works `workflow_inspector.py` dynamically discovers all controllable inputs in any workflow: - **Prompt** — `CLIPTextEncode` node with title containing "Positive Prompt" - **Negative prompt** — `CLIPTextEncode` node with title containing "Negative Prompt" - **Input image** — `LoadImage` nodes (first one = `input_image` key; additional ones get slugified title keys) - **Seed** — any node with `inputs.seed` or `inputs.noise_seed` (auto-randomized unless explicitly set) - **Steps, CFG, checkpoint, LoRA** — discovered and injectable via the web workflow form No hardcoded node IDs. Workflows only need to follow standard ComfyUI node title conventions. ### Workflow overrides persist across restarts Runtime changes are saved to `current-workflow-changes.json` automatically and restored on startup. ### Loading a custom workflow 1. Design and export your workflow in ComfyUI (Save → API Format) 2. Load it in Discord: `ttr!workflow-load path/to/workflow.json` or via the web UI: Workflow page → Upload 3. Set overrides and run: `ttr!workflow-gen` or use the web Generate/Workflow page ## Development ### Frontend development ```bash cd frontend npm run dev # HMR dev server on :5173, proxies /api + /ws to :8080 ``` For production, rebuild with `npm run build`. ### Adding a Discord command 1. Add your handler to the appropriate module in `commands/` (or create a new one) 2. Register it in `commands/__init__.py` → `register_all_commands()` 3. Use `@require_comfy_client` from `discord_utils.py` if the command needs `bot.comfy` See `CLAUDE.md` for full architectural details. ### Adding a web API endpoint 1. Create a router in `web/routers/` 2. Register it in `web/app.py` via `app.include_router()` 3. Use `require_auth` / `require_admin` from `web/auth.py` for protected routes ## Troubleshooting **Web UI shows a blank page in production** Windows may serve `.js` files as `text/plain`. This is fixed in `web/app.py` with explicit MIME type registration — ensure you are running the latest version. **ComfyUI connection refused** Check `COMFY_SERVER` in `.env` and confirm ComfyUI is running. Test with `curl http://localhost:8188`. **Commands not responding** Ensure the bot has **Message Content Intent** enabled in the Discord Developer Portal and has sufficient channel permissions. **Videos not delivered** Set `COMFY_OUTPUT_PATH` to the correct ComfyUI output directory. The bot reads video files directly from disk. **Web UI WebSocket disconnects immediately** Set a proper `WEB_SECRET_KEY` — an empty secret causes JWT validation failures. ## Credits Built with: - [discord.py](https://github.com/Rapptz/discord.py) — Discord API - [ComfyUI](https://github.com/comfyanonymous/ComfyUI) — AI image/video generation backend - [FastAPI](https://fastapi.tiangolo.com/) — Web API framework - [Uvicorn](https://www.uvicorn.org/) — ASGI server - [React](https://react.dev/) + [Vite](https://vitejs.dev/) + [Tailwind CSS](https://tailwindcss.com/) — Web frontend - [aiohttp](https://github.com/aio-libs/aiohttp) — Async HTTP client - [Pillow](https://python-pillow.org/) — Image compression