diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py
index 34db202f..93e71da6 100644
--- a/compiler/docs/compiler.py
+++ b/compiler/docs/compiler.py
@@ -264,6 +264,7 @@ def pyrogram_api():
send_recovery_code
recover_password
accept_terms_of_service
+ log_out
""",
advanced="""
Advanced
diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py
index 964bf440..62b09d4e 100644
--- a/pyrogram/client/client.py
+++ b/pyrogram/client/client.py
@@ -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):
- self.stop()
+ 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:
- 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"):
- continue
-
- if ":" in value:
- self.bot_token = value
- return await self.sign_in_bot(value)
- else:
- self.phone_number = value
-
try:
+ if not self.phone_number:
+ while True:
+ value = await ainput("Enter phone number or bot token: ")
+
+ 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
+
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,9 +1703,11 @@ 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
+ )
+ )
return self.storage.get_peer_by_username(peer_id)
else:
@@ -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)
diff --git a/pyrogram/client/ext/base_client.py b/pyrogram/client/ext/base_client.py
index 15906adf..0638e445 100644
--- a/pyrogram/client/ext/base_client.py
+++ b/pyrogram/client/ext/base_client.py
@@ -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
diff --git a/pyrogram/client/ext/dispatcher.py b/pyrogram/client/ext/dispatcher.py
index a2fa472b..cd05f3f9 100644
--- a/pyrogram/client/ext/dispatcher.py
+++ b/pyrogram/client/ext/dispatcher.py
@@ -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)
diff --git a/pyrogram/client/ext/syncer.py b/pyrogram/client/ext/syncer.py
index 8b48e6e2..a00d2c70 100644
--- a/pyrogram/client/ext/syncer.py
+++ b/pyrogram/client/ext/syncer.py
@@ -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
))
diff --git a/pyrogram/client/methods/chats/get_chat_members.py b/pyrogram/client/methods/chats/get_chat_members.py
index ad7d2218..d2c31c52 100644
--- a/pyrogram/client/methods/chats/get_chat_members.py
+++ b/pyrogram/client/methods/chats/get_chat_members.py
@@ -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))
diff --git a/pyrogram/client/methods/chats/get_dialogs.py b/pyrogram/client/methods/chats/get_dialogs.py
index ec25249d..af703615 100644
--- a/pyrogram/client/methods/chats/get_dialogs.py
+++ b/pyrogram/client/methods/chats/get_dialogs.py
@@ -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)
diff --git a/pyrogram/client/methods/contacts/get_contacts.py b/pyrogram/client/methods/contacts/get_contacts.py
index 10fd9950..d1ccd728 100644
--- a/pyrogram/client/methods/contacts/get_contacts.py
+++ b/pyrogram/client/methods/contacts/get_contacts.py
@@ -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)
diff --git a/pyrogram/client/methods/messages/get_history.py b/pyrogram/client/methods/messages/get_history.py
index 50bc4b8f..c4eec5ce 100644
--- a/pyrogram/client/methods/messages/get_history.py
+++ b/pyrogram/client/methods/messages/get_history.py
@@ -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
diff --git a/pyrogram/client/methods/messages/get_history_count.py b/pyrogram/client/methods/messages/get_history_count.py
index d235e79b..c496a82b 100644
--- a/pyrogram/client/methods/messages/get_history_count.py
+++ b/pyrogram/client/methods/messages/get_history_count.py
@@ -16,14 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-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(
diff --git a/pyrogram/client/methods/messages/get_messages.py b/pyrogram/client/methods/messages/get_messages.py
index c6c89af6..834e60c5 100644
--- a/pyrogram/client/methods/messages/get_messages.py
+++ b/pyrogram/client/methods/messages/get_messages.py
@@ -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
diff --git a/pyrogram/client/methods/messages/send_media_group.py b/pyrogram/client/methods/messages/send_media_group.py
index 1e16d0d3..8df58db9 100644
--- a/pyrogram/client/methods/messages/send_media_group.py
+++ b/pyrogram/client/methods/messages/send_media_group.py
@@ -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
diff --git a/pyrogram/client/parser/html.py b/pyrogram/client/parser/html.py
index d21f024f..7e5a37f0 100644
--- a/pyrogram/client/parser/html.py
+++ b/pyrogram/client/parser/html.py
@@ -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 = []
diff --git a/pyrogram/client/storage/file_storage.py b/pyrogram/client/storage/file_storage.py
index e6ba8420..a24a46db 100644
--- a/pyrogram/client/storage/file_storage.py
+++ b/pyrogram/client/storage/file_storage.py
@@ -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)
diff --git a/pyrogram/client/storage/memory_storage.py b/pyrogram/client/storage/memory_storage.py
index e69d247f..ff98fd59 100644
--- a/pyrogram/client/storage/memory_storage.py
+++ b/pyrogram/client/storage/memory_storage.py
@@ -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(
diff --git a/pyrogram/client/storage/storage.py b/pyrogram/client/storage/storage.py
index e0810645..cd6438d4 100644
--- a/pyrogram/client/storage/storage.py
+++ b/pyrogram/client/storage/storage.py
@@ -30,6 +30,9 @@ class Storage:
def close(self):
raise NotImplementedError
+ def destroy(self):
+ raise NotImplementedError
+
def update_peers(self, peers):
raise NotImplementedError
diff --git a/pyrogram/client/types/user_and_chats/__init__.py b/pyrogram/client/types/user_and_chats/__init__.py
index 29c45e09..fa0fcd46 100644
--- a/pyrogram/client/types/user_and_chats/__init__.py
+++ b/pyrogram/client/types/user_and_chats/__init__.py
@@ -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"
]
diff --git a/pyrogram/client/types/user_and_chats/chat_photo.py b/pyrogram/client/types/user_and_chats/chat_photo.py
index b19ea771..914f8d59 100644
--- a/pyrogram/client/types/user_and_chats/chat_photo.py
+++ b/pyrogram/client/types/user_and_chats/chat_photo.py
@@ -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):
diff --git a/pyrogram/connection/connection.py b/pyrogram/connection/connection.py
index 2cd55526..ef7ff39b 100644
--- a/pyrogram/connection/connection.py
+++ b/pyrogram/connection/connection.py
@@ -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:
diff --git a/pyrogram/connection/transport/tcp/tcp.py b/pyrogram/connection/transport/tcp/tcp.py
index 0d33fcd9..f76cb250 100644
--- a/pyrogram/connection/transport/tcp/tcp.py
+++ b/pyrogram/connection/transport/tcp/tcp.py
@@ -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
diff --git a/pyrogram/connection/transport/tcp/tcp_abridged.py b/pyrogram/connection/transport/tcp/tcp_abridged.py
index a155a9a2..9d460708 100644
--- a/pyrogram/connection/transport/tcp/tcp_abridged.py
+++ b/pyrogram/connection/transport/tcp/tcp_abridged.py
@@ -16,12 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-import logging
-
from .tcp import TCP
-log = logging.getLogger(__name__)
-
class TCPAbridged(TCP):
def __init__(self, ipv6: bool, proxy: dict):
diff --git a/pyrogram/connection/transport/tcp/tcp_abridged_o.py b/pyrogram/connection/transport/tcp/tcp_abridged_o.py
index 3ec6a002..cc5de744 100644
--- a/pyrogram/connection/transport/tcp/tcp_abridged_o.py
+++ b/pyrogram/connection/transport/tcp/tcp_abridged_o.py
@@ -16,14 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-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)
diff --git a/pyrogram/connection/transport/tcp/tcp_full.py b/pyrogram/connection/transport/tcp/tcp_full.py
index 9404a4aa..119adba0 100644
--- a/pyrogram/connection/transport/tcp/tcp_full.py
+++ b/pyrogram/connection/transport/tcp/tcp_full.py
@@ -16,14 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-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):
diff --git a/pyrogram/connection/transport/tcp/tcp_intermediate.py b/pyrogram/connection/transport/tcp/tcp_intermediate.py
index b2ab88a5..9d833a79 100644
--- a/pyrogram/connection/transport/tcp/tcp_intermediate.py
+++ b/pyrogram/connection/transport/tcp/tcp_intermediate.py
@@ -16,13 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-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):
diff --git a/pyrogram/connection/transport/tcp/tcp_intermediate_o.py b/pyrogram/connection/transport/tcp/tcp_intermediate_o.py
index 8d7113a5..234b2910 100644
--- a/pyrogram/connection/transport/tcp/tcp_intermediate_o.py
+++ b/pyrogram/connection/transport/tcp/tcp_intermediate_o.py
@@ -16,15 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-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)
diff --git a/pyrogram/crypto/aes.py b/pyrogram/crypto/aes.py
index d603caa0..9088d57f 100644
--- a/pyrogram/crypto/aes.py
+++ b/pyrogram/crypto/aes.py
@@ -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"
diff --git a/pyrogram/session/auth.py b/pyrogram/session/auth.py
index ba0f5aed..d153716e 100644
--- a/pyrogram/session/auth.py
+++ b/pyrogram/session/auth.py
@@ -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__
)
diff --git a/pyrogram/session/session.py b/pyrogram/session/session.py
index c19aeaf3..b5f69081 100644
--- a/pyrogram/session/session.py
+++ b/pyrogram/session/session.py
@@ -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)