mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-10 19:46:36 +03:00
Completely change errors (make one class for each)
This commit is contained in:
parent
5df4716164
commit
44ab85962b
19
README.rst
19
README.rst
|
@ -130,13 +130,20 @@ More examples are also available under the ``telethon_examples/`` folder.
|
||||||
Common errors
|
Common errors
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Some errors you may encounter when using Telethon can be the ``FloodWaitError``, which tells you
|
Errors resulting from Telegram queries all subclass the ``RPCError`` class.
|
||||||
that you've been trying to send the very same request many times, too quickly. You must wait
|
This class is further specialized into further errors:
|
||||||
``flood_wait_error.seconds`` before calling ``client.connect()`` again, since this error also
|
|
||||||
disconnects the client.
|
|
||||||
|
|
||||||
Another common one is the ``RPCError``, which usually has descriptive information on what went wrong.
|
* ``InvalidDCError`` (303), the request must be repeated on another DC.
|
||||||
However, you may encounter something strange. If you don't manage to solve it, please open an issue.
|
* ``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()``
|
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
|
function, which supports a ``str`` or a file handle as parameters. Otherwise, ``.invoke()`` may raise
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from .errors import *
|
|
||||||
from .telegram_bare_client import TelegramBareClient
|
from .telegram_bare_client import TelegramBareClient
|
||||||
from .telegram_client import TelegramClient
|
from .telegram_client import TelegramClient
|
||||||
from . import tl
|
from . import tl
|
||||||
|
|
|
@ -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
|
|
43
telethon/errors/__init__.py
Normal file
43
telethon/errors/__init__.py
Normal 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
37
telethon/errors/common.py
Normal 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
|
111
telethon/errors/rpc_errors.py
Normal file
111
telethon/errors/rpc_errors.py
Normal 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
|
51
telethon/errors/rpc_errors_303.py
Normal file
51
telethon/errors/rpc_errors_303.py
Normal 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
|
||||||
|
}
|
364
telethon/errors/rpc_errors_400.py
Normal file
364
telethon/errors/rpc_errors_400.py
Normal 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,
|
||||||
|
}
|
98
telethon/errors/rpc_errors_401.py
Normal file
98
telethon/errors/rpc_errors_401.py
Normal 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,
|
||||||
|
}
|
16
telethon/errors/rpc_errors_420.py
Normal file
16
telethon/errors/rpc_errors_420.py
Normal 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
|
||||||
|
}
|
|
@ -4,8 +4,7 @@ from threading import RLock
|
||||||
|
|
||||||
from .. import helpers as utils
|
from .. import helpers as utils
|
||||||
from ..crypto import AES
|
from ..crypto import AES
|
||||||
from ..errors import (BadMessageError, FloodWaitError,
|
from ..errors import BadMessageError, InvalidDCError, rpc_message_to_error
|
||||||
RPCError, InvalidDCError)
|
|
||||||
from ..tl.all_tlobjects import tlobjects
|
from ..tl.all_tlobjects import tlobjects
|
||||||
from ..tl.types import MsgsAck
|
from ..tl.types import MsgsAck
|
||||||
from ..extensions import BinaryReader, BinaryWriter
|
from ..extensions import BinaryReader, BinaryWriter
|
||||||
|
@ -325,15 +324,16 @@ class MtProtoSender:
|
||||||
request.confirm_received = True
|
request.confirm_received = True
|
||||||
|
|
||||||
if inner_code == 0x2144ca19: # RPC Error
|
if inner_code == 0x2144ca19: # RPC Error
|
||||||
error = RPCError(
|
error = rpc_message_to_error(
|
||||||
code=reader.read_int(), message=reader.tgread_string())
|
reader.read_int(), reader.tgread_string())
|
||||||
|
|
||||||
# Acknowledge that we received the error
|
# Acknowledge that we received the error
|
||||||
self._need_confirmation.append(request_id)
|
self._need_confirmation.append(request_id)
|
||||||
self._send_acknowledges()
|
self._send_acknowledges()
|
||||||
|
|
||||||
self._logger.warning('Read RPC error: %s', str(error))
|
self._logger.warning('Read RPC error: %s', str(error))
|
||||||
if error.must_resend:
|
if isinstance(error, InvalidDCError):
|
||||||
|
# Must resend this request
|
||||||
if not request:
|
if not request:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'The previously sent request must be resent. '
|
'The previously sent request must be resent. '
|
||||||
|
@ -341,14 +341,6 @@ class MtProtoSender:
|
||||||
'(possibly called from a different thread).')
|
'(possibly called from a different thread).')
|
||||||
request.confirm_received = False
|
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:
|
else:
|
||||||
if not request:
|
if not request:
|
||||||
|
|
|
@ -9,15 +9,16 @@ from . import TelegramBareClient
|
||||||
|
|
||||||
# Import some externalized utilities to work with the Telegram types and more
|
# Import some externalized utilities to work with the Telegram types and more
|
||||||
from . import helpers as utils
|
from . import helpers as utils
|
||||||
from .errors import (RPCError, InvalidDCError, InvalidParameterError,
|
from .errors import (RPCError, UnauthorizedError, InvalidParameterError,
|
||||||
ReadCancelledError)
|
ReadCancelledError, FileMigrateError, PhoneMigrateError,
|
||||||
from .network import authenticator, MtProtoSender, TcpTransport
|
NetworkMigrateError, UserMigrateError, PhoneCodeEmptyError,
|
||||||
|
PhoneCodeExpiredError, PhoneCodeHashEmptyError,
|
||||||
|
PhoneCodeInvalidError)
|
||||||
|
|
||||||
from .parser.markdown_parser import parse_message_entities
|
from .parser.markdown_parser import parse_message_entities
|
||||||
|
|
||||||
# For sending and receiving requests
|
# For sending and receiving requests
|
||||||
from .tl import MTProtoRequest, Session, JsonSession
|
from .tl import MTProtoRequest, Session, JsonSession
|
||||||
from .tl.all_tlobjects import layer
|
|
||||||
from .tl.functions import (InitConnectionRequest, InvokeWithLayerRequest)
|
|
||||||
|
|
||||||
# Required to get the password salt
|
# Required to get the password salt
|
||||||
from .tl.functions.account import GetPasswordRequest
|
from .tl.functions.account import GetPasswordRequest
|
||||||
|
@ -251,18 +252,13 @@ class TelegramClient(TelegramBareClient):
|
||||||
# TODO Retry if 'result' is None?
|
# TODO Retry if 'result' is None?
|
||||||
return result
|
return result
|
||||||
|
|
||||||
except InvalidDCError as e:
|
except (PhoneMigrateError, NetworkMigrateError, UserMigrateError) 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, '
|
self._logger.info('DC error when invoking request, '
|
||||||
'attempting to reconnect at DC {}'
|
'attempting to reconnect at DC {}'
|
||||||
.format(e.new_dc))
|
.format(e.new_dc))
|
||||||
|
|
||||||
self.reconnect(new_dc=e.new_dc)
|
self.reconnect(new_dc=e.new_dc)
|
||||||
return self.invoke(request, timeout=timeout)
|
return self.invoke(request, timeout=timeout)
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
self._lock.release()
|
self._lock.release()
|
||||||
|
@ -326,11 +322,9 @@ class TelegramClient(TelegramBareClient):
|
||||||
result = self.invoke(SignInRequest(
|
result = self.invoke(SignInRequest(
|
||||||
phone_number, self._phone_code_hashes[phone_number], code))
|
phone_number, self._phone_code_hashes[phone_number], code))
|
||||||
|
|
||||||
except RPCError as error:
|
except (PhoneCodeEmptyError, PhoneCodeExpiredError,
|
||||||
if error.message.startswith('PHONE_CODE_'):
|
PhoneCodeHashEmptyError, PhoneCodeInvalidError):
|
||||||
return None
|
return None
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
elif password:
|
elif password:
|
||||||
salt = self.invoke(GetPasswordRequest()).current_salt
|
salt = self.invoke(GetPasswordRequest()).current_salt
|
||||||
|
@ -386,11 +380,8 @@ class TelegramClient(TelegramBareClient):
|
||||||
or None if the request fails (hence, not authenticated)."""
|
or None if the request fails (hence, not authenticated)."""
|
||||||
try:
|
try:
|
||||||
return self.invoke(GetUsersRequest([InputUserSelf()]))[0]
|
return self.invoke(GetUsersRequest([InputUserSelf()]))[0]
|
||||||
except RPCError as e:
|
except UnauthorizedError:
|
||||||
if e.code == 401: # 401 UNAUTHORIZED
|
|
||||||
return None
|
return None
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_sessions():
|
def list_sessions():
|
||||||
|
@ -743,7 +734,7 @@ class TelegramClient(TelegramBareClient):
|
||||||
file_size=file_size,
|
file_size=file_size,
|
||||||
progress_callback=progress_callback
|
progress_callback=progress_callback
|
||||||
)
|
)
|
||||||
except InvalidDCError as e:
|
except FileMigrateError as e:
|
||||||
on_dc = e.new_dc
|
on_dc = e.new_dc
|
||||||
|
|
||||||
if on_dc is not None:
|
if on_dc is not None:
|
||||||
|
|
|
@ -2,7 +2,7 @@ import shutil
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
|
||||||
from telethon import TelegramClient
|
from telethon import TelegramClient
|
||||||
from telethon.errors import RPCError
|
from telethon.errors import SessionPasswordNeededError
|
||||||
from telethon.tl.types import UpdateShortChatMessage, UpdateShortMessage
|
from telethon.tl.types import UpdateShortChatMessage, UpdateShortMessage
|
||||||
from telethon.utils import get_display_name
|
from telethon.utils import get_display_name
|
||||||
|
|
||||||
|
@ -76,14 +76,11 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
self_user = self.sign_in(user_phone, code)
|
self_user = self.sign_in(user_phone, code)
|
||||||
|
|
||||||
# Two-step verification may be enabled
|
# Two-step verification may be enabled
|
||||||
except RPCError as e:
|
except SessionPasswordNeededError as e:
|
||||||
if e.password_required:
|
|
||||||
pw = getpass('Two step verification is enabled. '
|
pw = getpass('Two step verification is enabled. '
|
||||||
'Please enter your password: ')
|
'Please enter your password: ')
|
||||||
|
|
||||||
self_user = self.sign_in(password=pw)
|
self_user = self.sign_in(password=pw)
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# Listen for updates
|
# Listen for updates
|
||||||
|
|
Loading…
Reference in New Issue
Block a user