2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-29 13:27:47 +00:00

Merge branch 'develop'

This commit is contained in:
Dan 2018-11-03 14:46:59 +01:00
commit a1f5267399
128 changed files with 5687 additions and 5061 deletions

View File

@ -97,13 +97,13 @@ Copyright & License
<a href="https://t.me/PyrogramChat">
Community
</a>
<br><br>
<br>
<a href="compiler/api/source/main_api.tl">
<img src="https://img.shields.io/badge/SCHEME-LAYER%2082-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
alt="Scheme Layer">
<img src="https://img.shields.io/badge/schema-layer%2082-eda738.svg?longCache=true&colorA=262b30"
alt="Schema Layer">
</a>
<a href="https://github.com/pyrogram/tgcrypto">
<img src="https://img.shields.io/badge/TGCRYPTO-V1.1.1-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
alt="TgCrypto">
</a>
</p>

View File

@ -506,6 +506,7 @@ def start():
f.write("\n 0xb0700028: \"pyrogram.client.types.Dialog\",")
f.write("\n 0xb0700029: \"pyrogram.client.types.Dialogs\",")
f.write("\n 0xb0700030: \"pyrogram.client.types.ChatMembers\",")
f.write("\n 0xb0700031: \"pyrogram.client.types.UserStatus\"")
f.write("\n}\n")

View File

@ -88,7 +88,7 @@ def generate(source_path, base):
inner_path = base + "/" + k + "/index" + ".rst"
module = "pyrogram.api.{}.{}".format(base, k)
else:
for i in list(all_entities)[::-1]:
for i in sorted(list(all_entities), reverse=True):
if i != base:
entities.insert(0, "{0}/index".format(i))

View File

@ -67,3 +67,4 @@ 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
API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
USER_NOT_PARTICIPANT The user is not a member of this chat
CHANNEL_PRIVATE The channel/supergroup is not accessible
1 id message
67 USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups
68 API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
69 USER_NOT_PARTICIPANT The user is not a member of this chat
70 CHANNEL_PRIVATE The channel/supergroup is not accessible

View File

@ -1,2 +1,4 @@
id message
CHAT_WRITE_FORBIDDEN You don't have rights to send messages in this chat
RIGHT_FORBIDDEN One or more admin rights can't be applied to this kind of chat (channel/supergroup)
CHAT_ADMIN_INVITE_REQUIRED You don't have rights to invite other users
1 id message
2 CHAT_WRITE_FORBIDDEN You don't have rights to send messages in this chat
3 RIGHT_FORBIDDEN One or more admin rights can't be applied to this kind of chat (channel/supergroup)
4 CHAT_ADMIN_INVITE_REQUIRED You don't have rights to invite other users

View File

@ -23,13 +23,13 @@ Welcome to Pyrogram
<a href="https://t.me/PyrogramChat">
Community
</a>
<br><br>
<br>
<a href="https://github.com/pyrogram/pyrogram/blob/master/compiler/api/source/main_api.tl">
<img src="https://img.shields.io/badge/SCHEME-LAYER%2082-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
<img src="https://img.shields.io/badge/schema-layer%2082-eda738.svg?longCache=true&colorA=262b30"
alt="Scheme Layer">
</a>
<a href="https://github.com/pyrogram/tgcrypto">
<img src="https://img.shields.io/badge/TGCRYPTO-V1.1.1-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
alt="TgCrypto">
</a>
</p>
@ -49,14 +49,14 @@ Welcome to Pyrogram
app.run()
Welcome to Pyrogram's Documentation! Here you can find resources for learning how to use the library.
Contents are organized by topic and can be accessed from the sidebar, or by following them one by one using the Next
button at the end of each page. But first, here's a brief overview of what is this all about.
Contents are organized into self-contained topics and can be accessed from the sidebar, or by following them in order
using the Next button at the end of each page. But first, here's a brief overview of what is this all about.
About
-----
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for building
custom Telegram applications that interact with the MTProto API as both User and Bot.
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for
building custom Telegram applications that interact with the MTProto API as both User and Bot.
Features
--------
@ -64,7 +64,7 @@ Features
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
- **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 81 on top of MTProto 2.0.
- **Updated** to the latest Telegram API version, currently Layer 82 on top of MTProto 2.0.
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
@ -84,6 +84,7 @@ To get started, press the Next button.
resources/UpdateHandling
resources/UsingFilters
resources/SmartPlugins
resources/AutoAuthorization
resources/CustomizeSessions
resources/TgCrypto

View File

@ -30,6 +30,7 @@ Decorators
on_message
on_callback_query
on_deleted_messages
on_user_status
on_disconnect
on_raw_update
@ -96,7 +97,8 @@ Users
get_me
get_users
get_user_profile_photos
delete_profile_photos
set_user_profile_photo
delete_user_profile_photos
Contacts
--------

View File

@ -3,4 +3,3 @@ Filters
.. autoclass:: pyrogram.Filters
:members:
:undoc-members:

View File

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

View File

@ -10,6 +10,7 @@ Users & Chats
:nosignatures:
User
UserStatus
Chat
ChatPhoto
ChatMember
@ -73,6 +74,9 @@ Input Media
.. autoclass:: User
:members:
.. autoclass:: UserStatus
:members:
.. autoclass:: Chat
:members:

View File

@ -35,12 +35,9 @@ Example:
password="password" # (if you have one)
)
app.start()
with app:
print(app.get_me())
app.stop()
Sign Up
-------
@ -67,8 +64,5 @@ Example:
last_name="" # Can be an empty string
)
app.start()
with app:
print(app.get_me())
app.stop()

View File

@ -0,0 +1,126 @@
Smart Plugins
=============
Pyrogram embeds a **smart** (automatic) and lightweight plugin system that is meant to further simplify the organization
of large projects and to provide a way for creating pluggable components that can be **easily shared** across different
Pyrogram applications with **minimal boilerplate code**.
.. tip::
Smart Plugins are completely optional and disabled by default.
Introduction
------------
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize
your applications, you had to do something like this...
.. note::
This is an example application that replies in private chats with two messages: one containing the same
text message you sent and the other containing the reversed text message.
Example: *"Pyrogram"* replies with *"Pyrogram"* and *"margoryP"*
.. code-block:: text
myproject/
config.ini
handlers.py
main.py
- ``handlers.py``
.. code-block:: python
def echo(client, message):
message.reply(message.text)
def echo_reversed(client, message):
message.reply(message.text[::-1])
- ``main.py``
.. code-block:: python
from pyrogram import Client, MessageHandler, Filters
from handlers import echo, echo_reversed
app = Client("my_account")
app.add_handler(
MessageHandler(
echo,
Filters.text & Filters.private))
app.add_handler(
MessageHandler(
echo_reversed,
Filters.text & Filters.private),
group=1)
app.run()
...which is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to
manually ``import``, manually :meth:`add_handler <pyrogram.Client.add_handler>` and manually instantiate each
:obj:`MessageHandler <pyrogram.MessageHandler>` object because **you can't use those cool decorators** for your
functions. So... What if you could?
Using Smart Plugins
-------------------
Setting up your Pyrogram project to accommodate Smart Plugins is pretty straightforward:
#. Create a new folder to store all the plugins (e.g.: "plugins").
#. Put your files full of plugins inside.
#. Enable plugins in your Client.
.. note::
This is the same example application `as shown above <#introduction>`_, written using the Smart Plugin system.
.. code-block:: text
:emphasize-lines: 2, 3
myproject/
plugins/
handlers.py
config.ini
main.py
- ``plugins/handlers.py``
.. code-block:: python
:emphasize-lines: 4, 9
from pyrogram import Client, Filters
@Client.on_message(Filters.text & Filters.private)
def echo(client, message):
message.reply(message.text)
@Client.on_message(Filters.text & Filters.private, group=1)
def echo_reversed(client, message):
message.reply(message.text[::-1])
- ``main.py``
.. code-block:: python
from pyrogram import Client
Client("my_account", plugins_dir="plugins").run()
The first important thing to note is the new ``plugins`` folder, whose name is passed to the the ``plugins_dir``
parameter when creating a :obj:`Client <pyrogram.Client>` in the ``main.py`` file — you can put *any python file* in
there and each file can contain *any decorated function* (handlers) with only one limitation: within a single plugin
file you must use different names for each decorated function. Your Pyrogram Client instance will **automatically**
scan the folder upon creation to search for valid handlers and register them for you.
Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback
functions in a static way, i.e. **without having the Client instance around**: simply use ``@Client`` (Client class)
instead of the usual ``@app`` (Client instance) namespace and things will work just the same.

View File

@ -2,15 +2,15 @@ Update Handling
===============
Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...)
and can be handled by registering one or more callback functions in your app by using an `Handler <../pyrogram/Handlers.html>`_.
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
function(s) will be called back with the update itself as argument.
function(s) matching it will be called back with the update itself as argument.
Registering an Handler
----------------------
To explain how `Handlers <../pyrogram/Handlers.html>`_ work let's have a look at the most used one, the
To explain how handlers work let's have a look at the most used one, the
:obj:`MessageHandler <pyrogram.MessageHandler>`, which will be in charge for handling :obj:`Message <pyrogram.Message>`
updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles
settings them up once you learn from this section.

View File

@ -4,48 +4,85 @@ Installation
Being a Python library, Pyrogram requires Python to be installed in your system.
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) and pip
by following the instructions at https://pip.pypa.io/en/latest/installing/.
.. note::
Pyrogram supports Python 3 only, starting from version 3.4 and PyPy.
.. important::
Pyrogram supports **Python 3** only, starting from version 3.4. **PyPy** is supported too.
Install Pyrogram
----------------
- The easiest way to install and upgrade Pyrogram is by using **pip**:
- The easiest way to install and upgrade Pyrogram to its latest stable version is by using **pip**:
.. code-block:: bash
.. code-block:: text
$ pip3 install --upgrade pyrogram
- or, with TgCrypto_ (recommended):
- or, with TgCrypto_ as extra requirement (recommended):
.. code-block:: bash
.. code-block:: text
$ pip3 install --upgrade pyrogram[tgcrypto]
$ pip3 install --upgrade pyrogram[fast]
Bleeding Edge
-------------
If you want the latest development version of Pyrogram, you can install it straight from the develop_
branch using this command:
branch using this command (you might need to install **git** first):
.. code-block:: bash
.. code-block:: text
$ pip3 install --upgrade git+https://github.com/pyrogram/pyrogram.git
Asynchronous
------------
Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging client library after all), and here's
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).
Use this command to install:
.. code-block:: text
$ pip3 install --upgrade git+https://github.com/pyrogram/pyrogram.git@asyncio
Pyrogram API remains the same and features are kept up to date from the non-async, default develop branch, but you
are obviously required Python asyncio knowledge in order to take full advantage of it.
.. tip::
The idea to turn Pyrogram fully asynchronous is still under consideration, but is wise to expect that in future this
would be the one and only way to work with Pyrogram.
You can start using Pyrogram Async variant right now as an excuse to learn more about asynchronous programming and
do experiments with it!
.. raw:: html
<script async
src="https://telegram.org/js/telegram-widget.js?4"
data-telegram-post="Pyrogram/4"
data-width="100%">
</script>
.. centered:: Subscribe to `@Pyrogram <https://t.me/Pyrogram>`_ for news and announcements
Verifying
---------
To verify that Pyrogram is correctly installed, open a Python shell and import it.
If no error shows up you are good to go.
.. code-block:: bash
.. code-block:: python
>>> import pyrogram
>>> pyrogram.__version__
'0.8.0'
'0.9.0'
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
.. _develop: http://github.com/pyrogram/pyrogram

View File

@ -53,6 +53,7 @@ fits better for you:
)
.. note::
The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id*
and *api_hash* parameters usage.
@ -74,7 +75,7 @@ the :class:`Client <pyrogram.Client>` class by passing to it a ``session_name``
This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_)
and the **phone code** you will receive:
.. code::
.. code-block:: text
Enter phone number: +39**********
Is "+39**********" correct? (y/n): y
@ -84,7 +85,9 @@ After successfully authorizing yourself, a new file called ``my_account.session`
Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app,
and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number.
.. important:: Your ``*.session`` files are personal and must be kept secret.
.. important::
Your ``*.session`` files are personal and must be kept secret.
Bot Authorization
-----------------

View File

@ -10,35 +10,50 @@ High-level API
The easiest and recommended way to interact with Telegram is via the high-level Pyrogram methods_ and types_, which are
named after the `Telegram Bot API`_.
Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_):
- Get information about the authorized user:
Here's a simple example:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
app.start()
print(app.get_me())
app.send_message("me", "Hi there! I'm using **Pyrogram**")
app.send_location("me", 51.500729, -0.124583)
- Send a message to yourself (Saved Messages):
app.stop()
You can also use Pyrogram in a context manager with the ``with`` statement. The Client will automatically
:meth:`start <pyrogram.Client.start>` and :meth:`stop <pyrogram.Client.stop>` gracefully, even in case of unhandled
exceptions in your code:
.. code-block:: python
app.send_message("me", "Hi there! I'm using Pyrogram")
from pyrogram import Client
- Upload a new photo (with caption):
app = Client("my_account")
.. code-block:: python
with app:
print(app.get_me())
app.send_message("me", "Hi there! I'm using **Pyrogram**")
app.send_location("me", 51.500729, -0.124583)
app.send_photo("me", "/home/dan/perla.jpg", "Cute!")
More examples on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_.
Raw Functions
-------------
If you can't find a high-level method for your needs or if want complete, low-level access to the whole Telegram API,
If you can't find a high-level method for your needs or if you want complete, low-level access to the whole Telegram API,
you have to use the raw :mod:`functions <pyrogram.api.functions>` and :mod:`types <pyrogram.api.types>` 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.
.. hint:: Every high-level method mentioned in the section above is built on top of these raw functions.
.. hint::
Every high-level method mentioned in the section above is built on top of these raw functions.
Nothing stops you from using the raw functions only, but they are rather complex and `plenty of them`_ are already
re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API.
@ -54,9 +69,7 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
from pyrogram import Client
from pyrogram.api import functions
app = Client("my_account")
app.start()
with Client("my_account") as app:
app.send(
functions.account.UpdateProfile(
first_name="Dan", last_name="Tès",
@ -64,8 +77,6 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
)
)
app.stop()
- Share your Last Seen time only with your contacts:
.. code-block:: python
@ -73,9 +84,7 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
from pyrogram import Client
from pyrogram.api import functions, types
app = Client("my_account")
app.start()
with Client("my_account") as app:
app.send(
functions.account.SetPrivacy(
key=types.InputPrivacyKeyStatusTimestamp(),
@ -83,8 +92,6 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
)
)
app.stop()
- Invite users to your channel/supergroup:
.. code-block:: python
@ -92,9 +99,7 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
from pyrogram import Client
from pyrogram.api import functions, types
app = Client("my_account")
app.start()
with Client("my_account") as app:
app.send(
functions.channels.InviteToChannel(
channel=app.resolve_peer(123456789), # ID or Username
@ -106,8 +111,6 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
)
)
app.stop()
.. _methods: ../pyrogram/Client.html#messages
.. _plenty of them: ../pyrogram/Client.html#messages
.. _types: ../pyrogram/Types.html

121
examples/LICENSE Normal file
View File

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -5,14 +5,17 @@ This folder contains example scripts to show you how **Pyrogram** looks like.
Every script is working right away (provided you correctly set up your credentials), meaning
you can simply copy-paste and run. The only things you have to change are session names and target chats.
All the examples listed in this directory are licensed under the terms of the [CC0 1.0 Universal](LICENSE) license and
can be freely used as basic building blocks for your own applications without worrying about copyrights.
Example | Description
---: | :---
[**hello_world**](hello_world.py) | Demonstration of basic API usages
[**echo_bot**](echo_bot.py) | Echo bot that replies to every private text message
[**welcome_bot**](welcome_bot.py) | The Welcome Bot source code in [@PyrogramChat](https://t.me/pyrogramchat)
[**get_history**](get_history.py) | How to retrieve the full message history of a chat
[**get_participants**](get_participants.py) | How to get the first 10.000 members of a supergroup/channel
[**get_participants2**](get_participants2.py) | Improved version to get more than 10.000 users
[**get_chat_members**](get_chat_members.py) | How to get the first 10.000 members of a supergroup/channel
[**get_chat_members2**](get_chat_members2.py) | Improved version to get more than 10.000 members
[**query_inline_bots**](query_inline_bots.py) | How to query an inline bot and send a result to a chat
[**send_bot_keyboards**](send_bot_keyboards.py) | How to send normal and inline keyboards using regular bots
[**callback_query_handler**](callback_query_handler.py) | How to handle queries coming from inline button presses

View File

@ -1,37 +1,16 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram import Client
"""This example shows how to handle callback queries, i.e.: queries coming from inline button presses.
It uses the @on_callback_query decorator to register a CallbackQueryHandler."""
It uses the @on_callback_query decorator to register a CallbackQueryHandler.
"""
from pyrogram import Client
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
@app.on_callback_query()
def answer(client, callback_query):
client.answer_callback_query(
callback_query.id,
text='Button contains: "{}"'.format(callback_query.data),
show_alert=True
)
callback_query.answer('Button contains: "{}"'.format(callback_query.data), show_alert=True)
app.run() # Automatically start() and idle()

View File

@ -1,39 +1,17 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram import Client, Filters
"""This simple echo bot replies to every private text message.
It uses the @on_message decorator to register a MessageHandler
and applies two filters on it, Filters.text and Filters.private to make
sure it will only reply to private text messages.
It uses the @on_message decorator to register a MessageHandler and applies two filters on it:
Filters.text and Filters.private to make sure it will reply to private text messages only.
"""
from pyrogram import Client, Filters
app = Client("my_account")
@app.on_message(Filters.text & Filters.private)
def echo(client, message):
client.send_message(
message.chat.id, message.text,
reply_to_message_id=message.message_id
)
message.reply(message.text, quote=True)
app.run() # Automatically start() and idle()

View File

@ -0,0 +1,31 @@
"""This example shows you how to get the first 10.000 members of a chat.
Refer to get_chat_members2.py for more than 10.000 members.
"""
import time
from pyrogram import Client
from pyrogram.api.errors import FloodWait
app = Client("my_account")
target = "pyrogramchat" # Target channel/supergroup
members = [] # List that will contain all the members of the target chat
offset = 0 # Offset starts at 0
limit = 200 # Amount of users to retrieve for each API call (max 200)
with app:
while True:
try:
chunk = app.get_chat_members(target, offset)
except FloodWait as e: # Very large chats could trigger FloodWait
time.sleep(e.x) # When it happens, wait X seconds and try again
continue
if not chunk.chat_members:
break # No more members left
members.extend(chunk.chat_members)
offset += len(chunk.chat_members)
# Now the "members" list contains all the members of the target chat

View File

@ -0,0 +1,50 @@
"""This is an improved version of get_chat_members.py
Since Telegram will return at most 10.000 members for a single query, this script
repeats the search using numbers ("0" to "9") and all the available ascii letters ("a" to "z").
This can be further improved by also searching for non-ascii characters (e.g.: Japanese script),
as some user names may not contain ascii letters at all.
"""
import time
from string import ascii_lowercase
from pyrogram import Client
from pyrogram.api.errors import FloodWait
app = Client("my_account")
target = "pyrogramchat" # Target channel/supergroup
members = {} # List that will contain all the members of the target chat
limit = 200 # Amount of users to retrieve for each API call (max 200)
# "" + "0123456789" + "abcdefghijklmnopqrstuvwxyz" (as list)
queries = [""] + [str(i) for i in range(10)] + list(ascii_lowercase)
with app:
for q in queries:
print('Searching for "{}"'.format(q))
offset = 0 # For each query, offset restarts from 0
while True:
try:
chunk = app.get_chat_members(target, offset, query=q)
except FloodWait as e: # Very large chats could trigger FloodWait
print("Flood wait: {} seconds".format(e.x))
time.sleep(e.x) # When it happens, wait X seconds and try again
continue
if not chunk.chat_members:
print('Done searching for "{}"'.format(q))
print()
break # No more members left
members.update({i.user.id: i for i in chunk.chat_members})
offset += len(chunk.chat_members)
print("Total members: {}".format(len(members)))
print("Grand total: {}".format(len(members)))
# Now the "members" list contains all the members of the target chat

View File

@ -1,40 +1,20 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
"""This example shows how to retrieve the full message history of a chat"""
import time
from pyrogram import Client
from pyrogram.api.errors import FloodWait
"""This example shows how to retrieve the full message history of a chat"""
app = Client("my_account")
target = "me" # "me" refers to your own chat (Saved Messages)
messages = [] # List that will contain all the messages of the target chat
offset_id = 0 # ID of the last message of the chunk
app.start()
while True:
with app:
while True:
try:
m = app.get_history(target, offset_id=offset_id)
except FloodWait as e:
# For very large chats the method call can raise a FloodWait
except FloodWait as e: # For very large chats the method call can raise a FloodWait
print("waiting {}".format(e.x))
time.sleep(e.x) # Sleep X seconds before continuing
continue
@ -47,7 +27,5 @@ while True:
print("Messages: {}".format(len(messages)))
app.stop()
# Now the "messages" list contains all the messages sorted by date in
# descending order (from the most recent to the oldest one)

View File

@ -1,63 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import time
from pyrogram import Client
from pyrogram.api import functions, types
from pyrogram.api.errors import FloodWait
"""This simple GetParticipants method usage shows you how to get the first 10.000 users of a chat.
Refer to get_participants2.py for more than 10.000 users.
"""
app = Client("my_account")
target = "pyrogramchat" # Target channel/supergroup
users = [] # List that will contain all the users of the target chat
limit = 200 # Amount of users to retrieve for each API call
offset = 0 # Offset starts at 0
app.start()
while True:
try:
participants = app.send(
functions.channels.GetParticipants(
channel=app.resolve_peer(target),
filter=types.ChannelParticipantsSearch(""), # Filter by empty string (search for all)
offset=offset,
limit=limit,
hash=0
)
)
except FloodWait as e:
# Very large channels will trigger FloodWait.
# When happens, wait X seconds before continuing
time.sleep(e.x)
continue
if not participants.participants:
break # No more participants left
users.extend(participants.users)
offset += limit
app.stop()
# Now the "users" list contains all the members of the target chat

View File

@ -1,79 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import time
from string import ascii_lowercase
from pyrogram import Client
from pyrogram.api import functions, types
from pyrogram.api.errors import FloodWait
"""This is an improved version of get_participants.py
Since Telegram will return at most 10.000 users for a single query, this script
repeats the search using numbers ("0" to "9") and all the available ascii letters ("a" to "z").
This can be further improved by also searching for non-ascii characters (e.g.: Japanese script),
as some user names may not contain ascii letters at all.
"""
app = Client("my_account")
target = "pyrogramchat" # Target channel/supergroup username or id
users = {} # To ensure uniqueness, users will be stored in a dictionary with user_id as key
limit = 200 # Amount of users to retrieve for each API call (200 is the maximum)
# "" + "0123456789" + "abcdefghijklmnopqrstuvwxyz" (as list)
queries = [""] + [str(i) for i in range(10)] + list(ascii_lowercase)
app.start()
for q in queries:
print("Searching for '{}'".format(q))
offset = 0 # For each query, offset restarts from 0
while True:
try:
participants = app.send(
functions.channels.GetParticipants(
channel=app.resolve_peer(target),
filter=types.ChannelParticipantsSearch(q),
offset=offset,
limit=limit,
hash=0
)
)
except FloodWait as e:
# Very large chats could trigger FloodWait.
# When happens, wait X seconds before continuing
print("Flood wait: {} seconds".format(e.x))
time.sleep(e.x)
continue
if not participants.participants:
print("Done searching for '{}'".format(q))
print()
break # No more participants left
# User information are stored in the participants.users list.
# Add those users to the dictionary
users.update({i.id: i for i in participants.users})
offset += len(participants.participants)
print("Total users: {}".format(len(users)))
app.stop()

View File

@ -1,25 +1,7 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
"""This example demonstrates a basic API usage"""
from pyrogram import Client
"""This example demonstrates a basic API usage"""
# Create a new Client instance
app = Client("my_account")

View File

@ -1,25 +1,7 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
"""This example shows how to query an inline bot"""
from pyrogram import Client
"""This example shows how to query an inline bot"""
# Create a new Client
app = Client("my_account")

View File

@ -1,25 +1,7 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
"""This example shows how to handle raw updates"""
from pyrogram import Client
"""This example shows how to handle raw updates"""
app = Client("my_account")

View File

@ -1,23 +1,3 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
"""This example will show you how to send normal and inline keyboards.
You must log-in as a regular bot in order to send keyboards (use the token from @BotFather).
@ -27,6 +7,8 @@ send_message() is used as example, but a keyboard can be sent with any other sen
like send_audio(), send_document(), send_location(), etc...
"""
from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
# Create a client using your bot token
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
app.start()

View File

@ -1,52 +1,27 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram import Client, Emoji, Filters
"""This is the Welcome Bot in @PyrogramChat.
It uses the Emoji module to easily add emojis in your text messages and Filters
to make it only work for specific messages in a specific chat
to make it only work for specific messages in a specific chat.
"""
from pyrogram import Client, Emoji, Filters
MENTION = "[{}](tg://user?id={})"
MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!"
app = Client("my_account")
@app.on_message(Filters.chat("PyrogramChat") & Filters.new_chat_members)
def welcome(client, message):
# Build the new members list (with mentions) by using their first_name
new_members = ", ".join([
"[{}](tg://user?id={})".format(i.first_name, i.id)
for i in message.new_chat_members
])
new_members = [MENTION.format(i.first_name, i.id) for i in message.new_chat_members]
# Build the welcome message by using an emoji and the list we built above
text = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!".format(
Emoji.SPARKLES,
new_members
)
text = MESSAGE.format(Emoji.SPARKLES, ", ".join(new_members))
# Send the welcome message
client.send_message(
message.chat.id, text,
reply_to_message_id=message.message_id,
disable_web_page_preview=True
)
message.reply(text, disable_web_page_preview=True)
app.run() # Automatically start() and idle()

View File

@ -23,18 +23,18 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
"e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
)
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.8.0"
__version__ = "0.9.0"
from .api.errors import Error
from .client.types import (
Audio, Chat, ChatMember, ChatMembers, ChatPhoto, Contact, Document, InputMediaPhoto,
InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact,
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, Update, User,
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, Update, User, UserStatus,
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
)
from .client import (
Client, ChatAction, ParseMode, Emoji,
MessageHandler, DeletedMessagesHandler, CallbackQueryHandler,
RawUpdateHandler, DisconnectHandler, Filters
RawUpdateHandler, DisconnectHandler, UserStatusHandler, Filters
)

View File

@ -16,7 +16,6 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
from collections import OrderedDict
from datetime import datetime
from io import BytesIO
@ -24,23 +23,13 @@ from json import JSONEncoder, dumps
from ..all import objects
log = logging.getLogger(__name__)
class Object:
all = {}
@staticmethod
def read(b: BytesIO, *args):
constructor_id = int.from_bytes(b.read(4), "little")
try:
return Object.all[constructor_id].read(b, *args)
except KeyError:
log.error("Unknown constructor found: {}. Full data: {}".format(
hex(constructor_id),
b.getvalue().hex())
)
return Object.all[int.from_bytes(b.read(4), "little")].read(b, *args)
def write(self, *args) -> bytes:
pass

View File

@ -22,5 +22,5 @@ from .filters import Filters
from .handlers import (
MessageHandler, DeletedMessagesHandler,
CallbackQueryHandler, RawUpdateHandler,
DisconnectHandler
DisconnectHandler, UserStatusHandler
)

View File

@ -33,6 +33,8 @@ import time
from configparser import ConfigParser
from datetime import datetime
from hashlib import sha256, md5
from importlib import import_module
from pathlib import Path
from signal import signal, SIGINT, SIGTERM, SIGABRT
from threading import Thread
@ -43,8 +45,9 @@ from pyrogram.api.errors import (
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned,
VolumeLocNotFound, UserMigrate, FileIdInvalid)
VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate)
from pyrogram.client.handlers import DisconnectHandler
from pyrogram.client.handlers.handler import Handler
from pyrogram.crypto import AES
from pyrogram.session import Auth, Session
from .dispatcher import Dispatcher
@ -90,6 +93,10 @@ class Client(Methods, BaseClient):
Code of the language used on the client, in ISO 639-1 standard. Defaults to "en".
This is an alternative way to set it if you don't want to use the *config.ini* file.
ipv6 (``bool``, *optional*):
Pass True to connect to Telegram using IPv6.
Defaults to False (IPv4).
proxy (``dict``, *optional*):
Your SOCKS5 Proxy settings as dict,
e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*.
@ -136,6 +143,11 @@ class Client(Methods, BaseClient):
config_file (``str``, *optional*):
Path of the configuration file. Defaults to ./config.ini
plugins_dir (``str``, *optional*):
Define a custom directory for your plugins. The plugins directory is the location in your
filesystem where Pyrogram will automatically load your update handlers.
Defaults to None (plugins disabled).
"""
def __init__(self,
@ -155,9 +167,10 @@ class Client(Methods, BaseClient):
force_sms: bool = False,
first_name: str = None,
last_name: str = None,
workers: int = 4,
workdir: str = ".",
config_file: str = "./config.ini"):
workers: int = BaseClient.WORKERS,
workdir: str = BaseClient.WORKDIR,
config_file: str = BaseClient.CONFIG_FILE,
plugins_dir: str = None):
super().__init__()
self.session_name = session_name
@ -180,6 +193,7 @@ class Client(Methods, BaseClient):
self.workers = workers
self.workdir = workdir
self.config_file = config_file
self.plugins_dir = plugins_dir
self.dispatcher = Dispatcher(self, workers)
@ -204,7 +218,8 @@ class Client(Methods, BaseClient):
Requires no parameters.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ConnectionError`` in case you try to start an already started Client.
"""
if self.is_started:
raise ConnectionError("Client has already been started")
@ -215,6 +230,7 @@ class Client(Methods, BaseClient):
self.load_config()
self.load_session()
self.load_plugins()
self.session = Session(
self,
@ -281,6 +297,9 @@ class Client(Methods, BaseClient):
def stop(self):
"""Use this method to manually stop the Client.
Requires no parameters.
Raises:
``ConnectionError`` in case you try to stop an already stopped Client.
"""
if not self.is_started:
raise ConnectionError("Client is already stopped")
@ -340,7 +359,7 @@ class Client(Methods, BaseClient):
Requires no parameters.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
self.start()
self.idle()
@ -799,6 +818,7 @@ class Client(Methods, BaseClient):
message = update.message
if not isinstance(message, types.MessageEmpty):
try:
diff = self.send(
functions.updates.GetChannelDifference(
channel=self.resolve_peer(int("-100" + str(channel_id))),
@ -812,7 +832,9 @@ class Client(Methods, BaseClient):
limit=pts
)
)
except ChannelPrivate:
pass
else:
if not isinstance(diff, types.updates.ChannelDifferenceEmpty):
updates.users += diff.users
updates.chats += diff.chats
@ -869,7 +891,7 @@ class Client(Methods, BaseClient):
Args:
data (``Object``):
The API Scheme function filled with proper arguments.
The API Schema function filled with proper arguments.
retries (``int``):
Number of retries.
@ -878,7 +900,7 @@ class Client(Methods, BaseClient):
Timeout in seconds.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
if not self.is_started:
raise ConnectionError("Client has not been started")
@ -961,6 +983,45 @@ class Client(Methods, BaseClient):
if peer:
self.peers_by_phone[k] = peer
def load_plugins(self):
if self.plugins_dir is not None:
plugins_count = 0
for path in Path(self.plugins_dir).rglob("*.py"):
file_path = os.path.splitext(str(path))[0]
import_path = []
while file_path:
file_path, tail = os.path.split(file_path)
import_path.insert(0, tail)
import_path = ".".join(import_path)
module = import_module(import_path)
for name in dir(module):
# noinspection PyBroadException
try:
handler, group = getattr(module, name)
if isinstance(handler, Handler) and isinstance(group, int):
self.add_handler(handler, group)
log.info('{}("{}") from "{}" loaded in group {}'.format(
type(handler).__name__, name, import_path, group))
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:
log.warning('No plugin loaded: "{}" doesn\'t contain any valid plugin'.format(self.plugins_dir))
def save_session(self):
auth_key = base64.b64encode(self.auth_key).decode()
auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)]
@ -1028,7 +1089,8 @@ class Client(Methods, BaseClient):
On success, the resolved peer id is returned in form of an InputPeer object.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``KeyError`` in case the peer doesn't exist in the internal database.
"""
if type(peer_id) is str:
if peer_id in ("self", "me"):
@ -1068,6 +1130,13 @@ class Client(Methods, BaseClient):
progress_args: tuple = ()):
part_size = 512 * 1024
file_size = os.path.getsize(path)
if file_size == 0:
raise ValueError("File size equals to 0 B")
if file_size > 1500 * 1024 * 1024:
raise ValueError("Telegram doesn't support uploading files bigger than 1500 MiB")
file_total_parts = int(math.ceil(file_size / part_size))
is_big = True if file_size > 10 * 1024 * 1024 else False
is_missing_part = True if file_id is not None else False

View File

@ -25,7 +25,7 @@ from threading import Thread
import pyrogram
from pyrogram.api import types
from ..ext import utils
from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler
from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, UserStatusHandler
log = logging.getLogger(__name__)
@ -90,6 +90,7 @@ class Dispatcher:
def dispatch(self, update, users: dict = None, chats: dict = None, is_raw: bool = False):
for group in self.groups.values():
try:
for handler in group:
if is_raw:
if not isinstance(handler, RawUpdateHandler):
@ -107,6 +108,8 @@ class Dispatcher:
callback_query = update.callback_query
user_status = update.user_status
if message and isinstance(handler, MessageHandler):
if not handler.check(message):
continue
@ -122,11 +125,18 @@ class Dispatcher:
continue
args = (self.client, callback_query)
elif user_status and isinstance(handler, UserStatusHandler):
if not handler.check(user_status):
continue
args = (self.client, user_status)
else:
continue
handler.callback(*args)
break
except Exception as e:
log.error(e, exc_info=True)
def update_worker(self):
name = threading.current_thread().name
@ -202,7 +212,15 @@ class Dispatcher:
self.dispatch(
pyrogram.Update(
callback_query=utils.parse_inline_callback_query(
update, users
self.client, update, users
)
)
)
elif isinstance(update, types.UpdateUserStatus):
self.dispatch(
pyrogram.Update(
user_status=utils.parse_user_status(
update.status, update.user_id
)
)
)

View File

@ -49,6 +49,9 @@ class BaseClient:
UPDATES_WORKERS = 1
DOWNLOAD_WORKERS = 1
OFFLINE_SLEEP = 300
WORKERS = 4
WORKDIR = "."
CONFIG_FILE = "./config.ini"
MEDIA_TYPE_ID = {
0: "thumbnail",
@ -116,7 +119,8 @@ class BaseClient:
def get_messages(
self,
chat_id: int or str,
message_ids,
message_ids: int or list = None,
reply_to_message_ids: int or list = None,
replies: int = 1
):
pass

File diff suppressed because it is too large Load Diff

View File

@ -129,6 +129,30 @@ def parse_chat_photo(photo):
)
def parse_user_status(user_status, user_id: int = None, is_bot: bool = False) -> pyrogram_types.UserStatus or None:
if is_bot:
return None
status = pyrogram_types.UserStatus(user_id)
if isinstance(user_status, types.UserStatusOnline):
status.online = True
status.date = user_status.expires
elif isinstance(user_status, types.UserStatusOffline):
status.offline = True
status.date = user_status.was_online
elif isinstance(user_status, types.UserStatusRecently):
status.recently = True
elif isinstance(user_status, types.UserStatusLastWeek):
status.within_week = True
elif isinstance(user_status, types.UserStatusLastMonth):
status.within_month = True
else:
status.long_time_ago = True
return status
def parse_user(user: types.User) -> pyrogram_types.User or None:
return pyrogram_types.User(
id=user.id,
@ -142,7 +166,9 @@ def parse_user(user: types.User) -> pyrogram_types.User or None:
username=user.username,
language_code=user.lang_code,
phone_number=user.phone,
photo=parse_chat_photo(user.photo)
photo=parse_chat_photo(user.photo),
status=parse_user_status(user.status, is_bot=user.bot),
restriction_reason=user.restriction_reason
) if user else None
@ -162,7 +188,8 @@ def parse_user_chat(user: types.User) -> pyrogram_types.Chat:
username=user.username,
first_name=user.first_name,
last_name=user.last_name,
photo=parse_chat_photo(user.photo)
photo=parse_chat_photo(user.photo),
restriction_reason=user.restriction_reason
)
@ -187,7 +214,8 @@ def parse_channel_chat(channel: types.Channel) -> pyrogram_types.Chat:
type="supergroup" if channel.megagroup else "channel",
title=channel.title,
username=getattr(channel, "username", None),
photo=parse_chat_photo(getattr(channel, "photo", None))
photo=parse_chat_photo(getattr(channel, "photo", None)),
restriction_reason=getattr(channel, "restriction_reason")
)
@ -610,7 +638,8 @@ def parse_messages(
while True:
try:
m.reply_to_message = client.get_messages(
m.chat.id, message.reply_to_msg_id,
m.chat.id,
reply_to_message_ids=message.id,
replies=replies - 1
)
except FloodWait as e:
@ -722,7 +751,8 @@ def parse_messages(
while True:
try:
m.pinned_message = client.get_messages(
m.chat.id, message.reply_to_msg_id,
m.chat.id,
reply_to_message_ids=message.id,
replies=0
)
except FloodWait as e:
@ -865,11 +895,12 @@ def parse_callback_query(client, callback_query, users):
message=client.get_messages(peer_id, callback_query.msg_id),
chat_instance=str(callback_query.chat_instance),
data=callback_query.data.decode(),
game_short_name=callback_query.game_short_name
game_short_name=callback_query.game_short_name,
client=client
)
def parse_inline_callback_query(callback_query, users):
def parse_inline_callback_query(client, callback_query, users):
return pyrogram_types.CallbackQuery(
id=str(callback_query.query_id),
from_user=parse_user(users[callback_query.user_id]),
@ -883,7 +914,8 @@ def parse_inline_callback_query(callback_query, users):
),
b"-_"
).decode().rstrip("="),
game_short_name=callback_query.game_short_name
game_short_name=callback_query.game_short_name,
client=client
)
@ -918,7 +950,7 @@ def parse_chat_full(
if full_chat.pinned_msg_id:
parsed_chat.pinned_message = client.get_messages(
parsed_chat.id,
full_chat.pinned_msg_id
message_ids=full_chat.pinned_msg_id
)
if isinstance(full_chat.exported_invite, types.ChatInviteExported):

View File

@ -166,9 +166,11 @@ class Filters:
inline_keyboard = create("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup))
"""Filter messages containing inline keyboard markups"""
dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162))
@staticmethod
def command(command: str or list,
prefix: str = "/",
prefix: str or list = "/",
separator: str = " ",
case_sensitive: bool = False):
"""Filter commands, i.e.: text messages starting with "/" or any other custom prefix.
@ -180,9 +182,10 @@ class Filters:
a command arrives, the command itself and its arguments will be stored in the *command*
field of the :class:`Message <pyrogram.Message>`.
prefix (``str``, *optional*):
The command prefix. Defaults to "/" (slash).
Examples: /start, .help, !settings.
prefix (``str`` | ``list``, *optional*):
A prefix or a list of prefixes as string the filter should look for.
Defaults to "/" (slash). Examples: ".", "!", ["/", "!", "."].
Can be None or "" (empty string) to allow commands with no prefix at all.
separator (``str``, *optional*):
The command arguments separator. Defaults to " " (white space).
@ -194,11 +197,14 @@ class Filters:
"""
def f(_, m):
if m.text and m.text.startswith(_.p):
if m.text:
for i in _.p:
if m.text.startswith(i):
t = m.text.split(_.s)
c, a = t[0][len(_.p):], t[1:]
c, a = t[0][len(i):], t[1:]
c = c if _.cs else c.lower()
m.command = ([c] + a) if c in _.c else None
break
return bool(m.command)
@ -211,7 +217,7 @@ class Filters:
else {c if case_sensitive
else c.lower()
for c in command},
p=prefix,
p=set(prefix) if prefix else {""},
s=separator,
cs=case_sensitive
)

View File

@ -17,7 +17,8 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .callback_query_handler import CallbackQueryHandler
from .deleted_messages_handler import DeletedMessagesHandler
from .disconnect_handler import DisconnectHandler
from .message_handler import MessageHandler
from .deleted_messages_handler import DeletedMessagesHandler
from .raw_update_handler import RawUpdateHandler
from .user_status_handler import UserStatusHandler

View File

@ -0,0 +1,54 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .handler import Handler
class UserStatusHandler(Handler):
"""The UserStatus handler class. Used to handle user status updates (user going online or offline).
It is intended to be used with :meth:`add_handler() <pyrogram.Client.add_handler>`
For a nicer way to register this handler, have a look at the
:meth:`on_user_status() <pyrogram.Client.on_user_status>` decorator.
Args:
callback (``callable``):
Pass a function that will be called when a new UserStatus update arrives. It takes *(client, user_status)*
as positional arguments (look at the section below for a detailed description).
filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of messages to be passed
in your callback function.
Other parameters:
client (:obj:`Client <pyrogram.Client>`):
The Client itself, useful when you want to call other API methods inside the user status handler.
user_status (:obj:`UserStatus <pyrogram.UserStatus>`):
The received UserStatus update.
"""
def __init__(self, callback: callable, filters=None):
super().__init__(callback, filters)
def check(self, user_status):
return (
self.filters(user_status)
if callable(self.filters)
else True
)

View File

@ -50,6 +50,12 @@ class AnswerCallbackQuery(BaseClient):
cache_time (``int``):
The maximum amount of time in seconds that the result of the callback query may be cached client-side.
Telegram apps will support caching starting in version 3.14. Defaults to 0.
Returns:
True, on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
return self.send(
functions.messages.SetBotCallbackAnswer(

View File

@ -54,8 +54,8 @@ class GetInlineBotResults(BaseClient):
On Success, :obj:`BotResults <pyrogram.api.types.messages.BotResults>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
``TimeoutError``: If the bot fails to answer within 10 seconds
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``TimeoutError`` if the bot fails to answer within 10 seconds
"""
# TODO: Don't return the raw type

View File

@ -45,8 +45,8 @@ class RequestCallbackAnswer(BaseClient):
or as an alert.
Raises:
:class:`Error <pyrogram.Error>`
``TimeoutError``: If the bot fails to answer within 10 seconds
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``TimeoutError`` if the bot fails to answer within 10 seconds.
"""
return self.send(
functions.messages.GetBotCallbackAnswer(

View File

@ -53,7 +53,7 @@ class SendInlineBotResult(BaseClient):
On success, the sent Message is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
return self.send(
functions.messages.SendInlineBotResult(

View File

@ -38,8 +38,8 @@ class DeleteChatPhoto(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@ -35,7 +35,7 @@ class ExportChatInviteLink(BaseClient):
On success, the exported invite link as string is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
peer = self.resolve_peer(chat_id)

View File

@ -33,7 +33,7 @@ class GetChat(BaseClient):
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
peer = self.resolve_peer(chat_id)

View File

@ -39,7 +39,7 @@ class GetChatMember(BaseClient):
On success, a :obj:`ChatMember <pyrogram.ChatMember>` object is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
chat_id = self.resolve_peer(chat_id)
user_id = self.resolve_peer(user_id)

View File

@ -39,7 +39,7 @@ class GetChatMembers(BaseClient):
"""Use this method to get the members list of a chat.
A chat can be either a basic group, a supergroup or a channel.
You must be admin to retrieve the members (also known as "subscribers") list of a channel.
You must be admin to retrieve the members list of a channel (also known as "subscribers").
Args:
chat_id (``int`` | ``str``):
@ -51,7 +51,7 @@ class GetChatMembers(BaseClient):
limit (``int``, *optional*):
Limits the number of members to be retrieved.
Defaults to 200, which is also the maximum limit allowed per method call.
Defaults to 200, which is also the maximum server limit allowed per method call.
query (``str``, *optional*):
Query string to filter members based on their display names and usernames.
@ -68,9 +68,17 @@ class GetChatMembers(BaseClient):
*"administrators"* - chat administrators only.
Defaults to *"all"*.
.. [1] On supergroups and channels you can get up to 10,000 members for a single query string.
.. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members
on channels.
.. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only.
Returns:
On success, a :obj:`ChatMembers` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if you used an invalid filter or a chat_id that belongs to a user.
"""
peer = self.resolve_peer(chat_id)

View File

@ -32,8 +32,8 @@ class GetChatMembersCount(BaseClient):
On success, an integer is returned.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@ -47,7 +47,7 @@ class GetDialogs(BaseClient):
On success, a :obj:`Dialogs` object is returned.
Raises:
:class:`Error`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
if pinned_only:

View File

@ -30,7 +30,7 @@ class JoinChat(BaseClient):
channel/supergroup (in the format @username).
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
match = self.INVITE_LINK_RE.match(chat_id)

View File

@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from pyrogram.client.ext import utils
from ...ext import BaseClient
@ -52,13 +53,13 @@ class KickChatMember(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
chat_peer = self.resolve_peer(chat_id)
user_peer = self.resolve_peer(user_id)
if isinstance(chat_peer, types.InputPeerChannel):
self.send(
r = self.send(
functions.channels.EditBanned(
channel=chat_peer,
user_id=user_peer,
@ -76,11 +77,17 @@ class KickChatMember(BaseClient):
)
)
else:
self.send(
r = self.send(
functions.messages.DeleteChatUser(
chat_id=abs(chat_id),
user_id=user_peer
)
)
return True
for i in r.updates:
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
return utils.parse_messages(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats}
)

View File

@ -33,7 +33,7 @@ class LeaveChat(BaseClient):
Deletes the group chat dialog after leaving (for simple group chats, not supergroups).
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
peer = self.resolve_peer(chat_id)

View File

@ -41,8 +41,8 @@ class PinChatMessage(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id doesn't belong to a supergroup or a channel.
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
"""
peer = self.resolve_peer(chat_id)

View File

@ -25,12 +25,12 @@ class PromoteChatMember(BaseClient):
chat_id: int or str,
user_id: int or str,
can_change_info: bool = True,
can_post_messages: bool = True,
can_edit_messages: bool = True,
can_post_messages: bool = False,
can_edit_messages: bool = False,
can_delete_messages: bool = True,
can_invite_users: bool = True,
can_restrict_members: bool = True,
can_pin_messages: bool = True,
can_pin_messages: bool = False,
can_promote_members: bool = False):
"""Use this method to promote or demote a user in a supergroup or a channel.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@ -74,7 +74,7 @@ class PromoteChatMember(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
self.send(
functions.channels.EditAdmin(

View File

@ -64,7 +64,7 @@ class RestrictChatMember(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
send_messages = True
send_media = True

View File

@ -36,8 +36,8 @@ class SetChatDescription(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id doesn't belong to a supergroup or a channel.
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
"""
peer = self.resolve_peer(chat_id)

View File

@ -45,8 +45,8 @@ class SetChatPhoto(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@ -41,8 +41,8 @@ class SetChatTitle(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@ -40,7 +40,7 @@ class UnbanChatMember(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
self.send(
functions.channels.EditBanned(

View File

@ -34,8 +34,8 @@ class UnpinChatMessage(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id doesn't belong to a supergroup or a channel.
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
"""
peer = self.resolve_peer(chat_id)

View File

@ -32,7 +32,7 @@ class AddContacts(BaseClient):
On success, the added contacts are returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
imported_contacts = self.send(
functions.contacts.ImportContacts(

View File

@ -34,7 +34,7 @@ class DeleteContacts(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
contacts = []

View File

@ -36,7 +36,7 @@ class GetContacts(BaseClient):
On success, the user's contacts are returned
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
while True:
try:

View File

@ -17,11 +17,19 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .on_callback_query import OnCallbackQuery
from .on_deleted_messages import OnDeletedMessages
from .on_disconnect import OnDisconnect
from .on_message import OnMessage
from .on_deleted_messages import OnDeletedMessages
from .on_raw_update import OnRawUpdate
from .on_user_status import OnUserStatus
class Decorators(OnMessage, OnDeletedMessages, OnCallbackQuery, OnRawUpdate, OnDisconnect):
class Decorators(
OnMessage,
OnDeletedMessages,
OnCallbackQuery,
OnRawUpdate,
OnDisconnect,
OnUserStatus
):
pass

View File

@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.client.filters.filter import Filter
from ...ext import BaseClient
@ -36,7 +37,14 @@ class OnCallbackQuery(BaseClient):
"""
def decorator(func):
self.add_handler(pyrogram.CallbackQueryHandler(func, filters), group)
return func
handler = pyrogram.CallbackQueryHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.CallbackQueryHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return decorator

View File

@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.client.filters.filter import Filter
from ...ext import BaseClient
@ -36,7 +37,14 @@ class OnDeletedMessages(BaseClient):
"""
def decorator(func):
self.add_handler(pyrogram.DeletedMessagesHandler(func, filters), group)
return func
handler = pyrogram.DeletedMessagesHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.DeletedMessagesHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return decorator

View File

@ -28,7 +28,11 @@ class OnDisconnect(BaseClient):
"""
def decorator(func):
self.add_handler(pyrogram.DisconnectHandler(func))
return func
handler = pyrogram.DisconnectHandler(func)
if self is not None:
self.add_handler(handler)
return handler
return decorator

View File

@ -17,11 +17,12 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.client.filters.filter import Filter
from ...ext import BaseClient
class OnMessage(BaseClient):
def on_message(self, filters=None, group: int = 0):
def on_message(self=None, filters=None, group: int = 0):
"""Use this decorator to automatically register a function for handling
messages. This does the same thing as :meth:`add_handler` using the
:class:`MessageHandler`.
@ -36,7 +37,14 @@ class OnMessage(BaseClient):
"""
def decorator(func):
self.add_handler(pyrogram.MessageHandler(func, filters), group)
return func
handler = pyrogram.MessageHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.MessageHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return decorator

View File

@ -21,7 +21,7 @@ from ...ext import BaseClient
class OnRawUpdate(BaseClient):
def on_raw_update(self, group: int = 0):
def on_raw_update(self=None, group: int = 0):
"""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`.
@ -32,7 +32,14 @@ class OnRawUpdate(BaseClient):
"""
def decorator(func):
self.add_handler(pyrogram.RawUpdateHandler(func), group)
return func
handler = pyrogram.RawUpdateHandler(func)
if isinstance(self, int):
return handler, group if self is None else group
if self is not None:
self.add_handler(handler, group)
return handler, group
return decorator

View File

@ -0,0 +1,49 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.client.filters.filter import Filter
from ...ext import BaseClient
class OnUserStatus(BaseClient):
def on_user_status(self=None, filters=None, group: int = 0):
"""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`.
Args:
filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function.
group (``int``, *optional*):
The group identifier, defaults to 0.
"""
def decorator(func):
handler = pyrogram.UserStatusHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.UserStatusHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return decorator

View File

@ -53,7 +53,7 @@ class DeleteMessages(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
peer = self.resolve_peer(chat_id)
message_ids = list(message_ids) if not isinstance(message_ids, int) else [message_ids]

View File

@ -53,7 +53,7 @@ class EditMessageCaption(BaseClient):
On success, the edited :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -36,6 +36,34 @@ class EditMessageMedia(BaseClient):
message_id: int,
media,
reply_markup=None):
"""Use this method to edit audio, document, photo, or video messages.
If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise,
message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded.
Use previously uploaded file via its file_id or specify a URL. On success, if the edited message was sent
by the bot, the edited Message is returned, otherwise True is returned.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
message_id (``int``):
Message identifier in the chat specified in chat_id.
media (:obj:`InputMediaAnimation` | :obj:`InputMediaAudio` | :obj:`InputMediaDocument` | :obj:`InputMediaPhoto` | :obj:`InputMediaVideo`)
One of the InputMedia objects describing an animation, audio, document, photo or video.
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
An InlineKeyboardMarkup object.
Returns:
On success, the edited :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
style = self.html if media.parse_mode.lower() == "html" else self.markdown
caption = media.caption

View File

@ -44,7 +44,7 @@ class EditMessageReplyMarkup(BaseClient):
:obj:`Message <pyrogram.Message>` is returned, otherwise True is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(

View File

@ -57,7 +57,7 @@ class EditMessageText(BaseClient):
On success, the edited :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -54,7 +54,7 @@ class ForwardMessages(BaseClient):
is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
is_iterable = not isinstance(message_ids, int)
message_ids = list(message_ids) if is_iterable else [message_ids]

View File

@ -56,7 +56,7 @@ class GetHistory(BaseClient):
On success, a :obj:`Messages <pyrogram.Messages>` object is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(

View File

@ -23,9 +23,10 @@ from ...ext import BaseClient, utils
class GetMessages(BaseClient):
def get_messages(self,
chat_id: int or str,
message_ids,
message_ids: int or list = None,
reply_to_message_ids: int or list = None,
replies: int = 1):
"""Use this method to get messages that belong to a specific chat.
"""Use this method to get one or more messages that belong to a specific chat.
You can retrieve up to 200 messages at once.
Args:
@ -34,36 +35,46 @@ class GetMessages(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
message_ids (``iterable``):
A list of Message identifiers in the chat specified in *chat_id* or a single message id, as integer.
Iterators and Generators are also accepted.
message_ids (``iterable``, *optional*):
Pass a single message identifier or a list of message ids (as integers) to get the content of the
message themselves. Iterators and Generators are also accepted.
reply_to_message_ids (``iterable``, *optional*):
Pass a single message identifier or a list of message ids (as integers) to get the content of
the previous message you replied to using this message. Iterators and Generators are also accepted.
If *message_ids* is set, this argument will be ignored.
replies (``int``, *optional*):
The number of subsequent replies to get for each message. Defaults to 1.
Returns:
On success and in case *message_ids* was a list, the returned value will be a list of the requested
:obj:`Messages <pyrogram.Messages>` even if a list contains just one element, otherwise if
*message_ids* was an integer, the single requested :obj:`Message <pyrogram.Message>`
is returned.
On success and in case *message_ids* or *reply_to_message_ids* was a list, the returned value will be a
list of the requested :obj:`Messages <pyrogram.Messages>` even if a list contains just one element,
otherwise if *message_ids* or *reply_to_message_ids* was an integer, the single requested
:obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
ids, ids_type = (
(message_ids, types.InputMessageID) if message_ids
else (reply_to_message_ids, types.InputMessageReplyTo) if reply_to_message_ids
else (None, None)
)
if ids is None:
raise ValueError("No argument supplied")
peer = self.resolve_peer(chat_id)
is_iterable = not isinstance(message_ids, int)
message_ids = list(message_ids) if is_iterable else [message_ids]
message_ids = [types.InputMessageID(i) for i in message_ids]
is_iterable = not isinstance(ids, int)
ids = list(ids) if is_iterable else [ids]
ids = [ids_type(i) for i in ids]
if isinstance(peer, types.InputPeerChannel):
rpc = functions.channels.GetMessages(
channel=peer,
id=message_ids
)
rpc = functions.channels.GetMessages(channel=peer, id=ids)
else:
rpc = functions.messages.GetMessages(
id=message_ids
)
rpc = functions.messages.GetMessages(id=ids)
r = self.send(rpc)

View File

@ -56,7 +56,7 @@ class SendAnimation(BaseClient):
pass a file path as string to upload a new animation that exists on your local machine.
caption (``str``, *optional*):
Animation caption, 0-200 characters.
Animation caption, 0-1024 characters.
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
@ -116,7 +116,7 @@ class SendAnimation(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -58,7 +58,7 @@ class SendAudio(BaseClient):
pass a file path as string to upload a new audio file that exists on your local machine.
caption (``str``, *optional*):
Audio caption, 0-200 characters.
Audio caption, 0-1024 characters.
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
@ -118,7 +118,7 @@ class SendAudio(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -47,8 +47,8 @@ class SendChatAction(BaseClient):
On success, True is returned.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If the provided string is not a valid ChatAction
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
``ValueError`` if the provided string is not a valid ChatAction.
"""
# Resolve Enum type

View File

@ -65,7 +65,7 @@ class SendContact(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(

View File

@ -59,7 +59,7 @@ class SendDocument(BaseClient):
Thumbnails can't be reused and can be only uploaded as a new file.
caption (``str``, *optional*):
Document caption, 0-200 characters.
Document caption, 0-1024 characters.
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
@ -104,7 +104,7 @@ class SendDocument(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -57,7 +57,7 @@ class SendLocation(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(

View File

@ -64,7 +64,7 @@ class SendMessage(BaseClient):
On success, the sent :obj:`Message` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -52,7 +52,7 @@ class SendPhoto(BaseClient):
pass a file path as string to upload a new photo that exists on your local machine.
caption (``bool``, *optional*):
Photo caption, 0-200 characters.
Photo caption, 0-1024 characters.
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
@ -102,7 +102,7 @@ class SendPhoto(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -86,7 +86,7 @@ class SendSticker(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None

View File

@ -74,7 +74,7 @@ class SendVenue(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(

View File

@ -57,7 +57,7 @@ class SendVideo(BaseClient):
pass a file path as string to upload a new video that exists on your local machine.
caption (``str``, *optional*):
Video caption, 0-200 characters.
Video caption, 0-1024 characters.
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
@ -120,7 +120,7 @@ class SendVideo(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -102,7 +102,7 @@ class SendVideoNote(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None

View File

@ -53,7 +53,7 @@ class SendVoice(BaseClient):
pass a file path as string to upload a new audio that exists on your local machine.
caption (``str``, *optional*):
Voice message caption, 0-200 characters.
Voice message caption, 0-1024 characters.
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
@ -101,7 +101,7 @@ class SendVoice(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@ -41,7 +41,7 @@ class ChangeCloudPassword(BaseClient):
True on success, False otherwise.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(functions.account.GetPassword())

View File

@ -43,7 +43,7 @@ class EnableCloudPassword(BaseClient):
True on success, False otherwise.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(functions.account.GetPassword())

View File

@ -34,7 +34,7 @@ class RemoveCloudPassword(BaseClient):
True on success, False otherwise.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
r = self.send(functions.account.GetPassword())

View File

@ -16,15 +16,17 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .delete_profile_photos import DeleteProfilePhotos
from .delete_user_profile_photos import DeleteUserProfilePhotos
from .get_me import GetMe
from .get_user_profile_photos import GetUserProfilePhotos
from .get_users import GetUsers
from .set_user_profile_photo import SetUserProfilePhoto
class Users(
GetUserProfilePhotos,
DeleteProfilePhotos,
SetUserProfilePhoto,
DeleteUserProfilePhotos,
GetUsers,
GetMe
):

View File

@ -23,8 +23,8 @@ from pyrogram.api import functions, types
from ...ext import BaseClient
class DeleteProfilePhotos(BaseClient):
def delete_profile_photos(self, id: str or list):
class DeleteUserProfilePhotos(BaseClient):
def delete_user_profile_photos(self, id: str or list):
"""Use this method to delete your own profile photos
Args:
@ -36,7 +36,7 @@ class DeleteProfilePhotos(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
id = id if isinstance(id, list) else [id]
input_photos = []

View File

@ -28,7 +28,7 @@ class GetMe(BaseClient):
Basic information about the user or bot in form of a :obj:`User` object
Raises:
:class:`Error <pyrogram.Error>`
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
return utils.parse_user(
self.send(

Some files were not shown because too many files have changed in this diff Show More