mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-09-21 11:18:50 +03:00
6b8a347426
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")
141 lines
5.3 KiB
Python
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)))
|