Commit 97aa211

benny-dou <60535774+benny-dou@users.noreply.github.com>
2025-09-24 10:41:30
fix(media_group): fix repeated uploading of a media group
1 parent 43059ab
Changed files (2)
src
src/messages/sender.py
@@ -10,10 +10,10 @@ from pyrogram.errors import FloodWait
 from pyrogram.parser.markdown import BLOCKQUOTE_EXPANDABLE_DELIM, BLOCKQUOTE_EXPANDABLE_END_DELIM
 from pyrogram.types import Message, ReplyParameters
 
-from config import CAPTION_LENGTH
+from config import CAPTION_LENGTH, TID
 from messages.preprocess import preprocess_media, warp_media_group
 from messages.progress import modify_progress, telegram_uploading
-from messages.utils import get_reply_to, smart_split, summay_media, warp_comments
+from messages.utils import delete_message, get_reply_to, smart_split, summay_media, warp_comments
 from utils import to_int
 
 
@@ -220,13 +220,49 @@ async def send_single_media(
     return sent_messages
 
 
-async def send_media_group(client: Client, target_chat: int | str, media_group: list, reply_parameters: ReplyParameters) -> list[Message]:
+async def send_media_group(
+    client: Client,
+    target_chat: int | str,
+    media_group: list,
+    reply_parameters: ReplyParameters,
+    *,
+    force_use_temp_chat: bool = False,
+) -> list[Message]:
+    """Send a media group to Telegram.
+
+    If we get FloodWait error, the function will retry after the specified time.
+    But unfortunately, we can not know how many media files have been sent.
+    So the retry mechanism will send the same media group again, casuing the repeated media files.
+    To avoid this, we first send the media group to a temperary chat, and then copy the messages to the target chat.
+    """
+
+    async def send_to_temp(client: Client, media_group: list) -> list[Message]:
+        try:
+            return await client.send_media_group(to_int(TID.TEMP), media=media_group)
+        except FloodWait as e:
+            logger.warning(e)
+            await asyncio.sleep(e.value)  # type: ignore
+            return await send_to_temp(client, media_group)
+        except Exception as e:
+            logger.warning(f"Failed to send_media_group: {e}")
+        return []
+
+    # According to our observation, FloodWait usually occurs when len(media_group) > 6
+    if force_use_temp_chat or len(media_group) > 6:
+        sent = []
+        temp_msgs = await send_to_temp(client, media_group)
+        if len(temp_msgs) > 0 and isinstance(temp_msgs[0], Message) and temp_msgs[0].media_group_id:
+            sent = await client.copy_media_group(to_int(target_chat), from_chat_id=temp_msgs[0].chat.id, message_id=temp_msgs[0].id, reply_parameters=reply_parameters)
+        [await delete_message(m) for m in temp_msgs]
+        return [m for m in sent if isinstance(m, Message)]
+
+    # len(media_group) <= 6, send directly
     try:
-        return await client.send_media_group(target_chat, media=media_group, reply_parameters=reply_parameters)
+        return await client.send_media_group(to_int(target_chat), media=media_group, reply_parameters=reply_parameters)
     except FloodWait as e:
         logger.warning(e)
         await asyncio.sleep(e.value)  # type: ignore
-        return await send_media_group(client, target_chat, media_group, reply_parameters)
+        return await send_media_group(client, target_chat, media_group, reply_parameters, force_use_temp_chat=True)
     except Exception as e:
-        logger.warning(f"send_media_group: {e}")
+        logger.warning(f"Failed to send_media_group: {e}")
     return []
src/config.py
@@ -205,6 +205,7 @@ class COOKIE:  # See: https://github.com/easychen/CookieCloud
 
 class TID:  # see more TID usecase in `src/permission.py`
     ADMIN = os.getenv("TID_ADMIN", "")  # comma separated userid or @username
+    TEMP = os.getenv("TID_TEMP", "me")  # a temperary chat for some tasks
     HISTORY_ADMIN = os.getenv("TID_HISTORY_ADMIN", "")  # comma separated userid (@username is NOT supported!)
     # back up ytdlp audio if the user does not request it
     DAILY_SUMMARY = os.getenv("TID_DAILY_SUMMARY", "{}")  # {"source-chat-id": "target-chat-id"}, e.g. '{"-1001234567890": "-1009876543210"}'