Files

351 lines
13 KiB
Python

# Bot utilities package
from dataclasses import dataclass
from typing import Any, Dict, Optional
from aiogram import types
@dataclass
class MessageContent:
"""Класс для хранения информации о контенте сообщения"""
content_type: str
file_id: Optional[str] = None
text: Optional[str] = None
# Словари поддерживаемых параметров для каждого типа сообщения
SUPPORTED_PARAMS = {
"text": {
"parse_mode",
"entities",
"disable_web_page_preview",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
},
"photo": {
"caption",
"parse_mode",
"caption_entities",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
"has_spoiler",
},
"video": {
"duration",
"width",
"height",
"thumbnail",
"caption",
"parse_mode",
"caption_entities",
"supports_streaming",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
"has_spoiler",
},
"animation": {
"duration",
"width",
"height",
"thumbnail",
"caption",
"parse_mode",
"caption_entities",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
"has_spoiler",
},
"document": {
"thumbnail",
"caption",
"parse_mode",
"caption_entities",
"disable_content_type_detection",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
},
"audio": {
"caption",
"parse_mode",
"caption_entities",
"duration",
"performer",
"title",
"thumbnail",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
},
"voice": {
"caption",
"parse_mode",
"caption_entities",
"duration",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
},
"sticker": {
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
},
"video_note": {
"duration",
"length",
"thumbnail",
"disable_notification",
"protect_content",
"reply_markup",
"reply_to_message_id",
"allow_sending_without_reply",
"message_thread_id",
},
}
def filter_kwargs(content_type: str, kwargs: Dict[str, Any]) -> Dict[str, Any]:
"""Фильтрует kwargs, оставляя только поддерживаемые параметры для данного типа сообщения"""
supported = SUPPORTED_PARAMS.get(content_type, set())
return {k: v for k, v in kwargs.items() if k in supported}
def get_message_content(message: types.Message) -> MessageContent:
"""
Определяет тип контента сообщения и возвращает его данные.
Использует match/case вместо длинных if-elif цепочек.
"""
text = (message.text or message.caption or "").strip()
# Проверяем наличие медиа-контента
media_content = None
if message.photo:
media_content = ("photo", message.photo[-1].file_id)
elif message.video:
media_content = ("video", message.video.file_id)
elif message.animation:
media_content = ("animation", message.animation.file_id)
elif message.document:
media_content = ("document", message.document.file_id)
elif message.audio:
media_content = ("audio", message.audio.file_id)
elif message.voice:
media_content = ("voice", message.voice.file_id)
elif message.sticker:
media_content = ("sticker", message.sticker.file_id)
elif message.video_note:
media_content = ("video_note", message.video_note.file_id)
# Используем match/case для определения типа контента
match media_content:
case (content_type, file_id):
return MessageContent(content_type=content_type, file_id=file_id, text=text)
case None:
return MessageContent(content_type="text", text=text)
case _:
return MessageContent(content_type="text", text=text)
async def send_message_by_type(bot, chat_id: int, content: MessageContent, **kwargs) -> None:
"""
Отправляет сообщение указанного типа.
Использует match/case вместо длинных if-elif цепочек.
Автоматически фильтрует неподдерживаемые параметры.
"""
# Фильтруем kwargs для данного типа сообщения
filtered_kwargs = filter_kwargs(content.content_type, kwargs)
match content.content_type:
case "text":
await bot.send_message(chat_id=chat_id, text=content.text, **filtered_kwargs)
case "photo":
await bot.send_photo(
chat_id=chat_id,
photo=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "video":
await bot.send_video(
chat_id=chat_id,
video=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "animation":
await bot.send_animation(
chat_id=chat_id,
animation=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "document":
await bot.send_document(
chat_id=chat_id,
document=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "audio":
await bot.send_audio(
chat_id=chat_id,
audio=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "voice":
await bot.send_voice(
chat_id=chat_id,
voice=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "sticker":
await bot.send_sticker(chat_id=chat_id, sticker=content.file_id, **filtered_kwargs)
case "video_note":
await bot.send_video_note(
chat_id=chat_id, video_note=content.file_id, **filtered_kwargs
)
case _:
# Fallback для неизвестных типов - отправляем как текст
text_kwargs = filter_kwargs("text", kwargs)
await bot.send_message(
chat_id=chat_id, text=content.text or "Unknown content type", **text_kwargs
)
async def send_message_via_queue(
queue_manager, uid: int, content: MessageContent, **kwargs
) -> None:
"""
Отправляет сообщение через очередь в зависимости от типа контента.
Использует match/case вместо длинных if-elif цепочек.
Автоматически фильтрует неподдерживаемые параметры.
"""
# Фильтруем kwargs для данного типа сообщения
filtered_kwargs = filter_kwargs(content.content_type, kwargs)
match content.content_type:
case "text":
await queue_manager.send_message(chat_id=uid, text=content.text, **filtered_kwargs)
case "photo":
await queue_manager.send_photo(
chat_id=uid, photo=content.file_id, caption=content.text or None, **filtered_kwargs
)
case "video":
await queue_manager.send_video(
chat_id=uid, video=content.file_id, caption=content.text or None, **filtered_kwargs
)
case "animation":
await queue_manager.send_animation(
chat_id=uid,
animation=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "document":
await queue_manager.send_document(
chat_id=uid,
document=content.file_id,
caption=content.text or None,
**filtered_kwargs,
)
case "audio":
await queue_manager.send_audio(
chat_id=uid, audio=content.file_id, caption=content.text or None, **filtered_kwargs
)
case "voice":
await queue_manager.send_voice(
chat_id=uid, voice=content.file_id, caption=content.text or None, **filtered_kwargs
)
case "sticker":
await queue_manager.send_sticker(
chat_id=uid, sticker=content.file_id, **filtered_kwargs
)
case "video_note":
await queue_manager.send_video_note(
chat_id=uid, video_note=content.file_id, **filtered_kwargs
)
case _:
# Fallback для неизвестных типов - отправляем как текст
text_kwargs = filter_kwargs("text", kwargs)
await queue_manager.send_message(
chat_id=uid, text=content.text or "Unknown content type", **text_kwargs
)
async def send_direct_message(
bot, chat_id: int, content: MessageContent, extra_text: str = "", **kwargs
) -> None:
"""
Отправляет прямое сообщение с дополнительной обработкой для sticker и video_note.
Для этих типов медиа отправляется отдельное текстовое сообщение, т.к. они не поддерживают caption.
Автоматически фильтрует неподдерживаемые параметры.
""" # noqa: E501
match content.content_type:
case "sticker":
# Отправляем стикер с отфильтрованными параметрами
sticker_kwargs = filter_kwargs("sticker", kwargs)
await bot.send_sticker(chat_id=chat_id, sticker=content.file_id, **sticker_kwargs)
# Если есть текст с подписью, отправляем отдельно
if content.text or extra_text:
text_to_send = (content.text + extra_text) if content.text else extra_text
text_kwargs = filter_kwargs("text", kwargs)
await bot.send_message(chat_id, text_to_send, **text_kwargs)
case "video_note":
# Отправляем видео-заметку с отфильтрованными параметрами
video_note_kwargs = filter_kwargs("video_note", kwargs)
await bot.send_video_note(
chat_id=chat_id, video_note=content.file_id, **video_note_kwargs
)
# Если есть текст с подписью, отправляем отдельно
if content.text or extra_text:
text_to_send = (content.text + extra_text) if content.text else extra_text
text_kwargs = filter_kwargs("text", kwargs)
await bot.send_message(chat_id, text_to_send, **text_kwargs)
case "text":
# Для текста объединяем с extra_text
final_text = (content.text + extra_text) if content.text else extra_text
text_kwargs = filter_kwargs("text", kwargs)
await bot.send_message(chat_id=chat_id, text=final_text, **text_kwargs)
case _:
# Для остальных типов медиа используем caption
final_caption = (content.text + extra_text) if content.text else None
await send_message_by_type(
bot,
chat_id,
MessageContent(content.content_type, content.file_id, final_caption),
**kwargs,
)