187 lines
7.6 KiB
Python
187 lines
7.6 KiB
Python
# /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
|
|
|
|
from config.config import *
|
|
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")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|