handlers.py: проверка статуса модерации при входе и перед отправкой

This commit is contained in:
2026-08-14 13:50:46 +07:00
parent 7c74c19de6
commit 4418bcb3a3
+62 -20
View File
@@ -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"⏳ <b>У вас уже есть фото на модерации (ID: {photo_id}).</b>\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(),