2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-31 14:25:55 +00:00

Merge branch 'develop' into layer-95

# Conflicts:
#	pyrogram/__init__.py
This commit is contained in:
Dan
2019-03-01 18:38:27 +01:00
48 changed files with 3440 additions and 279 deletions

View File

@@ -10,27 +10,28 @@ Welcome to Pyrogram
</div>
<p align="center">
<b>Telegram MTProto API Client Library for Python</b>
<b>Telegram MTProto API Framework for Python</b>
<br>
<a href="https://github.com/pyrogram/pyrogram/releases/latest">
Download
<a href="https://docs.pyrogram.ml">
Documentation
</a>
<a href="https://github.com/pyrogram/pyrogram">
Source code
<a href="https://github.com/pyrogram/pyrogram/releases">
Changelog
</a>
<a href="https://t.me/PyrogramChat">
Community
</a>
<br>
<a href="https://github.com/pyrogram/pyrogram/blob/master/compiler/api/source/main_api.tl">
<a href="compiler/api/source/main_api.tl">
<img src="https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30"
alt="Scheme Layer">
alt="Schema Layer">
</a>
<a href="https://github.com/pyrogram/tgcrypto">
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
alt="TgCrypto">
alt="TgCrypto Version">
</a>
</p>
@@ -48,25 +49,27 @@ Welcome to Pyrogram
app.run()
Welcome to Pyrogram's Documentation! Here you can find resources for learning how to use the library.
Welcome to Pyrogram's Documentation! Here you can find resources for learning how to use the framework.
Contents are organized into self-contained topics and can be accessed from the sidebar, or by following them in order
using the Next button at the end of each page. But first, here's a brief overview of what is this all about.
About
-----
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for
building custom Telegram applications that interact with the MTProto API as both User and Bot.
**Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and C.
It enables you to easily create custom apps using both user and bot identities (bot API alternative) via the `MTProto API`_.
Features
--------
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
- **Easy**: You can install Pyrogram with pip and start building your applications right away.
- **Elegant**: Low-level details are abstracted and re-presented in a much nicer and easier way.
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Updated** to the latest Telegram API version, currently Layer 91 on top of MTProto 2.0.
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
- **Documented**: Pyrogram API methods, types and public interfaces are well documented.
- **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted.
- **Updated**, to the latest Telegram API version, currently Layer 91 on top of `MTProto 2.0`_.
- **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code.
- **Comprehensive**: Execute any advanced action an official client is able to do, and even more.
To get started, press the Next button.
@@ -85,6 +88,7 @@ To get started, press the Next button.
resources/UpdateHandling
resources/UsingFilters
resources/MoreOnUpdates
resources/ConfigurationFile
resources/SmartPlugins
resources/AutoAuthorization
resources/CustomizeSessions
@@ -95,6 +99,7 @@ To get started, press the Next button.
resources/ErrorHandling
resources/TestServers
resources/AdvancedUsage
resources/VoiceCalls
resources/Changelog
.. toctree::
@@ -111,4 +116,6 @@ To get started, press the Next button.
types/index
.. _`Telegram`: https://telegram.org/
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto/
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto/
.. _`MTProto API`: https://core.telegram.org/api#telegram-api
.. _`MTProto 2.0`: https://core.telegram.org/mtproto

View File

@@ -0,0 +1,90 @@
Configuration File
==================
As already mentioned in previous sections, Pyrogram can also 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 settings (private) 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>`_

View File

@@ -91,13 +91,14 @@ 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`` error (more suitable for raw updates only).
- Manually ``raise StopPropagation`` exception (more suitable for raw updates only).
.. note::
Note that ``.stop_propagation()`` is just an elegant and intuitive way to raise a ``StopPropagation`` error;
this means that any code coming *after* calling it won't be executed as your function just raised a custom exception
to signal the dispatcher not to propagate the update anymore.
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()``:
@@ -139,10 +140,82 @@ Example with ``raise StopPropagation``:
def _(client, message):
print(2)
The handler in group number 2 will never be executed because the propagation was stopped before. The output of both
examples will be:
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 group regardless of the next handler's filters. 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

View File

@@ -1,9 +1,9 @@
Smart Plugins
=============
Pyrogram embeds a **smart** (automatic) and lightweight plugin system that is meant to further simplify the organization
of large projects and to provide a way for creating pluggable components that can be **easily shared** across different
Pyrogram applications with **minimal boilerplate code**.
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::
@@ -13,7 +13,8 @@ Introduction
------------
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize
your applications, you had to do something like this...
your applications, you had to put your function definitions in separate files and register them inside your main script,
like this:
.. note::
@@ -63,19 +64,19 @@ your applications, you had to do something like this...
app.run()
...which is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to
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?
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:
Setting up your Pyrogram project to accommodate Smart Plugins is straightforward:
#. Create a new folder to store all the plugins (e.g.: "plugins").
#. Put your files full of plugins inside.
#. Enable plugins in your Client.
#. 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::
@@ -107,20 +108,252 @@ Setting up your Pyrogram project to accommodate Smart Plugins is pretty straight
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", plugins_dir="plugins").run()
Client("my_account").run()
The first important thing to note is the new ``plugins`` folder, whose name is passed to the the ``plugins_dir``
parameter when creating a :obj:`Client <pyrogram.Client>` in the ``main.py`` file — you can put *any python file* in
there and each file can contain *any decorated function* (handlers) with only one limitation: within a single plugin
file you must use different names for each decorated function. Your Pyrogram Client instance will **automatically**
scan the folder upon creation to search for valid handlers and register them for you.
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 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) namespace and things will work just the same.
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``
keys, 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 plugins 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 eleent 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)

View 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.

View File

@@ -82,7 +82,7 @@ If no error shows up you are good to go.
>>> import pyrogram
>>> pyrogram.__version__
'0.10.3'
'0.11.0'
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
.. _develop: http://github.com/pyrogram/pyrogram