mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-06-20 20:16:38 +00:00
Allow iterating over messages in reverse
This commit is contained in:
parent
ac6dbb8a5c
commit
dbca38c6f5
@ -25,7 +25,7 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
self, entity, limit=None, *, offset_date=None, offset_id=0,
|
self, entity, limit=None, *, offset_date=None, offset_id=0,
|
||||||
max_id=0, min_id=0, add_offset=0, search=None, filter=None,
|
max_id=0, min_id=0, add_offset=0, search=None, filter=None,
|
||||||
from_user=None, batch_size=100, wait_time=None, ids=None,
|
from_user=None, batch_size=100, wait_time=None, ids=None,
|
||||||
_total=None):
|
reverse=False, _total=None):
|
||||||
"""
|
"""
|
||||||
Iterator over the message history for the specified entity.
|
Iterator over the message history for the specified entity.
|
||||||
|
|
||||||
@ -94,6 +94,15 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
will appear in its place, so that zipping the list of IDs
|
will appear in its place, so that zipping the list of IDs
|
||||||
with the messages can match one-to-one.
|
with the messages can match one-to-one.
|
||||||
|
|
||||||
|
reverse (`bool`, optional):
|
||||||
|
If set to ``True``, the messages will be returned in reverse
|
||||||
|
order (from oldest to newest, instead of the default newest
|
||||||
|
to oldest). This also means that the meaning of `offset_id`
|
||||||
|
and `offset_date` parameters is reversed, although they will
|
||||||
|
still be exclusive. `min_id` becomes equivalent to `offset_id`
|
||||||
|
instead of being `max_id` as well since messages are returned
|
||||||
|
in ascending order.
|
||||||
|
|
||||||
_total (`list`, optional):
|
_total (`list`, optional):
|
||||||
A single-item list to pass the total parameter by reference.
|
A single-item list to pass the total parameter by reference.
|
||||||
|
|
||||||
@ -115,6 +124,8 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
if ids:
|
if ids:
|
||||||
if not utils.is_list_like(ids):
|
if not utils.is_list_like(ids):
|
||||||
ids = (ids,)
|
ids = (ids,)
|
||||||
|
if reverse:
|
||||||
|
ids = list(reversed(ids))
|
||||||
async for x in self._iter_ids(entity, ids, total=_total):
|
async for x in self._iter_ids(entity, ids, total=_total):
|
||||||
await yield_(x)
|
await yield_(x)
|
||||||
return
|
return
|
||||||
@ -124,10 +135,26 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
#
|
#
|
||||||
# We can emulate their behaviour locally by setting offset = max_id
|
# We can emulate their behaviour locally by setting offset = max_id
|
||||||
# and simply stopping once we hit a message with ID <= min_id.
|
# and simply stopping once we hit a message with ID <= min_id.
|
||||||
offset_id = max(offset_id, max_id)
|
if reverse:
|
||||||
if offset_id and min_id:
|
offset_id = max(offset_id, min_id)
|
||||||
if offset_id - min_id <= 1:
|
if offset_id and max_id:
|
||||||
return
|
if max_id - offset_id <= 1:
|
||||||
|
print('suck lol')
|
||||||
|
return
|
||||||
|
|
||||||
|
if not max_id:
|
||||||
|
max_id = float('inf')
|
||||||
|
else:
|
||||||
|
offset_id = max(offset_id, max_id)
|
||||||
|
if offset_id and min_id:
|
||||||
|
if offset_id - min_id <= 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
if reverse:
|
||||||
|
if offset_id:
|
||||||
|
offset_id += 1
|
||||||
|
else:
|
||||||
|
offset_id = 1
|
||||||
|
|
||||||
from_id = None
|
from_id = None
|
||||||
limit = float('inf') if limit is None else int(limit)
|
limit = float('inf') if limit is None else int(limit)
|
||||||
@ -142,7 +169,7 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
max_date=offset_date,
|
max_date=offset_date,
|
||||||
offset_id=offset_id,
|
offset_id=offset_id,
|
||||||
add_offset=add_offset,
|
add_offset=add_offset,
|
||||||
limit=1,
|
limit=0, # Search actually returns 0 items if we ask it to
|
||||||
max_id=0,
|
max_id=0,
|
||||||
min_id=0,
|
min_id=0,
|
||||||
hash=0,
|
hash=0,
|
||||||
@ -185,12 +212,24 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
wait_time = 1 if limit > 3000 else 0
|
wait_time = 1 if limit > 3000 else 0
|
||||||
|
|
||||||
have = 0
|
have = 0
|
||||||
last_id = float('inf')
|
last_id = 0 if reverse else float('inf')
|
||||||
batch_size = min(max(batch_size, 1), 100)
|
|
||||||
|
# Telegram has a hard limit of 100.
|
||||||
|
# We don't need to fetch 100 if the limit is less.
|
||||||
|
batch_size = min(max(batch_size, 1), min(100, limit))
|
||||||
|
|
||||||
|
# Use a negative offset to work around reversing the results
|
||||||
|
if reverse:
|
||||||
|
request.add_offset -= batch_size
|
||||||
|
|
||||||
while have < limit:
|
while have < limit:
|
||||||
start = time.time()
|
start = time.time()
|
||||||
# Telegram has a hard limit of 100
|
|
||||||
request.limit = min(limit - have, batch_size)
|
request.limit = min(limit - have, batch_size)
|
||||||
|
if reverse and request.limit != batch_size:
|
||||||
|
# Last batch needs special care if we're on reverse
|
||||||
|
request.add_offset += batch_size - request.limit + 1
|
||||||
|
|
||||||
r = await self(request)
|
r = await self(request)
|
||||||
if _total:
|
if _total:
|
||||||
_total[0] = getattr(r, 'count', len(r.messages))
|
_total[0] = getattr(r, 'count', len(r.messages))
|
||||||
@ -198,19 +237,23 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
entities = {utils.get_peer_id(x): x
|
entities = {utils.get_peer_id(x): x
|
||||||
for x in itertools.chain(r.users, r.chats)}
|
for x in itertools.chain(r.users, r.chats)}
|
||||||
|
|
||||||
for message in r.messages:
|
messages = reversed(r.messages) if reverse else r.messages
|
||||||
if message.id <= min_id:
|
for message in messages:
|
||||||
return
|
|
||||||
|
|
||||||
if (isinstance(message, types.MessageEmpty)
|
if (isinstance(message, types.MessageEmpty)
|
||||||
or message.id >= last_id
|
or from_id and message.from_id != from_id):
|
||||||
or (from_id and message.from_id != from_id)):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if reverse:
|
||||||
|
if message.id <= last_id or message.id >= max_id:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if message.id >= last_id or message.id <= min_id:
|
||||||
|
return
|
||||||
|
|
||||||
# There has been reports that on bad connections this method
|
# There has been reports that on bad connections this method
|
||||||
# was returning duplicated IDs sometimes. Using ``last_id``
|
# was returning duplicated IDs sometimes. Using ``last_id``
|
||||||
# is an attempt to avoid these duplicates, since the message
|
# is an attempt to avoid these duplicates, since the message
|
||||||
# IDs are returned in descending order.
|
# IDs are returned in descending order (or asc if reverse).
|
||||||
last_id = message.id
|
last_id = message.id
|
||||||
|
|
||||||
await yield_(custom.Message(self, message, entities, entity))
|
await yield_(custom.Message(self, message, entities, entity))
|
||||||
@ -219,11 +262,11 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
if len(r.messages) < request.limit:
|
if len(r.messages) < request.limit:
|
||||||
break
|
break
|
||||||
|
|
||||||
request.offset_id = r.messages[-1].id
|
|
||||||
# Find the first message that's not empty (in some rare cases
|
# Find the first message that's not empty (in some rare cases
|
||||||
# it can happen that the last message is :tl:`MessageEmpty`)
|
# it can happen that the last message is :tl:`MessageEmpty`)
|
||||||
last_message = None
|
last_message = None
|
||||||
for m in reversed(r.messages):
|
messages = r.messages if reverse else reversed(r.messages)
|
||||||
|
for m in messages:
|
||||||
if not isinstance(m, types.MessageEmpty):
|
if not isinstance(m, types.MessageEmpty):
|
||||||
last_message = m
|
last_message = m
|
||||||
break
|
break
|
||||||
@ -237,11 +280,16 @@ class MessageMethods(UploadMethods, MessageParseMethods):
|
|||||||
# should just give up since there won't be any new Message.
|
# should just give up since there won't be any new Message.
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
request.offset_id = last_message.id
|
||||||
if isinstance(request, functions.messages.GetHistoryRequest):
|
if isinstance(request, functions.messages.GetHistoryRequest):
|
||||||
request.offset_date = last_message.date
|
request.offset_date = last_message.date
|
||||||
else:
|
else:
|
||||||
request.max_date = last_message.date
|
request.max_date = last_message.date
|
||||||
|
|
||||||
|
if reverse:
|
||||||
|
# We want to skip the one we already have
|
||||||
|
request.add_offset -= 1
|
||||||
|
|
||||||
await asyncio.sleep(
|
await asyncio.sleep(
|
||||||
max(wait_time - (time.time() - start), 0), loop=self._loop)
|
max(wait_time - (time.time() - start), 0), loop=self._loop)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user