351 lines
13 KiB
Python
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,
|
|
)
|