Remove the aggressive hack from get_participants

This commit is contained in:
Lonami Exo 2021-09-17 20:13:05 +02:00
parent be3ed894c6
commit 1036c3cb52
3 changed files with 46 additions and 76 deletions

View File

@ -218,6 +218,13 @@ your handlers much more easily.
// TODO provide standalone alternative for this? // TODO provide standalone alternative for this?
The aggressive parameter hack has been removed
----------------------------------------------
The "aggressive" hack in ``get_participants`` (and ``iter_participants``) is now gone.
It was not reliable, and was a cause of flood wait errors.
The TelegramClient is no longer made out of mixins The TelegramClient is no longer made out of mixins
-------------------------------------------------- --------------------------------------------------

View File

@ -94,7 +94,7 @@ class _ChatAction:
class _ParticipantsIter(requestiter.RequestIter): class _ParticipantsIter(requestiter.RequestIter):
async def _init(self, entity, filter, search, aggressive): async def _init(self, entity, filter, search):
if isinstance(filter, type): if isinstance(filter, type):
if filter in (_tl.ChannelParticipantsBanned, if filter in (_tl.ChannelParticipantsBanned,
_tl.ChannelParticipantsKicked, _tl.ChannelParticipantsKicked,
@ -118,9 +118,6 @@ class _ParticipantsIter(requestiter.RequestIter):
else: else:
self.filter_entity = lambda ent: True self.filter_entity = lambda ent: True
# Only used for channels, but we should always set the attribute
self.requests = []
if ty == helpers._EntityType.CHANNEL: if ty == helpers._EntityType.CHANNEL:
if self.limit <= 0: if self.limit <= 0:
# May not have access to the channel, but getFull can get the .total. # May not have access to the channel, but getFull can get the .total.
@ -130,22 +127,13 @@ class _ParticipantsIter(requestiter.RequestIter):
raise StopAsyncIteration raise StopAsyncIteration
self.seen = set() self.seen = set()
if aggressive and not filter: self.request = _tl.fn.channels.GetParticipants(
self.requests.extend(_tl.fn.channels.GetParticipants( channel=entity,
channel=entity, filter=filter or _tl.ChannelParticipantsSearch(search),
filter=_tl.ChannelParticipantsSearch(x), offset=0,
offset=0, limit=_MAX_PARTICIPANTS_CHUNK_SIZE,
limit=_MAX_PARTICIPANTS_CHUNK_SIZE, hash=0
hash=0 )
) for x in (search or string.ascii_lowercase))
else:
self.requests.append(_tl.fn.channels.GetParticipants(
channel=entity,
filter=filter or _tl.ChannelParticipantsSearch(search),
offset=0,
limit=_MAX_PARTICIPANTS_CHUNK_SIZE,
hash=0
))
elif ty == helpers._EntityType.CHAT: elif ty == helpers._EntityType.CHAT:
full = await self.client( full = await self.client(
@ -184,24 +172,21 @@ class _ParticipantsIter(requestiter.RequestIter):
return True return True
async def _load_next_chunk(self): async def _load_next_chunk(self):
if not self.requests:
return True
# Only care about the limit for the first request # Only care about the limit for the first request
# (small amount of people, won't be aggressive). # (small amount of people).
# #
# Most people won't care about getting exactly 12,345 # Most people won't care about getting exactly 12,345
# members so it doesn't really matter not to be 100% # members so it doesn't really matter not to be 100%
# precise with being out of the offset/limit here. # precise with being out of the offset/limit here.
self.requests[0].limit = min( self.request.limit = min(
self.limit - self.requests[0].offset, _MAX_PARTICIPANTS_CHUNK_SIZE) self.limit - self.request.offset, _MAX_PARTICIPANTS_CHUNK_SIZE)
if self.requests[0].offset > self.limit: if self.request.offset > self.limit:
return True return True
if self.total is None: if self.total is None:
f = self.requests[0].filter f = self.request.filter
if len(self.requests) > 1 or ( if (
not isinstance(f, _tl.ChannelParticipantsRecent) not isinstance(f, _tl.ChannelParticipantsRecent)
and (not isinstance(f, _tl.ChannelParticipantsSearch) or f.q) and (not isinstance(f, _tl.ChannelParticipantsSearch) or f.q)
): ):
@ -209,42 +194,36 @@ class _ParticipantsIter(requestiter.RequestIter):
# if there's a filter which would reduce the real total number. # if there's a filter which would reduce the real total number.
# getParticipants is cheaper than getFull. # getParticipants is cheaper than getFull.
self.total = (await self.client(_tl.fn.channels.GetParticipants( self.total = (await self.client(_tl.fn.channels.GetParticipants(
channel=self.requests[0].channel, channel=self.request.channel,
filter=_tl.ChannelParticipantsRecent(), filter=_tl.ChannelParticipantsRecent(),
offset=0, offset=0,
limit=1, limit=1,
hash=0 hash=0
))).count ))).count
results = await self.client(self.requests) participants = await self.client(self.request)
for i in reversed(range(len(self.requests))): if self.total is None:
participants = results[i] # Will only get here if there was one request with a filter that matched all users.
if self.total is None: self.total = participants.count
# Will only get here if there was one request with a filter that matched all users.
self.total = participants.count
if not participants.users:
self.requests.pop(i)
continue
self.requests[i].offset += len(participants.participants) self.request.offset += len(participants.participants)
users = {user.id: user for user in participants.users} users = {user.id: user for user in participants.users}
for participant in participants.participants: for participant in participants.participants:
if isinstance(participant, _tl.ChannelParticipantBanned):
if isinstance(participant, _tl.ChannelParticipantBanned): if not isinstance(participant.peer, _tl.PeerUser):
if not isinstance(participant.peer, _tl.PeerUser): # May have the entire channel banned. See #3105.
# May have the entire channel banned. See #3105.
continue
user_id = participant.peer.user_id
else:
user_id = participant.user_id
user = users[user_id]
if not self.filter_entity(user) or user.id in self.seen:
continue continue
self.seen.add(user_id) user_id = participant.peer.user_id
user = users[user_id] else:
user.participant = participant user_id = participant.user_id
self.buffer.append(user)
user = users[user_id]
if not self.filter_entity(user) or user.id in self.seen:
continue
self.seen.add(user_id)
user = users[user_id]
user.participant = participant
self.buffer.append(user)
class _AdminLogIter(requestiter.RequestIter): class _AdminLogIter(requestiter.RequestIter):
@ -407,15 +386,13 @@ def get_participants(
limit: float = None, limit: float = None,
*, *,
search: str = '', search: str = '',
filter: '_tl.TypeChannelParticipantsFilter' = None, filter: '_tl.TypeChannelParticipantsFilter' = None) -> _ParticipantsIter:
aggressive: bool = False) -> _ParticipantsIter:
return _ParticipantsIter( return _ParticipantsIter(
self, self,
limit, limit,
entity=entity, entity=entity,
filter=filter, filter=filter,
search=search, search=search
aggressive=aggressive
) )

View File

@ -722,8 +722,7 @@ class TelegramClient:
limit: float = (), limit: float = (),
*, *,
search: str = '', search: str = '',
filter: '_tl.TypeChannelParticipantsFilter' = None, filter: '_tl.TypeChannelParticipantsFilter' = None) -> chats._ParticipantsIter:
aggressive: bool = False) -> chats._ParticipantsIter:
""" """
Iterator over the participants belonging to the specified chat. Iterator over the participants belonging to the specified chat.
@ -739,9 +738,6 @@ class TelegramClient:
search (`str`, optional): search (`str`, optional):
Look for participants with this string in name/username. Look for participants with this string in name/username.
If ``aggressive is True``, the symbols from this string will
be used.
filter (:tl:`ChannelParticipantsFilter`, optional): filter (:tl:`ChannelParticipantsFilter`, optional):
The filter to be used, if you want e.g. only admins The filter to be used, if you want e.g. only admins
Note that you might not have permissions for some filter. Note that you might not have permissions for some filter.
@ -753,16 +749,6 @@ class TelegramClient:
*restricted* users. If you want *banned* users you should *restricted* users. If you want *banned* users you should
use :tl:`ChannelParticipantsKicked` instead. use :tl:`ChannelParticipantsKicked` instead.
aggressive (`bool`, optional):
Aggressively looks for all participants in the chat.
This is useful for channels since 20 July 2018,
Telegram added a server-side limit where only the
first 200 members can be retrieved. With this flag
set, more than 200 will be often be retrieved.
This has no effect if a ``filter`` is given.
Yields Yields
The :tl:`User` objects returned by :tl:`GetParticipants` The :tl:`User` objects returned by :tl:`GetParticipants`
with an additional ``.participant`` attribute which is the with an additional ``.participant`` attribute which is the