Clean up documentation pages

This commit is contained in:
Dan 2019-04-12 15:52:06 +02:00
parent 3ac62ba941
commit 05aed5e0e1
18 changed files with 263 additions and 246 deletions

View File

@ -25,6 +25,10 @@ sys.path.insert(0, os.path.abspath('../..'))
# Import after sys.path.insert() to avoid issues # Import after sys.path.insert() to avoid issues
from pyrogram import __version__ from pyrogram import __version__
from pygments.styles.friendly import FriendlyStyle
FriendlyStyle.background_color = "#f3f2f1"
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
@ -60,7 +64,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = 'Pyrogram' project = 'Pyrogram'
copyright = '2017-2018, Dan Tès' copyright = '2017-2019, Dan Tès'
author = 'Dan Tès' author = 'Dan Tès'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
@ -85,7 +89,7 @@ language = None
exclude_patterns = [] exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'tango' pygments_style = 'friendly'
# If true, `todo` and `todoList` produce output, else they produce nothing. # If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False todo_include_todos = False

View File

@ -9,6 +9,7 @@ Handlers
MessageHandler MessageHandler
DeletedMessagesHandler DeletedMessagesHandler
CallbackQueryHandler CallbackQueryHandler
InlineQueryHandler
UserStatusHandler UserStatusHandler
DisconnectHandler DisconnectHandler
RawUpdateHandler RawUpdateHandler
@ -22,6 +23,9 @@ Handlers
.. autoclass:: CallbackQueryHandler .. autoclass:: CallbackQueryHandler
:members: :members:
.. autoclass:: InlineQueryHandler
:members:
.. autoclass:: UserStatusHandler .. autoclass:: UserStatusHandler
:members: :members:

View File

@ -1,40 +1,102 @@
Advanced Usage Advanced Usage
============== ==============
In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main Telegram Pyrogram's API, which consists of well documented convenience methods_ and facade types_, exists to provide a much
API with its raw functions and types. 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 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 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>` Telegram API, you have to use the raw :mod:`functions <pyrogram.api.functions>` and :mod:`types <pyrogram.api.types>`.
exposed by the ``pyrogram.api`` package and call any Telegram API method you wish using the
:meth:`send() <pyrogram.Client.send>` method provided by the Client class. 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:: .. hint::
Every available high-level method mentioned in the previous page is built on top of these raw functions. 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 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. 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_! If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_!
Caveats Invoking Functions
------- ^^^^^^^^^^^^^^^^^^
As hinted before, raw functions and types can be confusing, mainly because people don't realize they must accept Unlike the methods_ found in Pyrogram's API, which can be called in the usual simple way, functions to be invoked from
*exactly* the right values, but also because most of them don't have enough Python experience to fully grasp how things the raw Telegram API have a different way of usage and are more complex.
work.
This section will therefore explain some pitfalls to take into consideration when working with the raw API. 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 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. 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. 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. There are three different InputPeer types, one for each kind of Telegram entity.
Whenever an InputPeer is needed you must pass one of these: Whenever an InputPeer is needed you must pass one of these:
@ -56,66 +118,19 @@ kind of ID.
For example, given the ID *123456789*, here's how Pyrogram can tell entities apart: For example, given the ID *123456789*, here's how Pyrogram can tell entities apart:
- ``+ID`` - User: *123456789* - ``+ID`` User: *123456789*
- ``-ID`` - Chat: *-123456789* - ``-ID`` Chat: *-123456789*
- ``-100ID`` - Channel (and Supergroup): *-100123456789* - ``-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 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. high-level method.
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"
)
)
- Share your Last Seen time only with your contacts:
.. 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.InputPrivacyKeyStatusTimestamp(),
rules=[types.InputPrivacyValueAllowContacts()]
)
)
- 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
]
)
)
.. _methods: ../pyrogram/Client.html#messages
.. _types: ../pyrogram/Types.html
.. _plenty of them: ../pyrogram/Client.html#messages .. _plenty of them: ../pyrogram/Client.html#messages
.. _Raw Functions: Usage.html#using-raw-functions .. _raw functions: ../pyrogram/functions
.. _raw types: ../pyrogram/types
.. _Community: https://t.me/PyrogramChat .. _Community: https://t.me/PyrogramChat

View File

@ -1,13 +1,13 @@
Configuration File Configuration File
================== ==================
As already mentioned in previous sections, Pyrogram can also be configured by the use of an INI 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. This page explains how this file is structured in Pyrogram, how to use it and why.
Introduction Introduction
------------ ------------
The idea behind using a configuration file is to help keeping your code free of settings (private) information such as 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 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 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. fill in the necessary parts.
@ -46,7 +46,7 @@ These are all the sections Pyrogram uses in its configuration file:
Pyrogram Pyrogram
^^^^^^^^ ^^^^^^^^
The ``[pyrogram]`` section contains your Telegram API credentials *api_id* and *api_hash*. The ``[pyrogram]`` section contains your Telegram API credentials: *api_id* and *api_hash*.
.. code-block:: ini .. code-block:: ini

View File

@ -1,19 +1,19 @@
Customize Sessions Customize Sessions
================== ==================
As you may probably know, Telegram allows Users (and Bots) having more than one session (authorizations) registered As you may probably know, Telegram allows users (and bots) having more than one session (authorizations) registered
in the system at the same time. 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 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) and store some useful app (or by invoking `GetAuthorizations <../functions/account/GetAuthorizations.html>`_ with Pyrogram). They store some
information about the client who generated them. useful information such as the client who's using them and from which country and IP address.
.. figure:: https://i.imgur.com/lzGPCdZ.png .. figure:: https://i.imgur.com/lzGPCdZ.png
:width: 70% :width: 70%
:align: center :align: center
A Pyrogram session running on Linux, Python 3.6. **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. That's how a session looks like on the Android app, showing the three main pieces of information.

View File

@ -4,7 +4,7 @@ Error Handling
Errors are inevitable when working with the API, and they must be correctly handled with ``try..except`` blocks. 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 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) (which are in turn children of the :obj:`RPCError <pyrogram.RPCError>` superclass):
- :obj:`303 - See Other <pyrogram.errors.SeeOther>` - :obj:`303 - See Other <pyrogram.errors.SeeOther>`
- :obj:`400 - Bad Request <pyrogram.errors.BadRequest>` - :obj:`400 - Bad Request <pyrogram.errors.BadRequest>`

View File

@ -1,24 +1,22 @@
More on Updates More on Updates
=============== ===============
Here we'll show some advanced usages when working with updates. Here we'll show some advanced usages when working with `update handlers`_ and `filters`_.
.. note::
This page makes use of Handlers and Filters to show you how to handle updates.
Learn more at `Update Handling <UpdateHandling.html>`_ and `Using Filters <UsingFilters.html>`_.
Handler Groups Handler Groups
-------------- --------------
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored. 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 process the same update more than once, you can register your handler in a different group. In order to handle the very same update more than once, you have to register your handler in a different dispatching
Groups are identified by a number (number 0 being the default) and are sorted, that is, a lower group number has a group. Dispatching groups hold one or more handlers and are processed sequentially, they are identified by a number
higher priority. (number 0 being the default) and sorted, that is, a lower group number has a higher priority:
For example, in: For example, take these two handlers:
.. code-block:: python .. code-block:: python
:emphasize-lines: 1, 6
@app.on_message(Filters.text | Filters.sticker) @app.on_message(Filters.text | Filters.sticker)
def text_or_sticker(client, message): def text_or_sticker(client, message):
@ -29,8 +27,8 @@ For example, in:
def just_text(client, message): def just_text(client, message):
print("Just Text") print("Just Text")
``just_text`` is never executed because ``text_or_sticker`` already handles texts. To enable it, simply register the Here, ``just_text`` is never executed because ``text_or_sticker``, which has been registered first, already handles
function using a different group: texts (``Filters.text`` is shared and conflicting). To enable it, register the function using a different group:
.. code-block:: python .. code-block:: python
@ -69,7 +67,7 @@ continue to propagate the same update to the next groups until all the handlers
@app.on_message(Filters.private, group=1) @app.on_message(Filters.private, group=1)
def _(client, message): def _(client, message):
print(1 / 0) # Unhandled exception: ZeroDivisionError raise Exception("Unhandled exception!") # Simulate an unhandled exception
@app.on_message(Filters.private, group=2) @app.on_message(Filters.private, group=2)
@ -82,7 +80,7 @@ The output for each incoming update will therefore be:
.. code-block:: text .. code-block:: text
0 0
ZeroDivisionError: division by zero Exception: Unhandled exception!
2 2
Stop Propagation Stop Propagation
@ -153,9 +151,9 @@ Continue Propagation
As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the 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 `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 **the same group** regardless of the next handler's filters. This allows you to register multiple handlers with
filters in the same group; to let the dispatcher process the next handler you can do *one* of the following in each overlapping filters in the same group; to let the dispatcher process the next handler you can do *one* of the following
handler you want to grant permission to continue: in each handler you want to grant permission to continue:
- Call the update's bound-method ``.continue_propagation()`` (preferred way). - Call the update's bound-method ``.continue_propagation()`` (preferred way).
- Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only). - Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only).
@ -219,3 +217,6 @@ The output of both (equivalent) examples will be:
0 0
1 1
2 2
.. _`update handlers`: UpdateHandling.html
.. _`filters`: UsingFilters.html

View File

@ -13,8 +13,8 @@ Introduction
------------ ------------
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize 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, your applications, you had to put your function definitions in separate files and register them inside your main script
like this: after importing your modules, like this:
.. note:: .. note::
@ -72,7 +72,7 @@ functions. So, what if you could? Smart Plugins solve this issue by taking care
Using Smart Plugins Using Smart Plugins
------------------- -------------------
Setting up your Pyrogram project to accommodate Smart Plugins is straightforward: 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", ...). #. 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. #. Put your python files full of plugins inside. Organize them as you wish.
@ -129,18 +129,17 @@ Setting up your Pyrogram project to accommodate Smart Plugins is straightforward
from pyrogram import Client from pyrogram import Client
plugins = dict( plugins = dict(root="plugins")
root="plugins"
)
Client("my_account", plugins=plugins).run() 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 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 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. 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 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 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. 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 Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback
@ -161,7 +160,7 @@ found inside each module will be, instead, loaded in the order they are defined,
This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or 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`` 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: 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 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 ``include`` is given, only the specified plugins will be loaded, in the order they are passed.
@ -214,9 +213,7 @@ also organized in subfolders:
.. code-block:: python .. code-block:: python
plugins = dict( plugins = dict(root="plugins")
root="plugins"
)
Client("my_account", plugins=plugins).run() Client("my_account", plugins=plugins).run()
@ -293,7 +290,7 @@ 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 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 which to ignore before your Client starts. Here we'll show, instead, how to unload and load again a previously
registered plugins at runtime. registered plugin at runtime.
Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram updates 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 ) will be modified in such a way that, when you reference them later on, they will be actually pointing to a tuple of
@ -314,14 +311,14 @@ attribute. Here's an example:
- Printing ``echo`` will show something like ``(<MessageHandler object at 0x10e3abc50>, 0)``. - 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 - 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>``. Handler, will reveal the actual callback ``<function echo at 0x10e3b6598>``.
Unloading 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 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 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: name preceded by the star ``*`` operator as argument. Example:
- ``main.py`` - ``main.py``
@ -346,7 +343,7 @@ Loading
^^^^^^^ ^^^^^^^
Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time 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: using :meth:`add_handler() <pyrogram.Client.add_handler>` instead. Example:
- ``main.py`` - ``main.py``

View File

@ -1,11 +1,13 @@
Update Handling Update Handling
=============== ===============
Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...) Let's now dive right into the core of the framework.
and can be handled by registering one or more callback functions in your app by using `Handlers <../pyrogram/Handlers.html>`_.
To put it simply, whenever an update is received from Telegram it will be dispatched and your previously defined callback Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...)
function(s) matching it will be called back with the update itself as argument. and 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.
Registering an Handler Registering an Handler
---------------------- ----------------------
@ -15,13 +17,34 @@ To explain how handlers work let's have a look at the most used one, the
updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles 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. 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()
Using Decorators Using Decorators
---------------- ----------------
The easiest and nicest way to register a MessageHandler is by decorating your function with the A much nicer way to register a MessageHandler is by decorating your callback function with the
:meth:`on_message() <pyrogram.Client.on_message>` decorator. Here's a full example that prints out the content :meth:`on_message() <pyrogram.Client.on_message>` decorator, which will still make use of add_handler() under the hood.
of a message as soon as it arrives.
.. code-block:: python .. code-block:: python
@ -37,23 +60,13 @@ of a message as soon as it arrives.
app.run() app.run()
Using add_handler()
-------------------
If you prefer not to use decorators for any reason, there is an alternative way for registering Handlers. .. note::
This is useful, for example, when you want to keep your callback functions in separate files.
.. code-block:: python 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).
from pyrogram import Client, MessageHandler 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.
def my_handler(client, message):
print(message)
app = Client("my_account")
app.add_handler(MessageHandler(my_handler))
app.run()

View File

@ -1,17 +1,19 @@
Using Filters Using Filters
============= =============
For a finer grained control over what kind of messages will be allowed or not in your callback functions, you can use So far we've seen how to register a callback function that executes every time a specific update comes from the server,
:class:`Filters <pyrogram.Filters>`. but there's much more than that to come.
.. note:: Here we'll discuss about :class:`Filters <pyrogram.Filters>`. Filters enable a fine-grain control over what kind of
This page makes use of Handlers to show you how to handle updates. updates are allowed or not to be passed in your callback functions, based on their inner details.
Learn more at `Update Handling <UpdateHandling.html>`_.
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 - This example will show you how to **only** handle messages containing an :obj:`Audio <pyrogram.Audio>` object and
ignore any other message: ignore any other message. Filters are passed as the first argument of the decorator:
.. code-block:: python .. code-block:: python
:emphasize-lines: 4
from pyrogram import Filters from pyrogram import Filters
@ -20,9 +22,10 @@ For a finer grained control over what kind of messages will be allowed or not in
def my_handler(client, message): def my_handler(client, message):
print(message) print(message)
- or, without decorators: - or, without decorators. Here filters are passed as the second argument of the handler constructor:
.. code-block:: python .. code-block:: python
:emphasize-lines: 8
from pyrogram import Filters, MessageHandler from pyrogram import Filters, MessageHandler
@ -37,7 +40,7 @@ Combining Filters
----------------- -----------------
Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise
operators: operators ``~``, ``&`` and ``|``:
- Use ``~`` to invert a filter (behaves like the ``not`` operator). - Use ``~`` to invert a filter (behaves like the ``not`` operator).
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively). - Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
@ -74,7 +77,7 @@ can also accept arguments:
def my_handler(client, message): def my_handler(client, message):
print(message) print(message)
- Message is a **text** message matching the given **regex** pattern. - Message is a **text** message or a media **caption** matching the given **regex** pattern.
.. code-block:: python .. code-block:: python
@ -104,17 +107,17 @@ Custom Filters
-------------- --------------
Pyrogram already provides lots of built-in :class:`Filters <pyrogram.Filters>` to work with, but in case you can't find 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 handler, for a specific one for your needs or want to build a custom filter by yourself (to be used in a different kind of handler,
example) you can use :meth:`Filters.create() <pyrogram.Filters.create>`. for example) you can use :meth:`Filters.create() <pyrogram.Filters.create>`.
.. note:: .. note::
At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>` At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`
only. only.
An example to demonstrate how custom filters work is to show how to create and use one for the 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; :obj:`CallbackQueryHandler <pyrogram.CallbackQueryHandler>`. Note that callback queries updates are only received by
create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline keyboard to bots; create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline
yourself. This allows you to test your filter by pressing the inline button: keyboard to yourself. This allows you to test your filter by pressing the inline button:
.. code-block:: python .. code-block:: python
@ -133,26 +136,27 @@ Basic Filters
For this basic filter we will be using only the first two parameters of :meth:`Filters.create() <pyrogram.Filters.create>`. 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 callback data. This filter will only allow callback queries The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries
containing "pyrogram" as data: 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 .. code-block:: python
hardcoded_data = Filters.create( static_data = Filters.create(
name="HardcodedData", name="StaticdData",
func=lambda filter, callback_query: callback_query.data == b"pyrogram" 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 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 itself: 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 .. code-block:: python
def func(filter, callback_query): def func(flt, callback_query):
return callback_query.data == b"pyrogram" return callback_query.data == b"Pyrogram"
hardcoded_data = Filters.create( static_data = Filters.create(
name="HardcodedData", name="StaticData",
func=func func=func
) )
@ -160,14 +164,14 @@ The filter usage remains the same:
.. code-block:: python .. code-block:: python
@app.on_callback_query(hardcoded_data) @app.on_callback_query(static_data)
def pyrogram_data(client, callback_query): def pyrogram_data(client, callback_query):
client.answer_callback_query(callback_query.id, "it works!") client.answer_callback_query(callback_query.id, "it works!")
Filters with Arguments Filters with Arguments
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
A much cooler filter would be one that accepts "pyrogram" or any other data as argument at usage time. 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>`. 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: This is how a dynamic custom filter looks like:
@ -177,7 +181,7 @@ This is how a dynamic custom filter looks like:
def dynamic_data(data): def dynamic_data(data):
return Filters.create( return Filters.create(
name="DynamicData", name="DynamicData",
func=lambda filter, callback_query: filter.data == callback_query.data, func=lambda flt, callback_query: flt.data == callback_query.data,
data=data # "data" kwarg is accessed with "filter.data" data=data # "data" kwarg is accessed with "filter.data"
) )
@ -185,6 +189,6 @@ And its usage:
.. code-block:: python .. code-block:: python
@app.on_callback_query(dynamic_data(b"pyrogram")) @app.on_callback_query(dynamic_data(b"Pyrogram"))
def pyrogram_data(client, callback_query): def pyrogram_data(client, callback_query):
client.answer_callback_query(callback_query.id, "it works!") client.answer_callback_query(callback_query.id, "it works!")

View File

@ -1,11 +1,11 @@
Installation Installation
============ ============
Being a Python library, Pyrogram requires Python to be installed in your system. Being a Python library, **Pyrogram** requires Python to be installed in your system.
We recommend using the latest version of Python 3 and pip. We recommend using the latest version of Python 3 and pip.
Get Python 3 from https://www.python.org/downloads/ (or with your package manager) and pip - Get **Python 3** from https://www.python.org/downloads/ (or with your package manager)
by following the instructions at https://pip.pypa.io/en/latest/installing/. - Get **pip** by following the instructions at https://pip.pypa.io/en/latest/installing/.
.. important:: .. important::
@ -29,8 +29,12 @@ Install Pyrogram
Bleeding Edge Bleeding Edge
------------- -------------
If you want the latest development version of Pyrogram, you can install it straight from the develop_ Things are constantly evolving in Pyrogram, although new releases are published only when enough changes are added,
branch using this command (note "develop.zip" in the link): but this doesn't mean you can't try new features right now!
In case you would like to try out the latest Pyrogram features and additions, the `GitHub repo`_ is always kept updated
with new changes; you can install the development version straight from the ``develop`` branch using this command
(note "develop.zip" in the link):
.. code-block:: text .. code-block:: text
@ -39,10 +43,10 @@ branch using this command (note "develop.zip" in the link):
Asynchronous Asynchronous
------------ ------------
Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging client library after all), and here's Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging framework after all), and here's
where asyncio shines the most by providing extra performance while running on a single OS-level thread only. where asyncio shines the most by providing extra performance while running on a single OS-level thread only.
**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5+ required). **A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5.3+ required).
Use this command to install (note "asyncio.zip" in the link): Use this command to install (note "asyncio.zip" in the link):
.. code-block:: text .. code-block:: text
@ -85,4 +89,4 @@ If no error shows up you are good to go.
'0.12.0' '0.12.0'
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto .. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
.. _develop: http://github.com/pyrogram/pyrogram .. _`Github repo`: http://github.com/pyrogram/pyrogram

View File

@ -15,23 +15,22 @@ If you already have one you can skip this step, otherwise:
#. Fill out the form to register a new Telegram application. #. Fill out the form to register a new Telegram application.
#. Done. The API key consists of two parts: **App api_id** and **App api_hash**. #. Done. The API key consists of two parts: **App api_id** and **App api_hash**.
.. important:: .. important::
This API key is personal and should be kept secret. This API key is personal and must be kept secret.
Configuration Configuration
------------- -------------
The API key obtained in the `previous step <#api-keys>`_ defines a token for your application allowing you to access The API key obtained in the `previous step <#api-keys>`_ defines a token for your application allowing you to access
the Telegram database using the MTProto API — **it is therefore required for all authorizations of both Users and Bots**. the Telegram database using the MTProto API — **it is therefore required for all authorizations of both users and bots**.
Having it handy, it's time to configure your Pyrogram project. There are two ways to do so, and you can choose what Having it handy, it's time to configure your Pyrogram project. There are two ways to do so, and you can choose what
fits better for you: fits better for you:
- Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the - Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the
**api_id** and **api_hash** values with your own. This is the preferred method because allows you **api_id** and **api_hash** values with your own. This is the preferred method because allows you to keep your
to keep your credentials out of your code without having to deal with how to load them: credentials out of your code without having to deal with how to load them:
.. code-block:: ini .. code-block:: ini
@ -39,8 +38,8 @@ fits better for you:
api_id = 12345 api_id = 12345
api_hash = 0123456789abcdef0123456789abcdef api_hash = 0123456789abcdef0123456789abcdef
- Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* - Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* parameters of the
parameters of the Client class. This way you can have full control on how to store and load your credentials: Client class. This way you can have full control on how to store and load your credentials:
.. code-block:: python .. code-block:: python
@ -54,16 +53,16 @@ fits better for you:
.. note:: .. note::
The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id* From now on, the code snippets assume you are using the ``config.ini`` file, thus they won't show the *api_id* and
and *api_hash* parameters usage. *api_hash* parameters usage to keep them as clean as possible.
User Authorization User Authorization
------------------ ------------------
In order to use the API, Telegram requires that Users be authorized via their phone numbers. In order to use the API, Telegram requires that users be authorized via their phone numbers.
Pyrogram automatically manages this access, all you need to do is create an instance of Pyrogram automatically manages this access, all you need to do is create an instance of the
the :class:`Client <pyrogram.Client>` class by passing to it a ``session_name`` of your choice :class:`Client <pyrogram.Client>` class by passing to it a ``session_name`` of your choice (e.g.: "my_account") and call
(e.g.: "my_account") and call the :meth:`run() <pyrogram.Client.run>` method: the :meth:`run() <pyrogram.Client.run>` method:
.. code-block:: python .. code-block:: python
@ -80,31 +79,40 @@ and the **phone code** you will receive:
Enter phone number: +39********** Enter phone number: +39**********
Is "+39**********" correct? (y/n): y Is "+39**********" correct? (y/n): y
Enter phone code: 32768 Enter phone code: 32768
Logged in successfully as Dan
After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing Pyrogram
Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app, executing API calls with your identity. This file will be loaded again when you restart your app, and as long as you
and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number. keep the session alive, Pyrogram won't ask you again to enter your phone number.
.. important:: .. important::
Your ``*.session`` files are personal and must be kept secret. Your ``*.session`` files are personal and must be kept secret.
.. note::
The code above does nothing except asking for credentials and keeping the client online, hit ``CTRL+C`` now to stop
your application and keep reading.
Bot Authorization Bot Authorization
----------------- -----------------
Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by
BotFather_. Bot tokens replace the Users' phone numbers only — you still need to BotFather_. Bot tokens replace the users' phone numbers only — you still need to
`configure a Telegram API key <#configuration>`_ with Pyrogram, even when using Bots. `configure a Telegram API key <#configuration>`_ 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, 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. usually your bot username) and pass your bot token using the ``bot_token`` parameter. The session file will be named
The session file will be named after the session name, which will be ``pyrogrambot.session`` for the example below. after the session name, which will be ``pyrogrambot.session`` for the example below.
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
app = Client("pyrogrambot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") app = Client(
"pyrogrambot",
bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
)
app.run() app.run()
.. _installed Pyrogram: Installation.html .. _installed Pyrogram: Installation.html

View File

@ -1,7 +1,7 @@
Usage Usage
===== =====
Having your `project set up`_ and your account authorized_, it's time to play with the API. Let's start! Having your `project set up`_ and your account authorized_, it's time to start playing with the API. Let's start!
High-level API High-level API
-------------- --------------
@ -22,6 +22,7 @@ Here's a simple example:
print(app.get_me()) print(app.get_me())
app.send_message("me", "Hi there! I'm using **Pyrogram**") app.send_message("me", "Hi there! I'm using **Pyrogram**")
app.send_location("me", 51.500729, -0.124583) app.send_location("me", 51.500729, -0.124583)
app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
app.stop() app.stop()
@ -39,6 +40,7 @@ exceptions in your code:
print(app.get_me()) print(app.get_me())
app.send_message("me", "Hi there! I'm using **Pyrogram**") app.send_message("me", "Hi there! I'm using **Pyrogram**")
app.send_location("me", 51.500729, -0.124583) app.send_location("me", 51.500729, -0.124583)
app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
More examples on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_. More examples on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_.

View File

@ -33,13 +33,6 @@ class OnCallbackQuery(BaseClient):
"""Use this decorator to automatically register a function for handling callback queries. """Use this decorator to automatically register a function for handling callback queries.
This does the same thing as :meth:`add_handler` using the :class:`CallbackQueryHandler`. This does the same thing as :meth:`add_handler` using the :class:`CallbackQueryHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of callback queries to be passed Pass one or more filters to allow only a subset of callback queries to be passed

View File

@ -33,13 +33,6 @@ class OnDeletedMessages(BaseClient):
"""Use this decorator to automatically register a function for handling deleted messages. """Use this decorator to automatically register a function for handling deleted messages.
This does the same thing as :meth:`add_handler` using the :class:`DeletedMessagesHandler`. This does the same thing as :meth:`add_handler` using the :class:`DeletedMessagesHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of messages to be passed Pass one or more filters to allow only a subset of messages to be passed

View File

@ -33,13 +33,6 @@ class OnMessage(BaseClient):
"""Use this decorator to automatically register a function for handling messages. """Use this decorator to automatically register a function for handling messages.
This does the same thing as :meth:`add_handler` using the :class:`MessageHandler`. This does the same thing as :meth:`add_handler` using the :class:`MessageHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of messages to be passed Pass one or more filters to allow only a subset of messages to be passed

View File

@ -31,13 +31,6 @@ class OnRawUpdate(BaseClient):
"""Use this decorator to automatically register a function for handling raw updates. """Use this decorator to automatically register a function for handling raw updates.
This does the same thing as :meth:`add_handler` using the :class:`RawUpdateHandler`. This does the same thing as :meth:`add_handler` using the :class:`RawUpdateHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
group (``int``, *optional*): group (``int``, *optional*):
The group identifier, defaults to 0. The group identifier, defaults to 0.

View File

@ -33,13 +33,6 @@ class OnUserStatus(BaseClient):
"""Use this decorator to automatically register a function for handling user status updates. """Use this decorator to automatically register a function for handling user status updates.
This does the same thing as :meth:`add_handler` using the :class:`UserStatusHandler`. This does the same thing as :meth:`add_handler` using the :class:`UserStatusHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function. Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function.