mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-25 19:03:46 +03:00
Added custom errors, fixes to code generator
The code generator now handles okay the flags using True type Also, double checking for the flag is now avoided in cases where the flag was a Vector type
This commit is contained in:
parent
b027dd2c8f
commit
251c1830a5
|
@ -1,4 +1,4 @@
|
||||||
api_id=12345
|
api_id=12345
|
||||||
api_hash=0123456789abcdef0123456789abcdef
|
api_hash=0123456789abcdef0123456789abcdef
|
||||||
user_phone=34600000000
|
user_phone=+34600000000
|
||||||
session_name=anonymous
|
session_name=anonymous
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# This file is based on TLSharp
|
# This file is based on TLSharp
|
||||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/MTProto/Crypto/AuthKey.cs
|
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/MTProto/Crypto/AuthKey.cs
|
||||||
import utils
|
import utils
|
||||||
|
from errors import *
|
||||||
from utils import BinaryWriter, BinaryReader
|
from utils import BinaryWriter, BinaryReader
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ class AuthKey:
|
||||||
elif data:
|
elif data:
|
||||||
self.key = data
|
self.key = data
|
||||||
else:
|
else:
|
||||||
raise AssertionError('Either a gab integer or data bytes array must be provided')
|
raise InvalidParameterError('Either a gab integer or data bytes array must be provided')
|
||||||
|
|
||||||
with BinaryReader(utils.sha1(self.key)) as reader:
|
with BinaryReader(utils.sha1(self.key)) as reader:
|
||||||
self.aux_hash = reader.read_long(signed=False)
|
self.aux_hash = reader.read_long(signed=False)
|
||||||
|
|
82
errors.py
Normal file
82
errors.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
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, new_dc):
|
||||||
|
super().__init__(self, 'Your phone number is registered to #{} DC. '
|
||||||
|
'This should have been handled automatically; '
|
||||||
|
'if it has not, please restart the app.')
|
||||||
|
|
||||||
|
self.new_dc = new_dc
|
||||||
|
|
||||||
|
|
||||||
|
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 RPCError(Exception):
|
||||||
|
def __init__(self, message):
|
||||||
|
super().__init__(self, message)
|
||||||
|
self.message = 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
|
5
main.py
5
main.py
|
@ -16,6 +16,9 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
client.connect()
|
client.connect()
|
||||||
if not client.is_user_authorized():
|
if not client.is_user_authorized():
|
||||||
phone_code_hash = client.send_code_request(settings['user_phone'])
|
phone_code_hash = None
|
||||||
|
while phone_code_hash is None:
|
||||||
|
phone_code_hash = client.send_code_request(settings['user_phone'])
|
||||||
|
|
||||||
code = input('Enter the code you just received: ')
|
code = input('Enter the code you just received: ')
|
||||||
client.make_auth(settings['user_phone'], phone_code_hash, code)
|
client.make_auth(settings['user_phone'], phone_code_hash, code)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# This file is based on TLSharp
|
# This file is based on TLSharp
|
||||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/MtProtoSender.cs
|
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/MtProtoSender.cs
|
||||||
import re
|
import re
|
||||||
import zlib
|
import gzip
|
||||||
|
from errors import *
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
|
@ -64,27 +65,26 @@ class MtProtoSender:
|
||||||
"""Sends the given packet bytes with the additional information of the original request"""
|
"""Sends the given packet bytes with the additional information of the original request"""
|
||||||
request.msg_id = self.session.get_new_msg_id()
|
request.msg_id = self.session.get_new_msg_id()
|
||||||
|
|
||||||
# First calculate the ciphered bit
|
# First calculate plain_text to encrypt it
|
||||||
with BinaryWriter() as writer:
|
with BinaryWriter() as plain_writer:
|
||||||
writer.write_long(self.session.salt, signed=False)
|
plain_writer.write_long(self.session.salt, signed=False)
|
||||||
writer.write_long(self.session.id, signed=False)
|
plain_writer.write_long(self.session.id, signed=False)
|
||||||
writer.write_long(request.msg_id)
|
plain_writer.write_long(request.msg_id)
|
||||||
writer.write_int(self.generate_sequence(request.confirmed))
|
plain_writer.write_int(self.generate_sequence(request.confirmed))
|
||||||
writer.write_int(len(packet))
|
plain_writer.write_int(len(packet))
|
||||||
writer.write(packet)
|
plain_writer.write(packet)
|
||||||
|
|
||||||
msg_key = utils.calc_msg_key(writer.get_bytes())
|
msg_key = utils.calc_msg_key(plain_writer.get_bytes())
|
||||||
|
|
||||||
key, iv = utils.calc_key(self.session.auth_key.key, msg_key, True)
|
key, iv = utils.calc_key(self.session.auth_key.key, msg_key, True)
|
||||||
cipher_text = AES.encrypt_ige(writer.get_bytes(), key, iv)
|
cipher_text = AES.encrypt_ige(plain_writer.get_bytes(), key, iv)
|
||||||
|
|
||||||
# And then finally send the packet
|
# And then finally send the encrypted packet
|
||||||
with BinaryWriter() as writer:
|
with BinaryWriter() as cipher_writer:
|
||||||
writer.write_long(self.session.auth_key.key_id, signed=False)
|
cipher_writer.write_long(self.session.auth_key.key_id, signed=False)
|
||||||
writer.write(msg_key)
|
cipher_writer.write(msg_key)
|
||||||
writer.write(cipher_text)
|
cipher_writer.write(cipher_text)
|
||||||
|
self.transport.send(cipher_writer.get_bytes())
|
||||||
self.transport.send(writer.get_bytes())
|
|
||||||
|
|
||||||
def decode_msg(self, body):
|
def decode_msg(self, body):
|
||||||
"""Decodes an received encrypted message body bytes"""
|
"""Decodes an received encrypted message body bytes"""
|
||||||
|
@ -125,7 +125,7 @@ class MtProtoSender:
|
||||||
return self.handle_container(msg_id, sequence, reader, request)
|
return self.handle_container(msg_id, sequence, reader, request)
|
||||||
if code == 0x7abe77ec: # Ping
|
if code == 0x7abe77ec: # Ping
|
||||||
return self.handle_ping(msg_id, sequence, reader)
|
return self.handle_ping(msg_id, sequence, reader)
|
||||||
if code == 0x347773c5: # pong
|
if code == 0x347773c5: # Pong
|
||||||
return self.handle_pong(msg_id, sequence, reader)
|
return self.handle_pong(msg_id, sequence, reader)
|
||||||
if code == 0xae500895: # future_salts
|
if code == 0xae500895: # future_salts
|
||||||
return self.handle_future_salts(msg_id, sequence, reader)
|
return self.handle_future_salts(msg_id, sequence, reader)
|
||||||
|
@ -174,8 +174,13 @@ class MtProtoSender:
|
||||||
if not self.process_msg(inner_msg_id, sequence, reader, request):
|
if not self.process_msg(inner_msg_id, sequence, reader, request):
|
||||||
reader.set_position(begin_position + inner_length)
|
reader.set_position(begin_position + inner_length)
|
||||||
|
|
||||||
except:
|
except Exception as error:
|
||||||
reader.set_position(begin_position + inner_length)
|
if error is InvalidDCError:
|
||||||
|
print('Re-raise {}'.format(error))
|
||||||
|
raise error
|
||||||
|
else:
|
||||||
|
print('Error while handling container {}: {}'.format(type(error), error))
|
||||||
|
reader.set_position(begin_position + inner_length)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -216,42 +221,9 @@ class MtProtoSender:
|
||||||
code = reader.read_int(signed=False)
|
code = reader.read_int(signed=False)
|
||||||
request_id = reader.read_long(signed=False)
|
request_id = reader.read_long(signed=False)
|
||||||
request_sequence = reader.read_int()
|
request_sequence = reader.read_int()
|
||||||
|
|
||||||
error_code = reader.read_int()
|
error_code = reader.read_int()
|
||||||
|
raise BadMessageError(error_code)
|
||||||
if error_code == 16:
|
|
||||||
raise RuntimeError("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)")
|
|
||||||
if error_code == 17:
|
|
||||||
raise RuntimeError("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)")
|
|
||||||
if error_code == 18:
|
|
||||||
raise RuntimeError("Incorrect two lower order msg_id bits (the server expects client message msg_id "
|
|
||||||
"to be divisible by 4)")
|
|
||||||
if error_code == 19:
|
|
||||||
raise RuntimeError("Container msg_id is the same as msg_id of a previously received message "
|
|
||||||
"(this must never happen)")
|
|
||||||
if error_code == 20:
|
|
||||||
raise RuntimeError("Message too old, and it cannot be verified whether the server has received a "
|
|
||||||
"message with this msg_id or not")
|
|
||||||
if error_code == 32:
|
|
||||||
raise RuntimeError("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)")
|
|
||||||
if error_code == 33:
|
|
||||||
raise RuntimeError("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)")
|
|
||||||
if error_code == 34:
|
|
||||||
raise RuntimeError("An even msg_seqno expected (irrelevant message), but odd received")
|
|
||||||
if error_code == 35:
|
|
||||||
raise RuntimeError("Odd msg_seqno expected (relevant message), but even received")
|
|
||||||
if error_code == 48:
|
|
||||||
raise RuntimeError("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)")
|
|
||||||
if error_code == 64:
|
|
||||||
raise RuntimeError("Invalid container")
|
|
||||||
|
|
||||||
raise NotImplementedError('This should never happen!')
|
|
||||||
|
|
||||||
def hangle_msg_detailed_info(self, msg_id, sequence, reader):
|
def hangle_msg_detailed_info(self, msg_id, sequence, reader):
|
||||||
return False
|
return False
|
||||||
|
@ -262,8 +234,6 @@ class MtProtoSender:
|
||||||
|
|
||||||
if request_id == mtproto_request.msg_id:
|
if request_id == mtproto_request.msg_id:
|
||||||
mtproto_request.confirm_received = True
|
mtproto_request.confirm_received = True
|
||||||
else:
|
|
||||||
print('We did not get the ID we expected. Got {}, but it should have been {}'.format(request_id, mtproto_request.msg_id))
|
|
||||||
|
|
||||||
inner_code = reader.read_int(signed=False)
|
inner_code = reader.read_int(signed=False)
|
||||||
if inner_code == 0x2144ca19: # RPC Error
|
if inner_code == 0x2144ca19: # RPC Error
|
||||||
|
@ -277,21 +247,14 @@ class MtProtoSender:
|
||||||
|
|
||||||
elif error_msg.startswith('PHONE_MIGRATE_'):
|
elif error_msg.startswith('PHONE_MIGRATE_'):
|
||||||
dc_index = int(re.search(r'\d+', error_msg).group(0))
|
dc_index = int(re.search(r'\d+', error_msg).group(0))
|
||||||
raise ConnectionError('Your phone number is registered to {} DC. Please update settings. '
|
raise InvalidDCError(dc_index)
|
||||||
'See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x '
|
|
||||||
'for details.'.format(dc_index))
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(error_msg)
|
raise RPCError(error_msg)
|
||||||
|
|
||||||
elif inner_code == 0x3072cfa1: # GZip packed
|
elif inner_code == 0x3072cfa1: # GZip packed
|
||||||
try:
|
unpacked_data = gzip.decompress(reader.tgread_bytes())
|
||||||
packed_data = reader.tgread_bytes()
|
with BinaryReader(unpacked_data) as compressed_reader:
|
||||||
unpacked_data = zlib.decompress(packed_data)
|
mtproto_request.on_response(compressed_reader)
|
||||||
|
|
||||||
with BinaryReader(unpacked_data) as compressed_reader:
|
|
||||||
mtproto_request.on_response(compressed_reader)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
reader.seek(-4)
|
reader.seek(-4)
|
||||||
|
@ -300,7 +263,7 @@ class MtProtoSender:
|
||||||
def handle_gzip_packed(self, msg_id, sequence, reader, mtproto_request):
|
def handle_gzip_packed(self, msg_id, sequence, reader, mtproto_request):
|
||||||
code = reader.read_int(signed=False)
|
code = reader.read_int(signed=False)
|
||||||
packed_data = reader.tgread_bytes()
|
packed_data = reader.tgread_bytes()
|
||||||
unpacked_data = zlib.decompress(packed_data)
|
unpacked_data = gzip.decompress(packed_data)
|
||||||
|
|
||||||
with BinaryReader(unpacked_data) as compressed_reader:
|
with BinaryReader(unpacked_data) as compressed_reader:
|
||||||
self.process_msg(msg_id, sequence, compressed_reader, mtproto_request)
|
self.process_msg(msg_id, sequence, compressed_reader, mtproto_request)
|
||||||
|
|
|
@ -19,7 +19,7 @@ class TcpClient:
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
"""Writes (sends) the specified bytes to the connected peer"""
|
"""Writes (sends) the specified bytes to the connected peer"""
|
||||||
self.socket.send(data)
|
self.socket.sendall(data)
|
||||||
|
|
||||||
def read(self, buffer_size):
|
def read(self, buffer_size):
|
||||||
"""Reads (receives) the specified bytes from the connected peer"""
|
"""Reads (receives) the specified bytes from the connected peer"""
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/TcpMessage.cs
|
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/TcpMessage.cs
|
||||||
from utils import BinaryWriter, BinaryReader
|
from utils import BinaryWriter, BinaryReader
|
||||||
from binascii import crc32
|
from binascii import crc32
|
||||||
|
from errors import *
|
||||||
|
|
||||||
|
|
||||||
class TcpMessage:
|
class TcpMessage:
|
||||||
|
@ -11,7 +12,7 @@ class TcpMessage:
|
||||||
:param body: Message body byte array
|
:param body: Message body byte array
|
||||||
"""
|
"""
|
||||||
if body is None:
|
if body is None:
|
||||||
raise ValueError('body cannot be None')
|
raise InvalidParameterError('body cannot be None')
|
||||||
|
|
||||||
self.sequence_number = seq_number
|
self.sequence_number = seq_number
|
||||||
self.body = body
|
self.body = body
|
||||||
|
@ -40,15 +41,15 @@ class TcpMessage:
|
||||||
def decode(body):
|
def decode(body):
|
||||||
"""Returns a TcpMessage from the given encoded bytes, decoding them previously"""
|
"""Returns a TcpMessage from the given encoded bytes, decoding them previously"""
|
||||||
if body is None:
|
if body is None:
|
||||||
raise ValueError('body cannot be None')
|
raise InvalidParameterError('body cannot be None')
|
||||||
|
|
||||||
if len(body) < 12:
|
if len(body) < 12:
|
||||||
raise ValueError('Wrong size of input packet')
|
raise InvalidParameterError('Wrong size of input packet')
|
||||||
|
|
||||||
with BinaryReader(body) as reader:
|
with BinaryReader(body) as reader:
|
||||||
packet_len = int.from_bytes(reader.read(4), byteorder='little')
|
packet_len = int.from_bytes(reader.read(4), byteorder='little')
|
||||||
if packet_len < 12:
|
if packet_len < 12:
|
||||||
raise ValueError('Invalid packet length: {}'.format(packet_len))
|
raise InvalidParameterError('Invalid packet length in body: {}'.format(packet_len))
|
||||||
|
|
||||||
seq = reader.read_int()
|
seq = reader.read_int()
|
||||||
packet = reader.read(packet_len - 12)
|
packet = reader.read(packet_len - 12)
|
||||||
|
@ -56,6 +57,6 @@ class TcpMessage:
|
||||||
|
|
||||||
valid_checksum = crc32(body[:packet_len - 4])
|
valid_checksum = crc32(body[:packet_len - 4])
|
||||||
if checksum != valid_checksum:
|
if checksum != valid_checksum:
|
||||||
raise ValueError('Invalid checksum, skip')
|
raise InvalidChecksumError(checksum, valid_checksum)
|
||||||
|
|
||||||
return TcpMessage(seq, packet)
|
return TcpMessage(seq, packet)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/TcpTransport.cs
|
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/Network/TcpTransport.cs
|
||||||
from network import TcpMessage, TcpClient
|
from network import TcpMessage, TcpClient
|
||||||
from binascii import crc32
|
from binascii import crc32
|
||||||
|
from errors import *
|
||||||
|
|
||||||
|
|
||||||
class TcpTransport:
|
class TcpTransport:
|
||||||
|
@ -42,7 +43,7 @@ class TcpTransport:
|
||||||
valid_checksum = crc32(rv)
|
valid_checksum = crc32(rv)
|
||||||
|
|
||||||
if checksum != valid_checksum:
|
if checksum != valid_checksum:
|
||||||
raise ValueError('Invalid checksum, skip')
|
raise InvalidChecksumError(checksum, valid_checksum)
|
||||||
|
|
||||||
# If we passed the tests, we can then return a valid TcpMessage
|
# If we passed the tests, we can then return a valid TcpMessage
|
||||||
return TcpMessage(seq, body)
|
return TcpMessage(seq, body)
|
||||||
|
|
11
scheme.tl
11
scheme.tl
|
@ -1,9 +1,5 @@
|
||||||
// Core types (no need to gen)
|
// Core types (no need to gen)
|
||||||
|
|
||||||
// We handle some types in a special way
|
|
||||||
//boolFalse#bc799737 = Bool;
|
|
||||||
//boolTrue#997275b5 = Bool;
|
|
||||||
//true#3fedd339 = True;
|
|
||||||
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
|
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
@ -123,6 +119,13 @@ contest.saveDeveloperInfo#9a5f6e95 vk_id:int name:string phone_number:string age
|
||||||
|
|
||||||
---types---
|
---types---
|
||||||
|
|
||||||
|
//boolFalse#bc799737 = Bool;
|
||||||
|
//boolTrue#997275b5 = Bool;
|
||||||
|
|
||||||
|
//true#3fedd339 = True;
|
||||||
|
|
||||||
|
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
|
||||||
|
|
||||||
error#c4b9f9bb code:int text:string = Error;
|
error#c4b9f9bb code:int text:string = Error;
|
||||||
|
|
||||||
null#56730bcc = Null;
|
null#56730bcc = Null;
|
||||||
|
|
|
@ -6,13 +6,13 @@ import platform
|
||||||
import utils
|
import utils
|
||||||
import network.authenticator
|
import network.authenticator
|
||||||
from network import MtProtoSender, TcpTransport
|
from network import MtProtoSender, TcpTransport
|
||||||
|
from errors import *
|
||||||
|
|
||||||
from tl import Session
|
from tl import Session
|
||||||
from tl.types import InputPeerUser
|
from tl.types import InputPeerUser
|
||||||
from tl.functions import InvokeWithLayerRequest, InitConnectionRequest
|
from tl.functions import InvokeWithLayerRequest, InitConnectionRequest
|
||||||
from tl.functions.help import GetConfigRequest
|
from tl.functions.help import GetConfigRequest
|
||||||
from tl.functions.auth import CheckPhoneRequest, SendCodeRequest, SignInRequest
|
from tl.functions.auth import CheckPhoneRequest, SendCodeRequest, SignInRequest
|
||||||
from tl.functions.contacts import GetContactsRequest
|
|
||||||
from tl.functions.messages import SendMessageRequest
|
from tl.functions.messages import SendMessageRequest
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ class TelegramClient:
|
||||||
return request.result.phone_registered
|
return request.result.phone_registered
|
||||||
|
|
||||||
def send_code_request(self, phone_number):
|
def send_code_request(self, phone_number):
|
||||||
|
"""May return None if an error occured!"""
|
||||||
request = SendCodeRequest(phone_number, self.api_id, self.api_hash)
|
request = SendCodeRequest(phone_number, self.api_id, self.api_hash)
|
||||||
completed = False
|
completed = False
|
||||||
while not completed:
|
while not completed:
|
||||||
|
@ -91,14 +92,14 @@ class TelegramClient:
|
||||||
self.sender.send(request)
|
self.sender.send(request)
|
||||||
self.sender.receive(request)
|
self.sender.receive(request)
|
||||||
completed = True
|
completed = True
|
||||||
except ConnectionError as error:
|
except InvalidDCError as error:
|
||||||
if str(error).startswith('Your phone number is registered to'):
|
self.reconnect_to_dc(error.new_dc)
|
||||||
dc = int(re.search(r'\d+', str(error)).group(0))
|
|
||||||
self.reconnect_to_dc(dc)
|
if request.result is None:
|
||||||
else:
|
return None
|
||||||
raise error
|
else:
|
||||||
|
return request.result.phone_code_hash
|
||||||
|
|
||||||
return request.result.phone_code_hash
|
|
||||||
|
|
||||||
def make_auth(self, phone_number, phone_code_hash, code):
|
def make_auth(self, phone_number, phone_code_hash, code):
|
||||||
request = SignInRequest(phone_number, phone_code_hash, code)
|
request = SignInRequest(phone_number, phone_code_hash, code)
|
||||||
|
@ -111,12 +112,6 @@ class TelegramClient:
|
||||||
|
|
||||||
return self.session.user
|
return self.session.user
|
||||||
|
|
||||||
def import_contacts(self, phone_code_hash):
|
|
||||||
request = GetContactsRequest(phone_code_hash)
|
|
||||||
self.sender.send(request)
|
|
||||||
self.sender.receive(request)
|
|
||||||
return request.result.contacts, request.result.users
|
|
||||||
|
|
||||||
def send_message(self, user, message):
|
def send_message(self, user, message):
|
||||||
peer = InputPeerUser(user.id, user.access_hash)
|
peer = InputPeerUser(user.id, user.access_hash)
|
||||||
request = SendMessageRequest(peer, message, utils.generate_random_long())
|
request = SendMessageRequest(peer, message, utils.generate_random_long())
|
||||||
|
|
|
@ -101,9 +101,10 @@ def generate_tlobjects(scheme_file):
|
||||||
builder.writeln('"""')
|
builder.writeln('"""')
|
||||||
|
|
||||||
builder.writeln('super().__init__()')
|
builder.writeln('super().__init__()')
|
||||||
# Functions have a result object
|
# Functions have a result object and are confirmed by default
|
||||||
if tlobject.is_function:
|
if tlobject.is_function:
|
||||||
builder.writeln('self.result = None')
|
builder.writeln('self.result = None')
|
||||||
|
builder.writeln('self.confirmed = True # Confirmed by default')
|
||||||
|
|
||||||
# Set the arguments
|
# Set the arguments
|
||||||
if args:
|
if args:
|
||||||
|
@ -215,9 +216,13 @@ def write_onsend_code(builder, arg, args, name=None):
|
||||||
if name is None:
|
if name is None:
|
||||||
name = 'self.{}'.format(arg.name)
|
name = 'self.{}'.format(arg.name)
|
||||||
|
|
||||||
# The argument may be a flag, only write if it's not None!
|
# The argument may be a flag, only write if it's not None AND if it's not a True type
|
||||||
|
# True types are not actually sent, but instead only used to determine the flags
|
||||||
if arg.is_flag:
|
if arg.is_flag:
|
||||||
builder.writeln('if {} is not None:'.format(name))
|
if arg.type == 'true':
|
||||||
|
return # Exit, since True type is never written
|
||||||
|
else:
|
||||||
|
builder.writeln('if {} is not None:'.format(name))
|
||||||
|
|
||||||
if arg.is_vector:
|
if arg.is_vector:
|
||||||
builder.writeln("writer.write_int(0x1cb5c415, signed=False) # Vector's constructor ID")
|
builder.writeln("writer.write_int(0x1cb5c415, signed=False) # Vector's constructor ID")
|
||||||
|
@ -262,7 +267,7 @@ def write_onsend_code(builder, arg, args, name=None):
|
||||||
builder.writeln('writer.tgwrite_bool({})'.format(name))
|
builder.writeln('writer.tgwrite_bool({})'.format(name))
|
||||||
|
|
||||||
elif 'true' == arg.type: # Awkwardly enough, Telegram has both bool and "true", used in flags
|
elif 'true' == arg.type: # Awkwardly enough, Telegram has both bool and "true", used in flags
|
||||||
builder.writeln('writer.write_int(0x3fedd339) # true')
|
pass # These are actually NOT written! Only used for flags
|
||||||
|
|
||||||
elif 'bytes' == arg.type:
|
elif 'bytes' == arg.type:
|
||||||
builder.writeln('writer.write({})'.format(name))
|
builder.writeln('writer.write({})'.format(name))
|
||||||
|
@ -297,8 +302,12 @@ def write_onresponse_code(builder, arg, args, name=None):
|
||||||
name = 'self.{}'.format(arg.name)
|
name = 'self.{}'.format(arg.name)
|
||||||
|
|
||||||
# The argument may be a flag, only write that flag was given!
|
# The argument may be a flag, only write that flag was given!
|
||||||
|
was_flag = False
|
||||||
if arg.is_flag:
|
if arg.is_flag:
|
||||||
|
was_flag = True
|
||||||
builder.writeln('if (flags & (1 << {})) != 0:'.format(arg.flag_index))
|
builder.writeln('if (flags & (1 << {})) != 0:'.format(arg.flag_index))
|
||||||
|
# Temporary disable .is_flag not to enter this if again when calling the method recursively
|
||||||
|
arg.is_flag = False
|
||||||
|
|
||||||
if arg.is_vector:
|
if arg.is_vector:
|
||||||
builder.writeln("reader.read_int() # Vector's constructor ID")
|
builder.writeln("reader.read_int() # Vector's constructor ID")
|
||||||
|
@ -338,21 +347,23 @@ def write_onresponse_code(builder, arg, args, name=None):
|
||||||
builder.writeln('{} = reader.tgread_bool()'.format(name))
|
builder.writeln('{} = reader.tgread_bool()'.format(name))
|
||||||
|
|
||||||
elif 'true' == arg.type: # Awkwardly enough, Telegram has both bool and "true", used in flags
|
elif 'true' == arg.type: # Awkwardly enough, Telegram has both bool and "true", used in flags
|
||||||
builder.writeln('{} = reader.read_int() == 0x3fedd339 # true'.format(name))
|
builder.writeln('{} = True # Arbitrary not-None value, no need to read since it is a flag'.format(name))
|
||||||
|
|
||||||
elif 'bytes' == arg.type:
|
elif 'bytes' == arg.type:
|
||||||
builder.writeln('{} = reader.read()'.format(name))
|
builder.writeln('{} = reader.read()'.format(name))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Else it may be a custom type
|
# Else it may be a custom type
|
||||||
builder.writeln('{} = reader.tgread_object(reader)'.format(name))
|
builder.writeln('{} = reader.tgread_object()'.format(name))
|
||||||
|
|
||||||
# End vector and flag blocks if required (if we opened them before)
|
# End vector and flag blocks if required (if we opened them before)
|
||||||
if arg.is_vector:
|
if arg.is_vector:
|
||||||
builder.end_block()
|
builder.end_block()
|
||||||
|
|
||||||
if arg.is_flag:
|
if was_flag:
|
||||||
builder.end_block()
|
builder.end_block()
|
||||||
|
# Restore .is_flag
|
||||||
|
arg.is_flag = True
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if tlobjects_exist():
|
if tlobjects_exist():
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO, BufferedReader
|
from io import BytesIO, BufferedReader
|
||||||
from tl import tlobjects
|
from tl import tlobjects
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
from errors import *
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ class BinaryReader:
|
||||||
elif stream:
|
elif stream:
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
else:
|
else:
|
||||||
raise ValueError("Either bytes or a stream must be provided")
|
raise InvalidParameterError("Either bytes or a stream must be provided")
|
||||||
|
|
||||||
self.reader = BufferedReader(self.stream)
|
self.reader = BufferedReader(self.stream)
|
||||||
|
|
||||||
|
@ -96,11 +97,10 @@ class BinaryReader:
|
||||||
|
|
||||||
def tgread_object(self):
|
def tgread_object(self):
|
||||||
"""Reads a Telegram object"""
|
"""Reads a Telegram object"""
|
||||||
constructor_id = self.read_int()
|
constructor_id = self.read_int(signed=False)
|
||||||
clazz = tlobjects.get(constructor_id, None)
|
clazz = tlobjects.get(constructor_id, None)
|
||||||
if clazz is None:
|
if clazz is None:
|
||||||
raise ImportError('Could not find a matching ID for the TLObject that was supposed to be read. '
|
raise TypeNotFoundError(constructor_id)
|
||||||
'Found ID: {}'.format(hex(constructor_id)))
|
|
||||||
|
|
||||||
# Now we need to determine the number of parameters of the class, so we can
|
# Now we need to determine the number of parameters of the class, so we can
|
||||||
# instantiate it with all of them set to None, and still, no need to write
|
# instantiate it with all of them set to None, and still, no need to write
|
||||||
|
|
Loading…
Reference in New Issue
Block a user