281 lines
15 KiB
Markdown
281 lines
15 KiB
Markdown
# MpakoImageAIbot
|
||
|
||
Telegram-бот для генерации и редактирования изображений через удалённый ComfyUI API.
|
||
|
||
## Что умеет
|
||
|
||
- **Text-to-image**: текст → изображение (Z-Image Turbo, 1024×1024).
|
||
- **Image-to-image**: фото + подпись → редактирование (Krea2 edit).
|
||
- **Reply-to-image**: ответ текстом на готовую картинку бота → редактирование этой картинки.
|
||
- **Веб-история**: просмотр запросов с фильтрами по `user_id`, статусу и режиму, просмотр изображений.
|
||
- **Улучшение промптов**: перевод и переписывание запроса на английский через `TextGenerate` + `qwen3vl_4b` в ComfyUI.
|
||
- **Фильтрация промптов**: блокировка CSAM, зоофилии, NSFW с публичными людьми и обходных вариантов для не-админов.
|
||
- **Оплата**: 5 Telegram Stars за обычную очередь, 15 за премиум; админам бесплатно, возврат при ошибке.
|
||
- **Премиум-очередь**: чередуется с обычной (одна премиум, одна обычная); если одна пуста — работает другая.
|
||
- **Баланс и рефералы**: 3 стартовые генерации, +1 ежедневно в 00:00 МСК, +3 за первую оплату друга.
|
||
- **Управление пользователями**: в веб-интерфейсе можно выдавать админку, менять баланс и блокировать.
|
||
- **Подтверждение 18+**: без подтверждения возраста бот не принимает запросы.
|
||
- Глобальная очередь с одной активной задачей на пользователя.
|
||
- Позиция в очереди и прогресс генерации через WebSocket ComfyUI.
|
||
- PostgreSQL: запросы, статусы, seed, пути к файлам.
|
||
|
||
## Требования
|
||
|
||
- Python 3.10+ (проверено на 3.13).
|
||
- ComfyUI с доступом по сети, например:
|
||
```text
|
||
python main.py --listen 0.0.0.0 --port 8188
|
||
```
|
||
- Установленные кастомные ноды для текущих workflow: `Krea2ModelLoader`, `TextEncodeQwenImageEdit`, `UnetLoaderGGUF`, `CLIPLoaderGGUF`.
|
||
- Модели, указанные в `workflows/txt2img.json` и `workflows/img2img.json`.
|
||
|
||
## Настройка
|
||
|
||
```powershell
|
||
Copy-Item .env.example .env
|
||
```
|
||
|
||
Заполнить `.env`:
|
||
|
||
- `BOT_TOKEN` — токен от `@BotFather`.
|
||
- `TELEGRAM_PROXY` — прокси для Telegram API (пусто — напрямую).
|
||
- `COMFY_BASE_URL` — например `http://192.168.31.113:8188`.
|
||
- `DATABASE_URL` — строка подключения PostgreSQL. В Docker хост `db`, при локальном запуске — `localhost`.
|
||
- Остальные параметры можно оставить по умолчанию.
|
||
|
||
### Прокси для Telegram
|
||
|
||
Если сервер не имеет прямого доступа к Telegram, укажи прокси V2RayA/HTTP/SOCKS5:
|
||
|
||
```dotenv
|
||
# Docker на Linux (V2RayA слушает на хосте)
|
||
TELEGRAM_PROXY=http://host.docker.internal:20171
|
||
|
||
# Запуск без Docker, на том же хосте
|
||
# TELEGRAM_PROXY=http://127.0.0.1:20171
|
||
```
|
||
|
||
Для SOCKS5 используй `socks5h://host.docker.internal:20170`; зависимость `socksio` уже включена в `requirements.txt`.
|
||
|
||
## Запуск
|
||
|
||
```powershell
|
||
py -3 -m venv .venv
|
||
.\.venv\Scripts\Activate.ps1
|
||
pip install -r requirements.txt
|
||
python bot.py
|
||
```
|
||
|
||
Бот работает в приватных чатах. Пользователь отправляет текст или фото с подписью.
|
||
|
||
Для локального запуска нужен PostgreSQL и `DATABASE_URL` с хостом `localhost`.
|
||
|
||
## Docker
|
||
|
||
```powershell
|
||
docker compose up -d --build
|
||
```
|
||
|
||
Compose поднимает PostgreSQL, бота и веб. Каталог `./storage` монтируется в контейнеры, данные PostgreSQL лежат в `./data/postgres`. Для ComfyUI на другой машине используйте её LAN-IP, а не `localhost`.
|
||
|
||
## PostgreSQL
|
||
|
||
```dotenv
|
||
POSTGRES_DB=mpakoimageaibot
|
||
POSTGRES_USER=mpako
|
||
POSTGRES_PASSWORD=change-me
|
||
DATABASE_URL=postgresql://mpako:change-me@db:5432/mpakoimageaibot
|
||
DB_POOL_MIN_SIZE=1
|
||
DB_POOL_MAX_SIZE=10
|
||
```
|
||
|
||
Пароль в `DATABASE_URL` должен совпадать с `POSTGRES_PASSWORD`; спецсимволы в пароле нужно URL-кодировать.
|
||
|
||
### Перенос старой SQLite-базы
|
||
|
||
```powershell
|
||
python scripts/migrate_sqlite_to_postgres.py `
|
||
--sqlite data/bot.db `
|
||
--database-url "postgresql://mpako:change-me@localhost:5432/mpakoimageaibot"
|
||
```
|
||
|
||
Перед миграцией останови старый бот, чтобы SQLite закрылась и данные были консистентны. Скрипт создаёт схему, переносит данные и пропускает уже существующие записи.
|
||
|
||
## CI
|
||
|
||
При push в `main` или `master` Gitea Actions сначала запускает тесты на PostgreSQL, затем собирает образ на сервере, где запущен runner, и пушит его в Gitea Registry:
|
||
|
||
- `git.mpakosrv.ru/mpakobecex/mpakoimageaibot:latest`
|
||
- `git.mpakosrv.ru/mpakobecex/mpakoimageaibot:<sha>`
|
||
- локально: `mpakoimageaibot:latest`
|
||
|
||
Workflow: `.gitea/workflows/docker-build.yml`. Runner использует label `ubuntu-latest` и смонтированный Docker socket. Для входа в registry нужны секреты `REGISTRY_USER` и `REGISTRY_TOKEN`.
|
||
|
||
- `WORKFLOW_PROMPT` — workflow улучшения промптов (по умолчанию `workflows/prompt_enhance.json`).
|
||
- `PROMPT_ENHANCE_ENABLED` — включить улучшение промптов.
|
||
- `PROMPT_ENHANCE_TIMEOUT` — таймаут генерации промпта в секундах.
|
||
|
||
## Улучшение промптов
|
||
|
||
Перед генерацией исходный текст проходит через ComfyUI `TextGenerate` с уже установленной моделью `qwen3vl_4b_fp8_scaled.safetensors`:
|
||
|
||
- T2I: перевод и переписывание в английский промпт для генерации.
|
||
- I2I: перевод и переписывание инструкции для редактирования.
|
||
- Исходный и улучшенный промпты сохраняются в БД и видны в веб-истории.
|
||
- При ошибке или таймауте используется исходный промпт.
|
||
|
||
Отдельную модель скачивать не нужно, используется существующий CLIP-файл ComfyUI.
|
||
|
||
## Фильтрация промптов
|
||
|
||
Фильтр работает для всех, кроме пользователей из `ADMIN_USER_IDS`:
|
||
|
||
```dotenv
|
||
MODERATION_ENABLED=true
|
||
ADMIN_USER_IDS=7467236616
|
||
BLOCKED_PUBLIC_FIGURES=
|
||
```
|
||
|
||
`BLOCKED_PUBLIC_FIGURES` добавляет имена к встроенному списку (через запятую).
|
||
|
||
Что блокируется:
|
||
|
||
- CSAM: child/kid/minor/teen/young/loli/shota/school/underage + NSFW;
|
||
- зоофилия: sex/nude + animal/dog/cat/horse/beast;
|
||
- публичные люди + nude/naked/sex/undress/deepfake;
|
||
- undress/nude/naked + photo/real/person/woman/man;
|
||
- насилие + NSFW;
|
||
- экстремизм + инструкции/символика;
|
||
- обходные написания (`n_u_d_e`, `l0li`, `ch1ld` и т.п.).
|
||
|
||
Для i2i: если установлен OpenCV, проверяется наличие лица/человека. В базовом образе OpenCV нет, поэтому NSFW-промпты в i2i блокируются консервативно — как если бы на фото был человек.
|
||
|
||
При блокировке пользователь получает «Запрос нарушает правила. Звёзды не списаны.», а запись с `user_id`, временем, промптом и причиной сохраняется в таблицу `blocked_requests`.
|
||
|
||
## Управление пользователями
|
||
|
||
В веб-интерфейсе есть раздел **«Пользователи»** (`/users`):
|
||
|
||
- поиск по ID или username;
|
||
- установка точного баланса генераций;
|
||
- назначение/снятие админа;
|
||
- блокировка/разблокировка.
|
||
|
||
Заблокированный пользователь не может отправлять запросы, получать счета и оплачивать. Админ-флаг из веб-интерфейса действует сразу.
|
||
|
||
`ADMIN_USER_IDS` используется только для первичной инициализации админов; после первого запуска админы хранятся в БД и управляются через веб.
|
||
|
||
## Надёжность Telegram
|
||
|
||
```dotenv
|
||
TELEGRAM_RETRY_ATTEMPTS=0
|
||
TELEGRAM_RETRY_MAX_DELAY=30
|
||
TELEGRAM_HEALTH_INTERVAL=60
|
||
```
|
||
|
||
- `TELEGRAM_RETRY_ATTEMPTS=0` — повторять операции до успеха.
|
||
- Backoff растёт до `TELEGRAM_RETRY_MAX_DELAY`.
|
||
- Критичные отправки (результат, счёт, подтверждение) повторяются бесконечно; обновления статуса — ограниченно, чтобы не плодить зависшие задачи.
|
||
- Раз в `TELEGRAM_HEALTH_INTERVAL` выполняется `getMe`; при сбое проверка повторяется с backoff, пока Telegram не ответит.
|
||
|
||
## Подтверждение возраста
|
||
|
||
Перед использованием бота нужно подтвердить 18+:
|
||
|
||
- в `/start` показываются кнопки «Мне 18+» и «Мне меньше 18»;
|
||
- без подтверждения текстовые и фото-запросы отклоняются;
|
||
- отказ «мне меньше 18» сохраняется в БД и блокирует доступ;
|
||
- статус 18+ виден в веб-разделе «Пользователи».
|
||
|
||
Существующие пользователи проходят подтверждение один раз после обновления.
|
||
|
||
## Баланс и рефералы
|
||
|
||
- Новому пользователю начисляется 3 генерации.
|
||
- Каждый день в 00:00 МСК — ещё 1 генерация.
|
||
- Обычная генерация может быть оплачена 1 генерацией с баланса или 5 звёздами.
|
||
- Премиум-очередь всегда оплачивается 15 звёздами; баланс при этом не расходуется.
|
||
- `/balance` — текущий баланс.
|
||
- `/invite` — реферальная ссылка.
|
||
- За первую оплату приглашённого друга начисляется +3 генерации, реферер получает уведомление.
|
||
|
||
Баланс и рефералы хранятся в таблице `users`. Пропущенные дни начисляются при следующем запуске/полуночи.
|
||
|
||
## Оплата
|
||
|
||
Генерация стоит 5 Telegram Stars в обычной очереди и 15 звёзд в премиум-очереди:
|
||
|
||
```dotenv
|
||
PAYMENTS_ENABLED=true
|
||
STARS_PRICE=5
|
||
PREMIUM_STARS_PRICE=15
|
||
```
|
||
|
||
- Перед оплатой бот показывает количество людей в обычной и премиум-очередях.
|
||
- Пользователь выбирает очередь кнопкой, после чего получает счёт.
|
||
- Премиум- и обычные задачи чередуются: одна премиум, одна обычная; если одна очередь пуста, берётся из другой.
|
||
- Админы из `ADMIN_USER_IDS` генерируют бесплатно в обычной очереди.
|
||
- Не-админу выставляется счёт через Telegram Stars (`XTR`).
|
||
- Фильтрация промпта выполняется до выставления счёта.
|
||
- Если поставить задачу в очередь не удалось, звёзды возвращаются.
|
||
- При ошибке генерации звёзды также возвращаются.
|
||
- Оплата и `telegram_payment_charge_id` сохраняются в `pending_payments` и `requests`.
|
||
|
||
## Веб-история
|
||
|
||
После `docker compose up -d` доступна страница:
|
||
|
||
```text
|
||
http://192.168.31.158:8099
|
||
```
|
||
|
||
Возможности:
|
||
|
||
- фильтр по Telegram `user_id` (с подсказкой известных пользователей);
|
||
- в списке показывается `@username`, если он известен, иначе Telegram ID;
|
||
- фильтр по статусу и режиму (`t2i`/`i2i`);
|
||
- пагинация;
|
||
- превью результата в списке (кэшируются в `storage/.thumbnails`);
|
||
- карточка запроса с полным промптом, ошибкой, seed и изображениями.
|
||
|
||
Доступ только с локальной сети: Compose публикует порт на `WEB_BIND_IP`. Для доступа из LAN:
|
||
|
||
```dotenv
|
||
WEB_BIND_IP=192.168.31.158
|
||
WEB_PAGE_SIZE=50
|
||
```
|
||
|
||
Авторизации нет. Не публикуй порт `8099` наружу и не делай проброс на роутере.
|
||
|
||
После успешной сборки обновление контейнера:
|
||
|
||
```bash
|
||
docker compose up -d
|
||
```
|
||
|
||
Без `--build`: Compose возьмёт готовый образ `mpakoimageaibot:latest`.
|
||
|
||
## Workflow
|
||
|
||
- `workflows/txt2img.json` — Z-Image Turbo: `EmptySD3LatentImage → KSampler → VAEDecode → SaveImage`.
|
||
- `workflows/img2img.json` — Krea2 edit: `LoadImage → TextEncodeQwenImageEdit → KSamplerAdvanced → VAEDecode → SaveImage`.
|
||
- `workflows/mapping.json` — ID узлов, в которые подставляются prompt, negative, seed и входное изображение.
|
||
- `workflows/Krea2.json` и `workflows/ZImage.json` — исходные экспорты.
|
||
|
||
Если меняются workflow, обновите `mapping.json`. При старте бот проверяет, что все узлы и поля существуют.
|
||
|
||
## Тесты
|
||
|
||
```powershell
|
||
pip install -r requirements-dev.txt
|
||
pytest
|
||
```
|
||
|
||
## Ограничения текущей версии
|
||
|
||
- Один воркер и одна активная задача на пользователя.
|
||
- Нет `/cancel` и удаления задачи из очереди.
|
||
- Нет автоочистки `storage/` и `data/`.
|
||
- Прогресс в процентах зависит от WebSocket; при его недоступности используется polling без процентов.
|
||
- При рестарте задача в статусе `running` помечается `failed`.
|