diff --git a/.gitignore b/.gitignore index e81bec11..65fabceb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Generated code -/telethon/_tl/functions/ -/telethon/_tl/types/ +/telethon/_tl/fn/ +/telethon/_tl/*.py /telethon/_tl/alltlobjects.py /telethon/errors/rpcerrorlist.py diff --git a/setup.py b/setup.py index a498980a..2cb63901 100755 --- a/setup.py +++ b/setup.py @@ -55,7 +55,7 @@ METHODS_IN = GENERATOR_DIR / 'data/methods.csv' FRIENDLY_IN = GENERATOR_DIR / 'data/friendly.csv' TLOBJECT_IN_TLS = [Path(x) for x in GENERATOR_DIR.glob('data/*.tl')] -TLOBJECT_OUT = LIBRARY_DIR / 'tl' +TLOBJECT_OUT = LIBRARY_DIR / '_tl' IMPORT_DEPTH = 2 DOCS_IN_RES = GENERATOR_DIR / 'data/html' diff --git a/telethon/__init__.py b/telethon/__init__.py index d4e4c2c8..335abab6 100644 --- a/telethon/__init__.py +++ b/telethon/__init__.py @@ -1,8 +1,7 @@ from ._client.telegramclient import TelegramClient from .network import connection -from .tl import types, functions, custom -from .tl.custom import Button -from .tl import patched as _ # import for its side-effects +from ._tl import custom +from ._tl.custom import Button from . import version, events, utils, errors __version__ = version.__version__ diff --git a/telethon/_client/account.py b/telethon/_client/account.py index 46e0b6dc..0331a195 100644 --- a/telethon/_client/account.py +++ b/telethon/_client/account.py @@ -3,8 +3,7 @@ import inspect import typing from .users import _NOT_A_REQUEST -from .. import helpers, utils -from ..tl import functions, TLRequest +from .. import helpers, utils, _tl if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -50,7 +49,7 @@ class _TakeoutClient: self.__success = exc_type is None if self.__success is not None: - result = await self(functions.account.FinishTakeoutSessionRequest( + result = await self(_tl.fn.account.FinishTakeoutSession( self.__success)) if not result: raise ValueError("Failed to finish the takeout.") @@ -66,10 +65,10 @@ class _TakeoutClient: requests = ((request,) if single else request) wrapped = [] for r in requests: - if not isinstance(r, TLRequest): + if not isinstance(r, _tl.TLRequest): raise _NOT_A_REQUEST() await r.resolve(self, utils) - wrapped.append(functions.InvokeWithTakeoutRequest(takeout_id, r)) + wrapped.append(_tl.fn.InvokeWithTakeout(takeout_id, r)) return await self.__client( wrapped[0] if single else wrapped, ordered=ordered) @@ -127,7 +126,7 @@ def takeout( arg_specified = (arg is not None for arg in request_kwargs.values()) if self.session.takeout_id is None or any(arg_specified): - request = functions.account.InitTakeoutSessionRequest( + request = _tl.fn.account.InitTakeoutSession( **request_kwargs) else: request = None diff --git a/telethon/_client/auth.py b/telethon/_client/auth.py index 3699d795..859a4e87 100644 --- a/telethon/_client/auth.py +++ b/telethon/_client/auth.py @@ -5,8 +5,7 @@ import sys import typing import warnings -from .. import utils, helpers, errors, password as pwd_mod -from ..tl import types, functions, custom +from .. import utils, helpers, errors, password as pwd_mod, _tl if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -201,7 +200,7 @@ async def sign_in( *, password: str = None, bot_token: str = None, - phone_code_hash: str = None) -> 'typing.Union[types.User, types.auth.SentCode]': + phone_code_hash: str = None) -> 'typing.Union[_tl.User, _tl.auth.SentCode]': me = await self.get_me() if me: return me @@ -214,16 +213,16 @@ async def sign_in( # May raise PhoneCodeEmptyError, PhoneCodeExpiredError, # PhoneCodeHashEmptyError or PhoneCodeInvalidError. - request = functions.auth.SignInRequest( + request = _tl.fn.auth.SignIn( phone, phone_code_hash, str(code) ) elif password: - pwd = await self(functions.account.GetPasswordRequest()) - request = functions.auth.CheckPasswordRequest( + pwd = await self(_tl.fn.account.GetPassword()) + request = _tl.fn.auth.CheckPassword( pwd_mod.compute_check(pwd, password) ) elif bot_token: - request = functions.auth.ImportBotAuthorizationRequest( + request = _tl.fn.auth.ImportBotAuthorization( flags=0, bot_auth_token=bot_token, api_id=self.api_id, api_hash=self.api_hash ) @@ -234,7 +233,7 @@ async def sign_in( ) result = await self(request) - if isinstance(result, types.auth.AuthorizationSignUpRequired): + if isinstance(result, _tl.auth.AuthorizationSignUpRequired): # Emulate pre-layer 104 behaviour self._tos = result.terms_of_service raise errors.PhoneNumberUnoccupiedError(request=request) @@ -248,7 +247,7 @@ async def sign_up( last_name: str = '', *, phone: str = None, - phone_code_hash: str = None) -> 'types.User': + phone_code_hash: str = None) -> '_tl.User': me = await self.get_me() if me: return me @@ -281,7 +280,7 @@ async def sign_up( phone, phone_code_hash = \ self._parse_phone_and_hash(phone, phone_code_hash) - result = await self(functions.auth.SignUpRequest( + result = await self(_tl.fn.auth.SignUp( phone_number=phone, phone_code_hash=phone_code_hash, first_name=first_name, @@ -290,7 +289,7 @@ async def sign_up( if self._tos: await self( - functions.help.AcceptTermsOfServiceRequest(self._tos.id)) + _tl.fn.help.AcceptTermsOfService(self._tos.id)) return self._on_login(result.user) @@ -309,20 +308,20 @@ async def send_code_request( self: 'TelegramClient', phone: str, *, - force_sms: bool = False) -> 'types.auth.SentCode': + force_sms: bool = False) -> '_tl.auth.SentCode': result = None phone = utils.parse_phone(phone) or self._phone phone_hash = self._phone_code_hash.get(phone) if not phone_hash: try: - result = await self(functions.auth.SendCodeRequest( - phone, self.api_id, self.api_hash, types.CodeSettings())) + result = await self(_tl.fn.auth.SendCode( + phone, self.api_id, self.api_hash, _tl.CodeSettings())) except errors.AuthRestartError: return await self.send_code_request(phone, force_sms=force_sms) # If we already sent a SMS, do not resend the code (hash may be empty) - if isinstance(result.type, types.auth.SentCodeTypeSms): + if isinstance(result.type, _tl.auth.SentCodeTypeSms): force_sms = False # phone_code_hash may be empty, if it is, do not save it (#1283) @@ -335,7 +334,7 @@ async def send_code_request( if force_sms: result = await self( - functions.auth.ResendCodeRequest(phone, phone_hash)) + _tl.fn.auth.ResendCode(phone, phone_hash)) self._phone_code_hash[phone] = result.phone_code_hash @@ -348,7 +347,7 @@ async def qr_login(self: 'TelegramClient', ignored_ids: typing.List[int] = None) async def log_out(self: 'TelegramClient') -> bool: try: - await self(functions.auth.LogOutRequest()) + await self(_tl.fn.auth.LogOut()) except errors.RPCError: return False @@ -375,16 +374,16 @@ async def edit_2fa( if email and not callable(email_code_callback): raise ValueError('email present without email_code_callback') - pwd = await self(functions.account.GetPasswordRequest()) + pwd = await self(_tl.fn.account.GetPassword()) pwd.new_algo.salt1 += os.urandom(32) - assert isinstance(pwd, types.account.Password) + assert isinstance(pwd, _tl.account.Password) if not pwd.has_password and current_password: current_password = None if current_password: password = pwd_mod.compute_check(pwd, current_password) else: - password = types.InputCheckPasswordEmpty() + password = _tl.InputCheckPasswordEmpty() if new_password: new_password_hash = pwd_mod.compute_digest( @@ -393,9 +392,9 @@ async def edit_2fa( new_password_hash = b'' try: - await self(functions.account.UpdatePasswordSettingsRequest( + await self(_tl.fn.account.UpdatePasswordSettings( password=password, - new_settings=types.account.PasswordInputSettings( + new_settings=_tl.account.PasswordInputSettings( new_algo=pwd.new_algo, new_password_hash=new_password_hash, hint=hint, @@ -409,6 +408,6 @@ async def edit_2fa( code = await code code = str(code) - await self(functions.account.ConfirmPasswordEmailRequest(code)) + await self(_tl.fn.account.ConfirmPasswordEmail(code)) return True diff --git a/telethon/_client/bots.py b/telethon/_client/bots.py index 0912fc20..0e967ed9 100644 --- a/telethon/_client/bots.py +++ b/telethon/_client/bots.py @@ -1,7 +1,6 @@ import typing -from .. import hints -from ..tl import types, functions, custom +from .. import hints, _tl if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -14,14 +13,14 @@ async def inline_query( *, entity: 'hints.EntityLike' = None, offset: str = None, - geo_point: 'types.GeoPoint' = None) -> custom.InlineResults: + geo_point: '_tl.GeoPoint' = None) -> _tl.custom.InlineResults: bot = await self.get_input_entity(bot) if entity: peer = await self.get_input_entity(entity) else: - peer = types.InputPeerEmpty() + peer = _tl.InputPeerEmpty() - result = await self(functions.messages.GetInlineBotResultsRequest( + result = await self(_tl.fn.messages.GetInlineBotResults( bot=bot, peer=peer, query=query, @@ -29,4 +28,4 @@ async def inline_query( geo_point=geo_point )) - return custom.InlineResults(self, result, entity=peer if entity else None) + return _tl.custom.InlineResults(self, result, entity=peer if entity else None) diff --git a/telethon/_client/buttons.py b/telethon/_client/buttons.py index 41413708..5dd9c413 100644 --- a/telethon/_client/buttons.py +++ b/telethon/_client/buttons.py @@ -1,12 +1,11 @@ import typing -from .. import utils, hints -from ..tl import types, custom +from .. import utils, hints, _tl def build_reply_markup( buttons: 'typing.Optional[hints.MarkupLike]', - inline_only: bool = False) -> 'typing.Optional[types.TypeReplyMarkup]': + inline_only: bool = False) -> 'typing.Optional[_tl.TypeReplyMarkup]': if buttons is None: return None @@ -31,7 +30,7 @@ def build_reply_markup( for row in buttons: current = [] for button in row: - if isinstance(button, custom.Button): + if isinstance(button, _tl.custom.Button): if button.resize is not None: resize = button.resize if button.single_use is not None: @@ -40,10 +39,10 @@ def build_reply_markup( selective = button.selective button = button.button - elif isinstance(button, custom.MessageButton): + elif isinstance(button, _tl.custom.MessageButton): button = button.button - inline = custom.Button._is_inline(button) + inline = _tl.custom.Button._is_inline(button) is_inline |= inline is_normal |= not inline @@ -52,14 +51,14 @@ def build_reply_markup( current.append(button) if current: - rows.append(types.KeyboardButtonRow(current)) + rows.append(_tl.KeyboardButtonRow(current)) if inline_only and is_normal: raise ValueError('You cannot use non-inline buttons here') elif is_inline == is_normal and is_normal: raise ValueError('You cannot mix inline with normal buttons') elif is_inline: - return types.ReplyInlineMarkup(rows) + return _tl.ReplyInlineMarkup(rows) # elif is_normal: - return types.ReplyKeyboardMarkup( + return _tl.ReplyKeyboardMarkup( rows, resize=resize, single_use=single_use, selective=selective) diff --git a/telethon/_client/chats.py b/telethon/_client/chats.py index 4147b45b..904bba10 100644 --- a/telethon/_client/chats.py +++ b/telethon/_client/chats.py @@ -4,9 +4,8 @@ import itertools import string import typing -from .. import helpers, utils, hints, errors +from .. import helpers, utils, hints, errors, _tl from ..requestiter import RequestIter -from ..tl import types, functions, custom if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -18,28 +17,28 @@ _MAX_PROFILE_PHOTO_CHUNK_SIZE = 100 class _ChatAction: _str_mapping = { - 'typing': types.SendMessageTypingAction(), - 'contact': types.SendMessageChooseContactAction(), - 'game': types.SendMessageGamePlayAction(), - 'location': types.SendMessageGeoLocationAction(), - 'sticker': types.SendMessageChooseStickerAction(), + 'typing': _tl.SendMessageTypingAction(), + 'contact': _tl.SendMessageChooseContactAction(), + 'game': _tl.SendMessageGamePlayAction(), + 'location': _tl.SendMessageGeoLocationAction(), + 'sticker': _tl.SendMessageChooseStickerAction(), - 'record-audio': types.SendMessageRecordAudioAction(), - 'record-voice': types.SendMessageRecordAudioAction(), # alias - 'record-round': types.SendMessageRecordRoundAction(), - 'record-video': types.SendMessageRecordVideoAction(), + 'record-audio': _tl.SendMessageRecordAudioAction(), + 'record-voice': _tl.SendMessageRecordAudioAction(), # alias + 'record-round': _tl.SendMessageRecordRoundAction(), + 'record-video': _tl.SendMessageRecordVideoAction(), - 'audio': types.SendMessageUploadAudioAction(1), - 'voice': types.SendMessageUploadAudioAction(1), # alias - 'song': types.SendMessageUploadAudioAction(1), # alias - 'round': types.SendMessageUploadRoundAction(1), - 'video': types.SendMessageUploadVideoAction(1), + 'audio': _tl.SendMessageUploadAudioAction(1), + 'voice': _tl.SendMessageUploadAudioAction(1), # alias + 'song': _tl.SendMessageUploadAudioAction(1), # alias + 'round': _tl.SendMessageUploadRoundAction(1), + 'video': _tl.SendMessageUploadVideoAction(1), - 'photo': types.SendMessageUploadPhotoAction(1), - 'document': types.SendMessageUploadDocumentAction(1), - 'file': types.SendMessageUploadDocumentAction(1), # alias + 'photo': _tl.SendMessageUploadPhotoAction(1), + 'document': _tl.SendMessageUploadDocumentAction(1), + 'file': _tl.SendMessageUploadDocumentAction(1), # alias - 'cancel': types.SendMessageCancelAction() + 'cancel': _tl.SendMessageCancelAction() } def __init__(self, client, chat, action, *, delay, auto_cancel): @@ -58,7 +57,7 @@ class _ChatAction: # Since `self._action` is passed by reference we can avoid # recreating the request all the time and still modify # `self._action.progress` directly in `progress`. - self._request = functions.messages.SetTypingRequest( + self._request = _tl.fn.messages.SetTyping( self._chat, self._action) self._running = True @@ -85,8 +84,8 @@ class _ChatAction: pass except asyncio.CancelledError: if self._auto_cancel: - await self._client(functions.messages.SetTypingRequest( - self._chat, types.SendMessageCancelAction())) + await self._client(_tl.fn.messages.SetTyping( + self._chat, _tl.SendMessageCancelAction())) def progress(self, current, total): if hasattr(self._action, 'progress'): @@ -96,10 +95,10 @@ class _ChatAction: class _ParticipantsIter(RequestIter): async def _init(self, entity, filter, search, aggressive): if isinstance(filter, type): - if filter in (types.ChannelParticipantsBanned, - types.ChannelParticipantsKicked, - types.ChannelParticipantsSearch, - types.ChannelParticipantsContacts): + if filter in (_tl.ChannelParticipantsBanned, + _tl.ChannelParticipantsKicked, + _tl.ChannelParticipantsSearch, + _tl.ChannelParticipantsContacts): # These require a `q` parameter (support types for convenience) filter = filter('') else: @@ -125,23 +124,23 @@ class _ParticipantsIter(RequestIter): if self.limit <= 0: # May not have access to the channel, but getFull can get the .total. self.total = (await self.client( - functions.channels.GetFullChannelRequest(entity) + _tl.fn.channels.GetFullChannel(entity) )).full_chat.participants_count raise StopAsyncIteration self.seen = set() if aggressive and not filter: - self.requests.extend(functions.channels.GetParticipantsRequest( + self.requests.extend(_tl.fn.channels.GetParticipants( channel=entity, - filter=types.ChannelParticipantsSearch(x), + filter=_tl.ChannelParticipantsSearch(x), offset=0, limit=_MAX_PARTICIPANTS_CHUNK_SIZE, hash=0 ) for x in (search or string.ascii_lowercase)) else: - self.requests.append(functions.channels.GetParticipantsRequest( + self.requests.append(_tl.fn.channels.GetParticipants( channel=entity, - filter=filter or types.ChannelParticipantsSearch(search), + filter=filter or _tl.ChannelParticipantsSearch(search), offset=0, limit=_MAX_PARTICIPANTS_CHUNK_SIZE, hash=0 @@ -149,9 +148,9 @@ class _ParticipantsIter(RequestIter): elif ty == helpers._EntityType.CHAT: full = await self.client( - functions.messages.GetFullChatRequest(entity.chat_id)) + _tl.fn.messages.GetFullChat(entity.chat_id)) if not isinstance( - full.full_chat.participants, types.ChatParticipants): + full.full_chat.participants, _tl.ChatParticipants): # ChatParticipantsForbidden won't have ``.participants`` self.total = 0 raise StopAsyncIteration @@ -160,7 +159,7 @@ class _ParticipantsIter(RequestIter): users = {user.id: user for user in full.users} for participant in full.full_chat.participants.participants: - if isinstance(participant, types.ChannelParticipantBanned): + if isinstance(participant, _tl.ChannelParticipantBanned): user_id = participant.peer.user_id else: user_id = participant.user_id @@ -202,15 +201,15 @@ class _ParticipantsIter(RequestIter): if self.total is None: f = self.requests[0].filter if len(self.requests) > 1 or ( - not isinstance(f, types.ChannelParticipantsRecent) - and (not isinstance(f, types.ChannelParticipantsSearch) or f.q) + not isinstance(f, _tl.ChannelParticipantsRecent) + and (not isinstance(f, _tl.ChannelParticipantsSearch) or f.q) ): # Only do an additional getParticipants here to get the total # if there's a filter which would reduce the real total number. # getParticipants is cheaper than getFull. - self.total = (await self.client(functions.channels.GetParticipantsRequest( + self.total = (await self.client(_tl.fn.channels.GetParticipants( channel=self.requests[0].channel, - filter=types.ChannelParticipantsRecent(), + filter=_tl.ChannelParticipantsRecent(), offset=0, limit=1, hash=0 @@ -230,8 +229,8 @@ class _ParticipantsIter(RequestIter): users = {user.id: user for user in participants.users} for participant in participants.participants: - if isinstance(participant, types.ChannelParticipantBanned): - if not isinstance(participant.peer, types.PeerUser): + if isinstance(participant, _tl.ChannelParticipantBanned): + if not isinstance(participant.peer, _tl.PeerUser): # May have the entire channel banned. See #3105. continue user_id = participant.peer.user_id @@ -257,7 +256,7 @@ class _AdminLogIter(RequestIter): if any((join, leave, invite, restrict, unrestrict, ban, unban, promote, demote, info, settings, pinned, edit, delete, group_call)): - events_filter = types.ChannelAdminLogEventsFilter( + events_filter = _tl.ChannelAdminLogEventsFilter( join=join, leave=leave, invite=invite, ban=restrict, unban=unrestrict, kick=ban, unkick=unban, promote=promote, demote=demote, info=info, settings=settings, pinned=pinned, @@ -276,7 +275,7 @@ class _AdminLogIter(RequestIter): for admin in admins: admin_list.append(await self.client.get_input_entity(admin)) - self.request = functions.channels.GetAdminLogRequest( + self.request = _tl.fn.channels.GetAdminLog( self.entity, q=search or '', min_id=min_id, max_id=max_id, limit=0, events_filter=events_filter, admins=admin_list or None ) @@ -290,7 +289,7 @@ class _AdminLogIter(RequestIter): self.request.max_id = min((e.id for e in r.events), default=0) for ev in r.events: if isinstance(ev.action, - types.ChannelAdminLogEventActionEditMessage): + _tl.ChannelAdminLogEventActionEditMessage): ev.action.prev_message._finish_init( self.client, entities, self.entity) @@ -298,11 +297,11 @@ class _AdminLogIter(RequestIter): self.client, entities, self.entity) elif isinstance(ev.action, - types.ChannelAdminLogEventActionDeleteMessage): + _tl.ChannelAdminLogEventActionDeleteMessage): ev.action.message._finish_init( self.client, entities, self.entity) - self.buffer.append(custom.AdminLogEvent(ev, entities)) + self.buffer.append(_tl.custom.AdminLogEvent(ev, entities)) if len(r.events) < self.request.limit: return True @@ -315,17 +314,17 @@ class _ProfilePhotoIter(RequestIter): entity = await self.client.get_input_entity(entity) ty = helpers._entity_type(entity) if ty == helpers._EntityType.USER: - self.request = functions.photos.GetUserPhotosRequest( + self.request = _tl.fn.photos.GetUserPhotos( entity, offset=offset, max_id=max_id, limit=1 ) else: - self.request = functions.messages.SearchRequest( + self.request = _tl.fn.messages.Search( peer=entity, q='', - filter=types.InputMessagesFilterChatPhotos(), + filter=_tl.InputMessagesFilterChatPhotos(), min_date=None, max_date=None, offset_id=0, @@ -339,9 +338,9 @@ class _ProfilePhotoIter(RequestIter): if self.limit == 0: self.request.limit = 1 result = await self.client(self.request) - if isinstance(result, types.photos.Photos): + if isinstance(result, _tl.photos.Photos): self.total = len(result.photos) - elif isinstance(result, types.messages.Messages): + elif isinstance(result, _tl.messages.Messages): self.total = len(result.messages) else: # Luckily both photosSlice and messages have a count for total @@ -351,17 +350,17 @@ class _ProfilePhotoIter(RequestIter): self.request.limit = min(self.left, _MAX_PROFILE_PHOTO_CHUNK_SIZE) result = await self.client(self.request) - if isinstance(result, types.photos.Photos): + if isinstance(result, _tl.photos.Photos): self.buffer = result.photos self.left = len(self.buffer) self.total = len(self.buffer) - elif isinstance(result, types.messages.Messages): + elif isinstance(result, _tl.messages.Messages): self.buffer = [x.action.photo for x in result.messages - if isinstance(x.action, types.MessageActionChatEditPhoto)] + if isinstance(x.action, _tl.MessageActionChatEditPhoto)] self.left = len(self.buffer) self.total = len(self.buffer) - elif isinstance(result, types.photos.PhotosSlice): + elif isinstance(result, _tl.photos.PhotosSlice): self.buffer = result.photos self.total = result.count if len(self.buffer) < self.request.limit: @@ -381,16 +380,16 @@ class _ProfilePhotoIter(RequestIter): # Unconditionally fetch the full channel to obtain this photo and # yield it with the rest (unless it's a duplicate). seen_id = None - if isinstance(result, types.messages.ChannelMessages): - channel = await self.client(functions.channels.GetFullChannelRequest(self.request.peer)) + if isinstance(result, _tl.messages.ChannelMessages): + channel = await self.client(_tl.fn.channels.GetFullChannel(self.request.peer)) photo = channel.full_chat.chat_photo - if isinstance(photo, types.Photo): + if isinstance(photo, _tl.Photo): self.buffer.append(photo) seen_id = photo.id self.buffer.extend( x.action.photo for x in result.messages - if isinstance(x.action, types.MessageActionChatEditPhoto) + if isinstance(x.action, _tl.MessageActionChatEditPhoto) and x.action.photo.id != seen_id ) @@ -407,7 +406,7 @@ def iter_participants( limit: float = None, *, search: str = '', - filter: 'types.TypeChannelParticipantsFilter' = None, + filter: '_tl.TypeChannelParticipantsFilter' = None, aggressive: bool = False) -> _ParticipantsIter: return _ParticipantsIter( self, @@ -506,7 +505,7 @@ async def get_profile_photos( def action( self: 'TelegramClient', entity: 'hints.EntityLike', - action: 'typing.Union[str, types.TypeSendMessageAction]', + action: 'typing.Union[str, _tl.TypeSendMessageAction]', *, delay: float = 4, auto_cancel: bool = True) -> 'typing.Union[_ChatAction, typing.Coroutine]': @@ -516,17 +515,17 @@ def action( except KeyError: raise ValueError( 'No such action "{}"'.format(action)) from None - elif not isinstance(action, types.TLObject) or action.SUBCLASS_OF_ID != 0x20b2cc21: + elif not isinstance(action, _tl.TLObject) or action.SUBCLASS_OF_ID != 0x20b2cc21: # 0x20b2cc21 = crc32(b'SendMessageAction') if isinstance(action, type): raise ValueError('You must pass an instance, not the class') else: raise ValueError('Cannot use {} as action'.format(action)) - if isinstance(action, types.SendMessageCancelAction): + if isinstance(action, _tl.SendMessageCancelAction): # ``SetTypingRequest.resolve`` will get input peer of ``entity``. - return self(functions.messages.SetTypingRequest( - entity, types.SendMessageCancelAction())) + return self(_tl.fn.messages.SetTyping( + entity, _tl.SendMessageCancelAction())) return _ChatAction( self, entity, action, delay=delay, auto_cancel=auto_cancel) @@ -547,7 +546,7 @@ async def edit_admin( manage_call: bool = None, anonymous: bool = None, is_admin: bool = None, - title: str = None) -> types.Updates: + title: str = None) -> _tl.Updates: entity = await self.get_input_entity(entity) user = await self.get_input_entity(user) ty = helpers._entity_type(user) @@ -576,7 +575,7 @@ async def edit_admin( edit_messages = None perms = locals() - return await self(functions.channels.EditAdminRequest(entity, user, types.ChatAdminRights(**{ + return await self(_tl.fn.channels.EditAdmin(entity, user, _tl.ChatAdminRights(**{ # A permission is its explicit (not-None) value or `is_admin`. # This essentially makes `is_admin` be the default value. name: perms[name] if perms[name] is not None else is_admin @@ -589,7 +588,7 @@ async def edit_admin( if is_admin is None: is_admin = any(locals()[x] for x in perm_names) - return await self(functions.messages.EditChatAdminRequest( + return await self(_tl.fn.messages.EditChatAdmin( entity, user, is_admin=is_admin)) else: @@ -613,13 +612,13 @@ async def edit_permissions( send_polls: bool = True, change_info: bool = True, invite_users: bool = True, - pin_messages: bool = True) -> types.Updates: + pin_messages: bool = True) -> _tl.Updates: entity = await self.get_input_entity(entity) ty = helpers._entity_type(entity) if ty != helpers._EntityType.CHANNEL: raise ValueError('You must pass either a channel or a supergroup') - rights = types.ChatBannedRights( + rights = _tl.ChatBannedRights( until_date=until_date, view_messages=not view_messages, send_messages=not send_messages, @@ -636,7 +635,7 @@ async def edit_permissions( ) if user is None: - return await self(functions.messages.EditChatDefaultBannedRightsRequest( + return await self(_tl.fn.messages.EditChatDefaultBannedRights( peer=entity, banned_rights=rights )) @@ -646,10 +645,10 @@ async def edit_permissions( if ty != helpers._EntityType.USER: raise ValueError('You must pass a user entity') - if isinstance(user, types.InputPeerSelf): + if isinstance(user, _tl.InputPeerSelf): raise ValueError('You cannot restrict yourself') - return await self(functions.channels.EditBannedRequest( + return await self(_tl.fn.channels.EditBanned( channel=entity, participant=user, banned_rights=rights @@ -667,24 +666,24 @@ async def kick_participant( ty = helpers._entity_type(entity) if ty == helpers._EntityType.CHAT: - resp = await self(functions.messages.DeleteChatUserRequest(entity.chat_id, user)) + resp = await self(_tl.fn.messages.DeleteChatUser(entity.chat_id, user)) elif ty == helpers._EntityType.CHANNEL: - if isinstance(user, types.InputPeerSelf): + if isinstance(user, _tl.InputPeerSelf): # Despite no longer being in the channel, the account still # seems to get the service message. - resp = await self(functions.channels.LeaveChannelRequest(entity)) + resp = await self(_tl.fn.channels.LeaveChannel(entity)) else: - resp = await self(functions.channels.EditBannedRequest( + resp = await self(_tl.fn.channels.EditBanned( channel=entity, participant=user, - banned_rights=types.ChatBannedRights( + banned_rights=_tl.ChatBannedRights( until_date=None, view_messages=True) )) await asyncio.sleep(0.5) - await self(functions.channels.EditBannedRequest( + await self(_tl.fn.channels.EditBanned( channel=entity, participant=user, - banned_rights=types.ChatBannedRights(until_date=None) + banned_rights=_tl.ChatBannedRights(until_date=None) )) else: raise ValueError('You must pass either a channel or a chat') @@ -695,14 +694,14 @@ async def get_permissions( self: 'TelegramClient', entity: 'hints.EntityLike', user: 'hints.EntityLike' = None -) -> 'typing.Optional[custom.ParticipantPermissions]': +) -> 'typing.Optional[_tl.custom.ParticipantPermissions]': entity = await self.get_entity(entity) if not user: - if isinstance(entity, types.Channel): - FullChat = await self(functions.channels.GetFullChannelRequest(entity)) - elif isinstance(entity, types.Chat): - FullChat = await self(functions.messages.GetFullChatRequest(entity)) + if isinstance(entity, _tl.Channel): + FullChat = await self(_tl.fn.channels.GetFullChannel(entity)) + elif isinstance(entity, _tl.Chat): + FullChat = await self(_tl.fn.messages.GetFullChat(entity)) else: return return FullChat.chats[0].default_banned_rights @@ -712,20 +711,20 @@ async def get_permissions( if helpers._entity_type(user) != helpers._EntityType.USER: raise ValueError('You must pass a user entity') if helpers._entity_type(entity) == helpers._EntityType.CHANNEL: - participant = await self(functions.channels.GetParticipantRequest( + participant = await self(_tl.fn.channels.GetParticipant( entity, user )) - return custom.ParticipantPermissions(participant.participant, False) + return _tl.custom.ParticipantPermissions(participant.participant, False) elif helpers._entity_type(entity) == helpers._EntityType.CHAT: - chat = await self(functions.messages.GetFullChatRequest( + chat = await self(_tl.fn.messages.GetFullChat( entity )) - if isinstance(user, types.InputPeerSelf): + if isinstance(user, _tl.InputPeerSelf): user = await self.get_me(input_peer=True) for participant in chat.full_chat.participants.participants: if participant.user_id == user.user_id: - return custom.ParticipantPermissions(participant, True) + return _tl.custom.ParticipantPermissions(participant, True) raise errors.UserNotParticipantError(None) raise ValueError('You must pass either a channel or a chat') @@ -733,7 +732,7 @@ async def get_permissions( async def get_stats( self: 'TelegramClient', entity: 'hints.EntityLike', - message: 'typing.Union[int, types.Message]' = None, + message: 'typing.Union[int, _tl.Message]' = None, ): entity = await self.get_input_entity(entity) if helpers._entity_type(entity) != helpers._EntityType.CHANNEL: @@ -742,7 +741,7 @@ async def get_stats( message = utils.get_message_id(message) if message is not None: try: - req = functions.stats.GetMessageStatsRequest(entity, message) + req = _tl.fn.stats.GetMessageStats(entity, message) return await self(req) except errors.StatsMigrateError as e: dc = e.dc @@ -751,12 +750,12 @@ async def get_stats( # try to guess and if it fails we know it's the other one (best case # no extra request, worst just one). try: - req = functions.stats.GetBroadcastStatsRequest(entity) + req = _tl.fn.stats.GetBroadcastStats(entity) return await self(req) except errors.StatsMigrateError as e: dc = e.dc except errors.BroadcastRequiredError: - req = functions.stats.GetMegagroupStatsRequest(entity) + req = _tl.fn.stats.GetMegagroupStats(entity) try: return await self(req) except errors.StatsMigrateError as e: diff --git a/telethon/_client/dialogs.py b/telethon/_client/dialogs.py index 8471c7fb..aee8861d 100644 --- a/telethon/_client/dialogs.py +++ b/telethon/_client/dialogs.py @@ -3,9 +3,8 @@ import inspect import itertools import typing -from .. import helpers, utils, hints, errors +from .. import helpers, utils, hints, errors, _tl from ..requestiter import RequestIter -from ..tl import types, functions, custom _MAX_CHUNK_SIZE = 100 @@ -21,14 +20,14 @@ def _dialog_message_key(peer, message_id): 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 + return (peer.channel_id if isinstance(peer, _tl.PeerChannel) else None), message_id class _DialogsIter(RequestIter): async def _init( self, offset_date, offset_id, offset_peer, ignore_pinned, ignore_migrated, folder ): - self.request = functions.messages.GetDialogsRequest( + self.request = _tl.fn.messages.GetDialogs( offset_date=offset_date, offset_id=offset_id, offset_peer=offset_peer, @@ -56,7 +55,7 @@ class _DialogsIter(RequestIter): entities = {utils.get_peer_id(x): x for x in itertools.chain(r.users, r.chats) - if not isinstance(x, (types.UserEmpty, types.ChatEmpty))} + if not isinstance(x, (_tl.UserEmpty, _tl.ChatEmpty))} messages = {} for m in r.messages: @@ -80,7 +79,7 @@ class _DialogsIter(RequestIter): # Real world example: https://t.me/TelethonChat/271471 continue - cd = custom.Dialog(self.client, d, entities, message) + cd = _tl.custom.Dialog(self.client, d, entities, message) if cd.dialog.pts: self.client._channel_pts[cd.id] = cd.dialog.pts @@ -89,7 +88,7 @@ class _DialogsIter(RequestIter): self.buffer.append(cd) if len(r.dialogs) < self.request.limit\ - or not isinstance(r, types.messages.DialogsSlice): + or not isinstance(r, _tl.messages.DialogsSlice): # Less than we requested means we reached the end, or # we didn't get a DialogsSlice which means we got all. return True @@ -112,15 +111,15 @@ class _DialogsIter(RequestIter): class _DraftsIter(RequestIter): async def _init(self, entities, **kwargs): if not entities: - r = await self.client(functions.messages.GetAllDraftsRequest()) + r = await self.client(_tl.fn.messages.GetAllDrafts()) items = r.updates else: peers = [] for entity in entities: - peers.append(types.InputDialogPeer( + peers.append(_tl.InputDialogPeer( await self.client.get_input_entity(entity))) - r = await self.client(functions.messages.GetPeerDialogsRequest(peers)) + r = await self.client(_tl.fn.messages.GetPeerDialogs(peers)) items = r.dialogs # TODO Maybe there should be a helper method for this? @@ -128,7 +127,7 @@ class _DraftsIter(RequestIter): for x in itertools.chain(r.users, r.chats)} self.buffer.extend( - custom.Draft(self.client, entities[utils.get_peer_id(d.peer)], d.draft) + _tl.custom.Draft(self.client, entities[utils.get_peer_id(d.peer)], d.draft) for d in items ) @@ -142,7 +141,7 @@ def iter_dialogs( *, offset_date: 'hints.DateLike' = None, offset_id: int = 0, - offset_peer: 'hints.EntityLike' = types.InputPeerEmpty(), + offset_peer: 'hints.EntityLike' = _tl.InputPeerEmpty(), ignore_pinned: bool = False, ignore_migrated: bool = False, folder: int = None, @@ -192,12 +191,12 @@ async def edit_folder( folder: typing.Union[int, typing.Sequence[int]] = None, *, unpack=None -) -> types.Updates: +) -> _tl.Updates: if (entity is None) == (unpack is None): raise ValueError('You can only set either entities or unpack, not both') if unpack is not None: - return await self(functions.folders.DeleteFolderRequest( + return await self(_tl.fn.folders.DeleteFolder( folder_id=unpack )) @@ -214,8 +213,8 @@ async def edit_folder( elif len(entities) != len(folder): raise ValueError('Number of folders does not match number of entities') - return await self(functions.folders.EditPeerFoldersRequest([ - types.InputFolderPeer(x, folder_id=y) + return await self(_tl.fn.folders.EditPeerFolders([ + _tl.InputFolderPeer(x, folder_id=y) for x, y in zip(entities, folder) ])) @@ -227,7 +226,7 @@ async def delete_dialog( ): # If we have enough information (`Dialog.delete` gives it to us), # then we know we don't have to kick ourselves in deactivated chats. - if isinstance(entity, types.Chat): + if isinstance(entity, _tl.Chat): deactivated = entity.deactivated else: deactivated = False @@ -235,12 +234,12 @@ async def delete_dialog( entity = await self.get_input_entity(entity) ty = helpers._entity_type(entity) if ty == helpers._EntityType.CHANNEL: - return await self(functions.channels.LeaveChannelRequest(entity)) + return await self(_tl.fn.channels.LeaveChannel(entity)) if ty == helpers._EntityType.CHAT and not deactivated: try: - result = await self(functions.messages.DeleteChatUserRequest( - entity.chat_id, types.InputUserSelf(), revoke_history=revoke + result = await self(_tl.fn.messages.DeleteChatUser( + entity.chat_id, _tl.InputUserSelf(), revoke_history=revoke )) except errors.PeerIdInvalidError: # Happens if we didn't have the deactivated information @@ -249,6 +248,6 @@ async def delete_dialog( result = None if not await self.is_bot(): - await self(functions.messages.DeleteHistoryRequest(entity, 0, revoke=revoke)) + await self(_tl.fn.messages.DeleteHistory(entity, 0, revoke=revoke)) return result diff --git a/telethon/_client/downloads.py b/telethon/_client/downloads.py index aa8ed59a..974db2a0 100644 --- a/telethon/_client/downloads.py +++ b/telethon/_client/downloads.py @@ -8,9 +8,8 @@ import asyncio from ..crypto import AES -from .. import utils, helpers, errors, hints +from .. import utils, helpers, errors, hints, _tl from ..requestiter import RequestIter -from ..tl import TLObject, types, functions try: import aiohttp @@ -31,7 +30,7 @@ class _DirectDownloadIter(RequestIter): async def _init( self, file, dc_id, offset, stride, chunk_size, request_size, file_size, msg_data ): - self.request = functions.upload.GetFileRequest( + self.request = _tl.fn.upload.GetFile( file, offset=offset, limit=request_size) self.total = file_size @@ -50,7 +49,7 @@ class _DirectDownloadIter(RequestIter): self._sender = await self.client._borrow_exported_sender(dc_id) except errors.DcIdInvalidError: # Can't export a sender for the ID we are currently in - config = await self.client(functions.help.GetConfigRequest()) + config = await self.client(_tl.fn.help.GetConfig()) for option in config.dc_options: if option.ip_address == self.client.session.server_address: self.client.session.set_dc( @@ -75,7 +74,7 @@ class _DirectDownloadIter(RequestIter): try: result = await self.client._call(self._sender, self.request) self._timed_out = False - if isinstance(result, types.upload.FileCdnRedirect): + if isinstance(result, _tl.upload.FileCdnRedirect): raise NotImplementedError # TODO Implement else: return result.bytes @@ -99,7 +98,7 @@ class _DirectDownloadIter(RequestIter): except errors.FilerefUpgradeNeededError as e: # Only implemented for documents which are the ones that may take that long to download if not self._msg_data \ - or not isinstance(self.request.location, types.InputDocumentFileLocation) \ + or not isinstance(self.request.location, _tl.InputDocumentFileLocation) \ or self.request.location.thumb_size != '': raise @@ -107,7 +106,7 @@ class _DirectDownloadIter(RequestIter): chat, msg_id = self._msg_data msg = await self.client.get_messages(chat, ids=msg_id) - if not isinstance(msg.media, types.MessageMediaDocument): + if not isinstance(msg.media, _tl.MessageMediaDocument): raise document = msg.media.document @@ -200,7 +199,7 @@ async def download_profile_photo( ENTITIES = (0x2da17977, 0xc5af5d94, 0x1f4661b9, 0xd49a2697) # ('InputPeer', 'InputUser', 'InputChannel') INPUTS = (0xc91c90b6, 0xe669bf46, 0x40f202fd) - if not isinstance(entity, TLObject) or entity.SUBCLASS_OF_ID in INPUTS: + if not isinstance(entity, _tl.TLObject) or entity.SUBCLASS_OF_ID in INPUTS: entity = await self.get_entity(entity) thumb = -1 if download_big else 0 @@ -225,9 +224,9 @@ async def download_profile_photo( photo = entity.photo - if isinstance(photo, (types.UserProfilePhoto, types.ChatPhoto)): + if isinstance(photo, (_tl.UserProfilePhoto, _tl.ChatPhoto)): dc_id = photo.dc_id - loc = types.InputPeerPhotoFileLocation( + loc = _tl.InputPeerPhotoFileLocation( peer=await self.get_input_entity(entity), photo_id=photo.photo_id, big=download_big @@ -253,7 +252,7 @@ async def download_profile_photo( ie = await self.get_input_entity(entity) ty = helpers._entity_type(ie) if ty == helpers._EntityType.CHANNEL: - full = await self(functions.channels.GetFullChannelRequest(ie)) + full = await self(_tl.fn.channels.GetFullChannel(ie)) return await self._download_photo( full.full_chat.chat_photo, file, date=None, progress_callback=None, @@ -268,7 +267,7 @@ async def download_media( message: 'hints.MessageLike', file: 'hints.FileLike' = None, *, - thumb: 'typing.Union[int, types.TypePhotoSize]' = None, + thumb: 'typing.Union[int, _tl.TypePhotoSize]' = None, progress_callback: 'hints.ProgressCallback' = None) -> typing.Optional[typing.Union[str, bytes]]: # Downloading large documents may be slow enough to require a new file reference # to be obtained mid-download. Store (input chat, message id) so that the message @@ -276,7 +275,7 @@ async def download_media( msg_data = None # TODO This won't work for messageService - if isinstance(message, types.Message): + if isinstance(message, _tl.Message): date = message.date media = message.media msg_data = (message.input_chat, message.id) if message.input_chat else None @@ -287,28 +286,28 @@ async def download_media( if isinstance(media, str): media = utils.resolve_bot_file_id(media) - if isinstance(media, types.MessageService): + if isinstance(media, _tl.MessageService): if isinstance(message.action, - types.MessageActionChatEditPhoto): + _tl.MessageActionChatEditPhoto): media = media.photo - if isinstance(media, types.MessageMediaWebPage): - if isinstance(media.webpage, types.WebPage): + if isinstance(media, _tl.MessageMediaWebPage): + if isinstance(media.webpage, _tl.WebPage): media = media.webpage.document or media.webpage.photo - if isinstance(media, (types.MessageMediaPhoto, types.Photo)): + if isinstance(media, (_tl.MessageMediaPhoto, _tl.Photo)): return await self._download_photo( media, file, date, thumb, progress_callback ) - elif isinstance(media, (types.MessageMediaDocument, types.Document)): + elif isinstance(media, (_tl.MessageMediaDocument, _tl.Document)): return await self._download_document( media, file, date, thumb, progress_callback, msg_data ) - elif isinstance(media, types.MessageMediaContact) and thumb is None: + elif isinstance(media, _tl.MessageMediaContact) and thumb is None: return self._download_contact( media, file ) - elif isinstance(media, (types.WebDocument, types.WebDocumentNoProxy)) and thumb is None: + elif isinstance(media, (_tl.WebDocument, _tl.WebDocumentNoProxy)) and thumb is None: return await self._download_web_document( media, file, progress_callback ) @@ -488,15 +487,15 @@ def _get_thumb(thumbs, thumb): # last while this is the smallest (layer 116). Ensure we have the # sizes sorted correctly with a custom function. def sort_thumbs(thumb): - if isinstance(thumb, types.PhotoStrippedSize): + if isinstance(thumb, _tl.PhotoStrippedSize): return 1, len(thumb.bytes) - if isinstance(thumb, types.PhotoCachedSize): + if isinstance(thumb, _tl.PhotoCachedSize): return 1, len(thumb.bytes) - if isinstance(thumb, types.PhotoSize): + if isinstance(thumb, _tl.PhotoSize): return 1, thumb.size - if isinstance(thumb, types.PhotoSizeProgressive): + if isinstance(thumb, _tl.PhotoSizeProgressive): return 1, max(thumb.sizes) - if isinstance(thumb, types.VideoSize): + if isinstance(thumb, _tl.VideoSize): return 2, thumb.size # Empty size or invalid should go last @@ -508,7 +507,7 @@ def _get_thumb(thumbs, thumb): # :tl:`PhotoPathSize` is used for animated stickers preview, and the thumb is actually # a SVG path of the outline. Users expect thumbnails to be JPEG files, so pretend this # thumb size doesn't actually exist (#1655). - if isinstance(thumbs[i], types.PhotoPathSize): + if isinstance(thumbs[i], _tl.PhotoPathSize): thumbs.pop(i) if thumb is None: @@ -517,15 +516,15 @@ def _get_thumb(thumbs, thumb): return thumbs[thumb] elif isinstance(thumb, str): return next((t for t in thumbs if t.type == thumb), None) - elif isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize, - types.PhotoStrippedSize, types.VideoSize)): + elif isinstance(thumb, (_tl.PhotoSize, _tl.PhotoCachedSize, + _tl.PhotoStrippedSize, _tl.VideoSize)): return thumb else: return None def _download_cached_photo_size(self: 'TelegramClient', size, file): # No need to download anything, simply write the bytes - if isinstance(size, types.PhotoStrippedSize): + if isinstance(size, _tl.PhotoStrippedSize): data = utils.stripped_photo_to_jpg(size.bytes) else: data = size.bytes @@ -548,31 +547,31 @@ def _download_cached_photo_size(self: 'TelegramClient', size, file): async def _download_photo(self: 'TelegramClient', photo, file, date, thumb, progress_callback): """Specialized version of .download_media() for photos""" # Determine the photo and its largest size - if isinstance(photo, types.MessageMediaPhoto): + if isinstance(photo, _tl.MessageMediaPhoto): photo = photo.photo - if not isinstance(photo, types.Photo): + if not isinstance(photo, _tl.Photo): return # Include video sizes here (but they may be None so provide an empty list) size = self._get_thumb(photo.sizes + (photo.video_sizes or []), thumb) - if not size or isinstance(size, types.PhotoSizeEmpty): + if not size or isinstance(size, _tl.PhotoSizeEmpty): return - if isinstance(size, types.VideoSize): + if isinstance(size, _tl.VideoSize): file = self._get_proper_filename(file, 'video', '.mp4', date=date) else: file = self._get_proper_filename(file, 'photo', '.jpg', date=date) - if isinstance(size, (types.PhotoCachedSize, types.PhotoStrippedSize)): + if isinstance(size, (_tl.PhotoCachedSize, _tl.PhotoStrippedSize)): return self._download_cached_photo_size(size, file) - if isinstance(size, types.PhotoSizeProgressive): + if isinstance(size, _tl.PhotoSizeProgressive): file_size = max(size.sizes) else: file_size = size.size result = await self.download_file( - types.InputPhotoFileLocation( + _tl.InputPhotoFileLocation( id=photo.id, access_hash=photo.access_hash, file_reference=photo.file_reference, @@ -589,10 +588,10 @@ def _get_kind_and_names(attributes): kind = 'document' possible_names = [] for attr in attributes: - if isinstance(attr, types.DocumentAttributeFilename): + if isinstance(attr, _tl.DocumentAttributeFilename): possible_names.insert(0, attr.file_name) - elif isinstance(attr, types.DocumentAttributeAudio): + elif isinstance(attr, _tl.DocumentAttributeAudio): kind = 'audio' if attr.performer and attr.title: possible_names.append('{} - {}'.format( @@ -610,9 +609,9 @@ def _get_kind_and_names(attributes): async def _download_document( self, document, file, date, thumb, progress_callback, msg_data): """Specialized version of .download_media() for documents.""" - if isinstance(document, types.MessageMediaDocument): + if isinstance(document, _tl.MessageMediaDocument): document = document.document - if not isinstance(document, types.Document): + if not isinstance(document, _tl.Document): return if thumb is None: @@ -625,11 +624,11 @@ async def _download_document( else: file = self._get_proper_filename(file, 'photo', '.jpg', date=date) size = self._get_thumb(document.thumbs, thumb) - if isinstance(size, (types.PhotoCachedSize, types.PhotoStrippedSize)): + if isinstance(size, (_tl.PhotoCachedSize, _tl.PhotoStrippedSize)): return self._download_cached_photo_size(size, file) result = await self._download_file( - types.InputDocumentFileLocation( + _tl.InputDocumentFileLocation( id=document.id, access_hash=document.access_hash, file_reference=document.file_reference, diff --git a/telethon/_client/messageparse.py b/telethon/_client/messageparse.py index 0cbdb40c..d68ab38f 100644 --- a/telethon/_client/messageparse.py +++ b/telethon/_client/messageparse.py @@ -2,8 +2,7 @@ import itertools import re import typing -from .. import helpers, utils -from ..tl import types +from .. import helpers, utils, _tl if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -25,7 +24,7 @@ async def _replace_with_mention(self: 'TelegramClient', entities, i, user): or do nothing if it can't be found. """ try: - entities[i] = types.InputMessageEntityMentionName( + entities[i] = _tl.InputMessageEntityMentionName( entities[i].offset, entities[i].length, await self.get_input_entity(user) ) @@ -52,15 +51,15 @@ async def _parse_message_text(self: 'TelegramClient', message, parse_mode): for i in reversed(range(len(msg_entities))): e = msg_entities[i] - if isinstance(e, types.MessageEntityTextUrl): + if isinstance(e, _tl.MessageEntityTextUrl): m = re.match(r'^@|\+|tg://user\?id=(\d+)', e.url) if m: user = int(m.group(1)) if m.group(1) else e.url is_mention = await self._replace_with_mention(msg_entities, i, user) if not is_mention: del msg_entities[i] - elif isinstance(e, (types.MessageEntityMentionName, - types.InputMessageEntityMentionName)): + elif isinstance(e, (_tl.MessageEntityMentionName, + _tl.InputMessageEntityMentionName)): is_mention = await self._replace_with_mention(msg_entities, i, e.user_id) if not is_mention: del msg_entities[i] @@ -76,10 +75,10 @@ def _get_response_message(self: 'TelegramClient', request, result, input_chat): If ``request.random_id`` is a list, this method returns a list too. """ - if isinstance(result, types.UpdateShort): + if isinstance(result, _tl.UpdateShort): updates = [result.update] entities = {} - elif isinstance(result, (types.Updates, types.UpdatesCombined)): + elif isinstance(result, (_tl.Updates, _tl.UpdatesCombined)): updates = result.updates entities = {utils.get_peer_id(x): x for x in @@ -90,11 +89,11 @@ def _get_response_message(self: 'TelegramClient', request, result, input_chat): random_to_id = {} id_to_message = {} for update in updates: - if isinstance(update, types.UpdateMessageID): + if isinstance(update, _tl.UpdateMessageID): random_to_id[update.random_id] = update.id elif isinstance(update, ( - types.UpdateNewChannelMessage, types.UpdateNewMessage)): + _tl.UpdateNewChannelMessage, _tl.UpdateNewMessage)): update.message._finish_init(self, entities, input_chat) # Pinning a message with `updatePinnedMessage` seems to @@ -109,7 +108,7 @@ def _get_response_message(self: 'TelegramClient', request, result, input_chat): else: return update.message - elif (isinstance(update, types.UpdateEditMessage) + elif (isinstance(update, _tl.UpdateEditMessage) and helpers._entity_type(request.peer) != helpers._EntityType.CHANNEL): update.message._finish_init(self, entities, input_chat) @@ -120,26 +119,26 @@ def _get_response_message(self: 'TelegramClient', request, result, input_chat): elif request.id == update.message.id: return update.message - elif (isinstance(update, types.UpdateEditChannelMessage) + elif (isinstance(update, _tl.UpdateEditChannelMessage) and utils.get_peer_id(request.peer) == utils.get_peer_id(update.message.peer_id)): if request.id == update.message.id: update.message._finish_init(self, entities, input_chat) return update.message - elif isinstance(update, types.UpdateNewScheduledMessage): + elif isinstance(update, _tl.UpdateNewScheduledMessage): update.message._finish_init(self, entities, input_chat) # Scheduled IDs may collide with normal IDs. However, for a # single request there *shouldn't* be a mix between "some # scheduled and some not". id_to_message[update.message.id] = update.message - elif isinstance(update, types.UpdateMessagePoll): + elif isinstance(update, _tl.UpdateMessagePoll): if request.media.poll.id == update.poll_id: - m = types.Message( + m = _tl.Message( id=request.id, peer_id=utils.get_peer(request.peer), - media=types.MessageMediaPoll( + media=_tl.MessageMediaPoll( poll=update.poll, results=update.results ) diff --git a/telethon/_client/messages.py b/telethon/_client/messages.py index 1dde08ec..ba88c665 100644 --- a/telethon/_client/messages.py +++ b/telethon/_client/messages.py @@ -3,9 +3,8 @@ import itertools import typing import warnings -from .. import helpers, utils, errors, hints +from .. import helpers, utils, errors, hints, _tl from ..requestiter import RequestIter -from ..tl import types, functions _MAX_CHUNK_SIZE = 100 @@ -67,31 +66,31 @@ class _MessagesIter(RequestIter): # If we want to perform global a search with `from_user` we have to perform # a normal `messages.search`, *but* we can make the entity be `inputPeerEmpty`. if not self.entity and from_user: - self.entity = types.InputPeerEmpty() + self.entity = _tl.InputPeerEmpty() if filter is None: - filter = types.InputMessagesFilterEmpty() + filter = _tl.InputMessagesFilterEmpty() else: filter = filter() if isinstance(filter, type) else filter if not self.entity: - self.request = functions.messages.SearchGlobalRequest( + self.request = _tl.fn.messages.SearchGlobal( q=search or '', filter=filter, min_date=None, max_date=offset_date, offset_rate=0, - offset_peer=types.InputPeerEmpty(), + offset_peer=_tl.InputPeerEmpty(), offset_id=offset_id, limit=1 ) elif scheduled: - self.request = functions.messages.GetScheduledHistoryRequest( + self.request = _tl.fn.messages.GetScheduledHistory( peer=entity, hash=0 ) elif reply_to is not None: - self.request = functions.messages.GetRepliesRequest( + self.request = _tl.fn.messages.GetReplies( peer=self.entity, msg_id=reply_to, offset_id=offset_id, @@ -102,7 +101,7 @@ class _MessagesIter(RequestIter): min_id=0, hash=0 ) - elif search is not None or not isinstance(filter, types.InputMessagesFilterEmpty) or from_user: + elif search is not None or not isinstance(filter, _tl.InputMessagesFilterEmpty) or from_user: # Telegram completely ignores `from_id` in private chats ty = helpers._entity_type(self.entity) if ty == helpers._EntityType.USER: @@ -114,7 +113,7 @@ class _MessagesIter(RequestIter): # and set `from_id` to None to avoid checking it locally. self.from_id = None - self.request = functions.messages.SearchRequest( + self.request = _tl.fn.messages.Search( peer=self.entity, q=search or '', filter=filter, @@ -136,13 +135,13 @@ class _MessagesIter(RequestIter): # # Even better, using `filter` and `from_id` seems to always # trigger `RPC_CALL_FAIL` which is "internal issues"... - if not isinstance(filter, types.InputMessagesFilterEmpty) \ + if not isinstance(filter, _tl.InputMessagesFilterEmpty) \ and offset_date and not search and not offset_id: async for m in self.client.iter_messages( self.entity, 1, offset_date=offset_date): self.request.offset_id = m.id + 1 else: - self.request = functions.messages.GetHistoryRequest( + self.request = _tl.fn.messages.GetHistory( peer=self.entity, limit=1, offset_date=offset_date, @@ -156,7 +155,7 @@ class _MessagesIter(RequestIter): if self.limit <= 0: # No messages, but we still need to know the total message count result = await self.client(self.request) - if isinstance(result, types.messages.MessagesNotModified): + if isinstance(result, _tl.messages.MessagesNotModified): self.total = result.count else: self.total = getattr(result, 'count', len(result.messages)) @@ -189,7 +188,7 @@ class _MessagesIter(RequestIter): messages = reversed(r.messages) if self.reverse else r.messages for message in messages: - if (isinstance(message, types.MessageEmpty) + if (isinstance(message, _tl.MessageEmpty) or self.from_id and message.sender_id != self.from_id): continue @@ -245,7 +244,7 @@ class _MessagesIter(RequestIter): # We want to skip the one we already have self.request.offset_id += 1 - if isinstance(self.request, functions.messages.SearchRequest): + if isinstance(self.request, _tl.fn.messages.SearchRequest): # Unlike getHistory and searchGlobal that use *offset* date, # this is *max* date. This means that doing a search in reverse # will break it. Since it's not really needed once we're going @@ -255,11 +254,11 @@ class _MessagesIter(RequestIter): # getHistory, searchGlobal and getReplies call it offset_date self.request.offset_date = last_message.date - if isinstance(self.request, functions.messages.SearchGlobalRequest): + if isinstance(self.request, _tl.fn.messages.SearchGlobalRequest): if last_message.input_chat: self.request.offset_peer = last_message.input_chat else: - self.request.offset_peer = types.InputPeerEmpty() + self.request.offset_peer = _tl.InputPeerEmpty() self.request.offset_rate = getattr(response, 'next_rate', 0) @@ -287,16 +286,16 @@ class _IDsIter(RequestIter): if self._ty == helpers._EntityType.CHANNEL: try: r = await self.client( - functions.channels.GetMessagesRequest(self._entity, ids)) + _tl.fn.channels.GetMessages(self._entity, ids)) except errors.MessageIdsEmptyError: # All IDs were invalid, use a dummy result - r = types.messages.MessagesNotModified(len(ids)) + r = _tl.messages.MessagesNotModified(len(ids)) else: - r = await self.client(functions.messages.GetMessagesRequest(ids)) + r = await self.client(_tl.fn.messages.GetMessages(ids)) if self._entity: from_id = await self.client._get_peer(self._entity) - if isinstance(r, types.messages.MessagesNotModified): + if isinstance(r, _tl.messages.MessagesNotModified): self.buffer.extend(None for _ in ids) return @@ -312,7 +311,7 @@ class _IDsIter(RequestIter): # since the user can enter arbitrary numbers which can belong to # arbitrary chats. Validate these unless ``from_id is None``. for message in r.messages: - if isinstance(message, types.MessageEmpty) or ( + if isinstance(message, _tl.MessageEmpty) or ( from_id and message.peer_id != from_id): self.buffer.append(None) else: @@ -331,7 +330,7 @@ def iter_messages( min_id: int = 0, add_offset: int = 0, search: str = None, - filter: 'typing.Union[types.TypeMessagesFilter, typing.Type[types.TypeMessagesFilter]]' = None, + filter: 'typing.Union[_tl.TypeMessagesFilter, typing.Type[_tl.TypeMessagesFilter]]' = None, from_user: 'hints.EntityLike' = None, wait_time: float = None, ids: 'typing.Union[int, typing.Sequence[int]]' = None, @@ -393,9 +392,9 @@ async def get_messages(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalL async def _get_comment_data( self: 'TelegramClient', entity: 'hints.EntityLike', - message: 'typing.Union[int, types.Message]' + message: 'typing.Union[int, _tl.Message]' ): - r = await self(functions.messages.GetDiscussionMessageRequest( + r = await self(_tl.fn.messages.GetDiscussionMessage( peer=entity, msg_id=utils.get_message_id(message) )) @@ -408,10 +407,10 @@ async def send_message( entity: 'hints.EntityLike', message: 'hints.MessageLike' = '', *, - reply_to: 'typing.Union[int, types.Message]' = None, - attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None, + reply_to: 'typing.Union[int, _tl.Message]' = None, + attributes: 'typing.Sequence[_tl.TypeDocumentAttribute]' = None, parse_mode: typing.Optional[str] = (), - formatting_entities: typing.Optional[typing.List[types.TypeMessageEntity]] = None, + formatting_entities: typing.Optional[typing.List[_tl.TypeMessageEntity]] = None, link_preview: bool = True, file: 'typing.Union[hints.FileLike, typing.Sequence[hints.FileLike]]' = None, thumb: 'hints.FileLike' = None, @@ -422,8 +421,8 @@ async def send_message( background: bool = None, supports_streaming: bool = False, schedule: 'hints.DateLike' = None, - comment_to: 'typing.Union[int, types.Message]' = None -) -> 'types.Message': + comment_to: 'typing.Union[int, _tl.Message]' = None +) -> '_tl.Message': if file is not None: return await self.send_file( entity, file, caption=message, reply_to=reply_to, @@ -439,7 +438,7 @@ async def send_message( if comment_to is not None: entity, reply_to = await self._get_comment_data(entity, comment_to) - if isinstance(message, types.Message): + if isinstance(message, _tl.Message): if buttons is None: markup = message.reply_markup else: @@ -449,7 +448,7 @@ async def send_message( silent = message.silent if (message.media and not isinstance( - message.media, types.MessageMediaWebPage)): + message.media, _tl.MessageMediaWebPage)): return await self.send_file( entity, message.media, @@ -462,7 +461,7 @@ async def send_message( schedule=schedule ) - request = functions.messages.SendMessageRequest( + request = _tl.fn.messages.SendMessage( peer=entity, message=message.message or '', silent=silent, @@ -472,7 +471,7 @@ async def send_message( entities=message.entities, clear_draft=clear_draft, no_webpage=not isinstance( - message.media, types.MessageMediaWebPage), + message.media, _tl.MessageMediaWebPage), schedule_date=schedule ) message = message.message @@ -484,7 +483,7 @@ async def send_message( 'The message cannot be empty unless a file is provided' ) - request = functions.messages.SendMessageRequest( + request = _tl.fn.messages.SendMessage( peer=entity, message=message, entities=formatting_entities, @@ -498,8 +497,8 @@ async def send_message( ) result = await self(request) - if isinstance(result, types.UpdateShortSentMessage): - message = types.Message( + if isinstance(result, _tl.UpdateShortSentMessage): + message = _tl.Message( id=result.id, peer_id=await self._get_peer(entity), message=message, @@ -526,7 +525,7 @@ async def forward_messages( silent: bool = None, as_album: bool = None, schedule: 'hints.DateLike' = None -) -> 'typing.Sequence[types.Message]': +) -> 'typing.Sequence[_tl.Message]': if as_album is not None: warnings.warn('the as_album argument is deprecated and no longer has any effect') @@ -548,7 +547,7 @@ async def forward_messages( return from_peer_id raise ValueError('from_peer must be given if integer IDs are used') - elif isinstance(m, types.Message): + elif isinstance(m, _tl.Message): return m.chat_id else: raise TypeError('Cannot forward messages of type {}'.format(type(m))) @@ -562,7 +561,7 @@ async def forward_messages( chat = await chunk[0].get_input_chat() chunk = [m.id for m in chunk] - req = functions.messages.ForwardMessagesRequest( + req = _tl.fn.messages.ForwardMessages( from_peer=chat, id=chunk, to_peer=entity, @@ -578,13 +577,13 @@ async def forward_messages( async def edit_message( self: 'TelegramClient', - entity: 'typing.Union[hints.EntityLike, types.Message]', + entity: 'typing.Union[hints.EntityLike, _tl.Message]', message: 'hints.MessageLike' = None, text: str = None, *, parse_mode: str = (), - attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None, - formatting_entities: typing.Optional[typing.List[types.TypeMessageEntity]] = None, + attributes: 'typing.Sequence[_tl.TypeDocumentAttribute]' = None, + formatting_entities: typing.Optional[typing.List[_tl.TypeMessageEntity]] = None, link_preview: bool = True, file: 'hints.FileLike' = None, thumb: 'hints.FileLike' = None, @@ -592,11 +591,11 @@ async def edit_message( buttons: 'hints.MarkupLike' = None, supports_streaming: bool = False, schedule: 'hints.DateLike' = None -) -> 'types.Message': - if isinstance(entity, types.InputBotInlineMessageID): +) -> '_tl.Message': + if isinstance(entity, _tl.InputBotInlineMessageID): text = text or message message = entity - elif isinstance(entity, types.Message): + elif isinstance(entity, _tl.Message): text = message # Shift the parameters to the right message = entity entity = entity.peer_id @@ -609,8 +608,8 @@ async def edit_message( attributes=attributes, force_document=force_document) - if isinstance(entity, types.InputBotInlineMessageID): - request = functions.messages.EditInlineBotMessageRequest( + if isinstance(entity, _tl.InputBotInlineMessageID): + request = _tl.fn.messages.EditInlineBotMessage( id=entity, message=text, no_webpage=not link_preview, @@ -631,7 +630,7 @@ async def edit_message( return await self(request) entity = await self.get_input_entity(entity) - request = functions.messages.EditMessageRequest( + request = _tl.fn.messages.EditMessage( peer=entity, id=utils.get_message_id(message), message=text, @@ -649,13 +648,13 @@ async def delete_messages( entity: 'hints.EntityLike', message_ids: 'typing.Union[hints.MessageIDLike, typing.Sequence[hints.MessageIDLike]]', *, - revoke: bool = True) -> 'typing.Sequence[types.messages.AffectedMessages]': + revoke: bool = True) -> 'typing.Sequence[_tl.messages.AffectedMessages]': if not utils.is_list_like(message_ids): message_ids = (message_ids,) message_ids = ( m.id if isinstance(m, ( - types.Message, types.MessageService, types.MessageEmpty)) + _tl.Message, _tl.MessageService, _tl.MessageEmpty)) else int(m) for m in message_ids ) @@ -667,10 +666,10 @@ async def delete_messages( ty = helpers._EntityType.USER if ty == helpers._EntityType.CHANNEL: - return await self([functions.channels.DeleteMessagesRequest( + return await self([_tl.fn.channels.DeleteMessages( entity, list(c)) for c in utils.chunks(message_ids)]) else: - return await self([functions.messages.DeleteMessagesRequest( + return await self([_tl.fn.messages.DeleteMessages( list(c), revoke) for c in utils.chunks(message_ids)]) async def send_read_acknowledge( @@ -691,16 +690,16 @@ async def send_read_acknowledge( entity = await self.get_input_entity(entity) if clear_mentions: - await self(functions.messages.ReadMentionsRequest(entity)) + await self(_tl.fn.messages.ReadMentions(entity)) if max_id is None: return True if max_id is not None: if helpers._entity_type(entity) == helpers._EntityType.CHANNEL: - return await self(functions.channels.ReadHistoryRequest( + return await self(_tl.fn.channels.ReadHistory( utils.get_input_channel(entity), max_id=max_id)) else: - return await self(functions.messages.ReadHistoryRequest( + return await self(_tl.fn.messages.ReadHistory( entity, max_id=max_id)) return False @@ -728,10 +727,10 @@ async def _pin(self, entity, message, *, unpin, notify=False, pm_oneside=False): message = utils.get_message_id(message) or 0 entity = await self.get_input_entity(entity) if message <= 0: # old behaviour accepted negative IDs to unpin - await self(functions.messages.UnpinAllMessagesRequest(entity)) + await self(_tl.fn.messages.UnpinAllMessages(entity)) return - request = functions.messages.UpdatePinnedMessageRequest( + request = _tl.fn.messages.UpdatePinnedMessage( peer=entity, id=message, silent=not notify, diff --git a/telethon/_client/telegrambaseclient.py b/telethon/_client/telegrambaseclient.py index 256e8e6f..9c5d62d7 100644 --- a/telethon/_client/telegrambaseclient.py +++ b/telethon/_client/telegrambaseclient.py @@ -7,15 +7,13 @@ import platform import time import typing -from .. import version, helpers, __name__ as __base_name__ +from .. import version, helpers, __name__ as __base_name__, _tl from ..crypto import rsa from ..entitycache import EntityCache from ..extensions import markdown from ..network import MTProtoSender, Connection, ConnectionTcpFull, TcpMTProxy from ..sessions import Session, SQLiteSession, MemorySession from ..statecache import StateCache -from ..tl import functions, types -from ..tl.alltlobjects import LAYER DEFAULT_DC_ID = 2 DEFAULT_IPV4_IP = '149.154.167.51' @@ -122,7 +120,7 @@ def init( import warnings warnings.warn( 'The sqlite3 module is not available under this ' - 'Python installation and no custom session ' + 'Python installation and no _ session ' 'instance was given; using MemorySession.\n' 'You will need to re-login every time unless ' 'you use another session storage' @@ -198,7 +196,7 @@ def init( assert isinstance(connection, type) self._connection = connection init_proxy = None if not issubclass(connection, TcpMTProxy) else \ - types.InputClientProxy(*connection.address_info(proxy)) + _tl.InputClientProxy(*connection.address_info(proxy)) # Used on connection. Capture the variables in a lambda since # exporting clients need to create this InvokeWithLayerRequest. @@ -212,7 +210,7 @@ def init( default_device_model = system.machine default_system_version = re.sub(r'-.+','',system.release) - self._init_request = functions.InitConnectionRequest( + self._init_request = _tl.fn.InitConnection( api_id=self.api_id, device_model=device_model or default_device_model or 'Unknown', system_version=system_version or default_system_version or '1.0', @@ -322,10 +320,10 @@ async def connect(self: 'TelegramClient') -> None: self.session.auth_key = self._sender.auth_key self.session.save() - self._init_request.query = functions.help.GetConfigRequest() + self._init_request.query = _tl.fn.help.GetConfig() - await self._sender.send(functions.InvokeWithLayerRequest( - LAYER, self._init_request + await self._sender.send(_tl.fn.InvokeWithLayer( + _tl.alltlobjects.LAYER, self._init_request )) self._updates_handle = self.loop.create_task(self._update_loop()) @@ -339,7 +337,7 @@ async def disconnect(self: 'TelegramClient'): def set_proxy(self: 'TelegramClient', proxy: typing.Union[tuple, dict]): init_proxy = None if not issubclass(self._connection, TcpMTProxy) else \ - types.InputClientProxy(*self._connection.address_info(proxy)) + _tl.InputClientProxy(*self._connection.address_info(proxy)) self._init_request.proxy = init_proxy self._proxy = proxy @@ -386,7 +384,7 @@ async def _disconnect_coro(self: 'TelegramClient'): pts, date = self._state_cache[None] if pts and date: - self.session.set_update_state(0, types.updates.State( + self.session.set_update_state(0, _tl.updates.State( pts=pts, qts=0, date=date, @@ -436,10 +434,10 @@ async def _get_dc(self: 'TelegramClient', dc_id, cdn=False): """Gets the Data Center (DC) associated to 'dc_id'""" cls = self.__class__ if not cls._config: - cls._config = await self(functions.help.GetConfigRequest()) + cls._config = await self(_tl.fn.help.GetConfig()) if cdn and not self._cdn_config: - cls._cdn_config = await self(functions.help.GetCdnConfigRequest()) + cls._cdn_config = await self(_tl.fn.help.GetCdnConfig()) for pk in cls._cdn_config.public_keys: rsa.add_key(pk.public_key) @@ -481,9 +479,9 @@ async def _create_exported_sender(self: 'TelegramClient', dc_id): local_addr=self._local_addr )) self._log[__name__].info('Exporting auth for new borrowed sender in %s', dc) - auth = await self(functions.auth.ExportAuthorizationRequest(dc_id)) - self._init_request.query = functions.auth.ImportAuthorizationRequest(id=auth.id, bytes=auth.bytes) - req = functions.InvokeWithLayerRequest(LAYER, self._init_request) + auth = await self(_tl.fn.auth.ExportAuthorization(dc_id)) + self._init_request.query = _tl.fn.auth.ImportAuthorization(id=auth.id, bytes=auth.bytes) + req = _tl.fn.InvokeWithLayer(LAYER, self._init_request) await sender.send(req) return sender diff --git a/telethon/_client/telegramclient.py b/telethon/_client/telegramclient.py index 01cd5d14..d3935273 100644 --- a/telethon/_client/telegramclient.py +++ b/telethon/_client/telegramclient.py @@ -8,8 +8,7 @@ from . import ( account, auth, bots, buttons, chats, dialogs, downloads, messageparse, messages, telegrambaseclient, updates, uploads, users ) -from .. import helpers, version -from ..tl import types, custom +from .. import helpers, version, _tl from ..network import ConnectionTcpFull from ..events.common import EventBuilder, EventCommon @@ -369,7 +368,7 @@ class TelegramClient: *, password: str = None, bot_token: str = None, - phone_code_hash: str = None) -> 'typing.Union[types.User, types.auth.SentCode]': + phone_code_hash: str = None) -> 'typing.Union[_tl.User, _tl.auth.SentCode]': """ Logs in to Telegram to an existing user or bot account. @@ -428,7 +427,7 @@ class TelegramClient: last_name: str = '', *, phone: str = None, - phone_code_hash: str = None) -> 'types.User': + phone_code_hash: str = None) -> '_tl.User': """ Signs up to Telegram as a new user account. @@ -477,7 +476,7 @@ class TelegramClient: self: 'TelegramClient', phone: str, *, - force_sms: bool = False) -> 'types.auth.SentCode': + force_sms: bool = False) -> '_tl.auth.SentCode': """ Sends the Telegram code needed to login to the given phone number. @@ -630,7 +629,7 @@ class TelegramClient: *, entity: 'hints.EntityLike' = None, offset: str = None, - geo_point: 'types.GeoPoint' = None) -> custom.InlineResults: + geo_point: '_tl.GeoPoint' = None) -> custom.InlineResults: """ Makes an inline query to the specified bot (``@vote New Poll``). @@ -679,7 +678,7 @@ class TelegramClient: @staticmethod def build_reply_markup( buttons: 'typing.Optional[hints.MarkupLike]', - inline_only: bool = False) -> 'typing.Optional[types.TypeReplyMarkup]': + inline_only: bool = False) -> 'typing.Optional[_tl.TypeReplyMarkup]': """ Builds a :tl:`ReplyInlineMarkup` or :tl:`ReplyKeyboardMarkup` for the given buttons. @@ -722,7 +721,7 @@ class TelegramClient: limit: float = None, *, search: str = '', - filter: 'types.TypeChannelParticipantsFilter' = None, + filter: '_tl.TypeChannelParticipantsFilter' = None, aggressive: bool = False) -> chats._ParticipantsIter: """ Iterator over the participants belonging to the specified chat. @@ -1018,7 +1017,7 @@ class TelegramClient: def action( self: 'TelegramClient', entity: 'hints.EntityLike', - action: 'typing.Union[str, types.TypeSendMessageAction]', + action: 'typing.Union[str, _tl.TypeSendMessageAction]', *, delay: float = 4, auto_cancel: bool = True) -> 'typing.Union[_ChatAction, typing.Coroutine]': @@ -1109,7 +1108,7 @@ class TelegramClient: manage_call: bool = None, anonymous: bool = None, is_admin: bool = None, - title: str = None) -> types.Updates: + title: str = None) -> _tl.Updates: """ Edits admin permissions for someone in a chat. @@ -1216,7 +1215,7 @@ class TelegramClient: send_polls: bool = True, change_info: bool = True, invite_users: bool = True, - pin_messages: bool = True) -> types.Updates: + pin_messages: bool = True) -> _tl.Updates: """ Edits user restrictions in a chat. @@ -1396,7 +1395,7 @@ class TelegramClient: async def get_stats( self: 'TelegramClient', entity: 'hints.EntityLike', - message: 'typing.Union[int, types.Message]' = None, + message: 'typing.Union[int, _tl.Message]' = None, ): """ Retrieves statistics from the given megagroup or broadcast channel. @@ -1449,7 +1448,7 @@ class TelegramClient: *, offset_date: 'hints.DateLike' = None, offset_id: int = 0, - offset_peer: 'hints.EntityLike' = types.InputPeerEmpty(), + offset_peer: 'hints.EntityLike' = _tl.InputPeerEmpty(), ignore_pinned: bool = False, ignore_migrated: bool = False, folder: int = None, @@ -1604,7 +1603,7 @@ class TelegramClient: folder: typing.Union[int, typing.Sequence[int]] = None, *, unpack=None - ) -> types.Updates: + ) -> _tl.Updates: """ Edits the folder used by one or more dialogs to archive them. @@ -1756,7 +1755,7 @@ class TelegramClient: message: 'hints.MessageLike', file: 'hints.FileLike' = None, *, - thumb: 'typing.Union[int, types.TypePhotoSize]' = None, + thumb: 'typing.Union[int, _tl.TypePhotoSize]' = None, progress_callback: 'hints.ProgressCallback' = None) -> typing.Optional[typing.Union[str, bytes]]: """ Downloads the given media from a message object. @@ -1850,7 +1849,7 @@ class TelegramClient: input_location (:tl:`InputFileLocation`): The file location from which the file will be downloaded. See `telethon.utils.get_input_location` source for a complete - list of supported types. + list of supported _tl. file (`str` | `file`, optional): The output file path, directory, or stream-like object. @@ -2048,7 +2047,7 @@ class TelegramClient: min_id: int = 0, add_offset: int = 0, search: str = None, - filter: 'typing.Union[types.TypeMessagesFilter, typing.Type[types.TypeMessagesFilter]]' = None, + filter: 'typing.Union[_tl.TypeMessagesFilter, typing.Type[_tl.TypeMessagesFilter]]' = None, from_user: 'hints.EntityLike' = None, wait_time: float = None, ids: 'typing.Union[int, typing.Sequence[int]]' = None, @@ -2258,10 +2257,10 @@ class TelegramClient: entity: 'hints.EntityLike', message: 'hints.MessageLike' = '', *, - reply_to: 'typing.Union[int, types.Message]' = None, - attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None, + reply_to: 'typing.Union[int, _tl.Message]' = None, + attributes: 'typing.Sequence[_tl.TypeDocumentAttribute]' = None, parse_mode: typing.Optional[str] = (), - formatting_entities: typing.Optional[typing.List[types.TypeMessageEntity]] = None, + formatting_entities: typing.Optional[typing.List[_tl.TypeMessageEntity]] = None, link_preview: bool = True, file: 'typing.Union[hints.FileLike, typing.Sequence[hints.FileLike]]' = None, thumb: 'hints.FileLike' = None, @@ -2272,8 +2271,8 @@ class TelegramClient: background: bool = None, supports_streaming: bool = False, schedule: 'hints.DateLike' = None, - comment_to: 'typing.Union[int, types.Message]' = None - ) -> 'types.Message': + comment_to: 'typing.Union[int, _tl.Message]' = None + ) -> '_tl.Message': """ Sends a message to the specified user, chat or channel. @@ -2458,7 +2457,7 @@ class TelegramClient: silent: bool = None, as_album: bool = None, schedule: 'hints.DateLike' = None - ) -> 'typing.Sequence[types.Message]': + ) -> 'typing.Sequence[_tl.Message]': """ Forwards the given messages to the specified entity. @@ -2531,13 +2530,13 @@ class TelegramClient: async def edit_message( self: 'TelegramClient', - entity: 'typing.Union[hints.EntityLike, types.Message]', + entity: 'typing.Union[hints.EntityLike, _tl.Message]', message: 'hints.MessageLike' = None, text: str = None, *, parse_mode: str = (), - attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None, - formatting_entities: typing.Optional[typing.List[types.TypeMessageEntity]] = None, + attributes: 'typing.Sequence[_tl.TypeDocumentAttribute]' = None, + formatting_entities: typing.Optional[typing.List[_tl.TypeMessageEntity]] = None, link_preview: bool = True, file: 'hints.FileLike' = None, thumb: 'hints.FileLike' = None, @@ -2545,7 +2544,7 @@ class TelegramClient: buttons: 'hints.MarkupLike' = None, supports_streaming: bool = False, schedule: 'hints.DateLike' = None - ) -> 'types.Message': + ) -> '_tl.Message': """ Edits the given message to change its text or media. @@ -2663,7 +2662,7 @@ class TelegramClient: entity: 'hints.EntityLike', message_ids: 'typing.Union[hints.MessageIDLike, typing.Sequence[hints.MessageIDLike]]', *, - revoke: bool = True) -> 'typing.Sequence[types.messages.AffectedMessages]': + revoke: bool = True) -> 'typing.Sequence[_tl.messages.AffectedMessages]': """ Deletes the given messages, optionally "for everyone". @@ -3171,11 +3170,11 @@ class TelegramClient: clear_draft: bool = False, progress_callback: 'hints.ProgressCallback' = None, reply_to: 'hints.MessageIDLike' = None, - attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None, + attributes: 'typing.Sequence[_tl.TypeDocumentAttribute]' = None, thumb: 'hints.FileLike' = None, allow_cache: bool = True, parse_mode: str = (), - formatting_entities: typing.Optional[typing.List[types.TypeMessageEntity]] = None, + formatting_entities: typing.Optional[typing.List[_tl.TypeMessageEntity]] = None, voice_note: bool = False, video_note: bool = False, buttons: 'hints.MarkupLike' = None, @@ -3183,9 +3182,9 @@ class TelegramClient: background: bool = None, supports_streaming: bool = False, schedule: 'hints.DateLike' = None, - comment_to: 'typing.Union[int, types.Message]' = None, + comment_to: 'typing.Union[int, _tl.Message]' = None, ttl: int = None, - **kwargs) -> 'types.Message': + **kwargs) -> '_tl.Message': """ Sends message with the given file to the specified entity. @@ -3392,11 +3391,11 @@ class TelegramClient: # Dices, including dart and other future emoji from telethon.tl import types - await client.send_file(chat, types.InputMediaDice('')) - await client.send_file(chat, types.InputMediaDice('🎯')) + await client.send_file(chat, _tl.InputMediaDice('')) + await client.send_file(chat, _tl.InputMediaDice('🎯')) # Contacts - await client.send_file(chat, types.InputMediaContact( + await client.send_file(chat, _tl.InputMediaContact( phone_number='+34 123 456 789', first_name='Example', last_name='', @@ -3415,7 +3414,7 @@ class TelegramClient: use_cache: type = None, key: bytes = None, iv: bytes = None, - progress_callback: 'hints.ProgressCallback' = None) -> 'types.TypeInputFile': + progress_callback: 'hints.ProgressCallback' = None) -> '_tl.TypeInputFile': """ Uploads a file to Telegram's servers, without sending it. @@ -3522,7 +3521,7 @@ class TelegramClient: return users.call(self._sender, request, ordered=ordered) async def get_me(self: 'TelegramClient', input_peer: bool = False) \ - -> 'typing.Union[types.User, types.InputPeerUser]': + -> 'typing.Union[_tl.User, _tl.InputPeerUser]': """ Gets "me", the current :tl:`User` who is logged in. @@ -3633,7 +3632,7 @@ class TelegramClient: async def get_input_entity( self: 'TelegramClient', - peer: 'hints.EntityLike') -> 'types.TypeInputPeer': + peer: 'hints.EntityLike') -> '_tl.TypeInputPeer': """ Turns the given entity into its input entity version. diff --git a/telethon/_client/updates.py b/telethon/_client/updates.py index 89816530..e8c5709c 100644 --- a/telethon/_client/updates.py +++ b/telethon/_client/updates.py @@ -8,9 +8,8 @@ import traceback import typing import logging -from .. import events, utils, errors +from .. import events, utils, errors, _tl from ..events.common import EventBuilder, EventCommon -from ..tl import types, functions if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -22,7 +21,7 @@ Callback = typing.Callable[[typing.Any], typing.Any] async def _run_until_disconnected(self: 'TelegramClient'): try: # Make a high-level request to notify that we want updates - await self(functions.updates.GetStateRequest()) + await self(_tl.fn.updates.GetState()) return await self.disconnected except KeyboardInterrupt: pass @@ -32,7 +31,7 @@ async def _run_until_disconnected(self: 'TelegramClient'): async def set_receive_updates(self: 'TelegramClient', receive_updates): self._no_updates = not receive_updates if receive_updates: - await self(functions.updates.GetStateRequest()) + await self(_tl.fn.updates.GetState()) async def run_until_disconnected(self: 'TelegramClient'): return await self._run_until_disconnected() @@ -91,24 +90,24 @@ async def catch_up(self: 'TelegramClient'): self.session.catching_up = True try: while True: - d = await self(functions.updates.GetDifferenceRequest( + d = await self(_tl.fn.updates.GetDifference( pts, date, 0 )) - if isinstance(d, (types.updates.DifferenceSlice, - types.updates.Difference)): - if isinstance(d, types.updates.Difference): + if isinstance(d, (_tl.updates.DifferenceSlice, + _tl.updates.Difference)): + if isinstance(d, _tl.updates.Difference): state = d.state else: state = d.intermediate_state pts, date = state.pts, state.date - self._handle_update(types.Updates( + self._handle_update(_tl.Updates( users=d.users, chats=d.chats, date=state.date, seq=state.seq, updates=d.other_updates + [ - types.UpdateNewMessage(m, 0, 0) + _tl.UpdateNewMessage(m, 0, 0) for m in d.new_messages ] )) @@ -128,9 +127,9 @@ async def catch_up(self: 'TelegramClient'): # some). This can be used to detect collisions (i.e. # it would return an update we have already seen). else: - if isinstance(d, types.updates.DifferenceEmpty): + if isinstance(d, _tl.updates.DifferenceEmpty): date = d.date - elif isinstance(d, types.updates.DifferenceTooLong): + elif isinstance(d, _tl.updates.DifferenceTooLong): pts = d.pts break except (ConnectionError, asyncio.CancelledError): @@ -148,12 +147,12 @@ def _handle_update(self: 'TelegramClient', update): self.session.process_entities(update) self._entity_cache.add(update) - if isinstance(update, (types.Updates, types.UpdatesCombined)): + if isinstance(update, (_tl.Updates, _tl.UpdatesCombined)): entities = {utils.get_peer_id(x): x for x in itertools.chain(update.users, update.chats)} for u in update.updates: self._process_update(u, update.updates, entities=entities) - elif isinstance(update, types.UpdateShort): + elif isinstance(update, _tl.UpdateShort): self._process_update(update.update, None) else: self._process_update(update, None) @@ -230,7 +229,7 @@ async def _update_loop(self: 'TelegramClient'): continue try: - await self(functions.updates.GetStateRequest()) + await self(_tl.fn.updates.GetState()) except (ConnectionError, asyncio.CancelledError): return @@ -359,7 +358,7 @@ async def _get_difference(self: 'TelegramClient', update, channel_id, pts_date): assert isinstance(channel_id, int), 'channel_id was {}, not int in {}'.format(type(channel_id), update) try: # Wrap the ID inside a peer to ensure we get a channel back. - where = await self.get_input_entity(types.PeerChannel(channel_id)) + where = await self.get_input_entity(_tl.PeerChannel(channel_id)) except ValueError: # There's a high chance that this fails, since # we are getting the difference to fetch entities. @@ -367,15 +366,15 @@ async def _get_difference(self: 'TelegramClient', update, channel_id, pts_date): if not pts_date: # First-time, can't get difference. Get pts instead. - result = await self(functions.channels.GetFullChannelRequest( + result = await self(_tl.fn.channels.GetFullChannel( utils.get_input_channel(where) )) self._state_cache[channel_id] = result.full_chat.pts return - result = await self(functions.updates.GetChannelDifferenceRequest( + result = await self(_tl.fn.updates.GetChannelDifference( channel=where, - filter=types.ChannelMessagesFilterEmpty(), + filter=_tl.ChannelMessagesFilterEmpty(), pts=pts_date, # just pts limit=100, force=True @@ -383,20 +382,20 @@ async def _get_difference(self: 'TelegramClient', update, channel_id, pts_date): else: if not pts_date[0]: # First-time, can't get difference. Get pts instead. - result = await self(functions.updates.GetStateRequest()) + result = await self(_tl.fn.updates.GetState()) self._state_cache[None] = result.pts, result.date return - result = await self(functions.updates.GetDifferenceRequest( + result = await self(_tl.fn.updates.GetDifference( pts=pts_date[0], date=pts_date[1], qts=0 )) - if isinstance(result, (types.updates.Difference, - types.updates.DifferenceSlice, - types.updates.ChannelDifference, - types.updates.ChannelDifferenceTooLong)): + if isinstance(result, (_tl.updates.Difference, + _tl.updates.DifferenceSlice, + _tl.updates.ChannelDifference, + _tl.updates.ChannelDifferenceTooLong)): update._entities.update({ utils.get_peer_id(x): x for x in itertools.chain(result.users, result.chats) diff --git a/telethon/_client/uploads.py b/telethon/_client/uploads.py index 58f69bad..6c0d8146 100644 --- a/telethon/_client/uploads.py +++ b/telethon/_client/uploads.py @@ -9,8 +9,7 @@ from io import BytesIO from ..crypto import AES -from .. import utils, helpers, hints -from ..tl import types, functions, custom +from .. import utils, helpers, hints, _tl try: import PIL @@ -99,11 +98,11 @@ async def send_file( clear_draft: bool = False, progress_callback: 'hints.ProgressCallback' = None, reply_to: 'hints.MessageIDLike' = None, - attributes: 'typing.Sequence[types.TypeDocumentAttribute]' = None, + attributes: 'typing.Sequence[_tl.TypeDocumentAttribute]' = None, thumb: 'hints.FileLike' = None, allow_cache: bool = True, parse_mode: str = (), - formatting_entities: typing.Optional[typing.List[types.TypeMessageEntity]] = None, + formatting_entities: typing.Optional[typing.List[_tl.TypeMessageEntity]] = None, voice_note: bool = False, video_note: bool = False, buttons: 'hints.MarkupLike' = None, @@ -111,9 +110,9 @@ async def send_file( background: bool = None, supports_streaming: bool = False, schedule: 'hints.DateLike' = None, - comment_to: 'typing.Union[int, types.Message]' = None, + comment_to: 'typing.Union[int, _tl.Message]' = None, ttl: int = None, - **kwargs) -> 'types.Message': + **kwargs) -> '_tl.Message': # TODO Properly implement allow_cache to reuse the sha256 of the file # i.e. `None` was used if not file: @@ -182,7 +181,7 @@ async def send_file( raise TypeError('Cannot use {!r} as file'.format(file)) markup = self.build_reply_markup(buttons) - request = functions.messages.SendMediaRequest( + request = _tl.fn.messages.SendMedia( entity, media, reply_to_msg_id=reply_to, message=caption, entities=msg_entities, reply_markup=markup, silent=silent, schedule_date=schedule, clear_draft=clear_draft, @@ -225,14 +224,14 @@ async def _send_album(self: 'TelegramClient', entity, files, caption='', fh, fm, _ = await self._file_to_media( file, supports_streaming=supports_streaming, force_document=force_document, ttl=ttl) - if isinstance(fm, (types.InputMediaUploadedPhoto, types.InputMediaPhotoExternal)): - r = await self(functions.messages.UploadMediaRequest( + if isinstance(fm, (_tl.InputMediaUploadedPhoto, _tl.InputMediaPhotoExternal)): + r = await self(_tl.fn.messages.UploadMedia( entity, media=fm )) fm = utils.get_input_media(r.photo) - elif isinstance(fm, types.InputMediaUploadedDocument): - r = await self(functions.messages.UploadMediaRequest( + elif isinstance(fm, _tl.InputMediaUploadedDocument): + r = await self(_tl.fn.messages.UploadMedia( entity, media=fm )) @@ -243,7 +242,7 @@ async def _send_album(self: 'TelegramClient', entity, files, caption='', caption, msg_entities = captions.pop() else: caption, msg_entities = '', None - media.append(types.InputSingleMedia( + media.append(_tl.InputSingleMedia( fm, message=caption, entities=msg_entities @@ -251,7 +250,7 @@ async def _send_album(self: 'TelegramClient', entity, files, caption='', )) # Now we can construct the multi-media request - request = functions.messages.SendMultiMediaRequest( + request = _tl.fn.messages.SendMultiMedia( entity, reply_to_msg_id=reply_to, multi_media=media, silent=silent, schedule_date=schedule, clear_draft=clear_draft, background=background @@ -271,8 +270,8 @@ async def upload_file( use_cache: type = None, key: bytes = None, iv: bytes = None, - progress_callback: 'hints.ProgressCallback' = None) -> 'types.TypeInputFile': - if isinstance(file, (types.InputFile, types.InputFileBig)): + progress_callback: 'hints.ProgressCallback' = None) -> '_tl.TypeInputFile': + if isinstance(file, (_tl.InputFile, _tl.InputFileBig)): return file # Already uploaded pos = 0 @@ -343,10 +342,10 @@ async def upload_file( # The SavePartRequest is different depending on whether # the file is too large or not (over or less than 10MB) if is_big: - request = functions.upload.SaveBigFilePartRequest( + request = _tl.fn.upload.SaveBigFilePart( file_id, part_index, part_count, part) else: - request = functions.upload.SaveFilePartRequest( + request = _tl.fn.upload.SaveFilePart( file_id, part_index, part) result = await self(request) @@ -360,9 +359,9 @@ async def upload_file( 'Failed to upload file part {}.'.format(part_index)) if is_big: - return types.InputFileBig(file_id, part_count, file_name) + return _tl.InputFileBig(file_id, part_count, file_name) else: - return custom.InputSizedFile( + return _tl.custom.InputSizedFile( file_id, part_count, file_name, md5=hash_md5, size=file_size ) @@ -385,7 +384,7 @@ async def _file_to_media( # `aiofiles` do not base `io.IOBase` but do have `read`, so we # just check for the read attribute to see if it's file-like. - if not isinstance(file, (str, bytes, types.InputFile, types.InputFileBig))\ + if not isinstance(file, (str, bytes, _tl.InputFile, _tl.InputFileBig))\ and not hasattr(file, 'read'): # The user may pass a Message containing media (or the media, # or anything similar) that should be treated as a file. Try @@ -411,7 +410,7 @@ async def _file_to_media( media = None file_handle = None - if isinstance(file, (types.InputFile, types.InputFileBig)): + if isinstance(file, (_tl.InputFile, _tl.InputFileBig)): file_handle = file elif not isinstance(file, str) or os.path.isfile(file): file_handle = await self.upload_file( @@ -421,9 +420,9 @@ async def _file_to_media( ) elif re.match('https?://', file): if as_image: - media = types.InputMediaPhotoExternal(file, ttl_seconds=ttl) + media = _tl.InputMediaPhotoExternal(file, ttl_seconds=ttl) else: - media = types.InputMediaDocumentExternal(file, ttl_seconds=ttl) + media = _tl.InputMediaDocumentExternal(file, ttl_seconds=ttl) else: bot_file = utils.resolve_bot_file_id(file) if bot_file: @@ -437,7 +436,7 @@ async def _file_to_media( 'an HTTP URL or a valid bot-API-like file ID'.format(file) ) elif as_image: - media = types.InputMediaUploadedPhoto(file_handle, ttl_seconds=ttl) + media = _tl.InputMediaUploadedPhoto(file_handle, ttl_seconds=ttl) else: attributes, mime_type = utils.get_attributes( file, @@ -457,7 +456,7 @@ async def _file_to_media( thumb = str(thumb.absolute()) thumb = await self.upload_file(thumb, file_size=file_size) - media = types.InputMediaUploadedDocument( + media = _tl.InputMediaUploadedDocument( file=file_handle, mime_type=mime_type, attributes=attributes, diff --git a/telethon/_client/users.py b/telethon/_client/users.py index 0d871878..6209619a 100644 --- a/telethon/_client/users.py +++ b/telethon/_client/users.py @@ -4,10 +4,9 @@ import itertools import time import typing -from .. import errors, helpers, utils, hints +from .. import errors, helpers, utils, hints, _tl from ..errors import MultiError, RPCError from ..helpers import retry_range -from ..tl import TLRequest, types, functions _NOT_A_REQUEST = lambda: TypeError('You can only invoke requests, not types!') @@ -48,7 +47,7 @@ async def call(self: 'TelegramClient', sender, request, ordered=False, flood_sle raise errors.FloodWaitError(request=r, capture=diff) if self._no_updates: - r = functions.InvokeWithoutUpdatesRequest(r) + r = _tl.fn.InvokeWithoutUpdates(r) request_index = 0 last_error = None @@ -128,13 +127,13 @@ async def call(self: 'TelegramClient', sender, request, ordered=False, flood_sle async def get_me(self: 'TelegramClient', input_peer: bool = False) \ - -> 'typing.Union[types.User, types.InputPeerUser]': + -> 'typing.Union[_tl.User, _tl.InputPeerUser]': if input_peer and self._self_input_peer: return self._self_input_peer try: me = (await self( - functions.users.GetUsersRequest([types.InputUserSelf()])))[0] + _tl.fn.users.GetUsers([_tl.InputUserSelf()])))[0] self._bot = me.bot if not self._self_input_peer: @@ -165,7 +164,7 @@ async def is_user_authorized(self: 'TelegramClient') -> bool: if self._authorized is None: try: # Any request that requires authorization will work - await self(functions.updates.GetStateRequest()) + await self(_tl.fn.updates.GetState()) self._authorized = True except errors.RPCError: self._authorized = False @@ -209,14 +208,14 @@ async def get_entity( tmp = [] while users: curr, users = users[:200], users[200:] - tmp.extend(await self(functions.users.GetUsersRequest(curr))) + tmp.extend(await self(_tl.fn.users.GetUsers(curr))) users = tmp if chats: # TODO Handle chats slice? chats = (await self( - functions.messages.GetChatsRequest([x.chat_id for x in chats]))).chats + _tl.fn.messages.GetChats([x.chat_id for x in chats]))).chats if channels: channels = (await self( - functions.channels.GetChannelsRequest(channels))).chats + _tl.fn.channels.GetChannels(channels))).chats # Merge users, chats and channels into a single dictionary id_entity = { @@ -232,19 +231,19 @@ async def get_entity( for x in inputs: if isinstance(x, str): result.append(await self._get_entity_from_string(x)) - elif not isinstance(x, types.InputPeerSelf): + elif not isinstance(x, _tl.InputPeerSelf): result.append(id_entity[utils.get_peer_id(x)]) else: result.append(next( u for u in id_entity.values() - if isinstance(u, types.User) and u.is_self + if isinstance(u, _tl.User) and u.is_self )) return result[0] if single else result async def get_input_entity( self: 'TelegramClient', - peer: 'hints.EntityLike') -> 'types.TypeInputPeer': + peer: 'hints.EntityLike') -> '_tl.TypeInputPeer': # Short-circuit if the input parameter directly maps to an InputPeer try: return utils.get_input_peer(peer) @@ -261,7 +260,7 @@ async def get_input_entity( # Then come known strings that take precedence if peer in ('me', 'self'): - return types.InputPeerSelf() + return _tl.InputPeerSelf() # No InputPeer, cached peer, or known string. Fetch from disk cache try: @@ -279,10 +278,10 @@ async def get_input_entity( # If we're not a bot but the user is in our contacts, it seems to work # regardless. These are the only two special-cased requests. peer = utils.get_peer(peer) - if isinstance(peer, types.PeerUser): - users = await self(functions.users.GetUsersRequest([ - types.InputUser(peer.user_id, access_hash=0)])) - if users and not isinstance(users[0], types.UserEmpty): + if isinstance(peer, _tl.PeerUser): + users = await self(_tl.fn.users.GetUsers([ + _tl.InputUser(peer.user_id, access_hash=0)])) + if users and not isinstance(users[0], _tl.UserEmpty): # If the user passed a valid ID they expect to work for # channels but would be valid for users, we get UserEmpty. # Avoid returning the invalid empty input peer for that. @@ -291,12 +290,12 @@ async def get_input_entity( # it's not, work as a chat and try to validate it through # another request, but that becomes too much work. return utils.get_input_peer(users[0]) - elif isinstance(peer, types.PeerChat): - return types.InputPeerChat(peer.chat_id) - elif isinstance(peer, types.PeerChannel): + elif isinstance(peer, _tl.PeerChat): + return _tl.InputPeerChat(peer.chat_id) + elif isinstance(peer, _tl.PeerChannel): try: - channels = await self(functions.channels.GetChannelsRequest([ - types.InputChannel(peer.channel_id, access_hash=0)])) + channels = await self(_tl.fn.channels.GetChannels([ + _tl.InputChannel(peer.channel_id, access_hash=0)])) return utils.get_input_peer(channels.chats[0]) except errors.ChannelInvalidError: pass @@ -326,7 +325,7 @@ async def get_peer_id( except AttributeError: peer = await self.get_input_entity(peer) - if isinstance(peer, types.InputPeerSelf): + if isinstance(peer, _tl.InputPeerSelf): peer = await self.get_me(input_peer=True) return utils.get_peer_id(peer, add_mark=add_mark) @@ -348,7 +347,7 @@ async def _get_entity_from_string(self: 'TelegramClient', string): if phone: try: for user in (await self( - functions.contacts.GetContactsRequest(0))).users: + _tl.fn.contacts.GetContacts(0))).users: if user.phone == phone: return user except errors.BotMethodInvalidError: @@ -360,26 +359,26 @@ async def _get_entity_from_string(self: 'TelegramClient', string): username, is_join_chat = utils.parse_username(string) if is_join_chat: invite = await self( - functions.messages.CheckChatInviteRequest(username)) + _tl.fn.messages.CheckChatInvite(username)) - if isinstance(invite, types.ChatInvite): + if isinstance(invite, _tl.ChatInvite): raise ValueError( 'Cannot get entity from a channel (or group) ' 'that you are not part of. Join the group and retry' ) - elif isinstance(invite, types.ChatInviteAlready): + elif isinstance(invite, _tl.ChatInviteAlready): return invite.chat elif username: try: result = await self( - functions.contacts.ResolveUsernameRequest(username)) + _tl.fn.contacts.ResolveUsername(username)) except errors.UsernameNotOccupiedError as e: raise ValueError('No user has "{}" as username' .format(username)) from e try: pid = utils.get_peer_id(result.peer, add_mark=False) - if isinstance(result.peer, types.PeerUser): + if isinstance(result.peer, _tl.PeerUser): return next(x for x in result.users if x.id == pid) else: return next(x for x in result.chats if x.id == pid) @@ -407,11 +406,11 @@ async def _get_input_dialog(self: 'TelegramClient', dialog): dialog.peer = await self.get_input_entity(dialog.peer) return dialog elif dialog.SUBCLASS_OF_ID == 0xc91c90b6: # crc32(b'InputPeer') - return types.InputDialogPeer(dialog) + return _tl.InputDialogPeer(dialog) except AttributeError: pass - return types.InputDialogPeer(await self.get_input_entity(dialog)) + return _tl.InputDialogPeer(await self.get_input_entity(dialog)) async def _get_input_notify(self: 'TelegramClient', notify): """ @@ -421,10 +420,10 @@ async def _get_input_notify(self: 'TelegramClient', notify): """ try: if notify.SUBCLASS_OF_ID == 0x58981615: - if isinstance(notify, types.InputNotifyPeer): + if isinstance(notify, _tl.InputNotifyPeer): notify.peer = await self.get_input_entity(notify.peer) return notify except AttributeError: pass - return types.InputNotifyPeer(await self.get_input_entity(notify)) + return _tl.InputNotifyPeer(await self.get_input_entity(notify)) diff --git a/telethon/_crypto/cdndecrypter.py b/telethon/_crypto/cdndecrypter.py index dd615a5a..efdc3288 100644 --- a/telethon/_crypto/cdndecrypter.py +++ b/telethon/_crypto/cdndecrypter.py @@ -3,8 +3,7 @@ This module holds the CdnDecrypter utility class. """ from hashlib import sha256 -from ..tl.functions.upload import GetCdnFileRequest, ReuploadCdnFileRequest -from ..tl.types.upload import CdnFileReuploadNeeded, CdnFile +from .. import _tl from ..crypto import AESModeCTR from ..errors import CdnFileTamperedError @@ -52,14 +51,14 @@ class CdnDecrypter: cdn_aes, cdn_redirect.cdn_file_hashes ) - cdn_file = await cdn_client(GetCdnFileRequest( + cdn_file = await cdn_client(_tl.fn.upload.GetCdnFile( file_token=cdn_redirect.file_token, offset=cdn_redirect.cdn_file_hashes[0].offset, limit=cdn_redirect.cdn_file_hashes[0].limit )) - if isinstance(cdn_file, CdnFileReuploadNeeded): + if isinstance(cdn_file, _tl.upload.CdnFileReuploadNeeded): # We need to use the original client here - await client(ReuploadCdnFileRequest( + await client(_tl.fn.upload.ReuploadCdnFile( file_token=cdn_redirect.file_token, request_token=cdn_file.request_token )) @@ -82,13 +81,13 @@ class CdnDecrypter: """ if self.cdn_file_hashes: cdn_hash = self.cdn_file_hashes.pop(0) - cdn_file = self.client(GetCdnFileRequest( + cdn_file = self.client(_tl.fn.upload.GetCdnFile( self.file_token, cdn_hash.offset, cdn_hash.limit )) cdn_file.bytes = self.cdn_aes.encrypt(cdn_file.bytes) self.check(cdn_file.bytes, cdn_hash) else: - cdn_file = CdnFile(bytes(0)) + cdn_file = _tl.upload.CdnFile(bytes(0)) return cdn_file diff --git a/telethon/_crypto/rsa.py b/telethon/_crypto/rsa.py index 91ca7bad..d1f1b588 100644 --- a/telethon/_crypto/rsa.py +++ b/telethon/_crypto/rsa.py @@ -11,7 +11,7 @@ except ImportError: rsa = None raise ImportError('Missing module "rsa", please install via pip.') -from ..tl import TLObject +from .. import _tl # {fingerprint: (Crypto.PublicKey.RSA._RSAobj, old)} dictionary @@ -41,8 +41,8 @@ def _compute_fingerprint(key): :param key: the Crypto.RSA key. :return: its 8-bytes-long fingerprint. """ - n = TLObject.serialize_bytes(get_byte_array(key.n)) - e = TLObject.serialize_bytes(get_byte_array(key.e)) + n = _tl.TLObject.serialize_bytes(get_byte_array(key.n)) + e = _tl.TLObject.serialize_bytes(get_byte_array(key.e)) # Telegram uses the last 8 bytes as the fingerprint return struct.unpack(' tag, this tag is @@ -69,9 +62,9 @@ class HTMLToTelegramParser(HTMLParser): except KeyError: pass except KeyError: - EntityType = MessageEntityCode + EntityType = _tl.MessageEntityCode elif tag == 'pre': - EntityType = MessageEntityPre + EntityType = _tl.MessageEntityPre args['language'] = '' elif tag == 'a': try: @@ -80,12 +73,12 @@ class HTMLToTelegramParser(HTMLParser): return if url.startswith('mailto:'): url = url[len('mailto:'):] - EntityType = MessageEntityEmail + EntityType = _tl.MessageEntityEmail else: if self.get_starttag_text() == url: - EntityType = MessageEntityUrl + EntityType = _tl.MessageEntityUrl else: - EntityType = MessageEntityTextUrl + EntityType = _tl.MessageEntityTextUrl args['url'] = url url = None self._open_tags_meta.popleft() @@ -121,10 +114,10 @@ class HTMLToTelegramParser(HTMLParser): self.entities.append(entity) -def parse(html: str) -> Tuple[str, List[TypeMessageEntity]]: +def parse(html: str) -> Tuple[str, List[_tl.TypeMessageEntity]]: """ Parses the given HTML message and returns its stripped representation - plus a list of the MessageEntity's that were found. + plus a list of the _tl.MessageEntity's that were found. :param html: the message with HTML to be parsed. :return: a tuple consisting of (clean message, [message entities]). @@ -138,14 +131,14 @@ def parse(html: str) -> Tuple[str, List[TypeMessageEntity]]: return _del_surrogate(text), parser.entities -def unparse(text: str, entities: Iterable[TypeMessageEntity], _offset: int = 0, +def unparse(text: str, entities: Iterable[_tl.TypeMessageEntity], _offset: int = 0, _length: Optional[int] = None) -> str: """ Performs the reverse operation to .parse(), effectively returning HTML - given a normal text and its MessageEntity's. + given a normal text and its _tl.MessageEntity's. :param text: the text to be reconverted into HTML. - :param entities: the MessageEntity's applied to the text. + :param entities: the _tl.MessageEntity's applied to the text. :return: a HTML representation of the combination of both inputs. """ if not text: @@ -185,19 +178,19 @@ def unparse(text: str, entities: Iterable[TypeMessageEntity], _offset: int = 0, _offset=entity.offset, _length=length) entity_type = type(entity) - if entity_type == MessageEntityBold: + if entity_type == _tl.MessageEntityBold: html.append('{}'.format(entity_text)) - elif entity_type == MessageEntityItalic: + elif entity_type == _tl.MessageEntityItalic: html.append('{}'.format(entity_text)) - elif entity_type == MessageEntityCode: + elif entity_type == _tl.MessageEntityCode: html.append('{}'.format(entity_text)) - elif entity_type == MessageEntityUnderline: + elif entity_type == _tl.MessageEntityUnderline: html.append('{}'.format(entity_text)) - elif entity_type == MessageEntityStrike: + elif entity_type == _tl.MessageEntityStrike: html.append('{}'.format(entity_text)) - elif entity_type == MessageEntityBlockquote: + elif entity_type == _tl.MessageEntityBlockquote: html.append('
{}
'.format(entity_text)) - elif entity_type == MessageEntityPre: + elif entity_type == _tl.MessageEntityPre: if entity.language: html.append( "
\n"
@@ -208,14 +201,14 @@ def unparse(text: str, entities: Iterable[TypeMessageEntity], _offset: int = 0,
             else:
                 html.append('
{}
' .format(entity_text)) - elif entity_type == MessageEntityEmail: + elif entity_type == _tl.MessageEntityEmail: html.append('{0}'.format(entity_text)) - elif entity_type == MessageEntityUrl: + elif entity_type == _tl.MessageEntityUrl: html.append('{0}'.format(entity_text)) - elif entity_type == MessageEntityTextUrl: + elif entity_type == _tl.MessageEntityTextUrl: html.append('{}' .format(escape(entity.url), entity_text)) - elif entity_type == MessageEntityMentionName: + elif entity_type == _tl.MessageEntityMentionName: html.append('{}' .format(entity.user_id, entity_text)) else: diff --git a/telethon/_misc/markdown.py b/telethon/_misc/markdown.py index f6d59106..336da0b9 100644 --- a/telethon/_misc/markdown.py +++ b/telethon/_misc/markdown.py @@ -7,19 +7,14 @@ import re import warnings from ..helpers import add_surrogate, del_surrogate, within_surrogate, strip_text -from ..tl import TLObject -from ..tl.types import ( - MessageEntityBold, MessageEntityItalic, MessageEntityCode, - MessageEntityPre, MessageEntityTextUrl, MessageEntityMentionName, - MessageEntityStrike -) +from .. import _tl DEFAULT_DELIMITERS = { - '**': MessageEntityBold, - '__': MessageEntityItalic, - '~~': MessageEntityStrike, - '`': MessageEntityCode, - '```': MessageEntityPre + '**': _tl.MessageEntityBold, + '__': _tl.MessageEntityItalic, + '~~': _tl.MessageEntityStrike, + '`': _tl.MessageEntityCode, + '```': _tl.MessageEntityPre } DEFAULT_URL_RE = re.compile(r'\[([\S\s]+?)\]\((.+?)\)') @@ -33,7 +28,7 @@ def overlap(a, b, x, y): def parse(message, delimiters=None, url_re=None): """ Parses the given markdown message and returns its stripped representation - plus a list of the MessageEntity's that were found. + plus a list of the _tl.MessageEntity's that were found. :param message: the message with markdown-like syntax to be parsed. :param delimiters: the delimiters to be used, {delimiter: type}. @@ -98,13 +93,13 @@ def parse(message, delimiters=None, url_re=None): # Append the found entity ent = delimiters[delim] - if ent == MessageEntityPre: + if ent == _tl.MessageEntityPre: result.append(ent(i, end - i - len(delim), '')) # has 'lang' else: result.append(ent(i, end - i - len(delim))) # No nested entities inside code blocks - if ent in (MessageEntityCode, MessageEntityPre): + if ent in (_tl.MessageEntityCode, _tl.MessageEntityPre): i = end - len(delim) continue @@ -125,7 +120,7 @@ def parse(message, delimiters=None, url_re=None): if ent.offset + ent.length > m.start(): ent.length -= delim_size - result.append(MessageEntityTextUrl( + result.append(_tl.MessageEntityTextUrl( offset=m.start(), length=len(m.group(1)), url=del_surrogate(m.group(2)) )) @@ -141,10 +136,10 @@ def parse(message, delimiters=None, url_re=None): def unparse(text, entities, delimiters=None, url_fmt=None): """ Performs the reverse operation to .parse(), effectively returning - markdown-like syntax given a normal text and its MessageEntity's. + markdown-like syntax given a normal text and its _tl.MessageEntity's. :param text: the text to be reconverted into markdown. - :param entities: the MessageEntity's applied to the text. + :param entities: the _tl.MessageEntity's applied to the text. :return: a markdown-like text representing the combination of both inputs. """ if not text or not entities: @@ -158,7 +153,7 @@ def unparse(text, entities, delimiters=None, url_fmt=None): if url_fmt is not None: warnings.warn('url_fmt is deprecated') # since it complicates everything *a lot* - if isinstance(entities, TLObject): + if isinstance(entities, _tl.TLObject): entities = (entities,) text = add_surrogate(text) @@ -173,9 +168,9 @@ def unparse(text, entities, delimiters=None, url_fmt=None): insert_at.append((e, delimiter)) else: url = None - if isinstance(entity, MessageEntityTextUrl): + if isinstance(entity, _tl.MessageEntityTextUrl): url = entity.url - elif isinstance(entity, MessageEntityMentionName): + elif isinstance(entity, _tl.MessageEntityMentionName): url = 'tg://user?id={}'.format(entity.user_id) if url: insert_at.append((s, '[')) diff --git a/telethon/_misc/password.py b/telethon/_misc/password.py index 0f950254..e02c8eb8 100644 --- a/telethon/_misc/password.py +++ b/telethon/_misc/password.py @@ -2,7 +2,7 @@ import hashlib import os from .crypto import factorization -from .tl import types +from . import _tl def check_prime_and_good_check(prime: int, g: int): @@ -110,7 +110,7 @@ def pbkdf2sha512(password: bytes, salt: bytes, iterations: int): return hashlib.pbkdf2_hmac('sha512', password, salt, iterations) -def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, +def compute_hash(algo: _tl.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str): hash1 = sha256(algo.salt1, password.encode('utf-8'), algo.salt1) hash2 = sha256(algo.salt2, hash1, algo.salt2) @@ -118,7 +118,7 @@ def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter1000 return sha256(algo.salt2, hash3, algo.salt2) -def compute_digest(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, +def compute_digest(algo: _tl.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str): try: check_prime_and_good(algo.p, algo.g) @@ -133,9 +133,9 @@ def compute_digest(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter10 # https://github.com/telegramdesktop/tdesktop/blob/18b74b90451a7db2379a9d753c9cbaf8734b4d5d/Telegram/SourceFiles/core/core_cloud_password.cpp -def compute_check(request: types.account.Password, password: str): +def compute_check(request: _tl.account.Password, password: str): algo = request.current_algo - if not isinstance(algo, types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow): + if not isinstance(algo, _tl.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow): raise ValueError('unsupported password algorithm {}' .format(algo.__class__.__name__)) @@ -190,5 +190,5 @@ def compute_check(request: types.account.Password, password: str): K ) - return types.InputCheckPasswordSRP( + return _tl.InputCheckPasswordSRP( request.srp_id, bytes(a_for_hash), bytes(M1)) diff --git a/telethon/_misc/statecache.py b/telethon/_misc/statecache.py index 0e02bbd4..3f2475bf 100644 --- a/telethon/_misc/statecache.py +++ b/telethon/_misc/statecache.py @@ -1,6 +1,6 @@ import inspect -from .tl import types +from . import _tl # Which updates have the following fields? @@ -9,8 +9,8 @@ _has_channel_id = [] # TODO EntityCache does the same. Reuse? def _fill(): - for name in dir(types): - update = getattr(types, name) + for name in dir(_tl): + update = getattr(_tl, name) if getattr(update, 'SUBCLASS_OF_ID', None) == 0x9f89304e: cid = update.CONSTRUCTOR_ID sig = inspect.signature(update.__init__) @@ -51,41 +51,41 @@ class StateCache: *, channel_id=None, has_pts=frozenset(x.CONSTRUCTOR_ID for x in ( - types.UpdateNewMessage, - types.UpdateDeleteMessages, - types.UpdateReadHistoryInbox, - types.UpdateReadHistoryOutbox, - types.UpdateWebPage, - types.UpdateReadMessagesContents, - types.UpdateEditMessage, - types.updates.State, - types.updates.DifferenceTooLong, - types.UpdateShortMessage, - types.UpdateShortChatMessage, - types.UpdateShortSentMessage + _tl.UpdateNewMessage, + _tl.UpdateDeleteMessages, + _tl.UpdateReadHistoryInbox, + _tl.UpdateReadHistoryOutbox, + _tl.UpdateWebPage, + _tl.UpdateReadMessagesContents, + _tl.UpdateEditMessage, + _tl.updates.State, + _tl.updates.DifferenceTooLong, + _tl.UpdateShortMessage, + _tl.UpdateShortChatMessage, + _tl.UpdateShortSentMessage )), has_date=frozenset(x.CONSTRUCTOR_ID for x in ( - types.UpdateUserPhoto, - types.UpdateEncryption, - types.UpdateEncryptedMessagesRead, - types.UpdateChatParticipantAdd, - types.updates.DifferenceEmpty, - types.UpdateShortMessage, - types.UpdateShortChatMessage, - types.UpdateShort, - types.UpdatesCombined, - types.Updates, - types.UpdateShortSentMessage, + _tl.UpdateUserPhoto, + _tl.UpdateEncryption, + _tl.UpdateEncryptedMessagesRead, + _tl.UpdateChatParticipantAdd, + _tl.updates.DifferenceEmpty, + _tl.UpdateShortMessage, + _tl.UpdateShortChatMessage, + _tl.UpdateShort, + _tl.UpdatesCombined, + _tl.Updates, + _tl.UpdateShortSentMessage, )), has_channel_pts=frozenset(x.CONSTRUCTOR_ID for x in ( - types.UpdateChannelTooLong, - types.UpdateNewChannelMessage, - types.UpdateDeleteChannelMessages, - types.UpdateEditChannelMessage, - types.UpdateChannelWebPage, - types.updates.ChannelDifferenceEmpty, - types.updates.ChannelDifferenceTooLong, - types.updates.ChannelDifference + _tl.UpdateChannelTooLong, + _tl.UpdateNewChannelMessage, + _tl.UpdateDeleteChannelMessages, + _tl.UpdateEditChannelMessage, + _tl.UpdateChannelWebPage, + _tl.updates.ChannelDifferenceEmpty, + _tl.updates.ChannelDifferenceTooLong, + _tl.updates.ChannelDifference )), check_only=False ): @@ -120,8 +120,8 @@ class StateCache: has_channel_id=frozenset(_has_channel_id), # Hardcoded because only some with message are for channels has_message=frozenset(x.CONSTRUCTOR_ID for x in ( - types.UpdateNewChannelMessage, - types.UpdateEditChannelMessage + _tl.UpdateNewChannelMessage, + _tl.UpdateEditChannelMessage )) ): """ diff --git a/telethon/_misc/utils.py b/telethon/_misc/utils.py index e8c59c01..9826e551 100644 --- a/telethon/_misc/utils.py +++ b/telethon/_misc/utils.py @@ -21,7 +21,7 @@ from types import GeneratorType from .extensions import markdown, html from .helpers import add_surrogate, del_surrogate, strip_text -from .tl import types +from . import _tl try: import hachoir @@ -32,26 +32,26 @@ except ImportError: # Register some of the most common mime-types to avoid any issues. # See https://github.com/LonamiWebs/Telethon/issues/1096. -mimetypes.add_type('image/png', '.png') -mimetypes.add_type('image/jpeg', '.jpeg') -mimetypes.add_type('image/webp', '.webp') -mimetypes.add_type('image/gif', '.gif') -mimetypes.add_type('image/bmp', '.bmp') -mimetypes.add_type('image/x-tga', '.tga') -mimetypes.add_type('image/tiff', '.tiff') -mimetypes.add_type('image/vnd.adobe.photoshop', '.psd') +mime_tl.add_type('image/png', '.png') +mime_tl.add_type('image/jpeg', '.jpeg') +mime_tl.add_type('image/webp', '.webp') +mime_tl.add_type('image/gif', '.gif') +mime_tl.add_type('image/bmp', '.bmp') +mime_tl.add_type('image/x-tga', '.tga') +mime_tl.add_type('image/tiff', '.tiff') +mime_tl.add_type('image/vnd.adobe.photoshop', '.psd') -mimetypes.add_type('video/mp4', '.mp4') -mimetypes.add_type('video/quicktime', '.mov') -mimetypes.add_type('video/avi', '.avi') +mime_tl.add_type('video/mp4', '.mp4') +mime_tl.add_type('video/quicktime', '.mov') +mime_tl.add_type('video/avi', '.avi') -mimetypes.add_type('audio/mpeg', '.mp3') -mimetypes.add_type('audio/m4a', '.m4a') -mimetypes.add_type('audio/aac', '.aac') -mimetypes.add_type('audio/ogg', '.ogg') -mimetypes.add_type('audio/flac', '.flac') +mime_tl.add_type('audio/mpeg', '.mp3') +mime_tl.add_type('audio/m4a', '.m4a') +mime_tl.add_type('audio/aac', '.aac') +mime_tl.add_type('audio/ogg', '.ogg') +mime_tl.add_type('audio/flac', '.flac') -mimetypes.add_type('application/x-tgsticker', '.tgs') +mime_tl.add_type('application/x-tgsticker', '.tgs') USERNAME_RE = re.compile( r'@|(?:https?://)?(?:www\.)?(?:telegram\.(?:me|dog)|t\.me)/(@|joinchat/)?' @@ -92,7 +92,7 @@ def get_display_name(entity): Gets the display name for the given :tl:`User`, :tl:`Chat` or :tl:`Channel`. Returns an empty string otherwise. """ - if isinstance(entity, types.User): + if isinstance(entity, _tl.User): if entity.last_name and entity.first_name: return '{} {}'.format(entity.first_name, entity.last_name) elif entity.first_name: @@ -102,7 +102,7 @@ def get_display_name(entity): else: return '' - elif isinstance(entity, (types.Chat, types.ChatForbidden, types.Channel)): + elif isinstance(entity, (_tl.Chat, _tl.ChatForbidden, _tl.Channel)): return entity.title return '' @@ -117,14 +117,14 @@ def get_extension(media): return '.jpg' except TypeError: # These cases are not handled by input photo because it can't - if isinstance(media, (types.UserProfilePhoto, types.ChatPhoto)): + if isinstance(media, (_tl.UserProfilePhoto, _tl.ChatPhoto)): return '.jpg' # Documents will come with a mime type - if isinstance(media, types.MessageMediaDocument): + if isinstance(media, _tl.MessageMediaDocument): media = media.document if isinstance(media, ( - types.Document, types.WebDocument, types.WebDocumentNoProxy)): + _tl.Document, _tl.WebDocument, _tl.WebDocumentNoProxy)): if media.mime_type == 'application/octet-stream': # Octet stream are just bytes, which have no default extension return '' @@ -184,53 +184,53 @@ def get_input_peer(entity, allow_self=True, check_hash=True): else: _raise_cast_fail(entity, 'InputPeer') - if isinstance(entity, types.User): + if isinstance(entity, _tl.User): if entity.is_self and allow_self: - return types.InputPeerSelf() + return _tl.InputPeerSelf() elif (entity.access_hash is not None and not entity.min) or not check_hash: - return types.InputPeerUser(entity.id, entity.access_hash) + return _tl.InputPeerUser(entity.id, entity.access_hash) else: raise TypeError('User without access_hash or min info cannot be input') - if isinstance(entity, (types.Chat, types.ChatEmpty, types.ChatForbidden)): - return types.InputPeerChat(entity.id) + if isinstance(entity, (_tl.Chat, _tl.ChatEmpty, _tl.ChatForbidden)): + return _tl.InputPeerChat(entity.id) - if isinstance(entity, types.Channel): + if isinstance(entity, _tl.Channel): if (entity.access_hash is not None and not entity.min) or not check_hash: - return types.InputPeerChannel(entity.id, entity.access_hash) + return _tl.InputPeerChannel(entity.id, entity.access_hash) else: raise TypeError('Channel without access_hash or min info cannot be input') - if isinstance(entity, types.ChannelForbidden): + if isinstance(entity, _tl.ChannelForbidden): # "channelForbidden are never min", and since their hash is # also not optional, we assume that this truly is the case. - return types.InputPeerChannel(entity.id, entity.access_hash) + return _tl.InputPeerChannel(entity.id, entity.access_hash) - if isinstance(entity, types.InputUser): - return types.InputPeerUser(entity.user_id, entity.access_hash) + if isinstance(entity, _tl.InputUser): + return _tl.InputPeerUser(entity.user_id, entity.access_hash) - if isinstance(entity, types.InputChannel): - return types.InputPeerChannel(entity.channel_id, entity.access_hash) + if isinstance(entity, _tl.InputChannel): + return _tl.InputPeerChannel(entity.channel_id, entity.access_hash) - if isinstance(entity, types.InputUserSelf): - return types.InputPeerSelf() + if isinstance(entity, _tl.InputUserSelf): + return _tl.InputPeerSelf() - if isinstance(entity, types.InputUserFromMessage): - return types.InputPeerUserFromMessage(entity.peer, entity.msg_id, entity.user_id) + if isinstance(entity, _tl.InputUserFromMessage): + return _tl.InputPeerUserFromMessage(entity.peer, entity.msg_id, entity.user_id) - if isinstance(entity, types.InputChannelFromMessage): - return types.InputPeerChannelFromMessage(entity.peer, entity.msg_id, entity.channel_id) + if isinstance(entity, _tl.InputChannelFromMessage): + return _tl.InputPeerChannelFromMessage(entity.peer, entity.msg_id, entity.channel_id) - if isinstance(entity, types.UserEmpty): - return types.InputPeerEmpty() + if isinstance(entity, _tl.UserEmpty): + return _tl.InputPeerEmpty() - if isinstance(entity, types.UserFull): + if isinstance(entity, _tl.UserFull): return get_input_peer(entity.user) - if isinstance(entity, types.ChatFull): - return types.InputPeerChat(entity.id) + if isinstance(entity, _tl.ChatFull): + return _tl.InputPeerChat(entity.id) - if isinstance(entity, types.PeerChat): - return types.InputPeerChat(entity.chat_id) + if isinstance(entity, _tl.PeerChat): + return _tl.InputPeerChat(entity.chat_id) _raise_cast_fail(entity, 'InputPeer') @@ -251,14 +251,14 @@ def get_input_channel(entity): except AttributeError: _raise_cast_fail(entity, 'InputChannel') - if isinstance(entity, (types.Channel, types.ChannelForbidden)): - return types.InputChannel(entity.id, entity.access_hash or 0) + if isinstance(entity, (_tl.Channel, _tl.ChannelForbidden)): + return _tl.InputChannel(entity.id, entity.access_hash or 0) - if isinstance(entity, types.InputPeerChannel): - return types.InputChannel(entity.channel_id, entity.access_hash) + if isinstance(entity, _tl.InputPeerChannel): + return _tl.InputChannel(entity.channel_id, entity.access_hash) - if isinstance(entity, types.InputPeerChannelFromMessage): - return types.InputChannelFromMessage(entity.peer, entity.msg_id, entity.channel_id) + if isinstance(entity, _tl.InputPeerChannelFromMessage): + return _tl.InputChannelFromMessage(entity.peer, entity.msg_id, entity.channel_id) _raise_cast_fail(entity, 'InputChannel') @@ -279,26 +279,26 @@ def get_input_user(entity): except AttributeError: _raise_cast_fail(entity, 'InputUser') - if isinstance(entity, types.User): + if isinstance(entity, _tl.User): if entity.is_self: - return types.InputUserSelf() + return _tl.InputUserSelf() else: - return types.InputUser(entity.id, entity.access_hash or 0) + return _tl.InputUser(entity.id, entity.access_hash or 0) - if isinstance(entity, types.InputPeerSelf): - return types.InputUserSelf() + if isinstance(entity, _tl.InputPeerSelf): + return _tl.InputUserSelf() - if isinstance(entity, (types.UserEmpty, types.InputPeerEmpty)): - return types.InputUserEmpty() + if isinstance(entity, (_tl.UserEmpty, _tl.InputPeerEmpty)): + return _tl.InputUserEmpty() - if isinstance(entity, types.UserFull): + if isinstance(entity, _tl.UserFull): return get_input_user(entity.user) - if isinstance(entity, types.InputPeerUser): - return types.InputUser(entity.user_id, entity.access_hash) + if isinstance(entity, _tl.InputPeerUser): + return _tl.InputUser(entity.user_id, entity.access_hash) - if isinstance(entity, types.InputPeerUserFromMessage): - return types.InputUserFromMessage(entity.peer, entity.msg_id, entity.user_id) + if isinstance(entity, _tl.InputPeerUserFromMessage): + return _tl.InputUserFromMessage(entity.peer, entity.msg_id, entity.user_id) _raise_cast_fail(entity, 'InputUser') @@ -309,12 +309,12 @@ def get_input_dialog(dialog): if dialog.SUBCLASS_OF_ID == 0xa21c9795: # crc32(b'InputDialogPeer') return dialog if dialog.SUBCLASS_OF_ID == 0xc91c90b6: # crc32(b'InputPeer') - return types.InputDialogPeer(dialog) + return _tl.InputDialogPeer(dialog) except AttributeError: _raise_cast_fail(dialog, 'InputDialogPeer') try: - return types.InputDialogPeer(get_input_peer(dialog)) + return _tl.InputDialogPeer(get_input_peer(dialog)) except TypeError: pass @@ -329,18 +329,18 @@ def get_input_document(document): except AttributeError: _raise_cast_fail(document, 'InputDocument') - if isinstance(document, types.Document): - return types.InputDocument( + if isinstance(document, _tl.Document): + return _tl.InputDocument( id=document.id, access_hash=document.access_hash, file_reference=document.file_reference) - if isinstance(document, types.DocumentEmpty): - return types.InputDocumentEmpty() + if isinstance(document, _tl.DocumentEmpty): + return _tl.InputDocumentEmpty() - if isinstance(document, types.MessageMediaDocument): + if isinstance(document, _tl.MessageMediaDocument): return get_input_document(document.document) - if isinstance(document, types.Message): + if isinstance(document, _tl.Message): return get_input_document(document.media) _raise_cast_fail(document, 'InputDocument') @@ -354,32 +354,32 @@ def get_input_photo(photo): except AttributeError: _raise_cast_fail(photo, 'InputPhoto') - if isinstance(photo, types.Message): + if isinstance(photo, _tl.Message): photo = photo.media - if isinstance(photo, (types.photos.Photo, types.MessageMediaPhoto)): + if isinstance(photo, (_tl.photos.Photo, _tl.MessageMediaPhoto)): photo = photo.photo - if isinstance(photo, types.Photo): - return types.InputPhoto(id=photo.id, access_hash=photo.access_hash, + if isinstance(photo, _tl.Photo): + return _tl.InputPhoto(id=photo.id, access_hash=photo.access_hash, file_reference=photo.file_reference) - if isinstance(photo, types.PhotoEmpty): - return types.InputPhotoEmpty() + if isinstance(photo, _tl.PhotoEmpty): + return _tl.InputPhotoEmpty() - if isinstance(photo, types.messages.ChatFull): + if isinstance(photo, _tl.messages.ChatFull): photo = photo.full_chat - if isinstance(photo, types.ChannelFull): + if isinstance(photo, _tl.ChannelFull): return get_input_photo(photo.chat_photo) - elif isinstance(photo, types.UserFull): + elif isinstance(photo, _tl.UserFull): return get_input_photo(photo.profile_photo) - elif isinstance(photo, (types.Channel, types.Chat, types.User)): + elif isinstance(photo, (_tl.Channel, _tl.Chat, _tl.User)): return get_input_photo(photo.photo) - if isinstance(photo, (types.UserEmpty, types.ChatEmpty, - types.ChatForbidden, types.ChannelForbidden)): - return types.InputPhotoEmpty() + if isinstance(photo, (_tl.UserEmpty, _tl.ChatEmpty, + _tl.ChatForbidden, _tl.ChannelForbidden)): + return _tl.InputPhotoEmpty() _raise_cast_fail(photo, 'InputPhoto') @@ -390,15 +390,15 @@ def get_input_chat_photo(photo): if photo.SUBCLASS_OF_ID == 0xd4eb2d74: # crc32(b'InputChatPhoto') return photo elif photo.SUBCLASS_OF_ID == 0xe7655f1f: # crc32(b'InputFile'): - return types.InputChatUploadedPhoto(photo) + return _tl.InputChatUploadedPhoto(photo) except AttributeError: _raise_cast_fail(photo, 'InputChatPhoto') photo = get_input_photo(photo) - if isinstance(photo, types.InputPhoto): - return types.InputChatPhoto(photo) - elif isinstance(photo, types.InputPhotoEmpty): - return types.InputChatPhotoEmpty() + if isinstance(photo, _tl.InputPhoto): + return _tl.InputChatPhoto(photo) + elif isinstance(photo, _tl.InputPhotoEmpty): + return _tl.InputChatPhotoEmpty() _raise_cast_fail(photo, 'InputChatPhoto') @@ -411,16 +411,16 @@ def get_input_geo(geo): except AttributeError: _raise_cast_fail(geo, 'InputGeoPoint') - if isinstance(geo, types.GeoPoint): - return types.InputGeoPoint(lat=geo.lat, long=geo.long) + if isinstance(geo, _tl.GeoPoint): + return _tl.InputGeoPoint(lat=geo.lat, long=geo.long) - if isinstance(geo, types.GeoPointEmpty): - return types.InputGeoPointEmpty() + if isinstance(geo, _tl.GeoPointEmpty): + return _tl.InputGeoPointEmpty() - if isinstance(geo, types.MessageMediaGeo): + if isinstance(geo, _tl.MessageMediaGeo): return get_input_geo(geo.geo) - if isinstance(geo, types.Message): + if isinstance(geo, _tl.Message): return get_input_geo(geo.media) _raise_cast_fail(geo, 'InputGeoPoint') @@ -443,39 +443,39 @@ def get_input_media( if media.SUBCLASS_OF_ID == 0xfaf846f4: # crc32(b'InputMedia') return media elif media.SUBCLASS_OF_ID == 0x846363e0: # crc32(b'InputPhoto') - return types.InputMediaPhoto(media, ttl_seconds=ttl) + return _tl.InputMediaPhoto(media, ttl_seconds=ttl) elif media.SUBCLASS_OF_ID == 0xf33fdb68: # crc32(b'InputDocument') - return types.InputMediaDocument(media, ttl_seconds=ttl) + return _tl.InputMediaDocument(media, ttl_seconds=ttl) except AttributeError: _raise_cast_fail(media, 'InputMedia') - if isinstance(media, types.MessageMediaPhoto): - return types.InputMediaPhoto( + if isinstance(media, _tl.MessageMediaPhoto): + return _tl.InputMediaPhoto( id=get_input_photo(media.photo), ttl_seconds=ttl or media.ttl_seconds ) - if isinstance(media, (types.Photo, types.photos.Photo, types.PhotoEmpty)): - return types.InputMediaPhoto( + if isinstance(media, (_tl.Photo, _tl.photos.Photo, _tl.PhotoEmpty)): + return _tl.InputMediaPhoto( id=get_input_photo(media), ttl_seconds=ttl ) - if isinstance(media, types.MessageMediaDocument): - return types.InputMediaDocument( + if isinstance(media, _tl.MessageMediaDocument): + return _tl.InputMediaDocument( id=get_input_document(media.document), ttl_seconds=ttl or media.ttl_seconds ) - if isinstance(media, (types.Document, types.DocumentEmpty)): - return types.InputMediaDocument( + if isinstance(media, (_tl.Document, _tl.DocumentEmpty)): + return _tl.InputMediaDocument( id=get_input_document(media), ttl_seconds=ttl ) - if isinstance(media, (types.InputFile, types.InputFileBig)): + if isinstance(media, (_tl.InputFile, _tl.InputFileBig)): if is_photo: - return types.InputMediaUploadedPhoto(file=media, ttl_seconds=ttl) + return _tl.InputMediaUploadedPhoto(file=media, ttl_seconds=ttl) else: attrs, mime = get_attributes( media, @@ -485,29 +485,29 @@ def get_input_media( video_note=video_note, supports_streaming=supports_streaming ) - return types.InputMediaUploadedDocument( + return _tl.InputMediaUploadedDocument( file=media, mime_type=mime, attributes=attrs, force_file=force_document, ttl_seconds=ttl) - if isinstance(media, types.MessageMediaGame): - return types.InputMediaGame(id=types.InputGameID( + if isinstance(media, _tl.MessageMediaGame): + return _tl.InputMediaGame(id=_tl.InputGameID( id=media.game.id, access_hash=media.game.access_hash )) - if isinstance(media, types.MessageMediaContact): - return types.InputMediaContact( + if isinstance(media, _tl.MessageMediaContact): + return _tl.InputMediaContact( phone_number=media.phone_number, first_name=media.first_name, last_name=media.last_name, vcard='' ) - if isinstance(media, types.MessageMediaGeo): - return types.InputMediaGeoPoint(geo_point=get_input_geo(media.geo)) + if isinstance(media, _tl.MessageMediaGeo): + return _tl.InputMediaGeoPoint(geo_point=get_input_geo(media.geo)) - if isinstance(media, types.MessageMediaVenue): - return types.InputMediaVenue( + if isinstance(media, _tl.MessageMediaVenue): + return _tl.InputMediaVenue( geo_point=get_input_geo(media.geo), title=media.title, address=media.address, @@ -516,19 +516,19 @@ def get_input_media( venue_type='' ) - if isinstance(media, types.MessageMediaDice): - return types.InputMediaDice(media.emoticon) + if isinstance(media, _tl.MessageMediaDice): + return _tl.InputMediaDice(media.emoticon) if isinstance(media, ( - types.MessageMediaEmpty, types.MessageMediaUnsupported, - types.ChatPhotoEmpty, types.UserProfilePhotoEmpty, - types.ChatPhoto, types.UserProfilePhoto)): - return types.InputMediaEmpty() + _tl.MessageMediaEmpty, _tl.MessageMediaUnsupported, + _tl.ChatPhotoEmpty, _tl.UserProfilePhotoEmpty, + _tl.ChatPhoto, _tl.UserProfilePhoto)): + return _tl.InputMediaEmpty() - if isinstance(media, types.Message): + if isinstance(media, _tl.Message): return get_input_media(media.media, is_photo=is_photo, ttl=ttl) - if isinstance(media, types.MessageMediaPoll): + if isinstance(media, _tl.MessageMediaPoll): if media.poll.quiz: if not media.results.results: # A quiz has correct answers, which we don't know until answered. @@ -539,15 +539,15 @@ def get_input_media( else: correct_answers = None - return types.InputMediaPoll( + return _tl.InputMediaPoll( poll=media.poll, correct_answers=correct_answers, solution=media.results.solution, solution_entities=media.results.solution_entities, ) - if isinstance(media, types.Poll): - return types.InputMediaPoll(media) + if isinstance(media, _tl.Poll): + return _tl.InputMediaPoll(media) _raise_cast_fail(media, 'InputMedia') @@ -556,11 +556,11 @@ def get_input_message(message): """Similar to :meth:`get_input_peer`, but for input messages.""" try: if isinstance(message, int): # This case is really common too - return types.InputMessageID(message) + return _tl.InputMessageID(message) elif message.SUBCLASS_OF_ID == 0x54b6bcc5: # crc32(b'InputMessage'): return message elif message.SUBCLASS_OF_ID == 0x790009e3: # crc32(b'Message'): - return types.InputMessageID(message.id) + return _tl.InputMessageID(message.id) except AttributeError: pass @@ -573,7 +573,7 @@ def get_input_group_call(call): if call.SUBCLASS_OF_ID == 0x58611ab1: # crc32(b'InputGroupCall') return call elif call.SUBCLASS_OF_ID == 0x20b4f320: # crc32(b'GroupCall') - return types.InputGroupCall(id=call.id, access_hash=call.access_hash) + return _tl.InputGroupCall(id=call.id, access_hash=call.access_hash) except AttributeError: _raise_cast_fail(call, 'InputGroupCall') @@ -675,10 +675,10 @@ def get_attributes(file, *, attributes=None, mime_type=None, # Note: ``file.name`` works for :tl:`InputFile` and some `IOBase` streams name = file if isinstance(file, str) else getattr(file, 'name', 'unnamed') if mime_type is None: - mime_type = mimetypes.guess_type(name)[0] + mime_type = mime_tl.guess_type(name)[0] - attr_dict = {types.DocumentAttributeFilename: - types.DocumentAttributeFilename(os.path.basename(name))} + attr_dict = {_tl.DocumentAttributeFilename: + _tl.DocumentAttributeFilename(os.path.basename(name))} if is_audio(file): m = _get_metadata(file) @@ -690,8 +690,8 @@ def get_attributes(file, *, attributes=None, mime_type=None, else: performer = None - attr_dict[types.DocumentAttributeAudio] = \ - types.DocumentAttributeAudio( + attr_dict[_tl.DocumentAttributeAudio] = \ + _tl.DocumentAttributeAudio( voice=voice_note, title=m.get('title') if m.has('title') else None, performer=performer, @@ -702,7 +702,7 @@ def get_attributes(file, *, attributes=None, mime_type=None, if not force_document and is_video(file): m = _get_metadata(file) if m: - doc = types.DocumentAttributeVideo( + doc = _tl.DocumentAttributeVideo( round_message=video_note, w=m.get('width') if m.has('width') else 1, h=m.get('height') if m.has('height') else 1, @@ -719,22 +719,22 @@ def get_attributes(file, *, attributes=None, mime_type=None, if t_m and t_m.has("height"): height = t_m.get("height") - doc = types.DocumentAttributeVideo( + doc = _tl.DocumentAttributeVideo( 0, width, height, round_message=video_note, supports_streaming=supports_streaming) else: - doc = types.DocumentAttributeVideo( + doc = _tl.DocumentAttributeVideo( 0, 1, 1, round_message=video_note, supports_streaming=supports_streaming) - attr_dict[types.DocumentAttributeVideo] = doc + attr_dict[_tl.DocumentAttributeVideo] = doc if voice_note: - if types.DocumentAttributeAudio in attr_dict: - attr_dict[types.DocumentAttributeAudio].voice = True + if _tl.DocumentAttributeAudio in attr_dict: + attr_dict[_tl.DocumentAttributeAudio].voice = True else: - attr_dict[types.DocumentAttributeAudio] = \ - types.DocumentAttributeAudio(0, voice=True) + attr_dict[_tl.DocumentAttributeAudio] = \ + _tl.DocumentAttributeAudio(0, voice=True) # Now override the attributes if any. As we have a dict of # {cls: instance}, we can override any class with the list @@ -803,23 +803,23 @@ def _get_file_info(location): except AttributeError: _raise_cast_fail(location, 'InputFileLocation') - if isinstance(location, types.Message): + if isinstance(location, _tl.Message): location = location.media - if isinstance(location, types.MessageMediaDocument): + if isinstance(location, _tl.MessageMediaDocument): location = location.document - elif isinstance(location, types.MessageMediaPhoto): + elif isinstance(location, _tl.MessageMediaPhoto): location = location.photo - if isinstance(location, types.Document): - return _FileInfo(location.dc_id, types.InputDocumentFileLocation( + if isinstance(location, _tl.Document): + return _FileInfo(location.dc_id, _tl.InputDocumentFileLocation( id=location.id, access_hash=location.access_hash, file_reference=location.file_reference, thumb_size='' # Presumably to download one of its thumbnails ), location.size) - elif isinstance(location, types.Photo): - return _FileInfo(location.dc_id, types.InputPhotoFileLocation( + elif isinstance(location, _tl.Photo): + return _FileInfo(location.dc_id, _tl.InputPhotoFileLocation( id=location.id, access_hash=location.access_hash, file_reference=location.file_reference, @@ -860,7 +860,7 @@ def is_image(file): if match: return True else: - return isinstance(resolve_bot_file_id(file), types.Photo) + return isinstance(resolve_bot_file_id(file), _tl.Photo) def is_gif(file): @@ -881,7 +881,7 @@ def is_audio(file): return False else: file = 'a' + ext - return (mimetypes.guess_type(file)[0] or '').startswith('audio/') + return (mime_tl.guess_type(file)[0] or '').startswith('audio/') def is_video(file): @@ -895,7 +895,7 @@ def is_video(file): return False else: file = 'a' + ext - return (mimetypes.guess_type(file)[0] or '').startswith('video/') + return (mime_tl.guess_type(file)[0] or '').startswith('video/') def is_list_like(obj): @@ -971,27 +971,27 @@ def get_peer(peer): elif peer.SUBCLASS_OF_ID == 0x2d45687: return peer elif isinstance(peer, ( - types.contacts.ResolvedPeer, types.InputNotifyPeer, - types.TopPeer, types.Dialog, types.DialogPeer)): + _tl.contacts.ResolvedPeer, _tl.InputNotifyPeer, + _tl.TopPeer, _tl.Dialog, _tl.DialogPeer)): return peer.peer - elif isinstance(peer, types.ChannelFull): - return types.PeerChannel(peer.id) - elif isinstance(peer, types.UserEmpty): - return types.PeerUser(peer.id) - elif isinstance(peer, types.ChatEmpty): - return types.PeerChat(peer.id) + elif isinstance(peer, _tl.ChannelFull): + return _tl.PeerChannel(peer.id) + elif isinstance(peer, _tl.UserEmpty): + return _tl.PeerUser(peer.id) + elif isinstance(peer, _tl.ChatEmpty): + return _tl.PeerChat(peer.id) if peer.SUBCLASS_OF_ID in (0x7d7c6f86, 0xd9c7fc18): # ChatParticipant, ChannelParticipant - return types.PeerUser(peer.user_id) + return _tl.PeerUser(peer.user_id) peer = get_input_peer(peer, allow_self=False, check_hash=False) - if isinstance(peer, (types.InputPeerUser, types.InputPeerUserFromMessage)): - return types.PeerUser(peer.user_id) - elif isinstance(peer, types.InputPeerChat): - return types.PeerChat(peer.chat_id) - elif isinstance(peer, (types.InputPeerChannel, types.InputPeerChannelFromMessage)): - return types.PeerChannel(peer.channel_id) + if isinstance(peer, (_tl.InputPeerUser, _tl.InputPeerUserFromMessage)): + return _tl.PeerUser(peer.user_id) + elif isinstance(peer, _tl.InputPeerChat): + return _tl.PeerChat(peer.chat_id) + elif isinstance(peer, (_tl.InputPeerChannel, _tl.InputPeerChannelFromMessage)): + return _tl.PeerChannel(peer.channel_id) except (AttributeError, TypeError): pass _raise_cast_fail(peer, 'Peer') @@ -1017,7 +1017,7 @@ def get_peer_id(peer, add_mark=True): return peer if add_mark else resolve_id(peer)[0] # Tell the user to use their client to resolve InputPeerSelf if we got one - if isinstance(peer, types.InputPeerSelf): + if isinstance(peer, _tl.InputPeerSelf): _raise_cast_fail(peer, 'int (you might want to use client.get_peer_id)') try: @@ -1025,15 +1025,15 @@ def get_peer_id(peer, add_mark=True): except TypeError: _raise_cast_fail(peer, 'int') - if isinstance(peer, types.PeerUser): + if isinstance(peer, _tl.PeerUser): return peer.user_id - elif isinstance(peer, types.PeerChat): + elif isinstance(peer, _tl.PeerChat): # Check in case the user mixed things up to avoid blowing up if not (0 < peer.chat_id <= 0x7fffffff): peer.chat_id = resolve_id(peer.chat_id)[0] return -peer.chat_id if add_mark else peer.chat_id - else: # if isinstance(peer, types.PeerChannel): + else: # if isinstance(peer, _tl.PeerChannel): # Check in case the user mixed things up to avoid blowing up if not (0 < peer.channel_id <= 0x7fffffff): peer.channel_id = resolve_id(peer.channel_id)[0] @@ -1048,14 +1048,14 @@ def get_peer_id(peer, add_mark=True): def resolve_id(marked_id): """Given a marked ID, returns the original ID and its :tl:`Peer` type.""" if marked_id >= 0: - return marked_id, types.PeerUser + return marked_id, _tl.PeerUser marked_id = -marked_id if marked_id > 1000000000000: marked_id -= 1000000000000 - return marked_id, types.PeerChannel + return marked_id, _tl.PeerChannel else: - return marked_id, types.PeerChat + return marked_id, _tl.PeerChat def _rle_decode(data): @@ -1159,12 +1159,12 @@ def resolve_bot_file_id(file_id): attributes = [] if file_type == 3 or file_type == 9: - attributes.append(types.DocumentAttributeAudio( + attributes.append(_tl.DocumentAttributeAudio( duration=0, voice=file_type == 3 )) elif file_type == 4 or file_type == 13: - attributes.append(types.DocumentAttributeVideo( + attributes.append(_tl.DocumentAttributeVideo( duration=0, w=0, h=0, @@ -1172,14 +1172,14 @@ def resolve_bot_file_id(file_id): )) # elif file_type == 5: # other, cannot know which elif file_type == 8: - attributes.append(types.DocumentAttributeSticker( + attributes.append(_tl.DocumentAttributeSticker( alt='', - stickerset=types.InputStickerSetEmpty() + stickerset=_tl.InputStickerSetEmpty() )) elif file_type == 10: - attributes.append(types.DocumentAttributeAnimated()) + attributes.append(_tl.DocumentAttributeAnimated()) - return types.Document( + return _tl.Document( id=media_id, access_hash=access_hash, date=None, @@ -1210,12 +1210,12 @@ def resolve_bot_file_id(file_id): # Thumbnails (small) always have ID 0; otherwise size 'x' photo_size = 's' if media_id or access_hash else 'x' - return types.Photo( + return _tl.Photo( id=media_id, access_hash=access_hash, file_reference=b'', date=None, - sizes=[types.PhotoSize( + sizes=[_tl.PhotoSize( type=photo_size, w=0, h=0, @@ -1235,21 +1235,21 @@ def pack_bot_file_id(file): If an invalid parameter is given, it will ``return None``. """ - if isinstance(file, types.MessageMediaDocument): + if isinstance(file, _tl.MessageMediaDocument): file = file.document - elif isinstance(file, types.MessageMediaPhoto): + elif isinstance(file, _tl.MessageMediaPhoto): file = file.photo - if isinstance(file, types.Document): + if isinstance(file, _tl.Document): file_type = 5 for attribute in file.attributes: - if isinstance(attribute, types.DocumentAttributeAudio): + if isinstance(attribute, _tl.DocumentAttributeAudio): file_type = 3 if attribute.voice else 9 - elif isinstance(attribute, types.DocumentAttributeVideo): + elif isinstance(attribute, _tl.DocumentAttributeVideo): file_type = 13 if attribute.round_message else 4 - elif isinstance(attribute, types.DocumentAttributeSticker): + elif isinstance(attribute, _tl.DocumentAttributeSticker): file_type = 8 - elif isinstance(attribute, types.DocumentAttributeAnimated): + elif isinstance(attribute, _tl.DocumentAttributeAnimated): file_type = 10 else: continue @@ -1258,9 +1258,9 @@ def pack_bot_file_id(file): return _encode_telegram_base64(_rle_encode(struct.pack( ' 1: return super().filter(event) - class Event(EventCommon, SenderGetter): + class Event(EventCommon, _tl.custom.sendergetter.SenderGetter): """ Represents the event of a new album. @@ -150,7 +148,7 @@ class Album(EventBuilder): """ def __init__(self, messages): message = messages[0] - if not message.out and isinstance(message.peer_id, types.PeerUser): + if not message.out and isinstance(message.peer_id, _tl.PeerUser): # Incoming message (e.g. from a bot) has peer_id=us, and # from_id=bot (the actual "chat" from a user's perspective). chat_peer = message.from_id @@ -160,7 +158,7 @@ class Album(EventBuilder): super().__init__(chat_peer=chat_peer, msg_id=message.id, broadcast=bool(message.post)) - SenderGetter.__init__(self, message.sender_id) + _tl.custom.sendergetter.SenderGetter.__init__(self, message.sender_id) self.messages = messages def _set_client(self, client): diff --git a/telethon/events/callbackquery.py b/telethon/events/callbackquery.py index 94e03b7b..954ccf2d 100644 --- a/telethon/events/callbackquery.py +++ b/telethon/events/callbackquery.py @@ -2,9 +2,7 @@ import re import struct from .common import EventBuilder, EventCommon, name_inner_event -from .. import utils -from ..tl import types, functions -from ..tl.custom.sendergetter import SenderGetter +from .. import utils, _tl @name_inner_event @@ -88,13 +86,13 @@ class CallbackQuery(EventBuilder): @classmethod def build(cls, update, others=None, self_id=None): - if isinstance(update, types.UpdateBotCallbackQuery): + if isinstance(update, _tl.UpdateBotCallbackQuery): return cls.Event(update, update.peer, update.msg_id) - elif isinstance(update, types.UpdateInlineBotCallbackQuery): + elif isinstance(update, _tl.UpdateInlineBotCallbackQuery): # See https://github.com/LonamiWebs/Telethon/pull/1005 # The long message ID is actually just msg_id + peer_id mid, pid = struct.unpack('