2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-29 05:18:10 +00:00

Merge develop -> asyncio

This commit is contained in:
Dan 2019-09-08 13:26:10 +02:00
commit 8f0b8babc2
28 changed files with 214 additions and 528 deletions

View File

@ -264,6 +264,7 @@ def pyrogram_api():
send_recovery_code
recover_password
accept_terms_of_service
log_out
""",
advanced="""
Advanced

View File

@ -38,12 +38,8 @@ from pyrogram.client.handlers.handler import Handler
from pyrogram.client.methods.password.utils import compute_check
from pyrogram.crypto import AES
from pyrogram.errors import (
PhoneMigrate, NetworkMigrate, PhoneNumberInvalid,
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned,
VolumeLocNotFound, UserMigrate, ChannelPrivate, PhoneNumberOccupied,
PasswordRecoveryNa, PasswordEmpty, AuthBytesInvalid,
PhoneMigrate, NetworkMigrate, SessionPasswordNeeded,
FloodWait, PeerIdInvalid, VolumeLocNotFound, UserMigrate, ChannelPrivate, AuthBytesInvalid,
BadRequest)
from pyrogram.session import Auth, Session
from .ext import utils, Syncer, BaseClient, Dispatcher
@ -52,8 +48,6 @@ from .methods import Methods
from .storage import Storage, FileStorage, MemoryStorage
from .types import User, SentCode, TermsOfService
log = logging.getLogger(__name__)
class Client(Methods, BaseClient):
"""Pyrogram Client, the main means for interacting with Telegram.
@ -68,24 +62,24 @@ class Client(Methods, BaseClient):
:meth:`~pyrogram.Client.export_session_string` before stopping the client to get a session string you can
pass here as argument.
api_id (``int``, *optional*):
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
api_id (``int`` | ``str``, *optional*):
The *api_id* part of your Telegram API Key, as integer. E.g.: "12345".
This is an alternative way to pass it if you don't want to use the *config.ini* file.
api_hash (``str``, *optional*):
The *api_hash* part of your Telegram API Key, as string. E.g.: "0123456789abcdef0123456789abcdef".
This is an alternative way to pass it if you don't want to use the *config.ini* file.
This is an alternative way to set it if you don't want to use the *config.ini* file.
app_version (``str``, *optional*):
Application version. Defaults to "Pyrogram X.Y.Z"
Application version. Defaults to "Pyrogram |version|".
This is an alternative way to set it if you don't want to use the *config.ini* file.
device_model (``str``, *optional*):
Device model. Defaults to *platform.python_implementation() + " " + platform.python_version()*
Device model. Defaults to *platform.python_implementation() + " " + platform.python_version()*.
This is an alternative way to set it if you don't want to use the *config.ini* file.
system_version (``str``, *optional*):
Operating System version. Defaults to *platform.system() + " " + platform.release()*
Operating System version. Defaults to *platform.system() + " " + platform.release()*.
This is an alternative way to set it if you don't want to use the *config.ini* file.
lang_code (``str``, *optional*):
@ -99,69 +93,52 @@ class Client(Methods, BaseClient):
proxy (``dict``, *optional*):
Your SOCKS5 Proxy settings as dict,
e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*.
*username* and *password* can be omitted if your proxy doesn't require authorization.
The *username* and *password* can be omitted if your proxy doesn't require authorization.
This is an alternative way to setup a proxy if you don't want to use the *config.ini* file.
test_mode (``bool``, *optional*):
Enable or disable login to the test servers. Defaults to False.
Only applicable for new sessions and will be ignored in case previously
created sessions are loaded.
Enable or disable login to the test servers.
Only applicable for new sessions and will be ignored in case previously created sessions are loaded.
Defaults to False.
bot_token (``str``, *optional*):
Pass your Bot API token to create a bot session, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
Only applicable for new sessions.
This is an alternative way to set it if you don't want to use the *config.ini* file.
phone_number (``str`` | ``callable``, *optional*):
phone_number (``str``, *optional*):
Pass your phone number as string (with your Country Code prefix included) to avoid entering it manually.
Or pass a callback function which accepts no arguments and must return the correct phone number as string
(e.g., "391234567890").
Only applicable for new sessions.
phone_code (``str`` | ``callable``, *optional*):
Pass the phone code as string (for test numbers only) to avoid entering it manually. Or pass a callback
function which accepts a single positional argument *(phone_number)* and must return the correct phone code
as string (e.g., "12345").
phone_code (``str``, *optional*):
Pass the phone code as string (for test numbers only) to avoid entering it manually.
Only applicable for new sessions.
password (``str``, *optional*):
Pass your Two-Step Verification password as string (if you have one) to avoid entering it manually.
Or pass a callback function which accepts a single positional argument *(password_hint)* and must return
the correct password as string (e.g., "password").
Only applicable for new sessions.
recovery_code (``callable``, *optional*):
Pass a callback function which accepts a single positional argument *(email_pattern)* and must return the
correct password recovery code as string (e.g., "987654").
Only applicable for new sessions.
force_sms (``str``, *optional*):
force_sms (``bool``, *optional*):
Pass True to force Telegram sending the authorization code via SMS.
Only applicable for new sessions.
first_name (``str``, *optional*):
Pass a First Name as string to avoid entering it manually. Or pass a callback function which accepts no
arguments and must return the correct name as string (e.g., "Dan"). It will be used to automatically create
a new Telegram account in case the phone number you passed is not registered yet.
Only applicable for new sessions.
last_name (``str``, *optional*):
Same purpose as *first_name*; pass a Last Name to avoid entering it manually. It can
be an empty string: "". Only applicable for new sessions.
Defaults to False.
workers (``int``, *optional*):
Number of maximum concurrent workers for handling incoming updates. Defaults to 4.
Number of maximum concurrent workers for handling incoming updates.
Defaults to 4.
workdir (``str``, *optional*):
Define a custom working directory. The working directory is the location in your filesystem
where Pyrogram will store your session files. Defaults to the parent directory of the main script.
Define a custom working directory. The working directory is the location in your filesystem where Pyrogram
will store your session files.
Defaults to the parent directory of the main script.
config_file (``str``, *optional*):
Path of the configuration file. Defaults to ./config.ini
Path of the configuration file.
Defaults to ./config.ini
plugins (``dict``, *optional*):
Your Smart Plugins settings as dict, e.g.: *dict(root="plugins")*.
This is an alternative way to setup plugins if you don't want to use the *config.ini* file.
This is an alternative way setup plugins if you don't want to use the *config.ini* file.
no_updates (``bool``, *optional*):
Pass True to completely disable incoming updates for the current session.
@ -175,17 +152,6 @@ class Client(Methods, BaseClient):
download_media, ...) are less prone to throw FloodWait exceptions.
Only available for users, bots will ignore this parameter.
Defaults to False (normal session).
Example:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
with app:
app.send_message("me", "Hi!")
"""
def __init__(
@ -202,12 +168,9 @@ class Client(Methods, BaseClient):
test_mode: bool = False,
bot_token: str = None,
phone_number: str = None,
phone_code: Union[str, callable] = None,
phone_code: str = None,
password: str = None,
recovery_code: callable = None,
force_sms: bool = False,
first_name: str = None,
last_name: str = None,
workers: int = BaseClient.WORKERS,
workdir: str = BaseClient.WORKDIR,
config_file: str = BaseClient.CONFIG_FILE,
@ -232,10 +195,7 @@ class Client(Methods, BaseClient):
self.phone_number = phone_number
self.phone_code = phone_code
self.password = password
self.recovery_code = recovery_code
self.force_sms = force_sms
self.first_name = first_name
self.last_name = last_name
self.workers = workers
self.workdir = Path(workdir)
self.config_file = Path(config_file)
@ -260,7 +220,10 @@ class Client(Methods, BaseClient):
return self.start()
def __exit__(self, *args):
try:
self.stop()
except ConnectionError:
pass
async def __aenter__(self):
return await self.start()
@ -349,20 +312,18 @@ class Client(Methods, BaseClient):
asyncio.ensure_future(self.updates_worker())
)
log.info("Started {} UpdatesWorkerTasks".format(Client.UPDATES_WORKERS))
logging.info("Started {} UpdatesWorkerTasks".format(Client.UPDATES_WORKERS))
for _ in range(Client.DOWNLOAD_WORKERS):
self.download_worker_tasks.append(
asyncio.ensure_future(self.download_worker())
)
log.info("Started {} DownloadWorkerTasks".format(Client.DOWNLOAD_WORKERS))
logging.info("Started {} DownloadWorkerTasks".format(Client.DOWNLOAD_WORKERS))
await self.dispatcher.start()
await Syncer.add(self)
Syncer.add(self)
self.is_initialized = True
async def terminate(self):
@ -379,7 +340,7 @@ class Client(Methods, BaseClient):
if self.takeout_id:
await self.send(functions.account.FinishTakeoutSession())
log.warning("Takeout session {} finished".format(self.takeout_id))
logging.warning("Takeout session {} finished".format(self.takeout_id))
await Syncer.remove(self)
await self.dispatcher.stop()
@ -392,7 +353,7 @@ class Client(Methods, BaseClient):
self.download_worker_tasks.clear()
log.info("Stopped {} DownloadWorkerTasks".format(Client.DOWNLOAD_WORKERS))
logging.info("Stopped {} DownloadWorkerTasks".format(Client.DOWNLOAD_WORKERS))
for _ in range(Client.UPDATES_WORKERS):
self.updates_queue.put_nowait(None)
@ -402,7 +363,7 @@ class Client(Methods, BaseClient):
self.updates_worker_tasks.clear()
log.info("Stopped {} UpdatesWorkerTasks".format(Client.UPDATES_WORKERS))
logging.info("Stopped {} UpdatesWorkerTasks".format(Client.UPDATES_WORKERS))
for media_session in self.media_sessions.values():
await media_session.stop()
@ -689,36 +650,37 @@ class Client(Methods, BaseClient):
return True
async def authorize(self) -> User:
if self.bot_token is not None:
if self.bot_token:
return await self.sign_in_bot(self.bot_token)
while True:
if self.phone_number is None:
try:
if not self.phone_number:
while True:
value = await ainput("Enter phone number or bot token: ")
confirm = await ainput("Is \"{}\" correct? (y/n): ".format(value))
if confirm in ("y", "1"):
break
elif confirm in ("n", "2"):
if not value:
continue
confirm = input("Is \"{}\" correct? (y/N): ".format(value)).lower()
if confirm == "y":
break
if ":" in value:
self.bot_token = value
return await self.sign_in_bot(value)
else:
self.phone_number = value
try:
sent_code = await self.send_code(self.phone_number)
except BadRequest as e:
print(e.MESSAGE)
self.phone_number = None
self.bot_token = None
except FloodWait as e:
print(e.MESSAGE.format(x=e.x))
time.sleep(e.x)
except Exception as e:
log.error(e, exc_info=True)
else:
break
@ -735,7 +697,7 @@ class Client(Methods, BaseClient):
))
while True:
if self.phone_code is None:
if not self.phone_code:
self.phone_code = await ainput("Enter confirmation code: ")
try:
@ -749,14 +711,14 @@ class Client(Methods, BaseClient):
while True:
print("Password hint: {}".format(await self.get_password_hint()))
if self.password is None:
if not self.password:
self.password = await ainput("Enter password (empty to recover): ")
try:
if self.password == "":
if not self.password:
confirm = await ainput("Confirm password recovery (y/n): ")
if confirm in ("y", "1"):
if confirm == "y":
email_pattern = await self.send_recovery_code()
print("The recovery code has been sent to {}".format(email_pattern))
@ -771,10 +733,9 @@ class Client(Methods, BaseClient):
print(e.MESSAGE.format(x=e.x))
time.sleep(e.x)
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
raise
elif confirm in ("n", "2"):
else:
self.password = None
else:
return await self.check_password(self.password)
@ -784,14 +745,9 @@ class Client(Methods, BaseClient):
except FloodWait as e:
print(e.MESSAGE.format(x=e.x))
time.sleep(e.x)
except Exception as e:
log.error(e, exc_info=True)
raise
except FloodWait as e:
print(e.MESSAGE.format(x=e.x))
time.sleep(e.x)
except Exception as e:
log.error(e, exc_info=True)
else:
break
@ -799,20 +755,18 @@ class Client(Methods, BaseClient):
return signed_in
while True:
self.first_name = await ainput("Enter first name: ")
self.last_name = await ainput("Enter last name (empty to skip): ")
first_name = await ainput("Enter first name: ")
last_name = await ainput("Enter last name (empty to skip): ")
try:
signed_up = await self.sign_up(
self.phone_number,
sent_code.phone_code_hash,
self.first_name,
self.last_name
first_name,
last_name
)
except BadRequest as e:
print(e.MESSAGE)
self.first_name = None
self.last_name = None
except FloodWait as e:
print(e.MESSAGE.format(x=e.x))
time.sleep(e.x)
@ -825,7 +779,28 @@ class Client(Methods, BaseClient):
return signed_up
def start(self):
async def log_out(self):
"""Log out from Telegram and delete the *\\*.session* file.
When you log out, the current client is stopped and the storage session destroyed.
No more API calls can be made until you start the client and re-authorize again.
Returns:
``bool``: On success, True is returned.
Example:
.. code-block:: python
# Log out.
app.log_out()
"""
await self.send(functions.auth.LogOut())
await self.stop()
self.storage.destroy()
return True
async def start(self):
"""Start the client.
This method connects the client to Telegram and, in case of new sessions, automatically manages the full
@ -850,25 +825,25 @@ class Client(Methods, BaseClient):
app.stop()
"""
is_authorized = self.connect()
is_authorized = await self.connect()
try:
if not is_authorized:
self.authorize()
await self.authorize()
if not self.storage.is_bot and self.takeout:
self.takeout_id = self.send(functions.account.InitTakeoutSession()).id
log.warning("Takeout session {} initiated".format(self.takeout_id))
self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id
logging.warning("Takeout session {} initiated".format(self.takeout_id))
self.send(functions.updates.GetState())
except Exception as e:
self.disconnect()
raise e
await self.send(functions.updates.GetState())
except (Exception, KeyboardInterrupt):
await self.disconnect()
raise
else:
self.initialize()
await self.initialize()
return self
def stop(self):
async def stop(self):
"""Stop the Client.
This method disconnects the client from Telegram and stops the underlying tasks.
@ -892,8 +867,8 @@ class Client(Methods, BaseClient):
app.stop()
"""
self.terminate()
self.disconnect()
await self.terminate()
await self.disconnect()
return self
@ -976,8 +951,8 @@ class Client(Methods, BaseClient):
app3.stop()
"""
def signal_handler(*args):
log.info("Stop signal received ({}). Exiting...".format(args[0]))
def signal_handler(_, __):
logging.info("Stop signal received ({}). Exiting...".format(_))
Client.is_idling = False
for s in stop_signals:
@ -1199,254 +1174,6 @@ class Client(Methods, BaseClient):
self.parse_mode = parse_mode
async def authorize_bot(self):
try:
r = await self.send(
functions.auth.ImportBotAuthorization(
flags=0,
api_id=self.api_id,
api_hash=self.api_hash,
bot_auth_token=self.bot_token
)
)
except UserMigrate as e:
await self.session.stop()
self.storage.dc_id = e.x
self.storage.auth_key = await Auth(self, self.storage.dc_id).create()
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
await self.session.start()
await self.authorize_bot()
else:
self.storage.user_id = r.user.id
print("Logged in successfully as @{}".format(r.user.username))
async def authorize_user(self):
phone_number_invalid_raises = self.phone_number is not None
phone_code_invalid_raises = self.phone_code is not None
password_invalid_raises = self.password is not None
first_name_invalid_raises = self.first_name is not None
async def default_phone_number_callback():
while True:
phone_number = await ainput("Enter phone number: ")
confirm = await ainput("Is \"{}\" correct? (y/n): ".format(phone_number))
if confirm in ("y", "1"):
return phone_number
elif confirm in ("n", "2"):
continue
while True:
self.phone_number = (
await default_phone_number_callback() if self.phone_number is None
else str(await self.phone_number()) if callable(self.phone_number)
else str(self.phone_number)
)
self.phone_number = self.phone_number.strip("+")
try:
r = await self.send(
functions.auth.SendCode(
phone_number=self.phone_number,
api_id=self.api_id,
api_hash=self.api_hash,
settings=types.CodeSettings()
)
)
except (PhoneMigrate, NetworkMigrate) as e:
await self.session.stop()
self.storage.dc_id = e.x
self.storage.auth_key = await Auth(self, self.storage.dc_id).create()
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
await self.session.start()
except (PhoneNumberInvalid, PhoneNumberBanned) as e:
if phone_number_invalid_raises:
raise
else:
print(e.MESSAGE)
self.phone_number = None
except FloodWait as e:
if phone_number_invalid_raises:
raise
else:
print(e.MESSAGE.format(x=e.x))
await asyncio.sleep(e.x)
except Exception as e:
log.error(e, exc_info=True)
raise
else:
break
phone_registered = r.phone_registered
phone_code_hash = r.phone_code_hash
terms_of_service = r.terms_of_service
if terms_of_service and not Client.terms_of_service_displayed:
print("\n" + terms_of_service.text + "\n")
Client.terms_of_service_displayed = True
if self.force_sms:
await self.send(
functions.auth.ResendCode(
phone_number=self.phone_number,
phone_code_hash=phone_code_hash
)
)
while True:
if not phone_registered:
self.first_name = (
await ainput("First name: ") if self.first_name is None
else str(await self.first_name()) if callable(self.first_name)
else str(self.first_name)
)
self.last_name = (
await ainput("Last name: ") if self.last_name is None
else str(await self.last_name()) if callable(self.last_name)
else str(self.last_name)
)
self.phone_code = (
await ainput("Enter phone code: ") if self.phone_code is None
else str(await self.phone_code(self.phone_number)) if callable(self.phone_code)
else str(self.phone_code)
)
try:
if phone_registered:
try:
r = await self.send(
functions.auth.SignIn(
phone_number=self.phone_number,
phone_code_hash=phone_code_hash,
phone_code=self.phone_code
)
)
except PhoneNumberUnoccupied:
log.warning("Phone number unregistered")
phone_registered = False
continue
else:
try:
r = await self.send(
functions.auth.SignUp(
phone_number=self.phone_number,
phone_code_hash=phone_code_hash,
phone_code=self.phone_code,
first_name=self.first_name,
last_name=self.last_name
)
)
except PhoneNumberOccupied:
log.warning("Phone number already registered")
phone_registered = True
continue
except (PhoneCodeInvalid, PhoneCodeEmpty, PhoneCodeExpired, PhoneCodeHashEmpty) as e:
if phone_code_invalid_raises:
raise
else:
print(e.MESSAGE)
self.phone_code = None
except FirstnameInvalid as e:
if first_name_invalid_raises:
raise
else:
print(e.MESSAGE)
self.first_name = None
except SessionPasswordNeeded as e:
print(e.MESSAGE)
async def default_password_callback(password_hint: str) -> str:
print("Hint: {}".format(password_hint))
return await ainput("Enter password (empty to recover): ")
async def default_recovery_callback(email_pattern: str) -> str:
print("An e-mail containing the recovery code has been sent to {}".format(email_pattern))
return await ainput("Enter password recovery code: ")
while True:
try:
r = await self.send(functions.account.GetPassword())
self.password = (
await default_password_callback(r.hint) if self.password is None
else str((await self.password(r.hint)) or "") if callable(self.password)
else str(self.password)
)
if self.password == "":
r = await self.send(functions.auth.RequestPasswordRecovery())
self.recovery_code = (
await default_recovery_callback(r.email_pattern) if self.recovery_code is None
else str(await self.recovery_code(r.email_pattern)) if callable(self.recovery_code)
else str(self.recovery_code)
)
r = await self.send(
functions.auth.RecoverPassword(
code=self.recovery_code
)
)
else:
r = await self.send(
functions.auth.CheckPassword(
password=compute_check(r, self.password)
)
)
except (PasswordEmpty, PasswordRecoveryNa, PasswordHashInvalid) as e:
if password_invalid_raises:
raise
else:
print(e.MESSAGE)
self.password = None
self.recovery_code = None
except FloodWait as e:
if password_invalid_raises:
raise
else:
print(e.MESSAGE.format(x=e.x))
await asyncio.sleep(e.x)
self.password = None
self.recovery_code = None
except Exception as e:
log.error(e, exc_info=True)
raise
else:
break
break
except FloodWait as e:
if phone_code_invalid_raises or first_name_invalid_raises:
raise
else:
print(e.MESSAGE.format(x=e.x))
await asyncio.sleep(e.x)
except Exception as e:
log.error(e, exc_info=True)
raise
else:
break
if terms_of_service:
assert await self.send(
functions.help.AcceptTermsOfService(
id=terms_of_service.id
)
)
self.password = None
self.storage.user_id = r.user.id
print("Logged in successfully as {}".format(r.user.first_name))
def fetch_peers(
self,
peers: List[
@ -1546,7 +1273,7 @@ class Client(Methods, BaseClient):
os.makedirs(directory, exist_ok=True)
shutil.move(temp_file_path, final_file_path)
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
try:
os.remove(temp_file_path)
@ -1587,7 +1314,7 @@ class Client(Methods, BaseClient):
pts_count = getattr(update, "pts_count", None)
if isinstance(update, types.UpdateChannelTooLong):
log.warning(update)
logging.warning(update)
if isinstance(update, types.UpdateNewChannelMessage) and is_min:
message = update.message
@ -1639,14 +1366,11 @@ class Client(Methods, BaseClient):
elif isinstance(updates, types.UpdateShort):
self.dispatcher.updates_queue.put_nowait((updates.update, {}, {}))
elif isinstance(updates, types.UpdatesTooLong):
log.warning(updates)
logging.info(updates)
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
async def send(self,
data: TLObject,
retries: int = Session.MAX_RETRIES,
timeout: float = Session.WAIT_TIMEOUT):
async def send(self, data: TLObject, retries: int = Session.MAX_RETRIES, timeout: float = Session.WAIT_TIMEOUT):
"""Send raw Telegram queries.
This method makes it possible to manually call every single Telegram API method in a low-level manner.
@ -1819,7 +1543,7 @@ class Client(Methods, BaseClient):
if isinstance(handler, Handler) and isinstance(group, int):
self.add_handler(handler, group)
log.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format(
logging.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format(
self.session_name, type(handler).__name__, name, group, module_path))
count += 1
@ -1833,12 +1557,12 @@ class Client(Methods, BaseClient):
try:
module = import_module(module_path)
except ImportError:
log.warning('[{}] [LOAD] Ignoring non-existent module "{}"'.format(
logging.warning('[{}] [LOAD] Ignoring non-existent module "{}"'.format(
self.session_name, module_path))
continue
if "__path__" in dir(module):
log.warning('[{}] [LOAD] Ignoring namespace "{}"'.format(
logging.warning('[{}] [LOAD] Ignoring namespace "{}"'.format(
self.session_name, module_path))
continue
@ -1854,13 +1578,13 @@ class Client(Methods, BaseClient):
if isinstance(handler, Handler) and isinstance(group, int):
self.add_handler(handler, group)
log.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format(
logging.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format(
self.session_name, type(handler).__name__, name, group, module_path))
count += 1
except Exception:
if warn_non_existent_functions:
log.warning('[{}] [LOAD] Ignoring non-existent function "{}" from "{}"'.format(
logging.warning('[{}] [LOAD] Ignoring non-existent function "{}" from "{}"'.format(
self.session_name, name, module_path))
if exclude:
@ -1871,12 +1595,12 @@ class Client(Methods, BaseClient):
try:
module = import_module(module_path)
except ImportError:
log.warning('[{}] [UNLOAD] Ignoring non-existent module "{}"'.format(
logging.warning('[{}] [UNLOAD] Ignoring non-existent module "{}"'.format(
self.session_name, module_path))
continue
if "__path__" in dir(module):
log.warning('[{}] [UNLOAD] Ignoring namespace "{}"'.format(
logging.warning('[{}] [UNLOAD] Ignoring namespace "{}"'.format(
self.session_name, module_path))
continue
@ -1892,20 +1616,20 @@ class Client(Methods, BaseClient):
if isinstance(handler, Handler) and isinstance(group, int):
self.remove_handler(handler, group)
log.info('[{}] [UNLOAD] {}("{}") from group {} in "{}"'.format(
logging.info('[{}] [UNLOAD] {}("{}") from group {} in "{}"'.format(
self.session_name, type(handler).__name__, name, group, module_path))
count -= 1
except Exception:
if warn_non_existent_functions:
log.warning('[{}] [UNLOAD] Ignoring non-existent function "{}" from "{}"'.format(
logging.warning('[{}] [UNLOAD] Ignoring non-existent function "{}" from "{}"'.format(
self.session_name, name, module_path))
if count > 0:
log.warning('[{}] Successfully loaded {} plugin{} from "{}"'.format(
logging.warning('[{}] Successfully loaded {} plugin{} from "{}"'.format(
self.session_name, count, "s" if count > 1 else "", root))
else:
log.warning('[{}] No plugin loaded from "{}"'.format(
logging.warning('[{}] No plugin loaded from "{}"'.format(
self.session_name, root))
# def get_initial_dialogs_chunk(self, offset_date: int = 0):
@ -1922,10 +1646,10 @@ class Client(Methods, BaseClient):
# )
# )
# except FloodWait as e:
# log.warning("get_dialogs flood: waiting {} seconds".format(e.x))
# logging.warning("get_dialogs flood: waiting {} seconds".format(e.x))
# time.sleep(e.x)
# else:
# log.info("Total peers: {}".format(self.storage.peers_count))
# logging.info("Total peers: {}".format(self.storage.peers_count))
# return r
#
# def get_initial_dialogs(self):
@ -1940,8 +1664,7 @@ class Client(Methods, BaseClient):
#
# self.get_initial_dialogs_chunk()
async def resolve_peer(self,
peer_id: Union[int, str]):
async def resolve_peer(self, peer_id: Union[int, str]):
"""Get the InputPeer of a known peer id.
Useful whenever an InputPeer type is required.
@ -1980,7 +1703,9 @@ class Client(Methods, BaseClient):
try:
return self.storage.get_peer_by_username(peer_id)
except KeyError:
await self.send(functions.contacts.ResolveUsername(username=peer_id
await self.send(
functions.contacts.ResolveUsername(
username=peer_id
)
)
@ -2094,7 +1819,7 @@ class Client(Methods, BaseClient):
try:
await asyncio.ensure_future(session.send(data))
except Exception as e:
log.error(e)
logging.error(e)
part_size = 512 * 1024
file_size = os.path.getsize(path)
@ -2160,7 +1885,7 @@ class Client(Methods, BaseClient):
except Client.StopTransmission:
raise
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
else:
if is_big:
return types.InputFileBig(
@ -2392,7 +2117,7 @@ class Client(Methods, BaseClient):
raise e
except Exception as e:
if not isinstance(e, Client.StopTransmission):
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
try:
os.remove(file_name)

View File

@ -50,7 +50,7 @@ class BaseClient:
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
DIALOGS_AT_ONCE = 100
UPDATES_WORKERS = 1
UPDATES_WORKERS = 4
DOWNLOAD_WORKERS = 4
OFFLINE_SLEEP = 900
WORKERS = 4

View File

@ -34,8 +34,6 @@ from ..handlers import (
UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler
)
log = logging.getLogger(__name__)
class Dispatcher:
NEW_MESSAGE_UPDATES = (
@ -111,7 +109,7 @@ class Dispatcher:
asyncio.ensure_future(self.update_worker(self.locks_list[-1]))
)
log.info("Started {} UpdateWorkerTasks".format(self.workers))
logging.info("Started {} UpdateWorkerTasks".format(self.workers))
async def stop(self):
for i in range(self.workers):
@ -123,7 +121,7 @@ class Dispatcher:
self.update_worker_tasks.clear()
self.groups.clear()
log.info("Stopped {} UpdateWorkerTasks".format(self.workers))
logging.info("Stopped {} UpdateWorkerTasks".format(self.workers))
def add_handler(self, handler, group: int):
async def fn():
@ -185,7 +183,7 @@ class Dispatcher:
if handler.check(parsed_update):
args = (parsed_update,)
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
continue
elif isinstance(handler, RawUpdateHandler):
@ -201,10 +199,10 @@ class Dispatcher:
except pyrogram.ContinuePropagation:
continue
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
break
except pyrogram.StopPropagation:
pass
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)

View File

@ -20,8 +20,6 @@ import asyncio
import logging
import time
log = logging.getLogger(__name__)
class Syncer:
INTERVAL = 20
@ -83,9 +81,9 @@ class Syncer:
start = time.time()
client.storage.save()
except Exception as e:
log.critical(e, exc_info=True)
logging.critical(e, exc_info=True)
else:
log.info('Synced "{}" in {:.6} ms'.format(
logging.info('Synced "{}" in {:.6} ms'.format(
client.storage.name,
(time.time() - start) * 1000
))

View File

@ -26,8 +26,6 @@ from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
class Filters:
ALL = "all"
@ -154,7 +152,7 @@ class GetChatMembers(BaseClient):
return pyrogram.List(pyrogram.ChatMember._parse(self, member, users) for member in members)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
logging.warning("Sleeping for {}s".format(e.x))
await asyncio.sleep(e.x)
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@ -26,8 +26,6 @@ from pyrogram.errors import FloodWait
from ...ext import BaseClient, utils
log = logging.getLogger(__name__)
class GetDialogs(BaseClient):
async def get_dialogs(
@ -83,7 +81,7 @@ class GetDialogs(BaseClient):
)
)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
logging.warning("Sleeping for {}s".format(e.x))
await asyncio.sleep(e.x)
else:
break
@ -112,6 +110,6 @@ class GetDialogs(BaseClient):
if not isinstance(dialog, types.Dialog):
continue
parsed_dialogs.append(pyrogram.Dialog._parse(self, dialog, messages, users, chats))
parsed_dialogs.append(pyrogram.Dialogging._parse(self, dialog, messages, users, chats))
return pyrogram.List(parsed_dialogs)

View File

@ -25,8 +25,6 @@ from pyrogram.api import functions
from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
class GetContacts(BaseClient):
async def get_contacts(self) -> List["pyrogram.User"]:
@ -45,7 +43,7 @@ class GetContacts(BaseClient):
try:
contacts = await self.send(functions.contacts.GetContacts(hash=0))
except FloodWait as e:
log.warning("get_contacts flood: waiting {} seconds".format(e.x))
logging.warning("get_contacts flood: waiting {} seconds".format(e.x))
await asyncio.sleep(e.x)
else:
return pyrogram.List(pyrogram.User._parse(self, user) for user in contacts.users)

View File

@ -27,8 +27,6 @@ from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
class GetHistory(BaseClient):
async def get_history(
@ -104,7 +102,7 @@ class GetHistory(BaseClient):
)
)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
logging.warning("Sleeping for {}s".format(e.x))
await asyncio.sleep(e.x)
else:
break

View File

@ -16,14 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
from typing import Union
from pyrogram.api import types, functions
from pyrogram.client.ext import BaseClient
log = logging.getLogger(__name__)
class GetHistoryCount(BaseClient):
async def get_history_count(

View File

@ -26,8 +26,6 @@ from pyrogram.errors import FloodWait
from ...ext import BaseClient, utils
log = logging.getLogger(__name__)
# TODO: Rewrite using a flag for replied messages and have message_ids non-optional
@ -117,7 +115,7 @@ class GetMessages(BaseClient):
try:
r = await self.send(rpc)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
logging.warning("Sleeping for {}s".format(e.x))
await asyncio.sleep(e.x)
else:
break

View File

@ -26,8 +26,6 @@ from pyrogram.api import functions, types
from pyrogram.client.ext import BaseClient, utils
from pyrogram.errors import FloodWait
log = logging.getLogger(__name__)
class SendMediaGroup(BaseClient):
# TODO: Add progress parameter
@ -89,7 +87,7 @@ class SendMediaGroup(BaseClient):
)
)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
logging.warning("Sleeping for {}s".format(e.x))
await asyncio.sleep(e.x)
else:
break
@ -144,7 +142,7 @@ class SendMediaGroup(BaseClient):
)
)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
logging.warning("Sleeping for {}s".format(e.x))
await asyncio.sleep(e.x)
else:
break
@ -195,7 +193,7 @@ class SendMediaGroup(BaseClient):
)
)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
logging.warning("Sleeping for {}s".format(e.x))
await asyncio.sleep(e.x)
else:
break

View File

@ -28,8 +28,6 @@ from pyrogram.api import types
from pyrogram.errors import PeerIdInvalid
from . import utils
log = logging.getLogger(__name__)
class Parser(HTMLParser):
MENTION_RE = re.compile(r"tg://user\?id=(\d+)")
@ -97,7 +95,7 @@ class Parser(HTMLParser):
line, offset = self.getpos()
offset += 1
log.warning("Unmatched closing tag </{}> at line {}:{}".format(tag, line, offset))
logging.warning("Unmatched closing tag </{}> at line {}:{}".format(tag, line, offset))
else:
if not self.tag_entities[tag]:
self.tag_entities.pop(tag)
@ -123,7 +121,7 @@ class HTML:
for tag, entities in parser.tag_entities.items():
unclosed_tags.append("<{}> (x{})".format(tag, len(entities)))
log.warning("Unclosed tags: {}".format(", ".join(unclosed_tags)))
logging.warning("Unclosed tags: {}".format(", ".join(unclosed_tags)))
entities = []

View File

@ -19,14 +19,13 @@
import base64
import json
import logging
import os
import sqlite3
from pathlib import Path
from threading import Lock
from .memory_storage import MemoryStorage
log = logging.getLogger(__name__)
class FileStorage(MemoryStorage):
FILE_EXTENSION = ".session"
@ -82,20 +81,20 @@ class FileStorage(MemoryStorage):
except ValueError:
pass
else:
log.warning("JSON session storage detected! Converting it into an SQLite session storage...")
logging.warning("JSON session storage detected! Converting it into an SQLite session storage...")
path.rename(path.name + ".OLD")
log.warning('The old session file has been renamed to "{}.OLD"'.format(path.name))
logging.warning('The old session file has been renamed to "{}.OLD"'.format(path.name))
self.migrate_from_json(session_json)
log.warning("Done! The session has been successfully converted from JSON to SQLite storage")
logging.warning("Done! The session has been successfully converted from JSON to SQLite storage")
return
if Path(path.name + ".OLD").is_file():
log.warning('Old session file detected: "{}.OLD". You can remove this file now'.format(path.name))
logging.warning('Old session file detected: "{}.OLD". You can remove this file now'.format(path.name))
self.conn = sqlite3.connect(
str(path),
@ -108,3 +107,6 @@ class FileStorage(MemoryStorage):
with self.conn:
self.conn.execute("VACUUM")
def destroy(self):
os.remove(self.database)

View File

@ -18,7 +18,6 @@
import base64
import inspect
import logging
import sqlite3
import struct
import time
@ -29,8 +28,6 @@ from typing import List, Tuple
from pyrogram.api import types
from pyrogram.client.storage.storage import Storage
log = logging.getLogger(__name__)
class MemoryStorage(Storage):
SCHEMA_VERSION = 1
@ -97,6 +94,9 @@ class MemoryStorage(Storage):
with self.lock:
self.conn.close()
def destroy(self):
pass
def update_peers(self, peers: List[Tuple[int, int, str, str, str]]):
with self.lock:
self.conn.executemany(

View File

@ -30,6 +30,9 @@ class Storage:
def close(self):
raise NotImplementedError
def destroy(self):
raise NotImplementedError
def update_peers(self, peers):
raise NotImplementedError

View File

@ -22,8 +22,9 @@ from .chat_permissions import ChatPermissions
from .chat_photo import ChatPhoto
from .chat_preview import ChatPreview
from .dialog import Dialog
from .restriction import Restriction
from .user import User
__all__ = [
"Chat", "ChatMember", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "User"
"Chat", "ChatMember", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "User", "Restriction"
]

View File

@ -20,7 +20,6 @@ from struct import pack
import pyrogram
from pyrogram.api import types
from pyrogram.errors import PeerIdInvalid
from ..object import Object
from ...ext.utils import encode
@ -60,8 +59,10 @@ class ChatPhoto(Object):
loc_big = chat_photo.photo_big
try:
peer = client.resolve_peer(peer_id)
except PeerIdInvalid:
# We just want a local storage lookup by id, whose method is not async.
# Otherwise we have to turn this _parse method async and also all the other methods that use this one.
peer = client.storage.get_peer_by_id(peer_id)
except KeyError:
return None
if isinstance(peer, types.InputPeerUser):

View File

@ -22,8 +22,6 @@ import logging
from .transport import *
from ..session.internals import DataCenter
log = logging.getLogger(__name__)
class Connection:
MAX_RETRIES = 3
@ -51,14 +49,14 @@ class Connection:
self.protocol = self.mode(self.ipv6, self.proxy)
try:
log.info("Connecting...")
logging.info("Connecting...")
await self.protocol.connect(self.address)
except OSError as e:
log.warning(e) # TODO: Remove
logging.warning(e) # TODO: Remove
self.protocol.close()
await asyncio.sleep(1)
else:
log.info("Connected! {} DC{} - IPv{} - {}".format(
logging.info("Connected! {} DC{} - IPv{} - {}".format(
"Test" if self.test_mode else "Production",
self.dc_id,
"6" if self.ipv6 else "4",
@ -66,12 +64,12 @@ class Connection:
))
break
else:
log.warning("Connection failed! Trying again...")
logging.warning("Connection failed! Trying again...")
raise TimeoutError
def close(self):
self.protocol.close()
log.info("Disconnected")
logging.info("Disconnected")
async def send(self, data: bytes):
try:

View File

@ -31,8 +31,6 @@ except ImportError as e:
raise e
log = logging.getLogger(__name__)
class TCP:
TIMEOUT = 10
@ -67,7 +65,7 @@ class TCP:
password=proxy.get("password", None)
)
log.info("Using proxy {}:{}".format(hostname, port))
logging.info("Using proxy {}:{}".format(hostname, port))
else:
self.socket = socks.socksocket(
socket.AF_INET6 if ipv6

View File

@ -16,12 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
from .tcp import TCP
log = logging.getLogger(__name__)
class TCPAbridged(TCP):
def __init__(self, ipv6: bool, proxy: dict):

View File

@ -16,14 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
import os
from .tcp import TCP
from ....crypto.aes import AES
log = logging.getLogger(__name__)
class TCPAbridgedO(TCP):
RESERVED = (b"HEAD", b"POST", b"GET ", b"OPTI", b"\xee" * 4)

View File

@ -16,14 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
from binascii import crc32
from struct import pack, unpack
from .tcp import TCP
log = logging.getLogger(__name__)
class TCPFull(TCP):
def __init__(self, ipv6: bool, proxy: dict):

View File

@ -16,13 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
from struct import pack, unpack
from .tcp import TCP
log = logging.getLogger(__name__)
class TCPIntermediate(TCP):
def __init__(self, ipv6: bool, proxy: dict):

View File

@ -16,15 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
import os
from struct import pack, unpack
from .tcp import TCP
from ....crypto.aes import AES
log = logging.getLogger(__name__)
class TCPIntermediateO(TCP):
RESERVED = (b"HEAD", b"POST", b"GET ", b"OPTI", b"\xee" * 4)

View File

@ -18,12 +18,10 @@
import logging
log = logging.getLogger(__name__)
try:
import tgcrypto
log.info("Using TgCrypto")
logging.info("Using TgCrypto")
class AES:
@ -53,7 +51,7 @@ try:
except ImportError:
import pyaes
log.warning(
logging.warning(
"TgCrypto is missing! "
"Pyrogram will work the same, but at a much slower speed. "
"More info: https://docs.pyrogram.org/topics/tgcrypto"

View File

@ -30,8 +30,6 @@ from pyrogram.connection import Connection
from pyrogram.crypto import AES, RSA, Prime
from .internals import MsgId
log = logging.getLogger(__name__)
class Auth:
MAX_RETRIES = 5
@ -78,34 +76,34 @@ class Auth:
self.connection = Connection(self.dc_id, self.test_mode, self.ipv6, self.proxy)
try:
log.info("Start creating a new auth key on DC{}".format(self.dc_id))
logging.info("Start creating a new auth key on DC{}".format(self.dc_id))
await self.connection.connect()
# Step 1; Step 2
nonce = int.from_bytes(urandom(16), "little", signed=True)
log.debug("Send req_pq: {}".format(nonce))
logging.debug("Send req_pq: {}".format(nonce))
res_pq = await self.send(functions.ReqPqMulti(nonce=nonce))
log.debug("Got ResPq: {}".format(res_pq.server_nonce))
log.debug("Server public key fingerprints: {}".format(res_pq.server_public_key_fingerprints))
logging.debug("Got ResPq: {}".format(res_pq.server_nonce))
logging.debug("Server public key fingerprints: {}".format(res_pq.server_public_key_fingerprints))
for i in res_pq.server_public_key_fingerprints:
if i in RSA.server_public_keys:
log.debug("Using fingerprint: {}".format(i))
logging.debug("Using fingerprint: {}".format(i))
public_key_fingerprint = i
break
else:
log.debug("Fingerprint unknown: {}".format(i))
logging.debug("Fingerprint unknown: {}".format(i))
else:
raise Exception("Public key not found")
# Step 3
pq = int.from_bytes(res_pq.pq, "big")
log.debug("Start PQ factorization: {}".format(pq))
logging.debug("Start PQ factorization: {}".format(pq))
start = time.time()
g = Prime.decompose(pq)
p, q = sorted((g, pq // g)) # p < q
log.debug("Done PQ factorization ({}s): {} {}".format(round(time.time() - start, 3), p, q))
logging.debug("Done PQ factorization ({}s): {} {}".format(round(time.time() - start, 3), p, q))
# Step 4
server_nonce = res_pq.server_nonce
@ -125,10 +123,10 @@ class Auth:
data_with_hash = sha + data + padding
encrypted_data = RSA.encrypt(data_with_hash, public_key_fingerprint)
log.debug("Done encrypt data with RSA")
logging.debug("Done encrypt data with RSA")
# Step 5. TODO: Handle "server_DH_params_fail". Code assumes response is ok
log.debug("Send req_DH_params")
logging.debug("Send req_DH_params")
server_dh_params = await self.send(
functions.ReqDHParams(
nonce=nonce,
@ -162,12 +160,12 @@ class Auth:
server_dh_inner_data = TLObject.read(BytesIO(answer))
log.debug("Done decrypting answer")
logging.debug("Done decrypting answer")
dh_prime = int.from_bytes(server_dh_inner_data.dh_prime, "big")
delta_time = server_dh_inner_data.server_time - time.time()
log.debug("Delta time: {}".format(round(delta_time, 3)))
logging.debug("Delta time: {}".format(round(delta_time, 3)))
# Step 6
g = server_dh_inner_data.g
@ -188,7 +186,7 @@ class Auth:
data_with_hash = sha + data + padding
encrypted_data = AES.ige256_encrypt(data_with_hash, tmp_aes_key, tmp_aes_iv)
log.debug("Send set_client_DH_params")
logging.debug("Send set_client_DH_params")
set_client_dh_params_answer = await self.send(
functions.SetClientDHParams(
nonce=nonce,
@ -211,7 +209,7 @@ class Auth:
#######################
assert dh_prime == Prime.CURRENT_DH_PRIME
log.debug("DH parameters check: OK")
logging.debug("DH parameters check: OK")
# https://core.telegram.org/mtproto/security_guidelines#g-a-and-g-b-validation
g_b = int.from_bytes(g_b, "big")
@ -220,12 +218,12 @@ class Auth:
assert 1 < g_b < dh_prime - 1
assert 2 ** (2048 - 64) < g_a < dh_prime - 2 ** (2048 - 64)
assert 2 ** (2048 - 64) < g_b < dh_prime - 2 ** (2048 - 64)
log.debug("g_a and g_b validation: OK")
logging.debug("g_a and g_b validation: OK")
# https://core.telegram.org/mtproto/security_guidelines#checking-sha1-hash-values
answer = server_dh_inner_data.write() # Call .write() to remove padding
assert answer_with_hash[:20] == sha1(answer).digest()
log.debug("SHA1 hash values check: OK")
logging.debug("SHA1 hash values check: OK")
# https://core.telegram.org/mtproto/security_guidelines#checking-nonce-server-nonce-and-new-nonce-fields
# 1st message
@ -238,14 +236,14 @@ class Auth:
assert nonce == set_client_dh_params_answer.nonce
assert server_nonce == set_client_dh_params_answer.server_nonce
server_nonce = server_nonce.to_bytes(16, "little", signed=True)
log.debug("Nonce fields check: OK")
logging.debug("Nonce fields check: OK")
# Step 9
server_salt = AES.xor(new_nonce[:8], server_nonce[:8])
log.debug("Server salt: {}".format(int.from_bytes(server_salt, "little")))
logging.debug("Server salt: {}".format(int.from_bytes(server_salt, "little")))
log.info(
logging.info(
"Done auth key exchange: {}".format(
set_client_dh_params_answer.__class__.__name__
)

View File

@ -32,8 +32,6 @@ from pyrogram.crypto import MTProto
from pyrogram.errors import RPCError, InternalServerError, AuthKeyDuplicated
from .internals import MsgId, MsgFactory
log = logging.getLogger(__name__)
class Result:
def __init__(self):
@ -158,9 +156,9 @@ class Session:
self.ping_task = asyncio.ensure_future(self.ping())
log.info("Session initialized: Layer {}".format(layer))
log.info("Device: {} - {}".format(self.client.device_model, self.client.app_version))
log.info("System: {} ({})".format(self.client.system_version, self.client.lang_code.upper()))
logging.info("Session initialized: Layer {}".format(layer))
logging.info("Device: {} - {}".format(self.client.device_model, self.client.app_version))
logging.info("System: {} ({})".format(self.client.system_version, self.client.lang_code.upper()))
except AuthKeyDuplicated as e:
await self.stop()
@ -175,7 +173,7 @@ class Session:
self.is_connected.set()
log.info("Session started")
logging.info("Session started")
async def stop(self):
self.is_connected.clear()
@ -207,16 +205,16 @@ class Session:
try:
await self.client.disconnect_handler(self.client)
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
log.info("Session stopped")
logging.info("Session stopped")
async def restart(self):
await self.stop()
await self.start()
async def net_worker(self):
log.info("NetWorkerTask started")
logging.info("NetWorkerTask started")
while True:
packet = await self.recv_queue.get()
@ -238,7 +236,7 @@ class Session:
else [data]
)
log.debug(data)
logging.debug(data)
for msg in messages:
if msg.seq_no % 2 != 0:
@ -271,7 +269,7 @@ class Session:
self.results[msg_id].event.set()
if len(self.pending_acks) >= self.ACKS_THRESHOLD:
log.info("Send {} acks".format(len(self.pending_acks)))
logging.info("Send {} acks".format(len(self.pending_acks)))
try:
await self._send(types.MsgsAck(msg_ids=list(self.pending_acks)), False)
@ -280,12 +278,12 @@ class Session:
else:
self.pending_acks.clear()
except Exception as e:
log.error(e, exc_info=True)
logging.error(e, exc_info=True)
log.info("NetWorkerTask stopped")
logging.info("NetWorkerTask stopped")
async def ping(self):
log.info("PingTask started")
logging.info("PingTask started")
while True:
try:
@ -304,10 +302,10 @@ class Session:
except (OSError, TimeoutError, RPCError):
pass
log.info("PingTask stopped")
logging.info("PingTask stopped")
async def next_salt(self):
log.info("NextSaltTask started")
logging.info("NextSaltTask started")
while True:
now = datetime.now()
@ -317,7 +315,7 @@ class Session:
valid_until = datetime.fromtimestamp(self.current_salt.valid_until)
dt = (valid_until - now).total_seconds() - 900
log.info("Next salt in {:.0f}m {:.0f}s ({})".format(
logging.info("Next salt in {:.0f}m {:.0f}s ({})".format(
dt // 60, dt % 60,
now + timedelta(seconds=dt)
))
@ -335,10 +333,10 @@ class Session:
self.connection.close()
break
log.info("NextSaltTask stopped")
logging.info("NextSaltTask stopped")
async def recv(self):
log.info("RecvTask started")
logging.info("RecvTask started")
while True:
packet = await self.connection.recv()
@ -347,7 +345,7 @@ class Session:
self.recv_queue.put_nowait(None)
if packet:
log.warning("Server sent \"{}\"".format(Int.read(BytesIO(packet))))
logging.warning("Server sent \"{}\"".format(Int.read(BytesIO(packet))))
if self.is_connected.is_set():
asyncio.ensure_future(self.restart())
@ -356,7 +354,7 @@ class Session:
self.recv_queue.put_nowait(packet)
log.info("RecvTask stopped")
logging.info("RecvTask stopped")
async def _send(self, data: TLObject, wait_response: bool = True, timeout: float = WAIT_TIMEOUT):
message = self.msg_factory(data)