commit d94e8dd48ffc28868445d9b19a8f9a1c78d0f14b Author: MpakobecEx Date: Sun Oct 18 17:03:50 2026 +0500 chore: initial project plan and agent rules diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e996dc9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +.env +.env.* +!.env.example + +__pycache__/ +*.py[cod] +.venv/ +venv/ +.pytest_cache/ + +storage/ +data/ +*.db +*.db-wal +*.db-shm + +.idea/ +.vscode/ diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000..f47c0c2 --- /dev/null +++ b/AGENT.md @@ -0,0 +1,44 @@ +# AGENT.md — правила работы + +## Ответы + +- **Коротко и по делу.** Без вступлений, извинений и повторов. +- Сначала результат, потом минимальные пояснения. +- Язык — русский, если не попросили иначе. +- Не пересказывать очевидное; ссылаться на конкретные файлы. +- Нужны уточнения — задать 1–3 конкретных вопроса. +- Не выдумывать факты: проверить или спросить. + +## Проект + +- Корень: `D:\!Projects\MpakoImageAIbot`. +- Стек: Python 3.10+, python-telegram-bot 21+, httpx, aiosqlite, SQLite, Docker. +- Требования: `plan.md`. План работ: `checklist.md`; отмечать выполненные пункты. +- Следовать структуре, именованию и стилю существующего кода. +- Секреты — только в `.env`. Не логировать токены, не коммитить `.env`, `storage/`, `data/`. + +## Git / Gitea + +- Remote: `ssh://git@home:2222/mpakobecex/MpakoImageAIbot.git` + (алиас `home` → `192.168.31.158`). +- Основная ветка — `main`. +- Перед коммитом: `git diff`, тесты/линтер, если есть. +- Коммиты атомарные: `feat:`, `fix:`, `docs:`, `chore:`. +- Не делать force push и не переписывать историю. +- Push — после успешной проверки. + +## Работа с кодом + +- Сначала прочитать существующий код, затем менять. +- Изменения минимальные и точечные; без попутного рефакторинга. +- Не удалять и не перезаписывать пользовательские файлы без подтверждения. +- Сетевые вызовы — async, event loop не блокировать. +- Ошибки обрабатывать явно: понятное сообщение пользователю + лог. +- После изменений проверять запуск/тесты и сообщать результат. +- Не оставлять проект в нерабочем состоянии. + +## Безопасность + +- Не выводить содержимое `.env` и приватные ключи. +- Не выполнять деструктивные команды без подтверждения. +- Не добавлять зависимости без необходимости. diff --git a/checklist.md b/checklist.md new file mode 100644 index 0000000..4ce5924 --- /dev/null +++ b/checklist.md @@ -0,0 +1,311 @@ +# Checklist реализации MpakoImageAIbot + +Источник: `plan.md`. Бот на `python-telegram-bot` v21+ (async) генерирует изображения через удалённый ComfyUI API, ведёт глобальную очередь, показывает позицию/прогресс и хранит запросы в SQLite. + +Блоки идут в порядке зависимостей. В конце каждого блока — критерий готовности (DoD). + +## 0. Допущения и решения по умолчанию + +| Вопрос | Решение по умолчанию | +|---|---| +| HTTP-клиент | `httpx.AsyncClient`, без блокирующего `requests` | +| Очередь | Собственная очередь в приложении, по умолчанию 1 воркер; в ComfyUI уходит не более 1 задачи одновременно | +| Прогресс | WebSocket `/ws` + fallback-опрос `/history/{prompt_id}` | +| Один активный запрос на пользователя | Проверка в памяти + частичный уникальный индекс в SQLite | +| Задача в статусе `running` при рестарте | Пометить `failed` (не перезапускать автоматически) | +| Задачи `queued` при рестарте | Восстановить из БД и поставить в очередь заново | +| i2i без подписи | Отклонить и попросить промпт (не использовать дефолтный) | +| Чаты | Приватные; `user_id` считается id чата | +| Workflow | JSON в API-формате (экспорт «Save (API Format)» из ComfyUI) | +| Язык интерфейса | Русский | + +Согласовано: **1 воркер**, задача `running` при рестарте → **`failed`**, workflow готовим вместе (см. блок 1). + +## 1. Подготовка workflow и окружения (до кода) + +### 1.1 Экспорт workflow + +- [ ] Открыть ComfyUI, включить **Settings → Dev mode Options**, чтобы появился пункт **Save (API Format)**. +- [ ] Собрать t2i-граф: `CheckpointLoaderSimple → CLIPTextEncode (positive) → CLIPTextEncode (negative) → KSampler → VAEDecode → SaveImage`. +- [ ] Собрать i2i-граф: `LoadImage → VAEEncode → KSampler (denoise 0.4–0.7) → VAEDecode → SaveImage`. +- [ ] Экспортировать оба графа кнопкой **Save (API Format)** в `workflows/txt2img_api.json` и `workflows/img2img_api.json`. +- [ ] Убедиться, что в JSON только API-узлы (`class_type`, `inputs`), без UI-метаданных и без `extra`-полей. +- [ ] Определить ID нужных узлов. Удобно: выделить узел → `Properties` → `Node ID`, либо найти по `class_type` в JSON (`CLIPTextEncode`, `KSampler`, `LoadImage`). +- [ ] Заполнить `workflows/mapping.json` (ID ниже — примеры, заменить на реальные): + +```json +{ + "txt2img": { + "positive": "6", + "negative": "7", + "seed": "3", + "checkpoint": "4" + }, + "img2img": { + "positive": "6", + "negative": "7", + "seed": "3", + "input_image": "10", + "denoise": "3" + } +} +``` + +- [ ] Проверить t2i вручную: отправить JSON на `POST /prompt` и получить `prompt_id`, затем забрать результат через `/history` и `/view`. +- [ ] Проверить i2i: загрузить тестовое фото через `/upload/image`, подставить имя в `LoadImage`, выполнить генерацию. +- [ ] Проверить i2i с фото произвольного размера (портрет/ландшафт); при необходимости добавить `ImageScale` в граф. +- [ ] Зафиксировать дефолтные `steps`, `cfg`, `denoise`, negative prompt и размер изображения. + +### 1.2 Окружение + +- [ ] Убедиться, что Python 3.10+ доступен локально и в Docker-образе. +- [ ] Проверить сетевую доступность ComfyUI: `GET http://IP_WINDOWS:PORT/system_stats`. +- [ ] Получить токен бота у `@BotFather`. +- [ ] Определить значения лимитов: максимальная длина промпта, максимальный размер фото, размер очереди, таймаут генерации. +- [ ] Решить, где будет запускаться бот (локально / Docker) и как он видит ComfyUI (LAN, VPN, `host.docker.internal`). + +**DoD:** есть токен, два валидных workflow JSON, заполненный `mapping.json`; тестовый `POST /prompt` возвращает `prompt_id`, результат скачивается. + +## 2. Каркас проекта и конфигурация + +- [ ] Создать структуру: + +```text +. +├── bot.py +├── comfy_client.py +├── db.py +├── queue_manager.py +├── config.py +├── workflows/ +│ ├── txt2img_api.json +│ ├── img2img_api.json +│ └── mapping.json +├── storage/ # создаётся автоматически, в .gitignore +├── data/ # SQLite, в .gitignore +├── tests/ +├── requirements.txt +├── requirements-dev.txt +├── Dockerfile +├── .dockerignore +├── .env.example +├── .gitignore +└── README.md +``` + +- [ ] `requirements.txt`: `python-telegram-bot`, `httpx`, `aiosqlite`, `websockets`, `python-dotenv` (версии — диапазонами, например `>=21,<23`). +- [ ] `requirements-dev.txt`: `pytest`, `pytest-asyncio`, `respx`. +- [ ] `config.py`: dataclass/`Settings`, загрузка `.env`, валидация обязательных полей, вычисление WS-URL из base URL. +- [ ] Переменные окружения: `BOT_TOKEN`, `COMFY_BASE_URL`, `WORKFLOW_T2I`, `WORKFLOW_I2I`, `WORKFLOW_MAPPING`, `STORAGE_DIR`, `DB_PATH`, `WORKER_CONCURRENCY=1`, `MAX_QUEUE_SIZE`, `ALLOWED_USER_IDS`, `PROGRESS_INTERVAL`, `GENERATION_TIMEOUT`, `LOG_LEVEL`. +- [ ] Настроить логирование в stdout: уровень из env, формат с timestamp/level/logger, никогда не логировать токен и содержимое `.env`. +- [ ] `.env.example` со всеми переменными и комментариями. +- [ ] `.gitignore`: `.env`, `storage/`, `data/`, `__pycache__/`, `.pytest_cache/`. +- [ ] Проверить импорт конфига и падение с понятной ошибкой при отсутствии `BOT_TOKEN`. + +**DoD:** `python -c "from config import get_settings; print(get_settings())"` работает; секреты не попадают в репозиторий. + +## 3. Слой хранения (`db.py`) + +- [ ] Реализовать `init_db()`: подключение `aiosqlite`, `PRAGMA journal_mode=WAL`, `PRAGMA busy_timeout`, `PRAGMA foreign_keys=ON`, `row_factory`. +- [ ] Создать таблицу `requests` по плану: + +```sql +CREATE TABLE IF NOT EXISTS requests ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + timestamp TEXT NOT NULL, + prompt_text TEXT NOT NULL, + input_file_path TEXT, + output_file_path TEXT, + status TEXT NOT NULL DEFAULT 'queued' + CHECK (status IN ('queued', 'running', 'done', 'failed')), + comfy_prompt_id TEXT +); +``` + +- [ ] Добавить расширения схемы (согласовать): `mode TEXT NOT NULL CHECK (mode IN ('t2i','i2i'))`, `error_text TEXT`, `finished_at TEXT`, `seed INTEGER`. +- [ ] Индексы: `(user_id, status)`, `(status, id)`. +- [ ] Частичный уникальный индекс «один активный запрос на пользователя»: + +```sql +CREATE UNIQUE INDEX IF NOT EXISTS uq_requests_one_active +ON requests(user_id) WHERE status IN ('queued', 'running'); +``` + +- [ ] CRUD: `create_request`, `get_request`, `get_active_by_user`, `set_status`, `set_comfy_prompt_id`, `set_output_path`, `set_error`, `list_queued`, `count_ahead`, `mark_running_as_failed`. +- [ ] Все временные метки — ISO 8601 UTC (`datetime.now(timezone.utc).isoformat()`). +- [ ] Атомарность: создание запроса и проверка активного — в одной транзакции; при конфликте unique-индекса возвращать понятную ошибку `UserAlreadyQueued`. +- [ ] Восстановление при старте: `queued` читаются по `id`, `running` → `failed` с текстом «прервано перезапуском». +- [ ] Тесты: создание, смена статусов, запрет второго активного запроса, выборка очереди, восстановление. + +**DoD:** тесты БД зелёные; повторный `init_db()` не ломает существующую БД. + +## 4. Клиент ComfyUI (`comfy_client.py`) + +- [ ] Класс `ComfyClient` с ленивой инициализацией `httpx.AsyncClient` и явным `close()`. +- [ ] `submit_workflow(workflow, client_id) -> prompt_id`: `POST /prompt` с телом `{"prompt": workflow, "client_id": client_id}`. +- [ ] Обработка ответа `/prompt`: HTTP-ошибки, `error`, `node_errors` — поднимать `ComfyError` с человекочитаемым текстом. +- [ ] `upload_image(path, request_id) -> image_ref`: `POST /upload/image` (multipart), уникальное имя файла, `type=input`; вернуть имя/подпапку для `LoadImage`. +- [ ] `get_history(prompt_id) -> dict`: `GET /history/{prompt_id}`; извлекать список изображений из `outputs[*]["images"]`. +- [ ] `download_image(filename, subfolder, type, dest_path)`: `GET /view` потоково, запись через временный файл + `os.replace`. +- [ ] `wait_for_result(prompt_id, timeout)`: fallback-опрос истории с интервалом 2 с; таймаут → `ComfyTimeout`. +- [ ] WebSocket-прогресс: `ws://.../ws?clientId=...`, подключение **до** `POST /prompt`, чтобы не потерять ранние события. +- [ ] Разбор событий: `progress` (`value`/`max`, при наличии `prompt_id` — фильтровать), `executing`, `execution_success`, `execution_error`, `status`. +- [ ] Переподключение WS с backoff; при недоступности WS — не падать, переходить на polling. +- [ ] Таймауты: connect 10 с, запросы 30 с, ожидание генерации — `GENERATION_TIMEOUT`. +- [ ] Не ретраить слепо `POST /prompt` (риск дублей); ретраить только идемпотентные GET/upload. +- [ ] Типы ошибок: `ComfyConnectionError`, `ComfyHTTPError`, `ComfyExecutionError`, `ComfyTimeout`. +- [ ] Тесты с `respx`/fake-сервером: успешный submit, `node_errors`, таймаут, скачивание, парсинг progress. + +**DoD:** отдельный скрипт/тест отправляет тестовый workflow на реальный ComfyUI, получает `prompt_id`, progress и сохраняет картинку. + +## 5. Шаблоны workflow + +- [ ] Положить `txt2img_api.json` и `img2img_api.json` в `workflows/`. +- [ ] `workflows/mapping.json`: соответствие «роль узла → node id» для каждого workflow (positive, negative, seed, input_image, denoise). +- [ ] Валидация mapping при старте: все указанные узлы существуют, у них ожидаемые поля; иначе — падение с понятной ошибкой. +- [ ] `build_txt2img(prompt, negative, seed) -> dict`: глубокое копирование шаблона, подстановка prompt/negative/seed. +- [ ] `build_img2img(prompt, negative, seed, image_name, denoise) -> dict`: то же + имя загруженного изображения. +- [ ] Генерация seed: `secrets.randbelow(2**32)`, сохранять использованный seed в БД. +- [ ] Обрезка/нормализация промпта (strip, лимит длины), значение negative prompt по умолчанию из конфига. +- [ ] Гарантировать, что исходный шаблон не мутируется между запросами. +- [ ] Тесты: подстановка значений, отсутствующий узел, отсутствующий файл workflow. + +**DoD:** юнит-тесты подтверждают корректную подстановку; при старте приложения шаблоны и mapping валидны. + +## 6. Очередь и менеджер задач (`queue_manager.py`) + +- [ ] `QueueManager` с зависимостями: `db`, `ComfyClient`, `WorkflowBuilder`, `Settings`. +- [ ] Внутренняя очередь `collections.deque` + `asyncio.Event` (не `asyncio.Queue`, чтобы уметь считать позицию). +- [ ] `asyncio.Lock` для всех операций с очередью и реестром активных пользователей. +- [ ] `enqueue(user_id, mode, prompt, input_path) -> EnqueueResult(request, position)`: + - [ ] проверить активный запрос пользователя (память + БД); + - [ ] проверить `MAX_QUEUE_SIZE`; + - [ ] создать запись в БД, добавить id в очередь, вернуть позицию. +- [ ] Позиция: количество задач впереди + выполняющаяся задача; считать уникальных пользователей (по плану — «X человек»). +- [ ] Воркер: цикл `while not stopping`, брать задачу, никогда не умирать из-за ошибки одной задачи. +- [ ] Жизненный цикл задачи: `queued → running → done/failed`; обновлять `comfy_prompt_id`, `output_file_path`, `error_text`, `finished_at`. +- [ ] Для i2i: upload изображения → подстановка имени → submit. +- [ ] Уникальный `client_id` на задачу; WS-подключение до submit, закрытие после завершения. +- [ ] Прогресс: throttle (не чаще `PROGRESS_INTERVAL`, шаг ≥ 5%), вычисление процента из `value/max`. +- [ ] События наружу через callback/event bus: `request_started`, `progress`, `request_done`, `request_failed` — без импорта Telegram в менеджере. +- [ ] Обработка ошибок: любое исключение → `failed`, лог с `request_id`, событие для бота. +- [ ] `start()`: восстановить `queued` из БД, пометить `running` как `failed`, запустить воркер. +- [ ] `stop()`: остановить приём, дождаться воркера, пометить незавершённые задачи, закрыть клиент. +- [ ] Тесты с fake-клиентом: порядок FIFO, позиции, дубль пользователя, ошибка генерации, отмена при остановке. + +**DoD:** два пользователя получают корректные позиции; повторная заявка отклоняется; прогресс доходит до подписчика; после рестарта `queued` восстанавливаются. + +## 7. Telegram-слой (`bot.py`) + +- [ ] Инициализация `Application` (PTB v21+, async), регистрация `post_init`/`post_shutdown` для старта/остановки `QueueManager`. +- [ ] Команды `/start` и `/help`: краткая инструкция по двум режимам и формату ввода. +- [ ] (Опционально) `/status`: позиция и статус текущего запроса. +- [ ] Текстовый хендлер (`filters.TEXT & ~filters.COMMAND`): создать t2i-запрос, поставить в очередь. +- [ ] Фото-хендлер (`filters.PHOTO`): взять `photo[-1]`, скачать через `get_file().download_to_drive()`, сохранить в `storage/{user_id}/`. +- [ ] Для i2i: caption обязателен; пустой caption → подсказка «пришлите фото с описанием». +- [ ] Валидация: пустой/слишком длинный промпт, размер фото, поддерживаемые форматы; лимит caption 1024 символа. +- [ ] Ответ при дубле: «Ты уже в очереди» + текущая позиция (по плану). +- [ ] Ответ при постановке: «Принято. Перед тобой X человек» / «Ты первый, начинаю…». +- [ ] Статусное сообщение: сохранить `message_id` в памяти; редактировать при `request_started` («Генерация… 0%») и `progress`. +- [ ] Throttle редактирования статуса (не чаще 3 с), игнорировать `Message is not modified`. +- [ ] По завершении: отправить результат как фото (`FSInputFile`), статусное сообщение удалить или заменить на «Готово». +- [ ] По ошибке: редактировать статус на понятный текст, логировать техническую причину с `request_id`. +- [ ] (Опционально) `ALLOWED_USER_IDS`: отказ остальным. +- [ ] (Опционально) `ChatAction.UPLOAD_PHOTO` при отправке результата. +- [ ] Аккуратно обрабатывать `TelegramError`/`BadRequest`, не ронять приложение. +- [ ] Проверить, что команды не перехватываются текстовым хендлером. + +**DoD:** вручную проходят сценарии t2i и i2i; при дубле приходит корректный отказ; прогресс обновляется без флуда. + +## 8. Сквозная интеграция + +- [ ] Связать `bot.py → QueueManager → db → ComfyClient → storage`. +- [ ] t2i: текст → картинка → файл в `storage/{user_id}/` → отправка в чат → `done` в БД. +- [ ] i2i: фото + caption → upload → workflow → картинка → отправка → `done`. +- [ ] Очередь из 3 пользователей: позиции 1/2/3, затем корректное продвижение. +- [ ] ComfyUI недоступен: задача → `failed`, пользователь получает понятную ошибку, воркер жив. +- [ ] Ошибка выполнения workflow (`execution_error`): сообщение пользователю без стектрейса. +- [ ] Рестарт с задачами в очереди: `queued` продолжают выполняться, `running` помечены `failed`. +- [ ] Повторный запуск после падения: БД и файлы консистентны. +- [ ] Проверить отсутствие блокировки event loop (все I/O — async). + +**DoD:** все сценарии выше пройдены на реальном ComfyUI. + +## 9. Файловое хранилище + +- [ ] Структура: `storage/{user_id}/{request_id}_input.`, `storage/{user_id}/{request_id}_output.`. +- [ ] Безопасные имена (только сгенерированные, не из пользовательского ввода), защита от path traversal. +- [ ] Атомарная запись: `.part` → `os.replace`. +- [ ] Создание директорий при старте/записи. +- [ ] (Опционально) очистка файлов старше N дней по расписанию; настройка `FILE_RETENTION_DAYS`. +- [ ] (Опционально) проверка свободного места и понятная ошибка при переполнении. +- [ ] Не хранить бинарные данные в SQLite — только пути. + +**DoD:** входные и выходные файлы лежат по ожидаемым путям; повторный запуск не конфликтует по именам. + +## 10. Docker и деплой + +- [ ] `Dockerfile` на `python:3.11-slim`, непривилегированный пользователь, `PYTHONUNBUFFERED=1`. +- [ ] Копирование кода и установка `requirements.txt`; без лишних build-зависимостей. +- [ ] `.dockerignore`: `.env`, `storage/`, `data/`, `__pycache__/`, `tests/`, `.git/`. +- [ ] `docker-compose.yml`: `env_file: .env`, volume для `./storage` и `./data`, `restart: unless-stopped`. +- [ ] Сетевой доступ к ComfyUI: `host.docker.internal` для Windows/macOS или LAN-IP для Linux; зафиксировать в README. +- [ ] Проверить, что SQLite и storage переживают пересоздание контейнера. +- [ ] (Опционально) healthcheck. +- [ ] Логи только в stdout/stderr. + +**DoD:** `docker compose up -d` поднимает бота, генерация работает, данные сохраняются после `down/up`. + +## 11. Тесты и приёмка + +- [ ] `pytest` + `pytest-asyncio`, общая фикстура временной БД и storage. +- [ ] Юнит-тесты: `db.py`, подстановка workflow, расчёт позиции, throttle прогресса, парсинг WS-событий. +- [ ] Интеграционный тест: fake ComfyUI (HTTP + WS) → полный цикл `enqueue → running → done`, файл создан. +- [ ] Тест ошибок: ComfyUI 500, `node_errors`, таймаут, обрыв WS. +- [ ] Тест конкуренции: одновременные сообщения одного пользователя → только один запрос. +- [ ] Ручной e2e на реальном ComfyUI (t2i, i2i, очередь, рестарт). +- [ ] Проверка лимитов Telegram: размер результата, длина caption, частота редактирований. + +**DoD:** тесты зелёные; ручной чек-лист из блока 8 пройден. + +## 12. Документация + +- [ ] `README.md`: описание, схема работы, требования. +- [ ] Инструкция по экспорту workflow в API-формате и заполнению `mapping.json`. +- [ ] Таблица переменных окружения с примерами. +- [ ] Запуск локально и через Docker. +- [ ] Раздел troubleshooting: ComfyUI недоступен, WS не подключается, «ты уже в очереди», ошибки workflow. +- [ ] Краткое описание архитектуры и границ MVP. + +**DoD:** новый разработчик поднимает проект по README без устных пояснений. + +## Порядок работ + +1. Блок 1 (подготовка) — без него нельзя валидировать клиент и workflow. +2. Блок 2 (каркас) → Блок 3 (БД) → Блок 4 (ComfyUI-клиент) → Блок 5 (workflow). +3. Блок 6 (очередь) → Блок 7 (бот) → Блок 8 (интеграция). +4. Блоки 9 (storage) и 10 (Docker) можно делать параллельно после блока 2. +5. Блоки 11 (тесты) и 12 (документация) — параллельно с 6–10, финализация после 8. + +## Открытые вопросы + +Согласовано: 1 воркер; `running` при рестарте → `failed`; workflow готовим вместе. Осталось уточнить: + +1. Какой чекпоинт/модель используется (SD 1.5, SDXL, Flux) и какие дефолтные `steps`/`cfg`/`denoise` и размер изображения? +2. Какая версия ComfyUI? В старых сборках событие `progress` может не содержать `prompt_id` — нужен другой способ корреляции. +3. i2i без подписи: отклонять (по умолчанию) или использовать фиксированный промпт? +4. Нужны ли `/cancel`, удаление из очереди и ограничение доступа `ALLOWED_USER_IDS` в MVP? +5. Сколько хранить входные/выходные файлы? Нужна ли автоочистка? +6. Где будет хоститься бот и как он видит ComfyUI (LAN/VPN/интернет)? +7. Добавлять ли в схему `mode`, `seed`, `error_text`, `finished_at` (сейчас они предложены как расширение плана)? +8. Уведомлять ли пользователя, если его `running`-задача была помечена `failed` после рестарта? + +## Вне скоупа MVP + +- Платежи, подписки, квоты. +- Несколько моделей/чекпоинтов и выбор модели пользователем. +- Галерея, история генераций в боте, повтор генерации. +- LoRA/ControlNet и другие продвинутые workflow. +- Горизонтальное масштабирование и несколько экземпляров бота на одной SQLite. +- Админ-панель. diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..eda03b8 --- /dev/null +++ b/plan.md @@ -0,0 +1,44 @@ +Ты — senior Python-разработчик. Нужно написать Telegram-бота для генерации изображений через удалённый ComfyUI API. + +## Стек +- Python 3.10+ +- python-telegram-bot (v21+, async) [citation:3] +- SQLite (через aiosqlite или стандартный sqlite3 с осторожностью) +- requests / httpx для вызова ComfyUI API +- Docker для финальной упаковки + +## Функционал (два режима) +1. **Text-to-Image**: пользователь отправляет текст → бот шлёт POST на ComfyUI `/prompt` с подставленным промптом [citation:14]. +2. **Image-to-Image**: пользователь отправляет фото + текст → бот скачивает фото, загружает его в ComfyUI (через `/upload/image` или временную папку), подставляет в воркфлоу как input image, шлёт POST. + +## Очередь (критично) +- Один пользователь = одна активная задача. Если он пишет ещё раз, пока висит его задача — бот отвечает «ты уже в очереди». +- Нужна глобальная очередь. Показывай: + - Если в очереди: «Перед тобой X человек». + - Если генерация идёт: «Генерация… Y%» (прогресс брать из WebSocket ComfyUI, события `progress` [citation:25][citation:35]). + +## Хранение (SQLite) +Таблица `requests`: +- id (PK) +- user_id (telegram id) +- timestamp (ISO) +- prompt_text +- input_file_path (nullable) +- output_file_path (nullable) +- status (queued/running/done/failed) +- comfy_prompt_id (для отслеживания) + +Файлы (входные фото и результаты) сохраняй локально в папку `./storage/{user_id}/`. В БД только пути. + +## Вызов ComfyUI +- База: `http://IP_WINDOWS:PORT` +- POST `/prompt` → получаешь `prompt_id` [citation:2][citation:14]. +- Опрос статуса: либо через GET `/history/{prompt_id}` (проще, но медленнее), либо через WebSocket на `/ws` (для прогресса в реальном времени) [citation:30][citation:35]. +- После завершения скачай изображение через `/view?filename=...`. + +## Что сделать сейчас +1. Создай структуру проекта: `bot.py`, `comfy_client.py`, `db.py`, `queue_manager.py`. +2. Напиши `comfy_client.py` с функциями submit_workflow (POST /prompt) и get_result (скачивание). +3. Напиши `db.py` с init_db() и CRUD для requests. +4. Напиши `bot.py` с хендлерами для текста и фото, но очередь пока сделай простейшей (asyncio.Queue). +5. Добавь `requirements.txt` и `Dockerfile`. \ No newline at end of file