Telethon/tl/telegram_client.py
Lonami 6b8a347426 Several updates, fixes and additions (TcpClient, MtProto...)
README.md was updated to reflect more useful information
More errors from the official Telegrm website have been added
MtProtoSender now handles updates (and doesn't crash!)
Fixes on TcpClient to be able to receive whole large packets
Updated scheme.tl to the layer 55
Session is now saved more often (to prevent damages from crashes)
Fixes to the code generator (generated invalid code for reading "bytes")
2016-09-06 18:54:49 +02:00

141 lines
5.3 KiB
Python

# This file is based on TLSharp
# https://github.com/sochix/TLSharp/blob/master/TLSharp.Core/TelegramClient.cs
import platform
import utils
import network.authenticator
from network import MtProtoSender, TcpTransport
from errors import *
from tl import Session
from tl.types import InputPeerUser, InputPeerEmpty
from tl.functions import InvokeWithLayerRequest, InitConnectionRequest
from tl.functions.help import GetConfigRequest
from tl.functions.auth import CheckPhoneRequest, SendCodeRequest, SignInRequest
from tl.functions.messages import GetDialogsRequest, SendMessageRequest
class TelegramClient:
def __init__(self, session_user_id, layer, api_id=None, api_hash=None):
if api_id is None or api_hash is None:
raise PermissionError('Your API ID or Hash are invalid. Please read "Requirements" on README.md')
self.api_id = api_id
self.api_hash = api_hash
self.layer = layer
self.session = Session.try_load_or_create_new(session_user_id)
self.transport = TcpTransport(self.session.server_address, self.session.port)
# These will be set later
self.dc_options = None
self.sender = None
self.phone_code_hash = None
def connect(self, reconnect=False):
if not self.session.auth_key or reconnect:
self.session.auth_key, self.session.time_offset = network.authenticator.do_authentication(self.transport)
self.session.save()
self.sender = MtProtoSender(self.transport, self.session)
self.sender.add_update_handler(self.on_update)
# Always init connection by using the latest layer, not only when not reconnecting (as in original TLSharp's)
# Otherwise, the server thinks that were using the oldest layer!
# (Note that this is mainly untested, but it seems like it since some errors point in that direction)
request = InvokeWithLayerRequest(layer=self.layer,
query=InitConnectionRequest(api_id=self.api_id,
device_model=platform.node(),
system_version=platform.system(),
app_version='0.1',
lang_code='en',
query=GetConfigRequest()))
self.sender.send(request)
self.sender.receive(request)
# Result is a Config TLObject
self.dc_options = request.result.dc_options
return True
def reconnect_to_dc(self, dc_id):
if self.dc_options is None or not self.dc_options:
raise ConnectionError("Can't reconnect. Stabilise an initial connection first.")
# dc is a DcOption TLObject
dc = next(dc for dc in self.dc_options if dc.id == dc_id)
self.transport.close()
self.transport = TcpTransport(dc.ip_address, dc.port)
self.session.server_address = dc.ip_address
self.session.port = dc.port
self.session.save()
self.connect(reconnect=True)
def is_user_authorized(self):
return self.session.user is not None
def is_phone_registered(self, phone_number):
assert self.sender is not None, 'Not connected!'
request = CheckPhoneRequest(phone_number)
self.sender.send(request)
self.sender.receive(request)
# Result is an Auth.CheckedPhone
return request.result.phone_registered
def send_code_request(self, phone_number):
request = SendCodeRequest(phone_number, self.api_id, self.api_hash)
completed = False
while not completed:
try:
self.sender.send(request)
self.sender.receive(request)
completed = True
if request.result:
self.phone_code_hash = request.result.phone_code_hash
except InvalidDCError as error:
self.reconnect_to_dc(error.new_dc)
def make_auth(self, phone_number, code):
if not self.phone_code_hash:
raise ValueError('Please make sure you have called send_code_request first!')
request = SignInRequest(phone_number, self.phone_code_hash, code)
self.sender.send(request)
self.sender.receive(request)
# Result is an Auth.Authorization TLObject
self.session.user = request.result.user
self.session.save()
return self.session.user
def get_dialogs(self):
request = GetDialogsRequest(offset_date=0,
offset_id=0,
offset_peer=InputPeerEmpty(),
limit=20)
self.sender.send(request)
self.sender.receive(request)
print(request.result)
def send_message(self, user, message):
peer = InputPeerUser(user.id, user.access_hash)
request = SendMessageRequest(peer, message, utils.generate_random_long())
self.sender.send(request)
self.sender.receive(request)
def on_update(self, tlobject):
"""This method is fired when there are updates from Telegram.
Add your own implementation below, or simply override it!"""
print('We have an update: {}'.format(str(tlobject)))