diff --git a/telethon/client/telegrambaseclient.py b/telethon/client/telegrambaseclient.py index 8f9854a5..2d24a5d7 100644 --- a/telethon/client/telegrambaseclient.py +++ b/telethon/client/telegrambaseclient.py @@ -226,6 +226,9 @@ class TelegramBaseClient(abc.ABC): auto_reconnect_callback=self._handle_auto_reconnect ) + # Remember flood-waited requests to avoid making them again + self._flood_waited_requests = {} + # Cache ``{dc_id: (n, MTProtoSender)}`` for all borrowed senders, # being ``n`` the amount of borrows a given sender has; once ``n`` # reaches ``0`` it should be disconnected and removed. diff --git a/telethon/client/users.py b/telethon/client/users.py index 413657b2..f72eac1b 100644 --- a/telethon/client/users.py +++ b/telethon/client/users.py @@ -18,6 +18,20 @@ class UserMethods(TelegramBaseClient): raise _NOT_A_REQUEST await r.resolve(self, utils) + # Avoid making the request if it's already in a flood wait + if r.CONSTRUCTOR_ID in self._flood_waited_requests: + due = self._flood_waited_requests[r.CONSTRUCTOR_ID] + diff = round(due - time.time()) + if diff <= 3: # Flood waits below 3 seconds are "ignored" + self._flood_waited_requests.pop(r.CONSTRUCTOR_ID, None) + elif diff <= self.flood_sleep_threshold: + __log__.info('Sleeping early for %ds on flood wait', diff) + await asyncio.sleep(diff, loop=self._loop) + self._flood_waited_requests.pop(r.CONSTRUCTOR_ID, None) + else: + raise errors.FloodWaitError(capture=diff) + + request_index = 0 self._last_request = time.time() for _ in range(self._request_retries): try: @@ -28,6 +42,7 @@ class UserMethods(TelegramBaseClient): result = await f self.session.process_entities(result) results.append(result) + request_index += 1 return results else: result = await future @@ -37,6 +52,12 @@ class UserMethods(TelegramBaseClient): __log__.warning('Telegram is having internal issues %s: %s', e.__class__.__name__, e) except (errors.FloodWaitError, errors.FloodTestPhoneWaitError) as e: + if utils.is_list_like(request): + request = request[request_index] + + self._flood_waited_requests\ + [request.CONSTRUCTOR_ID] = time.time() + e.seconds + if e.seconds <= self.flood_sleep_threshold: __log__.info('Sleeping for %ds on flood wait', e.seconds) await asyncio.sleep(e.seconds, loop=self._loop) diff --git a/telethon/tl/tlobject.py b/telethon/tl/tlobject.py index cc0d9ab3..16bf4221 100644 --- a/telethon/tl/tlobject.py +++ b/telethon/tl/tlobject.py @@ -3,6 +3,9 @@ from datetime import datetime, date, timedelta class TLObject: + CONSTRUCTOR_ID = None + SUBCLASS_OF_ID = None + @staticmethod def pretty_format(obj, indent=None): """