Remove API key requirement for existing sessions

This commit is contained in:
Dan 2022-04-24 11:56:07 +02:00
parent b8f39725c5
commit 124bcb4db7
28 changed files with 250 additions and 435 deletions

View File

@ -1,6 +1,6 @@
id message
FILE_MIGRATE_X The file to be accessed is currently stored in DC{x}
NETWORK_MIGRATE_X The source IP address is associated with DC{x} (for registration)
PHONE_MIGRATE_X The phone number a user is trying to use for authorization is associated with DC{x}
STATS_MIGRATE_X The statistics of the group/channel are stored in DC{x}
USER_MIGRATE_X The user whose identity is being used to execute queries is associated with DC{x} (for registration)
FILE_MIGRATE_X The file to be accessed is currently stored in DC{value}
NETWORK_MIGRATE_X The source IP address is associated with DC{value} (for registration)
PHONE_MIGRATE_X The phone number a user is trying to use for authorization is associated with DC{value}
STATS_MIGRATE_X The statistics of the group/channel are stored in DC{value}
USER_MIGRATE_X The user whose identity is being used to execute queries is associated with DC{value} (for registration)
1 id message
2 FILE_MIGRATE_X The file to be accessed is currently stored in DC{x} The file to be accessed is currently stored in DC{value}
3 NETWORK_MIGRATE_X The source IP address is associated with DC{x} (for registration) The source IP address is associated with DC{value} (for registration)
4 PHONE_MIGRATE_X The phone number a user is trying to use for authorization is associated with DC{x} The phone number a user is trying to use for authorization is associated with DC{value}
5 STATS_MIGRATE_X The statistics of the group/channel are stored in DC{x} The statistics of the group/channel are stored in DC{value}
6 USER_MIGRATE_X The user whose identity is being used to execute queries is associated with DC{x} (for registration) The user whose identity is being used to execute queries is associated with DC{value} (for registration)

View File

@ -89,7 +89,7 @@ DOCUMENT_INVALID The document is invalid
EMAIL_HASH_EXPIRED The email hash expired and cannot be used to verify it
EMAIL_INVALID The email provided is invalid
EMAIL_UNCONFIRMED Email unconfirmed
EMAIL_UNCONFIRMED_X The provided email isn't confirmed, {x} is the length of the verification code that was just sent to the email
EMAIL_UNCONFIRMED_X The provided email isn't confirmed, {value} is the length of the verification code that was just sent to the email
EMAIL_VERIFY_EXPIRED The verification email has expired
EMOTICON_EMPTY The emoticon parameter is empty
EMOTICON_INVALID The emoticon parameter is invalid
@ -108,7 +108,7 @@ EXTERNAL_URL_INVALID The external media URL is invalid
FIELD_NAME_EMPTY The field with the name FIELD_NAME is missing
FIELD_NAME_INVALID The field with the name FIELD_NAME is invalid
FILE_ID_INVALID The file id is invalid
FILE_MIGRATE_X The file is in Data Center No. {x}
FILE_MIGRATE_X The file is in Data Center No. {value}
FILE_PARTS_INVALID Invalid number of parts. The value is not between 1 and 4000
FILE_PART_EMPTY The file part sent is empty
FILE_PART_INVALID The file part number is invalid. The value is not between 0 and 3999
@ -116,7 +116,7 @@ FILE_PART_LENGTH_INVALID The length of a file part is invalid
FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file
FILE_PART_SIZE_INVALID 512 KB cannot be evenly divided by part_size
FILE_PART_TOO_BIG The size limit (512 KB) for the content of the file part has been exceeded
FILE_PART_X_MISSING Part {x} of the file is missing from storage
FILE_PART_X_MISSING Part {value} of the file is missing from storage
FILE_REFERENCE_EMPTY The file id contains an empty file reference, you must obtain a valid one by fetching the message from the origin context
FILE_REFERENCE_EXPIRED The file id contains an expired file reference, you must obtain a valid one by fetching the message from the origin context
FILE_REFERENCE_INVALID The file id contains an invalid file reference, you must obtain a valid one by fetching the message from the origin context
@ -198,7 +198,7 @@ PASSWORD_HASH_INVALID The two-step verification password is invalid
PASSWORD_MISSING The account is missing the two-step verification password
PASSWORD_RECOVERY_NA The password recovery e-mail is not available
PASSWORD_REQUIRED The two-step verification password is required for this method
PASSWORD_TOO_FRESH_X The two-step verification password was added recently and you are required to wait {x} seconds
PASSWORD_TOO_FRESH_X The two-step verification password was added recently and you are required to wait {value} seconds
PAYMENT_PROVIDER_INVALID The payment provider was not recognised or its token was invalid
PEER_FLOOD The method can't be used because your account is currently limited
PEER_ID_INVALID The peer id being used is invalid or not known yet. Make sure you meet the peer before interacting with it
@ -349,4 +349,4 @@ WEBDOCUMENT_URL_EMPTY The web document URL is empty
WEBDOCUMENT_URL_INVALID The web document URL is invalid
WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL
WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media
YOU_BLOCKED_USER You blocked this user
YOU_BLOCKED_USER You blocked this user
1 id message
89 EMAIL_HASH_EXPIRED The email hash expired and cannot be used to verify it
90 EMAIL_INVALID The email provided is invalid
91 EMAIL_UNCONFIRMED Email unconfirmed
92 EMAIL_UNCONFIRMED_X The provided email isn't confirmed, {x} is the length of the verification code that was just sent to the email The provided email isn't confirmed, {value} is the length of the verification code that was just sent to the email
93 EMAIL_VERIFY_EXPIRED The verification email has expired
94 EMOTICON_EMPTY The emoticon parameter is empty
95 EMOTICON_INVALID The emoticon parameter is invalid
108 FIELD_NAME_EMPTY The field with the name FIELD_NAME is missing
109 FIELD_NAME_INVALID The field with the name FIELD_NAME is invalid
110 FILE_ID_INVALID The file id is invalid
111 FILE_MIGRATE_X The file is in Data Center No. {x} The file is in Data Center No. {value}
112 FILE_PARTS_INVALID Invalid number of parts. The value is not between 1 and 4000
113 FILE_PART_EMPTY The file part sent is empty
114 FILE_PART_INVALID The file part number is invalid. The value is not between 0 and 3999
116 FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file
117 FILE_PART_SIZE_INVALID 512 KB cannot be evenly divided by part_size
118 FILE_PART_TOO_BIG The size limit (512 KB) for the content of the file part has been exceeded
119 FILE_PART_X_MISSING Part {x} of the file is missing from storage Part {value} of the file is missing from storage
120 FILE_REFERENCE_EMPTY The file id contains an empty file reference, you must obtain a valid one by fetching the message from the origin context
121 FILE_REFERENCE_EXPIRED The file id contains an expired file reference, you must obtain a valid one by fetching the message from the origin context
122 FILE_REFERENCE_INVALID The file id contains an invalid file reference, you must obtain a valid one by fetching the message from the origin context
198 PASSWORD_MISSING The account is missing the two-step verification password
199 PASSWORD_RECOVERY_NA The password recovery e-mail is not available
200 PASSWORD_REQUIRED The two-step verification password is required for this method
201 PASSWORD_TOO_FRESH_X The two-step verification password was added recently and you are required to wait {x} seconds The two-step verification password was added recently and you are required to wait {value} seconds
202 PAYMENT_PROVIDER_INVALID The payment provider was not recognised or its token was invalid
203 PEER_FLOOD The method can't be used because your account is currently limited
204 PEER_ID_INVALID The peer id being used is invalid or not known yet. Make sure you meet the peer before interacting with it
349 WEBDOCUMENT_URL_INVALID The web document URL is invalid
350 WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL
351 WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media
352 YOU_BLOCKED_USER You blocked this user

View File

@ -1,6 +1,6 @@
id message
2FA_CONFIRM_WAIT_X A wait of {x} seconds is required because this account is active and protected by a 2FA password
FLOOD_TEST_PHONE_WAIT_X A wait of {x} seconds is required in the test servers
FLOOD_WAIT_X A wait of {x} seconds is required
SLOWMODE_WAIT_X A wait of {x} seconds is required to send messages in this chat.
TAKEOUT_INIT_DELAY_X You have to confirm the data export request using one of your mobile devices or wait {x} seconds
2FA_CONFIRM_WAIT_X A wait of {value} seconds is required because this account is active and protected by a 2FA password
FLOOD_TEST_PHONE_WAIT_X A wait of {value} seconds is required in the test servers
FLOOD_WAIT_X A wait of {value} seconds is required
SLOWMODE_WAIT_X A wait of {value} seconds is required to send messages in this chat.
TAKEOUT_INIT_DELAY_X You have to confirm the data export request using one of your mobile devices or wait {value} seconds
1 id message
2 2FA_CONFIRM_WAIT_X A wait of {x} seconds is required because this account is active and protected by a 2FA password A wait of {value} seconds is required because this account is active and protected by a 2FA password
3 FLOOD_TEST_PHONE_WAIT_X A wait of {x} seconds is required in the test servers A wait of {value} seconds is required in the test servers
4 FLOOD_WAIT_X A wait of {x} seconds is required A wait of {value} seconds is required
5 SLOWMODE_WAIT_X A wait of {x} seconds is required to send messages in this chat. A wait of {value} seconds is required to send messages in this chat.
6 TAKEOUT_INIT_DELAY_X You have to confirm the data export request using one of your mobile devices or wait {x} seconds You have to confirm the data export request using one of your mobile devices or wait {value} seconds

View File

@ -13,8 +13,8 @@ GROUPCALL_ADD_PARTICIPANTS_FAILED Failure while adding voice chat member due to
GROUPED_ID_OCCUPY_FAILED Telegram is having internal problems. Please try again later
HISTORY_GET_FAILED The chat history couldn't be retrieved due to Telegram having internal problems. Please try again later
IMAGE_ENGINE_DOWN Image engine down due to Telegram having internal problems. Please try again later
INTERDC_X_CALL_ERROR An error occurred while Telegram was intercommunicating with DC{x}. Please try again later
INTERDC_X_CALL_RICH_ERROR A rich error occurred while Telegram was intercommunicating with DC{x}. Please try again later
INTERDC_X_CALL_ERROR An error occurred while Telegram was intercommunicating with DC{value}. Please try again later
INTERDC_X_CALL_RICH_ERROR A rich error occurred while Telegram was intercommunicating with DC{value}. Please try again later
MEMBER_FETCH_FAILED Telegram is having internal problems. Please try again later
MEMBER_NO_LOCATION Couldn't find the member's location due to Telegram having internal problems. Please try again later
MEMBER_OCCUPY_PRIMARY_LOC_FAILED Telegram is having internal problems. Please try again later

1 id message
13 GROUPED_ID_OCCUPY_FAILED Telegram is having internal problems. Please try again later
14 HISTORY_GET_FAILED The chat history couldn't be retrieved due to Telegram having internal problems. Please try again later
15 IMAGE_ENGINE_DOWN Image engine down due to Telegram having internal problems. Please try again later
16 INTERDC_X_CALL_ERROR An error occurred while Telegram was intercommunicating with DC{x}. Please try again later An error occurred while Telegram was intercommunicating with DC{value}. Please try again later
17 INTERDC_X_CALL_RICH_ERROR A rich error occurred while Telegram was intercommunicating with DC{x}. Please try again later A rich error occurred while Telegram was intercommunicating with DC{value}. Please try again later
18 MEMBER_FETCH_FAILED Telegram is having internal problems. Please try again later
19 MEMBER_NO_LOCATION Couldn't find the member's location due to Telegram having internal problems. Please try again later
20 MEMBER_OCCUPY_PRIMARY_LOC_FAILED Telegram is having internal problems. Please try again later

View File

@ -1,3 +1,3 @@
id message
Timeout Telegram is having internal problems. Please try again later.
ApiCallError Telegram is having internal problems. Please try again later.
ApiCallError Telegram is having internal problems. Please try again later.
Timeout Telegram is having internal problems. Please try again later.
1 id message
2 Timeout ApiCallError Telegram is having internal problems. Please try again later.
3 ApiCallError Timeout Telegram is having internal problems. Please try again later.

View File

@ -0,0 +1,8 @@
NextCodeType
============
.. autoclass:: pyrogram.enums.NextCodeType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -25,6 +25,7 @@ to apply only a valid value among the expected ones.
ParseMode
PollType
SentCodeType
NextCodeType
UserStatus
.. toctree::
@ -42,4 +43,5 @@ to apply only a valid value among the expected ones.
ParseMode
PollType
SentCodeType
NextCodeType
UserStatus

View File

@ -111,12 +111,12 @@ Meta
intro/quickstart
intro/install
intro/setup
.. toctree::
:hidden:
:caption: Getting Started
start/setup
start/auth
start/invoking
start/updates
@ -144,7 +144,6 @@ Meta
topics/use-filters
topics/create-filters
topics/more-on-updates
topics/config-file
topics/smart-plugins
topics/client-settings
topics/tgcrypto

View File

@ -26,10 +26,12 @@ Get Pyrogram Real Fast
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
async def main():
async with Client("my_account", api_id, api_hash) as app:
await app.send_message("me", "Greetings from **Pyrogram**!")
asyncio.run(main())
4. Replace *api_id* and *api_hash* values with your own.

View File

@ -1,60 +0,0 @@
Project Setup
=============
We have just :doc:`installed Pyrogram <install>`. In this page we'll discuss what you need to do in order to set up a
project with the framework.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
API Keys
--------
The very first step requires you to obtain a valid Telegram API key (API id/hash pair):
#. Visit https://my.telegram.org/apps and log in with your Telegram Account.
#. Fill out the form with your details and register a new Telegram application.
#. Done. The API key consists of two parts: **api_id** and **api_hash**. Keep it secret.
.. note::
The API key defines a token for a Telegram *application* you are going to build.
This means that you are able to authorize multiple users or bots with a single API key.
Configuration
-------------
Having the API key from the previous step in handy, we can now begin to configure a Pyrogram project.
There are two ways to do so, and you can choose what fits better for you:
- First option: create a new ``config.ini`` file next to your main script, copy-paste the following and
replace the *api_id* and *api_hash* values with your own. This method allows you to keep your credentials out of
your code without having to deal with how to load them.
.. code-block:: ini
[pyrogram]
api_id = 12345
api_hash = 0123456789abcdef0123456789abcdef
- Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* parameters of the
Client class. This way you can have full control on how to store and load your credentials:
.. code-block:: python
from pyrogram import Client
app = Client(
"my_account",
api_id=12345,
api_hash="0123456789abcdef0123456789abcdef"
)
.. note::
To keep code snippets clean and concise, from now on it is assumed you are making use of the ``config.ini`` file,
thus, the *api_id* and *api_hash* parameters usage won't be shown anymore.

View File

@ -1,7 +1,7 @@
Authorization
=============
Once a :doc:`project is set up <../intro/setup>`, you will still have to follow a few steps before you can actually use Pyrogram to make
Once a :doc:`project is set up <setup>`, you will still have to follow a few steps before you can actually use Pyrogram to make
API calls. This section provides all the information you need in order to authorize yourself as user or bot.
.. contents:: Contents
@ -23,7 +23,11 @@ the :meth:`~pyrogram.Client.run` method:
from pyrogram import Client
app = Client("my_account")
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
app = Client("my_account", api_id=api_id, api_hash=api_hash)
app.run()
This starts an interactive shell asking you to input your **phone number**, including your `Country Code`_ (the plus
@ -38,8 +42,8 @@ authorized or via SMS:
Logged in successfully
After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing Pyrogram to
execute API calls with your identity. This file is personal and will be loaded again when you restart your app, and as
long as you keep the session alive, Pyrogram won't ask you again to enter your phone number.
execute API calls with your identity. This file is personal and will be loaded again when you restart your app.
You can now remove the api_id and api_hash values from the code as they are not needed anymore.
.. note::
@ -51,7 +55,7 @@ Bot Authorization
Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by
the `Bot Father`_. Bot tokens replace the users' phone numbers only — you still need to
:doc:`configure a Telegram API key <../intro/setup>` with Pyrogram, even when using bots.
:doc:`configure a Telegram API key <../start/setup>` with Pyrogram, even when using bots.
The authorization process is automatically managed. All you need to do is choose a ``session_name`` (can be anything,
usually your bot username) and pass your bot token using the ``bot_token`` parameter. The session file will be named
@ -61,12 +65,29 @@ after the session name, which will be ``my_bot.session`` for the example below.
from pyrogram import Client
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
bot_token = "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
app = Client(
"my_bot",
bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
api_id=api_id, api_hash=api_hash,
bot_token=bot_token
)
app.run()
.. _Country Code: https://en.wikipedia.org/wiki/List_of_country_calling_codes
.. _Bot Father: https://t.me/botfather
.. _Bot Father: https://t.me/botfather
.. note::
The API key (api_id and api_hash) and the bot_token is not needed anymore after a successful authorization.
This means you can now simply use:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
app.run()

View File

@ -22,10 +22,12 @@ Making API calls with Pyrogram is very simple. Here's a basic example we are goi
app = Client("my_account")
async def main():
async with app:
await app.send_message("me", "Hi!")
app.run(main())
Step-by-step
@ -76,11 +78,13 @@ Below there's the same example as above, but without the use of the context mana
app = Client("my_account")
async def main():
await app.start()
await app.send_message("me", "Hi!")
await app.stop()
app.run(main())
Using asyncio.run()
@ -95,10 +99,12 @@ be instantiated inside the main function.
import asyncio
from pyrogram import Client
async def main():
app = Client("my_account")
async with app:
await app.send_message("me", "Hi!")
asyncio.run(main())

View File

@ -0,0 +1,40 @@
Project Setup
=============
We have just :doc:`installed Pyrogram <../intro/install>`. In this page we'll discuss what you need to do in order to set up a
project with the framework.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
API Key
-------
The first step requires you to obtain a valid Telegram API key (api_id and api_hash pair):
#. Visit https://my.telegram.org/apps and log in with your Telegram account.
#. Fill out the form with your details and register a new Telegram application.
#. Done. The API key consists of two parts: **api_id** and **api_hash**. Keep it secret.
.. note::
The API key defines a token for a Telegram *application* you are going to build.
This means that you are able to authorize multiple users or bots with a single API key.
Configuration
-------------
Having the API key from the previous step in handy, we can now begin to configure a Pyrogram project: pass your API key to Pyrogram by using the *api_id* and *api_hash* parameters of the Client class:
.. code-block:: python
from pyrogram import Client
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
app = Client("my_account", api_id=api_id, api_hash=api_hash)

View File

@ -39,10 +39,12 @@ The most elegant way to register a message handler is by using the :meth:`~pyrog
app = Client("my_account")
@app.on_message()
async def my_handler(client, message):
await message.forward("me")
app.run()
The defined function ``my_handler``, which accepts the two arguments *(client, message)*, will be the function that gets
@ -63,9 +65,11 @@ function and registers it in your Client. It is useful in case you want to progr
from pyrogram import Client
from pyrogram.handlers import MessageHandler
async def my_function(client, message):
await message.forward("me")
app = Client("my_account")
my_handler = MessageHandler(my_function)

View File

@ -1,97 +0,0 @@
Configuration File
==================
As already mentioned in previous pages, Pyrogram can be configured by the use of an INI file.
This page explains how this file is structured, how to use it and why.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Introduction
------------
The idea behind using a configuration file is to help keeping your code free of private settings information such as
the API Key and Proxy, without having you to deal with how to load such settings. The configuration file, usually
referred as ``config.ini`` file, is automatically loaded from the root of your working directory; all you need to do is
fill in the necessary parts.
.. note::
The configuration file is optional, but recommended. If, for any reason, you prefer not to use it, there's always an
alternative way to configure Pyrogram via Client's parameters. Doing so, you can have full control on how to store
and load your settings.
Settings specified via Client's parameter have higher priority and will override any setting stored in the
configuration file.
The config.ini File
-------------------
By default, Pyrogram will look for a file named ``config.ini`` placed at the root of your working directory, that is,
the same folder of your running script. You can change the name or location of your configuration file by specifying it
in your Client's parameter *config_file*.
- Replace the default *config.ini* file with *my_configuration.ini*:
.. code-block:: python
from pyrogram import Client
app = Client("my_account", config_file="my_configuration.ini")
Configuration Sections
----------------------
These are all the sections Pyrogram uses in its configuration file:
Pyrogram
^^^^^^^^
The ``[pyrogram]`` section contains your Telegram API credentials: *api_id* and *api_hash*.
.. code-block:: ini
[pyrogram]
api_id = 12345
api_hash = 0123456789abcdef0123456789abcdef
`More info about API Key. <../intro/setup#api-keys>`_
Proxy
^^^^^
The ``[proxy]`` section contains settings about your SOCKS5 proxy.
.. code-block:: ini
[proxy]
enabled = True
hostname = 11.22.33.44
port = 1080
username = <your_username>
password = <your_password>
`More info about SOCKS5 Proxy. <proxy>`_
Plugins
^^^^^^^
The ``[plugins]`` section contains settings about Smart Plugins.
.. code-block:: ini
[plugins]
root = plugins
include =
module
folder.module
exclude =
module fn2
`More info about Smart Plugins. <smart-plugins>`_

View File

@ -1,8 +1,8 @@
SOCKS5 Proxy
============
Proxy Settings
==============
Pyrogram supports proxies with and without authentication. This feature allows Pyrogram to exchange data with Telegram
through an intermediate SOCKS5 proxy server.
through an intermediate SOCKS 4/5 or HTTP (CONNECT) proxy server.
.. contents:: Contents
:backlinks: none
@ -14,44 +14,22 @@ through an intermediate SOCKS5 proxy server.
Usage
-----
- To use Pyrogram with a proxy, simply append the following to your ``config.ini`` file and replace the values
with your own settings:
To use Pyrogram with a proxy, use the *proxy* parameter in the Client class. If your proxy doesn't require authorization
you can omit ``username`` and ``password``.
.. code-block:: ini
.. code-block:: python
[proxy]
enabled = True
hostname = 11.22.33.44
port = 1080
username = <your_username>
password = <your_password>
from pyrogram import Client
To enable or disable the proxy without deleting your settings from the config file,
change the ``enabled`` value as follows:
- ``1``, ``yes``, ``True`` or ``on``: Enables the proxy
- ``0``, ``no``, ``False`` or ``off``: Disables the proxy
- Alternatively, you can setup your proxy without the need of the ``config.ini`` file by using the *proxy* parameter
in the Client class:
.. code-block:: python
from pyrogram import Client
app = Client(
session_name="example",
proxy=dict(
hostname="11.22.33.44",
port=1080,
username="<your_username>",
password="<your_password>"
)
app = Client(
"my_account",
proxy=dict(
scheme="socks5", # "socks4", "socks5" and "http" are supported
hostname="11.22.33.44",
port=1234,
username="<your_username>",
password="<your_password>"
)
)
app.start()
...
.. note:: If your proxy doesn't require authorization you can omit ``username`` and ``password`` by either leaving the
values blank/empty or completely delete the lines.
app.run()

View File

@ -57,11 +57,11 @@ operators ``~``, ``&`` and ``|``:
Here are some examples:
- Message is a **text** message **and** is **not edited**.
- Message is a **text** message **or** a **photo**.
.. code-block:: python
@app.on_message(filters.text & ~filters.edited)
@app.on_message(filters.text | filters.photo)
def my_handler(client, message):
print(message)

View File

@ -27,7 +27,6 @@ import shutil
import sys
import tempfile
from concurrent.futures.thread import ThreadPoolExecutor
from configparser import ConfigParser
from hashlib import sha256
from importlib import import_module
from io import StringIO
@ -76,38 +75,37 @@ class Client(Methods):
pass here as argument.
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.
The *api_id* part of your Telegram API key, as integer.
E.g.: 12345.
api_hash (``str``, *optional*):
The *api_hash* part of your Telegram API Key, as string. E.g.: "0123456789abcdef0123456789abcdef".
This is an alternative way to set it if you don't want to use the *config.ini* file.
The *api_hash* part of your Telegram API key, as string.
E.g.: "0123456789abcdef0123456789abcdef".
app_version (``str``, *optional*):
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.
Application version.
Defaults to "Pyrogram x.y.z".
device_model (``str``, *optional*):
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.
Device model.
Defaults to *platform.python_implementation() + " " + platform.python_version()*.
system_version (``str``, *optional*):
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.
Operating System version.
Defaults to *platform.system() + " " + platform.release()*.
lang_code (``str``, *optional*):
Code of the language used on the client, in ISO 639-1 standard. Defaults to "en".
This is an alternative way to set it if you don't want to use the *config.ini* file.
Code of the language used on the client, in ISO 639-1 standard.
Defaults to "en".
ipv6 (``bool``, *optional*):
Pass True to connect to Telegram using IPv6.
Defaults to False (IPv4).
proxy (``dict``, *optional*):
Your SOCKS5 Proxy settings as dict,
e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*.
The Proxy settings as dict.
E.g.: *dict(scheme="socks5", hostname="11.22.33.44", port=1234, username="user", password="pass")*.
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.
@ -117,7 +115,6 @@ class Client(Methods):
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``, *optional*):
Pass your phone number as string (with your Country Code prefix included) to avoid entering it manually.
@ -131,11 +128,6 @@ class Client(Methods):
Pass your Two-Step Verification password as string (if you have one) to avoid entering it manually.
Only applicable for new sessions.
force_sms (``bool``, *optional*):
Pass True to force Telegram sending the authorization code via SMS.
Only applicable for new sessions.
Defaults to False.
workers (``int``, *optional*):
Number of maximum concurrent workers for handling incoming updates.
Defaults to ``min(32, os.cpu_count() + 4)``.
@ -145,13 +137,8 @@ class Client(Methods):
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
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.
parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
The parse mode, can be any of: *"combined"*, for the default combined mode. *"markdown"* or *"md"*
@ -194,7 +181,6 @@ class Client(Methods):
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/(?:joinchat/|\+))([\w-]+)$")
WORKERS = min(32, (os.cpu_count() or 0) + 4) # os.cpu_count() can be None
WORKDIR = PARENT_DIR
CONFIG_FILE = PARENT_DIR / "config.ini"
mimetypes = MimeTypes()
mimetypes.readfp(StringIO(mime_types))
@ -204,10 +190,10 @@ class Client(Methods):
session_name: Union[str, Storage],
api_id: int = None,
api_hash: str = None,
app_version: str = None,
device_model: str = None,
system_version: str = None,
lang_code: str = None,
app_version: str = APP_VERSION,
device_model: str = DEVICE_MODEL,
system_version: str = SYSTEM_VERSION,
lang_code: str = LANG_CODE,
ipv6: bool = False,
proxy: dict = None,
test_mode: bool = False,
@ -215,10 +201,8 @@ class Client(Methods):
phone_number: str = None,
phone_code: str = None,
password: str = None,
force_sms: bool = False,
workers: int = WORKERS,
workdir: str = WORKDIR,
config_file: str = CONFIG_FILE,
plugins: dict = None,
parse_mode: "enums.ParseMode" = enums.ParseMode.DEFAULT,
no_updates: bool = None,
@ -236,17 +220,14 @@ class Client(Methods):
self.system_version = system_version
self.lang_code = lang_code
self.ipv6 = ipv6
# TODO: Make code consistent, use underscore for private/protected fields
self._proxy = proxy
self.proxy = proxy
self.test_mode = test_mode
self.bot_token = bot_token
self.phone_number = phone_number
self.phone_code = phone_code
self.password = password
self.force_sms = force_sms
self.workers = workers
self.workdir = Path(workdir)
self.config_file = Path(config_file)
self.plugins = plugins
self.parse_mode = parse_mode
self.no_updates = no_updates
@ -295,29 +276,19 @@ class Client(Methods):
return self.start()
def __exit__(self, *args):
self.stop()
try:
self.stop()
except ConnectionError:
pass
async def __aenter__(self):
return await self.start()
async def __aexit__(self, *args):
await self.stop()
@property
def proxy(self):
return self._proxy
@proxy.setter
def proxy(self, value):
if value is None:
self._proxy = None
return
if self._proxy is None:
self._proxy = {}
self._proxy["enabled"] = bool(value.get("enabled", True))
self._proxy.update(value)
try:
await self.stop()
except ConnectionError:
pass
async def authorize(self) -> User:
if self.bot_token:
@ -355,17 +326,14 @@ class Client(Methods):
else:
break
if self.force_sms:
sent_code = await self.resend_code(self.phone_number, sent_code.phone_code_hash)
sent_code_descriptions = {
enums.SentCodeType.APP: "Telegram app",
enums.SentCodeType.SMS: "SMS",
enums.SentCodeType.CALL: "phone call",
enums.SentCodeType.FLASH_CALL: "phone flash call"
}
print("The confirmation code has been sent via {}".format(
{
"app": "Telegram app",
"sms": "SMS",
"call": "phone call",
"flash_call": "phone flash call"
}[sent_code.type]
))
print(f"The confirmation code has been sent via {sent_code_descriptions[sent_code.type]}")
while True:
if not self.phone_code:
@ -615,79 +583,6 @@ class Client(Methods):
elif isinstance(updates, raw.types.UpdatesTooLong):
log.info(updates)
def load_config(self):
parser = ConfigParser()
parser.read(str(self.config_file))
if self.bot_token:
pass
else:
self.bot_token = parser.get("pyrogram", "bot_token", fallback=None)
if self.api_id and self.api_hash:
pass
else:
if parser.has_section("pyrogram"):
self.api_id = parser.getint("pyrogram", "api_id")
self.api_hash = parser.get("pyrogram", "api_hash")
else:
raise AttributeError("No API Key found. More info: https://docs.pyrogram.org/intro/setup")
for option in ["app_version", "device_model", "system_version", "lang_code"]:
if getattr(self, option):
pass
else:
if parser.has_section("pyrogram"):
setattr(self, option, parser.get(
"pyrogram",
option,
fallback=getattr(Client, option.upper())
))
else:
setattr(self, option, getattr(Client, option.upper()))
if self._proxy:
self._proxy["enabled"] = bool(self._proxy.get("enabled", True))
else:
self._proxy = {}
if parser.has_section("proxy"):
self._proxy["enabled"] = parser.getboolean("proxy", "enabled", fallback=True)
self._proxy["hostname"] = parser.get("proxy", "hostname")
self._proxy["port"] = parser.getint("proxy", "port")
self._proxy["username"] = parser.get("proxy", "username", fallback=None) or None
self._proxy["password"] = parser.get("proxy", "password", fallback=None) or None
if self.plugins:
self.plugins = {
"enabled": bool(self.plugins.get("enabled", True)),
"root": self.plugins.get("root", None),
"include": self.plugins.get("include", []),
"exclude": self.plugins.get("exclude", [])
}
else:
try:
section = parser["plugins"]
self.plugins = {
"enabled": section.getboolean("enabled", True),
"root": section.get("root", None),
"include": section.get("include", []),
"exclude": section.get("exclude", [])
}
include = self.plugins["include"]
exclude = self.plugins["exclude"]
if include:
self.plugins["include"] = include.strip().split("\n")
if exclude:
self.plugins["exclude"] = exclude.strip().split("\n")
except KeyError:
self.plugins = None
async def load_session(self):
await self.storage.open()
@ -699,6 +594,12 @@ class Client(Methods):
])
if session_empty:
if not self.api_id or not self.api_hash:
raise AttributeError("The API key is required for new authorizations. "
"More info: https://docs.pyrogram.org/start/auth")
await self.storage.api_id(self.api_id)
await self.storage.dc_id(2)
await self.storage.date(0)
@ -711,6 +612,24 @@ class Client(Methods):
)
await self.storage.user_id(None)
await self.storage.is_bot(None)
else:
# Needed for migration from storage v2 to v3
if not await self.storage.api_id():
while True:
try:
value = int(await ainput("Enter the api_id part of the API key: "))
if value <= 0:
print("Invalid value")
continue
confirm = (await ainput(f'Is "{value}" correct? (y/N): ')).lower()
if confirm == "y":
await self.storage.api_id(value)
break
except Exception as e:
print(e)
def load_plugins(self):
if self.plugins:

View File

@ -47,9 +47,8 @@ class TCP:
self.lock = asyncio.Lock()
self.loop = asyncio.get_event_loop()
if proxy.get("enabled", False):
hostname = proxy.get("hostname", None)
port = proxy.get("port", None)
if proxy:
hostname = proxy.get("hostname")
try:
ip_address = ipaddress.ip_address(hostname)
@ -62,14 +61,14 @@ class TCP:
self.socket = socks.socksocket(socket.AF_INET)
self.socket.set_proxy(
proxy_type=socks.SOCKS5,
proxy_type=getattr(socks, proxy.get("scheme").upper()),
addr=hostname,
port=port,
port=proxy.get("port", None),
username=proxy.get("username", None),
password=proxy.get("password", None)
)
log.info(f"Using proxy {hostname}:{port}")
log.info(f"Using proxy {hostname}")
else:
self.socket = socks.socksocket(
socket.AF_INET6 if ipv6

View File

@ -25,6 +25,7 @@ from .message_entity_type import MessageEntityType
from .message_media import MessageMedia
from .message_service import MessageService
from .messages_filter import MessagesFilter
from .next_code_type import NextCodeType
from .parse_mode import ParseMode
from .poll_type import PollType
from .sent_code_type import SentCodeType

View File

@ -0,0 +1,36 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram import raw
from .auto_name import AutoName
class NextCodeType(AutoName):
"""Next code type enumeration used in :obj:`~pyrogram.types.SentCode`."""
CALL = raw.types.auth.CodeTypeCall
"The code will be sent via a phone call. A synthesized voice will tell the user which verification code to input."
FLASH_CALL = raw.types.auth.CodeTypeFlashCall
"The code will be sent via a flash phone call, that will be closed immediately."
MISSED_CALL = raw.types.auth.CodeTypeMissedCall
"Missed call."
SMS = raw.types.auth.CodeTypeSms
"The code was sent via SMS."

View File

@ -33,7 +33,7 @@ class SentCodeType(AutoName):
"The code will be sent via a flash phone call, that will be closed immediately."
MISSED_CALL = raw.types.auth.SentCodeTypeMissedCall
"Missed call"
"Missed call."
SMS = raw.types.auth.SentCodeTypeSms
"The code was sent via SMS."

View File

@ -37,7 +37,6 @@ class Connect:
if self.is_connected:
raise ConnectionError("Client is already connected")
self.load_config()
await self.load_session()
self.session = Session(

View File

@ -113,7 +113,7 @@ class Session:
raw.functions.InvokeWithLayer(
layer=layer,
query=raw.functions.InitConnection(
api_id=self.client.api_id,
api_id=await self.client.storage.api_id(),
app_version=self.client.app_version,
device_model=self.client.device_model,
system_version=self.client.system_version,

View File

@ -16,8 +16,6 @@
# 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 base64
import json
import logging
import os
import sqlite3
@ -36,37 +34,6 @@ class FileStorage(SQLiteStorage):
self.database = workdir / (self.name + self.FILE_EXTENSION)
def migrate_from_json(self, session_json: dict):
self.open()
self.dc_id(session_json["dc_id"])
self.test_mode(session_json["test_mode"])
self.auth_key(base64.b64decode("".join(session_json["auth_key"])))
self.user_id(session_json["user_id"])
self.date(session_json.get("date", 0))
self.is_bot(session_json.get("is_bot", False))
peers_by_id = session_json.get("peers_by_id", {})
peers_by_phone = session_json.get("peers_by_phone", {})
peers = {}
for k, v in peers_by_id.items():
if v is None:
type_ = "group"
elif k.startswith("-100"):
type_ = "channel"
else:
type_ = "user"
peers[int(k)] = [int(k), int(v) if v is not None else None, type_, None, None]
for k, v in peers_by_phone.items():
peers[v][4] = k
# noinspection PyTypeChecker
self.update_peers(peers.values())
def update(self):
version = self.version()
@ -76,34 +43,18 @@ class FileStorage(SQLiteStorage):
version += 1
if version == 2:
with self.lock, self.conn:
self.conn.execute("ALTER TABLE sessions ADD api_id INTEGER")
version += 1
self.version(version)
async def open(self):
path = self.database
file_exists = path.is_file()
if file_exists:
try:
with open(str(path), encoding="utf-8") as f:
session_json = json.load(f)
except ValueError:
pass
else:
log.warning("JSON session storage detected! Converting it into an SQLite session storage...")
path.rename(path.name + ".OLD")
log.warning(f'The old session file has been renamed to "{path.name}.OLD"')
self.migrate_from_json(session_json)
log.warning("Done! The session has been successfully converted from JSON to SQLite storage")
return
if Path(path.name + ".OLD").is_file():
log.warning(f'Old session file detected: "{path.name}.OLD". You can remove this file now')
self.conn = sqlite3.connect(str(path), timeout=1, check_same_thread=False)
if not file_exists:

View File

@ -31,6 +31,7 @@ SCHEMA = """
CREATE TABLE sessions
(
dc_id INTEGER PRIMARY KEY,
api_id INTEGER,
test_mode INTEGER,
auth_key BLOB,
date INTEGER NOT NULL,
@ -90,7 +91,7 @@ def get_input_peer(peer_id: int, access_hash: int, peer_type: str):
class SQLiteStorage(Storage):
VERSION = 2
VERSION = 3
USERNAME_TTL = 8 * 60 * 60
def __init__(self, name: str):
@ -109,8 +110,8 @@ class SQLiteStorage(Storage):
)
self.conn.execute(
"INSERT INTO sessions VALUES (?, ?, ?, ?, ?, ?)",
(2, None, None, 0, None, None)
"INSERT INTO sessions VALUES (?, ?, ?, ?, ?, ?, ?)",
(2, None, None, None, 0, None, None)
)
async def open(self):
@ -195,6 +196,9 @@ class SQLiteStorage(Storage):
async def dc_id(self, value: int = object):
return self._accessor(value)
async def api_id(self, value: int = object):
return self._accessor(value)
async def test_mode(self, value: bool = object):
return self._accessor(value)

View File

@ -59,6 +59,9 @@ class Storage:
async def dc_id(self, value: int = object):
raise NotImplementedError
async def api_id(self, value: int = object):
raise NotImplementedError
async def test_mode(self, value: bool = object):
raise NotImplementedError

View File

@ -31,7 +31,7 @@ class SentCode(Object):
Confirmation code identifier useful for the next authorization steps (either
:meth:`~pyrogram.Client.sign_in` or :meth:`~pyrogram.Client.sign_up`).
next_type (:obj:`~pyrogram.enums.SentCodeType`, *optional*):
next_type (:obj:`~pyrogram.enums.NextCodeType`, *optional*):
Type of the next code to be sent with :meth:`~pyrogram.Client.resend_code`.
timeout (``int``, *optional*):
@ -42,7 +42,7 @@ class SentCode(Object):
self, *,
type: "enums.SentCodeType",
phone_code_hash: str,
next_type: "enums.SentCodeType" = None,
next_type: "enums.NextCodeType" = None,
timeout: int = None
):
super().__init__()
@ -57,6 +57,6 @@ class SentCode(Object):
return SentCode(
type=enums.SentCodeType(type(sent_code.type)),
phone_code_hash=sent_code.phone_code_hash,
next_type=enums.SentCodeType(type(sent_code.next_type)) if sent_code.next_type else None,
next_type=enums.NextCodeType(type(sent_code.next_type)) if sent_code.next_type else None,
timeout=sent_code.timeout
)