mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-11-04 01:47:27 +03:00 
			
		
		
		
	Separate chat requests from the TelegramClient
This commit is contained in:
		
							parent
							
								
									1e91e5a83c
								
							
						
					
					
						commit
						ad29f2f5b7
					
				
							
								
								
									
										184
									
								
								telethon/client/chats.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								telethon/client/chats.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,184 @@
 | 
			
		|||
from collections import UserList
 | 
			
		||||
 | 
			
		||||
from .users import UserMethods
 | 
			
		||||
from .. import utils
 | 
			
		||||
from ..tl import types, functions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ChatMethods(UserMethods):
 | 
			
		||||
 | 
			
		||||
    # region Public methods
 | 
			
		||||
 | 
			
		||||
    async def iter_participants(
 | 
			
		||||
            self, entity, limit=None, search='',
 | 
			
		||||
            filter=None, aggressive=False, _total=None):
 | 
			
		||||
        """
 | 
			
		||||
        Iterator over the participants belonging to the specified chat.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            entity (`entity`):
 | 
			
		||||
                The entity from which to retrieve the participants list.
 | 
			
		||||
 | 
			
		||||
            limit (`int`):
 | 
			
		||||
                Limits amount of participants fetched.
 | 
			
		||||
 | 
			
		||||
            search (`str`, optional):
 | 
			
		||||
                Look for participants with this string in name/username.
 | 
			
		||||
 | 
			
		||||
            filter (:tl:`ChannelParticipantsFilter`, optional):
 | 
			
		||||
                The filter to be used, if you want e.g. only admins
 | 
			
		||||
                Note that you might not have permissions for some filter.
 | 
			
		||||
                This has no effect for normal chats or users.
 | 
			
		||||
 | 
			
		||||
            aggressive (`bool`, optional):
 | 
			
		||||
                Aggressively looks for all participants in the chat in
 | 
			
		||||
                order to get more than 10,000 members (a hard limit
 | 
			
		||||
                imposed by Telegram). Note that this might take a long
 | 
			
		||||
                time (over 5 minutes), but is able to return over 90,000
 | 
			
		||||
                participants on groups with 100,000 members.
 | 
			
		||||
 | 
			
		||||
                This has no effect for groups or channels with less than
 | 
			
		||||
                10,000 members, or if a ``filter`` is given.
 | 
			
		||||
 | 
			
		||||
            _total (`list`, optional):
 | 
			
		||||
                A single-item list to pass the total parameter by reference.
 | 
			
		||||
 | 
			
		||||
        Yields:
 | 
			
		||||
            The :tl:`User` objects returned by :tl:`GetParticipantsRequest`
 | 
			
		||||
            with an additional ``.participant`` attribute which is the
 | 
			
		||||
            matched :tl:`ChannelParticipant` type for channels/megagroups
 | 
			
		||||
            or :tl:`ChatParticipants` for normal chats.
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(filter, type):
 | 
			
		||||
            if filter in (types.ChannelParticipantsBanned,
 | 
			
		||||
                          types.ChannelParticipantsKicked,
 | 
			
		||||
                          types.ChannelParticipantsSearch):
 | 
			
		||||
                # These require a `q` parameter (support types for convenience)
 | 
			
		||||
                filter = filter('')
 | 
			
		||||
            else:
 | 
			
		||||
                filter = filter()
 | 
			
		||||
 | 
			
		||||
        entity = await self.get_input_entity(entity)
 | 
			
		||||
        if search and (filter
 | 
			
		||||
                       or not isinstance(entity, types.InputPeerChannel)):
 | 
			
		||||
            # We need to 'search' ourselves unless we have a PeerChannel
 | 
			
		||||
            search = search.lower()
 | 
			
		||||
 | 
			
		||||
            def filter_entity(ent):
 | 
			
		||||
                return search in utils.get_display_name(ent).lower() or\
 | 
			
		||||
                       search in (getattr(ent, 'username', '') or None).lower()
 | 
			
		||||
        else:
 | 
			
		||||
            def filter_entity(ent):
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
        limit = float('inf') if limit is None else int(limit)
 | 
			
		||||
        if isinstance(entity, types.InputPeerChannel):
 | 
			
		||||
            if _total or (aggressive and not filter):
 | 
			
		||||
                total = (await self(functions.channels.GetFullChannelRequest(
 | 
			
		||||
                    entity
 | 
			
		||||
                ))).full_chat.participants_count
 | 
			
		||||
                if _total:
 | 
			
		||||
                    _total[0] = total
 | 
			
		||||
            else:
 | 
			
		||||
                total = 0
 | 
			
		||||
 | 
			
		||||
            if limit == 0:
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            seen = set()
 | 
			
		||||
            if total > 10000 and aggressive and not filter:
 | 
			
		||||
                requests = [functions.channels.GetParticipantsRequest(
 | 
			
		||||
                    channel=entity,
 | 
			
		||||
                    filter=types.ChannelParticipantsSearch(search + chr(x)),
 | 
			
		||||
                    offset=0,
 | 
			
		||||
                    limit=200,
 | 
			
		||||
                    hash=0
 | 
			
		||||
                ) for x in range(ord('a'), ord('z') + 1)]
 | 
			
		||||
            else:
 | 
			
		||||
                requests = [functions.channels.GetParticipantsRequest(
 | 
			
		||||
                    channel=entity,
 | 
			
		||||
                    filter=filter or types.ChannelParticipantsSearch(search),
 | 
			
		||||
                    offset=0,
 | 
			
		||||
                    limit=200,
 | 
			
		||||
                    hash=0
 | 
			
		||||
                )]
 | 
			
		||||
 | 
			
		||||
            while requests:
 | 
			
		||||
                # Only care about the limit for the first request
 | 
			
		||||
                # (small amount of people, won't be aggressive).
 | 
			
		||||
                #
 | 
			
		||||
                # Most people won't care about getting exactly 12,345
 | 
			
		||||
                # members so it doesn't really matter not to be 100%
 | 
			
		||||
                # precise with being out of the offset/limit here.
 | 
			
		||||
                requests[0].limit = min(limit - requests[0].offset, 200)
 | 
			
		||||
                if requests[0].offset > limit:
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
                results = await self(requests)
 | 
			
		||||
                for i in reversed(range(len(requests))):
 | 
			
		||||
                    participants = results[i]
 | 
			
		||||
                    if not participants.users:
 | 
			
		||||
                        requests.pop(i)
 | 
			
		||||
                    else:
 | 
			
		||||
                        requests[i].offset += len(participants.participants)
 | 
			
		||||
                        users = {user.id: user for user in participants.users}
 | 
			
		||||
                        for participant in participants.participants:
 | 
			
		||||
                            user = users[participant.user_id]
 | 
			
		||||
                            if not filter_entity(user) or user.id in seen:
 | 
			
		||||
                                continue
 | 
			
		||||
 | 
			
		||||
                            seen.add(participant.user_id)
 | 
			
		||||
                            user = users[participant.user_id]
 | 
			
		||||
                            user.participant = participant
 | 
			
		||||
                            yield user
 | 
			
		||||
                            if len(seen) >= limit:
 | 
			
		||||
                                return
 | 
			
		||||
 | 
			
		||||
        elif isinstance(entity, types.InputPeerChat):
 | 
			
		||||
            # TODO We *could* apply the `filter` here ourselves
 | 
			
		||||
            full = await self(
 | 
			
		||||
                functions.messages.GetFullChatRequest(entity.chat_id))
 | 
			
		||||
            if not isinstance(
 | 
			
		||||
                    full.full_chat.participants, types.ChatParticipants):
 | 
			
		||||
                # ChatParticipantsForbidden won't have ``.participants``
 | 
			
		||||
                _total[0] = 0
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            if _total:
 | 
			
		||||
                _total[0] = len(full.full_chat.participants.participants)
 | 
			
		||||
 | 
			
		||||
            have = 0
 | 
			
		||||
            users = {user.id: user for user in full.users}
 | 
			
		||||
            for participant in full.full_chat.participants.participants:
 | 
			
		||||
                user = users[participant.user_id]
 | 
			
		||||
                if not filter_entity(user):
 | 
			
		||||
                    continue
 | 
			
		||||
                have += 1
 | 
			
		||||
                if have > limit:
 | 
			
		||||
                    break
 | 
			
		||||
                else:
 | 
			
		||||
                    user = users[participant.user_id]
 | 
			
		||||
                    user.participant = participant
 | 
			
		||||
                    yield user
 | 
			
		||||
        else:
 | 
			
		||||
            if _total:
 | 
			
		||||
                _total[0] = 1
 | 
			
		||||
            if limit != 0:
 | 
			
		||||
                user = await self.get_entity(entity)
 | 
			
		||||
                if filter_entity(user):
 | 
			
		||||
                    user.participant = None
 | 
			
		||||
                    yield user
 | 
			
		||||
 | 
			
		||||
    async def get_participants(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Same as :meth:`iter_participants`, but returns a list instead
 | 
			
		||||
        with an additional ``.total`` attribute on the list.
 | 
			
		||||
        """
 | 
			
		||||
        total = [0]
 | 
			
		||||
        kwargs['_total'] = total
 | 
			
		||||
        participants = UserList(x async for x in
 | 
			
		||||
                                self.iter_participants(*args, **kwargs))
 | 
			
		||||
        participants.total = total[0]
 | 
			
		||||
        return participants
 | 
			
		||||
 | 
			
		||||
    # endregion
 | 
			
		||||
| 
						 | 
				
			
			@ -1,22 +1,15 @@
 | 
			
		|||
import getpass
 | 
			
		||||
import hashlib
 | 
			
		||||
import io
 | 
			
		||||
import itertools
 | 
			
		||||
import logging
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
import warnings
 | 
			
		||||
from collections import UserList
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
from mimetypes import guess_type
 | 
			
		||||
 | 
			
		||||
from ..crypto import CdnDecrypter
 | 
			
		||||
from ..tl.custom import InputSizedFile
 | 
			
		||||
from ..tl.functions.help import AcceptTermsOfServiceRequest
 | 
			
		||||
from ..tl.functions.updates import GetDifferenceRequest
 | 
			
		||||
from ..tl.functions.upload import (
 | 
			
		||||
    SaveBigFilePartRequest, SaveFilePartRequest, GetFileRequest
 | 
			
		||||
    GetFileRequest
 | 
			
		||||
)
 | 
			
		||||
from ..tl.types.updates import (
 | 
			
		||||
    DifferenceSlice, DifferenceEmpty, Difference, DifferenceTooLong
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +36,6 @@ from ..errors import (
 | 
			
		|||
    SessionPasswordNeededError, FileMigrateError, PhoneNumberUnoccupiedError,
 | 
			
		||||
    PhoneNumberOccupiedError
 | 
			
		||||
)
 | 
			
		||||
from ..tl.custom import Draft, Dialog
 | 
			
		||||
from ..tl.functions.account import (
 | 
			
		||||
    GetPasswordRequest, UpdatePasswordSettingsRequest
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -51,41 +43,19 @@ from ..tl.functions.auth import (
 | 
			
		|||
    CheckPasswordRequest, LogOutRequest, SendCodeRequest, SignInRequest,
 | 
			
		||||
    SignUpRequest, ResendCodeRequest, ImportBotAuthorizationRequest
 | 
			
		||||
)
 | 
			
		||||
from ..tl.functions.messages import (
 | 
			
		||||
    GetDialogsRequest, GetHistoryRequest, SendMediaRequest,
 | 
			
		||||
    SendMessageRequest, GetAllDraftsRequest,
 | 
			
		||||
    ReadMentionsRequest, SendMultiMediaRequest,
 | 
			
		||||
    UploadMediaRequest, EditMessageRequest, GetFullChatRequest,
 | 
			
		||||
    ForwardMessagesRequest, SearchRequest
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from ..tl.functions import channels
 | 
			
		||||
from ..tl.functions import messages
 | 
			
		||||
 | 
			
		||||
from ..tl.functions.channels import (
 | 
			
		||||
    GetFullChannelRequest, GetParticipantsRequest
 | 
			
		||||
    GetFullChannelRequest
 | 
			
		||||
)
 | 
			
		||||
from ..tl.types import (
 | 
			
		||||
    DocumentAttributeAudio, DocumentAttributeFilename,
 | 
			
		||||
    InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerEmpty,
 | 
			
		||||
    Message, MessageMediaContact, MessageMediaDocument, MessageMediaPhoto,
 | 
			
		||||
    UserProfilePhoto, ChatPhoto, UpdateMessageID,
 | 
			
		||||
    UpdateNewChannelMessage, UpdateNewMessage, UpdateShortSentMessage,
 | 
			
		||||
    PeerUser, InputPeerChat, InputPeerChannel, MessageEmpty,
 | 
			
		||||
    Photo, InputSingleMedia, InputMediaPhoto, InputPhoto, InputFile, InputFileBig,
 | 
			
		||||
    InputDocument, InputMediaDocument, Document, MessageEntityTextUrl,
 | 
			
		||||
    InputMessageEntityMentionName, DocumentAttributeVideo,
 | 
			
		||||
    UpdateEditMessage, UpdateEditChannelMessage, UpdateShort, Updates,
 | 
			
		||||
    MessageMediaWebPage, ChannelParticipantsSearch, PhotoSize, PhotoCachedSize,
 | 
			
		||||
    PhotoSizeEmpty, MessageService, ChatParticipants, WebPage,
 | 
			
		||||
    ChannelParticipantsBanned, ChannelParticipantsKicked,
 | 
			
		||||
    InputMessagesFilterEmpty, UpdatesCombined
 | 
			
		||||
    UserProfilePhoto, ChatPhoto, UpdateNewMessage, InputPeerChannel, Photo,
 | 
			
		||||
    Document, Updates,
 | 
			
		||||
    MessageMediaWebPage, PhotoSize, PhotoCachedSize,
 | 
			
		||||
    PhotoSizeEmpty, WebPage
 | 
			
		||||
)
 | 
			
		||||
from ..tl.types.messages import DialogsSlice, MessagesNotModified
 | 
			
		||||
from ..tl.types.account import PasswordInputSettings, NoPassword
 | 
			
		||||
from ..tl import custom
 | 
			
		||||
from ..utils import Default
 | 
			
		||||
from ..extensions import markdown, html
 | 
			
		||||
 | 
			
		||||
__log__ = logging.getLogger(__name__)
 | 
			
		||||
import os
 | 
			
		||||
| 
						 | 
				
			
			@ -429,180 +399,6 @@ class TelegramClient(TelegramBaseClient):
 | 
			
		|||
 | 
			
		||||
    # endregion
 | 
			
		||||
 | 
			
		||||
    # region Dialogs ("chats") requests
 | 
			
		||||
 | 
			
		||||
    def iter_participants(self, entity, limit=None, search='',
 | 
			
		||||
                          filter=None, aggressive=False, _total=None):
 | 
			
		||||
        """
 | 
			
		||||
        Iterator over the participants belonging to the specified chat.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            entity (`entity`):
 | 
			
		||||
                The entity from which to retrieve the participants list.
 | 
			
		||||
 | 
			
		||||
            limit (`int`):
 | 
			
		||||
                Limits amount of participants fetched.
 | 
			
		||||
 | 
			
		||||
            search (`str`, optional):
 | 
			
		||||
                Look for participants with this string in name/username.
 | 
			
		||||
 | 
			
		||||
            filter (:tl:`ChannelParticipantsFilter`, optional):
 | 
			
		||||
                The filter to be used, if you want e.g. only admins
 | 
			
		||||
                Note that you might not have permissions for some filter.
 | 
			
		||||
                This has no effect for normal chats or users.
 | 
			
		||||
 | 
			
		||||
            aggressive (`bool`, optional):
 | 
			
		||||
                Aggressively looks for all participants in the chat in
 | 
			
		||||
                order to get more than 10,000 members (a hard limit
 | 
			
		||||
                imposed by Telegram). Note that this might take a long
 | 
			
		||||
                time (over 5 minutes), but is able to return over 90,000
 | 
			
		||||
                participants on groups with 100,000 members.
 | 
			
		||||
 | 
			
		||||
                This has no effect for groups or channels with less than
 | 
			
		||||
                10,000 members, or if a ``filter`` is given.
 | 
			
		||||
 | 
			
		||||
            _total (`list`, optional):
 | 
			
		||||
                A single-item list to pass the total parameter by reference.
 | 
			
		||||
 | 
			
		||||
        Yields:
 | 
			
		||||
            The :tl:`User` objects returned by :tl:`GetParticipantsRequest`
 | 
			
		||||
            with an additional ``.participant`` attribute which is the
 | 
			
		||||
            matched :tl:`ChannelParticipant` type for channels/megagroups
 | 
			
		||||
            or :tl:`ChatParticipants` for normal chats.
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(filter, type):
 | 
			
		||||
            if filter in (ChannelParticipantsBanned, ChannelParticipantsKicked,
 | 
			
		||||
                          ChannelParticipantsSearch):
 | 
			
		||||
                # These require a `q` parameter (support types for convenience)
 | 
			
		||||
                filter = filter('')
 | 
			
		||||
            else:
 | 
			
		||||
                filter = filter()
 | 
			
		||||
 | 
			
		||||
        entity = self.get_input_entity(entity)
 | 
			
		||||
        if search and (filter or not isinstance(entity, InputPeerChannel)):
 | 
			
		||||
            # We need to 'search' ourselves unless we have a PeerChannel
 | 
			
		||||
            search = search.lower()
 | 
			
		||||
 | 
			
		||||
            def filter_entity(ent):
 | 
			
		||||
                return search in utils.get_display_name(ent).lower() or\
 | 
			
		||||
                       search in (getattr(ent, 'username', '') or None).lower()
 | 
			
		||||
        else:
 | 
			
		||||
            def filter_entity(ent):
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
        limit = float('inf') if limit is None else int(limit)
 | 
			
		||||
        if isinstance(entity, InputPeerChannel):
 | 
			
		||||
            if _total or (aggressive and not filter):
 | 
			
		||||
                total = self(GetFullChannelRequest(
 | 
			
		||||
                    entity
 | 
			
		||||
                )).full_chat.participants_count
 | 
			
		||||
                if _total:
 | 
			
		||||
                    _total[0] = total
 | 
			
		||||
            else:
 | 
			
		||||
                total = 0
 | 
			
		||||
 | 
			
		||||
            if limit == 0:
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            seen = set()
 | 
			
		||||
            if total > 10000 and aggressive and not filter:
 | 
			
		||||
                requests = [GetParticipantsRequest(
 | 
			
		||||
                    channel=entity,
 | 
			
		||||
                    filter=ChannelParticipantsSearch(search + chr(x)),
 | 
			
		||||
                    offset=0,
 | 
			
		||||
                    limit=200,
 | 
			
		||||
                    hash=0
 | 
			
		||||
                ) for x in range(ord('a'), ord('z') + 1)]
 | 
			
		||||
            else:
 | 
			
		||||
                requests = [GetParticipantsRequest(
 | 
			
		||||
                    channel=entity,
 | 
			
		||||
                    filter=filter or ChannelParticipantsSearch(search),
 | 
			
		||||
                    offset=0,
 | 
			
		||||
                    limit=200,
 | 
			
		||||
                    hash=0
 | 
			
		||||
                )]
 | 
			
		||||
 | 
			
		||||
            while requests:
 | 
			
		||||
                # Only care about the limit for the first request
 | 
			
		||||
                # (small amount of people, won't be aggressive).
 | 
			
		||||
                #
 | 
			
		||||
                # Most people won't care about getting exactly 12,345
 | 
			
		||||
                # members so it doesn't really matter not to be 100%
 | 
			
		||||
                # precise with being out of the offset/limit here.
 | 
			
		||||
                requests[0].limit = min(limit - requests[0].offset, 200)
 | 
			
		||||
                if requests[0].offset > limit:
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
                results = self(requests)
 | 
			
		||||
                for i in reversed(range(len(requests))):
 | 
			
		||||
                    participants = results[i]
 | 
			
		||||
                    if not participants.users:
 | 
			
		||||
                        requests.pop(i)
 | 
			
		||||
                    else:
 | 
			
		||||
                        requests[i].offset += len(participants.participants)
 | 
			
		||||
                        users = {user.id: user for user in participants.users}
 | 
			
		||||
                        for participant in participants.participants:
 | 
			
		||||
                            user = users[participant.user_id]
 | 
			
		||||
                            if not filter_entity(user) or user.id in seen:
 | 
			
		||||
                                continue
 | 
			
		||||
 | 
			
		||||
                            seen.add(participant.user_id)
 | 
			
		||||
                            user = users[participant.user_id]
 | 
			
		||||
                            user.participant = participant
 | 
			
		||||
                            yield user
 | 
			
		||||
                            if len(seen) >= limit:
 | 
			
		||||
                                return
 | 
			
		||||
 | 
			
		||||
        elif isinstance(entity, InputPeerChat):
 | 
			
		||||
            # TODO We *could* apply the `filter` here ourselves
 | 
			
		||||
            full = self(GetFullChatRequest(entity.chat_id))
 | 
			
		||||
            if not isinstance(full.full_chat.participants, ChatParticipants):
 | 
			
		||||
                # ChatParticipantsForbidden won't have ``.participants``
 | 
			
		||||
                _total[0] = 0
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            if _total:
 | 
			
		||||
                _total[0] = len(full.full_chat.participants.participants)
 | 
			
		||||
 | 
			
		||||
            have = 0
 | 
			
		||||
            users = {user.id: user for user in full.users}
 | 
			
		||||
            for participant in full.full_chat.participants.participants:
 | 
			
		||||
                user = users[participant.user_id]
 | 
			
		||||
                if not filter_entity(user):
 | 
			
		||||
                    continue
 | 
			
		||||
                have += 1
 | 
			
		||||
                if have > limit:
 | 
			
		||||
                    break
 | 
			
		||||
                else:
 | 
			
		||||
                    user = users[participant.user_id]
 | 
			
		||||
                    user.participant = participant
 | 
			
		||||
                    yield user
 | 
			
		||||
        else:
 | 
			
		||||
            if _total:
 | 
			
		||||
                _total[0] = 1
 | 
			
		||||
            if limit != 0:
 | 
			
		||||
                user = self.get_entity(entity)
 | 
			
		||||
                if filter_entity(user):
 | 
			
		||||
                    user.participant = None
 | 
			
		||||
                    yield user
 | 
			
		||||
 | 
			
		||||
    def get_participants(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Same as :meth:`iter_participants`, but returns a list instead
 | 
			
		||||
        with an additional ``.total`` attribute on the list.
 | 
			
		||||
        """
 | 
			
		||||
        total = [0]
 | 
			
		||||
        kwargs['_total'] = total
 | 
			
		||||
        participants = UserList(self.iter_participants(*args, **kwargs))
 | 
			
		||||
        participants.total = total[0]
 | 
			
		||||
        return participants
 | 
			
		||||
 | 
			
		||||
    # endregion
 | 
			
		||||
 | 
			
		||||
    # region Uploading files
 | 
			
		||||
 | 
			
		||||
    # endregion
 | 
			
		||||
 | 
			
		||||
    # region Downloading media requests
 | 
			
		||||
 | 
			
		||||
    def download_profile_photo(self, entity, file=None, download_big=True):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user