diff --git a/photo_bot/handlers.py b/photo_bot/handlers.py index e38f2b6..4ab4f82 100644 --- a/photo_bot/handlers.py +++ b/photo_bot/handlers.py @@ -109,6 +109,27 @@ async def handle_photo_error(msg: Message, user_id: str, action: str, error_deta await msg.answer(photo_error_handling(), parse_mode="html") +async def check_moderation_status(user_id: str) -> dict: + """Проверяет текущий статус модерации пользователя через API""" + from photo_bot.photo_processor import API_URL, API_KEY + import requests + + status_url = API_URL.replace("/upload/", "/status/") + try: + response = requests.get( + status_url, + params={"user_id": user_id}, + headers={"X-API-Key": API_KEY}, + timeout=15, + verify=False + ) + response.raise_for_status() + return response.json() + except Exception as e: + logger.error(f"Ошибка проверки статуса модерации: {e}") + return {"status": "error", "reason": str(e)} + + # ========================================================= # ОСНОВНОЙ ОБРАБОТЧИК # ========================================================= @@ -149,12 +170,28 @@ async def photo_module_handler(msg: Message): await msg.answer(MENU_TEXT, parse_mode="html") return + # --- ПЕРВАЯ ПРОВЕРКА: если нет фото в pending_photos, проверяем статус модерации --- + if user_id not in pending_photos and not is_attachment: + status = await check_moderation_status(user_id) + if status.get("status") == "ok" and status.get("moderation_status") == "pending": + photo_id = status.get("photo_id", "???") + await msg.answer( + f"⏳ У вас уже есть фото на модерации (ID: {photo_id}).\n\n" + f"Ожидайте решения модератора.\n\n" + f"{EMOJI_DIGITS['0']} — В главное меню", + parse_mode="html" + ) + return + elif status.get("status") != "ok": + # Ошибка проверки — не блокируем, просто логируем + logger.warning(f"Не удалось проверить статус модерации для {user_id}: {status.get('reason')}") + # --- ПОДТВЕРЖДЕНИЕ ОТПРАВКИ (Кнопка 1) --- if not is_attachment and user_id in pending_photos: if cmd == "1": photo_data = pending_photos[user_id] original_path = photo_data['path'] - + await msg.answer(PHOTO_AI_PROCESSING_TEXT, parse_mode="html") try: @@ -162,7 +199,7 @@ async def photo_module_handler(msg: Message): result = await asyncio.to_thread( prepare_ad_photo, original_path, user_login ) - + if result["success"]: verdict = result.get("verdict") if verdict == "published": @@ -175,11 +212,16 @@ async def photo_module_handler(msg: Message): msg_text = result.get("message", "Фото обработано") await msg.answer(f"✅ {msg_text}", parse_mode="html") else: - # Любая ошибка API — системная заглушка - await msg.answer(photo_error_handling(), parse_mode="html") + error_msg = result.get("error", "Неизвестная ошибка") + # Если фото уже на модерации — показываем конкретное сообщение + if "уже есть фото на модерации" in error_msg: + log_menu_stats(user_id, "Photo Bot", "Попытка повторной загрузки на модерации") + await msg.answer(photo_error_already_pending(), parse_mode="html") + else: + await msg.answer(photo_error_handling(), parse_mode="html") except Exception as e: await handle_photo_error(msg, user_id, "Отправка фото через API", str(e)) - + # Убираем за собой if os.path.exists(original_path): os.remove(original_path) @@ -188,7 +230,7 @@ async def photo_module_handler(msg: Message): states.clear_state(user_id) if user_id in pending_photos: del pending_photos[user_id] - + return # --- ОТМЕНА ФОТО (Кнопка 2) --- @@ -220,7 +262,7 @@ async def photo_module_handler(msg: Message): try: downloaded_file = await states.tc_bot.download_file_by_id(file_id) logger.info(f"Скачан файл для {user_login}: type={type(downloaded_file)}, len={len(downloaded_file) if isinstance(downloaded_file, (bytes, str)) else 'N/A'}") - + # Скачиваем в байты напрямую if isinstance(downloaded_file, bytes): raw_bytes = downloaded_file @@ -235,13 +277,13 @@ async def photo_module_handler(msg: Message): raw_bytes = f.read() else: raise Exception(f"Не удалось получить байты файла: {type(downloaded_file)}") - + logger.info(f"Прочитано {len(raw_bytes)} байт для {user_login}") - + # Ищем лицо и получаем координаты coords = await asyncio.to_thread(get_crop_coordinates, raw_bytes) logger.info(f"get_crop_coordinates вернул: {coords} для {user_login}") - + if not coords: await msg.answer( "❌ Лицо не найдено. Пожалуйста, сделайте фото анфас при хорошем освещении.\n\n" @@ -249,7 +291,7 @@ async def photo_module_handler(msg: Message): parse_mode="html" ) return - + # Генерируем превью try: from PIL import Image @@ -257,40 +299,40 @@ async def photo_module_handler(msg: Message): img = Image.open(io.BytesIO(raw_bytes)) if img.mode != 'RGB': img = img.convert('RGB') - + x1, y1, x2, y2 = coords cropped = img.crop((x1, y1, x2, y2)) - + # Ресайз до квадрата side = min(cropped.size[0], cropped.size[1]) left = (cropped.size[0] - side) // 2 top = (cropped.size[1] - side) // 2 cropped = cropped.crop((left, top, left + side, top + side)) - + preview = cropped.resize((400, 400), Image.Resampling.LANCZOS) - + preview_path = f"/tmp/prev_{user_login}.jpg" preview.save(preview_path, format="JPEG", quality=85) - + preview_doc = FSInputFile(preview_path, filename="preview.jpg") await msg.answer_photo(preview_doc, preview_doc) except Exception as lib_err: await asyncio.to_thread(log_and_notify_photo_error, user_id, "Генерация превью", str(lib_err)) raise Exception("Ошибка генерации превью") - + # Сохраняем оригинал и координаты original_path = f"/tmp/orig_{user_login}.jpg" with open(original_path, 'wb') as f: f.write(raw_bytes) - + pending_photos[user_id] = { 'path': original_path, 'coords': coords, 'preview_path': preview_path } - + log_menu_stats(user_id, "Photo Bot", "Загрузка и обработка нового фото") - + await asyncio.sleep(0.5) await msg.answer( photo_preview_ready(),