mirror of
https://github.com/pyrogram/pyrogram
synced 2025-09-01 06:45:39 +00:00
Docs revamp. Part 2
This commit is contained in:
136
docs/source/topics/advanced-usage.rst
Normal file
136
docs/source/topics/advanced-usage.rst
Normal file
@@ -0,0 +1,136 @@
|
||||
Advanced Usage
|
||||
==============
|
||||
|
||||
Pyrogram's API, which consists of well documented convenience methods_ and facade types_, exists to provide a much
|
||||
easier interface to the undocumented and often confusing Telegram API.
|
||||
|
||||
In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main "raw"
|
||||
Telegram API with its functions and types.
|
||||
|
||||
Telegram Raw API
|
||||
----------------
|
||||
|
||||
If you can't find a high-level method for your needs or if you want complete, low-level access to the whole
|
||||
Telegram API, you have to use the raw :mod:`functions <pyrogram.api.functions>` and :mod:`types <pyrogram.api.types>`.
|
||||
|
||||
As already hinted, raw functions and types can be really confusing, mainly because people don't realize soon enough they
|
||||
accept *only* the right types and that all required parameters must be filled in. This section will therefore explain
|
||||
some pitfalls to take into consideration when working with the raw API.
|
||||
|
||||
.. hint::
|
||||
|
||||
Every available high-level methods in Pyrogram is built on top of these raw functions.
|
||||
|
||||
Nothing stops you from using the raw functions only, but they are rather complex and `plenty of them`_ are already
|
||||
re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API (yet much more
|
||||
powerful).
|
||||
|
||||
If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_!
|
||||
|
||||
Invoking Functions
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Unlike the methods_ found in Pyrogram's API, which can be called in the usual simple way, functions to be invoked from
|
||||
the raw Telegram API have a different way of usage and are more complex.
|
||||
|
||||
First of all, both `raw functions`_ and `raw types`_ live in their respective packages (and sub-packages):
|
||||
``pyrogram.api.functions``, ``pyrogram.api.types``. They all exist as Python classes, meaning you need to create an
|
||||
instance of each every time you need them and fill them in with the correct values using named arguments.
|
||||
|
||||
Next, to actually invoke the raw function you have to use the :meth:`send() <pyrogram.Client.send>` method provided by
|
||||
the Client class and pass the function object you created.
|
||||
|
||||
Here's some examples:
|
||||
|
||||
- Update first name, last name and bio:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.api import functions
|
||||
|
||||
with Client("my_account") as app:
|
||||
app.send(
|
||||
functions.account.UpdateProfile(
|
||||
first_name="Dan", last_name="Tès",
|
||||
about="Bio written from Pyrogram"
|
||||
)
|
||||
)
|
||||
|
||||
- Disable links to your account when someone forwards your messages:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.api import functions, types
|
||||
|
||||
with Client("my_account") as app:
|
||||
app.send(
|
||||
functions.account.SetPrivacy(
|
||||
key=types.PrivacyKeyForwards(),
|
||||
rules=[types.InputPrivacyValueDisallowAll()]
|
||||
)
|
||||
)
|
||||
|
||||
- Invite users to your channel/supergroup:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.api import functions, types
|
||||
|
||||
with Client("my_account") as app:
|
||||
app.send(
|
||||
functions.channels.InviteToChannel(
|
||||
channel=app.resolve_peer(123456789), # ID or Username
|
||||
users=[ # The users you want to invite
|
||||
app.resolve_peer(23456789), # By ID
|
||||
app.resolve_peer("username"), # By username
|
||||
app.resolve_peer("+393281234567"), # By phone number
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
Chat IDs
|
||||
^^^^^^^^
|
||||
|
||||
The way Telegram works makes it impossible to directly send a message to a user or a chat by using their IDs only.
|
||||
Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. Pyrogram allows
|
||||
sending messages with IDs only thanks to cached access hashes.
|
||||
|
||||
There are three different InputPeer types, one for each kind of Telegram entity.
|
||||
Whenever an InputPeer is needed you must pass one of these:
|
||||
|
||||
- `InputPeerUser <https://docs.pyrogram.ml/types/InputPeerUser>`_ - Users
|
||||
- `InputPeerChat <https://docs.pyrogram.ml/types/InputPeerChat>`_ - Basic Chats
|
||||
- `InputPeerChannel <https://docs.pyrogram.ml/types/InputPeerChannel>`_ - Either Channels or Supergroups
|
||||
|
||||
But you don't necessarily have to manually instantiate each object because, luckily for you, Pyrogram already provides
|
||||
:meth:`resolve_peer() <pyrogram.Client.resolve_peer>` as a convenience utility method that returns the correct InputPeer
|
||||
by accepting a peer ID only.
|
||||
|
||||
Another thing to take into consideration about chat IDs is the way they are represented: they are all integers and
|
||||
all positive within their respective raw types.
|
||||
|
||||
Things are different when working with Pyrogram's API because having them in the same space can theoretically lead to
|
||||
collisions, and that's why Pyrogram (as well as the official Bot API) uses a slightly different representation for each
|
||||
kind of ID.
|
||||
|
||||
For example, given the ID *123456789*, here's how Pyrogram can tell entities apart:
|
||||
|
||||
- ``+ID`` User: *123456789*
|
||||
- ``-ID`` Chat: *-123456789*
|
||||
- ``-100ID`` Channel (and Supergroup): *-100123456789*
|
||||
|
||||
So, every time you take a raw ID, make sure to translate it into the correct ID when you want to use it with an
|
||||
high-level method.
|
||||
|
||||
|
||||
|
||||
|
||||
.. _methods: ../pyrogram/Client.html#messages
|
||||
.. _types: ../pyrogram/Types.html
|
||||
.. _plenty of them: ../pyrogram/Client.html#messages
|
||||
.. _raw functions: ../pyrogram/functions
|
||||
.. _raw types: ../pyrogram/types
|
||||
.. _Community: https://t.me/PyrogramChat
|
68
docs/source/topics/auto-authorization.rst
Normal file
68
docs/source/topics/auto-authorization.rst
Normal file
@@ -0,0 +1,68 @@
|
||||
Auto Authorization
|
||||
==================
|
||||
|
||||
Manually writing phone number, phone code and password on the terminal every time you want to login can be tedious.
|
||||
Pyrogram is able to automate both **Log In** and **Sign Up** processes, all you need to do is pass the relevant
|
||||
parameters when creating a new :class:`Client <pyrogram.Client>`.
|
||||
|
||||
.. note:: If you omit any of the optional parameter required for the authorization, Pyrogram will ask you to
|
||||
manually write it. For instance, if you don't want to set a ``last_name`` when creating a new account you
|
||||
have to explicitly pass an empty string ""; the default value (None) will trigger the input() call.
|
||||
|
||||
Log In
|
||||
-------
|
||||
|
||||
To automate the **Log In** process, pass your ``phone_number`` and ``password`` (if you have one) in the Client parameters.
|
||||
If you want to retrieve the phone code programmatically, pass a callback function in the ``phone_code`` field — this
|
||||
function accepts a single positional argument (phone_number) and must return the correct phone code (e.g., "12345")
|
||||
— otherwise, ignore this parameter, Pyrogram will ask you to input the phone code manually.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
def phone_code_callback(phone_number):
|
||||
code = ... # Get your code programmatically
|
||||
return code # e.g., "12345"
|
||||
|
||||
|
||||
app = Client(
|
||||
session_name="example",
|
||||
phone_number="39**********",
|
||||
phone_code=phone_code_callback, # Note the missing parentheses
|
||||
password="password" # (if you have one)
|
||||
)
|
||||
|
||||
with app:
|
||||
print(app.get_me())
|
||||
|
||||
Sign Up
|
||||
-------
|
||||
|
||||
To automate the **Sign Up** process (i.e., automatically create a new Telegram account), simply fill **both**
|
||||
``first_name`` and ``last_name`` fields alongside the other parameters; they will be used to automatically create a new
|
||||
Telegram account in case the phone number you passed is not registered yet.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
def phone_code_callback(phone_number):
|
||||
code = ... # Get your code programmatically
|
||||
return code # e.g., "12345"
|
||||
|
||||
|
||||
app = Client(
|
||||
session_name="example",
|
||||
phone_number="39**********",
|
||||
phone_code=phone_code_callback, # Note the missing parentheses
|
||||
first_name="Pyrogram",
|
||||
last_name="" # Can be an empty string
|
||||
)
|
||||
|
||||
with app:
|
||||
print(app.get_me())
|
44
docs/source/topics/bots-interaction.rst
Normal file
44
docs/source/topics/bots-interaction.rst
Normal file
@@ -0,0 +1,44 @@
|
||||
Bots Interaction
|
||||
================
|
||||
|
||||
Users can interact with other bots via plain text messages as well as inline queries.
|
||||
|
||||
Inline Bots
|
||||
-----------
|
||||
|
||||
- If a bot accepts inline queries, you can call it by using
|
||||
:meth:`get_inline_bot_results() <pyrogram.Client.get_inline_bot_results>` to get the list of its inline results
|
||||
for a query:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Get bot results for "Fuzz Universe" from the inline bot @vid
|
||||
bot_results = app.get_inline_bot_results("vid", "Fuzz Universe")
|
||||
|
||||
.. figure:: https://i.imgur.com/IAqLs54.png
|
||||
:width: 90%
|
||||
:align: center
|
||||
:figwidth: 60%
|
||||
|
||||
``get_inline_bot_results()`` is the equivalent action of writing ``@vid Fuzz Universe`` and getting the
|
||||
results list.
|
||||
|
||||
- After you retrieved the bot results, you can use
|
||||
:meth:`send_inline_bot_result() <pyrogram.Client.send_inline_bot_result>` to send a chosen result to any chat:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Send the first result to your own chat
|
||||
app.send_inline_bot_result(
|
||||
"me",
|
||||
bot_results.query_id,
|
||||
bot_results.results[0].id
|
||||
)
|
||||
|
||||
.. figure:: https://i.imgur.com/wwxr7B7.png
|
||||
:width: 90%
|
||||
:align: center
|
||||
:figwidth: 60%
|
||||
|
||||
``send_inline_bot_result()`` is the equivalent action of choosing a result from the list and sending it
|
||||
to a chat.
|
11
docs/source/topics/changelog.rst
Normal file
11
docs/source/topics/changelog.rst
Normal file
@@ -0,0 +1,11 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Currently, all Pyrogram release notes live inside the GitHub repository web page:
|
||||
https://github.com/pyrogram/pyrogram/releases
|
||||
|
||||
(You will be automatically redirected in 10 seconds.)
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<meta http-equiv="refresh" content="10; URL=https://github.com/pyrogram/pyrogram/releases"/>
|
90
docs/source/topics/configuration-file.rst
Normal file
90
docs/source/topics/configuration-file.rst
Normal file
@@ -0,0 +1,90 @@
|
||||
Configuration File
|
||||
==================
|
||||
|
||||
As already mentioned in previous sections, Pyrogram can be configured by the use of an INI file.
|
||||
This page explains how this file is structured in Pyrogram, how to use it and why.
|
||||
|
||||
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 even 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 (e.g.: from environment variables).
|
||||
|
||||
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. <../start/Setup.html#configuration>`_
|
||||
|
||||
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. <SOCKS5Proxy.html>`_
|
||||
|
||||
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. <SmartPlugins.html>`_
|
66
docs/source/topics/customize-sessions.rst
Normal file
66
docs/source/topics/customize-sessions.rst
Normal file
@@ -0,0 +1,66 @@
|
||||
Customize Sessions
|
||||
==================
|
||||
|
||||
As you may probably know, Telegram allows users (and bots) having more than one session (authorizations) registered
|
||||
in the system at the same time.
|
||||
|
||||
Briefly explaining, sessions are simply new logins in your account. They can be reviewed in the settings of an official
|
||||
app (or by invoking `GetAuthorizations <../functions/account/GetAuthorizations.html>`_ with Pyrogram). They store some
|
||||
useful information such as the client who's using them and from which country and IP address.
|
||||
|
||||
|
||||
.. figure:: https://i.imgur.com/lzGPCdZ.png
|
||||
:width: 70%
|
||||
:align: center
|
||||
|
||||
**A Pyrogram session running on Linux, Python 3.6.**
|
||||
|
||||
That's how a session looks like on the Android app, showing the three main pieces of information.
|
||||
|
||||
- ``app_version``: **Pyrogram 🔥 0.7.5**
|
||||
- ``device_model``: **CPython 3.6.5**
|
||||
- ``system_version``: **Linux 4.15.0-23-generic**
|
||||
|
||||
Set Custom Values
|
||||
-----------------
|
||||
|
||||
To set custom values, you can either make use of the ``config.ini`` file, this way:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pyrogram]
|
||||
app_version = 1.2.3
|
||||
device_model = PC
|
||||
system_version = Linux
|
||||
|
||||
Or, pass the arguments directly in the Client's constructor.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = Client(
|
||||
"my_account",
|
||||
app_version="1.2.3",
|
||||
device_model="PC",
|
||||
system_version="Linux"
|
||||
)
|
||||
|
||||
Set Custom Languages
|
||||
--------------------
|
||||
|
||||
To tell Telegram in which language should speak to you (terms of service, bots, service messages, ...) you can
|
||||
set ``lang_code`` in `ISO 639-1 <https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes>`_ standard (defaults to "en",
|
||||
English).
|
||||
|
||||
With the following code we make Telegram know we want it to speak in Italian (it):
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pyrogram]
|
||||
lang_code = it
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = Client(
|
||||
"my_account",
|
||||
lang_code="it",
|
||||
)
|
59
docs/source/topics/error-handling.rst
Normal file
59
docs/source/topics/error-handling.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
Error Handling
|
||||
==============
|
||||
|
||||
Errors are inevitable when working with the API, and they must be correctly handled with ``try..except`` blocks.
|
||||
|
||||
There are many errors that Telegram could return, but they all fall in one of these categories
|
||||
(which are in turn children of the :obj:`RPCError <pyrogram.RPCError>` superclass):
|
||||
|
||||
- :obj:`303 - See Other <pyrogram.errors.SeeOther>`
|
||||
- :obj:`400 - Bad Request <pyrogram.errors.BadRequest>`
|
||||
- :obj:`401 - Unauthorized <pyrogram.errors.Unauthorized>`
|
||||
- :obj:`403 - Forbidden <pyrogram.errors.Forbidden>`
|
||||
- :obj:`406 - Not Acceptable <pyrogram.errors.NotAcceptable>`
|
||||
- :obj:`420 - Flood <pyrogram.errors.Flood>`
|
||||
- :obj:`500 - Internal Server Error <pyrogram.errors.InternalServerError>`
|
||||
|
||||
As stated above, there are really many (too many) errors, and in case Pyrogram does not know anything yet about a
|
||||
specific one, it raises a special :obj:`520 Unknown Error <pyrogram.errors.UnknownError>` exception and logs it
|
||||
in the ``unknown_errors.txt`` file. Users are invited to report these unknown errors; in later versions of Pyrogram
|
||||
some kind of automatic error reporting module might be implemented.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram.errors import (
|
||||
BadRequest, Flood, InternalServerError,
|
||||
SeeOther, Unauthorized, UnknownError
|
||||
)
|
||||
|
||||
try:
|
||||
...
|
||||
except BadRequest:
|
||||
pass
|
||||
except Flood:
|
||||
pass
|
||||
except InternalServerError:
|
||||
pass
|
||||
except SeeOther:
|
||||
pass
|
||||
except Unauthorized:
|
||||
pass
|
||||
except UnknownError:
|
||||
pass
|
||||
|
||||
Exception objects may also contain some informative values.
|
||||
E.g.: :obj:`FloodWait <pyrogram.errors.exceptions.flood_420.FloodWait>` holds the amount of seconds you have to wait
|
||||
before you can try again. The value is always stored in the ``x`` field of the returned exception object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import time
|
||||
from pyrogram.errors import FloodWait
|
||||
|
||||
try:
|
||||
...
|
||||
except FloodWait as e:
|
||||
time.sleep(e.x)
|
222
docs/source/topics/more-on-updates.rst
Normal file
222
docs/source/topics/more-on-updates.rst
Normal file
@@ -0,0 +1,222 @@
|
||||
More on Updates
|
||||
===============
|
||||
|
||||
Here we'll show some advanced usages when working with `update handlers`_ and `filters`_.
|
||||
|
||||
Handler Groups
|
||||
--------------
|
||||
|
||||
If you register handlers with overlapping (conflicting) filters, only the first one is executed and any other handler
|
||||
will be ignored. This is intended by design.
|
||||
|
||||
In order to handle the very same update more than once, you have to register your handler in a different dispatching
|
||||
group. Dispatching groups hold one or more handlers and are processed sequentially, they are identified by a number
|
||||
(number 0 being the default) and sorted, that is, a lower group number has a higher priority:
|
||||
|
||||
For example, take these two handlers:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 1, 6
|
||||
|
||||
@app.on_message(Filters.text | Filters.sticker)
|
||||
def text_or_sticker(client, message):
|
||||
print("Text or Sticker")
|
||||
|
||||
|
||||
@app.on_message(Filters.text)
|
||||
def just_text(client, message):
|
||||
print("Just Text")
|
||||
|
||||
Here, ``just_text`` is never executed because ``text_or_sticker``, which has been registered first, already handles
|
||||
texts (``Filters.text`` is shared and conflicting). To enable it, register the function using a different group:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.text, group=1)
|
||||
def just_text(client, message):
|
||||
print("Just Text")
|
||||
|
||||
Or, if you want ``just_text`` to be fired *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.text, group=-1)
|
||||
def just_text(client, message):
|
||||
print("Just Text")
|
||||
|
||||
With :meth:`add_handler() <pyrogram.Client.add_handler>` (without decorators) the same can be achieved with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.add_handler(MessageHandler(just_text, Filters.text), -1)
|
||||
|
||||
Update propagation
|
||||
------------------
|
||||
|
||||
Registering multiple handlers, each in a different group, becomes useful when you want to handle the same update more
|
||||
than once. Any incoming update will be sequentially processed by all of your registered functions by respecting the
|
||||
groups priority policy described above. Even in case any handler raises an unhandled exception, Pyrogram will still
|
||||
continue to propagate the same update to the next groups until all the handlers are done. Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(0)
|
||||
|
||||
|
||||
@app.on_message(Filters.private, group=1)
|
||||
def _(client, message):
|
||||
raise Exception("Unhandled exception!") # Simulate an unhandled exception
|
||||
|
||||
|
||||
@app.on_message(Filters.private, group=2)
|
||||
def _(client, message):
|
||||
print(2)
|
||||
|
||||
All these handlers will handle the same kind of messages, that are, messages sent or received in private chats.
|
||||
The output for each incoming update will therefore be:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
0
|
||||
Exception: Unhandled exception!
|
||||
2
|
||||
|
||||
Stop Propagation
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to prevent further propagation of an update in the dispatching phase, you can do *one* of the following:
|
||||
|
||||
- Call the update's bound-method ``.stop_propagation()`` (preferred way).
|
||||
- Manually ``raise StopPropagation`` exception (more suitable for raw updates only).
|
||||
|
||||
.. note::
|
||||
|
||||
Internally, the propagation is stopped by handling a custom exception. ``.stop_propagation()`` is just an elegant
|
||||
and intuitive way to ``raise StopPropagation``; this also means that any code coming *after* calling the method
|
||||
won't be executed as your function just raised an exception to signal the dispatcher not to propagate the
|
||||
update anymore.
|
||||
|
||||
Example with ``stop_propagation()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(0)
|
||||
|
||||
|
||||
@app.on_message(Filters.private, group=1)
|
||||
def _(client, message):
|
||||
print(1)
|
||||
message.stop_propagation()
|
||||
|
||||
|
||||
@app.on_message(Filters.private, group=2)
|
||||
def _(client, message):
|
||||
print(2)
|
||||
|
||||
Example with ``raise StopPropagation``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import StopPropagation
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(0)
|
||||
|
||||
|
||||
@app.on_message(Filters.private, group=1)
|
||||
def _(client, message):
|
||||
print(1)
|
||||
raise StopPropagation
|
||||
|
||||
|
||||
@app.on_message(Filters.private, group=2)
|
||||
def _(client, message):
|
||||
print(2)
|
||||
|
||||
Each handler is registered in a different group, but the handler in group number 2 will never be executed because the
|
||||
propagation was stopped earlier. The output of both (equivalent) examples will be:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
0
|
||||
1
|
||||
|
||||
Continue Propagation
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the
|
||||
`handler groups <#handler-groups>`_, you can signal the internal dispatcher to continue the update propagation within
|
||||
**the same group** despite having conflicting filters in the next registered handler. This allows you to register
|
||||
multiple handlers with overlapping filters in the same group; to let the dispatcher process the next handler you can do
|
||||
*one* of the following in each handler you want to grant permission to continue:
|
||||
|
||||
- Call the update's bound-method ``.continue_propagation()`` (preferred way).
|
||||
- Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only).
|
||||
|
||||
.. note::
|
||||
|
||||
Internally, the propagation is continued by handling a custom exception. ``.continue_propagation()`` is just an
|
||||
elegant and intuitive way to ``raise ContinuePropagation``; this also means that any code coming *after* calling the
|
||||
method won't be executed as your function just raised an exception to signal the dispatcher to continue with the
|
||||
next available handler.
|
||||
|
||||
|
||||
Example with ``continue_propagation()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(0)
|
||||
message.continue_propagation()
|
||||
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(1)
|
||||
message.continue_propagation()
|
||||
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(2)
|
||||
|
||||
Example with ``raise ContinuePropagation``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import ContinuePropagation
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(0)
|
||||
raise ContinuePropagation
|
||||
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(1)
|
||||
raise ContinuePropagation
|
||||
|
||||
|
||||
@app.on_message(Filters.private)
|
||||
def _(client, message):
|
||||
print(2)
|
||||
|
||||
Three handlers are registered in the same group, and all of them will be executed because the propagation was continued
|
||||
in each handler (except in the last one, where is useless to do so since there is no more handlers after).
|
||||
The output of both (equivalent) examples will be:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
|
||||
.. _`update handlers`: UpdateHandling.html
|
||||
.. _`filters`: UsingFilters.html
|
356
docs/source/topics/smart-plugins.rst
Normal file
356
docs/source/topics/smart-plugins.rst
Normal file
@@ -0,0 +1,356 @@
|
||||
Smart Plugins
|
||||
=============
|
||||
|
||||
Pyrogram embeds a **smart**, lightweight yet powerful plugin system that is meant to further simplify the organization
|
||||
of large projects and to provide a way for creating pluggable (modular) components that can be **easily shared** across
|
||||
different Pyrogram applications with **minimal boilerplate code**.
|
||||
|
||||
.. tip::
|
||||
|
||||
Smart Plugins are completely optional and disabled by default.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize
|
||||
your applications, you had to put your function definitions in separate files and register them inside your main script
|
||||
after importing your modules, like this:
|
||||
|
||||
.. note::
|
||||
|
||||
This is an example application that replies in private chats with two messages: one containing the same
|
||||
text message you sent and the other containing the reversed text message.
|
||||
|
||||
Example: *"Pyrogram"* replies with *"Pyrogram"* and *"margoryP"*
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
myproject/
|
||||
config.ini
|
||||
handlers.py
|
||||
main.py
|
||||
|
||||
- ``handlers.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def echo(client, message):
|
||||
message.reply(message.text)
|
||||
|
||||
|
||||
def echo_reversed(client, message):
|
||||
message.reply(message.text[::-1])
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client, MessageHandler, Filters
|
||||
|
||||
from handlers import echo, echo_reversed
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
app.add_handler(
|
||||
MessageHandler(
|
||||
echo,
|
||||
Filters.text & Filters.private))
|
||||
|
||||
app.add_handler(
|
||||
MessageHandler(
|
||||
echo_reversed,
|
||||
Filters.text & Filters.private),
|
||||
group=1)
|
||||
|
||||
app.run()
|
||||
|
||||
This is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to
|
||||
manually ``import``, manually :meth:`add_handler <pyrogram.Client.add_handler>` and manually instantiate each
|
||||
:obj:`MessageHandler <pyrogram.MessageHandler>` object because **you can't use those cool decorators** for your
|
||||
functions. So, what if you could? Smart Plugins solve this issue by taking care of handlers registration automatically.
|
||||
|
||||
Using Smart Plugins
|
||||
-------------------
|
||||
|
||||
Setting up your Pyrogram project to accommodate Smart Plugins is pretty straightforward:
|
||||
|
||||
#. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...).
|
||||
#. Put your python files full of plugins inside. Organize them as you wish.
|
||||
#. Enable plugins in your Client or via the *config.ini* file.
|
||||
|
||||
.. note::
|
||||
|
||||
This is the same example application `as shown above <#introduction>`_, written using the Smart Plugin system.
|
||||
|
||||
.. code-block:: text
|
||||
:emphasize-lines: 2, 3
|
||||
|
||||
myproject/
|
||||
plugins/
|
||||
handlers.py
|
||||
config.ini
|
||||
main.py
|
||||
|
||||
- ``plugins/handlers.py``
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 4, 9
|
||||
|
||||
from pyrogram import Client, Filters
|
||||
|
||||
|
||||
@Client.on_message(Filters.text & Filters.private)
|
||||
def echo(client, message):
|
||||
message.reply(message.text)
|
||||
|
||||
|
||||
@Client.on_message(Filters.text & Filters.private, group=1)
|
||||
def echo_reversed(client, message):
|
||||
message.reply(message.text[::-1])
|
||||
|
||||
- ``config.ini``
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[plugins]
|
||||
root = plugins
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
Client("my_account").run()
|
||||
|
||||
Alternatively, without using the *config.ini* file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
plugins = dict(root="plugins")
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
|
||||
The first important thing to note is the new ``plugins`` folder. You can put *any python file* in *any subfolder* and
|
||||
each file can contain *any decorated function* (handlers) with one limitation: within a single module (file) you must
|
||||
use different names for each decorated function.
|
||||
|
||||
The second thing is telling Pyrogram where to look for your plugins: you can either use the *config.ini* file or
|
||||
the Client parameter "plugins"; the *root* value must match the name of your plugins root folder. Your Pyrogram Client
|
||||
instance will **automatically** scan the folder upon starting to search for valid handlers and register them for you.
|
||||
|
||||
Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback
|
||||
functions in a static way, i.e. **without having the Client instance around**: simply use ``@Client`` (Client class)
|
||||
instead of the usual ``@app`` (Client instance) and things will work just the same.
|
||||
|
||||
Specifying the Plugins to include
|
||||
---------------------------------
|
||||
|
||||
By default, if you don't explicitly supply a list of plugins, every valid one found inside your plugins root folder will
|
||||
be included by following the alphabetical order of the directory structure (files and subfolders); the single handlers
|
||||
found inside each module will be, instead, loaded in the order they are defined, from top to bottom.
|
||||
|
||||
.. note::
|
||||
|
||||
Remember: there can be at most one handler, within a group, dealing with a specific update. Plugins with overlapping
|
||||
filters included a second time will not work. Learn more at `More on Updates <MoreOnUpdates.html>`_.
|
||||
|
||||
This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or
|
||||
exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude``
|
||||
directives, either in the *config.ini* file or in the dictionary passed as Client argument. Here's how they work:
|
||||
|
||||
- If both ``include`` and ``exclude`` are omitted, all plugins are loaded as described above.
|
||||
- If ``include`` is given, only the specified plugins will be loaded, in the order they are passed.
|
||||
- If ``exclude`` is given, the plugins specified here will be unloaded.
|
||||
|
||||
The ``include`` and ``exclude`` value is a **list of strings**. Each string containing the path of the module relative
|
||||
to the plugins root folder, in Python notation (dots instead of slashes).
|
||||
|
||||
E.g.: ``subfolder.module`` refers to ``plugins/subfolder/module.py``, with ``root="plugins"``.
|
||||
|
||||
You can also choose the order in which the single handlers inside a module are loaded, thus overriding the default
|
||||
top-to-bottom loading policy. You can do this by appending the name of the functions to the module path, each one
|
||||
separated by a blank space.
|
||||
|
||||
E.g.: ``subfolder.module fn2 fn1 fn3`` will load *fn2*, *fn1* and *fn3* from *subfolder.module*, in this order.
|
||||
|
||||
Examples
|
||||
^^^^^^^^
|
||||
|
||||
Given this plugins folder structure with three modules, each containing their own handlers (fn1, fn2, etc...), which are
|
||||
also organized in subfolders:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
myproject/
|
||||
plugins/
|
||||
subfolder1/
|
||||
plugins1.py
|
||||
- fn1
|
||||
- fn2
|
||||
- fn3
|
||||
subfolder2/
|
||||
plugins2.py
|
||||
...
|
||||
plugins0.py
|
||||
...
|
||||
...
|
||||
|
||||
- Load every handler from every module, namely *plugins0.py*, *plugins1.py* and *plugins2.py* in alphabetical order
|
||||
(files) and definition order (handlers inside files):
|
||||
|
||||
Using *config.ini* file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[plugins]
|
||||
root = plugins
|
||||
|
||||
Using *Client*'s parameter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(root="plugins")
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
- Load only handlers defined inside *plugins2.py* and *plugins0.py*, in this order:
|
||||
|
||||
Using *config.ini* file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[plugins]
|
||||
root = plugins
|
||||
include =
|
||||
subfolder2.plugins2
|
||||
plugins0
|
||||
|
||||
Using *Client*'s parameter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(
|
||||
root="plugins",
|
||||
include=[
|
||||
"subfolder2.plugins2",
|
||||
"plugins0"
|
||||
]
|
||||
)
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
- Load everything except the handlers inside *plugins2.py*:
|
||||
|
||||
Using *config.ini* file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[plugins]
|
||||
root = plugins
|
||||
exclude = subfolder2.plugins2
|
||||
|
||||
Using *Client*'s parameter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(
|
||||
root="plugins",
|
||||
exclude=["subfolder2.plugins2"]
|
||||
)
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
- Load only *fn3*, *fn1* and *fn2* (in this order) from *plugins1.py*:
|
||||
|
||||
Using *config.ini* file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[plugins]
|
||||
root = plugins
|
||||
include = subfolder1.plugins1 fn3 fn1 fn2
|
||||
|
||||
Using *Client*'s parameter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(
|
||||
root="plugins",
|
||||
include=["subfolder1.plugins1 fn3 fn1 fn2"]
|
||||
)
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
Load/Unload Plugins at Runtime
|
||||
------------------------------
|
||||
|
||||
In the `previous section <#specifying-the-plugins-to-include>`_ we've explained how to specify which plugins to load and
|
||||
which to ignore before your Client starts. Here we'll show, instead, how to unload and load again a previously
|
||||
registered plugin at runtime.
|
||||
|
||||
Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram updates
|
||||
) will be modified in such a way that, when you reference them later on, they will be actually pointing to a tuple of
|
||||
*(handler: Handler, group: int)*. The actual callback function is therefore stored inside the handler's *callback*
|
||||
attribute. Here's an example:
|
||||
|
||||
- ``plugins/handlers.py``
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 5, 6
|
||||
|
||||
@Client.on_message(Filters.text & Filters.private)
|
||||
def echo(client, message):
|
||||
message.reply(message.text)
|
||||
|
||||
print(echo)
|
||||
print(echo[0].callback)
|
||||
|
||||
- Printing ``echo`` will show something like ``(<MessageHandler object at 0x10e3abc50>, 0)``.
|
||||
|
||||
- Printing ``echo[0].callback``, that is, the *callback* attribute of the first element of the tuple, which is an
|
||||
Handler, will reveal the actual callback ``<function echo at 0x10e3b6598>``.
|
||||
|
||||
Unloading
|
||||
^^^^^^^^^
|
||||
|
||||
In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it by importing the
|
||||
relevant module and call :meth:`remove_handler() <pyrogram.Client.remove_handler>` Client's method with your function
|
||||
name preceded by the star ``*`` operator as argument. Example:
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from plugins.handlers import echo
|
||||
|
||||
...
|
||||
|
||||
app.remove_handler(*echo)
|
||||
|
||||
The star ``*`` operator is used to unpack the tuple into positional arguments so that *remove_handler* will receive
|
||||
exactly what is needed. The same could have been achieved with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
handler, group = echo
|
||||
app.remove_handler(handler, group)
|
||||
|
||||
Loading
|
||||
^^^^^^^
|
||||
|
||||
Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time
|
||||
using :meth:`add_handler() <pyrogram.Client.add_handler>` instead. Example:
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from plugins.handlers import echo
|
||||
|
||||
...
|
||||
|
||||
app.add_handler(*echo)
|
50
docs/source/topics/socks5-proxy.rst
Normal file
50
docs/source/topics/socks5-proxy.rst
Normal file
@@ -0,0 +1,50 @@
|
||||
SOCKS5 Proxy
|
||||
============
|
||||
|
||||
Pyrogram supports proxies with and without authentication. This feature allows Pyrogram to exchange data with Telegram
|
||||
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:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[proxy]
|
||||
enabled = True
|
||||
hostname = 11.22.33.44
|
||||
port = 1080
|
||||
username = <your_username>
|
||||
password = <your_password>
|
||||
|
||||
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.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.
|
39
docs/source/topics/test-servers.rst
Normal file
39
docs/source/topics/test-servers.rst
Normal file
@@ -0,0 +1,39 @@
|
||||
Test Servers
|
||||
============
|
||||
|
||||
If you wish to test your application in a separate environment, Pyrogram is able to authorize your account into
|
||||
Telegram's test servers without hassle. All you need to do is start a new session (e.g.: "my_account_test") using
|
||||
``test_mode=True``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
with Client("my_account_test", test_mode=True) as app:
|
||||
print(app.get_me())
|
||||
|
||||
.. note::
|
||||
|
||||
If this is the first time you login into test servers, you will be asked to register your account first.
|
||||
Don't worry about your contacts and chats, they will be kept untouched inside the production environment;
|
||||
accounts authorized on test servers reside in a different, parallel instance of a Telegram database.
|
||||
|
||||
Test Mode in Official Apps
|
||||
--------------------------
|
||||
|
||||
You can also login yourself into test servers using official desktop apps, such as Webogram and TDesktop:
|
||||
|
||||
- **Webogram**: Login here: https://web.telegram.org/?test=1
|
||||
- **TDesktop**: Open settings and type ``testmode``.
|
||||
|
||||
Test Numbers
|
||||
------------
|
||||
|
||||
Beside normal numbers, the test environment allows you to login with reserved test numbers.
|
||||
Valid phone numbers follow the pattern ``99966XYYYY``, where ``X`` is the DC number (1 to 3) and ``YYYY`` are random
|
||||
numbers. Users with such numbers always get ``XXXXX`` as the confirmation code (the DC number, repeated five times).
|
||||
|
||||
.. important::
|
||||
|
||||
Do not store any important or private information in such test users' accounts; anyone can make use of the
|
||||
simplified authorization mechanism and login at any time.
|
97
docs/source/topics/text-formatting.rst
Normal file
97
docs/source/topics/text-formatting.rst
Normal file
@@ -0,0 +1,97 @@
|
||||
Text Formatting
|
||||
===============
|
||||
|
||||
Pyrogram, just like the `Telegram Bot API`_, natively supports basic Markdown and HTML formatting styles for text
|
||||
messages and media captions.
|
||||
|
||||
Markdown style uses the same syntax as Telegram Desktop's and is enabled by default.
|
||||
|
||||
Beside bold, italic, and pre-formatted code, **Pyrogram does also support inline URLs and inline mentions of users**.
|
||||
|
||||
Markdown Style
|
||||
--------------
|
||||
|
||||
To use this mode, pass :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or "markdown" in the *parse_mode* field when using
|
||||
:obj:`send_message() <pyrogram.Client.send_message>`. Use the following syntax in your message:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
**bold text**
|
||||
|
||||
__italic text__
|
||||
|
||||
[inline URL](https://docs.pyrogram.ml/)
|
||||
|
||||
[inline mention of a user](tg://user?id=23122162)
|
||||
|
||||
`inline fixed-width code`
|
||||
|
||||
```block_language
|
||||
pre-formatted fixed-width code block
|
||||
```
|
||||
|
||||
|
||||
HTML Style
|
||||
----------
|
||||
|
||||
To use this mode, pass :obj:`HTML <pyrogram.ParseMode.HTML>` or "html" in the *parse_mode* field when using
|
||||
:obj:`send_message() <pyrogram.Client.send_message>`. The following tags are currently supported:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
<b>bold</b>, <strong>bold</strong>
|
||||
|
||||
<i>italic</i>, <em>italic</em>
|
||||
|
||||
<a href="http://docs.pyrogram.ml/">inline URL</a>
|
||||
|
||||
<a href="tg://user?id=23122162">inline mention of a user</a>
|
||||
|
||||
<code>inline fixed-width code</code>
|
||||
|
||||
<pre>pre-formatted fixed-width code block</pre>
|
||||
|
||||
.. note:: Mentions are only guaranteed to work if you have already met the user (in groups or private chats).
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
- Markdown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.send_message(
|
||||
chat_id="haskell",
|
||||
text=(
|
||||
"**bold**, "
|
||||
"__italic__, "
|
||||
"[mention](tg://user?id=23122162), "
|
||||
"[URL](https://docs.pyrogram.ml), "
|
||||
"`code`, "
|
||||
"```"
|
||||
"for i in range(10):\n"
|
||||
" print(i)```"
|
||||
)
|
||||
)
|
||||
|
||||
- HTML:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.send_message(
|
||||
chat_id="haskell",
|
||||
text=(
|
||||
"<b>bold</b>, "
|
||||
"<i>italic</i>, "
|
||||
"<a href=\"tg://user?id=23122162\">mention</a>, "
|
||||
"<a href=\"https://pyrogram.ml/\">URL</a>, "
|
||||
"<code>code</code>, "
|
||||
"<pre>"
|
||||
"for i in range(10):\n"
|
||||
" print(i)"
|
||||
"</pre>"
|
||||
),
|
||||
parse_mode="html"
|
||||
)
|
||||
|
||||
.. _Telegram Bot API: https://core.telegram.org/bots/api#formatting-options
|
32
docs/source/topics/tgcrypto.rst
Normal file
32
docs/source/topics/tgcrypto.rst
Normal file
@@ -0,0 +1,32 @@
|
||||
Fast Crypto
|
||||
===========
|
||||
|
||||
Pyrogram's speed can be *dramatically* boosted up by TgCrypto_, a high-performance, easy-to-install Telegram Crypto
|
||||
Library specifically written in C for Pyrogram [#f1]_ as a Python extension.
|
||||
|
||||
TgCrypto is a replacement for the much slower PyAES and implements the crypto algorithms Telegram requires, namely
|
||||
**AES-IGE 256 bit** (used in MTProto v2.0) and **AES-CTR 256 bit** (used for CDN encrypted files).
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip3 install --upgrade tgcrypto
|
||||
|
||||
.. note:: Being a C extension for Python, TgCrypto is an optional but *highly recommended* dependency; when TgCrypto is
|
||||
not detected in your system, Pyrogram will automatically fall back to PyAES and will show you a warning.
|
||||
|
||||
The reason about being an optional package is that TgCrypto requires some extra system tools in order to be compiled.
|
||||
The errors you receive when trying to install TgCrypto are system dependent, but also descriptive enough to understand
|
||||
what you should do next:
|
||||
|
||||
- **Windows**: Install `Visual C++ 2015 Build Tools <http://landinghub.visualstudio.com/visual-cpp-build-tools>`_.
|
||||
- **macOS**: A pop-up will automatically ask you to install the command line developer tools.
|
||||
- **Linux**: Install a proper C compiler (``gcc``, ``clang``) and the Python header files (``python3-dev``).
|
||||
- **Termux (Android)**: Install ``clang`` and ``python-dev`` packages.
|
||||
|
||||
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
|
||||
|
||||
.. [#f1] Although TgCrypto is intended for Pyrogram, it is shipped as a standalone package and can thus be used for
|
||||
other Python projects too.
|
109
docs/source/topics/update-handling.rst
Normal file
109
docs/source/topics/update-handling.rst
Normal file
@@ -0,0 +1,109 @@
|
||||
Update Handling
|
||||
===============
|
||||
|
||||
Calling `API methods`_ sequentially is cool, but how to react when, for example, a new message arrives? This page deals
|
||||
with updates and how to handle them in Pyrogram. Let's have a look at how they work.
|
||||
|
||||
First, let's define what are these updates. Updates are simply events that happen in your Telegram account (incoming
|
||||
messages, new members join, button presses, etc...), which are meant to notify you about a new specific state that
|
||||
changed. These updates are handled by registering one or more callback functions in your app using
|
||||
`Handlers <../pyrogram/Handlers.html>`_.
|
||||
|
||||
Each handler deals with a specific event and once a matching update arrives from Telegram, your registered callback
|
||||
function will be called and its body executed.
|
||||
|
||||
Registering an Handler
|
||||
----------------------
|
||||
|
||||
To explain how handlers work let's have a look at the most used one, the
|
||||
:obj:`MessageHandler <pyrogram.MessageHandler>`, which will be in charge for handling :obj:`Message <pyrogram.Message>`
|
||||
updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles
|
||||
settings them up once you learn from this section.
|
||||
|
||||
Using add_handler()
|
||||
-------------------
|
||||
|
||||
The :meth:`add_handler() <pyrogram.Client.add_handler>` method takes any handler instance that wraps around your defined
|
||||
callback function and registers it in your Client. Here's a full example that prints out the content of a message as
|
||||
soon as it arrives:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client, MessageHandler
|
||||
|
||||
|
||||
def my_function(client, message):
|
||||
print(message)
|
||||
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
my_handler = MessageHandler(my_function)
|
||||
app.add_handler(my_handler)
|
||||
|
||||
app.run()
|
||||
|
||||
Let's examine these four new pieces. First one: a callback function we defined which accepts two arguments -
|
||||
*(client, message)*. This will be the function that gets executed every time a new message arrives and Pyrogram will
|
||||
call that function by passing the client instance and the new message instance as argument.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def my_function(client, message):
|
||||
print(message)
|
||||
|
||||
Second one: the :obj:`MessageHandler <pyrogram.MessageHandler>`. This object tells Pyrogram the function we defined
|
||||
above must only handle updates that are in form of a :obj:`Message <pyrogram.Message>`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
my_handler = MessageHandler(my_function)
|
||||
|
||||
Third: the method :meth:`add_handler() <pyrogram.Client.add_handler>`. This method is used to actually register the
|
||||
handler and let Pyrogram know it needs to be taken into consideration when new updates arrive and the dispatching phase
|
||||
begins.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.add_handler(my_handler)
|
||||
|
||||
Last one, the :meth:`run() <pyrogram.Client.run>` method. What this does is simply calling
|
||||
:meth:`start() <pyrogram.Client.start>` and a special method :meth:`idle() <pyrogram.Client.idle>` that keeps your main
|
||||
scripts alive until you press ``CTRL+C``; the client will be automatically stopped after that.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.run()
|
||||
|
||||
Using Decorators
|
||||
----------------
|
||||
|
||||
All of the above will become quite verbose, especially in case you have lots of handlers to register. A much nicer way
|
||||
to do so is by decorating your callback function with the :meth:`on_message() <pyrogram.Client.on_message>` decorator.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
@app.on_message()
|
||||
def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
|
||||
app.run()
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Due to how these decorators work in Pyrogram, they will wrap your defined callback function in a tuple consisting of
|
||||
``(handler, group)``; this will be the value held by your function identifier (e.g.: *my_function* from the example
|
||||
above).
|
||||
|
||||
In case, for some reason, you want to get your own function back after it has been decorated, you need to access
|
||||
``my_function[0].callback``, that is, the *callback* field of the *handler* object which is the first element in the
|
||||
tuple, accessed by bracket notation *[0]*.
|
||||
|
||||
.. _API methods: usage.html
|
85
docs/source/topics/usage.rst
Normal file
85
docs/source/topics/usage.rst
Normal file
@@ -0,0 +1,85 @@
|
||||
API Usage
|
||||
=========
|
||||
|
||||
At this point, we have successfully `installed Pyrogram`_ and authorized_ our account and we are now pointing towards
|
||||
the core of the library. It's time to start playing with the API!
|
||||
|
||||
Make API Method Calls
|
||||
---------------------
|
||||
|
||||
Making API method calls with Pyrogram is very simple.
|
||||
Here's an example we are going to examine:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
app.start()
|
||||
|
||||
print(app.get_me())
|
||||
app.send_message("me", "Hi, it's me!")
|
||||
app.send_location("me", 51.500729, -0.124583)
|
||||
app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
|
||||
|
||||
app.stop()
|
||||
|
||||
Let's begin by importing the Client class from the Pyrogram package:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
Now instantiate a new Client object, "my_account" is a session name of your choice:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
To actually make use of any method, the client has to be started:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.start()
|
||||
|
||||
Now, you can call any method you like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
print(app.get_me()) # Print information about yourself
|
||||
|
||||
# Send messages to yourself:
|
||||
app.send_message("me", "Hi!") # Text message
|
||||
app.send_location("me", 51.500729, -0.124583) # Location
|
||||
app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") # Sticker
|
||||
|
||||
Finally, when done, simply stop the client:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.stop()
|
||||
|
||||
Context Manager
|
||||
---------------
|
||||
|
||||
You can also use Pyrogram's Client in a context manager with the ``with`` statement. The client will automatically
|
||||
:meth:`start <pyrogram.Client.start>` and :meth:`stop <pyrogram.Client.stop>` gracefully, even in case of unhandled
|
||||
exceptions in your code. The example above can be therefore rewritten in a much nicer way, this way:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
with app:
|
||||
print(app.get_me())
|
||||
app.send_message("me", "Hi there! I'm using **Pyrogram**")
|
||||
app.send_location("me", 51.500729, -0.124583)
|
||||
app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
|
||||
|
||||
More examples can be found on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_.
|
||||
|
||||
.. _installed Pyrogram: ../intro/install.html
|
||||
.. _authorized: ../intro/setup.html
|
194
docs/source/topics/using-filters.rst
Normal file
194
docs/source/topics/using-filters.rst
Normal file
@@ -0,0 +1,194 @@
|
||||
Using Filters
|
||||
=============
|
||||
|
||||
So far we've seen how to register a callback function that executes every time a specific update comes from the server,
|
||||
but there's much more than that to come.
|
||||
|
||||
Here we'll discuss about :class:`Filters <pyrogram.Filters>`. Filters enable a fine-grain control over what kind of
|
||||
updates are allowed or not to be passed in your callback functions, based on their inner details.
|
||||
|
||||
Let's start right away with a simple example:
|
||||
|
||||
- This example will show you how to **only** handle messages containing an :obj:`Audio <pyrogram.Audio>` object and
|
||||
ignore any other message. Filters are passed as the first argument of the decorator:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 4
|
||||
|
||||
from pyrogram import Filters
|
||||
|
||||
|
||||
@app.on_message(Filters.audio)
|
||||
def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
- or, without decorators. Here filters are passed as the second argument of the handler constructor:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 8
|
||||
|
||||
from pyrogram import Filters, MessageHandler
|
||||
|
||||
|
||||
def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
|
||||
app.add_handler(MessageHandler(my_handler, Filters.audio))
|
||||
|
||||
Combining Filters
|
||||
-----------------
|
||||
|
||||
Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise
|
||||
operators ``~``, ``&`` and ``|``:
|
||||
|
||||
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
|
||||
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
|
||||
|
||||
Here are some examples:
|
||||
|
||||
- Message is a **text** message **and** is **not edited**.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.text & ~Filters.edited)
|
||||
def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.sticker & (Filters.channel | Filters.private))
|
||||
def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
Advanced Filters
|
||||
----------------
|
||||
|
||||
Some filters, like :meth:`command() <pyrogram.Filters.command>` or :meth:`regex() <pyrogram.Filters.regex>`
|
||||
can also accept arguments:
|
||||
|
||||
- Message is either a */start* or */help* **command**.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.command(["start", "help"]))
|
||||
def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
- Message is a **text** message or a media **caption** matching the given **regex** pattern.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.regex("pyrogram"))
|
||||
def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
More handlers using different filters can also live together.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(Filters.command("start"))
|
||||
def start_command(client, message):
|
||||
print("This is the /start command")
|
||||
|
||||
|
||||
@app.on_message(Filters.command("help"))
|
||||
def help_command(client, message):
|
||||
print("This is the /help command")
|
||||
|
||||
|
||||
@app.on_message(Filters.chat("PyrogramChat"))
|
||||
def from_pyrogramchat(client, message):
|
||||
print("New message in @PyrogramChat")
|
||||
|
||||
Custom Filters
|
||||
--------------
|
||||
|
||||
Pyrogram already provides lots of built-in :class:`Filters <pyrogram.Filters>` to work with, but in case you can't find
|
||||
a specific one for your needs or want to build a custom filter by yourself (to be used in a different kind of handler,
|
||||
for example) you can use :meth:`Filters.create() <pyrogram.Filters.create>`.
|
||||
|
||||
.. note::
|
||||
At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`
|
||||
only.
|
||||
|
||||
An example to demonstrate how custom filters work is to show how to create and use one for the
|
||||
:obj:`CallbackQueryHandler <pyrogram.CallbackQueryHandler>`. Note that callback queries updates are only received by
|
||||
bots; create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline
|
||||
keyboard to yourself. This allows you to test your filter by pressing the inline button:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
app.send_message(
|
||||
"username", # Change this to your username or id
|
||||
"Pyrogram's custom filter test",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[[InlineKeyboardButton("Press me", b"pyrogram")]]
|
||||
)
|
||||
)
|
||||
|
||||
Basic Filters
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
For this basic filter we will be using only the first two parameters of :meth:`Filters.create() <pyrogram.Filters.create>`.
|
||||
|
||||
The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries
|
||||
containing "Pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data
|
||||
equals to ``b"Pyrogram"``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
static_data = Filters.create(
|
||||
name="StaticdData",
|
||||
func=lambda flt, callback_query: callback_query.data == b"Pyrogram"
|
||||
)
|
||||
|
||||
The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same
|
||||
could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def func(flt, callback_query):
|
||||
return callback_query.data == b"Pyrogram"
|
||||
|
||||
static_data = Filters.create(
|
||||
name="StaticData",
|
||||
func=func
|
||||
)
|
||||
|
||||
The filter usage remains the same:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_callback_query(static_data)
|
||||
def pyrogram_data(client, callback_query):
|
||||
client.answer_callback_query(callback_query.id, "it works!")
|
||||
|
||||
Filters with Arguments
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A much cooler filter would be one that accepts "Pyrogram" or any other data as argument at usage time.
|
||||
A dynamic filter like this will make use of the third parameter of :meth:`Filters.create() <pyrogram.Filters.create>`.
|
||||
|
||||
This is how a dynamic custom filter looks like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def dynamic_data(data):
|
||||
return Filters.create(
|
||||
name="DynamicData",
|
||||
func=lambda flt, callback_query: flt.data == callback_query.data,
|
||||
data=data # "data" kwarg is accessed with "filter.data"
|
||||
)
|
||||
|
||||
And its usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_callback_query(dynamic_data(b"Pyrogram"))
|
||||
def pyrogram_data(client, callback_query):
|
||||
client.answer_callback_query(callback_query.id, "it works!")
|
10
docs/source/topics/voice-calls.rst
Normal file
10
docs/source/topics/voice-calls.rst
Normal file
@@ -0,0 +1,10 @@
|
||||
Voice Calls
|
||||
===========
|
||||
|
||||
A working proof-of-concept of Telegram voice calls using Pyrogram can be found here:
|
||||
https://github.com/bakatrouble/pylibtgvoip. Thanks to `@bakatrouble <https://t.me/bakatrouble>`_.
|
||||
|
||||
.. note::
|
||||
|
||||
This page will be updated with more information once voice calls become eventually more usable and more integrated
|
||||
in Pyrogram itself.
|
Reference in New Issue
Block a user