diff --git a/telethon/extensions/binary_reader.py b/telethon/extensions/binary_reader.py index 1402083f..ecf7dd1b 100644 --- a/telethon/extensions/binary_reader.py +++ b/telethon/extensions/binary_reader.py @@ -133,6 +133,8 @@ class BinaryReader: return True elif value == 0xbc799737: # boolFalse return False + elif value == 0x1cb5c415: # Vector + return [self.tgread_object() for _ in range(self.read_int())] # If there was still no luck, give up self.seek(-4) # Go back diff --git a/telethon/tl/tlobject.py b/telethon/tl/tlobject.py index 7c86a24a..ac0b65f8 100644 --- a/telethon/tl/tlobject.py +++ b/telethon/tl/tlobject.py @@ -7,6 +7,7 @@ class TLObject: def __init__(self): self.confirm_received = Event() self.rpc_error = None + self.result = None # These should be overrode self.content_related = False # Only requests/functions/queries are @@ -143,6 +144,16 @@ class TLObject: raise TypeError('Cannot interpret "{}" as a date.'.format(dt)) + # These are nearly always the same for all subclasses + def on_response(self, reader): + self.result = reader.tgread_object() + + def __str__(self): + return TLObject.pretty_format(self) + + def stringify(self): + return TLObject.pretty_format(self, indent=0) + # These should be overrode def resolve(self, client, utils): pass diff --git a/telethon_generator/tl_generator.py b/telethon_generator/tl_generator.py index 39bad15f..fb8ca4bd 100644 --- a/telethon_generator/tl_generator.py +++ b/telethon_generator/tl_generator.py @@ -395,23 +395,30 @@ class TLGenerator: if not a.flag_indicator and not a.generic_definition ) )) - builder.end_block() # Only requests can have a different response that's not their # serialized body, that is, we'll be setting their .result. - if tlobject.is_function: + # + # The default behaviour is reading a TLObject too, so no need + # to override it unless necessary. + if tlobject.is_function and not TLGenerator._is_boxed(tlobject.result): + builder.end_block() builder.writeln('def on_response(self, reader):') TLGenerator.write_request_result_code(builder, tlobject) - builder.end_block() - # Write the __str__(self) and stringify(self) functions - builder.writeln('def __str__(self):') - builder.writeln('return TLObject.pretty_format(self)') - builder.end_block() - - builder.writeln('def stringify(self):') - builder.writeln('return TLObject.pretty_format(self, indent=0)') - # builder.end_block() # No need to end the last block + @staticmethod + def _is_boxed(type_): + # https://core.telegram.org/mtproto/serialize#boxed-and-bare-types + # TL;DR; boxed types start with uppercase always, so we can use + # this to check whether everything in it is boxed or not. + # + # The API always returns a boxed type, but it may inside a Vector<> + # or a namespace, and the Vector may have a not-boxed type. For this + # reason we find whatever index, '<' or '.'. If neither are present + # we will get -1, and the 0th char is always upper case thus works. + # For Vector types and namespaces, it will check in the right place. + check_after = max(type_.find('<'), type_.find('.')) + return type_[check_after + 1].isupper() @staticmethod def _write_self_assign(builder, arg, get_input_code): @@ -697,13 +704,13 @@ class TLGenerator: # not parsed as arguments are and it's a bit harder to tell which # is which. if tlobject.result == 'Vector': - builder.writeln('reader.read_int() # Vector id') + builder.writeln('reader.read_int() # Vector ID') builder.writeln('count = reader.read_int()') builder.writeln( 'self.result = [reader.read_int() for _ in range(count)]' ) elif tlobject.result == 'Vector': - builder.writeln('reader.read_int() # Vector id') + builder.writeln('reader.read_int() # Vector ID') builder.writeln('count = reader.read_long()') builder.writeln( 'self.result = [reader.read_long() for _ in range(count)]'