"""Service classes for group handlers""" # Standard library imports from typing import Optional, Protocol # Third-party imports from aiogram import types # Local imports from helper_bot.utils.helper_func import send_text_message # Local imports - metrics from helper_bot.utils.metrics import db_query_time, track_errors, track_time from logs.custom_logger import logger from .exceptions import NoReplyToMessageError, UserNotFoundError class DatabaseProtocol(Protocol): """Protocol for database operations""" async def get_user_by_message_id(self, message_id: int) -> Optional[int]: ... async def add_message( self, message_text: str, user_id: int, message_id: int, date: int = None ): ... class AdminReplyService: """Service for admin reply operations""" def __init__(self, db: DatabaseProtocol) -> None: self.db = db @track_time("get_user_id_for_reply", "admin_reply_service") @track_errors("admin_reply_service", "get_user_id_for_reply") @db_query_time("get_user_id_for_reply", "users", "select") async def get_user_id_for_reply(self, message_id: int) -> int: """ Get user ID for reply by message ID. Args: message_id: ID of the message to reply to Returns: User ID for the reply Raises: UserNotFoundError: If user is not found in database """ user_id = await self.db.get_user_by_message_id(message_id) if user_id is None: raise UserNotFoundError(f"User not found for message_id: {message_id}") return user_id @track_time("send_reply_to_user", "admin_reply_service") @track_errors("admin_reply_service", "send_reply_to_user") async def send_reply_to_user( self, chat_id: int, message: types.Message, reply_text: str, markup: types.ReplyKeyboardMarkup, ) -> None: """ Send reply to user. Args: chat_id: User's chat ID message: Original message from admin reply_text: Text to send to user markup: Reply keyboard markup """ await send_text_message(chat_id, message, reply_text, markup) logger.info( f'Ответ админа "{reply_text}" отправлен пользователю с ID: {chat_id} ' f'на сообщение {message.reply_to_message.message_id if message.reply_to_message else "N/A"}' )