Completely change errors (make one class for each)

This commit is contained in:
Lonami Exo 2017-06-10 11:47:51 +02:00
parent 5df4716164
commit 44ab85962b
13 changed files with 763 additions and 323 deletions

View File

@ -130,13 +130,20 @@ More examples are also available under the ``telethon_examples/`` folder.
Common errors
-------------
Some errors you may encounter when using Telethon can be the ``FloodWaitError``, which tells you
that you've been trying to send the very same request many times, too quickly. You must wait
``flood_wait_error.seconds`` before calling ``client.connect()`` again, since this error also
disconnects the client.
Errors resulting from Telegram queries all subclass the ``RPCError`` class.
This class is further specialized into further errors:
Another common one is the ``RPCError``, which usually has descriptive information on what went wrong.
However, you may encounter something strange. If you don't manage to solve it, please open an issue.
* ``InvalidDCError`` (303), the request must be repeated on another DC.
* ``BadRequestError`` (400), the request contained errors.
* ``UnauthorizedError`` (401), the user is not authorized yet.
* ``ForbiddenError`` (403), privacy violation error.
* ``NotFoundError`` (404), make sure you're invoking ``Request``'s!
* ``FloodError`` (420), the same request was repeated many times. Must wait ``.seconds``.
Further specialization is also available, for instance, the ``SessionPasswordNeededError``
when signing in means that a password must be provided to continue.
If the error is not recognised, it will only be an ``RPCError``.
Unless you know what you're doing, you should download media by always using the ``.download_file()``
function, which supports a ``str`` or a file handle as parameters. Otherwise, ``.invoke()`` may raise

View File

@ -1,4 +1,3 @@
from .errors import *
from .telegram_bare_client import TelegramBareClient
from .telegram_client import TelegramClient
from . import tl

View File

@ -1,266 +0,0 @@
import re
class ReadCancelledError(Exception):
"""Occurs when a read operation was cancelled"""
def __init__(self):
super().__init__(self, 'The read operation was cancelled.')
class InvalidParameterError(Exception):
"""Occurs when an invalid parameter is given, for example,
when either A or B are required but none is given"""
class TypeNotFoundError(Exception):
"""Occurs when a type is not found, for example,
when trying to read a TLObject with an invalid constructor code"""
def __init__(self, invalid_constructor_id):
super().__init__(
self, 'Could not find a matching Constructor ID for the TLObject '
'that was supposed to be read with ID {}. Most likely, a TLObject '
'was trying to be read when it should not be read.'
.format(hex(invalid_constructor_id)))
self.invalid_constructor_id = invalid_constructor_id
class InvalidDCError(Exception):
def __init__(self, rpc_error):
self.new_dc = rpc_error.__dict__.pop('additional_data')
self.__dict__.update(rpc_error.__dict__)
class InvalidChecksumError(Exception):
def __init__(self, checksum, valid_checksum):
super().__init__(
self,
'Invalid checksum ({} when {} was expected). This packet should be skipped.'
.format(checksum, valid_checksum))
self.checksum = checksum
self.valid_checksum = valid_checksum
class FloodWaitError(Exception):
def __init__(self, seconds):
super().__init__(
self,
'Too many requests were made too fast. Must wait {} seconds.'
.format(seconds)
)
self.seconds = seconds
class RPCError(Exception):
CodeMessages = {
303:
('ERROR_SEE_OTHER',
'The request must be repeated, but directed to a different data center.'
),
400:
('BAD_REQUEST',
'The query contains errors. In the event that a request was created using a '
'form and contains user generated data, the user should be notified that the '
'data must be corrected before the query is repeated.'),
401:
('UNAUTHORIZED',
'There was an unauthorized attempt to use functionality available only to '
'authorized users.'),
403:
('FORBIDDEN',
'Privacy violation. For example, an attempt to write a message to someone who '
'has blacklisted the current user.'),
404: ('NOT_FOUND',
'An attempt to invoke a non-existent object, such as a method.'),
420:
('FLOOD',
'The maximum allowed number of attempts to invoke the given method with '
'the given input parameters has been exceeded. For example, in an attempt '
'to request a large number of text messages (SMS) for the same phone number.'
),
500:
('INTERNAL',
'An internal server error occurred while a request was being processed; '
'for example, there was a disruption while accessing a database or file storage.'
)
}
ErrorMessages = {
# 303 ERROR_SEE_OTHER
'FILE_MIGRATE_(\d+)':
'The file to be accessed is currently stored in a different data center (#{}).',
'PHONE_MIGRATE_(\d+)':
'The phone number a user is trying to use for authorization is associated '
'with a different data center (#{}).',
'NETWORK_MIGRATE_(\d+)':
'The source IP address is associated with a different data center (#{}, '
'for registration).',
'USER_MIGRATE_(\d+)':
'The user whose identity is being used to execute queries is associated with '
'a different data center (#{} for registration).',
# 400 BAD_REQUEST
'FIRSTNAME_INVALID': 'The first name is invalid.',
'LASTNAME_INVALID': 'The last name is invalid.',
'PHONE_NUMBER_INVALID': 'The phone number is invalid.',
'PHONE_CODE_HASH_EMPTY': 'The phone code hash is missing.',
'PHONE_CODE_EMPTY': 'The phone code is missing.',
'PHONE_CODE_INVALID': 'The phone code entered was invalid.',
'PHONE_CODE_EXPIRED': 'The confirmation code has expired.',
'PHONE_NUMBER_BANNED':
'The used phone number has been banned from Telegram and cannot '
'be used anymore. Possibly check https://www.telegram.org/faq_spam.',
'API_ID_INVALID': 'The api_id/api_hash combination is invalid.',
'PHONE_NUMBER_OCCUPIED': 'The phone number is already in use.',
'PHONE_NUMBER_UNOCCUPIED': 'The phone number is not yet being used.',
'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).',
'TYPE_CONSTRUCTOR_INVALID': 'The type constructor is invalid.',
'FILE_PART_INVALID': 'The file part number is invalid.',
'FILE_PARTS_INVALID': 'The number of file parts is invalid.',
'FILE_PART_(\d+)_MISSING':
'Part {} of the file is missing from storage.',
'MD5_CHECKSUM_INVALID': 'The MD5 check-sums do not match.',
'PHOTO_INVALID_DIMENSIONS': 'The photo dimensions are invalid.',
'FIELD_NAME_INVALID': 'The field with the name FIELD_NAME is invalid.',
'FIELD_NAME_EMPTY': 'The field with the name FIELD_NAME is missing.',
'MSG_WAIT_FAILED': 'A waiting call returned an error.',
'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).',
'PASSWORD_HASH_INVALID':
'The password (and thus its hash value) you entered 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.',
'PEER_ID_INVALID':
'An invalid Peer was used. Make sure to pass the right peer type.',
'MESSAGE_EMPTY': 'Empty or invalid UTF-8 message was sent.',
'MESSAGE_TOO_LONG':
'Message was too long. Current maximum length is 4096 UTF-8 characters.',
'USERNAME_INVALID':
'Unacceptable username. Must match r"[a-zA-Z][\w\d]{4,32}"',
'USERNAME_OCCUPIED': 'The username is already taken.',
'USERNAME_NOT_OCCUPIED':
'See issue #96 for Telethon - try upgrading the library.',
'USERNAME_NOT_MODIFIED':
'The username is not different from the current username',
'USER_ID_INVALID':
'Invalid object ID for an user. Make sure to pass the right types.',
'CHAT_ID_INVALID':
'Invalid object ID for a chat. Make sure to pass the right types.',
'CHANNEL_INVALID':
'Invalid channel object. Make sure to pass the right types.',
'MESSAGE_ID_INVALID': 'The specified message ID is invalid.',
'CONNECTION_LAYER_INVALID':
'The very first request must always be InvokeWithLayerRequest.',
'INPUT_METHOD_INVALID':
'The invoked method does not exist anymore or has never existed.',
'DC_ID_INVALID':
'This occurs when an authorization is tried to be exported for '
'the same data center one is currently connected to.',
# 401 UNAUTHORIZED
'AUTH_KEY_UNREGISTERED': 'The key is not registered in the system.',
'AUTH_KEY_INVALID': 'The key is invalid.',
'USER_DEACTIVATED': 'The user has been deleted/deactivated.',
'SESSION_REVOKED':
'The authorization has been invalidated, because of the user terminating all sessions.',
'SESSION_EXPIRED': 'The authorization has expired.',
'ACTIVE_USER_REQUIRED':
'The method is only available to already activated users.',
'AUTH_KEY_PERM_EMPTY':
'The method is unavailable for temporary authorization key, not bound to permanent.',
'SESSION_PASSWORD_NEEDED':
'Two-steps verification is enabled and a password is required.',
'USER_ALREADY_PARTICIPANT':
'The authenticated user is already a participant of the chat.',
'INVITE_HASH_EXPIRED':
'The chat the user tried to join has expired and is not valid anymore.',
# 420 FLOOD
'FLOOD_WAIT_(\d+)': 'A wait of {} seconds is required.'
}
def __init__(self, code, message):
self.code = code
self.code_meaning = RPCError.CodeMessages[code]
self.message = message
self.must_resend = code == 303 # ERROR_SEE_OTHER, "The request must be repeated"
called_super = False
for key, error_msg in RPCError.ErrorMessages.items():
match = re.match(key, message)
if match:
error_msg = '{} ({}): {}'.format(
self.message, self.code, error_msg)
# Get additional_data, if any
if match.groups():
self.additional_data = int(match.group(1))
super().__init__(self,
error_msg.format(self.additional_data))
else:
self.additional_data = None
super().__init__(self, error_msg)
# Add another field to easily determine whether this error
# should be handled as a password-required error
self.password_required = message == 'SESSION_PASSWORD_NEEDED'
called_super = True
break
if not called_super:
super().__init__(
self, 'Unknown error message with code {}: {}'.format(code,
message))
class BadMessageError(Exception):
"""Occurs when handling a bad_message_notification"""
ErrorMessages = {
16:
'msg_id too low (most likely, client time is wrong it would be worthwhile to '
'synchronize it using msg_id notifications and re-send the original message '
'with the "correct" msg_id or wrap it in a container with a new msg_id if the '
'original message had waited too long on the client to be transmitted).',
17:
'msg_id too high (similar to the previous case, the client time has to be '
'synchronized, and the message re-sent with the correct msg_id).',
18:
'Incorrect two lower order msg_id bits (the server expects client message msg_id '
'to be divisible by 4).',
19:
'Container msg_id is the same as msg_id of a previously received message '
'(this must never happen).',
20:
'Message too old, and it cannot be verified whether the server has received a '
'message with this msg_id or not.',
32:
'msg_seqno too low (the server has already received a message with a lower '
'msg_id but with either a higher or an equal and odd seqno).',
33:
'msg_seqno too high (similarly, there is a message with a higher msg_id but with '
'either a lower or an equal and odd seqno).',
34:
'An even msg_seqno expected (irrelevant message), but odd received.',
35: 'Odd msg_seqno expected (relevant message), but even received.',
48:
'Incorrect server salt (in this case, the bad_server_salt response is received with '
'the correct salt, and the message is to be re-sent with it).',
64: 'Invalid container.'
}
def __init__(self, code):
super().__init__(self, BadMessageError.ErrorMessages.get(
code,
'Unknown error code (this should not happen): {}.'.format(code)))
self.code = code

View File

@ -0,0 +1,43 @@
import re
from .common import (
ReadCancelledError, InvalidParameterError, TypeNotFoundError,
InvalidChecksumError
)
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 *
def rpc_message_to_error(code, message):
errors = {
303: rpc_303_errors,
400: rpc_400_errors,
401: rpc_401_errors,
420: rpc_420_errors
}.get(code, None)
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)
elif code == 403:
return ForbiddenError()
elif code == 404:
return NotFoundError()
elif code == 500:
return ServerError()
return RPCError('{} (code {})'.format(message, code))

37
telethon/errors/common.py Normal file
View File

@ -0,0 +1,37 @@
"""Errors not related to the Telegram API itself"""
class ReadCancelledError(Exception):
"""Occurs when a read operation was cancelled"""
def __init__(self):
super().__init__(self, 'The read operation was cancelled.')
class InvalidParameterError(Exception):
"""Occurs when an invalid parameter is given, for example,
when either A or B are required but none is given"""
class TypeNotFoundError(Exception):
"""Occurs when a type is not found, for example,
when trying to read a TLObject with an invalid constructor code"""
def __init__(self, invalid_constructor_id):
super().__init__(
self, 'Could not find a matching Constructor ID for the TLObject '
'that was supposed to be read with ID {}. Most likely, a TLObject '
'was trying to be read when it should not be read.'
.format(hex(invalid_constructor_id)))
self.invalid_constructor_id = invalid_constructor_id
class InvalidChecksumError(Exception):
def __init__(self, checksum, valid_checksum):
super().__init__(
self,
'Invalid checksum ({} when {} was expected). This packet should be skipped.'
.format(checksum, valid_checksum))
self.checksum = checksum
self.valid_checksum = valid_checksum

View File

@ -0,0 +1,111 @@
class RPCError(Exception):
code = None
message = None
class InvalidDCError(RPCError):
"""
The request must be repeated, but directed to a different data center.
"""
code = 303
message = 'ERROR_SEE_OTHER'
class BadRequestError(RPCError):
"""
The query contains errors. In the event that a request was created
using a form and contains user generated data, the user should be
notified that the data must be corrected before the query is repeated.
"""
code = 400
message = 'BAD_REQUEST'
class UnauthorizedError(RPCError):
"""
There was an unauthorized attempt to use functionality available only
to authorized users.
"""
code = 401
message = 'UNAUTHORIZED'
class ForbiddenError(RPCError):
"""
Privacy violation. For example, an attempt to write a message to
someone who has blacklisted the current user.
"""
code = 403
message = 'FORBIDDEN'
class NotFoundError(RPCError):
"""
An attempt to invoke a non-existent object, such as a method.
"""
code = 404
message = 'NOT_FOUND'
class FloodError(RPCError):
"""
The maximum allowed number of attempts to invoke the given method
with the given input parameters has been exceeded. For example, in an
attempt to request a large number of text messages (SMS) for the same
phone number.
"""
code = 420
message = 'FLOOD'
class ServerError(RPCError):
"""
An internal server error occurred while a request was being processed
for example, there was a disruption while accessing a database or file
storage.
"""
code = 500
message = 'INTERNAL'
class BadMessageError(Exception):
"""Occurs when handling a bad_message_notification"""
ErrorMessages = {
16:
'msg_id too low (most likely, client time is wrong it would be worthwhile to '
'synchronize it using msg_id notifications and re-send the original message '
'with the "correct" msg_id or wrap it in a container with a new msg_id if the '
'original message had waited too long on the client to be transmitted).',
17:
'msg_id too high (similar to the previous case, the client time has to be '
'synchronized, and the message re-sent with the correct msg_id).',
18:
'Incorrect two lower order msg_id bits (the server expects client message msg_id '
'to be divisible by 4).',
19:
'Container msg_id is the same as msg_id of a previously received message '
'(this must never happen).',
20:
'Message too old, and it cannot be verified whether the server has received a '
'message with this msg_id or not.',
32:
'msg_seqno too low (the server has already received a message with a lower '
'msg_id but with either a higher or an equal and odd seqno).',
33:
'msg_seqno too high (similarly, there is a message with a higher msg_id but with '
'either a lower or an equal and odd seqno).',
34:
'An even msg_seqno expected (irrelevant message), but odd received.',
35: 'Odd msg_seqno expected (relevant message), but even received.',
48:
'Incorrect server salt (in this case, the bad_server_salt response is received with '
'the correct salt, and the message is to be re-sent with it).',
64: 'Invalid container.'
}
def __init__(self, code):
super().__init__(self, self.ErrorMessages.get(
code,
'Unknown error code (this should not happen): {}.'.format(code)))
self.code = code

View File

@ -0,0 +1,51 @@
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_303_errors = {
'FILE_MIGRATE_(\d+)': FileMigrateError,
'PHONE_MIGRATE_(\d+)': PhoneMigrateError,
'NETWORK_MIGRATE_(\d+)': NetworkMigrateError,
'USER_MIGRATE_(\d+)': UserMigrateError
}

View File

@ -0,0 +1,364 @@
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 ChannelInvalidError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'Invalid channel object. Make sure to pass the right types.'
)
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.'
)
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 LastNameInvalidError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'The last name is invalid.'
)
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 MsgWaitFailedError(BadRequestError):
def __init__(self, **kwargs):
super(Exception, self).__init__(
self,
'A waiting call returned an error.'
)
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,32}"'
)
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,
'See issue #96 for Telethon - try upgrading the library.'
)
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.'
)
rpc_400_errors = {
'API_ID_INVALID': ApiIdInvalidError,
'BOT_METHOD_INVALID': BotMethodInvalidError,
'CHANNEL_INVALID': ChannelInvalidError,
'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,
'LASTNAME_INVALID': LastNameInvalidError,
'MD5_CHECKSUM_INVALID': Md5ChecksumInvalidError,
'MESSAGE_EMPTY': MessageEmptyError,
'MESSAGE_ID_INVALID': MessageIdInvalidError,
'MESSAGE_TOO_LONG': MessageTooLongError,
'MSG_WAIT_FAILED': MsgWaitFailedError,
'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,
}

View File

@ -0,0 +1,98 @@
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_401_errors = {
'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,
}

View File

@ -0,0 +1,16 @@
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_420_errors = {
'FLOOD_WAIT_(\d+)': FloodWaitError
}

View File

@ -4,8 +4,7 @@ from threading import RLock
from .. import helpers as utils
from ..crypto import AES
from ..errors import (BadMessageError, FloodWaitError,
RPCError, InvalidDCError)
from ..errors import BadMessageError, InvalidDCError, rpc_message_to_error
from ..tl.all_tlobjects import tlobjects
from ..tl.types import MsgsAck
from ..extensions import BinaryReader, BinaryWriter
@ -106,7 +105,7 @@ class MtProtoSender:
if updates:
break # No request but one update read, exit
elif request.confirm_received:
break # Request, and result read, exit
break # Request, and result read, exit
self._logger.info('Request result received')
self._logger.debug('receive() released the lock')
@ -325,15 +324,16 @@ class MtProtoSender:
request.confirm_received = True
if inner_code == 0x2144ca19: # RPC Error
error = RPCError(
code=reader.read_int(), message=reader.tgread_string())
error = rpc_message_to_error(
reader.read_int(), reader.tgread_string())
# Acknowledge that we received the error
self._need_confirmation.append(request_id)
self._send_acknowledges()
self._logger.warning('Read RPC error: %s', str(error))
if error.must_resend:
if isinstance(error, InvalidDCError):
# Must resend this request
if not request:
raise ValueError(
'The previously sent request must be resent. '
@ -341,15 +341,7 @@ class MtProtoSender:
'(possibly called from a different thread).')
request.confirm_received = False
if error.message.startswith('FLOOD_WAIT_'):
self._updates_thread_sleep = error.additional_data
raise FloodWaitError(seconds=error.additional_data)
elif '_MIGRATE_' in error.message:
raise InvalidDCError(error)
else:
raise error
raise error
else:
if not request:
raise ValueError(

View File

@ -9,15 +9,16 @@ from . import TelegramBareClient
# Import some externalized utilities to work with the Telegram types and more
from . import helpers as utils
from .errors import (RPCError, InvalidDCError, InvalidParameterError,
ReadCancelledError)
from .network import authenticator, MtProtoSender, TcpTransport
from .errors import (RPCError, UnauthorizedError, InvalidParameterError,
ReadCancelledError, FileMigrateError, PhoneMigrateError,
NetworkMigrateError, UserMigrateError, PhoneCodeEmptyError,
PhoneCodeExpiredError, PhoneCodeHashEmptyError,
PhoneCodeInvalidError)
from .parser.markdown_parser import parse_message_entities
# For sending and receiving requests
from .tl import MTProtoRequest, Session, JsonSession
from .tl.all_tlobjects import layer
from .tl.functions import (InitConnectionRequest, InvokeWithLayerRequest)
# Required to get the password salt
from .tl.functions.account import GetPasswordRequest
@ -251,18 +252,13 @@ class TelegramClient(TelegramBareClient):
# TODO Retry if 'result' is None?
return result
except InvalidDCError as e:
if not e.message.startswith('FILE_MIGRATE_'):
# Only reconnect unless we're trying to download media,
# this is, on login (user migrate, phone migrate, etc.)
self._logger.info('DC error when invoking request, '
'attempting to reconnect at DC {}'
.format(e.new_dc))
except (PhoneMigrateError, NetworkMigrateError, UserMigrateError) as e:
self._logger.info('DC error when invoking request, '
'attempting to reconnect at DC {}'
.format(e.new_dc))
self.reconnect(new_dc=e.new_dc)
return self.invoke(request, timeout=timeout)
else:
raise
self.reconnect(new_dc=e.new_dc)
return self.invoke(request, timeout=timeout)
finally:
self._lock.release()
@ -326,11 +322,9 @@ class TelegramClient(TelegramBareClient):
result = self.invoke(SignInRequest(
phone_number, self._phone_code_hashes[phone_number], code))
except RPCError as error:
if error.message.startswith('PHONE_CODE_'):
return None
else:
raise
except (PhoneCodeEmptyError, PhoneCodeExpiredError,
PhoneCodeHashEmptyError, PhoneCodeInvalidError):
return None
elif password:
salt = self.invoke(GetPasswordRequest()).current_salt
@ -386,11 +380,8 @@ class TelegramClient(TelegramBareClient):
or None if the request fails (hence, not authenticated)."""
try:
return self.invoke(GetUsersRequest([InputUserSelf()]))[0]
except RPCError as e:
if e.code == 401: # 401 UNAUTHORIZED
return None
else:
raise
except UnauthorizedError:
return None
@staticmethod
def list_sessions():
@ -743,7 +734,7 @@ class TelegramClient(TelegramBareClient):
file_size=file_size,
progress_callback=progress_callback
)
except InvalidDCError as e:
except FileMigrateError as e:
on_dc = e.new_dc
if on_dc is not None:

View File

@ -2,7 +2,7 @@ import shutil
from getpass import getpass
from telethon import TelegramClient
from telethon.errors import RPCError
from telethon.errors import SessionPasswordNeededError
from telethon.tl.types import UpdateShortChatMessage, UpdateShortMessage
from telethon.utils import get_display_name
@ -76,14 +76,11 @@ class InteractiveTelegramClient(TelegramClient):
self_user = self.sign_in(user_phone, code)
# Two-step verification may be enabled
except RPCError as e:
if e.password_required:
pw = getpass('Two step verification is enabled. '
'Please enter your password: ')
except SessionPasswordNeededError as e:
pw = getpass('Two step verification is enabled. '
'Please enter your password: ')
self_user = self.sign_in(password=pw)
else:
raise
self_user = self.sign_in(password=pw)
def run(self):
# Listen for updates