handlers.py: проверка статуса модерации при входе и перед отправкой
This commit is contained in:
+62
-20
@@ -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")
|
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")
|
await msg.answer(MENU_TEXT, parse_mode="html")
|
||||||
return
|
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) ---
|
# --- ПОДТВЕРЖДЕНИЕ ОТПРАВКИ (Кнопка 1) ---
|
||||||
if not is_attachment and user_id in pending_photos:
|
if not is_attachment and user_id in pending_photos:
|
||||||
if cmd == "1":
|
if cmd == "1":
|
||||||
photo_data = pending_photos[user_id]
|
photo_data = pending_photos[user_id]
|
||||||
original_path = photo_data['path']
|
original_path = photo_data['path']
|
||||||
|
|
||||||
await msg.answer(PHOTO_AI_PROCESSING_TEXT, parse_mode="html")
|
await msg.answer(PHOTO_AI_PROCESSING_TEXT, parse_mode="html")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -162,7 +199,7 @@ async def photo_module_handler(msg: Message):
|
|||||||
result = await asyncio.to_thread(
|
result = await asyncio.to_thread(
|
||||||
prepare_ad_photo, original_path, user_login
|
prepare_ad_photo, original_path, user_login
|
||||||
)
|
)
|
||||||
|
|
||||||
if result["success"]:
|
if result["success"]:
|
||||||
verdict = result.get("verdict")
|
verdict = result.get("verdict")
|
||||||
if verdict == "published":
|
if verdict == "published":
|
||||||
@@ -175,11 +212,16 @@ async def photo_module_handler(msg: Message):
|
|||||||
msg_text = result.get("message", "Фото обработано")
|
msg_text = result.get("message", "Фото обработано")
|
||||||
await msg.answer(f"✅ {msg_text}", parse_mode="html")
|
await msg.answer(f"✅ {msg_text}", parse_mode="html")
|
||||||
else:
|
else:
|
||||||
# Любая ошибка API — системная заглушка
|
error_msg = result.get("error", "Неизвестная ошибка")
|
||||||
await msg.answer(photo_error_handling(), parse_mode="html")
|
# Если фото уже на модерации — показываем конкретное сообщение
|
||||||
|
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:
|
except Exception as e:
|
||||||
await handle_photo_error(msg, user_id, "Отправка фото через API", str(e))
|
await handle_photo_error(msg, user_id, "Отправка фото через API", str(e))
|
||||||
|
|
||||||
# Убираем за собой
|
# Убираем за собой
|
||||||
if os.path.exists(original_path):
|
if os.path.exists(original_path):
|
||||||
os.remove(original_path)
|
os.remove(original_path)
|
||||||
@@ -188,7 +230,7 @@ async def photo_module_handler(msg: Message):
|
|||||||
states.clear_state(user_id)
|
states.clear_state(user_id)
|
||||||
if user_id in pending_photos:
|
if user_id in pending_photos:
|
||||||
del pending_photos[user_id]
|
del pending_photos[user_id]
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# --- ОТМЕНА ФОТО (Кнопка 2) ---
|
# --- ОТМЕНА ФОТО (Кнопка 2) ---
|
||||||
@@ -220,7 +262,7 @@ async def photo_module_handler(msg: Message):
|
|||||||
try:
|
try:
|
||||||
downloaded_file = await states.tc_bot.download_file_by_id(file_id)
|
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'}")
|
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):
|
if isinstance(downloaded_file, bytes):
|
||||||
raw_bytes = downloaded_file
|
raw_bytes = downloaded_file
|
||||||
@@ -235,13 +277,13 @@ async def photo_module_handler(msg: Message):
|
|||||||
raw_bytes = f.read()
|
raw_bytes = f.read()
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Не удалось получить байты файла: {type(downloaded_file)}")
|
raise Exception(f"Не удалось получить байты файла: {type(downloaded_file)}")
|
||||||
|
|
||||||
logger.info(f"Прочитано {len(raw_bytes)} байт для {user_login}")
|
logger.info(f"Прочитано {len(raw_bytes)} байт для {user_login}")
|
||||||
|
|
||||||
# Ищем лицо и получаем координаты
|
# Ищем лицо и получаем координаты
|
||||||
coords = await asyncio.to_thread(get_crop_coordinates, raw_bytes)
|
coords = await asyncio.to_thread(get_crop_coordinates, raw_bytes)
|
||||||
logger.info(f"get_crop_coordinates вернул: {coords} для {user_login}")
|
logger.info(f"get_crop_coordinates вернул: {coords} для {user_login}")
|
||||||
|
|
||||||
if not coords:
|
if not coords:
|
||||||
await msg.answer(
|
await msg.answer(
|
||||||
"❌ Лицо не найдено. Пожалуйста, сделайте фото анфас при хорошем освещении.\n\n"
|
"❌ Лицо не найдено. Пожалуйста, сделайте фото анфас при хорошем освещении.\n\n"
|
||||||
@@ -249,7 +291,7 @@ async def photo_module_handler(msg: Message):
|
|||||||
parse_mode="html"
|
parse_mode="html"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Генерируем превью
|
# Генерируем превью
|
||||||
try:
|
try:
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -257,40 +299,40 @@ async def photo_module_handler(msg: Message):
|
|||||||
img = Image.open(io.BytesIO(raw_bytes))
|
img = Image.open(io.BytesIO(raw_bytes))
|
||||||
if img.mode != 'RGB':
|
if img.mode != 'RGB':
|
||||||
img = img.convert('RGB')
|
img = img.convert('RGB')
|
||||||
|
|
||||||
x1, y1, x2, y2 = coords
|
x1, y1, x2, y2 = coords
|
||||||
cropped = img.crop((x1, y1, x2, y2))
|
cropped = img.crop((x1, y1, x2, y2))
|
||||||
|
|
||||||
# Ресайз до квадрата
|
# Ресайз до квадрата
|
||||||
side = min(cropped.size[0], cropped.size[1])
|
side = min(cropped.size[0], cropped.size[1])
|
||||||
left = (cropped.size[0] - side) // 2
|
left = (cropped.size[0] - side) // 2
|
||||||
top = (cropped.size[1] - side) // 2
|
top = (cropped.size[1] - side) // 2
|
||||||
cropped = cropped.crop((left, top, left + side, top + side))
|
cropped = cropped.crop((left, top, left + side, top + side))
|
||||||
|
|
||||||
preview = cropped.resize((400, 400), Image.Resampling.LANCZOS)
|
preview = cropped.resize((400, 400), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
preview_path = f"/tmp/prev_{user_login}.jpg"
|
preview_path = f"/tmp/prev_{user_login}.jpg"
|
||||||
preview.save(preview_path, format="JPEG", quality=85)
|
preview.save(preview_path, format="JPEG", quality=85)
|
||||||
|
|
||||||
preview_doc = FSInputFile(preview_path, filename="preview.jpg")
|
preview_doc = FSInputFile(preview_path, filename="preview.jpg")
|
||||||
await msg.answer_photo(preview_doc, preview_doc)
|
await msg.answer_photo(preview_doc, preview_doc)
|
||||||
except Exception as lib_err:
|
except Exception as lib_err:
|
||||||
await asyncio.to_thread(log_and_notify_photo_error, user_id, "Генерация превью", str(lib_err))
|
await asyncio.to_thread(log_and_notify_photo_error, user_id, "Генерация превью", str(lib_err))
|
||||||
raise Exception("Ошибка генерации превью")
|
raise Exception("Ошибка генерации превью")
|
||||||
|
|
||||||
# Сохраняем оригинал и координаты
|
# Сохраняем оригинал и координаты
|
||||||
original_path = f"/tmp/orig_{user_login}.jpg"
|
original_path = f"/tmp/orig_{user_login}.jpg"
|
||||||
with open(original_path, 'wb') as f:
|
with open(original_path, 'wb') as f:
|
||||||
f.write(raw_bytes)
|
f.write(raw_bytes)
|
||||||
|
|
||||||
pending_photos[user_id] = {
|
pending_photos[user_id] = {
|
||||||
'path': original_path,
|
'path': original_path,
|
||||||
'coords': coords,
|
'coords': coords,
|
||||||
'preview_path': preview_path
|
'preview_path': preview_path
|
||||||
}
|
}
|
||||||
|
|
||||||
log_menu_stats(user_id, "Photo Bot", "Загрузка и обработка нового фото")
|
log_menu_stats(user_id, "Photo Bot", "Загрузка и обработка нового фото")
|
||||||
|
|
||||||
await asyncio.sleep(0.5)
|
await asyncio.sleep(0.5)
|
||||||
await msg.answer(
|
await msg.answer(
|
||||||
photo_preview_ready(),
|
photo_preview_ready(),
|
||||||
|
|||||||
Reference in New Issue
Block a user