Telegram-бот с mini app для генерации изображений через ComfyUI.
Внутри:
- Backend на Node.js/Express.
- Telegram webhook-бот: команда
/startприсылает кнопку mini app. - Frontend на React/Vite.
- Генерация через ComfyUI
/prompt. - Очередь генераций с лимитами на пользователя и глобальной конкурентностью.
- Прогресс через WebSocket ComfyUI → WebSocket mini app.
- История генераций с картинками, параметрами, избранным, поиском и копированием параметров обратно в форму.
- Пресеты генерации из JSON-файла.
- Checkpoint/sampler/scheduler/LoRA подтягиваются из ComfyUI
/object_info, если ComfyUI доступен. - Workflow-адаптеры: текущая реализация содержит
sd15-text2img, но структура готова под SDXL/Flux/img2img/upscale. - Конфиги для Cloudflare Named Tunnel.
- Опциональный автостарт
cloudflaredвместе с backend и остановка при завершении Node-процесса.
Нужны:
- Node.js 20+.
- Запущенный ComfyUI.
- Для тайловой оптимизации VRAM: установленный в ComfyUI custom node
ComfyUI-TiledDiffusion. - Telegram bot token от BotFather.
- HTTPS-домен через Cloudflare Named Tunnel, например
https://your-bot.example.com.
Скопируй конфиг:
cp config.example.yaml config.yamlЗаполни минимум:
server:
publicBaseUrl: https://your-bot.example.com
miniAppUrl: https://your-bot.example.com
telegram:
botToken: "123456:ABC..."
webhookSecret: "long-random-secret"
comfy:
httpUrl: http://127.0.0.1:8188
wsUrl: ws://127.0.0.1:8188/wsЕсли backend запущен в Docker, а ComfyUI на хост-машине, обычно удобнее так:
comfy:
httpUrl: http://host.docker.internal:8188
wsUrl: ws://host.docker.internal:8188/wsnpm install
npm run build
npm startBackend поднимется на http://127.0.0.1:8080.
Поставь Telegram webhook:
npm run set-webhookПроверь:
curl http://127.0.0.1:8080/api/healthДля визуальной разработки и тестирования полного приложения запусти:
npm run dev:browserЗатем открой http://127.0.0.1:5173.
Этот режим использует обычные настройки ComfyUI, workflow, presets и лимиты из
config.yaml, поэтому генерация, история, hires-fix, ControlNet и остальные
функции работают так же, как в Telegram Mini App. Telegram initData и бот для
локального открытия не нужны.
Browser-dev режим изолирован от обычных пользователей:
- backend и Vite слушают только
127.0.0.1; - Cloudflare tunnel не запускается;
- используется отдельный пользователь
browser-dev; - история и изображения хранятся в
tmp/browser-dev/data; - временное хранилище очищается при каждом новом запуске
npm run dev:browser, но сохраняется при автоматическом рестарте backend после изменения исходников; backend/dataи история Telegram-пользователей не читаются и не изменяются.
docker compose up -d --buildПосле этого поставь webhook. Можно выполнить внутри контейнера:
docker compose exec tg-comfy-miniapp npm run set-webhookПараметры очереди лежат в config.yaml:
jobs:
maxConcurrentGlobal: 1
maxQueuePerUser: 3
allowInterruptRunning: falsemaxConcurrentGlobal определяет, сколько генераций backend одновременно отправляет в ComfyUI.
maxQueuePerUser ограничивает количество ожидающих задач одного Telegram-пользователя.
При отмене backend сначала проверяет /queue: ожидающий prompt удаляется адресно,
а выполняющийся prompt прерывается, если он единственный активный в ComfyUI.
allowInterruptRunning по умолчанию выключен, потому что ComfyUI /interrupt
глобальный. Если одновременно выполняются другие prompt'ы, backend откажется
прерывать задачу, чтобы не остановить чужую генерацию. Включай
allowInterruptRunning, только если этот bot — единственный пользователь
ComfyUI и допустима глобальная остановка всех выполняющихся prompt'ов.
Пресеты лежат в JSON-файле:
presets:
file: ./presets/default.jsonФормат:
{
"presets": [
{
"id": "fast-draft",
"name": "Быстрый черновик",
"description": "Меньше шагов, удобно быстро проверять промпт.",
"settings": {
"workflowType": "sd15-text2img",
"width": 512,
"height": 512,
"steps": 12,
"cfg": 6,
"seed": -1
}
}
]
}Настройки пресета накладываются поверх defaultSettings backend-а.
По умолчанию используется файл:
comfy:
workflowFile: ./workflows/sd15-basic.json
defaultWorkflowType: sd15-text2imgЭто простой API workflow:
CheckpointLoaderSimple → CLIPTextEncode → EmptyLatentImage → KSampler → VAEDecode → SaveImage
LoRA добавляются динамически через LoraLoader между checkpoint и CLIP/KSampler.
Текущий адаптер sd15-text2img ожидает наличие этих node-классов:
CheckpointLoaderSimpleCLIPTextEncode— два узла: positive и negativeEmptyLatentImageKSamplerSaveImage
Для SDXL, Flux, img2img, inpaint, upscale или ControlNet добавляй новый адаптер в backend/src/comfy/workflows/ и регистрируй его в backend/src/comfy/workflowBuilder.js.
В расширенных настройках доступны три режима:
Выключена— обычный workflow, максимальная скорость.Тайловый VAE— тайловые encode/decode; уменьшает пик памяти на VAE-этапах.Полная тайловая— дополнительно делит diffusion/sampling на тайлы; сильнее экономит VRAM на больших изображениях, но работает медленнее и может изменить композицию или добавить швы.
Тайловые режимы требуют ComfyUI-TiledDiffusion. После установки перезапусти
ComfyUI и обнови ресурсы в mini app. Если нужных нод нет, режимы будут
недоступны в интерфейсе, а backend отклонит прямой API-запрос с понятной
ошибкой.
Рекомендуемая отправная точка:
Diffusion: 768×768, overlap 64, tile batch size 1
VAE encode/decode: tile size 512, fast enabled
Доступные параметры:
method:MultiDiffusion,Mixture of DiffusersилиSpotDiffusion;tile width/tile height: размер diffusion-тайла,16–8192, шаг16;tile overlap: перекрытие diffusion-тайлов,0–2048, шаг32;tile batch size: число одновременно обрабатываемых тайлов,1–8192;VAE encode tile size:256–4096, шаг16;VAE decode tile size:384–4096, шаг16;fastиcolor fixдля поддерживающих их VAE-этапов.
При нехватке памяти сначала уменьши размеры тайлов и оставь batch size равным
1. При заметных границах между тайлами увеличь overlap. Увеличивай tile batch
size только при наличии свободной VRAM.
Обычная генерация и hires-fix используют один и тот же компонент настроек, но хранят независимые значения. Hires-fix также показывает параметры VAE encode, поскольку он начинает работу с исходной картинки.
Все API-запросы mini app отправляют заголовок:
X-Telegram-Init-Data: <Telegram.WebApp.initData>Backend валидирует подпись initData через bot token. Для локальной отладки можно временно поставить:
telegram:
enforceAuth: falseНе оставляй это в проде.
Новые изображения отдаются через авторизованный endpoint /api/history/:id/images/:filename.
Старый публичный /generated больше не включается по умолчанию. Если нужно временно открыть старые URL для миграции уже сохранённой истории, включи:
server:
legacyGeneratedRoute: trueПосле миграции лучше снова вернуть false.
Можно разрешить только конкретные Telegram user ID:
telegram:
allowedUserIds:
- 123456789
- 987654321Пустой список означает: разрешён любой пользователь, который открыл mini app через бота.
История и скачанные результаты сохраняются здесь:
backend/data/db.json
backend/data/generated/
Backend держит актуальное состояние в памяти и пишет db.json с небольшим debounce, чтобы progress-события не долбили файл на каждый процент.
Для более серьёзного продакшена следующий естественный шаг — заменить JsonGenerationRepository на SQLite-репозиторий с тем же интерфейсом.
Локальный вариант:
cloudflared tunnel login
cloudflared tunnel create tg-comfy-miniapp
cloudflared tunnel route dns tg-comfy-miniapp your-bot.example.comСкопируй пример:
cp cloudflared/config.yml.example cloudflared/config.ymlВпиши tunnel, credentials-file, hostname. Для ручного запуска tunnel:
cloudflared tunnel --config cloudflared/config.yml run tg-comfy-miniappМожно сделать так, чтобы npm start запускал и backend, и cloudflared. Для этого в config.yaml включи:
cloudflare:
autoStart: true
executable: cloudflared
configFile: ./cloudflared/config.yml
tunnelName: tg-comfy-miniapp
hostname: your-bot.example.com
logLevel: info
restartOnExit: true
restartDelayMs: 5000На Windows, если cloudflared не лежит в PATH, укажи полный путь:
cloudflare:
executable: C:\Programs\cloudflared\cloudflared.exeПосле этого обычный запуск:
npm startВ консоли должны появиться строки с префиксом [cloudflared]. При Ctrl+C backend отправит cloudflared сигнал остановки.
Если запускаешь cloudflared внутри docker-compose, используй:
cp cloudflared/config.docker.yml.example cloudflared/config.ymlИ раскомментируй сервис cloudflared в docker-compose.yml. Для Docker обычно не нужно включать cloudflare.autoStart, потому что tunnel лучше держать отдельным сервисом compose.
Проверь, что server.publicBaseUrl и server.miniAppUrl начинаются с https://. Telegram Web Apps требуют HTTPS URL.
Проверь comfy.httpUrl и comfy.wsUrl. Если ComfyUI запущен на хосте, используй host.docker.internal и оставь extra_hosts в docker-compose.yml.
Backend пытается прочитать /object_info у ComfyUI. Если не получилось, используются fallback-значения из config.yaml.
Имя checkpoint в форме должно совпадать с файлом в ComfyUI. Открой /api/comfy/resources после авторизации или посмотри список в mini app.
Важно: этот бот работает через Telegram webhook, не через long polling. Поэтому npm start только запускает backend; после этого webhook должен быть установлен командой:
npm run set-webhookПроверить всю цепочку можно так:
npm run check-setupЕсли запуск через Docker:
docker compose logs -f tg-comfy-miniapp
docker compose exec tg-comfy-miniapp npm run check-setupЧто должно быть в норме:
local backend /api/healthвозвращает200.public backend /api/healthвозвращает200через твой Cloudflare HTTPS-домен.telegram getWebhookInfoпоказывает URL видаhttps://твой-домен/telegram/webhook.- В
last_error_messageу Telegram пусто. - После сообщения
/startв логах backend появляется строка[telegram] update=... text="/start".
Если public backend /api/health не открывается, проблема почти точно в Cloudflare Tunnel/домене/ingress.
Если включён cloudflare.autoStart, проверь, что после npm start в консоли есть строки [cloudflared]. Если там failed to start, укажи полный путь к cloudflared.exe в cloudflare.executable.
Если health открывается, но в логах нет [telegram] update=..., проблема в webhook URL или npm run set-webhook не был выполнен после смены домена.
Если лог есть, но кнопка не приходит, смотри ошибку Telegram API в логах backend.