Команда /context
Build and push Docker image / tests (push) Successful in 14s Details
Build and push Docker image / docker (push) Successful in 9s Details

Показывает текущий промпт, базовую сцену, последний запрос и время обновления контекста.
This commit is contained in:
MpakobecEx 2026-10-20 01:25:42 +05:00
parent bb096dec47
commit 6bbd9cc04a
4 changed files with 81 additions and 3 deletions

View File

@ -204,6 +204,7 @@ TELEGRAM_HEALTH_INTERVAL=60
- `/balance` — текущий баланс. - `/balance` — текущий баланс.
- `/invite` — реферальная ссылка. - `/invite` — реферальная ссылка.
- `/restart` — сбросить контекст и начать новую сцену (алиас `/reset`). - `/restart` — сбросить контекст и начать новую сцену (алиас `/reset`).
- `/context` — показать текущий контекст: промпт, базовую сцену и последний запрос.
- `/settings` — выбор модели Krea2: стандартная Q4 или улучшенная Q6. - `/settings` — выбор модели Krea2: стандартная Q4 или улучшенная Q6.
- При старте бот проверяет `setMyCommands` и включает кнопку меню с командами. - При старте бот проверяет `setMyCommands` и включает кнопку меню с командами.
- За первую оплату приглашённого друга начисляется +3 генерации, реферер получает уведомление. - За первую оплату приглашённого друга начисляется +3 генерации, реферер получает уведомление.
@ -215,6 +216,7 @@ TELEGRAM_HEALTH_INTERVAL=60
- Первый текстовый запрос создаёт контекст: базовую сцену и текущий улучшенный промпт. - Первый текстовый запрос создаёт контекст: базовую сцену и текущий улучшенный промпт.
- Следующий текст обрабатывается вместе с предыдущим промптом: бот сохраняет сцену и применяет только запрошенное изменение. - Следующий текст обрабатывается вместе с предыдущим промптом: бот сохраняет сцену и применяет только запрошенное изменение.
- `/restart` очищает контекст; следующий запрос начнёт новую сцену. - `/restart` очищает контекст; следующий запрос начнёт новую сцену.
- `/context` показывает текущий промпт, базовую сцену и последний запрос.
- Новое фото с подписью автоматически начинает новую цепочку. - Новое фото с подписью автоматически начинает новую цепочку.
- Для точного редактирования конкретной картинки ответь на сообщение бота с изображением. - Для точного редактирования конкретной картинки ответь на сообщение бота с изображением.
- `/restart` не отменяет уже запущенную генерацию: она завершится со своим снимком контекста. - `/restart` не отменяет уже запущенную генерацию: она завершится со своим снимком контекста.

49
bot.py
View File

@ -35,7 +35,7 @@ from telegram.ext import (
from comfy_client import ComfyClient from comfy_client import ComfyClient
from config import Settings, setup_logging from config import Settings, setup_logging
from db import Database, Request, UserAlreadyQueued from db import Database, Request, UserAlreadyQueued, UserContext
from moderation import PromptBlocked, detect_person, is_prompt_allowed from moderation import PromptBlocked, detect_person, is_prompt_allowed
from prompt_enhancer import PromptEnhancer from prompt_enhancer import PromptEnhancer
from queue_manager import QueueEvent, QueueFull, QueueManager from queue_manager import QueueEvent, QueueFull, QueueManager
@ -62,6 +62,34 @@ def commands_need_update(
return current_pairs != desired_pairs return current_pairs != desired_pairs
def format_context(context: UserContext | None) -> str:
if context is None:
return "Контекст пуст. Следующий запрос начнёт новую сцену."
parts = ["Текущий контекст:", "", f"Промпт: {context.current_prompt}"]
if (
context.base_prompt
and context.base_prompt != context.current_prompt
):
parts.extend(["", f"Базовая сцена: {context.base_prompt}"])
if context.last_request_id is not None:
mode = f" ({context.last_mode})" if context.last_mode else ""
parts.append(
f"Последний запрос: #{context.last_request_id}{mode}"
)
if context.updated_at:
try:
updated = datetime.fromisoformat(
context.updated_at
).astimezone(MSK)
except ValueError:
pass
else:
parts.append(f"Обновлён: {updated.strftime('%d.%m.%Y %H:%M')}")
parts.extend(["", "Сбросить: /restart"])
return "\n".join(parts)
def result_caption(prompt: str, *, limit: int = CAPTION_LIMIT) -> str: def result_caption(prompt: str, *, limit: int = CAPTION_LIMIT) -> str:
value = (prompt or "").strip() value = (prompt or "").strip()
suffix = f"\n\n{RESTART_HINT}" suffix = f"\n\n{RESTART_HINT}"
@ -93,6 +121,7 @@ START_TEXT = (
"/invite — пригласить друга.\n" "/invite — пригласить друга.\n"
"/status — состояние текущей задачи.\n" "/status — состояние текущей задачи.\n"
"/restart — сброс контекста.\n" "/restart — сброс контекста.\n"
"/context — текущий контекст.\n"
"/settings — качество Krea2." "/settings — качество Krea2."
) )
BOT_COMMANDS = ( BOT_COMMANDS = (
@ -101,6 +130,7 @@ BOT_COMMANDS = (
BotCommand("balance", "Баланс генераций"), BotCommand("balance", "Баланс генераций"),
BotCommand("invite", "Пригласить друга"), BotCommand("invite", "Пригласить друга"),
BotCommand("status", "Статус текущей задачи"), BotCommand("status", "Статус текущей задачи"),
BotCommand("context", "Текущий контекст"),
BotCommand("settings", "Качество Krea2"), BotCommand("settings", "Качество Krea2"),
BotCommand("restart", "Сброс контекста"), BotCommand("restart", "Сброс контекста"),
) )
@ -163,6 +193,9 @@ class MpakoBot:
application.add_handler(CommandHandler("status", self._status)) application.add_handler(CommandHandler("status", self._status))
application.add_handler(CommandHandler("balance", self._balance)) application.add_handler(CommandHandler("balance", self._balance))
application.add_handler(CommandHandler("invite", self._invite)) application.add_handler(CommandHandler("invite", self._invite))
application.add_handler(
CommandHandler("context", self._context_command)
)
application.add_handler( application.add_handler(
CommandHandler("settings", self._settings_command) CommandHandler("settings", self._settings_command)
) )
@ -583,6 +616,20 @@ class MpakoBot:
) )
await self._retry(message.reply_text, text) await self._retry(message.reply_text, text)
async def _context_command(
self, update: Update, context: ContextTypes.DEFAULT_TYPE
) -> None:
if not await self._check_access(update):
return
message = update.effective_message
user = update.effective_user
if message is None or user is None:
return
user_context = await self._db.get_user_context(user.id)
await self._retry(
message.reply_text, format_context(user_context)
)
@staticmethod @staticmethod
def _quality_keyboard(quality: str) -> InlineKeyboardMarkup: def _quality_keyboard(quality: str) -> InlineKeyboardMarkup:
def label(name: str, value: str) -> str: def label(name: str, value: str) -> str:

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). - Реализованы блоки 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 и веб-история. - Вживую проверены: T2I (Z-Image), I2I (Krea2), WebSocket-прогресс, улучшение промптов, Docker, CI и веб-история.
- 78 тестов: 50 проходят локально без БД, 28 тестов БД выполняются в CI на PostgreSQL через `TEST_DATABASE_URL`. Покрыты workflows, очередь, веб-история, enhancer, модерация, платежи, премиум-очередь, баланс/рефералы, управление пользователями, retry, миграция, watchdog, контекст, админ-панель, скрытие истории, настройки Krea2. - 80 тестов: 52 проходят локально без БД, 28 тестов БД выполняются в CI на PostgreSQL через `TEST_DATABASE_URL`. Покрыты workflows, очередь, веб-история, enhancer, модерация, платежи, премиум-очередь, баланс/рефералы, управление пользователями, retry, миграция, watchdog, контекст, админ-панель, скрытие истории, настройки Krea2.
- Не проверено: реальная оплата Stars и реферальная выплата в Telegram, очередь из 3+ пользователей, падение ComfyUI, рестарт с `running`-задачей, детекция лица на реальном фото. PostgreSQL-миграция и тесты БД ещё не запускались вживую — это сделает CI и первый деплой. - Не проверено: реальная оплата Stars и реферальная выплата в Telegram, очередь из 3+ пользователей, падение ComfyUI, рестарт с `running`-задачей, детекция лица на реальном фото. PostgreSQL-миграция и тесты БД ещё не запускались вживую — это сделает CI и первый деплой.
## 0. Допущения и решения по умолчанию ## 0. Допущения и решения по умолчанию
@ -388,6 +388,7 @@ ON requests(user_id) WHERE status IN ('queued', 'running');
- [x] Следующий текстовый запрос обрабатывается вместе с предыдущим промптом. - [x] Следующий текстовый запрос обрабатывается вместе с предыдущим промптом.
- [x] Контекст обновляется только после успешной отправки результата. - [x] Контекст обновляется только после успешной отправки результата.
- [x] `/restart` очищает контекст; новое фото начинает новую цепочку. - [x] `/restart` очищает контекст; новое фото начинает новую цепочку.
- [x] `/context` показывает текущий промпт, базовую сцену и последний запрос.
- [x] В подписи результата итоговый промпт и подсказка `/restart`. - [x] В подписи результата итоговый промпт и подсказка `/restart`.
- [x] Тесты БД, enhancer, очереди и подписи. - [x] Тесты БД, enhancer, очереди и подписи.
@ -419,7 +420,7 @@ ON requests(user_id) WHERE status IN ('queued', 'running');
## 24. Команды и меню ## 24. Команды и меню
- [x] При старте бот получает `getMyCommands` и обновляет список при расхождении. - [x] При старте бот получает `getMyCommands` и обновляет список при расхождении.
- [x] `setMyCommands` для `/start`, `/help`, `/balance`, `/invite`, `/status`, `/settings`, `/restart`. - [x] `setMyCommands` для `/start`, `/help`, `/balance`, `/invite`, `/status`, `/context`, `/settings`, `/restart`.
- [x] `setChatMenuButton` включает `MenuButtonCommands` — кнопка меню слева снизу. - [x] `setChatMenuButton` включает `MenuButtonCommands` — кнопка меню слева снизу.
- [x] Ошибки настройки команд не блокируют запуск бота. - [x] Ошибки настройки команд не блокируют запуск бота.
- [x] Тесты сравнения команд и идемпотентной настройки. - [x] Тесты сравнения команд и идемпотентной настройки.

28
tests/test_context.py Normal file
View File

@ -0,0 +1,28 @@
from __future__ import annotations
from bot import format_context
from db import UserContext
def test_format_context_empty() -> None:
text = format_context(None)
assert "Контекст пуст" in text
def test_format_context_shows_prompt_and_reset_hint() -> None:
context = UserContext(
user_id=1,
base_prompt="A red apple on a table",
current_prompt="A blue apple on a table",
last_request_id=42,
last_mode="t2i",
updated_at="2026-10-19T12:00:00+00:00",
)
text = format_context(context)
assert "A blue apple on a table" in text
assert "A red apple on a table" in text
assert "Последний запрос: #42 (t2i)" in text
assert "Обновлён: 19.10.2026 15:00" in text
assert "/restart" in text