- FastAPI приложение для отправки мониторинговых алертов в мессенджеры - Поддержка Telegram и MAX/VK - Интеграция с Grafana, Zabbix, AlertManager - Автоматическое создание тикетов в Jira - Управление группами мессенджеров через API - Декораторы для авторизации и скрытия эндпоинтов - Подробная документация в папке docs/ Автор: Сергей Антропов Сайт: https://devops.org.ru
256 lines
10 KiB
Python
256 lines
10 KiB
Python
"""
|
||
Адаптер для работы с 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
|
||
)
|
||
|