- FastAPI приложение для отправки мониторинговых алертов в мессенджеры - Поддержка Telegram и MAX/VK - Интеграция с Grafana, Zabbix, AlertManager - Автоматическое создание тикетов в Jira - Управление группами мессенджеров через API - Декораторы для авторизации и скрытия эндпоинтов - Подробная документация в папке docs/ Автор: Сергей Антропов Сайт: https://devops.org.ru
469 lines
16 KiB
Python
469 lines
16 KiB
Python
"""
|
|
Эндпоинты для отправки простых сообщений в мессенджеры.
|
|
|
|
Автор: Сергей Антропов
|
|
Сайт: https://devops.org.ru
|
|
"""
|
|
import logging
|
|
from typing import Dict, Any, Optional
|
|
from fastapi import APIRouter, HTTPException, Query, Request, Body
|
|
|
|
from app.core.metrics import metrics
|
|
from app.core.groups import groups_config
|
|
from app.core.messenger_factory import MessengerFactory
|
|
from app.core.auth import require_api_key, require_api_key_dependency
|
|
from app.models.message import (
|
|
SendMessageRequest,
|
|
SendPhotoRequest,
|
|
SendVideoRequest,
|
|
SendAudioRequest,
|
|
SendDocumentRequest
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/message", tags=["message"])
|
|
|
|
|
|
async def _send_message_to_group(
|
|
group_name: str,
|
|
thread_id: int,
|
|
messenger: Optional[str],
|
|
send_func,
|
|
*args,
|
|
**kwargs
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Вспомогательная функция для отправки сообщений в группу.
|
|
|
|
Args:
|
|
group_name: Имя группы из конфигурации.
|
|
thread_id: ID треда в группе (0 для основной группы).
|
|
messenger: Тип мессенджера (опционально).
|
|
send_func: Функция отправки сообщения.
|
|
*args: Дополнительные аргументы для функции отправки.
|
|
**kwargs: Дополнительные параметры для функции отправки.
|
|
|
|
Returns:
|
|
Результат отправки сообщения.
|
|
|
|
Raises:
|
|
HTTPException: Если группа не найдена или произошла ошибка отправки.
|
|
"""
|
|
# Получаем конфигурацию группы
|
|
group_config = await groups_config.get_group_config(group_name, messenger)
|
|
if group_config is None:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Группа '{group_name}' не найдена в конфигурации"
|
|
)
|
|
|
|
messenger_type = group_config.get("messenger", "telegram")
|
|
chat_id = group_config.get("chat_id")
|
|
group_thread_id = group_config.get("thread_id", 0)
|
|
|
|
# Используем thread_id из параметра, если указан, иначе из конфигурации группы
|
|
final_thread_id = thread_id if thread_id > 0 else group_thread_id
|
|
|
|
# Создаем клиент мессенджера
|
|
messenger_client = MessengerFactory.create_from_config(group_config)
|
|
|
|
# Проверяем поддержку тредов
|
|
if not messenger_client.supports_threads and final_thread_id > 0:
|
|
logger.warning(f"Мессенджер '{messenger_type}' не поддерживает треды, thread_id будет проигнорирован")
|
|
final_thread_id = None
|
|
elif final_thread_id == 0:
|
|
final_thread_id = None
|
|
|
|
# Вызываем функцию отправки
|
|
success = await send_func(
|
|
messenger_client,
|
|
chat_id=chat_id,
|
|
thread_id=final_thread_id,
|
|
*args,
|
|
**kwargs
|
|
)
|
|
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"Ошибка отправки сообщения в {messenger_type}"
|
|
)
|
|
|
|
return {
|
|
"status": "ok",
|
|
"message": f"Сообщение отправлено в чат {group_name}, тред {thread_id if thread_id > 0 else 0}"
|
|
}
|
|
|
|
|
|
@require_api_key
|
|
@router.post(
|
|
"/text",
|
|
name="Отправить текстовое сообщение",
|
|
response_model=Dict[str, Any],
|
|
dependencies=[require_api_key_dependency],
|
|
responses={
|
|
200: {
|
|
"description": "Сообщение отправлено успешно",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {
|
|
"status": "ok",
|
|
"message": "Сообщение отправлено в чат monitoring, тред 0"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
400: {
|
|
"description": "Ошибка запроса",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Группа 'monitoring' не найдена в конфигурации"}
|
|
}
|
|
}
|
|
},
|
|
401: {
|
|
"description": "Требуется API ключ",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Неверный или отсутствующий API ключ"}
|
|
}
|
|
}
|
|
},
|
|
500: {
|
|
"description": "Ошибка отправки сообщения",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Ошибка отправки сообщения в telegram"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
async def send_text_message(
|
|
request: Request,
|
|
body: SendMessageRequest = Body(...),
|
|
messenger: Optional[str] = Query(None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"])
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Отправить текстовое сообщение в группу мессенджера.
|
|
|
|
Подробная документация: см. docs/api/message.md
|
|
"""
|
|
metrics.increment_api_endpoint("message_text")
|
|
|
|
async def send_func(client, chat_id, thread_id, **kwargs):
|
|
return await client.send_message(
|
|
chat_id=chat_id,
|
|
text=body.text,
|
|
thread_id=thread_id,
|
|
disable_web_page_preview=body.disable_web_page_preview,
|
|
parse_mode=body.parse_mode
|
|
)
|
|
|
|
return await _send_message_to_group(
|
|
group_name=body.tg_group,
|
|
thread_id=body.tg_thread_id,
|
|
messenger=messenger,
|
|
send_func=send_func
|
|
)
|
|
|
|
|
|
@require_api_key
|
|
@router.post(
|
|
"/photo",
|
|
name="Отправить фото",
|
|
response_model=Dict[str, Any],
|
|
dependencies=[require_api_key_dependency],
|
|
responses={
|
|
200: {
|
|
"description": "Фото отправлено успешно",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {
|
|
"status": "ok",
|
|
"message": "Фото отправлено в чат monitoring, тред 0"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
400: {
|
|
"description": "Ошибка запроса",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Группа 'monitoring' не найдена в конфигурации"}
|
|
}
|
|
}
|
|
},
|
|
401: {
|
|
"description": "Требуется API ключ",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Неверный или отсутствующий API ключ"}
|
|
}
|
|
}
|
|
},
|
|
500: {
|
|
"description": "Ошибка отправки фото",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Ошибка отправки фото в telegram"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
async def send_photo(
|
|
request: Request,
|
|
body: SendPhotoRequest = Body(...),
|
|
messenger: Optional[str] = Query(None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"])
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Отправить фото в группу мессенджера.
|
|
|
|
Подробная документация: см. docs/api/message.md
|
|
"""
|
|
metrics.increment_api_endpoint("message_photo")
|
|
|
|
async def send_func(client, chat_id, thread_id, **kwargs):
|
|
return await client.send_photo(
|
|
chat_id=chat_id,
|
|
photo=body.photo,
|
|
caption=body.caption,
|
|
thread_id=thread_id,
|
|
parse_mode=body.parse_mode
|
|
)
|
|
|
|
return await _send_message_to_group(
|
|
group_name=body.tg_group,
|
|
thread_id=body.tg_thread_id,
|
|
messenger=messenger,
|
|
send_func=send_func
|
|
)
|
|
|
|
|
|
@require_api_key
|
|
@router.post(
|
|
"/video",
|
|
name="Отправить видео",
|
|
response_model=Dict[str, Any],
|
|
dependencies=[require_api_key_dependency],
|
|
responses={
|
|
200: {
|
|
"description": "Видео отправлено успешно",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {
|
|
"status": "ok",
|
|
"message": "Видео отправлено в чат monitoring, тред 0"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
400: {
|
|
"description": "Ошибка запроса",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Группа 'monitoring' не найдена в конфигурации"}
|
|
}
|
|
}
|
|
},
|
|
401: {
|
|
"description": "Требуется API ключ",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Неверный или отсутствующий API ключ"}
|
|
}
|
|
}
|
|
},
|
|
500: {
|
|
"description": "Ошибка отправки видео",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Ошибка отправки видео в telegram"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
async def send_video(
|
|
request: Request,
|
|
body: SendVideoRequest = Body(...),
|
|
messenger: Optional[str] = Query(None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"])
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Отправить видео в группу мессенджера.
|
|
|
|
Подробная документация: см. docs/api/message.md
|
|
"""
|
|
metrics.increment_api_endpoint("message_video")
|
|
|
|
async def send_func(client, chat_id, thread_id, **kwargs):
|
|
return await client.send_video(
|
|
chat_id=chat_id,
|
|
video=body.video,
|
|
caption=body.caption,
|
|
thread_id=thread_id,
|
|
parse_mode=body.parse_mode,
|
|
duration=body.duration,
|
|
width=body.width,
|
|
height=body.height
|
|
)
|
|
|
|
return await _send_message_to_group(
|
|
group_name=body.tg_group,
|
|
thread_id=body.tg_thread_id,
|
|
messenger=messenger,
|
|
send_func=send_func
|
|
)
|
|
|
|
|
|
@require_api_key
|
|
@router.post(
|
|
"/audio",
|
|
name="Отправить аудио",
|
|
response_model=Dict[str, Any],
|
|
dependencies=[require_api_key_dependency],
|
|
responses={
|
|
200: {
|
|
"description": "Аудио отправлено успешно",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {
|
|
"status": "ok",
|
|
"message": "Аудио отправлено в чат monitoring, тред 0"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
400: {
|
|
"description": "Ошибка запроса",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Группа 'monitoring' не найдена в конфигурации"}
|
|
}
|
|
}
|
|
},
|
|
401: {
|
|
"description": "Требуется API ключ",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Неверный или отсутствующий API ключ"}
|
|
}
|
|
}
|
|
},
|
|
500: {
|
|
"description": "Ошибка отправки аудио",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Ошибка отправки аудио в telegram"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
async def send_audio(
|
|
request: Request,
|
|
body: SendAudioRequest = Body(...),
|
|
messenger: Optional[str] = Query(None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"])
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Отправить аудио в группу мессенджера.
|
|
|
|
Подробная документация: см. docs/api/message.md
|
|
"""
|
|
metrics.increment_api_endpoint("message_audio")
|
|
|
|
async def send_func(client, chat_id, thread_id, **kwargs):
|
|
return await client.send_audio(
|
|
chat_id=chat_id,
|
|
audio=body.audio,
|
|
caption=body.caption,
|
|
thread_id=thread_id,
|
|
parse_mode=body.parse_mode,
|
|
duration=body.duration,
|
|
performer=body.performer,
|
|
title=body.title
|
|
)
|
|
|
|
return await _send_message_to_group(
|
|
group_name=body.tg_group,
|
|
thread_id=body.tg_thread_id,
|
|
messenger=messenger,
|
|
send_func=send_func
|
|
)
|
|
|
|
|
|
@require_api_key
|
|
@router.post(
|
|
"/document",
|
|
name="Отправить документ",
|
|
response_model=Dict[str, Any],
|
|
dependencies=[require_api_key_dependency],
|
|
responses={
|
|
200: {
|
|
"description": "Документ отправлен успешно",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {
|
|
"status": "ok",
|
|
"message": "Документ отправлен в чат monitoring, тред 0"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
400: {
|
|
"description": "Ошибка запроса",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Группа 'monitoring' не найдена в конфигурации"}
|
|
}
|
|
}
|
|
},
|
|
401: {
|
|
"description": "Требуется API ключ",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Неверный или отсутствующий API ключ"}
|
|
}
|
|
}
|
|
},
|
|
500: {
|
|
"description": "Ошибка отправки документа",
|
|
"content": {
|
|
"application/json": {
|
|
"example": {"detail": "Ошибка отправки документа в telegram"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
async def send_document(
|
|
request: Request,
|
|
body: SendDocumentRequest = Body(...),
|
|
messenger: Optional[str] = Query(None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"])
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Отправить документ в группу мессенджера.
|
|
|
|
Подробная документация: см. docs/api/message.md
|
|
"""
|
|
metrics.increment_api_endpoint("message_document")
|
|
|
|
async def send_func(client, chat_id, thread_id, **kwargs):
|
|
return await client.send_document(
|
|
chat_id=chat_id,
|
|
document=body.document,
|
|
caption=body.caption,
|
|
thread_id=thread_id,
|
|
parse_mode=body.parse_mode,
|
|
filename=body.filename
|
|
)
|
|
|
|
return await _send_message_to_group(
|
|
group_name=body.tg_group,
|
|
thread_id=body.tg_thread_id,
|
|
messenger=messenger,
|
|
send_func=send_func
|
|
)
|