Итоговый промпт в подписи и /restart
Build and push Docker image / tests (push) Successful in 23s Details
Build and push Docker image / docker (push) Successful in 12s Details

В подписи результата показываю итоговый промпт и подсказку /restart. /reset оставил алиасом для совместимости.
This commit is contained in:
MpakobecEx 2026-10-19 22:20:44 +05:00
parent 67d3180ef6
commit d4263c8420
4 changed files with 45 additions and 10 deletions

View File

@ -15,7 +15,7 @@ Telegram-бот для генерации и редактирования изо
- **Баланс и рефералы**: 3 стартовые генерации, +1 ежедневно в 00:00 МСК, +3 за первую оплату друга.
- **Управление пользователями**: в веб-интерфейсе можно выдавать админку, менять баланс и блокировать.
- **Подтверждение 18+**: без подтверждения возраста бот не принимает запросы.
- **Контекст**: следующий текстовый запрос продолжает предыдущую сцену; `/reset` начинает новую.
- **Контекст**: следующий текстовый запрос продолжает предыдущую сцену; `/restart` начинает новую.
- Глобальная очередь с одной активной задачей на пользователя.
- Позиция в очереди и прогресс генерации через WebSocket ComfyUI.
- PostgreSQL: запросы, статусы, seed, пути к файлам.
@ -202,7 +202,7 @@ TELEGRAM_HEALTH_INTERVAL=60
- Премиум-очередь всегда оплачивается 15 звёздами; баланс при этом не расходуется.
- `/balance` — текущий баланс.
- `/invite` — реферальная ссылка.
- `/reset` — сбросить контекст и начать новую сцену.
- `/restart` — сбросить контекст и начать новую сцену (алиас `/reset`).
- За первую оплату приглашённого друга начисляется +3 генерации, реферер получает уведомление.
Баланс и рефералы хранятся в таблице `users`. Пропущенные дни начисляются при следующем запуске/полуночи.
@ -211,10 +211,10 @@ TELEGRAM_HEALTH_INTERVAL=60
- Первый текстовый запрос создаёт контекст: базовую сцену и текущий улучшенный промпт.
- Следующий текст обрабатывается вместе с предыдущим промптом: бот сохраняет сцену и применяет только запрошенное изменение.
- `/reset` очищает контекст; следующий запрос начнёт новую сцену.
- `/restart` очищает контекст; следующий запрос начнёт новую сцену.
- Новое фото с подписью автоматически начинает новую цепочку.
- Для точного редактирования конкретной картинки ответь на сообщение бота с изображением.
- `/reset` не отменяет уже запущенную генерацию: она завершится со своим снимком контекста.
- `/restart` не отменяет уже запущенную генерацию: она завершится со своим снимком контекста.
Контекст хранится в таблице `user_contexts` и обновляется только после успешной отправки результата.

19
bot.py
View File

@ -43,6 +43,19 @@ logger = logging.getLogger(__name__)
MSK = timezone(timedelta(hours=3))
POLLING_WATCHDOG_INTERVAL = 60.0
POLLING_WATCHDOG_STUCK_AFTER = 300.0
RESTART_HINT = "/restart — сброс контекста"
CAPTION_LIMIT = 1024
def result_caption(prompt: str, *, limit: int = CAPTION_LIMIT) -> str:
value = (prompt or "").strip()
suffix = f"\n\n{RESTART_HINT}"
available = max(0, limit - len(suffix))
if len(value) > available:
value = value[: max(0, available - 1)].rstrip()
if value:
value += "…"
return f"{value}{suffix}" if value else RESTART_HINT
def polling_is_stuck(
@ -64,7 +77,7 @@ START_TEXT = (
"/balance — баланс генераций.\n"
"/invite — пригласить друга.\n"
"/status — состояние текущей задачи.\n"
"/reset — начать новую сцену."
"/restart — сброс контекста."
)
BLOCKED_TEXT = "Запрос нарушает правила. Звёзды не списаны."
AGE_TEXT = (
@ -125,6 +138,7 @@ class MpakoBot:
application.add_handler(CommandHandler("status", self._status))
application.add_handler(CommandHandler("balance", self._balance))
application.add_handler(CommandHandler("invite", self._invite))
application.add_handler(CommandHandler("restart", self._reset))
application.add_handler(CommandHandler("reset", self._reset))
application.add_handler(
PreCheckoutQueryHandler(self._pre_checkout)
@ -1284,7 +1298,8 @@ class MpakoBot:
if self._bot is None:
return
caption = f"Готово. Seed: {request.seed}" if request.seed is not None else "Готово."
prompt = request.enhanced_prompt or request.prompt_text
caption = result_caption(prompt)
filename = Path(output_path).name
try:
content = await asyncio.to_thread(Path(output_path).read_bytes)

View File

@ -8,7 +8,7 @@
- Реализованы блоки 2–7, базовое хранилище (9), файлы Docker (10), тесты (11), README (12), веб-история (13), улучшение промптов (14), модерация (15), оплата Stars (16), баланс/рефералы (17), управление пользователями (18), возрастной gate (19) и надёжность Telegram (20).
- Вживую проверены: T2I (Z-Image), I2I (Krea2), WebSocket-прогресс, улучшение промптов, Docker, CI и веб-история.
- 66 тестов: 39 проходят локально без БД, 27 тестов БД выполняются в CI на PostgreSQL через `TEST_DATABASE_URL`. Покрыты workflows, очередь, веб-история, enhancer, модерация, платежи, премиум-очередь, баланс/рефералы, управление пользователями, retry, миграция, watchdog, контекст, админ-панель, скрытие истории.
- 69 тестов: 42 проходят локально без БД, 27 тестов БД выполняются в CI на PostgreSQL через `TEST_DATABASE_URL`. Покрыты workflows, очередь, веб-история, enhancer, модерация, платежи, премиум-очередь, баланс/рефералы, управление пользователями, retry, миграция, watchdog, контекст, админ-панель, скрытие истории.
- Не проверено: реальная оплата Stars и реферальная выплата в Telegram, очередь из 3+ пользователей, падение ComfyUI, рестарт с `running`-задачей, детекция лица на реальном фото. PostgreSQL-миграция и тесты БД ещё не запускались вживую — это сделает CI и первый деплой.
## 0. Допущения и решения по умолчанию
@ -387,10 +387,11 @@ ON requests(user_id) WHERE status IN ('queued', 'running');
- [x] Снимок контекста сохраняется в `requests` и `pending_payments`.
- [x] Следующий текстовый запрос обрабатывается вместе с предыдущим промптом.
- [x] Контекст обновляется только после успешной отправки результата.
- [x] `/reset` очищает контекст; новое фото начинает новую цепочку.
- [x] Тесты БД, enhancer и очереди.
- [x] `/restart` очищает контекст; новое фото начинает новую цепочку.
- [x] В подписи результата итоговый промпт и подсказка `/restart`.
- [x] Тесты БД, enhancer, очереди и подписи.
**DoD:** продолжение сцены работает по тексту, `/reset` возвращает к новой сцене.
**DoD:** продолжение сцены работает по тексту, `/restart` возвращает к новой сцене.
## 22. Админ-панель и очистка ComfyUI

19
tests/test_caption.py Normal file
View File

@ -0,0 +1,19 @@
from __future__ import annotations
from bot import RESTART_HINT, result_caption
def test_result_caption_contains_prompt_and_restart_hint() -> None:
caption = result_caption("A red apple on a table")
assert caption == f"A red apple on a table\n\n{RESTART_HINT}"
def test_result_caption_truncates_long_prompt() -> None:
caption = result_caption("x" * 2000, limit=100)
assert len(caption) <= 100
assert caption.endswith(RESTART_HINT)
assert "…" in caption
def test_result_caption_empty_prompt() -> None:
assert result_caption("") == RESTART_HINT