diff --git a/.gitignore b/.gitignore index 65fabceb..1e497d62 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ __pycache__/ /docs/ # File used to manually test new changes, contains sensitive data -/example.py +/example*.py diff --git a/readthedocs/misc/v2-migration-guide.rst b/readthedocs/misc/v2-migration-guide.rst index de1af171..8f9bab4d 100644 --- a/readthedocs/misc/v2-migration-guide.rst +++ b/readthedocs/misc/v2-migration-guide.rst @@ -49,6 +49,8 @@ The following modules have been moved inside ``_misc``: * ``statecache.py`` * ``utils.py`` +// TODO review telethon/__init__.py isn't exposing more than it should + The TelegramClient is no longer made out of mixins -------------------------------------------------- diff --git a/telethon/__init__.py b/telethon/__init__.py index 335abab6..fa01de5b 100644 --- a/telethon/__init__.py +++ b/telethon/__init__.py @@ -1,6 +1,12 @@ +# Note: the import order matters +from ._misc import helpers # no dependencies +from . import _tl # no dependencies +from ._misc import utils # depends on helpers and _tl +from ._tl import custom # depends on utils +from ._misc import hints # depends on custom + from ._client.telegramclient import TelegramClient -from .network import connection -from ._tl import custom +from ._network import connection from ._tl.custom import Button from . import version, events, utils, errors diff --git a/telethon/_client/auth.py b/telethon/_client/auth.py index 859a4e87..ccfaa68b 100644 --- a/telethon/_client/auth.py +++ b/telethon/_client/auth.py @@ -5,7 +5,9 @@ import sys import typing import warnings -from .. import utils, helpers, errors, password as pwd_mod, _tl +from .._misc import utils, helpers, password as pwd_mod +from .. import errors, _tl +from .._tl import custom if typing.TYPE_CHECKING: from .telegramclient import TelegramClient diff --git a/telethon/_client/bots.py b/telethon/_client/bots.py index 0e967ed9..8c2d50fe 100644 --- a/telethon/_client/bots.py +++ b/telethon/_client/bots.py @@ -1,6 +1,7 @@ import typing from .. import hints, _tl +from .._tl import custom if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -13,7 +14,7 @@ async def inline_query( *, entity: 'hints.EntityLike' = None, offset: str = None, - geo_point: '_tl.GeoPoint' = None) -> _tl.custom.InlineResults: + geo_point: '_tl.GeoPoint' = None) -> custom.InlineResults: bot = await self.get_input_entity(bot) if entity: peer = await self.get_input_entity(entity) @@ -28,4 +29,4 @@ async def inline_query( geo_point=geo_point )) - return _tl.custom.InlineResults(self, result, entity=peer if entity else None) + return custom.InlineResults(self, result, entity=peer if entity else None) diff --git a/telethon/_client/buttons.py b/telethon/_client/buttons.py index 5dd9c413..897b4703 100644 --- a/telethon/_client/buttons.py +++ b/telethon/_client/buttons.py @@ -1,6 +1,8 @@ import typing -from .. import utils, hints, _tl +from .._misc import utils, hints +from .. import _tl +from .._tl import custom def build_reply_markup( @@ -30,7 +32,7 @@ def build_reply_markup( for row in buttons: current = [] for button in row: - if isinstance(button, _tl.custom.Button): + if isinstance(button, custom.Button): if button.resize is not None: resize = button.resize if button.single_use is not None: @@ -39,10 +41,10 @@ def build_reply_markup( selective = button.selective button = button.button - elif isinstance(button, _tl.custom.MessageButton): + elif isinstance(button, custom.MessageButton): button = button.button - inline = _tl.custom.Button._is_inline(button) + inline = custom.Button._is_inline(button) is_inline |= inline is_normal |= not inline diff --git a/telethon/_client/chats.py b/telethon/_client/chats.py index 904bba10..0fb88ed8 100644 --- a/telethon/_client/chats.py +++ b/telethon/_client/chats.py @@ -4,8 +4,9 @@ import itertools import string import typing -from .. import helpers, utils, hints, errors, _tl -from ..requestiter import RequestIter +from .. import hints, errors, _tl +from .._misc import helpers, utils, requestiter +from .._tl import custom if typing.TYPE_CHECKING: from .telegramclient import TelegramClient @@ -92,7 +93,7 @@ class _ChatAction: self._action.progress = 100 * round(current / total) -class _ParticipantsIter(RequestIter): +class _ParticipantsIter(requestiter.RequestIter): async def _init(self, entity, filter, search, aggressive): if isinstance(filter, type): if filter in (_tl.ChannelParticipantsBanned, @@ -246,7 +247,7 @@ class _ParticipantsIter(RequestIter): self.buffer.append(user) -class _AdminLogIter(RequestIter): +class _AdminLogIter(requestiter.RequestIter): async def _init( self, entity, admins, search, min_id, max_id, join, leave, invite, restrict, unrestrict, ban, unban, @@ -301,13 +302,13 @@ class _AdminLogIter(RequestIter): ev.action.message._finish_init( self.client, entities, self.entity) - self.buffer.append(_tl.custom.AdminLogEvent(ev, entities)) + self.buffer.append(custom.AdminLogEvent(ev, entities)) if len(r.events) < self.request.limit: return True -class _ProfilePhotoIter(RequestIter): +class _ProfilePhotoIter(requestiter.RequestIter): async def _init( self, entity, offset, max_id ): @@ -694,7 +695,7 @@ async def get_permissions( self: 'TelegramClient', entity: 'hints.EntityLike', user: 'hints.EntityLike' = None -) -> 'typing.Optional[_tl.custom.ParticipantPermissions]': +) -> 'typing.Optional[custom.ParticipantPermissions]': entity = await self.get_entity(entity) if not user: @@ -715,7 +716,7 @@ async def get_permissions( entity, user )) - return _tl.custom.ParticipantPermissions(participant.participant, False) + return custom.ParticipantPermissions(participant.participant, False) elif helpers._entity_type(entity) == helpers._EntityType.CHAT: chat = await self(_tl.fn.messages.GetFullChat( entity @@ -724,7 +725,7 @@ async def get_permissions( user = await self.get_me(input_peer=True) for participant in chat.full_chat.participants.participants: if participant.user_id == user.user_id: - return _tl.custom.ParticipantPermissions(participant, True) + return custom.ParticipantPermissions(participant, True) raise errors.UserNotParticipantError(None) raise ValueError('You must pass either a channel or a chat') diff --git a/telethon/_client/dialogs.py b/telethon/_client/dialogs.py index aee8861d..bfd76f61 100644 --- a/telethon/_client/dialogs.py +++ b/telethon/_client/dialogs.py @@ -3,8 +3,9 @@ import inspect import itertools import typing -from .. import helpers, utils, hints, errors, _tl -from ..requestiter import RequestIter +from .. import hints, errors, _tl +from .._misc import helpers, utils, requestiter +from .._tl import custom _MAX_CHUNK_SIZE = 100 @@ -23,7 +24,7 @@ def _dialog_message_key(peer, message_id): return (peer.channel_id if isinstance(peer, _tl.PeerChannel) else None), message_id -class _DialogsIter(RequestIter): +class _DialogsIter(requestiter.RequestIter): async def _init( self, offset_date, offset_id, offset_peer, ignore_pinned, ignore_migrated, folder ): @@ -79,7 +80,7 @@ class _DialogsIter(RequestIter): # Real world example: https://t.me/TelethonChat/271471 continue - cd = _tl.custom.Dialog(self.client, d, entities, message) + cd = custom.Dialog(self.client, d, entities, message) if cd.dialog.pts: self.client._channel_pts[cd.id] = cd.dialog.pts @@ -108,7 +109,7 @@ class _DialogsIter(RequestIter): self.request.offset_peer = self.buffer[-1].input_entity -class _DraftsIter(RequestIter): +class _DraftsIter(requestiter.RequestIter): async def _init(self, entities, **kwargs): if not entities: r = await self.client(_tl.fn.messages.GetAllDrafts()) @@ -127,7 +128,7 @@ class _DraftsIter(RequestIter): for x in itertools.chain(r.users, r.chats)} self.buffer.extend( - _tl.custom.Draft(self.client, entities[utils.get_peer_id(d.peer)], d.draft) + custom.Draft(self.client, entities[utils.get_peer_id(d.peer)], d.draft) for d in items ) diff --git a/telethon/_client/downloads.py b/telethon/_client/downloads.py index 974db2a0..dbb279f3 100644 --- a/telethon/_client/downloads.py +++ b/telethon/_client/downloads.py @@ -6,10 +6,9 @@ import typing import inspect import asyncio -from ..crypto import AES - -from .. import utils, helpers, errors, hints, _tl -from ..requestiter import RequestIter +from .._crypto import AES +from .._misc import utils, helpers, requestiter +from .. import errors, hints, _tl try: import aiohttp @@ -26,7 +25,7 @@ MAX_CHUNK_SIZE = 512 * 1024 # 2021-01-15, users reported that `errors.TimeoutError` can occur while downloading files. TIMED_OUT_SLEEP = 1 -class _DirectDownloadIter(RequestIter): +class _DirectDownloadIter(requestiter.RequestIter): async def _init( self, file, dc_id, offset, stride, chunk_size, request_size, file_size, msg_data ): diff --git a/telethon/_client/messages.py b/telethon/_client/messages.py index ba88c665..00ee418a 100644 --- a/telethon/_client/messages.py +++ b/telethon/_client/messages.py @@ -3,8 +3,8 @@ import itertools import typing import warnings -from .. import helpers, utils, errors, hints, _tl -from ..requestiter import RequestIter +from .. import errors, hints, _tl +from .._misc import helpers, utils, requestiter _MAX_CHUNK_SIZE = 100 @@ -12,7 +12,7 @@ if typing.TYPE_CHECKING: from .telegramclient import TelegramClient -class _MessagesIter(RequestIter): +class _MessagesIter(requestiter.RequestIter): """ Common factor for all requests that need to iterate over messages. """ @@ -263,7 +263,7 @@ class _MessagesIter(RequestIter): self.request.offset_rate = getattr(response, 'next_rate', 0) -class _IDsIter(RequestIter): +class _IDsIter(requestiter.RequestIter): async def _init(self, entity, ids): self.total = len(ids) self._ids = list(reversed(ids)) if self.reverse else ids diff --git a/telethon/_client/telegrambaseclient.py b/telethon/_client/telegrambaseclient.py index 9c5d62d7..c89b1809 100644 --- a/telethon/_client/telegrambaseclient.py +++ b/telethon/_client/telegrambaseclient.py @@ -8,12 +8,10 @@ import time import typing 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 .._crypto import rsa +from .._misc import markdown, entitycache, statecache +from .._network import MTProtoSender, Connection, ConnectionTcpFull, TcpMTProxy from ..sessions import Session, SQLiteSession, MemorySession -from ..statecache import StateCache DEFAULT_DC_ID = 2 DEFAULT_IPV4_IP = '149.154.167.51' @@ -151,7 +149,7 @@ def init( # TODO Session should probably return all cached # info of entities, not just the input versions self.session = session - self._entity_cache = EntityCache() + self._entity_cache = entitycache.EntityCache() self.api_id = int(api_id) self.api_hash = api_hash @@ -259,7 +257,7 @@ def init( # Update state (for catching up after a disconnection) # TODO Get state from channels too - self._state_cache = StateCache( + self._state_cache = statecache.StateCache( self.session.get_update_state(0), self._log) # Some further state for subclasses diff --git a/telethon/_client/telegramclient.py b/telethon/_client/telegramclient.py index d3935273..986b89f0 100644 --- a/telethon/_client/telegramclient.py +++ b/telethon/_client/telegramclient.py @@ -9,7 +9,8 @@ from . import ( telegrambaseclient, updates, uploads, users ) from .. import helpers, version, _tl -from ..network import ConnectionTcpFull +from .._tl import custom +from .._network import ConnectionTcpFull from ..events.common import EventBuilder, EventCommon @@ -3390,7 +3391,7 @@ class TelegramClient: await client.send_file(chat, file, progress_callback=callback) # Dices, including dart and other future emoji - from telethon.tl import types + from telethon import _tl await client.send_file(chat, _tl.InputMediaDice('')) await client.send_file(chat, _tl.InputMediaDice('🎯')) diff --git a/telethon/_client/uploads.py b/telethon/_client/uploads.py index 6c0d8146..db2cdd77 100644 --- a/telethon/_client/uploads.py +++ b/telethon/_client/uploads.py @@ -7,9 +7,11 @@ import re import typing from io import BytesIO -from ..crypto import AES +from .._crypto import AES -from .. import utils, helpers, hints, _tl +from .._misc import utils, helpers +from .. import hints, _tl +from .._tl import custom try: import PIL @@ -361,7 +363,7 @@ async def upload_file( if is_big: return _tl.InputFileBig(file_id, part_count, file_name) else: - return _tl.custom.InputSizedFile( + return custom.InputSizedFile( file_id, part_count, file_name, md5=hash_md5, size=file_size ) diff --git a/telethon/_client/users.py b/telethon/_client/users.py index 6209619a..e493ea61 100644 --- a/telethon/_client/users.py +++ b/telethon/_client/users.py @@ -4,9 +4,9 @@ import itertools import time import typing -from .. import errors, helpers, utils, hints, _tl +from .. import errors, hints, _tl +from .._misc import helpers, utils from ..errors import MultiError, RPCError -from ..helpers import retry_range _NOT_A_REQUEST = lambda: TypeError('You can only invoke requests, not types!') @@ -53,7 +53,7 @@ async def call(self: 'TelegramClient', sender, request, ordered=False, flood_sle last_error = None self._last_request = time.time() - for attempt in retry_range(self._request_retries): + for attempt in helpers.retry_range(self._request_retries): try: future = sender.send(request, ordered=ordered) if isinstance(future, list): diff --git a/telethon/_crypto/authkey.py b/telethon/_crypto/authkey.py index 8475ec17..fa6fbb78 100644 --- a/telethon/_crypto/authkey.py +++ b/telethon/_crypto/authkey.py @@ -4,7 +4,7 @@ This module holds the AuthKey class. import struct from hashlib import sha1 -from ..extensions import BinaryReader +from .._misc import BinaryReader class AuthKey: diff --git a/telethon/_crypto/cdndecrypter.py b/telethon/_crypto/cdndecrypter.py index efdc3288..8347a561 100644 --- a/telethon/_crypto/cdndecrypter.py +++ b/telethon/_crypto/cdndecrypter.py @@ -4,7 +4,7 @@ This module holds the CdnDecrypter utility class. from hashlib import sha256 from .. import _tl -from ..crypto import AESModeCTR +from .._crypto import AESModeCTR from ..errors import CdnFileTamperedError diff --git a/telethon/_misc/entitycache.py b/telethon/_misc/entitycache.py index f3116b7d..b6f87697 100644 --- a/telethon/_misc/entitycache.py +++ b/telethon/_misc/entitycache.py @@ -1,7 +1,8 @@ import inspect import itertools -from . import utils, _tl +from .._misc import utils +from .. import _tl # Which updates have the following fields? _has_field = { diff --git a/telethon/_misc/hints.py b/telethon/_misc/hints.py index 7b1ec5ae..a5299a25 100644 --- a/telethon/_misc/hints.py +++ b/telethon/_misc/hints.py @@ -1,7 +1,9 @@ import datetime import typing -from . import helpers, _tl +from . import helpers +from .. import _tl +from .._tl import custom Phone = str Username = str diff --git a/telethon/_misc/markdown.py b/telethon/_misc/markdown.py index 336da0b9..b9661af3 100644 --- a/telethon/_misc/markdown.py +++ b/telethon/_misc/markdown.py @@ -6,7 +6,7 @@ since they seem to count as two characters and it's a bit strange. import re import warnings -from ..helpers import add_surrogate, del_surrogate, within_surrogate, strip_text +from .helpers import add_surrogate, del_surrogate, within_surrogate, strip_text from .. import _tl DEFAULT_DELIMITERS = { diff --git a/telethon/_misc/messagepacker.py b/telethon/_misc/messagepacker.py index c0f46f48..f4efb1ac 100644 --- a/telethon/_misc/messagepacker.py +++ b/telethon/_misc/messagepacker.py @@ -3,9 +3,9 @@ import collections import io import struct -from ..tl import TLRequest -from ..tl.core.messagecontainer import MessageContainer -from ..tl.core.tlmessage import TLMessage +from .._tl import TLRequest +from .._tl.core.messagecontainer import MessageContainer +from .._tl.core.tlmessage import TLMessage class MessagePacker: diff --git a/telethon/_misc/password.py b/telethon/_misc/password.py index e02c8eb8..b18e6b10 100644 --- a/telethon/_misc/password.py +++ b/telethon/_misc/password.py @@ -1,8 +1,8 @@ import hashlib import os -from .crypto import factorization -from . import _tl +from .._crypto import factorization +from .. import _tl def check_prime_and_good_check(prime: int, g: int): diff --git a/telethon/_misc/statecache.py b/telethon/_misc/statecache.py index 3f2475bf..7f3ddf59 100644 --- a/telethon/_misc/statecache.py +++ b/telethon/_misc/statecache.py @@ -1,6 +1,6 @@ import inspect -from . import _tl +from .. import _tl # Which updates have the following fields? diff --git a/telethon/_misc/utils.py b/telethon/_misc/utils.py index 9826e551..956a154d 100644 --- a/telethon/_misc/utils.py +++ b/telethon/_misc/utils.py @@ -19,9 +19,9 @@ from collections import namedtuple from mimetypes import guess_extension from types import GeneratorType -from .extensions import markdown, html from .helpers import add_surrogate, del_surrogate, strip_text -from . import _tl +from . import markdown, html +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. -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('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('video/mp4', '.mp4') -mime_tl.add_type('video/quicktime', '.mov') -mime_tl.add_type('video/avi', '.avi') +mimetypes.add_type('video/mp4', '.mp4') +mimetypes.add_type('video/quicktime', '.mov') +mimetypes.add_type('video/avi', '.avi') -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('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('application/x-tgsticker', '.tgs') +mimetypes.add_type('application/x-tgsticker', '.tgs') USERNAME_RE = re.compile( r'@|(?:https?://)?(?:www\.)?(?:telegram\.(?:me|dog)|t\.me)/(@|joinchat/)?' @@ -675,7 +675,7 @@ 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 = mime_tl.guess_type(name)[0] + mime_type = mimetypes.guess_type(name)[0] attr_dict = {_tl.DocumentAttributeFilename: _tl.DocumentAttributeFilename(os.path.basename(name))} @@ -881,7 +881,7 @@ def is_audio(file): return False else: file = 'a' + ext - return (mime_tl.guess_type(file)[0] or '').startswith('audio/') + return (mimetypes.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 (mime_tl.guess_type(file)[0] or '').startswith('video/') + return (mimetypes.guess_type(file)[0] or '').startswith('video/') def is_list_like(obj): diff --git a/telethon/_network/authenticator.py b/telethon/_network/authenticator.py index 04b6f5e3..f5b3591c 100644 --- a/telethon/_network/authenticator.py +++ b/telethon/_network/authenticator.py @@ -7,9 +7,9 @@ import time from hashlib import sha1 from .. import helpers, _tl -from ..crypto import AES, AuthKey, Factorization, rsa +from .._crypto import AES, AuthKey, Factorization, rsa from ..errors import SecurityError -from ..extensions import BinaryReader +from .._misc import BinaryReader async def do_authentication(sender): diff --git a/telethon/_network/connection/tcpmtproxy.py b/telethon/_network/connection/tcpmtproxy.py index 69a43bce..db18a61c 100644 --- a/telethon/_network/connection/tcpmtproxy.py +++ b/telethon/_network/connection/tcpmtproxy.py @@ -9,7 +9,7 @@ from .tcpintermediate import ( RandomizedIntermediatePacketCodec ) -from ...crypto import AESModeCTR +from ..._crypto import AESModeCTR class MTProxyIO: diff --git a/telethon/_network/connection/tcpobfuscated.py b/telethon/_network/connection/tcpobfuscated.py index cf2e6af5..2aeeeac1 100644 --- a/telethon/_network/connection/tcpobfuscated.py +++ b/telethon/_network/connection/tcpobfuscated.py @@ -3,7 +3,7 @@ import os from .tcpabridged import AbridgedPacketCodec from .connection import ObfuscatedConnection -from ...crypto import AESModeCTR +from ..._crypto import AESModeCTR class ObfuscatedIO: diff --git a/telethon/_network/mtprotoplainsender.py b/telethon/_network/mtprotoplainsender.py index 563affd7..433c3795 100644 --- a/telethon/_network/mtprotoplainsender.py +++ b/telethon/_network/mtprotoplainsender.py @@ -6,7 +6,7 @@ import struct from .mtprotostate import MTProtoState from ..errors import InvalidBufferError -from ..extensions import BinaryReader +from .._misc import BinaryReader class MTProtoPlainSender: diff --git a/telethon/_network/mtprotosender.py b/telethon/_network/mtprotosender.py index ca592ac0..59f14a7a 100644 --- a/telethon/_network/mtprotosender.py +++ b/telethon/_network/mtprotosender.py @@ -3,27 +3,20 @@ import collections import struct from . import authenticator -from ..extensions.messagepacker import MessagePacker +from .._misc.messagepacker import MessagePacker from .mtprotoplainsender import MTProtoPlainSender from .requeststate import RequestState from .mtprotostate import MTProtoState -from ..tl.tlobject import TLRequest -from .. import helpers, utils +from .._tl.tlobject import TLRequest +from .. import helpers, utils, _tl from ..errors import ( BadMessageError, InvalidBufferError, SecurityError, TypeNotFoundError, rpc_message_to_error ) -from ..extensions import BinaryReader -from ..tl.core import RpcResult, MessageContainer, GzipPacked -from ..tl.functions.auth import LogOutRequest -from ..tl.functions import PingRequest, DestroySessionRequest -from ..tl.types import ( - MsgsAck, Pong, BadServerSalt, BadMsgNotification, FutureSalts, - MsgNewDetailedInfo, NewSessionCreated, MsgDetailedInfo, MsgsStateReq, - MsgsStateInfo, MsgsAllInfo, MsgResendReq, upload, DestroySessionOk, DestroySessionNone, -) -from ..crypto import AuthKey -from ..helpers import retry_range +from .._misc import BinaryReader +from .._tl.core import RpcResult, MessageContainer, GzipPacked +from .._crypto import AuthKey +from .._misc.helpers import retry_range class MTProtoSender: @@ -97,19 +90,19 @@ class MTProtoSender: RpcResult.CONSTRUCTOR_ID: self._handle_rpc_result, MessageContainer.CONSTRUCTOR_ID: self._handle_container, GzipPacked.CONSTRUCTOR_ID: self._handle_gzip_packed, - Pong.CONSTRUCTOR_ID: self._handle_pong, - BadServerSalt.CONSTRUCTOR_ID: self._handle_bad_server_salt, - BadMsgNotification.CONSTRUCTOR_ID: self._handle_bad_notification, - MsgDetailedInfo.CONSTRUCTOR_ID: self._handle_detailed_info, - MsgNewDetailedInfo.CONSTRUCTOR_ID: self._handle_new_detailed_info, - NewSessionCreated.CONSTRUCTOR_ID: self._handle_new_session_created, - MsgsAck.CONSTRUCTOR_ID: self._handle_ack, - FutureSalts.CONSTRUCTOR_ID: self._handle_future_salts, - MsgsStateReq.CONSTRUCTOR_ID: self._handle_state_forgotten, - MsgResendReq.CONSTRUCTOR_ID: self._handle_state_forgotten, - MsgsAllInfo.CONSTRUCTOR_ID: self._handle_msg_all, - DestroySessionOk: self._handle_destroy_session, - DestroySessionNone: self._handle_destroy_session, + _tl.Pong.CONSTRUCTOR_ID: self._handle_pong, + _tl.BadServerSalt.CONSTRUCTOR_ID: self._handle_bad_server_salt, + _tl.BadMsgNotification.CONSTRUCTOR_ID: self._handle_bad_notification, + _tl.MsgDetailedInfo.CONSTRUCTOR_ID: self._handle_detailed_info, + _tl.MsgNewDetailedInfo.CONSTRUCTOR_ID: self._handle_new_detailed_info, + _tl.NewSessionCreated.CONSTRUCTOR_ID: self._handle_new_session_created, + _tl.MsgsAck.CONSTRUCTOR_ID: self._handle_ack, + _tl.FutureSalts.CONSTRUCTOR_ID: self._handle_future_salts, + _tl.MsgsStateReq.CONSTRUCTOR_ID: self._handle_state_forgotten, + _tl.MsgResendReq.CONSTRUCTOR_ID: self._handle_state_forgotten, + _tl.MsgsAllInfo.CONSTRUCTOR_ID: self._handle_msg_all, + _tl.DestroySessionOk: self._handle_destroy_session, + _tl.DestroySessionNone: self._handle_destroy_session, } # Public API @@ -433,7 +426,7 @@ class MTProtoSender: # TODO this is ugly, update loop shouldn't worry about this, sender should if self._ping is None: self._ping = rnd_id - self.send(PingRequest(rnd_id)) + self.send(_tl.fn.Ping(rnd_id)) else: self._start_reconnect(None) @@ -448,7 +441,7 @@ class MTProtoSender: """ while self._user_connected and not self._reconnecting: if self._pending_ack: - ack = RequestState(MsgsAck(list(self._pending_ack))) + ack = RequestState(_tl.MsgsAck(list(self._pending_ack))) self._send_queue.append(ack) self._last_acks.append(ack) self._pending_ack.clear() @@ -598,7 +591,7 @@ class MTProtoSender: # which contain the real response right after. try: with BinaryReader(rpc_result.body) as reader: - if not isinstance(reader.tgread_object(), upload.File): + if not isinstance(reader.tgread_object(), _tl.upload.File): raise ValueError('Not an upload.File') except (TypeNotFoundError, ValueError): self._log.info('Received response without parent request: %s', rpc_result.body) @@ -607,7 +600,7 @@ class MTProtoSender: if rpc_result.error: error = rpc_message_to_error(rpc_result.error, state.request) self._send_queue.append( - RequestState(MsgsAck([state.msg_id]))) + RequestState(_tl.MsgsAck([state.msg_id]))) if not state.future.cancelled(): state.future.set_exception(error) @@ -777,7 +770,7 @@ class MTProtoSender: self._log.debug('Handling acknowledge for %s', str(ack.msg_ids)) for msg_id in ack.msg_ids: state = self._pending_state.get(msg_id) - if state and isinstance(state.request, LogOutRequest): + if state and isinstance(state.request, _tl.fn.auth.LogOut): del self._pending_state[msg_id] if not state.future.cancelled(): state.future.set_result(True) @@ -802,7 +795,7 @@ class MTProtoSender: Handles both :tl:`MsgsStateReq` and :tl:`MsgResendReq` by enqueuing a :tl:`MsgsStateInfo` to be sent at a later point. """ - self._send_queue.append(RequestState(MsgsStateInfo( + self._send_queue.append(RequestState(_tl.MsgsStateInfo( req_msg_id=message.msg_id, info=chr(1) * len(message.obj.msg_ids) ))) @@ -817,7 +810,7 @@ class MTProtoSender: It behaves pretty much like handling an RPC result. """ for msg_id, state in self._pending_state.items(): - if isinstance(state.request, DestroySessionRequest)\ + if isinstance(state.request, _tl.fn.DestroySession)\ and state.request.session_id == message.obj.session_id: break else: diff --git a/telethon/_network/mtprotostate.py b/telethon/_network/mtprotostate.py index 0fe9cc08..e2c6c21a 100644 --- a/telethon/_network/mtprotostate.py +++ b/telethon/_network/mtprotostate.py @@ -3,13 +3,13 @@ import struct import time from hashlib import sha256 -from ..crypto import AES +from .._crypto import AES from ..errors import SecurityError, InvalidBufferError -from ..extensions import BinaryReader -from ..tl.core import TLMessage -from ..tl.tlobject import TLRequest -from ..tl.functions import InvokeAfterMsgRequest -from ..tl.core.gzippacked import GzipPacked +from .._misc import BinaryReader +from .._tl.core import TLMessage +from .._tl.tlobject import TLRequest +from .. import _tl +from .._tl.core.gzippacked import GzipPacked class _OpaqueRequest(TLRequest): @@ -103,7 +103,7 @@ class MTProtoState: # The `RequestState` stores `bytes(request)`, not the request itself. # `invokeAfterMsg` wants a `TLRequest` though, hence the wrapping. body = GzipPacked.gzip_if_smaller(content_related, - bytes(InvokeAfterMsgRequest(after_id, _OpaqueRequest(data)))) + bytes(_tl.fn.InvokeAfterMsgRequest(after_id, _OpaqueRequest(data)))) buffer.write(struct.pack('`). """ - return isinstance(self._chat_peer, types.PeerUser) if self._chat_peer else None + return isinstance(self._chat_peer, _tl.PeerUser) if self._chat_peer else None @property def is_group(self): @@ -128,20 +127,20 @@ class ChatGetter(abc.ABC): if self._broadcast is None and hasattr(self.chat, 'broadcast'): self._broadcast = bool(self.chat.broadcast) - if isinstance(self._chat_peer, types.PeerChannel): + if isinstance(self._chat_peer, _tl.PeerChannel): if self._broadcast is None: return None else: return not self._broadcast - return isinstance(self._chat_peer, types.PeerChat) + return isinstance(self._chat_peer, _tl.PeerChat) @property def is_channel(self): """`True` if the message was sent on a megagroup or channel.""" # The only case where chat peer could be none is in MessageDeleted, # however those always have the peer in channels. - return isinstance(self._chat_peer, types.PeerChannel) + return isinstance(self._chat_peer, _tl.PeerChannel) async def _refetch_chat(self): """ diff --git a/telethon/_tl/custom/dialog.py b/telethon/_tl/custom/dialog.py index 955bbdf2..cc307b4b 100644 --- a/telethon/_tl/custom/dialog.py +++ b/telethon/_tl/custom/dialog.py @@ -1,6 +1,6 @@ from . import Draft -from .. import TLObject, types, functions -from ... import utils +from ... import _tl +from ..._misc import utils class Dialog: @@ -89,12 +89,12 @@ class Dialog: self.draft = Draft(client, self.entity, self.dialog.draft) - self.is_user = isinstance(self.entity, types.User) + self.is_user = isinstance(self.entity, _tl.User) self.is_group = ( - isinstance(self.entity, (types.Chat, types.ChatForbidden)) or - (isinstance(self.entity, types.Channel) and self.entity.megagroup) + isinstance(self.entity, (_tl.Chat, _tl.ChatForbidden)) or + (isinstance(self.entity, _tl.Channel) and self.entity.megagroup) ) - self.is_channel = isinstance(self.entity, types.Channel) + self.is_channel = isinstance(self.entity, _tl.Channel) async def send_message(self, *args, **kwargs): """ @@ -141,7 +141,7 @@ class Dialog: dialog.archive(0) """ return await self._client(_tl.fn.folders.EditPeerFolders([ - types.InputFolderPeer(self.input_entity, folder_id=folder) + _tl.InputFolderPeer(self.input_entity, folder_id=folder) ])) def to_dict(self): @@ -155,7 +155,7 @@ class Dialog: } def __str__(self): - return TLObject.pretty_format(self.to_dict()) + return _tl.TLObject.pretty_format(self.to_dict()) def stringify(self): - return TLObject.pretty_format(self.to_dict(), indent=0) + return _tl.TLObject.pretty_format(self.to_dict(), indent=0) diff --git a/telethon/_tl/custom/draft.py b/telethon/_tl/custom/draft.py index ed6360b0..fb1df26f 100644 --- a/telethon/_tl/custom/draft.py +++ b/telethon/_tl/custom/draft.py @@ -2,8 +2,8 @@ import datetime from ... import _tl from ...errors import RPCError -from ...extensions import markdown -from ...utils import get_input_peer, get_peer +from ..._misc import markdown +from ..._misc.utils import get_input_peer, get_peer class Draft: diff --git a/telethon/_tl/custom/file.py b/telethon/_tl/custom/file.py index 210eb53d..228727ea 100644 --- a/telethon/_tl/custom/file.py +++ b/telethon/_tl/custom/file.py @@ -1,8 +1,8 @@ import mimetypes import os -from ... import utils -from ...tl import types +from ..._misc import utils +from ... import _tl class File: @@ -38,7 +38,7 @@ class File: """ The file name of this document. """ - return self._from_attr(types.DocumentAttributeFilename, 'file_name') + return self._from_attr(_tl.DocumentAttributeFilename, 'file_name') @property def ext(self): @@ -49,7 +49,7 @@ class File: from the file name (if any) will be used. """ return ( - mimetypes.guess_extension(self.mime_type) + mime_tl.guess_extension(self.mime_type) or os.path.splitext(self.name or '')[-1] or None ) @@ -59,9 +59,9 @@ class File: """ The mime-type of this file. """ - if isinstance(self.media, types.Photo): + if isinstance(self.media, _tl.Photo): return 'image/jpeg' - elif isinstance(self.media, types.Document): + elif isinstance(self.media, _tl.Document): return self.media.mime_type @property @@ -69,22 +69,22 @@ class File: """ The width in pixels of this media if it's a photo or a video. """ - if isinstance(self.media, types.Photo): + if isinstance(self.media, _tl.Photo): return max(getattr(s, 'w', 0) for s in self.media.sizes) return self._from_attr(( - types.DocumentAttributeImageSize, types.DocumentAttributeVideo), 'w') + _tl.DocumentAttributeImageSize, _tl.DocumentAttributeVideo), 'w') @property def height(self): """ The height in pixels of this media if it's a photo or a video. """ - if isinstance(self.media, types.Photo): + if isinstance(self.media, _tl.Photo): return max(getattr(s, 'h', 0) for s in self.media.sizes) return self._from_attr(( - types.DocumentAttributeImageSize, types.DocumentAttributeVideo), 'h') + _tl.DocumentAttributeImageSize, _tl.DocumentAttributeVideo), 'h') @property def duration(self): @@ -92,35 +92,35 @@ class File: The duration in seconds of the audio or video. """ return self._from_attr(( - types.DocumentAttributeAudio, types.DocumentAttributeVideo), 'duration') + _tl.DocumentAttributeAudio, _tl.DocumentAttributeVideo), 'duration') @property def title(self): """ The title of the song. """ - return self._from_attr(types.DocumentAttributeAudio, 'title') + return self._from_attr(_tl.DocumentAttributeAudio, 'title') @property def performer(self): """ The performer of the song. """ - return self._from_attr(types.DocumentAttributeAudio, 'performer') + return self._from_attr(_tl.DocumentAttributeAudio, 'performer') @property def emoji(self): """ A string with all emoji that represent the current sticker. """ - return self._from_attr(types.DocumentAttributeSticker, 'alt') + return self._from_attr(_tl.DocumentAttributeSticker, 'alt') @property def sticker_set(self): """ The :tl:`InputStickerSet` to which the sticker file belongs. """ - return self._from_attr(types.DocumentAttributeSticker, 'stickerset') + return self._from_attr(_tl.DocumentAttributeSticker, 'stickerset') @property def size(self): @@ -129,13 +129,13 @@ class File: For photos, this is the heaviest thumbnail, as it often repressents the largest dimensions. """ - if isinstance(self.media, types.Photo): + if isinstance(self.media, _tl.Photo): return max(filter(None, map(utils._photo_size_byte_count, self.media.sizes)), default=None) - elif isinstance(self.media, types.Document): + elif isinstance(self.media, _tl.Document): return self.media.size def _from_attr(self, cls, field): - if isinstance(self.media, types.Document): + if isinstance(self.media, _tl.Document): for attr in self.media.attributes: if isinstance(attr, cls): return getattr(attr, field, None) diff --git a/telethon/_tl/custom/forward.py b/telethon/_tl/custom/forward.py index a95eae30..d6a46cb7 100644 --- a/telethon/_tl/custom/forward.py +++ b/telethon/_tl/custom/forward.py @@ -1,7 +1,6 @@ from .chatgetter import ChatGetter from .sendergetter import SenderGetter -from ... import utils, helpers -from ...tl import types +from ..._misc import utils, helpers class Forward(ChatGetter, SenderGetter): diff --git a/telethon/_tl/custom/inlinebuilder.py b/telethon/_tl/custom/inlinebuilder.py index f3851f35..b401ab04 100644 --- a/telethon/_tl/custom/inlinebuilder.py +++ b/telethon/_tl/custom/inlinebuilder.py @@ -1,7 +1,7 @@ import hashlib -from .. import functions, types -from ... import utils +from ... import _tl +from ..._misc import utils _TYPE_TO_MIMES = { 'gif': ['image/gif'], # 'video/mp4' too, but that's used for video @@ -126,7 +126,7 @@ class InlineBuilder: # TODO Does 'article' work always? # article, photo, gif, mpeg4_gif, video, audio, # voice, document, location, venue, contact, game - result = types.InputBotInlineResult( + result = _tl.InputBotInlineResult( id=id or '', type='article', send_message=await self._message( @@ -194,15 +194,15 @@ class InlineBuilder: _, media, _ = await self._client._file_to_media( file, allow_cache=True, as_image=True ) - if isinstance(media, types.InputPhoto): + if isinstance(media, _tl.InputPhoto): fh = media else: r = await self._client(_tl.fn.messages.UploadMedia( - types.InputPeerSelf(), media=media + _tl.InputPeerSelf(), media=media )) fh = utils.get_input_photo(r.photo) - result = types.InputBotInlineResultPhoto( + result = _tl.InputBotInlineResultPhoto( id=id or '', type='photo', photo=fh, @@ -314,15 +314,15 @@ class InlineBuilder: video_note=video_note, allow_cache=use_cache ) - if isinstance(media, types.InputDocument): + if isinstance(media, _tl.InputDocument): fh = media else: r = await self._client(_tl.fn.messages.UploadMedia( - types.InputPeerSelf(), media=media + _tl.InputPeerSelf(), media=media )) fh = utils.get_input_document(r.document) - result = types.InputBotInlineResultDocument( + result = _tl.InputBotInlineResultDocument( id=id or '', type=type, document=fh, @@ -361,7 +361,7 @@ class InlineBuilder: short_name (`str`): The short name of the game to use. """ - result = types.InputBotInlineResultGame( + result = _tl.InputBotInlineResultGame( id=id or '', short_name=short_name, send_message=await self._message( @@ -400,31 +400,31 @@ class InlineBuilder: # "MediaAuto" means it will use whatever media the inline # result itself has (stickers, photos, or documents), while # respecting the user's text (caption) and formatting. - return types.InputBotInlineMessageMediaAuto( + return _tl.InputBotInlineMessageMediaAuto( message=text, entities=msg_entities, reply_markup=markup ) else: - return types.InputBotInlineMessageText( + return _tl.InputBotInlineMessageText( message=text, no_webpage=not link_preview, entities=msg_entities, reply_markup=markup ) - elif isinstance(geo, (types.InputGeoPoint, types.GeoPoint)): - return types.InputBotInlineMessageMediaGeo( + elif isinstance(geo, (_tl.InputGeoPoint, _tl.GeoPoint)): + return _tl.InputBotInlineMessageMediaGeo( geo_point=utils.get_input_geo(geo), period=period, reply_markup=markup ) - elif isinstance(geo, (types.InputMediaVenue, types.MessageMediaVenue)): - if isinstance(geo, types.InputMediaVenue): + elif isinstance(geo, (_tl.InputMediaVenue, _tl.MessageMediaVenue)): + if isinstance(geo, _tl.InputMediaVenue): geo_point = geo.geo_point else: geo_point = geo.geo - return types.InputBotInlineMessageMediaVenue( + return _tl.InputBotInlineMessageMediaVenue( geo_point=geo_point, title=geo.title, address=geo.address, @@ -434,8 +434,8 @@ class InlineBuilder: reply_markup=markup ) elif isinstance(contact, ( - types.InputMediaContact, types.MessageMediaContact)): - return types.InputBotInlineMessageMediaContact( + _tl.InputMediaContact, _tl.MessageMediaContact)): + return _tl.InputBotInlineMessageMediaContact( phone_number=contact.phone_number, first_name=contact.first_name, last_name=contact.last_name, @@ -443,7 +443,7 @@ class InlineBuilder: reply_markup=markup ) elif game: - return types.InputBotInlineMessageGame( + return _tl.InputBotInlineMessageGame( reply_markup=markup ) else: diff --git a/telethon/_tl/custom/inlineresult.py b/telethon/_tl/custom/inlineresult.py index eefbd7b4..fa617af1 100644 --- a/telethon/_tl/custom/inlineresult.py +++ b/telethon/_tl/custom/inlineresult.py @@ -1,5 +1,5 @@ -from .. import types, functions -from ... import utils +from ... import _tl +from ..._misc import utils class InlineResult: diff --git a/telethon/_tl/custom/inputsizedfile.py b/telethon/_tl/custom/inputsizedfile.py index fcb743f6..4183ecb7 100644 --- a/telethon/_tl/custom/inputsizedfile.py +++ b/telethon/_tl/custom/inputsizedfile.py @@ -1,7 +1,7 @@ -from ..types import InputFile +from ... import _tl -class InputSizedFile(InputFile): +class InputSizedFile(_tl.InputFile): """InputFile class with two extra parameters: md5 (digest) and size""" def __init__(self, id_, parts, name, md5, size): super().__init__(id_, parts, name, md5.hexdigest()) diff --git a/telethon/_tl/custom/message.py b/telethon/_tl/custom/message.py index fb134ac3..7672de2c 100644 --- a/telethon/_tl/custom/message.py +++ b/telethon/_tl/custom/message.py @@ -5,13 +5,13 @@ from .sendergetter import SenderGetter from .messagebutton import MessageButton from .forward import Forward from .file import File -from .. import TLObject, types, functions, alltlobjects -from ... import utils, errors +from ..._misc import utils +from ... import errors, _tl # TODO Figure out a way to have the code generator error on missing fields # Maybe parsing the init function alone if that's possible. -class Message(ChatGetter, SenderGetter, TLObject): +class Message(ChatGetter, SenderGetter, _tl.TLObject): """ This custom class aggregates both :tl:`Message` and :tl:`MessageService` to ease accessing their members. @@ -163,7 +163,7 @@ class Message(ChatGetter, SenderGetter, TLObject): self, id: int, # Common to Message and MessageService (mandatory) - peer_id: types.TypePeer = None, + peer_id: _tl.TypePeer = None, date: Optional[datetime] = None, # Common to Message and MessageService (flags) @@ -172,19 +172,19 @@ class Message(ChatGetter, SenderGetter, TLObject): media_unread: Optional[bool] = None, silent: Optional[bool] = None, post: Optional[bool] = None, - from_id: Optional[types.TypePeer] = None, - reply_to: Optional[types.TypeMessageReplyHeader] = None, + from_id: Optional[_tl.TypePeer] = None, + reply_to: Optional[_tl.TypeMessageReplyHeader] = None, ttl_period: Optional[int] = None, # For Message (mandatory) message: Optional[str] = None, # For Message (flags) - fwd_from: Optional[types.TypeMessageFwdHeader] = None, + fwd_from: Optional[_tl.TypeMessageFwdHeader] = None, via_bot_id: Optional[int] = None, - media: Optional[types.TypeMessageMedia] = None, - reply_markup: Optional[types.TypeReplyMarkup] = None, - entities: Optional[List[types.TypeMessageEntity]] = None, + media: Optional[_tl.TypeMessageMedia] = None, + reply_markup: Optional[_tl.TypeReplyMarkup] = None, + entities: Optional[List[_tl.TypeMessageEntity]] = None, views: Optional[int] = None, edit_date: Optional[datetime] = None, post_author: Optional[str] = None, @@ -193,12 +193,12 @@ class Message(ChatGetter, SenderGetter, TLObject): legacy: Optional[bool] = None, edit_hide: Optional[bool] = None, pinned: Optional[bool] = None, - restriction_reason: Optional[types.TypeRestrictionReason] = None, + restriction_reason: Optional[_tl.TypeRestrictionReason] = None, forwards: Optional[int] = None, - replies: Optional[types.TypeMessageReplies] = None, + replies: Optional[_tl.TypeMessageReplies] = None, # For MessageAction (mandatory) - action: Optional[types.TypeMessageAction] = None + action: Optional[_tl.TypeMessageAction] = None ): # Common properties to messages, then to service (in the order they're defined in the `.tl`) self.out = bool(out) @@ -217,7 +217,7 @@ class Message(ChatGetter, SenderGetter, TLObject): self.reply_to = reply_to self.date = date self.message = message - self.media = None if isinstance(media, types.MessageMediaEmpty) else media + self.media = None if isinstance(media, _tl.MessageMediaEmpty) else media self.reply_markup = reply_markup self.entities = entities self.views = views @@ -253,7 +253,7 @@ class Message(ChatGetter, SenderGetter, TLObject): # ...or... # incoming messages in private conversations no longer have from_id # (layer 119+), but the sender can only be the chat we're in. - if post or (not out and isinstance(peer_id, types.PeerUser)): + if post or (not out and isinstance(peer_id, _tl.PeerUser)): sender_id = utils.get_peer_id(peer_id) # Note that these calls would reset the client @@ -272,7 +272,7 @@ class Message(ChatGetter, SenderGetter, TLObject): # Make messages sent to ourselves outgoing unless they're forwarded. # This makes it consistent with official client's appearance. - if self.peer_id == types.PeerUser(client._self_id) and not self.fwd_from: + if self.peer_id == _tl.PeerUser(client._self_id) and not self.fwd_from: self.out = True cache = client._entity_cache @@ -294,25 +294,25 @@ class Message(ChatGetter, SenderGetter, TLObject): self._forward = Forward(self._client, self.fwd_from, entities) if self.action: - if isinstance(self.action, (types.MessageActionChatAddUser, - types.MessageActionChatCreate)): + if isinstance(self.action, (_tl.MessageActionChatAddUser, + _tl.MessageActionChatCreate)): self._action_entities = [entities.get(i) for i in self.action.users] - elif isinstance(self.action, types.MessageActionChatDeleteUser): + elif isinstance(self.action, _tl.MessageActionChatDeleteUser): self._action_entities = [entities.get(self.action.user_id)] - elif isinstance(self.action, types.MessageActionChatJoinedByLink): + elif isinstance(self.action, _tl.MessageActionChatJoinedByLink): self._action_entities = [entities.get(self.action.inviter_id)] - elif isinstance(self.action, types.MessageActionChatMigrateTo): + elif isinstance(self.action, _tl.MessageActionChatMigrateTo): self._action_entities = [entities.get(utils.get_peer_id( - types.PeerChannel(self.action.channel_id)))] + _tl.PeerChannel(self.action.channel_id)))] elif isinstance( - self.action, types.MessageActionChannelMigrateFrom): + self.action, _tl.MessageActionChannelMigrateFrom): self._action_entities = [entities.get(utils.get_peer_id( - types.PeerChat(self.action.chat_id)))] + _tl.PeerChat(self.action.chat_id)))] if self.replies and self.replies.channel_id: self._linked_chat = entities.get(utils.get_peer_id( - types.PeerChannel(self.replies.channel_id))) + _tl.PeerChannel(self.replies.channel_id))) # endregion Initialization @@ -435,7 +435,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ if self._buttons_count is None: if isinstance(self.reply_markup, ( - types.ReplyInlineMarkup, types.ReplyKeyboardMarkup)): + _tl.ReplyInlineMarkup, _tl.ReplyKeyboardMarkup)): self._buttons_count = sum( len(row.buttons) for row in self.reply_markup.rows) else: @@ -471,14 +471,14 @@ class Message(ChatGetter, SenderGetter, TLObject): action is :tl:`MessageActionChatEditPhoto`, or if the message has a web preview with a photo. """ - if isinstance(self.media, types.MessageMediaPhoto): - if isinstance(self.media.photo, types.Photo): + if isinstance(self.media, _tl.MessageMediaPhoto): + if isinstance(self.media.photo, _tl.Photo): return self.media.photo - elif isinstance(self.action, types.MessageActionChatEditPhoto): + elif isinstance(self.action, _tl.MessageActionChatEditPhoto): return self.action.photo else: web = self.web_preview - if web and isinstance(web.photo, types.Photo): + if web and isinstance(web.photo, _tl.Photo): return web.photo @property @@ -486,12 +486,12 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`Document` media in this message, if any. """ - if isinstance(self.media, types.MessageMediaDocument): - if isinstance(self.media.document, types.Document): + if isinstance(self.media, _tl.MessageMediaDocument): + if isinstance(self.media.document, _tl.Document): return self.media.document else: web = self.web_preview - if web and isinstance(web.document, types.Document): + if web and isinstance(web.document, _tl.Document): return web.document @property @@ -499,8 +499,8 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`WebPage` media in this message, if any. """ - if isinstance(self.media, types.MessageMediaWebPage): - if isinstance(self.media.webpage, types.WebPage): + if isinstance(self.media, _tl.MessageMediaWebPage): + if isinstance(self.media.webpage, _tl.WebPage): return self.media.webpage @property @@ -508,7 +508,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`Document` media in this message, if it's an audio file. """ - return self._document_by_attribute(types.DocumentAttributeAudio, + return self._document_by_attribute(_tl.DocumentAttributeAudio, lambda attr: not attr.voice) @property @@ -516,7 +516,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`Document` media in this message, if it's a voice note. """ - return self._document_by_attribute(types.DocumentAttributeAudio, + return self._document_by_attribute(_tl.DocumentAttributeAudio, lambda attr: attr.voice) @property @@ -524,14 +524,14 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`Document` media in this message, if it's a video. """ - return self._document_by_attribute(types.DocumentAttributeVideo) + return self._document_by_attribute(_tl.DocumentAttributeVideo) @property def video_note(self): """ The :tl:`Document` media in this message, if it's a video note. """ - return self._document_by_attribute(types.DocumentAttributeVideo, + return self._document_by_attribute(_tl.DocumentAttributeVideo, lambda attr: attr.round_message) @property @@ -543,21 +543,21 @@ class Message(ChatGetter, SenderGetter, TLObject): sound, the so called "animated" media. However, it may be the actual gif format if the file is too large. """ - return self._document_by_attribute(types.DocumentAttributeAnimated) + return self._document_by_attribute(_tl.DocumentAttributeAnimated) @property def sticker(self): """ The :tl:`Document` media in this message, if it's a sticker. """ - return self._document_by_attribute(types.DocumentAttributeSticker) + return self._document_by_attribute(_tl.DocumentAttributeSticker) @property def contact(self): """ The :tl:`MessageMediaContact` in this message, if it's a contact. """ - if isinstance(self.media, types.MessageMediaContact): + if isinstance(self.media, _tl.MessageMediaContact): return self.media @property @@ -565,7 +565,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`Game` media in this message, if it's a game. """ - if isinstance(self.media, types.MessageMediaGame): + if isinstance(self.media, _tl.MessageMediaGame): return self.media.game @property @@ -573,9 +573,9 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`GeoPoint` media in this message, if it has a location. """ - if isinstance(self.media, (types.MessageMediaGeo, - types.MessageMediaGeoLive, - types.MessageMediaVenue)): + if isinstance(self.media, (_tl.MessageMediaGeo, + _tl.MessageMediaGeoLive, + _tl.MessageMediaVenue)): return self.media.geo @property @@ -583,7 +583,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`MessageMediaInvoice` in this message, if it's an invoice. """ - if isinstance(self.media, types.MessageMediaInvoice): + if isinstance(self.media, _tl.MessageMediaInvoice): return self.media @property @@ -591,7 +591,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`MessageMediaPoll` in this message, if it's a poll. """ - if isinstance(self.media, types.MessageMediaPoll): + if isinstance(self.media, _tl.MessageMediaPoll): return self.media @property @@ -599,7 +599,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`MessageMediaVenue` in this message, if it's a venue. """ - if isinstance(self.media, types.MessageMediaVenue): + if isinstance(self.media, _tl.MessageMediaVenue): return self.media @property @@ -607,7 +607,7 @@ class Message(ChatGetter, SenderGetter, TLObject): """ The :tl:`MessageMediaDice` in this message, if it's a dice roll. """ - if isinstance(self.media, types.MessageMediaDice): + if isinstance(self.media, _tl.MessageMediaDice): return self.media @property @@ -616,7 +616,7 @@ class Message(ChatGetter, SenderGetter, TLObject): Returns a list of entities that took part in this action. Possible cases for this are :tl:`MessageActionChatAddUser`, - :tl:`types.MessageActionChatCreate`, :tl:`MessageActionChatDeleteUser`, + :tl:`_tl.MessageActionChatCreate`, :tl:`MessageActionChatDeleteUser`, :tl:`MessageActionChatJoinedByLink` :tl:`MessageActionChatMigrateTo` and :tl:`MessageActionChannelMigrateFrom`. @@ -660,7 +660,7 @@ class Message(ChatGetter, SenderGetter, TLObject): # If the client wasn't set we can't emulate the behaviour correctly, # so as a best-effort simply return the chat peer. if self._client and not self.out and self.is_private: - return types.PeerUser(self._client._self_id) + return _tl.PeerUser(self._client._self_id) return self.peer_id @@ -722,7 +722,7 @@ class Message(ChatGetter, SenderGetter, TLObject): # However they can access them through replies... self._reply_message = await self._client.get_messages( await self.get_input_chat() if self.is_channel else None, - ids=types.InputMessageReplyTo(self.id) + ids=_tl.InputMessageReplyTo(self.id) ) if not self._reply_message: # ...unless the current message got deleted. @@ -883,7 +883,7 @@ class Message(ChatGetter, SenderGetter, TLObject): Clicks the first button or poll option for which the callable returns `True`. The callable should accept a single `MessageButton ` - or `PollAnswer ` argument. + or `PollAnswer ` argument. If you need to select multiple options in a poll, pass a list of indices to the ``i`` parameter. @@ -950,7 +950,7 @@ class Message(ChatGetter, SenderGetter, TLObject): if not chat: return None - but = types.KeyboardButtonCallback('', data) + but = _tl.KeyboardButtonCallback('', data) return await MessageButton(self._client, but, chat, None, self.id).click( share_phone=share_phone, share_geo=share_geo, password=password) @@ -1098,7 +1098,7 @@ class Message(ChatGetter, SenderGetter, TLObject): Helper methods to set the buttons given the input sender and chat. """ if self._client and isinstance(self.reply_markup, ( - types.ReplyInlineMarkup, types.ReplyKeyboardMarkup)): + _tl.ReplyInlineMarkup, _tl.ReplyKeyboardMarkup)): self._buttons = [[ MessageButton(self._client, button, chat, bot, self.id) for button in row.buttons @@ -1114,12 +1114,12 @@ class Message(ChatGetter, SenderGetter, TLObject): cannot be found but is needed. Returns `None` if it's not needed. """ if self._client and not isinstance(self.reply_markup, ( - types.ReplyInlineMarkup, types.ReplyKeyboardMarkup)): + _tl.ReplyInlineMarkup, _tl.ReplyKeyboardMarkup)): return None for row in self.reply_markup.rows: for button in row.buttons: - if isinstance(button, types.KeyboardButtonSwitchInline): + if isinstance(button, _tl.KeyboardButtonSwitchInline): # no via_bot_id means the bot sent the message itself (#1619) if button.same_peer or not self.via_bot_id: bot = self.input_sender diff --git a/telethon/_tl/custom/messagebutton.py b/telethon/_tl/custom/messagebutton.py index 61e39dc6..8596ff97 100644 --- a/telethon/_tl/custom/messagebutton.py +++ b/telethon/_tl/custom/messagebutton.py @@ -1,5 +1,5 @@ -from .. import types, functions -from ... import password as pwd_mod +from ..._misc import password as pwd_mod +from ... import _tl from ...errors import BotResponseTimeoutError import webbrowser import os @@ -46,19 +46,19 @@ class MessageButton: @property def data(self): """The `bytes` data for :tl:`KeyboardButtonCallback` objects.""" - if isinstance(self.button, types.KeyboardButtonCallback): + if isinstance(self.button, _tl.KeyboardButtonCallback): return self.button.data @property def inline_query(self): """The query `str` for :tl:`KeyboardButtonSwitchInline` objects.""" - if isinstance(self.button, types.KeyboardButtonSwitchInline): + if isinstance(self.button, _tl.KeyboardButtonSwitchInline): return self.button.query @property def url(self): """The url `str` for :tl:`KeyboardButtonUrl` objects.""" - if isinstance(self.button, types.KeyboardButtonUrl): + if isinstance(self.button, _tl.KeyboardButtonUrl): return self.button.url async def click(self, share_phone=None, share_geo=None, *, password=None): @@ -91,10 +91,10 @@ class MessageButton: this value a lot quickly may not work as expected. You may also pass a :tl:`InputGeoPoint` if you find the order confusing. """ - if isinstance(self.button, types.KeyboardButton): + if isinstance(self.button, _tl.KeyboardButton): return await self._client.send_message( self._chat, self.button.text, parse_mode=None) - elif isinstance(self.button, types.KeyboardButtonCallback): + elif isinstance(self.button, _tl.KeyboardButtonCallback): if password is not None: pwd = await self._client(_tl.fn.account.GetPassword()) password = pwd_mod.compute_check(pwd, password) @@ -107,13 +107,13 @@ class MessageButton: return await self._client(req) except BotResponseTimeoutError: return None - elif isinstance(self.button, types.KeyboardButtonSwitchInline): + elif isinstance(self.button, _tl.KeyboardButtonSwitchInline): return await self._client(_tl.fn.messages.StartBot( bot=self._bot, peer=self._chat, start_param=self.button.query )) - elif isinstance(self.button, types.KeyboardButtonUrl): + elif isinstance(self.button, _tl.KeyboardButtonUrl): return webbrowser.open(self.button.url) - elif isinstance(self.button, types.KeyboardButtonGame): + elif isinstance(self.button, _tl.KeyboardButtonGame): req = _tl.fn.messages.GetBotCallbackAnswer( peer=self._chat, msg_id=self._msg_id, game=True ) @@ -121,13 +121,13 @@ class MessageButton: return await self._client(req) except BotResponseTimeoutError: return None - elif isinstance(self.button, types.KeyboardButtonRequestPhone): + elif isinstance(self.button, _tl.KeyboardButtonRequestPhone): if not share_phone: raise ValueError('cannot click on phone buttons unless share_phone=True') if share_phone == True or isinstance(share_phone, str): me = await self._client.get_me() - share_phone = types.InputMediaContact( + share_phone = _tl.InputMediaContact( phone_number=me.phone if share_phone == True else share_phone, first_name=me.first_name or '', last_name=me.last_name or '', @@ -135,12 +135,12 @@ class MessageButton: ) return await self._client.send_file(self._chat, share_phone) - elif isinstance(self.button, types.KeyboardButtonRequestGeoLocation): + elif isinstance(self.button, _tl.KeyboardButtonRequestGeoLocation): if not share_geo: raise ValueError('cannot click on geo buttons unless share_geo=(longitude, latitude)') if isinstance(share_geo, (tuple, list)): long, lat = share_geo - share_geo = types.InputMediaGeoPoint(types.InputGeoPoint(lat=lat, long=long)) + share_geo = _tl.InputMediaGeoPoint(_tl.InputGeoPoint(lat=lat, long=long)) return await self._client.send_file(self._chat, share_geo) diff --git a/telethon/_tl/custom/participantpermissions.py b/telethon/_tl/custom/participantpermissions.py index d3719778..6d4db912 100644 --- a/telethon/_tl/custom/participantpermissions.py +++ b/telethon/_tl/custom/participantpermissions.py @@ -1,4 +1,4 @@ -from .. import types +from ... import _tl def _admin_prop(field_name, doc): @@ -85,7 +85,7 @@ class ParticipantPermissions: Whether the user left the chat. """ return isinstance(self.participant, types.ChannelParticipantLeft) - + @property def add_admins(self): """ @@ -132,7 +132,7 @@ class ParticipantPermissions: anonymous = property(**_admin_prop('anonymous', """ Whether the administrator will remain anonymous when sending messages. """)) - + manage_call = property(**_admin_prop('manage_call', """ Whether the user will be able to manage group calls. """)) diff --git a/telethon/_tl/custom/qrlogin.py b/telethon/_tl/custom/qrlogin.py index 38105921..3f2a0207 100644 --- a/telethon/_tl/custom/qrlogin.py +++ b/telethon/_tl/custom/qrlogin.py @@ -2,8 +2,7 @@ import asyncio import base64 import datetime -from .. import types, functions -from ... import events +from ... import events, _tl class QRLogin: diff --git a/telethon/_tl/patched/__init__.py b/telethon/_tl/patched/__init__.py index 2951f2af..ddffeb4c 100644 --- a/telethon/_tl/patched/__init__.py +++ b/telethon/_tl/patched/__init__.py @@ -1,4 +1,4 @@ -from .. import types, alltlobjects +from .. import _tl from ..custom.message import Message as _Message class MessageEmpty(_Message, types.MessageEmpty): diff --git a/telethon/events/album.py b/telethon/events/album.py index e06be9ee..0317db61 100644 --- a/telethon/events/album.py +++ b/telethon/events/album.py @@ -3,7 +3,9 @@ import time import weakref from .common import EventBuilder, EventCommon, name_inner_event -from .. import utils, _tl +from .._misc import utils +from .. import _tl +from .._tl import custom _IGNORE_MAX_SIZE = 100 # len() _IGNORE_MAX_AGE = 5 # seconds @@ -138,7 +140,7 @@ class Album(EventBuilder): if len(event.messages) > 1: return super().filter(event) - class Event(EventCommon, _tl.custom.sendergetter.SenderGetter): + class Event(EventCommon, custom.sendergetter.SenderGetter): """ Represents the event of a new album. @@ -158,7 +160,7 @@ class Album(EventBuilder): super().__init__(chat_peer=chat_peer, msg_id=message.id, broadcast=bool(message.post)) - _tl.custom.sendergetter.SenderGetter.__init__(self, message.sender_id) + 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 954ccf2d..f850ecd5 100644 --- a/telethon/events/callbackquery.py +++ b/telethon/events/callbackquery.py @@ -2,7 +2,9 @@ import re import struct from .common import EventBuilder, EventCommon, name_inner_event -from .. import utils, _tl +from .._misc import utils +from .. import _tl +from .._tl import custom @name_inner_event @@ -121,7 +123,7 @@ class CallbackQuery(EventBuilder): return self.func(event) return True - class Event(EventCommon, _tl.custom.sendergetter.SenderGetter): + class Event(EventCommon, custom.sendergetter.SenderGetter): """ Represents the event of a new callback query. @@ -139,7 +141,7 @@ class CallbackQuery(EventBuilder): """ def __init__(self, query, peer, msg_id): super().__init__(peer, msg_id=msg_id) - _tl.custom.sendergetter.SenderGetter.__init__(self, query.user_id) + custom.sendergetter.SenderGetter.__init__(self, query.user_id) self.query = query self.data_match = None self.pattern_match = None diff --git a/telethon/events/chataction.py b/telethon/events/chataction.py index 09e1de70..d330656a 100644 --- a/telethon/events/chataction.py +++ b/telethon/events/chataction.py @@ -1,5 +1,6 @@ from .common import EventBuilder, EventCommon, name_inner_event -from .. import utils, _tl +from .._misc import utils +from .. import _tl @name_inner_event diff --git a/telethon/events/common.py b/telethon/events/common.py index 8367ce94..cce243e6 100644 --- a/telethon/events/common.py +++ b/telethon/events/common.py @@ -2,7 +2,8 @@ import abc import asyncio import warnings -from .. import utils, _tl +from .._misc import utils +from .._tl.custom.chatgetter import ChatGetter async def _into_id_set(client, chats): diff --git a/telethon/events/inlinequery.py b/telethon/events/inlinequery.py index dc602af9..f2c13d3d 100644 --- a/telethon/events/inlinequery.py +++ b/telethon/events/inlinequery.py @@ -4,7 +4,9 @@ import re import asyncio from .common import EventBuilder, EventCommon, name_inner_event -from .. import utils, _tl +from .._misc import utils +from .. import _tl +from .._tl import custom @name_inner_event @@ -72,7 +74,7 @@ class InlineQuery(EventBuilder): return super().filter(event) - class Event(EventCommon, _tl.custom.sendergetter.SenderGetter): + class Event(EventCommon, custom.sendergetter.SenderGetter): """ Represents the event of a new callback query. @@ -89,7 +91,7 @@ class InlineQuery(EventBuilder): """ def __init__(self, query): super().__init__(chat_peer=_tl.PeerUser(query.user_id)) - _tl.custom.sendergetter.SenderGetter.__init__(self, query.user_id) + custom.sendergetter.SenderGetter.__init__(self, query.user_id) self.query = query self.pattern_match = None self._answered = False diff --git a/telethon/events/messageread.py b/telethon/events/messageread.py index a25e5f66..5c37eb2c 100644 --- a/telethon/events/messageread.py +++ b/telethon/events/messageread.py @@ -1,5 +1,6 @@ from .common import EventBuilder, EventCommon, name_inner_event -from .. import utils, _tl +from .._misc import utils +from .. import _tl @name_inner_event diff --git a/telethon/events/newmessage.py b/telethon/events/newmessage.py index 192f9937..cfe7b88a 100644 --- a/telethon/events/newmessage.py +++ b/telethon/events/newmessage.py @@ -1,7 +1,8 @@ import re from .common import EventBuilder, EventCommon, name_inner_event, _into_id_set -from .. import utils, _tl +from .._misc import utils +from .. import _tl @name_inner_event diff --git a/telethon/events/raw.py b/telethon/events/raw.py index 84910778..68fdfc0c 100644 --- a/telethon/events/raw.py +++ b/telethon/events/raw.py @@ -1,5 +1,5 @@ from .common import EventBuilder -from .. import utils +from .._misc import utils class Raw(EventBuilder): diff --git a/telethon/events/userupdate.py b/telethon/events/userupdate.py index 8b6642a8..8144cadb 100644 --- a/telethon/events/userupdate.py +++ b/telethon/events/userupdate.py @@ -2,7 +2,9 @@ import datetime import functools from .common import EventBuilder, EventCommon, name_inner_event -from .. import utils, _tl +from .._misc import utils +from .. import _tl +from .._tl import custom # TODO Either the properties are poorly named or they should be @@ -63,7 +65,7 @@ class UserUpdate(EventBuilder): return cls.Event(update.user_id, typing=update.action) - class Event(EventCommon, _tl.custom.sendergetter.SenderGetter): + class Event(EventCommon, custom.sendergetter.SenderGetter): """ Represents the event of a user update such as gone online, started typing, etc. @@ -85,7 +87,7 @@ class UserUpdate(EventBuilder): """ def __init__(self, peer, *, status=None, chat_peer=None, typing=None): super().__init__(chat_peer or peer) - _tl.custom.sendergetter.SenderGetter.__init__(self, utils.get_peer_id(peer)) + custom.sendergetter.SenderGetter.__init__(self, utils.get_peer_id(peer)) self.status = status self.action = typing diff --git a/telethon/sessions/memory.py b/telethon/sessions/memory.py index 0fda05ba..9f5314a3 100644 --- a/telethon/sessions/memory.py +++ b/telethon/sessions/memory.py @@ -1,7 +1,8 @@ from enum import Enum from .abstract import Session -from .. import utils, _tl +from .._misc import utils +from .. import _tl class _SentFileType(Enum): diff --git a/telethon/sessions/sqlite.py b/telethon/sessions/sqlite.py index 32336000..5b4505c8 100644 --- a/telethon/sessions/sqlite.py +++ b/telethon/sessions/sqlite.py @@ -3,8 +3,9 @@ import os import time from .memory import MemorySession, _SentFileType -from .. import utils, _tl -from ..crypto import AuthKey +from .._misc import utils +from .. import _tl +from .._crypto import AuthKey try: import sqlite3 diff --git a/telethon/sessions/string.py b/telethon/sessions/string.py index fb971d82..72617f24 100644 --- a/telethon/sessions/string.py +++ b/telethon/sessions/string.py @@ -4,7 +4,7 @@ import struct from .abstract import Session from .memory import MemorySession -from ..crypto import AuthKey +from .._crypto import AuthKey _STRUCT_PREFORMAT = '>B{}sH256s' diff --git a/telethon_generator/generators/docs.py b/telethon_generator/generators/docs.py index 8b46e4d1..d2da55d1 100755 --- a/telethon_generator/generators/docs.py +++ b/telethon_generator/generators/docs.py @@ -9,7 +9,7 @@ from pathlib import Path from ..docswriter import DocsWriter from ..parsers import TLObject, Usability -from ..utils import snake_to_camel_case +from .._misc.utils import snake_to_camel_case CORE_TYPES = { 'int', 'long', 'int128', 'int256', 'double', diff --git a/telethon_generator/generators/tlobject.py b/telethon_generator/generators/tlobject.py index cc37cb92..04003a6b 100644 --- a/telethon_generator/generators/tlobject.py +++ b/telethon_generator/generators/tlobject.py @@ -7,7 +7,7 @@ from collections import defaultdict from zlib import crc32 from ..sourcebuilder import SourceBuilder -from ..utils import snake_to_camel_case +from .._misc.utils import snake_to_camel_case AUTO_GEN_NOTICE = \ '"""File generated by TLObjects\' generator. All changes will be ERASED"""' diff --git a/telethon_generator/parsers/errors.py b/telethon_generator/parsers/errors.py index 04cd3412..9bac2142 100644 --- a/telethon_generator/parsers/errors.py +++ b/telethon_generator/parsers/errors.py @@ -1,7 +1,7 @@ import csv import re -from ..utils import snake_to_camel_case +from .._misc.utils import snake_to_camel_case # Core base classes depending on the integer error code KNOWN_BASE_CLASSES = { diff --git a/telethon_generator/parsers/tlobject/tlobject.py b/telethon_generator/parsers/tlobject/tlobject.py index 60b9e996..0f753fa2 100644 --- a/telethon_generator/parsers/tlobject/tlobject.py +++ b/telethon_generator/parsers/tlobject/tlobject.py @@ -2,7 +2,7 @@ import re import struct import zlib -from ...utils import snake_to_camel_case +from ..._misc.utils import snake_to_camel_case # https://github.com/telegramdesktop/tdesktop/blob/4bf66cb6e93f3965b40084771b595e93d0b11bcd/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py#L57-L62 WHITELISTED_MISMATCHING_IDS = { diff --git a/tests/telethon/tl/test_serialization.py b/tests/telethon/tl/test_serialization.py index 4b455784..7bcdf25b 100644 --- a/tests/telethon/tl/test_serialization.py +++ b/tests/telethon/tl/test_serialization.py @@ -1,13 +1,13 @@ import pytest -from telethon.tl import types, functions +from telethon import _tl def test_nested_invalid_serialization(): large_long = 2**62 request = _tl.fn.account.SetPrivacy( - key=types.InputPrivacyKeyChatInvite(), - rules=[types.InputPrivacyValueDisallowUsers(users=[large_long])] + key=_tl.InputPrivacyKeyChatInvite(), + rules=[_tl.InputPrivacyValueDisallowUsers(users=[large_long])] ) with pytest.raises(TypeError): bytes(request)