diff --git a/photo_bot/__pycache__/handlers.cpython-313.pyc b/photo_bot/__pycache__/handlers.cpython-313.pyc index 1dec9c5..688ef26 100644 Binary files a/photo_bot/__pycache__/handlers.cpython-313.pyc and b/photo_bot/__pycache__/handlers.cpython-313.pyc differ diff --git a/photo_bot/handlers.py b/photo_bot/handlers.py index dd53d4b..e02371a 100644 --- a/photo_bot/handlers.py +++ b/photo_bot/handlers.py @@ -31,6 +31,8 @@ from utils.texts import ( photo_preview_ready, photo_moderation_pending, photo_welcome_with_status, + photo_invalid_cmd, + photo_test_welcome, PHOTO_TEST_WELCOME_TEXT, PHOTO_TEST_PREVIEW_TEXT, PHOTO_TEST_AI_CHECKING_TEXT, @@ -51,6 +53,10 @@ pending_photos = {} # Пользователи, находящиеся в под-режиме «тест проверка фото» test_submode_users = set() +# Пользователи, у которых фото на модерации (вход заблокирован, +# модуль остаётся открыт: в тест-режиме можно отправить фото на тест) +moderation_pending_users = set() + def in_test_submode(user_id: str) -> bool: """Проверяет, находится ли пользователь в под-режиме тест-проверки фото""" if user_id not in test_submode_users: @@ -157,8 +163,9 @@ async def check_moderation_status(user_id: str) -> dict: async def send_dynamic_photo_main_menu(msg: Message, user_id: str): """Вход в раздел фото (паттерн ЛК): модуль сам запрашивает данные и показывает их. - - pending — отдельное сообщение «фото на модерации», модуль НЕ открывается - (кнопок загрузки фото нет); + - pending — сообщение «фото на модерации»; модуль остаётся открыт + (цифры → ошибка неверного выбора, 0 — выход). Тест-режим: + фото можно отправить на проверку в ИИ; - rejected — ОДНО сообщение: меню + причина отказа (кнопки на месте); - иначе/ошибка API — штатное меню входа. Статус проверяется и показывается всегда, включая тестовый режим. @@ -166,13 +173,21 @@ async def send_dynamic_photo_main_menu(msg: Message, user_id: str): # Статус модерации запрашиваем всегда (и в тестовом режиме тоже) status = await check_moderation_status(user_id) if status.get("status") == "ok" and status.get("moderation_status") == "pending": - # Фото на модерации — не вводим в модуль (кнопок загрузки нет) + # Фото на модерации — штатная загрузка заблокирована, + # но модуль остаётся открыт (PHOTO_MODE), чтобы цифры не ушли в главное меню photo_id = status.get("photo_id", "???") log_menu_stats(user_id, "Photo Bot", "Вход заблокирован — фото на модерации") - await msg.answer( - photo_moderation_pending(photo_id), - parse_mode="html" - ) + states.set_state(user_id, "PHOTO_MODE") + moderation_pending_users.add(user_id) + if config.PHOTO_TEST_MODE: + # Тест-режим: можно отправить фото на проверку в ИИ + test_submode_users.add(user_id) + await msg.answer(photo_test_welcome(photo_id), parse_mode="html") + else: + await msg.answer( + photo_moderation_pending(photo_id), + parse_mode="html" + ) return if status.get("status") == "ok" and status.get("moderation_status") == "rejected": log_menu_stats(user_id, "Photo Bot", "Вход — фото отклонено, показана причина") @@ -219,6 +234,7 @@ async def photo_module_handler(msg: Message): log_menu_stats(user_id, "Photo Bot", "Выход в главное меню") states.clear_state(user_id) test_submode_users.discard(user_id) + moderation_pending_users.discard(user_id) if user_id in pending_photos: photo_data = pending_photos[user_id] if os.path.exists(photo_data['path']): @@ -230,6 +246,35 @@ async def photo_module_handler(msg: Message): await msg.answer(MENU_TEXT, parse_mode="html") return + # --- СОСТОЯНИЕ «ФОТО НА МОДЕРАЦИИ»: активного фото нет --- + if user_id in moderation_pending_users and user_id not in pending_photos: + if not is_attachment: + status = await check_moderation_status(user_id) + still_pending = ( + status.get("status") == "ok" + and status.get("moderation_status") == "pending" + ) + if still_pending: + photo_id = status.get("photo_id", "???") + if config.PHOTO_TEST_MODE and in_test_submode(user_id): + # тест-режим: повторяем инфо (можно отправить фото на тест) + await msg.answer(photo_test_welcome(photo_id), parse_mode="html") + else: + # стандартная ошибка: кнопок, кроме 0, нет + await msg.answer(photo_invalid_cmd(), parse_mode="html") + return + # Статус изменился (одобрено/отклонено) — разблокируем + moderation_pending_users.discard(user_id) + if config.PHOTO_TEST_MODE: + await msg.answer(PHOTO_TEST_WELCOME_TEXT, parse_mode="html") + else: + await msg.answer( + photo_welcome_with_status(status), parse_mode="html" + ) + return + # is_attachment — пропускаем: фото обрабатывается штатно ниже + # (тест-под-режим: превью + отправка в ИИ) + # --- ПРОВЕРКА СТАТУСА: если нет фото в pending_photos, проверяем статус модерации --- # (в тестовом режиме статусы тоже показываем — поведение как в обычном режиме) if user_id not in pending_photos and not is_attachment: diff --git a/utils/__pycache__/texts.cpython-313.pyc b/utils/__pycache__/texts.cpython-313.pyc index 5e5c893..82b9084 100644 Binary files a/utils/__pycache__/texts.cpython-313.pyc and b/utils/__pycache__/texts.cpython-313.pyc differ diff --git a/utils/texts.py b/utils/texts.py index cdf5757..91c9a3e 100644 --- a/utils/texts.py +++ b/utils/texts.py @@ -225,6 +225,13 @@ def _photo_unknown(emojis: dict) -> str: f"{emojis['0']} — В главное меню" ) +def _photo_invalid_cmd(emojis: dict) -> str: + return ( + f"⚠️ Неверный выбор пункта меню.\n" + f"Пожалуйста, используйте кнопки из сообщения выше.\n\n" + f"{emojis['0']} — В главное меню" + ) + def _photo_processing_face(emojis: dict) -> str: return "⏳ Ищу лицо на фото, пожалуйста, подождите..." @@ -269,13 +276,20 @@ def _photo_status_info(status: dict = None, emojis: dict = None) -> str | None: ) return None -def _photo_test_welcome(emojis: dict) -> str: - return ( +def _photo_test_welcome(emojis: dict, photo_id: str = None) -> str: + """Тест-приветствие. photo_id — если у пользователя ещё есть фото + на модерации (тестовая проверка его не затрагивает).""" + text = ( f"🧪 Тестовый режим фото-проверки\n\n" f"📸 Пришлите фотографию — она уйдёт напрямую в ИИ.\n" - f"Бот покажет вердикт ИИ. Фото никуда не публикуется.\n\n" - f"{emojis['0']} — В главное меню" + f"Бот покажет вердикт ИИ. Фото никуда не публикуется.\n" ) + if photo_id: + text += ( + f"\n⏳ У вас уже есть фото на модерации (№ {photo_id}).\n" + f"Ожидайте решения модератора. Тестовая проверка его не затрагивает.\n" + ) + return text + f"\n\n{emojis['0']} — В главное меню" def _photo_test_preview(emojis: dict) -> str: return ( @@ -367,6 +381,9 @@ PHOTO_MAIN_MENU_TEXT = _photo_welcome(EMOJI_DIGITS) PHOTO_TEST_ENTRY_TEXT = _photo_welcome(EMOJI_DIGITS, show_test=True) PHOTO_UNKNOWN_CMD_TEXT = _photo_unknown(EMOJI_DIGITS) +def photo_invalid_cmd() -> str: + return _photo_invalid_cmd(EMOJI_DIGITS) + def photo_error_handling() -> str: return system_error_text(EMOJI_DIGITS, show_back=False) @@ -403,6 +420,10 @@ def photo_welcome_with_status(status: dict = None, show_test: bool = False) -> s return _photo_welcome(EMOJI_DIGITS, show_test=show_test, info=info) PHOTO_TEST_WELCOME_TEXT = _photo_test_welcome(EMOJI_DIGITS) + +def photo_test_welcome(photo_id: str = None) -> str: + """Тест-приветствие; photo_id — если у пользователя ещё фото на модерации.""" + return _photo_test_welcome(EMOJI_DIGITS, photo_id) PHOTO_TEST_PREVIEW_TEXT = _photo_test_preview(EMOJI_DIGITS) PHOTO_TEST_AI_CHECKING_TEXT = _photo_test_ai_checking(EMOJI_DIGITS) PHOTO_TEST_RECEIVING_TEXT = _photo_test_receiving(EMOJI_DIGITS)