""" Адаптер для работы с Telegram через MessengerClient интерфейс. Автор: Сергей Антропов Сайт: https://devops.org.ru """ import logging import io from typing import Optional, Union, Dict, Any from telegram import InlineKeyboardMarkup from app.core.messengers.base import MessengerClient from app.core.telegram_client import TelegramClient from app.core.button_utils import convert_dict_to_telegram_buttons logger = logging.getLogger(__name__) class TelegramMessengerClient(MessengerClient): """Адаптер для Telegram, реализующий интерфейс MessengerClient.""" def __init__(self, bot_token: Optional[str] = None): """ Инициализация клиента Telegram. Args: bot_token: Токен бота Telegram. Если не указан, используется из настроек. """ self._client = TelegramClient(bot_token=bot_token) @property def messenger_type(self) -> str: """Тип мессенджера.""" return "telegram" @property def supports_threads(self) -> bool: """Telegram поддерживает треды.""" return True async def send_message( self, chat_id: Union[str, int], text: str, thread_id: Optional[int] = None, reply_markup: Optional[Dict[str, Any]] = None, disable_web_page_preview: bool = True, parse_mode: str = "HTML", **kwargs ) -> bool: """ Отправить текстовое сообщение в Telegram. Args: chat_id: ID чата или группы (преобразуется в int). text: Текст сообщения. thread_id: ID треда в группе (опционально). reply_markup: Клавиатура с кнопками (опционально). disable_web_page_preview: Отключить превью ссылок. parse_mode: Режим парсинга (HTML, Markdown). **kwargs: Дополнительные параметры (игнорируются). Returns: True если сообщение отправлено успешно, False в противном случае. """ # Преобразуем chat_id в int chat_id_int = int(chat_id) if isinstance(chat_id, str) else chat_id # Преобразуем reply_markup из Dict в InlineKeyboardMarkup, если нужно telegram_reply_markup = None if reply_markup: if isinstance(reply_markup, InlineKeyboardMarkup): telegram_reply_markup = reply_markup elif isinstance(reply_markup, dict): # Преобразуем словарь в InlineKeyboardMarkup telegram_reply_markup = convert_dict_to_telegram_buttons(reply_markup) return await self._client.send_message( chat_id=chat_id_int, text=text, message_thread_id=thread_id, reply_markup=telegram_reply_markup, disable_web_page_preview=disable_web_page_preview, parse_mode=parse_mode ) async def send_photo( self, chat_id: Union[str, int], photo: Union[str, bytes], caption: Optional[str] = None, thread_id: Optional[int] = None, parse_mode: str = "HTML", **kwargs ) -> bool: """ Отправить фото в Telegram. Args: chat_id: ID чата или группы (преобразуется в int). photo: Путь к файлу, URL, bytes или BytesIO объект с фото. caption: Подпись к фото (опционально). thread_id: ID треда в группе (опционально). parse_mode: Режим парсинга (HTML, Markdown). **kwargs: Дополнительные параметры (игнорируются). Returns: True если фото отправлено успешно, False в противном случае. """ chat_id_int = int(chat_id) if isinstance(chat_id, str) else chat_id # Преобразуем bytes в BytesIO, если нужно if isinstance(photo, bytes): photo = io.BytesIO(photo) return await self._client.send_photo( chat_id=chat_id_int, photo=photo, caption=caption, message_thread_id=thread_id, parse_mode=parse_mode ) async def send_video( self, chat_id: Union[str, int], video: Union[str, bytes], caption: Optional[str] = None, thread_id: Optional[int] = None, parse_mode: str = "HTML", duration: Optional[int] = None, width: Optional[int] = None, height: Optional[int] = None, **kwargs ) -> bool: """ Отправить видео в Telegram. Args: chat_id: ID чата или группы (преобразуется в int). video: Путь к файлу, URL, bytes или BytesIO объект с видео. caption: Подпись к видео (опционально). thread_id: ID треда в группе (опционально). parse_mode: Режим парсинга (HTML, Markdown). duration: Длительность видео в секундах (опционально). width: Ширина видео (опционально). height: Высота видео (опционально). **kwargs: Дополнительные параметры (игнорируются). Returns: True если видео отправлено успешно, False в противном случае. """ chat_id_int = int(chat_id) if isinstance(chat_id, str) else chat_id # Преобразуем bytes в BytesIO, если нужно if isinstance(video, bytes): video = io.BytesIO(video) return await self._client.send_video( chat_id=chat_id_int, video=video, caption=caption, message_thread_id=thread_id, parse_mode=parse_mode, duration=duration, width=width, height=height ) async def send_audio( self, chat_id: Union[str, int], audio: Union[str, bytes], caption: Optional[str] = None, thread_id: Optional[int] = None, parse_mode: str = "HTML", duration: Optional[int] = None, performer: Optional[str] = None, title: Optional[str] = None, **kwargs ) -> bool: """ Отправить аудио в Telegram. Args: chat_id: ID чата или группы (преобразуется в int). audio: Путь к файлу, URL, bytes или BytesIO объект с аудио. caption: Подпись к аудио (опционально). thread_id: ID треда в группе (опционально). parse_mode: Режим парсинга (HTML, Markdown). duration: Длительность аудио в секундах (опционально). performer: Исполнитель (опционально). title: Название трека (опционально). **kwargs: Дополнительные параметры (игнорируются). Returns: True если аудио отправлено успешно, False в противном случае. """ chat_id_int = int(chat_id) if isinstance(chat_id, str) else chat_id # Преобразуем bytes в BytesIO, если нужно if isinstance(audio, bytes): audio = io.BytesIO(audio) return await self._client.send_audio( chat_id=chat_id_int, audio=audio, caption=caption, message_thread_id=thread_id, parse_mode=parse_mode, duration=duration, performer=performer, title=title ) async def send_document( self, chat_id: Union[str, int], document: Union[str, bytes], caption: Optional[str] = None, thread_id: Optional[int] = None, parse_mode: str = "HTML", filename: Optional[str] = None, **kwargs ) -> bool: """ Отправить документ в Telegram. Args: chat_id: ID чата или группы (преобразуется в int). document: Путь к файлу, URL, bytes или BytesIO объект с документом. caption: Подпись к документу (опционально). thread_id: ID треда в группе (опционально). parse_mode: Режим парсинга (HTML, Markdown). filename: Имя файла (опционально). **kwargs: Дополнительные параметры (игнорируются). Returns: True если документ отправлен успешно, False в противном случае. """ chat_id_int = int(chat_id) if isinstance(chat_id, str) else chat_id # Преобразуем bytes в BytesIO, если нужно if isinstance(document, bytes): document = io.BytesIO(document) return await self._client.send_document( chat_id=chat_id_int, document=document, caption=caption, message_thread_id=thread_id, parse_mode=parse_mode, filename=filename )