mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-11 03:09:31 +00:00
Merge branch 'master' into asyncio
This commit is contained in:
@@ -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
|
||||
}
|
@@ -42,7 +42,7 @@ async def _do_authentication(connection):
|
||||
req_pq_request = ReqPqRequest(
|
||||
nonce=int.from_bytes(os.urandom(16), 'big', signed=True)
|
||||
)
|
||||
await sender.send(req_pq_request.to_bytes())
|
||||
await sender.send(bytes(req_pq_request))
|
||||
with BinaryReader(await sender.receive()) as reader:
|
||||
req_pq_request.on_response(reader)
|
||||
|
||||
@@ -60,12 +60,12 @@ async def _do_authentication(connection):
|
||||
p, q = rsa.get_byte_array(min(p, q)), rsa.get_byte_array(max(p, q))
|
||||
new_nonce = int.from_bytes(os.urandom(32), 'little', signed=True)
|
||||
|
||||
pq_inner_data = PQInnerData(
|
||||
pq_inner_data = bytes(PQInnerData(
|
||||
pq=rsa.get_byte_array(pq), p=p, q=q,
|
||||
nonce=res_pq.nonce,
|
||||
server_nonce=res_pq.server_nonce,
|
||||
new_nonce=new_nonce
|
||||
).to_bytes()
|
||||
))
|
||||
|
||||
# sha_digest + data + random_bytes
|
||||
cipher_text, target_fingerprint = None, None
|
||||
@@ -90,7 +90,7 @@ async def _do_authentication(connection):
|
||||
public_key_fingerprint=target_fingerprint,
|
||||
encrypted_data=cipher_text
|
||||
)
|
||||
await sender.send(req_dh_params.to_bytes())
|
||||
await sender.send(bytes(req_dh_params))
|
||||
|
||||
# Step 2 response: DH Exchange
|
||||
with BinaryReader(await sender.receive()) as reader:
|
||||
@@ -138,12 +138,12 @@ async def _do_authentication(connection):
|
||||
gab = pow(g_a, b, dh_prime)
|
||||
|
||||
# Prepare client DH Inner Data
|
||||
client_dh_inner = ClientDHInnerData(
|
||||
client_dh_inner = bytes(ClientDHInnerData(
|
||||
nonce=res_pq.nonce,
|
||||
server_nonce=res_pq.server_nonce,
|
||||
retry_id=0, # TODO Actual retry ID
|
||||
g_b=rsa.get_byte_array(gb)
|
||||
).to_bytes()
|
||||
))
|
||||
|
||||
client_dh_inner_hashed = sha1(client_dh_inner).digest() + client_dh_inner
|
||||
|
||||
@@ -156,7 +156,7 @@ async def _do_authentication(connection):
|
||||
server_nonce=res_pq.server_nonce,
|
||||
encrypted_data=client_dh_encrypted,
|
||||
)
|
||||
await sender.send(set_client_dh.to_bytes())
|
||||
await sender.send(bytes(set_client_dh))
|
||||
|
||||
# Step 3 response: Complete DH Exchange
|
||||
with BinaryReader(await sender.receive()) as reader:
|
||||
|
@@ -39,7 +39,7 @@ class MtProtoSender:
|
||||
self._logger = logging.getLogger(__name__)
|
||||
|
||||
# Message IDs that need confirmation
|
||||
self._need_confirmation = []
|
||||
self._need_confirmation = set()
|
||||
|
||||
# Requests (as msg_id: Message) sent waiting to be received
|
||||
self._pending_receive = {}
|
||||
@@ -74,7 +74,7 @@ class MtProtoSender:
|
||||
# Pack everything in the same container if we need to send AckRequests
|
||||
if self._need_confirmation:
|
||||
messages.append(
|
||||
TLMessage(self.session, MsgsAck(self._need_confirmation))
|
||||
TLMessage(self.session, MsgsAck(list(self._need_confirmation)))
|
||||
)
|
||||
self._need_confirmation.clear()
|
||||
|
||||
@@ -125,7 +125,7 @@ class MtProtoSender:
|
||||
|
||||
plain_text = \
|
||||
struct.pack('<QQ', self.session.salt, self.session.id) \
|
||||
+ message.to_bytes()
|
||||
+ bytes(message)
|
||||
|
||||
msg_key = utils.calc_msg_key(plain_text)
|
||||
key_id = struct.pack('<Q', self.session.auth_key.key_id)
|
||||
@@ -174,7 +174,7 @@ class MtProtoSender:
|
||||
"""
|
||||
|
||||
# TODO Check salt, session_id and sequence_number
|
||||
self._need_confirmation.append(msg_id)
|
||||
self._need_confirmation.add(msg_id)
|
||||
|
||||
code = reader.read_int(signed=False)
|
||||
reader.seek(-4)
|
||||
@@ -217,7 +217,7 @@ class MtProtoSender:
|
||||
r = self._pop_request_of_type(msg_id, LogOutRequest)
|
||||
if r:
|
||||
r.result = True # Telegram won't send this value
|
||||
r.confirm_received()
|
||||
r.confirm_received.set()
|
||||
self._logger.debug('Message ack confirmed', r)
|
||||
|
||||
return True
|
||||
@@ -261,7 +261,7 @@ class MtProtoSender:
|
||||
|
||||
def _clear_all_pending(self):
|
||||
for r in self._pending_receive.values():
|
||||
r.confirm_received.set()
|
||||
r.request.confirm_received.set()
|
||||
self._pending_receive.clear()
|
||||
|
||||
async def _handle_pong(self, msg_id, sequence, reader):
|
||||
@@ -303,6 +303,7 @@ class MtProtoSender:
|
||||
self.session.salt = struct.unpack(
|
||||
'<Q', struct.pack('<q', bad_salt.new_server_salt)
|
||||
)[0]
|
||||
self.session.save()
|
||||
|
||||
request = self._pop_request(bad_salt.bad_msg_id)
|
||||
if request:
|
||||
@@ -411,6 +412,11 @@ class MtProtoSender:
|
||||
async def _handle_gzip_packed(self, msg_id, sequence, reader, state):
|
||||
self._logger.debug('Handling gzip packed data')
|
||||
with BinaryReader(GzipPacked.read(reader)) as compressed_reader:
|
||||
# We are reentering process_msg, which seemingly the same msg_id
|
||||
# to the self._need_confirmation set. Remove it from there first
|
||||
# to avoid any future conflicts (i.e. if we "ignore" messages
|
||||
# that we are already aware of, see 1a91c02 and old 63dfb1e)
|
||||
self._need_confirmation -= {msg_id}
|
||||
return await self._process_msg(msg_id, sequence, compressed_reader, state)
|
||||
|
||||
# endregion
|
||||
|
@@ -56,7 +56,7 @@ class TelegramBareClient:
|
||||
"""
|
||||
|
||||
# Current TelegramClient version
|
||||
__version__ = '0.15.2'
|
||||
__version__ = '0.15.3'
|
||||
|
||||
# TODO Make this thread-safe, all connections share the same DC
|
||||
_dc_options = None
|
||||
@@ -124,7 +124,7 @@ class TelegramBareClient:
|
||||
self._user_connected = False
|
||||
|
||||
# Save whether the user is authorized here (a.k.a. logged in)
|
||||
self._authorized = False
|
||||
self._authorized = None # None = We don't know yet
|
||||
|
||||
# Uploaded files cache so subsequent calls are instant
|
||||
self._upload_cache = {}
|
||||
@@ -198,12 +198,14 @@ class TelegramBareClient:
|
||||
# another data center and this would raise UserMigrateError)
|
||||
# to also assert whether the user is logged in or not.
|
||||
self._user_connected = True
|
||||
if _sync_updates and not _cdn:
|
||||
if self._authorized is None and _sync_updates and not _cdn:
|
||||
try:
|
||||
await self.sync_updates()
|
||||
self._set_connected_and_authorized()
|
||||
except UnauthorizedError:
|
||||
self._authorized = False
|
||||
elif self._authorized:
|
||||
self._set_connected_and_authorized()
|
||||
|
||||
return True
|
||||
|
||||
@@ -383,6 +385,8 @@ class TelegramBareClient:
|
||||
|
||||
# TODO Determine the sender to be used (main or a new connection)
|
||||
sender = self._sender # .clone(), .connect()
|
||||
# We're on the same connection so no need to pass update_state=None
|
||||
# to avoid getting messages that we haven't acknowledged yet.
|
||||
|
||||
try:
|
||||
for _ in range(retries):
|
||||
@@ -426,10 +430,7 @@ class TelegramBareClient:
|
||||
else:
|
||||
while self._user_connected and not await self._reconnect():
|
||||
sleep(0.1) # Retry forever until we can send the request
|
||||
|
||||
finally:
|
||||
if sender != self._sender:
|
||||
sender.disconnect()
|
||||
return None
|
||||
|
||||
try:
|
||||
raise next(x.rpc_error for x in requests if x.rpc_error)
|
||||
@@ -673,13 +674,14 @@ class TelegramBareClient:
|
||||
def add_update_handler(self, handler):
|
||||
"""Adds an update handler (a function which takes a TLObject,
|
||||
an update, as its parameter) and listens for updates"""
|
||||
if not self.updates.get_workers:
|
||||
warnings.warn("There are no update workers running, so adding an update handler will have no effect.")
|
||||
if self.updates.workers is None:
|
||||
warnings.warn(
|
||||
"You have not setup any workers, so you won't receive updates."
|
||||
" Pass update_workers=4 when creating the TelegramClient,"
|
||||
" or set client.self.updates.workers = 4"
|
||||
)
|
||||
|
||||
sync = not self.updates.handlers
|
||||
self.updates.handlers.append(handler)
|
||||
if sync:
|
||||
self.sync_updates()
|
||||
|
||||
def remove_update_handler(self, handler):
|
||||
self.updates.handlers.remove(handler)
|
||||
|
@@ -13,21 +13,21 @@ class GzipPacked(TLObject):
|
||||
|
||||
@staticmethod
|
||||
def gzip_if_smaller(request):
|
||||
"""Calls request.to_bytes(), and based on a certain threshold,
|
||||
"""Calls bytes(request), and based on a certain threshold,
|
||||
optionally gzips the resulting data. If the gzipped data is
|
||||
smaller than the original byte array, this is returned instead.
|
||||
|
||||
Note that this only applies to content related requests.
|
||||
"""
|
||||
data = request.to_bytes()
|
||||
data = bytes(request)
|
||||
# TODO This threshold could be configurable
|
||||
if request.content_related and len(data) > 512:
|
||||
gzipped = GzipPacked(data).to_bytes()
|
||||
gzipped = bytes(GzipPacked(data))
|
||||
return gzipped if len(gzipped) < len(data) else data
|
||||
else:
|
||||
return data
|
||||
|
||||
def to_bytes(self):
|
||||
def __bytes__(self):
|
||||
# TODO Maybe compress level could be an option
|
||||
return struct.pack('<I', GzipPacked.CONSTRUCTOR_ID) + \
|
||||
TLObject.serialize_bytes(gzip.compress(self.data))
|
||||
|
@@ -11,10 +11,10 @@ class MessageContainer(TLObject):
|
||||
self.content_related = False
|
||||
self.messages = messages
|
||||
|
||||
def to_bytes(self):
|
||||
def __bytes__(self):
|
||||
return struct.pack(
|
||||
'<Ii', MessageContainer.CONSTRUCTOR_ID, len(self.messages)
|
||||
) + b''.join(m.to_bytes() for m in self.messages)
|
||||
) + b''.join(bytes(m) for m in self.messages)
|
||||
|
||||
@staticmethod
|
||||
def iter_read(reader):
|
||||
|
@@ -12,6 +12,6 @@ class TLMessage(TLObject):
|
||||
self.seq_no = session.generate_sequence(request.content_related)
|
||||
self.request = request
|
||||
|
||||
def to_bytes(self):
|
||||
def __bytes__(self):
|
||||
body = GzipPacked.gzip_if_smaller(self.request)
|
||||
return struct.pack('<qii', self.msg_id, self.seq_no, len(body)) + body
|
||||
|
@@ -125,7 +125,7 @@ class TLObject:
|
||||
def to_dict(self, recursive=True):
|
||||
return {}
|
||||
|
||||
def to_bytes(self):
|
||||
def __bytes__(self):
|
||||
return b''
|
||||
|
||||
@staticmethod
|
||||
|
Reference in New Issue
Block a user