photo.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import asyncio
  2. from asyncio import CancelledError
  3. from typing import Dict, List
  4. from aiogram import Bot, F, Router
  5. from aiogram.exceptions import TelegramBadRequest, TelegramForbiddenError
  6. from aiogram.types import InputMediaPhoto, InputMediaVideo, Message
  7. from simpleforward.bot.message_manager import MessageManager
  8. from simpleforward.config import models
  9. class PhotoRouter(Router):
  10. def __init__(self,
  11. forwarding_config: models.Forwarding,
  12. message_manager: MessageManager):
  13. super().__init__()
  14. self.config = forwarding_config
  15. self.message_manager = message_manager
  16. self.media_groups: Dict[int, dict] = {}
  17. self.media_groups_tasks: Dict[int, asyncio.Task] = {}
  18. self.media_groups_lock = asyncio.Lock()
  19. self._register_handlers()
  20. def _register_handlers(self):
  21. @self.message(F.photo | F.video)
  22. async def on_photo(message: Message, bot: Bot):
  23. if "photo" not in self.config.types and "video" not in self.config.types:
  24. return
  25. def can_send_media(msgs: List[Message]):
  26. photos = len([msg for msg in msgs if msg.photo])
  27. videos = len([msg for msg in msgs if msg.video])
  28. if (photos and "photo" in self.config.types) or (videos and "video" in self.config.types):
  29. return True
  30. else:
  31. return False
  32. def get_media(msg: Message):
  33. caption = self.config.message_template.format(text=msg.caption) if msg.caption else None
  34. parse_mode = "HTML" if msg.caption else None
  35. if msg.photo and "photo" in self.config.types:
  36. return InputMediaPhoto(
  37. media=msg.photo[-1].file_id,
  38. caption=caption,
  39. parse_mode=parse_mode
  40. )
  41. elif msg.video and "video" in self.config.types:
  42. return InputMediaVideo(
  43. media=msg.video.file_id,
  44. caption=caption,
  45. parse_mode=parse_mode
  46. )
  47. async def process_messages(messages: list[Message]):
  48. if not messages: return
  49. reply_to_message_id = messages[0].message_id
  50. try:
  51. if can_send_media(messages):
  52. if len(messages) > 1:
  53. group_message_id = (await bot.send_media_group(
  54. self.config.target_chat_id,
  55. [
  56. get_media(msg)
  57. for msg in messages
  58. ]
  59. ))[0].message_id
  60. elif len(messages) == 1:
  61. msg = messages[0]
  62. func = bot.send_photo if msg.photo else bot.send_video
  63. file_id = msg.photo[-1].file_id if msg.photo else msg.video.file_id
  64. group_message_id = (await func(
  65. self.config.target_chat_id,
  66. file_id,
  67. caption=self.config.message_template.format(text=msg.caption or ""),
  68. parse_mode="HTML"
  69. )).message_id
  70. self.message_manager.add(reply_to_message_id, group_message_id, message.chat.id)
  71. await message.answer("✅ Сообщение успешно отправлено!")
  72. except (TelegramBadRequest, TelegramForbiddenError) as e:
  73. await message.answer(f'❌ Не удалось отправить сообщение: "{e}"')
  74. media_group_id = message.media_group_id
  75. async def await_media_group():
  76. try:
  77. await asyncio.sleep(2)
  78. async with self.media_groups_lock:
  79. messages = self.media_groups.pop(media_group_id, [])
  80. self.media_groups_tasks.pop(media_group_id, None)
  81. await process_messages(messages)
  82. except CancelledError:
  83. pass
  84. if media_group_id:
  85. self.media_groups.setdefault(media_group_id, []).append(message)
  86. task = self.media_groups_tasks.get(media_group_id)
  87. if task: task.cancel()
  88. self.media_groups_tasks[media_group_id] = asyncio.create_task(await_media_group())
  89. return
  90. await process_messages([message])