mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-11-04 09:57:29 +03:00 
			
		
		
		
	Split error_generator code into parsers/generators packages
This commit is contained in:
		
							parent
							
								
									8b2afa3530
								
							
						
					
					
						commit
						463fcedf27
					
				| 
						 | 
					@ -1,151 +0,0 @@
 | 
				
			||||||
import json
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
from collections import defaultdict
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
known_base_classes = {
 | 
					 | 
				
			||||||
    303: 'InvalidDCError',
 | 
					 | 
				
			||||||
    400: 'BadRequestError',
 | 
					 | 
				
			||||||
    401: 'UnauthorizedError',
 | 
					 | 
				
			||||||
    403: 'ForbiddenError',
 | 
					 | 
				
			||||||
    404: 'NotFoundError',
 | 
					 | 
				
			||||||
    406: 'AuthKeyError',
 | 
					 | 
				
			||||||
    420: 'FloodError',
 | 
					 | 
				
			||||||
    500: 'ServerError',
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# The API doesn't return the code for some (vital) errors. They are
 | 
					 | 
				
			||||||
# all assumed to be 400, except these well-known ones that aren't.
 | 
					 | 
				
			||||||
known_codes = {
 | 
					 | 
				
			||||||
    'ACTIVE_USER_REQUIRED': 401,
 | 
					 | 
				
			||||||
    'AUTH_KEY_UNREGISTERED': 401,
 | 
					 | 
				
			||||||
    'USER_DEACTIVATED': 401
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def get_class_name(error_code):
 | 
					 | 
				
			||||||
    if isinstance(error_code, int):
 | 
					 | 
				
			||||||
        return known_base_classes.get(
 | 
					 | 
				
			||||||
            error_code, 'RPCError' + str(error_code).replace('-', 'Neg')
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if 'FIRSTNAME' in error_code:
 | 
					 | 
				
			||||||
        error_code = error_code.replace('FIRSTNAME', 'FIRST_NAME')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result = re.sub(
 | 
					 | 
				
			||||||
        r'_([a-z])', lambda m: m.group(1).upper(), error_code.lower()
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    return result[:1].upper() + result[1:].replace('_', '') + 'Error'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def write_error(f, code, name, desc, capture_name):
 | 
					 | 
				
			||||||
    f.write(
 | 
					 | 
				
			||||||
        '\n\nclass {}({}):\n    def __init__(self, **kwargs):\n        '
 | 
					 | 
				
			||||||
        ''.format(name, get_class_name(code))
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    if capture_name:
 | 
					 | 
				
			||||||
        f.write(
 | 
					 | 
				
			||||||
            "self.{} = int(kwargs.get('capture', 0))\n        ".format(capture_name)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    f.write('super(Exception, self).__init__({}'.format(repr(desc)))
 | 
					 | 
				
			||||||
    if capture_name:
 | 
					 | 
				
			||||||
        f.write('.format(self.{})'.format(capture_name))
 | 
					 | 
				
			||||||
    f.write(')\n')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def generate_code(output, json_file, errors_desc):
 | 
					 | 
				
			||||||
    with open(json_file, encoding='utf-8') as f:
 | 
					 | 
				
			||||||
        data = json.load(f)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    errors = defaultdict(set)
 | 
					 | 
				
			||||||
    # PWRTelegram's API doesn't return all errors, which we do need here.
 | 
					 | 
				
			||||||
    # Add some special known-cases manually first.
 | 
					 | 
				
			||||||
    errors[420].update((
 | 
					 | 
				
			||||||
        'FLOOD_WAIT_X', 'FLOOD_TEST_PHONE_WAIT_X'
 | 
					 | 
				
			||||||
    ))
 | 
					 | 
				
			||||||
    errors[401].update((
 | 
					 | 
				
			||||||
        'AUTH_KEY_INVALID', 'SESSION_EXPIRED', 'SESSION_REVOKED'
 | 
					 | 
				
			||||||
    ))
 | 
					 | 
				
			||||||
    errors[303].update((
 | 
					 | 
				
			||||||
        'FILE_MIGRATE_X', 'PHONE_MIGRATE_X',
 | 
					 | 
				
			||||||
        'NETWORK_MIGRATE_X', 'USER_MIGRATE_X'
 | 
					 | 
				
			||||||
    ))
 | 
					 | 
				
			||||||
    for error_code, method_errors in data['result'].items():
 | 
					 | 
				
			||||||
        for error_list in method_errors.values():
 | 
					 | 
				
			||||||
            for error in error_list:
 | 
					 | 
				
			||||||
                errors[int(error_code)].add(re.sub('_\d+', '_X', error).upper())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Some errors are in the human result, but not with a code. Assume code 400
 | 
					 | 
				
			||||||
    for error in data['human_result']:
 | 
					 | 
				
			||||||
        if error[0] != '-' and not error.isdigit():
 | 
					 | 
				
			||||||
            error = re.sub('_\d+', '_X', error).upper()
 | 
					 | 
				
			||||||
            if not any(error in es for es in errors.values()):
 | 
					 | 
				
			||||||
                errors[known_codes.get(error, 400)].add(error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Some error codes are not known, so create custom base classes if needed
 | 
					 | 
				
			||||||
    needed_base_classes = [
 | 
					 | 
				
			||||||
        (e, get_class_name(e)) for e in errors if e not in known_base_classes
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Prefer the descriptions that are related with Telethon way of coding to
 | 
					 | 
				
			||||||
    # those that PWRTelegram's API provides.
 | 
					 | 
				
			||||||
    telethon_descriptions = {}
 | 
					 | 
				
			||||||
    with open(errors_desc, encoding='utf-8') as f:
 | 
					 | 
				
			||||||
        for line in f:
 | 
					 | 
				
			||||||
            line = line.strip()
 | 
					 | 
				
			||||||
            if line and not line.startswith('#'):
 | 
					 | 
				
			||||||
                equal = line.index('=')
 | 
					 | 
				
			||||||
                message, description = line[:equal], line[equal + 1:]
 | 
					 | 
				
			||||||
                telethon_descriptions[message.rstrip()] = description.lstrip()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Names for the captures, or 'x' if unknown
 | 
					 | 
				
			||||||
    capture_names = {
 | 
					 | 
				
			||||||
        'FloodWaitError': 'seconds',
 | 
					 | 
				
			||||||
        'FloodTestPhoneWaitError': 'seconds',
 | 
					 | 
				
			||||||
        'FileMigrateError': 'new_dc',
 | 
					 | 
				
			||||||
        'NetworkMigrateError': 'new_dc',
 | 
					 | 
				
			||||||
        'PhoneMigrateError': 'new_dc',
 | 
					 | 
				
			||||||
        'UserMigrateError': 'new_dc',
 | 
					 | 
				
			||||||
        'FilePartMissingError': 'which'
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Everything ready, generate the code
 | 
					 | 
				
			||||||
    with open(output, 'w', encoding='utf-8') as f:
 | 
					 | 
				
			||||||
        f.write(
 | 
					 | 
				
			||||||
            'from .rpc_base_errors import RPCError, BadMessageError, {}\n'.format(
 | 
					 | 
				
			||||||
                ", ".join(known_base_classes.values()))
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        for code, cls in needed_base_classes:
 | 
					 | 
				
			||||||
            f.write(
 | 
					 | 
				
			||||||
                '\n\nclass {}(RPCError):\n    code = {}\n'.format(cls, code)
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        patterns = []  # Save this dictionary later in the generated code
 | 
					 | 
				
			||||||
        for error_code, error_set in errors.items():
 | 
					 | 
				
			||||||
            for error in sorted(error_set):
 | 
					 | 
				
			||||||
                description = telethon_descriptions.get(
 | 
					 | 
				
			||||||
                    error, '\n'.join(data['human_result'].get(
 | 
					 | 
				
			||||||
                        error, ['No description known.']
 | 
					 | 
				
			||||||
                    ))
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                has_captures = '_X' in error
 | 
					 | 
				
			||||||
                if has_captures:
 | 
					 | 
				
			||||||
                    name = get_class_name(error.replace('_X', ''))
 | 
					 | 
				
			||||||
                    pattern = error.replace('_X', r'_(\d+)')
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    name, pattern = get_class_name(error), error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                patterns.append((pattern, name))
 | 
					 | 
				
			||||||
                capture = capture_names.get(name, 'x') if has_captures else None
 | 
					 | 
				
			||||||
                # TODO Some errors have the same name but different code,
 | 
					 | 
				
			||||||
                # split this across different files?
 | 
					 | 
				
			||||||
                write_error(f, error_code, name, description, capture)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        f.write('\n\nrpc_errors_all = {\n')
 | 
					 | 
				
			||||||
        for pattern, name in patterns:
 | 
					 | 
				
			||||||
            f.write('    {}: {},\n'.format(repr(pattern), name))
 | 
					 | 
				
			||||||
        f.write('}\n')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    generate_code('../telethon/errors/rpc_error_list.py',
 | 
					 | 
				
			||||||
                  'errors.json', 'error_descriptions')
 | 
					 | 
				
			||||||
							
								
								
									
										15
									
								
								telethon_generator/generator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								telethon_generator/generator.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					from telethon_generator.parsers import parse_errors
 | 
				
			||||||
 | 
					from telethon_generator.generators import generate_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INPUT_JSON = 'errors.json'
 | 
				
			||||||
 | 
					INPUT_DESCRIPTIONS = 'error_descriptions'
 | 
				
			||||||
 | 
					OUTPUT = '../telethon/errors/rpc_error_list.py'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    with open(OUTPUT, 'w', encoding='utf-8') as file:
 | 
				
			||||||
 | 
					        generate_errors(
 | 
				
			||||||
 | 
					            errors=list(parse_errors(INPUT_JSON, INPUT_DESCRIPTIONS)),
 | 
				
			||||||
 | 
					            f=file
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
							
								
								
									
										1
									
								
								telethon_generator/generators/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								telethon_generator/generators/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					from .errors import generate_errors
 | 
				
			||||||
							
								
								
									
										52
									
								
								telethon_generator/generators/errors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								telethon_generator/generators/errors.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					import itertools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generate_errors(errors, f):
 | 
				
			||||||
 | 
					    # Exact/regex match to create {CODE: ErrorClassName}
 | 
				
			||||||
 | 
					    exact_match = []
 | 
				
			||||||
 | 
					    regex_match = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Find out what subclasses to import and which to create
 | 
				
			||||||
 | 
					    import_base, create_base = set(), {}
 | 
				
			||||||
 | 
					    for error in errors:
 | 
				
			||||||
 | 
					        if error.subclass_exists:
 | 
				
			||||||
 | 
					            import_base.add(error.subclass)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            create_base[error.subclass] = error.int_code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if error.has_captures:
 | 
				
			||||||
 | 
					            regex_match.append(error)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            exact_match.append(error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Imports and new subclass creation
 | 
				
			||||||
 | 
					    f.write('from .rpc_base_errors import RPCError, {}\n'
 | 
				
			||||||
 | 
					            .format(", ".join(sorted(import_base))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for cls, int_code in sorted(create_base.items(), key=lambda t: t[1]):
 | 
				
			||||||
 | 
					        f.write('\n\nclass {}(RPCError):\n    code = {}\n'
 | 
				
			||||||
 | 
					                .format(cls, int_code))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Error classes generation
 | 
				
			||||||
 | 
					    for error in errors:
 | 
				
			||||||
 | 
					        f.write('\n\nclass {}({}):\n    def __init__(self, **kwargs):\n'
 | 
				
			||||||
 | 
					                '        '.format(error.name, error.subclass))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if error.has_captures:
 | 
				
			||||||
 | 
					            f.write("self.{} = int(kwargs.get('capture', 0))\n        "
 | 
				
			||||||
 | 
					                    .format(error.capture_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        f.write('super(Exception, self).__init__({}'
 | 
				
			||||||
 | 
					                .format(repr(error.description)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if error.has_captures:
 | 
				
			||||||
 | 
					            f.write('.format(self.{})'.format(error.capture_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        f.write(')\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Create the actual {CODE: ErrorClassName} dict once classes are defined
 | 
				
			||||||
 | 
					    # TODO Actually make a difference between regex/exact
 | 
				
			||||||
 | 
					    f.write('\n\nrpc_errors_all = {\n')
 | 
				
			||||||
 | 
					    for error in itertools.chain(regex_match, exact_match):
 | 
				
			||||||
 | 
					        f.write('    {}: {},\n'.format(repr(error.pattern), error.name))
 | 
				
			||||||
 | 
					    f.write('}\n')
 | 
				
			||||||
							
								
								
									
										1
									
								
								telethon_generator/parsers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								telethon_generator/parsers/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					from .errors import Error, parse_errors
 | 
				
			||||||
							
								
								
									
										145
									
								
								telethon_generator/parsers/errors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								telethon_generator/parsers/errors.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,145 @@
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					from collections import defaultdict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Core base classes depending on the integer error code
 | 
				
			||||||
 | 
					KNOWN_BASE_CLASSES = {
 | 
				
			||||||
 | 
					    303: 'InvalidDCError',
 | 
				
			||||||
 | 
					    400: 'BadRequestError',
 | 
				
			||||||
 | 
					    401: 'UnauthorizedError',
 | 
				
			||||||
 | 
					    403: 'ForbiddenError',
 | 
				
			||||||
 | 
					    404: 'NotFoundError',
 | 
				
			||||||
 | 
					    406: 'AuthKeyError',
 | 
				
			||||||
 | 
					    420: 'FloodError',
 | 
				
			||||||
 | 
					    500: 'ServerError',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The API doesn't return the code for some (vital) errors. They are
 | 
				
			||||||
 | 
					# all assumed to be 400, except these well-known ones that aren't.
 | 
				
			||||||
 | 
					KNOWN_CODES = {
 | 
				
			||||||
 | 
					    'ACTIVE_USER_REQUIRED': 401,
 | 
				
			||||||
 | 
					    'AUTH_KEY_UNREGISTERED': 401,
 | 
				
			||||||
 | 
					    'USER_DEACTIVATED': 401
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Give better semantic names to some captures
 | 
				
			||||||
 | 
					CAPTURE_NAMES = {
 | 
				
			||||||
 | 
					    'FloodWaitError': 'seconds',
 | 
				
			||||||
 | 
					    'FloodTestPhoneWaitError': 'seconds',
 | 
				
			||||||
 | 
					    'FileMigrateError': 'new_dc',
 | 
				
			||||||
 | 
					    'NetworkMigrateError': 'new_dc',
 | 
				
			||||||
 | 
					    'PhoneMigrateError': 'new_dc',
 | 
				
			||||||
 | 
					    'UserMigrateError': 'new_dc',
 | 
				
			||||||
 | 
					    'FilePartMissingError': 'which'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_class_name(error_code):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Gets the corresponding class name for the given error code,
 | 
				
			||||||
 | 
					    this either being an integer (thus base error name) or str.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    if isinstance(error_code, int):
 | 
				
			||||||
 | 
					        return KNOWN_BASE_CLASSES.get(
 | 
				
			||||||
 | 
					            error_code, 'RPCError' + str(error_code).replace('-', 'Neg')
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if 'FIRSTNAME' in error_code:
 | 
				
			||||||
 | 
					        error_code = error_code.replace('FIRSTNAME', 'FIRST_NAME')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result = re.sub(
 | 
				
			||||||
 | 
					        r'_([a-z])', lambda m: m.group(1).upper(), error_code.lower()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    return result[:1].upper() + result[1:].replace('_', '') + 'Error'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Error:
 | 
				
			||||||
 | 
					    def __init__(self, int_code, str_code, description):
 | 
				
			||||||
 | 
					        # TODO Some errors have the same str_code but different int_code
 | 
				
			||||||
 | 
					        # Should these be split into different files or doesn't really matter?
 | 
				
			||||||
 | 
					        # Telegram isn't exactly consistent with returned errors anyway.
 | 
				
			||||||
 | 
					        self.int_code = int_code
 | 
				
			||||||
 | 
					        self.str_code = str_code
 | 
				
			||||||
 | 
					        self.subclass = _get_class_name(int_code)
 | 
				
			||||||
 | 
					        self.subclass_exists = int_code in KNOWN_BASE_CLASSES
 | 
				
			||||||
 | 
					        self.description = description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.has_captures = '_X' in str_code
 | 
				
			||||||
 | 
					        if self.has_captures:
 | 
				
			||||||
 | 
					            self.name = _get_class_name(str_code.replace('_X', ''))
 | 
				
			||||||
 | 
					            self.pattern = str_code.replace('_X', r'_(\d+)')
 | 
				
			||||||
 | 
					            self.capture_name = CAPTURE_NAMES.get(self.name, 'x')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.name = _get_class_name(str_code)
 | 
				
			||||||
 | 
					            self.pattern = str_code
 | 
				
			||||||
 | 
					            self.capture_name = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def parse_errors(json_file, descriptions_file):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Parses the given JSON file in the following format:
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "ok": true,
 | 
				
			||||||
 | 
					            "human_result": {"int_code": ["descriptions"]},
 | 
				
			||||||
 | 
					            "result": {"int_code": {"full_method_name": ["str_error"]}}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The descriptions file, which has precedence over the JSON's human_result,
 | 
				
			||||||
 | 
					    should have the following format:
 | 
				
			||||||
 | 
					        # comment
 | 
				
			||||||
 | 
					        str_error=Description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The method yields `Error` instances as a result.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    with open(json_file, encoding='utf-8') as f:
 | 
				
			||||||
 | 
					        data = json.load(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    errors = defaultdict(set)
 | 
				
			||||||
 | 
					    # PWRTelegram's API doesn't return all errors, which we do need here.
 | 
				
			||||||
 | 
					    # Add some special known-cases manually first.
 | 
				
			||||||
 | 
					    errors[420].update((
 | 
				
			||||||
 | 
					        'FLOOD_WAIT_X', 'FLOOD_TEST_PHONE_WAIT_X'
 | 
				
			||||||
 | 
					    ))
 | 
				
			||||||
 | 
					    errors[401].update((
 | 
				
			||||||
 | 
					        'AUTH_KEY_INVALID', 'SESSION_EXPIRED', 'SESSION_REVOKED'
 | 
				
			||||||
 | 
					    ))
 | 
				
			||||||
 | 
					    errors[303].update((
 | 
				
			||||||
 | 
					        'FILE_MIGRATE_X', 'PHONE_MIGRATE_X',
 | 
				
			||||||
 | 
					        'NETWORK_MIGRATE_X', 'USER_MIGRATE_X'
 | 
				
			||||||
 | 
					    ))
 | 
				
			||||||
 | 
					    for int_code, method_errors in data['result'].items():
 | 
				
			||||||
 | 
					        for error_list in method_errors.values():
 | 
				
			||||||
 | 
					            for error in error_list:
 | 
				
			||||||
 | 
					                errors[int(int_code)].add(re.sub('_\d+', '_X', error).upper())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Some errors are in the human result, but not with a code. Assume 400
 | 
				
			||||||
 | 
					    for error in data['human_result']:
 | 
				
			||||||
 | 
					        if error[0] != '-' and not error.isdigit():
 | 
				
			||||||
 | 
					            error = re.sub('_\d+', '_X', error).upper()
 | 
				
			||||||
 | 
					            if not any(error in es for es in errors.values()):
 | 
				
			||||||
 | 
					                errors[KNOWN_CODES.get(error, 400)].add(error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Prefer the descriptions that are related with Telethon way of coding
 | 
				
			||||||
 | 
					    # to those that PWRTelegram's API provides.
 | 
				
			||||||
 | 
					    telethon_descriptions = {}
 | 
				
			||||||
 | 
					    with open(descriptions_file, encoding='utf-8') as f:
 | 
				
			||||||
 | 
					        for line in f:
 | 
				
			||||||
 | 
					            line = line.strip()
 | 
				
			||||||
 | 
					            if line and not line.startswith('#'):
 | 
				
			||||||
 | 
					                equal = line.index('=')
 | 
				
			||||||
 | 
					                message, description = line[:equal], line[equal + 1:]
 | 
				
			||||||
 | 
					                telethon_descriptions[message.rstrip()] = description.lstrip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for int_code, error_set in errors.items():
 | 
				
			||||||
 | 
					        for str_code in sorted(error_set):
 | 
				
			||||||
 | 
					            description = telethon_descriptions.get(
 | 
				
			||||||
 | 
					                str_code, '\n'.join(data['human_result'].get(
 | 
				
			||||||
 | 
					                    str_code, ['No description known.']
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            yield Error(
 | 
				
			||||||
 | 
					                int_code=int_code,
 | 
				
			||||||
 | 
					                str_code=str_code,
 | 
				
			||||||
 | 
					                description=description,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user