# /opt/trueconf_bot/transcription_bot/handlers.py import io import os import asyncio import logging import httpx from ldap3 import Server, Connection, ALL from trueconf import Router, Message import config.config as config from config.config import * from config.config import DEFAULT_EMAIL_FROM, SMTP_SERVER, SMTP_PORT, ALERTS_SUPPORT_EMAILS from utils import states from utils.menu import MENU_TEXT from utils.texts import ( EMOJI_DIGITS, TRANSCRIPTION_MAIN_MENU_TEXT, transcription_error_bad_extension, TRANSCRIPTION_UPLOADING_TEXT, transcription_success_received, system_error_text, UNKNOWN_MAIN_CMD_TEXT, ) # 🔌 Импортируем централизованную функцию сбора статистики из main from utils.stats_logger import log_menu_stats # ========================================================= # СИСТЕМА ОТПРАВКИ ОШИБОК НА ПОЧТУ # ========================================================= global _last_transcription_email_time _last_transcription_email_time = 0.0 def _transcription_log_and_notify_email(action: str, error_details: str): global _last_transcription_email_time current_time = time.time() timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') logger.info(f"📧 [EMAIL ALERT] Attempting to send alert for: {action}") if current_time - _last_transcription_email_time < 10.0: logger.info(f"📧 [EMAIL ALERT] Cooldown active, skipping. Last: {_last_transcription_email_time}") return _last_transcription_email_time = current_time try: import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart msg = MIMEMultipart() default_from = getattr(config, 'DEFAULT_EMAIL_FROM', 'bot@noreply.com') smtp_server = getattr(config, 'SMTP_SERVER', 'localhost') smtp_port = getattr(config, 'SMTP_PORT', 25) to_emails = getattr(config, 'ALERTS_SUPPORT_EMAILS', ['admin@example.com']) logger.info(f"📧 [EMAIL ALERT] From={default_from}, To={to_emails}, SMTP={smtp_server}:{smtp_port}") msg['From'] = str(default_from) msg['To'] = ', '.join(to_emails) if isinstance(to_emails, list) else str(to_emails) msg['Subject'] = f'TrueConf Bot Error: Транскрипция - {action}' body = f'Обнаружена ошибка в транскрипции.\nВремя: {timestamp}\nДействие: {action}\nДетали:\n{error_details}' msg.attach(MIMEText(body, 'plain', 'utf-8')) logger.info(f"📧 [EMAIL ALERT] Connecting to SMTP...") with smtplib.SMTP(smtp_server, int(smtp_port)) as server: logger.info(f"📧 [EMAIL ALERT] Sending message...") server.send_message(msg) logger.info(f"📧 [EMAIL ALERT] SUCCESS!") except Exception as e: logger.error(f"📧 [EMAIL ALERT] FAILED: {e}") import traceback logger.error(f"📧 [EMAIL ALERT] Traceback: {traceback.format_exc()}") pass import time from datetime import datetime logger = logging.getLogger(__name__) router = Router() # ✅ Полный список разрешенных расширений (Аудио, Видео, Проф. форматы) ALLOWED_EXTENSIONS = [ # Аудио '.mp3', '.aac', '.m4a', '.flac', '.ogg', '.oga', '.opus', '.wma', '.aif', '.aiff', '.ac3', '.eac3', '.dts', '.thd', '.au', '.caf', '.wv', '.ape', '.tta', '.wav', # Видео '.mp4', '.m4v', '.mkv', '.mka', '.mk3d', '.avi', '.mov', '.flv', '.webm', '.wmv', '.ts', '.m2ts', '.mpg', '.mpeg', '.vob', # Профессиональные и вещательные '.mxf', '.gxf', '.nucap', # Специфические '.4xm', '.bik', '.smk', '.cak' ] # --- Функция получения Email из Active Directory --- def get_user_email_sync(login: str) -> str: try: from utils.ad_search import search_by_login entries = search_by_login(login, ["mail"]) if entries and 'mail' in entries[0] and entries[0].mail.value: return str(entries[0].mail.value) except Exception as e: logger.error(f"Ошибка поиска email в AD: {e}") _transcription_log_and_notify_email("Поиск email в AD", str(e)) return DEFAULT_REQUESTER @router.message() async def transcription_handler(msg: Message): user_id = msg.from_user.id # Работаем только если включен режим расшифровки if states.get_state(user_id) != "TRANSCRIPTION_MODE": return # Помечаем сообщение как обработанное msg.handled = True user_login = user_id.split('@')[0] if '@' in user_id else user_id msg_text = getattr(msg.content, "text", "").strip() if hasattr(msg.content, "text") else "" is_attachment = hasattr(msg.type, "name") and msg.type.name == "ATTACHMENT" cmd = msg_text.lower() # 🔄 НОРМАЛИЗАЦИЯ КНОПОК ВК-ЭМОДЗИ for raw_num, emoji_num in EMOJI_DIGITS.items(): if cmd == emoji_num: cmd = raw_num break # --- ВЫХОД (Кнопки 0 и 9 приведены к единому стандарту) --- if cmd in ["0", "9", "/start", "меню"]: log_menu_stats(user_id, "Speech-to-Text", "Выход в главное меню") states.clear_state(user_id) await msg.answer(MENU_TEXT, parse_mode="html") return # --- ОБРАБОТКА ФАЙЛА --- if is_attachment: file_name = getattr(msg.content, "file_name", "").lower() file_id = getattr(msg.content, "file_id", None) # Проверка расширения ext = os.path.splitext(file_name)[1] if ext not in ALLOWED_EXTENSIONS: await msg.answer(transcription_error_bad_extension(ext), parse_mode="html") return await msg.answer(TRANSCRIPTION_UPLOADING_TEXT, parse_mode="html") try: # 1. Добываем почту из AD user_email = await asyncio.to_thread(get_user_email_sync, user_login) # 2. Качаем файл (возвращает bytes) downloaded_file = await states.tc_bot.download_file_by_id(file_id) # 3. Отправка на API async with httpx.AsyncClient(timeout=120.0) as client: files = {'file': (file_name, io.BytesIO(downloaded_file), 'application/octet-stream')} payload = { 'email': user_email, 'generate_summary': 'true' } response = await client.post(TRANSCRIPTION_API_URL, files=files, data=payload) if response.status_code == 200: log_menu_stats(user_id, "Speech-to-Text", f"Отправка встречи на расшифровку ({ext})") await msg.answer(transcription_success_received(user_email), parse_mode="html") else: _transcription_log_and_notify_email(f"API транскрипции вернул статус {response.status_code}", str(response.text[:500])) await msg.answer(system_error_text(EMOJI_DIGITS), parse_mode="html") except Exception as e: logger.error(f"Transcription Upload Error: {e}") _transcription_log_and_notify_email("Загрузка/отправка файла на транскрипцию", str(e)) await msg.answer(system_error_text(EMOJI_DIGITS), parse_mode="html") return # --- ЗАГЛУШКА НА НЕИЗВЕСТНЫЙ ТЕКСТ / СТАНДАРТНАЯ ОШИБКА ВВОДА --- await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html")