diff --git a/telethon/client/updates.py b/telethon/client/updates.py index 2c2b2836..47b42839 100644 --- a/telethon/client/updates.py +++ b/telethon/client/updates.py @@ -348,7 +348,7 @@ class UpdateMethods: # We also don't really care about their result. # Just send them periodically. try: - self._sender.send(functions.PingRequest(rnd())) + self._sender._keepalive_ping(rnd()) except (ConnectionError, asyncio.CancelledError): return @@ -399,7 +399,7 @@ class UpdateMethods: pass except ValueError: # There is a chance that GetFullChannelRequest and GetDifferenceRequest - # inside the _get_difference() function will end up with + # inside the _get_difference() function will end up with # ValueError("Request was unsuccessful N time(s)") for whatever reasons. pass diff --git a/telethon/network/mtprotosender.py b/telethon/network/mtprotosender.py index aada87e5..2eba38e5 100644 --- a/telethon/network/mtprotosender.py +++ b/telethon/network/mtprotosender.py @@ -16,6 +16,7 @@ from ..errors import ( from ..extensions import BinaryReader from ..tl.core import RpcResult, MessageContainer, GzipPacked from ..tl.functions.auth import LogOutRequest +from ..tl.functions import PingRequest from ..tl.types import ( MsgsAck, Pong, BadServerSalt, BadMsgNotification, FutureSalts, MsgNewDetailedInfo, NewSessionCreated, MsgDetailedInfo, MsgsStateReq, @@ -55,6 +56,7 @@ class MTProtoSender: self._update_callback = update_callback self._auto_reconnect_callback = auto_reconnect_callback self._connect_lock = asyncio.Lock() + self._ping = None # Whether the user has explicitly connected or disconnected. # @@ -217,7 +219,7 @@ class MTProtoSender: self._log.info('Connecting to %s...', self._connection) connected = False - + for attempt in retry_range(self._retries): if not connected: connected = await self._try_connect(attempt) @@ -358,7 +360,7 @@ class MTProtoSender: self._state.reset() retries = self._retries if self._auto_reconnect else 0 - + attempt = 0 ok = True # We're already "retrying" to connect, so we don't want to force retries @@ -419,6 +421,18 @@ class MTProtoSender: self._reconnecting = True asyncio.get_event_loop().create_task(self._reconnect(error)) + def _keepalive_ping(self, rnd_id): + """ + Send a keep-alive ping. If a pong for the last ping was not received + yet, this means we're probably not connected. + """ + # TODO this is ugly, update loop shouldn't worry about this, sender should + if self._ping is None: + self._ping = rnd_id + self.send(PingRequest(rnd_id)) + else: + self._start_reconnect(None) + # Loops async def _send_loop(self): @@ -641,6 +655,9 @@ class MTProtoSender: """ pong = message.obj self._log.debug('Handling pong for message %d', pong.msg_id) + if self._ping == pong.ping_id: + self._ping = None + state = self._pending_state.pop(pong.msg_id, None) if state: state.future.set_result(pong)