Commit 97aa211
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"}'