From 387a255221b71b07eebf70943b055e2bdff12dfe Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 25 Apr 2018 09:51:50 +0200 Subject: [PATCH 01/14] Faster iter_messages by sleeping only as much as needed --- telethon/telegram_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index c6c574c0..860d394f 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -1111,6 +1111,7 @@ class TelegramClient(TelegramBareClient): have = 0 batch_size = min(max(batch_size, 1), 100) while have < limit: + start = time.time() # Telegram has a hard limit of 100 request.limit = min(limit - have, batch_size) r = self(request) @@ -1157,7 +1158,7 @@ class TelegramClient(TelegramBareClient): else: request.max_date = r.messages[-1].date - time.sleep(wait_time) + time.sleep(max(wait_time - (time.time() - start), 0)) def get_messages(self, *args, **kwargs): """ From 5d9cf513bded5b98126fe2b4df2098c06519699a Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 25 Apr 2018 09:55:34 +0200 Subject: [PATCH 02/14] Update usage of deprecated methods in the docs --- readthedocs/extra/basic/getting-started.rst | 4 ++-- readthedocs/extra/basic/telegram-client.rst | 2 +- readthedocs/extra/examples/chats-and-channels.rst | 2 +- telethon_examples/interactive_telegram_client.py | 4 ++-- telethon_tests/test_higher_level.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/readthedocs/extra/basic/getting-started.rst b/readthedocs/extra/basic/getting-started.rst index e40bae44..12d8babe 100644 --- a/readthedocs/extra/basic/getting-started.rst +++ b/readthedocs/extra/basic/getting-started.rst @@ -48,7 +48,7 @@ Basic Usage # Retrieving messages from a chat from telethon import utils - for message in client.get_message_history('username', limit=10): + for message in client.iter_messages('username', limit=10): print(utils.get_display_name(message.sender), message.message) # Listing all the dialogs (conversations you have open) @@ -60,7 +60,7 @@ Basic Usage # Once you have a message with .media (if message.media) # you can download it using client.download_media(): - messages = client.get_message_history('username') + messages = client.get_messages('username') client.download_media(messages[0]) **More details**: :ref:`telegram-client` diff --git a/readthedocs/extra/basic/telegram-client.rst b/readthedocs/extra/basic/telegram-client.rst index 81edf83a..d466646a 100644 --- a/readthedocs/extra/basic/telegram-client.rst +++ b/readthedocs/extra/basic/telegram-client.rst @@ -67,7 +67,7 @@ Many other common methods for quick scripts are also available: # The utils package has some goodies, like .get_display_name() from telethon import utils - for message in client.get_message_history('username', limit=10): + for message in client.iter_messages('username', limit=10): print(utils.get_display_name(message.sender), message.message) # Dialogs are the conversations you have open diff --git a/readthedocs/extra/examples/chats-and-channels.rst b/readthedocs/extra/examples/chats-and-channels.rst index f59277a7..04d52187 100644 --- a/readthedocs/extra/examples/chats-and-channels.rst +++ b/readthedocs/extra/examples/chats-and-channels.rst @@ -288,7 +288,7 @@ use :tl:`GetMessagesViewsRequest`, setting ``increment=True``: # Obtain `channel' through dialogs or through client.get_entity() or anyhow. - # Obtain `msg_ids' through `.get_message_history()` or anyhow. Must be a list. + # Obtain `msg_ids' through `.get_messages()` or anyhow. Must be a list. client(GetMessagesViewsRequest( peer=channel, diff --git a/telethon_examples/interactive_telegram_client.py b/telethon_examples/interactive_telegram_client.py index 44185995..1e4cf128 100644 --- a/telethon_examples/interactive_telegram_client.py +++ b/telethon_examples/interactive_telegram_client.py @@ -207,7 +207,7 @@ class InteractiveTelegramClient(TelegramClient): # History elif msg == '!h': # First retrieve the messages and some information - messages = self.get_message_history(entity, limit=10) + messages = self.get_messages(entity, limit=10) # Iterate over all (in reverse order so the latest appear # the last in the console) and print them with format: @@ -216,7 +216,7 @@ class InteractiveTelegramClient(TelegramClient): # Note that the .sender attribute is only there for # convenience, the API returns it differently. But # this shouldn't concern us. See the documentation - # for .get_message_history() for more information. + # for .iter_messages() for more information. name = get_display_name(msg.sender) # Format the message content diff --git a/telethon_tests/test_higher_level.py b/telethon_tests/test_higher_level.py index 8e933056..67fac515 100644 --- a/telethon_tests/test_higher_level.py +++ b/telethon_tests/test_higher_level.py @@ -34,7 +34,7 @@ class HigherLevelTests(unittest.TestCase): progress_callback=lambda c, t: print('test_cdn_download:uploading {:.2%}...'.format(c/t)) ) - msg = client.get_message_history(me)[1][0] + msg = client.get_messages(me)[1][0] out = BytesIO() client.download_media(msg, out) From e2a0de191351002e8b73adabcf54575ea217ce62 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 25 Apr 2018 10:06:11 +0200 Subject: [PATCH 03/14] Don't retry forever on TcpClient.connect() --- telethon/extensions/tcp_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/telethon/extensions/tcp_client.py b/telethon/extensions/tcp_client.py index 1b7d0113..8a5800fb 100644 --- a/telethon/extensions/tcp_client.py +++ b/telethon/extensions/tcp_client.py @@ -91,7 +91,9 @@ class TcpClient: # to none to recreate it on the next iteration self._socket = None time.sleep(timeout) - timeout = min(timeout * 2, MAX_TIMEOUT) + timeout *= 2 + if timeout > MAX_TIMEOUT: + raise else: raise From 2a00bcaa1222fe746b4a983388a711269aa38ba9 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Wed, 25 Apr 2018 13:37:29 +0200 Subject: [PATCH 04/14] Persist updates.State upon disconnection --- telethon/sessions/abstract.py | 19 +++++++++++++++++++ telethon/sessions/memory.py | 7 +++++++ telethon/sessions/sqlite.py | 18 ++++++++++++++++++ telethon/telegram_bare_client.py | 1 + telethon/update_state.py | 4 ++++ 5 files changed, 49 insertions(+) diff --git a/telethon/sessions/abstract.py b/telethon/sessions/abstract.py index 75324077..fb0778bd 100644 --- a/telethon/sessions/abstract.py +++ b/telethon/sessions/abstract.py @@ -67,6 +67,25 @@ class Session(ABC): """ raise NotImplementedError + @abstractmethod + def get_update_state(self, entity_id): + """ + Returns the ``UpdateState`` associated with the given `entity_id`. + If the `entity_id` is 0, it should return the ``UpdateState`` for + no specific channel (the "general" state). If no state is known + it should ``return None``. + """ + raise NotImplementedError + + @abstractmethod + def set_update_state(self, entity_id, state): + """ + Sets the given ``UpdateState`` for the specified `entity_id`, which + should be 0 if the ``UpdateState`` is the "general" state (and not + for any specific channel). + """ + raise NotImplementedError + @abstractmethod def close(self): """ diff --git a/telethon/sessions/memory.py b/telethon/sessions/memory.py index 330a82c1..acd09a77 100644 --- a/telethon/sessions/memory.py +++ b/telethon/sessions/memory.py @@ -35,6 +35,7 @@ class MemorySession(Session): self._files = {} self._entities = set() + self._update_states = {} def set_dc(self, dc_id, server_address, port): self._dc_id = dc_id or 0 @@ -57,6 +58,12 @@ class MemorySession(Session): def auth_key(self, value): self._auth_key = value + def get_update_state(self, entity_id): + return self._update_states.get(entity_id, None) + + def set_update_state(self, entity_id, state): + self._update_states[entity_id] = state + def close(self): pass diff --git a/telethon/sessions/sqlite.py b/telethon/sessions/sqlite.py index 1b7d3c31..7e7380af 100644 --- a/telethon/sessions/sqlite.py +++ b/telethon/sessions/sqlite.py @@ -5,6 +5,8 @@ from base64 import b64decode from os.path import isfile as file_exists from threading import Lock, RLock +from telethon.tl import types + from .memory import MemorySession, _SentFileType from .. import utils from ..crypto import AuthKey @@ -226,6 +228,22 @@ class SQLiteSession(MemorySession): )) c.close() + def get_update_state(self, entity_id): + c = self._cursor() + row = c.execute('select pts, qts, date, seq from update_state ' + 'where id = ?', (entity_id,)).fetchone() + c.close() + if row: + return types.updates.State(*row) + + def set_update_state(self, entity_id, state): + with self._db_lock: + c = self._cursor() + c.execute('insert or replace into update_state values (?,?,?,?,?)', + (entity_id, state.pts, state.qts, state.date, state.seq)) + c.close() + self.save() + def save(self): """Saves the current session object as session_user_id.session""" with self._db_lock: diff --git a/telethon/telegram_bare_client.py b/telethon/telegram_bare_client.py index d1f4333f..826cc155 100644 --- a/telethon/telegram_bare_client.py +++ b/telethon/telegram_bare_client.py @@ -275,6 +275,7 @@ class TelegramBareClient: # TODO Shall we clear the _exported_sessions, or may be reused? self._first_request = True # On reconnect it will be first again + self.session.set_update_state(0, self.updates.get_update_state(0)) self.session.close() def _reconnect(self, new_dc=None): diff --git a/telethon/update_state.py b/telethon/update_state.py index e679e99e..eaedcfc3 100644 --- a/telethon/update_state.py +++ b/telethon/update_state.py @@ -110,6 +110,10 @@ class UpdateState: # We don't want to crash a worker thread due to any reason __log__.exception('Unhandled exception on worker %d', wid) + def get_update_state(self, entity_id): + """Gets the updates.State corresponding to the given entity or 0.""" + return self._state + def process(self, update): """Processes an update object. This method is normally called by the library itself. From 9ef40102eb29e380aed4527e58e3583038613c83 Mon Sep 17 00:00:00 2001 From: Lonami Date: Thu, 26 Apr 2018 19:00:45 +0200 Subject: [PATCH 05/14] Be more strict in the ISSUE_TEMPLATE --- .github/ISSUE_TEMPLATE.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index eb08ea77..3c2b1320 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,9 +1,10 @@ From b71511cd63d914aa716f4f151e6861371d86a958 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Fri, 27 Apr 2018 20:51:23 +0200 Subject: [PATCH 06/14] Fix saving update state in the SqliteSession --- telethon/sessions/sqlite.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/telethon/sessions/sqlite.py b/telethon/sessions/sqlite.py index 7e7380af..c18b5cd3 100644 --- a/telethon/sessions/sqlite.py +++ b/telethon/sessions/sqlite.py @@ -1,3 +1,4 @@ +import datetime import json import os import sqlite3 @@ -6,7 +7,6 @@ from os.path import isfile as file_exists from threading import Lock, RLock from telethon.tl import types - from .memory import MemorySession, _SentFileType from .. import utils from ..crypto import AuthKey @@ -234,13 +234,16 @@ class SQLiteSession(MemorySession): 'where id = ?', (entity_id,)).fetchone() c.close() if row: - return types.updates.State(*row) + pts, qts, date, seq = row + date = datetime.datetime.utcfromtimestamp(date) + return types.updates.State(pts, qts, date, seq, unread_count=0) def set_update_state(self, entity_id, state): with self._db_lock: c = self._cursor() c.execute('insert or replace into update_state values (?,?,?,?,?)', - (entity_id, state.pts, state.qts, state.date, state.seq)) + (entity_id, state.pts, state.qts, + state.date.timestamp(), state.seq)) c.close() self.save() From f16289cf93e1b01d1a4dc5a87d7a4e5610cc60dd Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Fri, 27 Apr 2018 20:58:08 +0200 Subject: [PATCH 07/14] Support download_file with None path to return bytes --- telethon/telegram_client.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 860d394f..0068b5b6 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -2174,7 +2174,7 @@ class TelegramClient(TelegramBareClient): def download_file(self, input_location, - file, + file=None, part_size_kb=None, file_size=None, progress_callback=None): @@ -2185,10 +2185,13 @@ class TelegramClient(TelegramBareClient): input_location (:tl:`InputFileLocation`): The file location from which the file will be downloaded. - file (`str` | `file`): + file (`str` | `file`, optional): The output file path, directory, or stream-like object. If the path exists and is a file, it will be overwritten. + If the file path is ``None``, then the result will be + saved in memory and returned as `bytes`. + part_size_kb (`int`, optional): Chunk size when downloading files. The larger, the less requests will be made (up to 512KB maximum). @@ -2219,7 +2222,10 @@ class TelegramClient(TelegramBareClient): raise ValueError( 'The part size must be evenly divisible by 4096.') - if isinstance(file, str): + in_memory = file is None + if in_memory: + f = io.BytesIO() + elif isinstance(file, str): # Ensure that we'll be able to download the media helpers.ensure_parent_dir_exists(file) f = open(file, 'wb') @@ -2261,7 +2267,11 @@ class TelegramClient(TelegramBareClient): # So there is nothing left to download and write if not result.bytes: # Return some extra information, unless it's a CDN file - return getattr(result, 'type', '') + if in_memory: + f.flush() + return f.getvalue() + else: + return getattr(result, 'type', '') f.write(result.bytes) __log__.debug('Saved %d more bytes', len(result.bytes)) @@ -2276,7 +2286,7 @@ class TelegramClient(TelegramBareClient): cdn_decrypter.client.disconnect() except: pass - if isinstance(file, str): + if isinstance(file, str) or in_memory: f.close() # endregion From dc273ab6bcb7689eae45229803cfa4f2f973f50c Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Fri, 27 Apr 2018 21:10:41 +0200 Subject: [PATCH 08/14] Add utils.get_input_location --- telethon/telegram_client.py | 1 + telethon/utils.py | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 0068b5b6..191ce71c 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -2235,6 +2235,7 @@ class TelegramClient(TelegramBareClient): # The used client will change if FileMigrateError occurs client = self cdn_decrypter = None + input_location = utils.get_input_location(input_location) __log__.info('Downloading file in chunks of %d bytes', part_size) try: diff --git a/telethon/utils.py b/telethon/utils.py index 0c6693b1..b0d2693d 100644 --- a/telethon/utils.py +++ b/telethon/utils.py @@ -25,7 +25,8 @@ from .tl.types import ( InputPhotoEmpty, FileLocation, ChatPhotoEmpty, UserProfilePhotoEmpty, FileLocationUnavailable, InputMediaUploadedDocument, ChannelFull, InputMediaUploadedPhoto, DocumentAttributeFilename, photos, - TopPeer, InputNotifyPeer, InputMessageID + TopPeer, InputNotifyPeer, InputMessageID, InputFileLocation, + InputDocumentFileLocation, PhotoSizeEmpty ) from .tl.types.contacts import ResolvedPeer @@ -352,6 +353,39 @@ def get_input_message(message): _raise_cast_fail(message, 'InputMedia') +def get_input_location(location): + """Similar to :meth:`get_input_peer`, but for input messages.""" + try: + if location.SUBCLASS_OF_ID == 0x1523d462: + return location # crc32(b'InputFileLocation'): + except AttributeError: + _raise_cast_fail(location, 'InputFileLocation') + + if isinstance(location, Message): + location = location.media + + if isinstance(location, MessageMediaDocument): + location = location.document + elif isinstance(location, MessageMediaPhoto): + location = location.photo + + if isinstance(location, Document): + return InputDocumentFileLocation( + location.id, location.access_hash, location.version) + elif isinstance(location, Photo): + try: + location = next(x for x in reversed(location.sizes) + if not isinstance(x, PhotoSizeEmpty)).location + except StopIteration: + pass + + if isinstance(location, (FileLocation, FileLocationUnavailable)): + return InputFileLocation( + location.volume_id, location.local_id, location.secret) + + _raise_cast_fail(location, 'InputFileLocation') + + def is_image(file): """ Returns ``True`` if the file extension looks like an image file to Telegram. From 3008ada98dd5ecf34a3a20a5e9c3b3a4cc6aec88 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sat, 28 Apr 2018 10:19:55 +0200 Subject: [PATCH 09/14] Distinguish between mtproto/telegram TL like tdlib does --- setup.py | 10 +- telethon_generator/data/mtproto_api.tl | 90 +++++++++++ .../data/{scheme.tl => telegram_api.tl} | 146 ++---------------- telethon_generator/generator.py | 8 +- 4 files changed, 116 insertions(+), 138 deletions(-) create mode 100644 telethon_generator/data/mtproto_api.tl rename telethon_generator/data/{scheme.tl => telegram_api.tl} (93%) diff --git a/setup.py b/setup.py index 29507397..f0b958c2 100755 --- a/setup.py +++ b/setup.py @@ -10,14 +10,13 @@ Extra supported commands are: * pypi, to generate sdist, bdist_wheel, and push to PyPi """ +import itertools import os import re -# To use a consistent encoding import shutil from codecs import open from sys import argv, version_info -# Always prefer setuptools over distutils from setuptools import find_packages, setup @@ -44,7 +43,8 @@ ERRORS_IN_JSON = os.path.join(GENERATOR_DIR, 'data', 'errors.json') ERRORS_IN_DESC = os.path.join(GENERATOR_DIR, 'data', 'error_descriptions') ERRORS_OUT = os.path.join(LIBRARY_DIR, 'errors', 'rpc_error_list.py') -TLOBJECT_IN_TL = os.path.join(GENERATOR_DIR, 'data', 'scheme.tl') +TLOBJECT_IN_CORE_TL = os.path.join(GENERATOR_DIR, 'data', 'mtproto_api.tl') +TLOBJECT_IN_TL = os.path.join(GENERATOR_DIR, 'data', 'telegram_api.tl') TLOBJECT_OUT = os.path.join(LIBRARY_DIR, 'tl') IMPORT_DEPTH = 2 @@ -57,7 +57,9 @@ def generate(which): from telethon_generator.generators import\ generate_errors, generate_tlobjects, generate_docs, clean_tlobjects - tlobjects = list(parse_tl(TLOBJECT_IN_TL, ignore_core=True)) + tlobjects = list(itertools.chain( + parse_tl(TLOBJECT_IN_CORE_TL), parse_tl(TLOBJECT_IN_TL))) + errors = list(parse_errors(ERRORS_IN_JSON, ERRORS_IN_DESC)) layer = find_layer(TLOBJECT_IN_TL) diff --git a/telethon_generator/data/mtproto_api.tl b/telethon_generator/data/mtproto_api.tl new file mode 100644 index 00000000..b3aa44e1 --- /dev/null +++ b/telethon_generator/data/mtproto_api.tl @@ -0,0 +1,90 @@ +//int ? = Int; +//long ? = Long; +//double ? = Double; +//string ? = String; + +dummyHttpWait = HttpWait; + +//vector {t:Type} # [ t ] = Vector t; + +//int128 4*[ int ] = Int128; +//int256 8*[ int ] = Int256; + +resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector = ResPQ; + +p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data; +p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data; + +server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params; +server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params; + +server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data; + +client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data; + +dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer; +dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer; +dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer; + +bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner; + +//rpc_result#f35c6d01 req_msg_id:long result:string = RpcResult; +rpc_error#2144ca19 error_code:int error_message:string = RpcError; + +rpc_answer_unknown#5e2ad36e = RpcDropAnswer; +rpc_answer_dropped_running#cd78e586 = RpcDropAnswer; +rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer; + +future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt; +future_salts#ae500895 req_msg_id:long now:int salts:vector = FutureSalts; + +pong#347773c5 msg_id:long ping_id:long = Pong; + +destroy_session_ok#e22045fc session_id:long = DestroySessionRes; +destroy_session_none#62d350c9 session_id:long = DestroySessionRes; + +new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession; + +//msg_container#73f1f8dc messages:vector<%Message> = MessageContainer; +//message msg_id:long seqno:int bytes:int body:string = Message; +//msg_copy#e06046b2 orig_message:Message = MessageCopy; + +gzip_packed#3072cfa1 packed_data:string = Object; + +msgs_ack#62d6b459 msg_ids:Vector = MsgsAck; + +bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification; +bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification; + +msg_resend_req#7d861a08 msg_ids:Vector = MsgResendReq; +msgs_state_req#da69fb52 msg_ids:Vector = MsgsStateReq; +msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo; +msgs_all_info#8cc0d131 msg_ids:Vector info:string = MsgsAllInfo; +msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo; +msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo; + +rsa_public_key n:string e:string = RSAPublicKey; + +---functions--- + +req_pq_multi#be7e8ef1 nonce:int128 = ResPQ; + +req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params; + +set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer; + +rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer; +get_future_salts#b921bd04 num:int = FutureSalts; +ping#7abe77ec ping_id:long = Pong; +ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong; +destroy_session#e7512126 session_id:long = DestroySessionRes; + +http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait; + +//test.useGzipPacked = GzipPacked; +//test.useServerDhInnerData = Server_DH_inner_data; +//test.useNewSessionCreated = NewSession; +//test.useMsgsAck = MsgsAck; +//test.useBadMsgNotification = BadMsgNotification; + +//test.useOther key:rsa_public_key p_q_data:P_Q_inner_data dh_data:client_DH_inner_data = RpcError; diff --git a/telethon_generator/data/scheme.tl b/telethon_generator/data/telegram_api.tl similarity index 93% rename from telethon_generator/data/scheme.tl rename to telethon_generator/data/telegram_api.tl index 2ed348da..9122c32d 100644 --- a/telethon_generator/data/scheme.tl +++ b/telethon_generator/data/telegram_api.tl @@ -1,147 +1,29 @@ -// Core types (no need to gen) +//int ? = Int; +//long ? = Long; +//double ? = Double; +//string ? = String; + +//bytes = Bytes; + +//true#3fedd339 = True; + +//boolFalse#bc799737 = Bool; +//boolTrue#997275b5 = Bool; //vector#1cb5c415 {t:Type} # [ t ] = Vector t; -/////////////////////////////// -/////////////////// Layer cons -/////////////////////////////// - -//invokeAfterMsg#cb9f372d msg_id:long query:!X = X; -//invokeAfterMsgs#3dc4b4f0 msg_ids:Vector query:!X = X; -//invokeWithLayer1#53835315 query:!X = X; -//invokeWithLayer2#289dd1f6 query:!X = X; -//invokeWithLayer3#b7475268 query:!X = X; -//invokeWithLayer4#dea0d430 query:!X = X; -//invokeWithLayer5#417a57ae query:!X = X; -//invokeWithLayer6#3a64d54d query:!X = X; -//invokeWithLayer7#a5be56d3 query:!X = X; -//invokeWithLayer8#e9abd9fd query:!X = X; -//invokeWithLayer9#76715a63 query:!X = X; -//invokeWithLayer10#39620c41 query:!X = X; -//invokeWithLayer11#a6b88fdf query:!X = X; -//invokeWithLayer12#dda60d3c query:!X = X; -//invokeWithLayer13#427c8ea2 query:!X = X; -//invokeWithLayer14#2b9b08fa query:!X = X; -//invokeWithLayer15#b4418b64 query:!X = X; -//invokeWithLayer16#cf5f0987 query:!X = X; -//invokeWithLayer17#50858a19 query:!X = X; -//invokeWithLayer18#1c900537 query:!X = X; -//invokeWithLayer#da9b0d0d layer:int query:!X = X; // after 18 layer - -/////////////////////////////// -/// Authorization key creation -/////////////////////////////// - -resPQ#05162463 nonce:int128 server_nonce:int128 pq:bytes server_public_key_fingerprints:Vector = ResPQ; - -p_q_inner_data#83c95aec pq:bytes p:bytes q:bytes nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data; - -server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params; -server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:bytes = Server_DH_Params; - -server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:bytes g_a:bytes server_time:int = Server_DH_inner_data; - -client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:bytes = Client_DH_Inner_Data; - -dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer; -dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer; -dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer; - -destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes; -destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes; -destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes; - ----functions--- - -// Deprecated since somewhere around February of 2018 -// See https://core.telegram.org/mtproto/auth_key -req_pq#60469778 nonce:int128 = ResPQ; -req_pq_multi#be7e8ef1 nonce:int128 = ResPQ; - -req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params; - -set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer; - -destroy_auth_key#d1435160 = DestroyAuthKeyRes; - -/////////////////////////////// -////////////// System messages -/////////////////////////////// - ----types--- - -msgs_ack#62d6b459 msg_ids:Vector = MsgsAck; - -bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification; -bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification; - -msgs_state_req#da69fb52 msg_ids:Vector = MsgsStateReq; -msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo; -msgs_all_info#8cc0d131 msg_ids:Vector info:string = MsgsAllInfo; - -msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo; -msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo; - -msg_resend_req#7d861a08 msg_ids:Vector = MsgResendReq; - -//rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult; // parsed manually - -rpc_error#2144ca19 error_code:int error_message:string = RpcError; - -rpc_answer_unknown#5e2ad36e = RpcDropAnswer; -rpc_answer_dropped_running#cd78e586 = RpcDropAnswer; -rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer; - -future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt; -future_salts#ae500895 req_msg_id:long now:int salts:vector = FutureSalts; - -pong#347773c5 msg_id:long ping_id:long = Pong; - -destroy_session_ok#e22045fc session_id:long = DestroySessionRes; -destroy_session_none#62d350c9 session_id:long = DestroySessionRes; - -new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession; - -//message msg_id:long seqno:int bytes:int body:Object = Message; // parsed manually -//msg_container#73f1f8dc messages:vector = MessageContainer; // parsed manually -//msg_copy#e06046b2 orig_message:Message = MessageCopy; // parsed manually, not used - use msg_container -//gzip_packed#3072cfa1 packed_data:string = Object; // parsed manually - -http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait; +error#c4b9f9bb code:int text:string = Error; ipPort ipv4:int port:int = IpPort; help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector = help.ConfigSimple; ---functions--- -rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer; - -get_future_salts#b921bd04 num:int = FutureSalts; - -ping#7abe77ec ping_id:long = Pong; -ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong; - -destroy_session#e7512126 session_id:long = DestroySessionRes; - -contest.saveDeveloperInfo#9a5f6e95 vk_id:int name:string phone_number:string age:int city:string = Bool; - -/////////////////////////////// -///////// Main application API -/////////////////////////////// +test.useError = Error; +test.useConfigSimple = help.ConfigSimple; ---types--- -boolFalse#bc799737 = Bool; -boolTrue#997275b5 = Bool; - -true#3fedd339 = True; - -vector#1cb5c415 {t:Type} # [ t ] = Vector t; - -error#c4b9f9bb code:int text:string = Error; - -null#56730bcc = Null; - inputPeerEmpty#7f3b18ea = InputPeer; inputPeerSelf#7da07ec9 = InputPeer; inputPeerChat#179be863 chat_id:int = InputPeer; diff --git a/telethon_generator/generator.py b/telethon_generator/generator.py index 4314bd26..f1dad375 100644 --- a/telethon_generator/generator.py +++ b/telethon_generator/generator.py @@ -1,13 +1,15 @@ from telethon_generator.parsers import parse_errors, parse_tl, find_layer from telethon_generator.generators import\ generate_errors, generate_tlobjects, generate_docs +import itertools ERRORS_INPUT_JSON = 'data/errors.json' ERRORS_INPUT_DESC = 'data/error_descriptions' ERRORS_OUTPUT = '../telethon/errors/rpc_error_list.py' -TLOBJECT_INPUT_TL = 'data/scheme.tl' +TLOBJECT_INPUT_CORE_TL = 'data/mtproto_api.tl' +TLOBJECT_INPUT_TL = 'data/telegram_api.tl' TLOBJECT_OUTPUT = '../telethon/tl' DOCS_INPUT_RES = 'data/html' @@ -15,7 +17,9 @@ DOCS_OUTPUT = '../docs' if __name__ == '__main__': - tlobjects = list(parse_tl(TLOBJECT_INPUT_TL, ignore_core=True)) + tlobjects = list(itertools.chain( + parse_tl(TLOBJECT_INPUT_CORE_TL), parse_tl(TLOBJECT_INPUT_TL))) + errors = list(parse_errors(ERRORS_INPUT_JSON, ERRORS_INPUT_DESC)) layer = find_layer(TLOBJECT_INPUT_TL) From 08dbc42718215de131b1f01a6996cfb68679fd0e Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sat, 28 Apr 2018 11:49:43 +0200 Subject: [PATCH 10/14] Update to layer 76 --- telethon/telegram_client.py | 1 + telethon/utils.py | 20 +++++++- telethon_generator/data/telegram_api.tl | 56 ++++++++++++++--------- telethon_generator/generators/tlobject.py | 1 + 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 191ce71c..31ce984e 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -1083,6 +1083,7 @@ class TelegramClient(TelegramBareClient): limit=1, max_id=max_id, min_id=min_id, + hash=0, from_id=self.get_input_entity(from_user) if from_user else None ) else: diff --git a/telethon/utils.py b/telethon/utils.py index b0d2693d..009cea15 100644 --- a/telethon/utils.py +++ b/telethon/utils.py @@ -26,7 +26,7 @@ from .tl.types import ( FileLocationUnavailable, InputMediaUploadedDocument, ChannelFull, InputMediaUploadedPhoto, DocumentAttributeFilename, photos, TopPeer, InputNotifyPeer, InputMessageID, InputFileLocation, - InputDocumentFileLocation, PhotoSizeEmpty + InputDocumentFileLocation, PhotoSizeEmpty, InputDialogPeer ) from .tl.types.contacts import ResolvedPeer @@ -182,6 +182,24 @@ def get_input_user(entity): _raise_cast_fail(entity, 'InputUser') +def get_input_dialog(dialog): + """Similar to :meth:`get_input_peer`, but for dialogs""" + try: + if dialog.SUBCLASS_OF_ID == 0xa21c9795: # crc32(b'InputDialogPeer') + return dialog + if dialog.SUBCLASS_OF_ID == 0xc91c90b6: # crc32(b'InputPeer') + return InputDialogPeer(dialog) + except AttributeError: + _raise_cast_fail(dialog, 'InputDialogPeer') + + try: + return InputDialogPeer(get_input_peer(dialog)) + except TypeError: + pass + + _raise_cast_fail(dialog, 'InputDialogPeer') + + def get_input_document(document): """Similar to :meth:`get_input_peer`, but for documents""" try: diff --git a/telethon_generator/data/telegram_api.tl b/telethon_generator/data/telegram_api.tl index 9122c32d..711b2d38 100644 --- a/telethon_generator/data/telegram_api.tl +++ b/telethon_generator/data/telegram_api.tl @@ -155,6 +155,7 @@ messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAct messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction; messageActionScreenshotTaken#4792929b = MessageAction; messageActionCustomAction#fae69f56 message:string = MessageAction; +messageActionBotAllowed#abe9affe domain:string = MessageAction; dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog; @@ -307,8 +308,8 @@ updateRecentStickers#9a422c20 = Update; updateConfig#a229dd06 = Update; updatePtsChanged#3354678f = Update; updateChannelWebPage#40771900 channel_id:int webpage:WebPage pts:int pts_count:int = Update; -updateDialogPinned#d711a2cc flags:# pinned:flags.0?true peer:Peer = Update; -updatePinnedDialogs#d8caf68d flags:# order:flags.0?Vector = Update; +updateDialogPinned#19d27f3c flags:# pinned:flags.0?true peer:DialogPeer = Update; +updatePinnedDialogs#ea4cb65b flags:# order:flags.0?Vector = Update; updateBotWebhookJSON#8317c0c3 data:DataJSON = Update; updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Update; updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update; @@ -342,11 +343,11 @@ photos.photosSlice#15051f54 count:int photos:Vector users:Vector = photos.photo#20212ca8 photo:Photo users:Vector = photos.Photo; upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File; -upload.fileCdnRedirect#ea52fe5a dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes cdn_file_hashes:Vector = upload.File; +upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector = upload.File; dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int = DcOption; -config#9c840964 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int disabled_features:Vector = Config; +config#86b5778e flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config; nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; @@ -451,8 +452,6 @@ stickerPack#12b299d4 emoticon:string documents:Vector = StickerPack; messages.allStickersNotModified#e86602c3 = messages.AllStickers; messages.allStickers#edfd405f hash:int sets:Vector = messages.AllStickers; -disabledFeature#ae636f24 feature:string description:string = DisabledFeature; - messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages; contactLinkUnknown#5f4f9247 = ContactLink; @@ -490,7 +489,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; -stickerSet#cd303b41 flags:# installed:flags.0?true archived:flags.1?true official:flags.2?true masks:flags.3?true id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet; +stickerSet#5585a139 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet; messages.stickerSet#b60a24a6 set:StickerSet packs:Vector documents:Vector = messages.StickerSet; @@ -527,6 +526,8 @@ messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity; messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity; messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity; inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity; +messageEntityPhone#9b69e34b offset:int length:int = MessageEntity; +messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity; inputChannelEmpty#ee8c1e86 = InputChannel; inputChannel#afeb712e channel_id:int access_hash:long = InputChannel; @@ -577,7 +578,7 @@ inputBotInlineMessageMediaVenue#aaafadc8 flags:# geo_point:InputGeoPoint title:s inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; -inputBotInlineResult#2cbbe15a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:InputBotInlineMessage = InputBotInlineResult; +inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult; inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult; inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult; inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult; @@ -588,7 +589,7 @@ botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:f botInlineMessageMediaVenue#4366232e flags:# geo:GeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; -botInlineResult#9bebaeb9 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:BotInlineMessage = BotInlineResult; +botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult; botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult; messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector cache_time:int users:Vector = messages.BotResults; @@ -637,7 +638,7 @@ messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers; messages.featuredStickers#f89d88e5 hash:int sets:Vector unread:Vector = messages.FeaturedStickers; messages.recentStickersNotModified#b17f890 = messages.RecentStickers; -messages.recentStickers#5ce20970 hash:int stickers:Vector = messages.RecentStickers; +messages.recentStickers#22f3afb3 hash:int packs:Vector stickers:Vector dates:Vector = messages.RecentStickers; messages.archivedStickers#4fcba9c8 count:int sets:Vector = messages.ArchivedStickers; @@ -719,6 +720,7 @@ paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string e paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials; webDocument#c61acbd8 url:string access_hash:long size:int mime_type:string attributes:Vector dc_id:int = WebDocument; +webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector = WebDocument; inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector = InputWebDocument; @@ -807,8 +809,6 @@ channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?tru popularContact#5ce14175 client_id:long importers:int = PopularContact; -cdnFileHash#77eec38f offset:int limit:int hash:bytes = CdnFileHash; - messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers; messages.favedStickers#f37f2f16 hash:int packs:Vector stickers:Vector = messages.FavedStickers; @@ -830,6 +830,15 @@ inputMessageID#a676a322 id:int = InputMessage; inputMessageReplyTo#bad88395 id:int = InputMessage; inputMessagePinned#86872538 = InputMessage; +inputDialogPeer#fcaafeb7 peer:InputPeer = InputDialogPeer; + +dialogPeer#e56dbf05 peer:Peer = DialogPeer; + +messages.foundStickerSetsNotModified#d54b65d = messages.FoundStickerSets; +messages.foundStickerSets#5108d648 hash:int sets:Vector = messages.FoundStickerSets; + +fileHash#6242c773 offset:int limit:int hash:bytes = FileHash; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -856,7 +865,7 @@ auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentC auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool; auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector = Bool; -account.registerDevice#1389cc token_type:int token:string app_sandbox:Bool other_uids:Vector = Bool; +account.registerDevice#5cbea590 token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector = Bool; account.unregisterDevice#3076c4bf token_type:int token:string other_uids:Vector = Bool; account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool; account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings; @@ -909,7 +918,7 @@ contacts.resetSaved#879537f1 = Bool; messages.getMessages#63c66506 id:Vector = messages.Messages; messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs; messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; -messages.search#39e9ea0 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; +messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory; messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; @@ -921,6 +930,7 @@ messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6 messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.hideReportSpam#a8f1709b peer:InputPeer = Bool; messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; +messages.report#bd82b658 peer:InputPeer id:Vector reason:ReportReason = Bool; messages.getChats#3c6aa187 id:Vector = messages.Chats; messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; messages.editChatTitle#dc452855 chat_id:int title:string = Updates; @@ -940,7 +950,7 @@ messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long da messages.receivedQueue#55a5bb66 max_qts:int = Vector; messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool; messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages; -messages.getStickers#ae22e045 emoticon:string hash:string = messages.Stickers; +messages.getStickers#85cb5182 flags:# exclude_featured:flags.0?true emoticon:string hash:string = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite; @@ -968,7 +978,7 @@ messages.editMessage#5d1b8dd flags:# no_webpage:flags.1?true stop_geo_live:flags messages.editInlineBotMessage#b0e08243 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector geo_point:flags.13?InputGeoPoint = Bool; messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer; messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool; -messages.getPeerDialogs#2d9776b9 peers:Vector = messages.PeerDialogs; +messages.getPeerDialogs#e470bcfd peers:Vector = messages.PeerDialogs; messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector = Bool; messages.getAllDrafts#6a3f8d65 = Updates; messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers; @@ -986,8 +996,8 @@ messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:Inpu messages.getCommonChats#d0a48c4 user_id:InputUser max_id:int limit:int = messages.Chats; messages.getAllChats#eba80ff0 except_ids:Vector = messages.Chats; messages.getWebPage#32ca8f91 url:string hash:int = WebPage; -messages.toggleDialogPin#3289be6a flags:# pinned:flags.0?true peer:InputPeer = Bool; -messages.reorderPinnedDialogs#959ff644 flags:# force:flags.0?true order:Vector = Bool; +messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; +messages.reorderPinnedDialogs#5b51d63f flags:# force:flags.0?true order:Vector = Bool; messages.getPinnedDialogs#e254d64e = messages.PeerDialogs; messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector = Bool; messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool; @@ -997,9 +1007,10 @@ messages.getFavedStickers#21ce0b0e hash:int = messages.FavedStickers; messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool; messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory; -messages.getRecentLocations#249431e2 peer:InputPeer limit:int = messages.Messages; +messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages; messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector = Updates; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; +messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1015,8 +1026,9 @@ upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile; upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile; -upload.reuploadCdnFile#1af91c09 file_token:bytes request_token:bytes = Vector; -upload.getCdnFileHashes#f715c87b file_token:bytes offset:int = Vector; +upload.reuploadCdnFile#9b2754a8 file_token:bytes request_token:bytes = Vector; +upload.getCdnFileHashes#4da54231 file_token:bytes offset:int = Vector; +upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector; help.getConfig#c4f9186b = Config; help.getNearestDc#1fb33026 = NearestDc; @@ -1092,4 +1104,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector = Vector; -// LAYER 75 +// LAYER 76 diff --git a/telethon_generator/generators/tlobject.py b/telethon_generator/generators/tlobject.py index 74debc10..36d1f013 100644 --- a/telethon_generator/generators/tlobject.py +++ b/telethon_generator/generators/tlobject.py @@ -17,6 +17,7 @@ AUTO_CASTS = { 'InputPeer': 'utils.get_input_peer(client.get_input_entity({}))', 'InputChannel': 'utils.get_input_channel(client.get_input_entity({}))', 'InputUser': 'utils.get_input_user(client.get_input_entity({}))', + 'InputDialogPeer': 'utils.get_input_dialog(client.get_input_entity({}))', 'InputMedia': 'utils.get_input_media({})', 'InputPhoto': 'utils.get_input_photo({})', 'InputMessage': 'utils.get_input_message({})' From 7ba044730d747747ad605b6889c9ec7a981e2dbc Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sat, 28 Apr 2018 12:21:55 +0200 Subject: [PATCH 11/14] Fix "Other X with this type" missing from the docs --- telethon_generator/generators/docs.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/telethon_generator/generators/docs.py b/telethon_generator/generators/docs.py index 7f52d82d..646fdca8 100755 --- a/telethon_generator/generators/docs.py +++ b/telethon_generator/generators/docs.py @@ -439,9 +439,9 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir): # List all the methods which take this type as input docs.write_title('Methods accepting this type as input', level=3) other_methods = sorted( - (t for t in tlobjects - if any(t == a.type for a in t.args) and t.is_function), - key=lambda t: t.name + (u for u in tlobjects + if any(a.type == t for a in u.args) and u.is_function), + key=lambda u: u.name ) if not other_methods: docs.write_text( @@ -464,10 +464,9 @@ def _write_html_pages(tlobjects, errors, layer, input_res, output_dir): # List every other type which has this type as a member docs.write_title('Other types containing this type', level=3) other_types = sorted( - (t for t in tlobjects - if any(t == a.type for a in t.args) - and not t.is_function - ), key=lambda t: t.name + (u for u in tlobjects + if any(a.type == t for a in u.args) and not u.is_function), + key=lambda u: u.name ) if not other_types: From 5c6ac18a5297aa59e71a3da2ac77ebd2fd66fc9c Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sat, 28 Apr 2018 12:58:41 +0200 Subject: [PATCH 12/14] Attach original_update to all events --- telethon/events/common.py | 1 + telethon/telegram_client.py | 1 + 2 files changed, 2 insertions(+) diff --git a/telethon/events/common.py b/telethon/events/common.py index 54289726..3b52380a 100644 --- a/telethon/events/common.py +++ b/telethon/events/common.py @@ -97,6 +97,7 @@ class EventCommon(abc.ABC): self._chat = None self.pattern_match = None + self.original_update = None self.is_private = isinstance(chat_peer, types.PeerUser) self.is_group = ( diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 31ce984e..91627ded 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -2323,6 +2323,7 @@ class TelegramClient(TelegramBareClient): event = builder.build(update) if event: event._client = self + event.original_update = update try: callback(event) except events.StopPropagation: From ce7e5abb589b32c3411b0e2c0f9de60d2dabf3cb Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sat, 28 Apr 2018 13:37:19 +0200 Subject: [PATCH 13/14] Support filtering events.Raw by update type --- telethon/events/__init__.py | 2 +- telethon/events/common.py | 11 ----------- telethon/events/raw.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 telethon/events/raw.py diff --git a/telethon/events/__init__.py b/telethon/events/__init__.py index 4ff0699d..7614dbfe 100644 --- a/telethon/events/__init__.py +++ b/telethon/events/__init__.py @@ -1,4 +1,4 @@ -from .common import Raw +from .raw import Raw from .chataction import ChatAction from .messagedeleted import MessageDeleted from .messageedited import MessageEdited diff --git a/telethon/events/common.py b/telethon/events/common.py index 3b52380a..a5dfc7e7 100644 --- a/telethon/events/common.py +++ b/telethon/events/common.py @@ -209,17 +209,6 @@ class EventCommon(abc.ABC): return d -class Raw(EventBuilder): - """ - Represents a raw event. The event is the update itself. - """ - def resolve(self, client): - pass - - def build(self, update): - return update - - def name_inner_event(cls): """Decorator to rename cls.Event 'Event' as 'cls.Event'""" if hasattr(cls, 'Event'): diff --git a/telethon/events/raw.py b/telethon/events/raw.py new file mode 100644 index 00000000..5972d45c --- /dev/null +++ b/telethon/events/raw.py @@ -0,0 +1,30 @@ +from .common import EventBuilder +from .. import utils + + +class Raw(EventBuilder): + """ + Represents a raw event. The event is the update itself. + + Args: + types (`list` | `tuple` | `type`, optional): + The type or types that the :tl:`Update` instance must be. + Equivalent to ``if not isinstance(update, types): return``. + """ + def __init__(self, types=None): + super().__init__() + if not types: + self.types = None + elif not utils.is_list_like(types): + assert isinstance(types, type) + self.types = types + else: + assert all(isinstance(x, type) for x in types) + self.types = tuple(types) + + def resolve(self, client): + pass + + def build(self, update): + if not self.types or isinstance(update, self.types): + return update From d6935355ae044809d5f916055f4119ceeac61930 Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Sat, 28 Apr 2018 13:42:36 +0200 Subject: [PATCH 14/14] Fix two tiny typos --- telethon/events/chataction.py | 3 +-- telethon/telegram_client.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/telethon/events/chataction.py b/telethon/events/chataction.py index 3ca18f5e..2927c8d0 100644 --- a/telethon/events/chataction.py +++ b/telethon/events/chataction.py @@ -95,7 +95,6 @@ class ChatAction(EventBuilder): photo (:tl:`Photo`, optional): The new photo (or ``None`` if it was removed). - user_added (`bool`): ``True`` if the user was added by some other. @@ -111,7 +110,7 @@ class ChatAction(EventBuilder): created (`bool`, optional): ``True`` if this chat was just created. - new_title (`bool`, optional): + new_title (`str`, optional): The new title string for the chat, if applicable. unpin (`bool`): diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 91627ded..23af0f57 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -2593,7 +2593,7 @@ class TelegramClient(TelegramBareClient): raise ValueError( 'Could not find the input entity for "{}". Please read https://' 'telethon.readthedocs.io/en/latest/extra/basic/entities.html to' - 'find out more details.' + ' find out more details.' .format(peer) )