Initial commit: Message Gateway project
- FastAPI приложение для отправки мониторинговых алертов в мессенджеры - Поддержка Telegram и MAX/VK - Интеграция с Grafana, Zabbix, AlertManager - Автоматическое создание тикетов в Jira - Управление группами мессенджеров через API - Декораторы для авторизации и скрытия эндпоинтов - Подробная документация в папке docs/ Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
255
app/core/messengers/telegram.py
Normal file
255
app/core/messengers/telegram.py
Normal file
@@ -0,0 +1,255 @@
|
||||
"""
|
||||
Адаптер для работы с 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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user