from datetime import datetime from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.models import ( BotNotification, BotNotificationSubscription, BotUser, ) from app.settings_cache import SettingsCache try: from app.models.models import User except ImportError: User = None async def _get_max_user_id_by_role(db: AsyncSession, role: str) -> int | None: if User is None: return None result = await db.execute( select(User.max_user_id).where( User.role == role, User.is_active == True, User.max_user_id.isnot(None) ).limit(1) ) return result.scalar_one_or_none() async def _get_user_max_ids_by_role(db: AsyncSession, role: str) -> list[int]: if User is None: return [] result = await db.execute( select(User.max_user_id).where( User.role == role, User.is_active == True, User.max_user_id.isnot(None) ) ) return [row[0] for row in result.fetchall() if row[0]] async def _log_notification(db: AsyncSession, ntype: str, recipient_type: str, recipient_max_user_id: int | None, title: str, body: str, related_type: str = "", related_id: int | None = None): n = BotNotification( type=ntype, recipient_type=recipient_type, recipient_max_user_id=recipient_max_user_id, title=title, body=body, related_type=related_type, related_id=related_id ) db.add(n) await db.flush() return n async def notify_operator(db: AsyncSession, max_api, text: str, title: str = "", related_type: str = "", related_id: int | None = None): max_ids = await _get_user_max_ids_by_role(db, "owner") sent_count = 0 for mid in max_ids: try: await max_api.send_message(user_id=mid, text=text, format="markdown") sent_count += 1 except Exception: pass await _log_notification( db, "operator_alert", "operator", max_ids[0] if max_ids else None, title or "Уведомление оператору", text, related_type, related_id ) return sent_count async def notify_level2(db: AsyncSession, max_api, text: str, title: str = "", related_type: str = "", related_id: int | None = None): max_ids = await _get_user_max_ids_by_role(db, "engineer") sent_count = 0 for mid in max_ids: try: await max_api.send_message(user_id=mid, text=text, format="markdown") sent_count += 1 except Exception: pass await _log_notification( db, "level2_alert", "level2", max_ids[0] if max_ids else None, title or "Уведомление инженеру", text, related_type, related_id ) return sent_count async def notify_client_status_change(db: AsyncSession, max_api, bot_user: BotUser, conv_id: int, new_status: str): text = f"Статус вашего обращения №{conv_id} изменён на «{new_status}»." try: await max_api.send_message(user_id=bot_user.max_user_id, text=text, format="markdown") except Exception: pass await _log_notification( db, "status_change", "client", bot_user.max_user_id, "Статус обращения", text, "conversation", conv_id ) async def notify_ticket_created(db: AsyncSession, ticket, max_api): ticket_text = ( f"🆕 **Новый тикет {ticket.ticket_number}**\n" f"Приоритет: {ticket.priority}\n" f"Описание: {ticket.description[:200]}\n" f"Обращение №{ticket.id}" ) await notify_level2( db, max_api, ticket_text, title=f"Тикет {ticket.ticket_number}", related_type="ticket", related_id=ticket.id ) async def notify_handoff_escalation(db: AsyncSession, max_api, bot_user: BotUser, conv_id: int, text: str = ""): msg = ( f"🔴 **Эскалация оператору**\n" f"Пользователь: {bot_user.first_name or '—'} (ID {bot_user.max_user_id})\n" f"Обращение №{conv_id}\n" f"Текст: {text[:200] if text else '—'}" ) await notify_operator( db, max_api, msg, title="Эскалация", related_type="conversation", related_id=conv_id ) async def get_subscribed_users(db: AsyncSession, notify_type: str = "broadcast") -> list[BotUser]: col_map = { "broadcast": BotNotificationSubscription.notify_on_broadcast, "status_change": BotNotificationSubscription.notify_on_status_change, "reply": BotNotificationSubscription.notify_on_reply, } col = col_map.get(notify_type) if col is None: return [] result = await db.execute( select(BotUser).join( BotNotificationSubscription, BotNotificationSubscription.user_id == BotUser.id ).where(col == True) ) return list(result.scalars().all())