mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-25 10:53:44 +03:00
Generate errors from PWRTelegram's API
This commit is contained in:
parent
f37b9ed20e
commit
38ccd6d1d9
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
|||
telethon/tl/functions/
|
||||
telethon/tl/types/
|
||||
telethon/tl/all_tlobjects.py
|
||||
telethon/tl/errors/rpc_error_list.py
|
||||
|
||||
# User session
|
||||
*.session
|
||||
|
|
|
@ -8,15 +8,8 @@ from .common import (
|
|||
CdnFileTamperedError
|
||||
)
|
||||
|
||||
from .rpc_errors import (
|
||||
RPCError, InvalidDCError, BadRequestError, UnauthorizedError,
|
||||
ForbiddenError, NotFoundError, FloodError, ServerError, BadMessageError
|
||||
)
|
||||
|
||||
from .rpc_errors_303 import *
|
||||
from .rpc_errors_400 import *
|
||||
from .rpc_errors_401 import *
|
||||
from .rpc_errors_420 import *
|
||||
# This imports the base errors too, as they're imported there
|
||||
from .rpc_error_list import *
|
||||
|
||||
|
||||
def report_error(code, message, report_method):
|
||||
|
@ -43,27 +36,31 @@ def rpc_message_to_error(code, message, report_method=None):
|
|||
args=(code, message, report_method)
|
||||
).start()
|
||||
|
||||
errors = {
|
||||
303: rpc_errors_303_all,
|
||||
400: rpc_errors_400_all,
|
||||
401: rpc_errors_401_all,
|
||||
420: rpc_errors_420_all
|
||||
}.get(code, None)
|
||||
# Try to get the error by direct look-up, otherwise regex
|
||||
# TODO Maybe regexes could live in a separate dictionary?
|
||||
cls = rpc_errors_all.get(message, None)
|
||||
if cls:
|
||||
return cls()
|
||||
|
||||
if errors is not None:
|
||||
for msg, cls in errors.items():
|
||||
m = re.match(msg, message)
|
||||
if m:
|
||||
extra = int(m.group(1)) if m.groups() else None
|
||||
return cls(extra=extra)
|
||||
for msg_regex, cls in rpc_errors_all.items():
|
||||
m = re.match(msg_regex, message)
|
||||
if m:
|
||||
capture = int(m.group(1)) if m.groups() else None
|
||||
return cls(capture=capture)
|
||||
|
||||
elif code == 403:
|
||||
if code == 400:
|
||||
return BadRequestError(message)
|
||||
|
||||
if code == 401:
|
||||
return UnauthorizedError(message)
|
||||
|
||||
if code == 403:
|
||||
return ForbiddenError(message)
|
||||
|
||||
elif code == 404:
|
||||
if code == 404:
|
||||
return NotFoundError(message)
|
||||
|
||||
elif code == 500:
|
||||
if code == 500:
|
||||
return ServerError(message)
|
||||
|
||||
return RPCError('{} (code {})'.format(message, code))
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
from . import InvalidDCError
|
||||
|
||||
|
||||
class FileMigrateError(InvalidDCError):
|
||||
def __init__(self, **kwargs):
|
||||
self.new_dc = kwargs['extra']
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The file to be accessed is currently stored in DC {}.'
|
||||
.format(self.new_dc)
|
||||
)
|
||||
|
||||
|
||||
class PhoneMigrateError(InvalidDCError):
|
||||
def __init__(self, **kwargs):
|
||||
self.new_dc = kwargs['extra']
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The phone number a user is trying to use for authorization is '
|
||||
'associated with DC {}.'
|
||||
.format(self.new_dc)
|
||||
)
|
||||
|
||||
|
||||
class NetworkMigrateError(InvalidDCError):
|
||||
def __init__(self, **kwargs):
|
||||
self.new_dc = kwargs['extra']
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The source IP address is associated with DC {}.'
|
||||
.format(self.new_dc)
|
||||
)
|
||||
|
||||
|
||||
class UserMigrateError(InvalidDCError):
|
||||
def __init__(self, **kwargs):
|
||||
self.new_dc = kwargs['extra']
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The user whose identity is being used to execute queries is '
|
||||
'associated with DC {}.'
|
||||
.format(self.new_dc)
|
||||
)
|
||||
|
||||
|
||||
rpc_errors_303_all = {
|
||||
'FILE_MIGRATE_(\d+)': FileMigrateError,
|
||||
'PHONE_MIGRATE_(\d+)': PhoneMigrateError,
|
||||
'NETWORK_MIGRATE_(\d+)': NetworkMigrateError,
|
||||
'USER_MIGRATE_(\d+)': UserMigrateError
|
||||
}
|
|
@ -1,453 +0,0 @@
|
|||
from . import BadRequestError
|
||||
|
||||
|
||||
class ApiIdInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The api_id/api_hash combination is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class BotMethodInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The API access for bot users is restricted. The method you '
|
||||
'tried to invoke cannot be executed as a bot.'
|
||||
)
|
||||
|
||||
|
||||
class CdnMethodInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'This method cannot be invoked on a CDN server. Refer to '
|
||||
'https://core.telegram.org/cdn#schema for available methods.'
|
||||
)
|
||||
|
||||
|
||||
class ChannelInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Invalid channel object. Make sure to pass the right types,'
|
||||
' for instance making sure that the request is designed for '
|
||||
'channels or otherwise look for a different one more suited.'
|
||||
)
|
||||
|
||||
|
||||
class ChannelPrivateError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The channel specified is private and you lack permission to '
|
||||
'access it. Another reason may be that you were banned from it.'
|
||||
)
|
||||
|
||||
|
||||
class ChatAdminRequiredError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Chat admin privileges are required to do that in the specified '
|
||||
'chat (for example, to send a message in a channel which is not '
|
||||
'yours).'
|
||||
)
|
||||
|
||||
|
||||
class ChatIdInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Invalid object ID for a chat. Make sure to pass the right types,'
|
||||
' for instance making sure that the request is designed for chats'
|
||||
' (not channels/megagroups) or otherwise look for a different one'
|
||||
' more suited.\nAn example working with a megagroup and'
|
||||
' AddChatUserRequest, it will fail because megagroups are channels'
|
||||
'. Use InviteToChannelRequest instead.'
|
||||
)
|
||||
|
||||
|
||||
class ConnectionLangPackInvalid(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The specified language pack is not valid. This is meant to be '
|
||||
'used by official applications only so far, leave it empty.'
|
||||
)
|
||||
|
||||
|
||||
class ConnectionLayerInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The very first request must always be InvokeWithLayerRequest.'
|
||||
)
|
||||
|
||||
|
||||
class DcIdInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'This occurs when an authorization is tried to be exported for '
|
||||
'the same data center one is currently connected to.'
|
||||
)
|
||||
|
||||
|
||||
class FieldNameEmptyError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The field with the name FIELD_NAME is missing.'
|
||||
)
|
||||
|
||||
|
||||
class FieldNameInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The field with the name FIELD_NAME is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class FilePartsInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The number of file parts is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class FilePartMissingError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
self.which = kwargs['extra']
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Part {} of the file is missing from storage.'.format(self.which)
|
||||
)
|
||||
|
||||
|
||||
class FilePartInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The file part number is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class FirstNameInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The first name is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class InputMethodInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The invoked method does not exist anymore or has never existed.'
|
||||
)
|
||||
|
||||
|
||||
class InputRequestTooLongError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The input request was too long. This may be a bug in the library '
|
||||
'as it can occur when serializing more bytes than it should (like'
|
||||
'appending the vector constructor code at the end of a message).'
|
||||
)
|
||||
|
||||
|
||||
class LastNameInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The last name is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class LimitInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'An invalid limit was provided. See '
|
||||
'https://core.telegram.org/api/files#downloading-files'
|
||||
)
|
||||
|
||||
|
||||
class LocationInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The location given for a file was invalid. See '
|
||||
'https://core.telegram.org/api/files#downloading-files'
|
||||
)
|
||||
|
||||
|
||||
class Md5ChecksumInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The MD5 check-sums do not match.'
|
||||
)
|
||||
|
||||
|
||||
class MessageEmptyError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Empty or invalid UTF-8 message was sent.'
|
||||
)
|
||||
|
||||
|
||||
class MessageIdInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The specified message ID is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class MessageTooLongError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Message was too long. Current maximum length is 4096 UTF-8 '
|
||||
'characters.'
|
||||
)
|
||||
|
||||
|
||||
class MessageNotModifiedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Content of the message was not modified.'
|
||||
)
|
||||
|
||||
|
||||
class MsgWaitFailedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'A waiting call returned an error.'
|
||||
)
|
||||
|
||||
|
||||
class OffsetInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The given offset was invalid, it must be divisible by 1KB. '
|
||||
'See https://core.telegram.org/api/files#downloading-files'
|
||||
)
|
||||
|
||||
|
||||
|
||||
class PasswordHashInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The password (and thus its hash value) you entered is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class PeerIdInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'An invalid Peer was used. Make sure to pass the right peer type.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneCodeEmptyError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The phone code is missing.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneCodeExpiredError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The confirmation code has expired.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneCodeHashEmptyError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The phone code hash is missing.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneCodeInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The phone code entered was invalid.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneNumberBannedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The used phone number has been banned from Telegram and cannot '
|
||||
'be used anymore. Maybe check https://www.telegram.org/faq_spam.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneNumberInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The phone number is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneNumberOccupiedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The phone number is already in use.'
|
||||
)
|
||||
|
||||
|
||||
class PhoneNumberUnoccupiedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The phone number is not yet being used.'
|
||||
)
|
||||
|
||||
|
||||
class PhotoInvalidDimensionsError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The photo dimensions are invalid.'
|
||||
)
|
||||
|
||||
|
||||
class TypeConstructorInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The type constructor is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class UsernameInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Unacceptable username. Must match r"[a-zA-Z][\w\d]{4,31}".'
|
||||
)
|
||||
|
||||
|
||||
class UsernameNotModifiedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The username is not different from the current username.'
|
||||
)
|
||||
|
||||
|
||||
class UsernameNotOccupiedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The username is not in use by anyone else yet.'
|
||||
)
|
||||
|
||||
|
||||
class UsernameOccupiedError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The username is already taken.'
|
||||
)
|
||||
|
||||
|
||||
class UsersTooFewError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Not enough users (to create a chat, for example).'
|
||||
)
|
||||
|
||||
|
||||
class UsersTooMuchError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The maximum number of users has been exceeded (to create a '
|
||||
'chat, for example).'
|
||||
)
|
||||
|
||||
|
||||
class UserIdInvalidError(BadRequestError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Invalid object ID for an user. Make sure to pass the right types,'
|
||||
'for instance making sure that the request is designed for users'
|
||||
'or otherwise look for a different one more suited.'
|
||||
)
|
||||
|
||||
|
||||
rpc_errors_400_all = {
|
||||
'API_ID_INVALID': ApiIdInvalidError,
|
||||
'BOT_METHOD_INVALID': BotMethodInvalidError,
|
||||
'CDN_METHOD_INVALID': CdnMethodInvalidError,
|
||||
'CHANNEL_INVALID': ChannelInvalidError,
|
||||
'CHANNEL_PRIVATE': ChannelPrivateError,
|
||||
'CHAT_ADMIN_REQUIRED': ChatAdminRequiredError,
|
||||
'CHAT_ID_INVALID': ChatIdInvalidError,
|
||||
'CONNECTION_LAYER_INVALID': ConnectionLayerInvalidError,
|
||||
'DC_ID_INVALID': DcIdInvalidError,
|
||||
'FIELD_NAME_EMPTY': FieldNameEmptyError,
|
||||
'FIELD_NAME_INVALID': FieldNameInvalidError,
|
||||
'FILE_PARTS_INVALID': FilePartsInvalidError,
|
||||
'FILE_PART_(\d+)_MISSING': FilePartMissingError,
|
||||
'FILE_PART_INVALID': FilePartInvalidError,
|
||||
'FIRSTNAME_INVALID': FirstNameInvalidError,
|
||||
'INPUT_METHOD_INVALID': InputMethodInvalidError,
|
||||
'INPUT_REQUEST_TOO_LONG': InputRequestTooLongError,
|
||||
'LASTNAME_INVALID': LastNameInvalidError,
|
||||
'LIMIT_INVALID': LimitInvalidError,
|
||||
'LOCATION_INVALID': LocationInvalidError,
|
||||
'MD5_CHECKSUM_INVALID': Md5ChecksumInvalidError,
|
||||
'MESSAGE_EMPTY': MessageEmptyError,
|
||||
'MESSAGE_ID_INVALID': MessageIdInvalidError,
|
||||
'MESSAGE_TOO_LONG': MessageTooLongError,
|
||||
'MESSAGE_NOT_MODIFIED': MessageNotModifiedError,
|
||||
'MSG_WAIT_FAILED': MsgWaitFailedError,
|
||||
'OFFSET_INVALID': OffsetInvalidError,
|
||||
'PASSWORD_HASH_INVALID': PasswordHashInvalidError,
|
||||
'PEER_ID_INVALID': PeerIdInvalidError,
|
||||
'PHONE_CODE_EMPTY': PhoneCodeEmptyError,
|
||||
'PHONE_CODE_EXPIRED': PhoneCodeExpiredError,
|
||||
'PHONE_CODE_HASH_EMPTY': PhoneCodeHashEmptyError,
|
||||
'PHONE_CODE_INVALID': PhoneCodeInvalidError,
|
||||
'PHONE_NUMBER_BANNED': PhoneNumberBannedError,
|
||||
'PHONE_NUMBER_INVALID': PhoneNumberInvalidError,
|
||||
'PHONE_NUMBER_OCCUPIED': PhoneNumberOccupiedError,
|
||||
'PHONE_NUMBER_UNOCCUPIED': PhoneNumberUnoccupiedError,
|
||||
'PHOTO_INVALID_DIMENSIONS': PhotoInvalidDimensionsError,
|
||||
'TYPE_CONSTRUCTOR_INVALID': TypeConstructorInvalidError,
|
||||
'USERNAME_INVALID': UsernameInvalidError,
|
||||
'USERNAME_NOT_MODIFIED': UsernameNotModifiedError,
|
||||
'USERNAME_NOT_OCCUPIED': UsernameNotOccupiedError,
|
||||
'USERNAME_OCCUPIED': UsernameOccupiedError,
|
||||
'USERS_TOO_FEW': UsersTooFewError,
|
||||
'USERS_TOO_MUCH': UsersTooMuchError,
|
||||
'USER_ID_INVALID': UserIdInvalidError,
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
from . import UnauthorizedError
|
||||
|
||||
|
||||
class ActiveUserRequiredError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The method is only available to already activated users.'
|
||||
)
|
||||
|
||||
|
||||
class AuthKeyInvalidError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The key is invalid.'
|
||||
)
|
||||
|
||||
|
||||
class AuthKeyPermEmptyError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The method is unavailable for temporary authorization key, not '
|
||||
'bound to permanent.'
|
||||
)
|
||||
|
||||
|
||||
class AuthKeyUnregisteredError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The key is not registered in the system.'
|
||||
)
|
||||
|
||||
|
||||
class InviteHashExpiredError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The chat the user tried to join has expired and is not valid '
|
||||
'anymore.'
|
||||
)
|
||||
|
||||
|
||||
class SessionExpiredError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The authorization has expired.'
|
||||
)
|
||||
|
||||
|
||||
class SessionPasswordNeededError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'Two-steps verification is enabled and a password is required.'
|
||||
)
|
||||
|
||||
|
||||
class SessionRevokedError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The authorization has been invalidated, because of the user '
|
||||
'terminating all sessions.'
|
||||
)
|
||||
|
||||
|
||||
class UserAlreadyParticipantError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The authenticated user is already a participant of the chat.'
|
||||
)
|
||||
|
||||
|
||||
class UserDeactivatedError(UnauthorizedError):
|
||||
def __init__(self, **kwargs):
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'The user has been deleted/deactivated.'
|
||||
)
|
||||
|
||||
|
||||
rpc_errors_401_all = {
|
||||
'ACTIVE_USER_REQUIRED': ActiveUserRequiredError,
|
||||
'AUTH_KEY_INVALID': AuthKeyInvalidError,
|
||||
'AUTH_KEY_PERM_EMPTY': AuthKeyPermEmptyError,
|
||||
'AUTH_KEY_UNREGISTERED': AuthKeyUnregisteredError,
|
||||
'INVITE_HASH_EXPIRED': InviteHashExpiredError,
|
||||
'SESSION_EXPIRED': SessionExpiredError,
|
||||
'SESSION_PASSWORD_NEEDED': SessionPasswordNeededError,
|
||||
'SESSION_REVOKED': SessionRevokedError,
|
||||
'USER_ALREADY_PARTICIPANT': UserAlreadyParticipantError,
|
||||
'USER_DEACTIVATED': UserDeactivatedError,
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
from . import FloodError
|
||||
|
||||
|
||||
class FloodWaitError(FloodError):
|
||||
def __init__(self, **kwargs):
|
||||
self.seconds = kwargs['extra']
|
||||
super(Exception, self).__init__(
|
||||
self,
|
||||
'A wait of {} seconds is required.'
|
||||
.format(self.seconds)
|
||||
)
|
||||
|
||||
|
||||
rpc_errors_420_all = {
|
||||
'FLOOD_WAIT_(\d+)': FloodWaitError
|
||||
}
|
65
telethon_generator/error_descriptions
Normal file
65
telethon_generator/error_descriptions
Normal file
|
@ -0,0 +1,65 @@
|
|||
# These are comments. Spaces around the = are optional. Empty lines ignored.
|
||||
#CODE=Human readable description
|
||||
|
||||
FILE_MIGRATE_X=The file to be accessed is currently stored in DC {}
|
||||
PHONE_MIGRATE_X=The phone number a user is trying to use for authorization is associated with DC {}
|
||||
NETWORK_MIGRATE_X=The source IP address is associated with DC {}
|
||||
USER_MIGRATE_X=The user whose identity is being used to execute queries is associated with DC {}
|
||||
API_ID_INVALID=The api_id/api_hash combination is invalid
|
||||
BOT_METHOD_INVALID=The API access for bot users is restricted. The method you tried to invoke cannot be executed as a bot
|
||||
CDN_METHOD_INVALID=This method cannot be invoked on a CDN server. Refer to https://core.telegram.org/cdn#schema for available methods
|
||||
CHANNEL_INVALID=Invalid channel object. Make sure to pass the right types, for instance making sure that the request is designed for channels or otherwise look for a different one more suited
|
||||
CHANNEL_PRIVATE=The channel specified is private and you lack permission to access it. Another reason may be that you were banned from it
|
||||
CHAT_ADMIN_REQUIRED=Chat admin privileges are required to do that in the specified chat (for example, to send a message in a channel which is not yours)
|
||||
CHAT_ID_INVALID=Invalid object ID for a chat. Make sure to pass the right types, for instance making sure that the request is designed for chats (not channels/megagroups) or otherwise look for a different one more suited\nAn example working with a megagroup and AddChatUserRequest, it will fail because megagroups are channels. Use InviteToChannelRequest instead
|
||||
CONNECTION_LANG_PACK_INVALID=The specified language pack is not valid. This is meant to be used by official applications only so far, leave it empty
|
||||
CONNECTION_LAYER_INVALID=The very first request must always be InvokeWithLayerRequest
|
||||
DC_ID_INVALID=This occurs when an authorization is tried to be exported for the same data center one is currently connected to
|
||||
FIELD_NAME_EMPTY=The field with the name FIELD_NAME is missing
|
||||
FIELD_NAME_INVALID=The field with the name FIELD_NAME is invalid
|
||||
FILE_PARTS_INVALID=The number of file parts is invalid
|
||||
FILE_PART_X_MISSING=Part {} of the file is missing from storage
|
||||
FILE_PART_INVALID=The file part number is invalid
|
||||
FIRSTNAME_INVALID=The first name is invalid
|
||||
INPUT_METHOD_INVALID=The invoked method does not exist anymore or has never existed
|
||||
INPUT_REQUEST_TOO_LONG=The input request was too long. This may be a bug in the library as it can occur when serializing more bytes than it should (likeappending the vector constructor code at the end of a message)
|
||||
LASTNAME_INVALID=The last name is invalid
|
||||
LIMIT_INVALID=An invalid limit was provided. See https://core.telegram.org/api/files#downloading-files
|
||||
LOCATION_INVALID=The location given for a file was invalid. See https://core.telegram.org/api/files#downloading-files
|
||||
MD5_CHECKSUM_INVALID=The MD5 check-sums do not match
|
||||
MESSAGE_EMPTY=Empty or invalid UTF-8 message was sent
|
||||
MESSAGE_ID_INVALID=The specified message ID is invalid
|
||||
MESSAGE_TOO_LONG=Message was too long. Current maximum length is 4096 UTF-8 characters
|
||||
MESSAGE_NOT_MODIFIED=Content of the message was not modified
|
||||
MSG_WAIT_FAILED=A waiting call returned an error
|
||||
OFFSET_INVALID=The given offset was invalid, it must be divisible by 1KB. See https://core.telegram.org/api/files#downloading-files
|
||||
PASSWORD_HASH_INVALID=The password (and thus its hash value) you entered is invalid
|
||||
PEER_ID_INVALID=An invalid Peer was used. Make sure to pass the right peer type
|
||||
PHONE_CODE_EMPTY=The phone code is missing
|
||||
PHONE_CODE_EXPIRED=The confirmation code has expired
|
||||
PHONE_CODE_HASH_EMPTY=The phone code hash is missing
|
||||
PHONE_CODE_INVALID=The phone code entered was invalid
|
||||
PHONE_NUMBER_BANNED=The used phone number has been banned from Telegram and cannot be used anymore. Maybe check https://www.telegram.org/faq_spam
|
||||
PHONE_NUMBER_INVALID=The phone number is invalid
|
||||
PHONE_NUMBER_OCCUPIED=The phone number is already in use
|
||||
PHONE_NUMBER_UNOCCUPIED=The phone number is not yet being used
|
||||
PHOTO_INVALID_DIMENSIONS=The photo dimensions are invalid
|
||||
TYPE_CONSTRUCTOR_INVALID=The type constructor is invalid
|
||||
USERNAME_INVALID=Unacceptable username. Must match r"[a-zA-Z][\w\d]{4,31}"
|
||||
USERNAME_NOT_MODIFIED=The username is not different from the current username
|
||||
USERNAME_NOT_OCCUPIED=The username is not in use by anyone else yet
|
||||
USERNAME_OCCUPIED=The username is already taken
|
||||
USERS_TOO_FEW=Not enough users (to create a chat, for example)
|
||||
USERS_TOO_MUCH=The maximum number of users has been exceeded (to create a chat, for example)
|
||||
USER_ID_INVALID=Invalid object ID for an user. Make sure to pass the right types, for instance making sure that the request is designed for users or otherwise look for a different one more suited
|
||||
ACTIVE_USER_REQUIRED=The method is only available to already activated users
|
||||
AUTH_KEY_INVALID=The key is invalid
|
||||
AUTH_KEY_PERM_EMPTY=The method is unavailable for temporary authorization key, not bound to permanent
|
||||
AUTH_KEY_UNREGISTERED=The key is not registered in the system
|
||||
INVITE_HASH_EXPIRED=The chat the user tried to join has expired and is not valid anymore
|
||||
SESSION_EXPIRED=The authorization has expired
|
||||
SESSION_PASSWORD_NEEDED=Two-steps verification is enabled and a password is required
|
||||
SESSION_REVOKED=The authorization has been invalidated, because of the user terminating all sessions
|
||||
USER_ALREADY_PARTICIPANT=The authenticated user is already a participant of the chat
|
||||
USER_DEACTIVATED=The user has been deleted/deactivated
|
||||
FLOOD_WAIT_X=A wait of {} seconds is required
|
172
telethon_generator/error_generator.py
Normal file
172
telethon_generator/error_generator.py
Normal file
|
@ -0,0 +1,172 @@
|
|||
import json
|
||||
import re
|
||||
import urllib.request
|
||||
from collections import defaultdict
|
||||
|
||||
URL = 'https://rpc.pwrtelegram.xyz/?all'
|
||||
OUTPUT = '../telethon/errors/rpc_error_list.py'
|
||||
JSON_OUTPUT = 'errors.json'
|
||||
|
||||
known_base_classes = {
|
||||
303: 'InvalidDCError',
|
||||
400: 'BadRequestError',
|
||||
401: 'UnauthorizedError',
|
||||
403: 'ForbiddenError',
|
||||
404: 'NotFoundError',
|
||||
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 fetch_errors(url=URL, output=JSON_OUTPUT):
|
||||
print('Opening a connection to', url, '...')
|
||||
r = urllib.request.urlopen(url)
|
||||
print('Checking response...')
|
||||
data = json.loads(
|
||||
r.read().decode(r.info().get_param('charset') or 'utf-8')
|
||||
)
|
||||
if data.get('ok'):
|
||||
print('Response was okay, saving data')
|
||||
with open(output, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f)
|
||||
return True
|
||||
else:
|
||||
print('The data received was not okay:')
|
||||
print(json.dumps(data, indent=4))
|
||||
return False
|
||||
|
||||
|
||||
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(
|
||||
f'\n'
|
||||
f'\n'
|
||||
f'class {name}({get_class_name(code)}):\n'
|
||||
f' def __init__(self, **kwargs):\n'
|
||||
f' '
|
||||
)
|
||||
if capture_name:
|
||||
f.write(
|
||||
f"self.{capture_name} = int(kwargs.get('capture', 0))\n"
|
||||
f" "
|
||||
)
|
||||
f.write(f'super(Exception, self).__init__(self, {repr(desc)}')
|
||||
if capture_name:
|
||||
f.write(f'.format(self.{capture_name})')
|
||||
f.write(')\n')
|
||||
|
||||
|
||||
def generate_code(json_file=JSON_OUTPUT, output=OUTPUT):
|
||||
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].add('FLOOD_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('error_descriptions', 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',
|
||||
'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(
|
||||
f'from .rpc_base_errors import RPCError, BadMessageError, '
|
||||
f'{", ".join(known_base_classes.values())}\n'
|
||||
)
|
||||
for code, cls in needed_base_classes:
|
||||
f.write(
|
||||
f'\n'
|
||||
f'\n'
|
||||
f'class {cls}(RPCError):\n'
|
||||
f' code = {code}\n'
|
||||
)
|
||||
|
||||
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 accross different files?
|
||||
write_error(f, error_code, name, description, capture)
|
||||
|
||||
f.write('\n\nrpc_errors_all = {\n')
|
||||
for pattern, name in patterns:
|
||||
f.write(f' {repr(pattern)}: {name},\n')
|
||||
f.write('}\n')
|
1
telethon_generator/errors.json
Normal file
1
telethon_generator/errors.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user