Khoa (Revenovich) Tran Gia 1748cbf8d2 fix: suppress Uvicorn WebSocket rejection noise in logs
Unknown-path WS probes (e.g. /waapi) generate three INFO lines from
uvicorn.access + uvicorn.error on every attempt. Install a logging.Filter
on both loggers at startup to drop:
  - access log entries:  "WebSocket <path>" 403
  - error log entries:   "connection rejected ..." / "connection closed"

These are handled gracefully by _SPAStaticFiles; the logs add no value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 11:52:44 +07:00

ComfyUI Discord Bot + Web UI

A Discord bot and web interface that integrates with 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:

pip install discord.py aiohttp websockets python-dotenv fastapi uvicorn pillow

Installation

1. Clone and install Python dependencies

git clone <repo-url>
cd the-third-rev
pip install discord.py aiohttp websockets python-dotenv fastapi uvicorn pillow

2. Build the frontend

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:

# ── 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:

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

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:<text> ttr!gen Generate using prompt mode
ttr!generate prompt:<text> negative_prompt:<text> 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 <path> 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 <text> ttr!scwc Set prompt override
ttr!set-current-workflow-changes type:negative_prompt <text> Set negative prompt

History

Command Alias Description
ttr!history List recent generations
ttr!get-history <id> ttr!gh Retrieve output from a past generation

Presets

Command Description
ttr!preset-save <name> Save current workflow overrides as a preset
ttr!preset-load <name> Apply a saved preset
ttr!preset-list List all presets
ttr!preset-delete <name> 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:

  • PromptCLIPTextEncode node with title containing "Positive Prompt"
  • Negative promptCLIPTextEncode node with title containing "Negative Prompt"
  • Input imageLoadImage 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

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__.pyregister_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:

Description
No description provided
Readme 244 KiB
Languages
Python 74.6%
TypeScript 25.2%