diff --git a/setup.py b/setup.py index f0b958c2..4769dbf7 100755 --- a/setup.py +++ b/setup.py @@ -57,11 +57,11 @@ def generate(which): from telethon_generator.generators import\ generate_errors, generate_tlobjects, generate_docs, clean_tlobjects - 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) + errors = list(parse_errors(ERRORS_IN_JSON, ERRORS_IN_DESC)) + tlobjects = list(itertools.chain( + parse_tl(TLOBJECT_IN_CORE_TL, layer=layer), + parse_tl(TLOBJECT_IN_TL, layer=layer))) if not which: which.extend(('tl', 'errors')) diff --git a/telethon/utils.py b/telethon/utils.py index a1a3c897..0ba64177 100644 --- a/telethon/utils.py +++ b/telethon/utils.py @@ -41,7 +41,7 @@ USERNAME_RE = re.compile( # See https://telegram.org/blog/inline-bots#how-does-it-work VALID_USERNAME_RE = re.compile( r'^([a-z][\w\d]{3,30}[a-z\d]' - r'|gif|vid|pic|bing|wiki|imdb|bold|vote|like|coub)$', + r'|gif|vid|pic|bing|wiki|imdb|bold|vote|like|coub|ya)$', re.IGNORECASE ) diff --git a/telethon_generator/generator.py b/telethon_generator/generator.py deleted file mode 100644 index f1dad375..00000000 --- a/telethon_generator/generator.py +++ /dev/null @@ -1,30 +0,0 @@ -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_CORE_TL = 'data/mtproto_api.tl' -TLOBJECT_INPUT_TL = 'data/telegram_api.tl' -TLOBJECT_OUTPUT = '../telethon/tl' - -DOCS_INPUT_RES = 'data/html' -DOCS_OUTPUT = '../docs' - - -if __name__ == '__main__': - 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) - - generate_tlobjects(tlobjects, layer, TLOBJECT_OUTPUT) - with open(ERRORS_OUTPUT, 'w', encoding='utf-8') as file: - generate_errors(errors, file) - - generate_docs(tlobjects, errors, layer, DOCS_INPUT_RES, DOCS_OUTPUT) diff --git a/telethon_generator/parsers/tlobject.py b/telethon_generator/parsers/tlobject.py index d2a3684e..07bf6145 100644 --- a/telethon_generator/parsers/tlobject.py +++ b/telethon_generator/parsers/tlobject.py @@ -10,9 +10,17 @@ CORE_TYPES = ( 0x1cb5c415, # vector#1cb5c415 {t:Type} # [ t ] = Vector t; ) +# https://github.com/telegramdesktop/tdesktop/blob/4bf66cb6e93f3965b40084771b595e93d0b11bcd/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py#L57-L62 +WHITELISTED_MISMATCHING_IDS = { + # 0 represents any layer + 0: {'ipPortSecret', 'accessPointRule', 'help.configSimple'}, + 77: {'channel'}, + 78: {'channel'} +} + class TLObject: - def __init__(self, fullname, object_id, args, result, is_function): + def __init__(self, fullname, object_id, args, result, is_function, layer): """ Initializes a new TLObject, given its properties. @@ -22,6 +30,7 @@ class TLObject: :param args: The arguments, if any, of the TL object :param result: The result type of the TL object :param is_function: Is the object a function or a type? + :param layer: The layer this TLObject belongs to. """ # The name can or not have a namespace self.fullname = fullname @@ -38,11 +47,12 @@ class TLObject: self.id = self.infer_id() else: self.id = int(object_id, base=16) - # As of layer 78 ipPortSecret won't match, Telegram may still be - # developing this layer and more changes shall be to expect. - # - # assert self.id == self.infer_id(),\ - # 'Invalid inferred ID for ' + repr(self) + whitelist = WHITELISTED_MISMATCHING_IDS[0] |\ + WHITELISTED_MISMATCHING_IDS.get(layer, set()) + + if self.fullname not in whitelist: + assert self.id == self.infer_id(),\ + 'Invalid inferred ID for ' + repr(self) self.class_name = snake_to_camel_case( self.name, suffix='Request' if self.is_function else '') @@ -208,7 +218,7 @@ class TLArg: return str(self).replace(':date', ':int').replace('?date', '?int') -def _from_line(line, is_function): +def _from_line(line, is_function, layer): match = re.match( r'^([\w.]+)' # 'name' r'(?:#([0-9a-fA-F]+))?' # '#optionalcode' @@ -234,12 +244,13 @@ def _from_line(line, is_function): object_id=match.group(2), result=match.group(3), is_function=is_function, + layer=layer, args=[TLArg(name, arg_type, brace != '') for brace, name, arg_type in args_match] ) -def parse_tl(file_path, ignore_core=False): +def parse_tl(file_path, layer, ignore_core=False): """This method yields TLObjects from a given .tl file.""" with open(file_path, encoding='utf-8') as file: is_function = False @@ -259,7 +270,7 @@ def parse_tl(file_path, ignore_core=False): continue try: - result = _from_line(line, is_function) + result = _from_line(line, is_function, layer=layer) if not ignore_core or result.id not in CORE_TYPES: yield result except ValueError as e: