""" Эндпоинты для отправки простых сообщений в мессенджеры. Автор: Сергей Антропов Сайт: 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 )