From 5a225d1668390ec37486ccdd6bbaccec26c87bd1 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Tue, 23 Jul 2019 12:44:19 +0200 Subject: [PATCH] Fix a dialog's message could be wrong in rare cases --- telethon/client/dialogs.py | 27 ++++++++++++++++++--------- telethon/tl/custom/dialog.py | 4 ++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/telethon/client/dialogs.py b/telethon/client/dialogs.py index 1156bdd0..58a28d6f 100644 --- a/telethon/client/dialogs.py +++ b/telethon/client/dialogs.py @@ -12,6 +12,17 @@ if typing.TYPE_CHECKING: from .telegramclient import TelegramClient +def _dialog_message_key(peer, message_id): + """ + Get the key to get messages from a dialog. + + We cannot just use the message ID because channels share message IDs, + and the peer ID is required to distinguish between them. But it is not + necessary in small group chats and private chats. + """ + return (peer.channel_id if isinstance(peer, types.PeerChannel) else None), message_id + + class _DialogsIter(RequestIter): async def _init( self, offset_date, offset_id, offset_peer, ignore_pinned, ignore_migrated, folder @@ -48,21 +59,20 @@ class _DialogsIter(RequestIter): messages = {} for m in r.messages: m._finish_init(self.client, entities, None) - messages[m.id] = m + messages[_dialog_message_key(m.to_id, m.id)] = m for d in r.dialogs: # We check the offset date here because Telegram may ignore it + message = messages.get(_dialog_message_key(d.peer, d.top_message)) if self.offset_date: - date = getattr(messages.get( - d.top_message, None), 'date', None) - + date = getattr(message, 'date', None) if not date or date.timestamp() > self.offset_date.timestamp(): continue peer_id = utils.get_peer_id(d.peer) if peer_id not in self.seen: self.seen.add(peer_id) - cd = custom.Dialog(self.client, d, entities, messages) + cd = custom.Dialog(self.client, d, entities, message) if cd.dialog.pts: self.client._channel_pts[cd.id] = cd.dialog.pts @@ -80,11 +90,10 @@ class _DialogsIter(RequestIter): # Why? Because pinned dialogs will mess with the order # in this list. Instead, we find the last dialog which # has a message, and use it as an offset. - last_message = next(( - messages[d.top_message] + last_message = next(filter(None, ( + messages.get(_dialog_message_key(d.peer, d.top_message)) for d in reversed(r.dialogs) - if d.top_message in messages - ), None) + )), None) self.request.exclude_pinned = True self.request.offset_id = last_message.id if last_message else 0 diff --git a/telethon/tl/custom/dialog.py b/telethon/tl/custom/dialog.py index bdc219cc..c434e12e 100644 --- a/telethon/tl/custom/dialog.py +++ b/telethon/tl/custom/dialog.py @@ -69,14 +69,14 @@ class Dialog: is_channel (`bool`): `True` if the `entity` is a :tl:`Channel`. """ - def __init__(self, client, dialog, entities, messages): + def __init__(self, client, dialog, entities, message): # Both entities and messages being dicts {ID: item} self._client = client self.dialog = dialog self.pinned = bool(dialog.pinned) self.folder_id = dialog.folder_id self.archived = dialog.folder_id is not None - self.message = messages.get(dialog.top_message, None) + self.message = message self.date = getattr(self.message, 'date', None) self.entity = entities[utils.get_peer_id(dialog.peer)]