diff --git a/telethon_generator/parsers/errors.py b/telethon_generator/parsers/errors.py index 04cd3412..b049ef52 100644 --- a/telethon_generator/parsers/errors.py +++ b/telethon_generator/parsers/errors.py @@ -44,6 +44,7 @@ class Error: # Should these be split into different files or doesn't really matter? # Telegram isn't exactly consistent with returned errors anyway. self.int_code = codes[0] + self.int_codes = codes self.str_code = name self.subclass = _get_class_name(codes[0]) self.subclass_exists = abs(codes[0]) in KNOWN_BASE_CLASSES diff --git a/telethon_generator/parsers/methods.py b/telethon_generator/parsers/methods.py index ebc7e22f..22f77b1e 100644 --- a/telethon_generator/parsers/methods.py +++ b/telethon_generator/parsers/methods.py @@ -9,6 +9,15 @@ class Usability(enum.Enum): BOT = 2 BOTH = 4 + @property + def key(self): + return { + Usability.UNKNOWN: 'unknown', + Usability.USER: 'user', + Usability.BOT: 'bot', + Usability.BOTH: 'both', + }[self] + class MethodInfo: def __init__(self, name, usability, errors, friendly): diff --git a/telethon_generator/syncerrors.py b/telethon_generator/syncerrors.py new file mode 100644 index 00000000..9a63aae3 --- /dev/null +++ b/telethon_generator/syncerrors.py @@ -0,0 +1,53 @@ +# Should be fed with the JSON obtained from https://core.telegram.org/api/errors#error-database +import re +import csv +import sys +import json +from pathlib import Path + +sys.path.insert(0, '..') + +from telethon_generator.parsers.errors import parse_errors, Error +from telethon_generator.parsers.methods import parse_methods, MethodInfo + +ERRORS = Path('data/errors.csv') +METHODS = Path('data/methods.csv') +FRIENDLY = Path('data/friendly.csv') + + +def main(): + new_errors = [] + new_methods = [] + + self_errors = {e.str_code: e for e in parse_errors(ERRORS)} + self_methods = {m.name: m for m in parse_methods(METHODS, FRIENDLY, self_errors)} + + tg_data = json.load(sys.stdin) + + def get_desc(code): + return re.sub(r'\s*&\w+;\s*', '', (tg_data['descriptions'].get(code) or '').rstrip('.')) + + for int_code, errors in tg_data['errors'].items(): + int_code = int(int_code) # json does not support non-string keys + for code, methods in errors.items(): + str_code = code.replace('%d', 'X') + if error := self_errors.get(str_code): + error.int_codes.append(int_code) # de-duplicated once later + if not error.description: # prefer our descriptions + if not error.has_captures: # need descriptions with specific text if error has captures + error.description = get_desc(code) + else: + self_errors[str_code] = Error([int_code], str_code, get_desc(code)) + + new_errors.extend((e.str_code, ' '.join(map(str, sorted(set(e.int_codes)))), e.description) for e in self_errors.values()) + new_methods.extend((m.name, m.usability.key, ' '.join(sorted(e.str_code for e in m.errors))) for m in self_methods.values()) + + csv.register_dialect('plain', lineterminator='\n') + with ERRORS.open('w', encoding='utf-8', newline='') as fd: + csv.writer(fd, 'plain').writerows((('name', 'codes', 'description'), *sorted(new_errors))) + with METHODS.open('w', encoding='utf-8', newline='') as fd: + csv.writer(fd, 'plain').writerows((('method', 'usability', 'errors'), *sorted(new_methods))) + + +if __name__ == '__main__': + main()