mirror of
https://github.com/TeamPGM/pyrogram.git
synced 2024-11-17 21:22:40 +00:00
Merge branch 'develop' into asyncio
# Conflicts: # pyrogram/client/client.py
This commit is contained in:
commit
61e9762977
42
README.rst
42
README.rst
@ -3,6 +3,8 @@
|
|||||||
Pyrogram
|
Pyrogram
|
||||||
========
|
========
|
||||||
|
|
||||||
|
`A fully asynchronous variant is also available! <https://github.com/pyrogram/pyrogram/issues/181>`_
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from pyrogram import Client, Filters
|
from pyrogram import Client, Filters
|
||||||
@ -17,18 +19,20 @@ Pyrogram
|
|||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for
|
**Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and C.
|
||||||
building custom Telegram applications that interact with the MTProto API as both User and Bot.
|
It enables you to easily build custom Telegram applications that interact with the MTProto API as both user and bot.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
|
- **Easy**: You can install Pyrogram with pip and start building your app right away.
|
||||||
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
|
- **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.
|
- **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**: Pyrogram API methods, types and public interfaces are well documented.
|
||||||
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
|
- **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted.
|
||||||
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
|
- **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.
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
@ -47,7 +51,7 @@ Getting Started
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
- The Docs contain lots of resources to help you getting started with Pyrogram: https://docs.pyrogram.ml.
|
- The Docs contain lots of resources to help you getting started with Pyrogram: https://docs.pyrogram.ml.
|
||||||
- Reading Examples_ in this repository is also a good way for learning how things work.
|
- Reading `Examples in this repository`_ is also a good way for learning how Pyrogram works.
|
||||||
- Seeking extra help? Don't be shy, come join and ask our Community_!
|
- Seeking extra help? Don't be shy, come join and ask our Community_!
|
||||||
- For other requests you can send an Email_ or a Message_.
|
- For other requests you can send an Email_ or a Message_.
|
||||||
|
|
||||||
@ -67,7 +71,7 @@ Copyright & License
|
|||||||
.. _`Telegram`: https://telegram.org/
|
.. _`Telegram`: https://telegram.org/
|
||||||
.. _`Telegram API key`: https://docs.pyrogram.ml/start/ProjectSetup#api-keys
|
.. _`Telegram API key`: https://docs.pyrogram.ml/start/ProjectSetup#api-keys
|
||||||
.. _`Community`: https://t.me/PyrogramChat
|
.. _`Community`: https://t.me/PyrogramChat
|
||||||
.. _`Examples`: https://github.com/pyrogram/pyrogram/tree/master/examples
|
.. _`Examples in this repository`: https://github.com/pyrogram/pyrogram/tree/master/examples
|
||||||
.. _`GitHub`: https://github.com/pyrogram/pyrogram/issues
|
.. _`GitHub`: https://github.com/pyrogram/pyrogram/issues
|
||||||
.. _`Email`: admin@pyrogram.ml
|
.. _`Email`: admin@pyrogram.ml
|
||||||
.. _`Message`: https://t.me/haskell
|
.. _`Message`: https://t.me/haskell
|
||||||
@ -83,17 +87,17 @@ Copyright & License
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<b>Telegram MTProto API Client Library for Python</b>
|
<b>Telegram MTProto API Framework for Python</b>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/pyrogram/pyrogram/releases/latest">
|
|
||||||
Download
|
|
||||||
</a>
|
|
||||||
•
|
|
||||||
<a href="https://docs.pyrogram.ml">
|
<a href="https://docs.pyrogram.ml">
|
||||||
Documentation
|
Documentation
|
||||||
</a>
|
</a>
|
||||||
•
|
•
|
||||||
|
<a href="https://github.com/pyrogram/pyrogram/releases">
|
||||||
|
Changelog
|
||||||
|
</a>
|
||||||
|
•
|
||||||
<a href="https://t.me/PyrogramChat">
|
<a href="https://t.me/PyrogramChat">
|
||||||
Community
|
Community
|
||||||
</a>
|
</a>
|
||||||
@ -104,7 +108,7 @@ Copyright & License
|
|||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/pyrogram/tgcrypto">
|
<a href="https://github.com/pyrogram/tgcrypto">
|
||||||
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
||||||
alt="TgCrypto">
|
alt="TgCrypto Version">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -112,12 +116,12 @@ Copyright & License
|
|||||||
:target: https://pyrogram.ml
|
:target: https://pyrogram.ml
|
||||||
:alt: Pyrogram
|
:alt: Pyrogram
|
||||||
|
|
||||||
.. |description| replace:: **Telegram MTProto API Client Library for Python**
|
.. |description| replace:: **Telegram MTProto API Framework for Python**
|
||||||
|
|
||||||
.. |scheme| image:: "https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30"
|
.. |schema| image:: "https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30"
|
||||||
:target: compiler/api/source/main_api.tl
|
:target: compiler/api/source/main_api.tl
|
||||||
:alt: Scheme Layer
|
:alt: Schema Layer
|
||||||
|
|
||||||
.. |tgcrypto| image:: "https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
.. |tgcrypto| image:: "https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
||||||
:target: https://github.com/pyrogram/tgcrypto
|
:target: https://github.com/pyrogram/tgcrypto
|
||||||
:alt: TgCrypto
|
:alt: TgCrypto Version
|
||||||
|
@ -62,7 +62,7 @@ USER_IS_BOT A bot cannot send messages to other bots or to itself
|
|||||||
WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL
|
WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL
|
||||||
STICKERSET_INVALID The requested sticker set is invalid
|
STICKERSET_INVALID The requested sticker set is invalid
|
||||||
PEER_FLOOD The method can't be used because your account is limited
|
PEER_FLOOD The method can't be used because your account is limited
|
||||||
MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
|
MEDIA_CAPTION_TOO_LONG The media caption is longer than 1024 characters
|
||||||
USER_NOT_MUTUAL_CONTACT The user is not a mutual contact
|
USER_NOT_MUTUAL_CONTACT The user is not a mutual contact
|
||||||
USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups
|
USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups
|
||||||
API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
|
API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
|
||||||
@ -86,4 +86,4 @@ TAKEOUT_REQUIRED The method must be invoked inside a takeout session
|
|||||||
MESSAGE_POLL_CLOSED You can't interact with a closed poll
|
MESSAGE_POLL_CLOSED You can't interact with a closed poll
|
||||||
MEDIA_INVALID The media is invalid
|
MEDIA_INVALID The media is invalid
|
||||||
BOT_SCORE_NOT_MODIFIED The bot score was not modified
|
BOT_SCORE_NOT_MODIFIED The bot score was not modified
|
||||||
USER_BOT_REQUIRED The method can be used by bots only
|
USER_BOT_REQUIRED The method can be used by bots only
|
||||||
|
|
@ -10,27 +10,28 @@ Welcome to Pyrogram
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<b>Telegram MTProto API Client Library for Python</b>
|
<b>Telegram MTProto API Framework for Python</b>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/pyrogram/pyrogram/releases/latest">
|
<a href="https://docs.pyrogram.ml">
|
||||||
Download
|
Documentation
|
||||||
</a>
|
</a>
|
||||||
•
|
•
|
||||||
<a href="https://github.com/pyrogram/pyrogram">
|
<a href="https://github.com/pyrogram/pyrogram/releases">
|
||||||
Source code
|
Changelog
|
||||||
</a>
|
</a>
|
||||||
•
|
•
|
||||||
<a href="https://t.me/PyrogramChat">
|
<a href="https://t.me/PyrogramChat">
|
||||||
Community
|
Community
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<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"
|
<img src="https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30"
|
||||||
alt="Scheme Layer">
|
alt="Schema Layer">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/pyrogram/tgcrypto">
|
<a href="https://github.com/pyrogram/tgcrypto">
|
||||||
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
||||||
alt="TgCrypto">
|
alt="TgCrypto Version">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -48,25 +49,27 @@ Welcome to Pyrogram
|
|||||||
|
|
||||||
app.run()
|
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
|
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.
|
using the Next button at the end of each page. But first, here's a brief overview of what is this all about.
|
||||||
|
|
||||||
About
|
About
|
||||||
-----
|
-----
|
||||||
|
|
||||||
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for
|
**Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and
|
||||||
building custom Telegram applications that interact with the MTProto API as both User and Bot.
|
C. It enables you to easily build custom Telegram applications that interact with the MTProto API as both user and bot.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
|
- **Easy**: You can install Pyrogram with pip and start building your app right away.
|
||||||
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
|
- **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.
|
- **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**: Pyrogram API methods, types and public interfaces are well documented.
|
||||||
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
|
- **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted.
|
||||||
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
|
- **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.
|
To get started, press the Next button.
|
||||||
|
|
||||||
@ -85,6 +88,7 @@ To get started, press the Next button.
|
|||||||
resources/UpdateHandling
|
resources/UpdateHandling
|
||||||
resources/UsingFilters
|
resources/UsingFilters
|
||||||
resources/MoreOnUpdates
|
resources/MoreOnUpdates
|
||||||
|
resources/ConfigurationFile
|
||||||
resources/SmartPlugins
|
resources/SmartPlugins
|
||||||
resources/AutoAuthorization
|
resources/AutoAuthorization
|
||||||
resources/CustomizeSessions
|
resources/CustomizeSessions
|
||||||
|
90
docs/source/resources/ConfigurationFile.rst
Normal file
90
docs/source/resources/ConfigurationFile.rst
Normal 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>`_
|
@ -1,9 +1,9 @@
|
|||||||
Smart Plugins
|
Smart Plugins
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Pyrogram embeds a **smart** (automatic) and lightweight plugin system that is meant to further simplify the organization
|
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 components that can be **easily shared** across different
|
of large projects and to provide a way for creating pluggable (modular) components that can be **easily shared** across
|
||||||
Pyrogram applications with **minimal boilerplate code**.
|
different Pyrogram applications with **minimal boilerplate code**.
|
||||||
|
|
||||||
.. tip::
|
.. tip::
|
||||||
|
|
||||||
@ -13,7 +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 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::
|
.. note::
|
||||||
|
|
||||||
@ -63,19 +64,19 @@ your applications, you had to do something like this...
|
|||||||
|
|
||||||
app.run()
|
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
|
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
|
: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
|
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").
|
#. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...).
|
||||||
#. Put your files full of plugins inside.
|
#. Put your python files full of plugins inside. Organize them as you wish.
|
||||||
#. Enable plugins in your Client.
|
#. Enable plugins in your Client or via the *config.ini* file.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -107,20 +108,252 @@ Setting up your Pyrogram project to accommodate Smart Plugins is pretty straight
|
|||||||
def echo_reversed(client, message):
|
def echo_reversed(client, message):
|
||||||
message.reply(message.text[::-1])
|
message.reply(message.text[::-1])
|
||||||
|
|
||||||
|
- ``config.ini``
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
root = plugins
|
||||||
|
|
||||||
- ``main.py``
|
- ``main.py``
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from pyrogram import Client
|
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``
|
Alternatively, without using the *config.ini* file:
|
||||||
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
|
.. code-block:: python
|
||||||
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.
|
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
|
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)
|
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)
|
@ -1,5 +1,5 @@
|
|||||||
TgCrypto
|
Fast Crypto
|
||||||
========
|
===========
|
||||||
|
|
||||||
Pyrogram's speed can be *dramatically* boosted up by TgCrypto_, a high-performance, easy-to-install Telegram 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.
|
Library specifically written in C for Pyrogram [#f1]_ as a Python extension.
|
||||||
|
@ -159,10 +159,8 @@ class Client(Methods, BaseClient):
|
|||||||
config_file (``str``, *optional*):
|
config_file (``str``, *optional*):
|
||||||
Path of the configuration file. Defaults to ./config.ini
|
Path of the configuration file. Defaults to ./config.ini
|
||||||
|
|
||||||
plugins_dir (``str``, *optional*):
|
plugins (``dict``, *optional*):
|
||||||
Define a custom directory for your plugins. The plugins directory is the location in your
|
TODO: doctrings
|
||||||
filesystem where Pyrogram will automatically load your update handlers.
|
|
||||||
Defaults to None (plugins disabled).
|
|
||||||
|
|
||||||
no_updates (``bool``, *optional*):
|
no_updates (``bool``, *optional*):
|
||||||
Pass True to completely disable incoming updates for the current session.
|
Pass True to completely disable incoming updates for the current session.
|
||||||
@ -199,7 +197,7 @@ class Client(Methods, BaseClient):
|
|||||||
workers: int = BaseClient.WORKERS,
|
workers: int = BaseClient.WORKERS,
|
||||||
workdir: str = BaseClient.WORKDIR,
|
workdir: str = BaseClient.WORKDIR,
|
||||||
config_file: str = BaseClient.CONFIG_FILE,
|
config_file: str = BaseClient.CONFIG_FILE,
|
||||||
plugins_dir: str = None,
|
plugins: dict = None,
|
||||||
no_updates: bool = None,
|
no_updates: bool = None,
|
||||||
takeout: bool = None):
|
takeout: bool = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -225,7 +223,7 @@ class Client(Methods, BaseClient):
|
|||||||
self.workers = workers
|
self.workers = workers
|
||||||
self.workdir = workdir
|
self.workdir = workdir
|
||||||
self.config_file = config_file
|
self.config_file = config_file
|
||||||
self.plugins_dir = plugins_dir
|
self.plugins = plugins
|
||||||
self.no_updates = no_updates
|
self.no_updates = no_updates
|
||||||
self.takeout = takeout
|
self.takeout = takeout
|
||||||
|
|
||||||
@ -243,7 +241,14 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
@proxy.setter
|
@proxy.setter
|
||||||
def proxy(self, value):
|
def proxy(self, value):
|
||||||
self._proxy["enabled"] = True
|
if value is None:
|
||||||
|
self._proxy = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._proxy is None:
|
||||||
|
self._proxy = {}
|
||||||
|
|
||||||
|
self._proxy["enabled"] = bool(value.get("enabled", True))
|
||||||
self._proxy.update(value)
|
self._proxy.update(value)
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
@ -1056,17 +1061,41 @@ class Client(Methods, BaseClient):
|
|||||||
setattr(self, option, getattr(Client, option.upper()))
|
setattr(self, option, getattr(Client, option.upper()))
|
||||||
|
|
||||||
if self._proxy:
|
if self._proxy:
|
||||||
self._proxy["enabled"] = True
|
self._proxy["enabled"] = bool(self._proxy.get("enabled", True))
|
||||||
else:
|
else:
|
||||||
self._proxy = {}
|
self._proxy = {}
|
||||||
|
|
||||||
if parser.has_section("proxy"):
|
if parser.has_section("proxy"):
|
||||||
self._proxy["enabled"] = parser.getboolean("proxy", "enabled")
|
self._proxy["enabled"] = parser.getboolean("proxy", "enabled", fallback=True)
|
||||||
self._proxy["hostname"] = parser.get("proxy", "hostname")
|
self._proxy["hostname"] = parser.get("proxy", "hostname")
|
||||||
self._proxy["port"] = parser.getint("proxy", "port")
|
self._proxy["port"] = parser.getint("proxy", "port")
|
||||||
self._proxy["username"] = parser.get("proxy", "username", fallback=None) or None
|
self._proxy["username"] = parser.get("proxy", "username", fallback=None) or None
|
||||||
self._proxy["password"] = parser.get("proxy", "password", fallback=None) or None
|
self._proxy["password"] = parser.get("proxy", "password", fallback=None) or None
|
||||||
|
|
||||||
|
if self.plugins:
|
||||||
|
self.plugins["enabled"] = bool(self.plugins.get("enabled", True))
|
||||||
|
self.plugins["include"] = "\n".join(self.plugins.get("include", [])) or None
|
||||||
|
self.plugins["exclude"] = "\n".join(self.plugins.get("exclude", [])) or None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
section = parser["plugins"]
|
||||||
|
|
||||||
|
self.plugins = {
|
||||||
|
"enabled": section.getboolean("enabled", True),
|
||||||
|
"root": section.get("root"),
|
||||||
|
"include": section.get("include") or None,
|
||||||
|
"exclude": section.get("exclude") or None
|
||||||
|
}
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for option in ["include", "exclude"]:
|
||||||
|
if self.plugins[option] is not None:
|
||||||
|
self.plugins[option] = [
|
||||||
|
(i.split()[0], i.split()[1:] or None)
|
||||||
|
for i in self.plugins[option].strip().split("\n")
|
||||||
|
]
|
||||||
|
|
||||||
async def load_session(self):
|
async def load_session(self):
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(self.workdir, "{}.session".format(self.session_name)), encoding="utf-8") as f:
|
with open(os.path.join(self.workdir, "{}.session".format(self.session_name)), encoding="utf-8") as f:
|
||||||
@ -1098,43 +1127,108 @@ class Client(Methods, BaseClient):
|
|||||||
self.peers_by_phone[k] = peer
|
self.peers_by_phone[k] = peer
|
||||||
|
|
||||||
def load_plugins(self):
|
def load_plugins(self):
|
||||||
if self.plugins_dir is not None:
|
if self.plugins.get("enabled", False):
|
||||||
plugins_count = 0
|
root = self.plugins["root"]
|
||||||
|
include = self.plugins["include"]
|
||||||
|
exclude = self.plugins["exclude"]
|
||||||
|
|
||||||
for path in Path(self.plugins_dir).rglob("*.py"):
|
count = 0
|
||||||
file_path = os.path.splitext(str(path))[0]
|
|
||||||
import_path = []
|
|
||||||
|
|
||||||
while file_path:
|
if include is None:
|
||||||
file_path, tail = os.path.split(file_path)
|
for path in sorted(Path(root).rglob("*.py")):
|
||||||
import_path.insert(0, tail)
|
module_path = os.path.splitext(str(path))[0].replace("/", ".")
|
||||||
|
module = import_module(module_path)
|
||||||
|
|
||||||
import_path = ".".join(import_path)
|
for name in vars(module).keys():
|
||||||
module = import_module(import_path)
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
handler, group = getattr(module, name)
|
||||||
|
|
||||||
for name in dir(module):
|
if isinstance(handler, Handler) and isinstance(group, int):
|
||||||
# noinspection PyBroadException
|
self.add_handler(handler, group)
|
||||||
try:
|
|
||||||
handler, group = getattr(module, name)
|
|
||||||
|
|
||||||
if isinstance(handler, Handler) and isinstance(group, int):
|
log.info('[LOAD] {}("{}") in group {} from "{}"'.format(
|
||||||
self.add_handler(handler, group)
|
type(handler).__name__, name, group, module_path))
|
||||||
|
|
||||||
log.info('{}("{}") from "{}" loaded in group {}'.format(
|
count += 1
|
||||||
type(handler).__name__, name, import_path, group))
|
except Exception:
|
||||||
|
pass
|
||||||
plugins_count += 1
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if plugins_count > 0:
|
|
||||||
log.warning('Successfully loaded {} plugin{} from "{}"'.format(
|
|
||||||
plugins_count,
|
|
||||||
"s" if plugins_count > 1 else "",
|
|
||||||
self.plugins_dir
|
|
||||||
))
|
|
||||||
else:
|
else:
|
||||||
log.warning('No plugin loaded: "{}" doesn\'t contain any valid plugin'.format(self.plugins_dir))
|
for path, handlers in include:
|
||||||
|
module_path = root + "." + path
|
||||||
|
warn_non_existent_functions = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = import_module(module_path)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
log.warning('[LOAD] Ignoring non-existent module "{}"'.format(module_path))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "__path__" in dir(module):
|
||||||
|
log.warning('[LOAD] Ignoring namespace "{}"'.format(module_path))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if handlers is None:
|
||||||
|
handlers = vars(module).keys()
|
||||||
|
warn_non_existent_functions = False
|
||||||
|
|
||||||
|
for name in handlers:
|
||||||
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
handler, group = getattr(module, name)
|
||||||
|
|
||||||
|
if isinstance(handler, Handler) and isinstance(group, int):
|
||||||
|
self.add_handler(handler, group)
|
||||||
|
|
||||||
|
log.info('[LOAD] {}("{}") in group {} from "{}"'.format(
|
||||||
|
type(handler).__name__, name, group, module_path))
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
except Exception:
|
||||||
|
if warn_non_existent_functions:
|
||||||
|
log.warning('[LOAD] Ignoring non-existent function "{}" from "{}"'.format(
|
||||||
|
name, module_path))
|
||||||
|
|
||||||
|
if exclude is not None:
|
||||||
|
for path, handlers in exclude:
|
||||||
|
module_path = root + "." + path
|
||||||
|
warn_non_existent_functions = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = import_module(module_path)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
log.warning('[UNLOAD] Ignoring non-existent module "{}"'.format(module_path))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "__path__" in dir(module):
|
||||||
|
log.warning('[UNLOAD] Ignoring namespace "{}"'.format(module_path))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if handlers is None:
|
||||||
|
handlers = vars(module).keys()
|
||||||
|
warn_non_existent_functions = False
|
||||||
|
|
||||||
|
for name in handlers:
|
||||||
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
handler, group = getattr(module, name)
|
||||||
|
|
||||||
|
if isinstance(handler, Handler) and isinstance(group, int):
|
||||||
|
self.remove_handler(handler, group)
|
||||||
|
|
||||||
|
log.info('[UNLOAD] {}("{}") from group {} in "{}"'.format(
|
||||||
|
type(handler).__name__, name, group, module_path))
|
||||||
|
|
||||||
|
count -= 1
|
||||||
|
except Exception:
|
||||||
|
if warn_non_existent_functions:
|
||||||
|
log.warning('[UNLOAD] Ignoring non-existent function "{}" from "{}"'.format(
|
||||||
|
name, module_path))
|
||||||
|
|
||||||
|
if count > 0:
|
||||||
|
log.warning('Successfully loaded {} plugin{} from "{}"'.format(count, "s" if count > 1 else "", root))
|
||||||
|
else:
|
||||||
|
log.warning('No plugin loaded from "{}"'.format(root))
|
||||||
|
|
||||||
def save_session(self):
|
def save_session(self):
|
||||||
auth_key = base64.b64encode(self.auth_key).decode()
|
auth_key = base64.b64encode(self.auth_key).decode()
|
||||||
|
@ -125,3 +125,6 @@ class BaseClient:
|
|||||||
|
|
||||||
async def get_chat_members(self, *args, **kwargs):
|
async def get_chat_members(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_chat_members_count(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -67,6 +67,8 @@ class GetChatMember(BaseClient):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return pyrogram.ChatMember._parse(self, r.participant, r.users[0])
|
users = {i.id: i for i in r.users}
|
||||||
|
|
||||||
|
return pyrogram.ChatMember._parse(self, r.participant, users)
|
||||||
else:
|
else:
|
||||||
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
|
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
|
||||||
|
@ -84,9 +84,14 @@ class IterChatMembers(BaseClient):
|
|||||||
yielded = set()
|
yielded = set()
|
||||||
queries = [query] if query else QUERIES
|
queries = [query] if query else QUERIES
|
||||||
total = limit or (1 << 31) - 1
|
total = limit or (1 << 31) - 1
|
||||||
filter = Filters.RECENT if total <= 10000 and filter == Filters.ALL else filter
|
|
||||||
limit = min(200, total)
|
limit = min(200, total)
|
||||||
|
|
||||||
|
filter = (
|
||||||
|
Filters.RECENT
|
||||||
|
if self.get_chat_members_count(chat_id) <= 10000 and filter == Filters.ALL
|
||||||
|
else filter
|
||||||
|
)
|
||||||
|
|
||||||
if filter not in QUERYABLE_FILTERS:
|
if filter not in QUERYABLE_FILTERS:
|
||||||
queries = [""]
|
queries = [""]
|
||||||
|
|
||||||
|
@ -33,6 +33,19 @@ class ChatMember(PyrogramType):
|
|||||||
The member's status in the chat. Can be "creator", "administrator", "member", "restricted",
|
The member's status in the chat. Can be "creator", "administrator", "member", "restricted",
|
||||||
"left" or "kicked".
|
"left" or "kicked".
|
||||||
|
|
||||||
|
date (``int``, *optional*):
|
||||||
|
Date when the user joined, unix time. Not available for creator.
|
||||||
|
|
||||||
|
invited_by (:obj:`User <pyrogram.User>`, *optional*):
|
||||||
|
Administrators and self member only. Information about the user who invited this member.
|
||||||
|
In case the user joined by himself this will be the same as "user".
|
||||||
|
|
||||||
|
promoted_by (:obj:`User <pyrogram.User>`, *optional*):
|
||||||
|
Administrators only. Information about the user who promoted this member as administrator.
|
||||||
|
|
||||||
|
restricted_by (:obj:`User <pyrogram.User>`, *optional*):
|
||||||
|
Restricted and kicked only. Information about the user who restricted or kicked this member.
|
||||||
|
|
||||||
until_date (``int``, *optional*):
|
until_date (``int``, *optional*):
|
||||||
Restricted and kicked only. Date when restrictions will be lifted for this user, unix time.
|
Restricted and kicked only. Date when restrictions will be lifted for this user, unix time.
|
||||||
|
|
||||||
@ -86,6 +99,10 @@ class ChatMember(PyrogramType):
|
|||||||
client: "pyrogram.client.ext.BaseClient",
|
client: "pyrogram.client.ext.BaseClient",
|
||||||
user: "pyrogram.User",
|
user: "pyrogram.User",
|
||||||
status: str,
|
status: str,
|
||||||
|
date: int = None,
|
||||||
|
invited_by: "pyrogram.User" = None,
|
||||||
|
promoted_by: "pyrogram.User" = None,
|
||||||
|
restricted_by: "pyrogram.User" = None,
|
||||||
until_date: int = None,
|
until_date: int = None,
|
||||||
can_be_edited: bool = None,
|
can_be_edited: bool = None,
|
||||||
can_change_info: bool = None,
|
can_change_info: bool = None,
|
||||||
@ -104,6 +121,10 @@ class ChatMember(PyrogramType):
|
|||||||
|
|
||||||
self.user = user
|
self.user = user
|
||||||
self.status = status
|
self.status = status
|
||||||
|
self.date = date
|
||||||
|
self.invited_by = invited_by
|
||||||
|
self.promoted_by = promoted_by
|
||||||
|
self.restricted_by = restricted_by
|
||||||
self.until_date = until_date
|
self.until_date = until_date
|
||||||
self.can_be_edited = can_be_edited
|
self.can_be_edited = can_be_edited
|
||||||
self.can_change_info = can_change_info
|
self.can_change_info = can_change_info
|
||||||
@ -120,17 +141,18 @@ class ChatMember(PyrogramType):
|
|||||||
self.can_add_web_page_previews = can_add_web_page_previews
|
self.can_add_web_page_previews = can_add_web_page_previews
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse(client, member, user) -> "ChatMember":
|
def _parse(client, member, users) -> "ChatMember":
|
||||||
user = pyrogram.User._parse(client, user)
|
user = pyrogram.User._parse(client, users[member.user_id])
|
||||||
|
invited_by = pyrogram.User._parse(client, users[member.inviter_id]) if hasattr(member, "inviter_id") else None
|
||||||
|
|
||||||
if isinstance(member, (types.ChannelParticipant, types.ChannelParticipantSelf, types.ChatParticipant)):
|
if isinstance(member, (types.ChannelParticipant, types.ChannelParticipantSelf, types.ChatParticipant)):
|
||||||
return ChatMember(user=user, status="member", client=client)
|
return ChatMember(user=user, status="member", date=member.date, invited_by=invited_by, client=client)
|
||||||
|
|
||||||
if isinstance(member, (types.ChannelParticipantCreator, types.ChatParticipantCreator)):
|
if isinstance(member, (types.ChannelParticipantCreator, types.ChatParticipantCreator)):
|
||||||
return ChatMember(user=user, status="creator", client=client)
|
return ChatMember(user=user, status="creator", client=client)
|
||||||
|
|
||||||
if isinstance(member, types.ChatParticipantAdmin):
|
if isinstance(member, types.ChatParticipantAdmin):
|
||||||
return ChatMember(user=user, status="administrator", client=client)
|
return ChatMember(user=user, status="administrator", date=member.date, invited_by=invited_by, client=client)
|
||||||
|
|
||||||
if isinstance(member, types.ChannelParticipantAdmin):
|
if isinstance(member, types.ChannelParticipantAdmin):
|
||||||
rights = member.admin_rights
|
rights = member.admin_rights
|
||||||
@ -138,6 +160,9 @@ class ChatMember(PyrogramType):
|
|||||||
return ChatMember(
|
return ChatMember(
|
||||||
user=user,
|
user=user,
|
||||||
status="administrator",
|
status="administrator",
|
||||||
|
date=member.date,
|
||||||
|
invited_by=invited_by,
|
||||||
|
promoted_by=pyrogram.User._parse(client, users[member.promoted_by]),
|
||||||
can_be_edited=member.can_edit,
|
can_be_edited=member.can_edit,
|
||||||
can_change_info=rights.change_info,
|
can_change_info=rights.change_info,
|
||||||
can_post_messages=rights.post_messages,
|
can_post_messages=rights.post_messages,
|
||||||
@ -155,7 +180,13 @@ class ChatMember(PyrogramType):
|
|||||||
|
|
||||||
chat_member = ChatMember(
|
chat_member = ChatMember(
|
||||||
user=user,
|
user=user,
|
||||||
status="kicked" if rights.view_messages else "restricted",
|
status=(
|
||||||
|
"kicked" if rights.view_messages
|
||||||
|
else "left" if member.left
|
||||||
|
else "restricted"
|
||||||
|
),
|
||||||
|
date=member.date,
|
||||||
|
restricted_by=pyrogram.User._parse(client, users[member.kicked_by]),
|
||||||
until_date=0 if rights.until_date == (1 << 31) - 1 else rights.until_date,
|
until_date=0 if rights.until_date == (1 << 31) - 1 else rights.until_date,
|
||||||
client=client
|
client=client
|
||||||
)
|
)
|
||||||
|
@ -59,7 +59,7 @@ class ChatMembers(PyrogramType):
|
|||||||
total_count = len(members)
|
total_count = len(members)
|
||||||
|
|
||||||
for member in members:
|
for member in members:
|
||||||
chat_members.append(ChatMember._parse(client, member, users[member.user_id]))
|
chat_members.append(ChatMember._parse(client, member, users))
|
||||||
|
|
||||||
return ChatMembers(
|
return ChatMembers(
|
||||||
total_count=total_count,
|
total_count=total_count,
|
||||||
|
Loading…
Reference in New Issue
Block a user