Merge branch 'develop' into asyncio

# Conflicts:
#	pyrogram/client/methods/chats/kick_chat_member.py
This commit is contained in:
Dan 2018-10-21 15:56:02 +02:00
commit 66bcc7cde4
25 changed files with 4582 additions and 4498 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

@ -84,7 +84,7 @@ To get started, press the Next button.
resources/UpdateHandling
resources/UsingFilters
resources/Plugins
resources/SmartPlugins
resources/AutoAuthorization
resources/CustomizeSessions
resources/TgCrypto

View File

@ -1,116 +0,0 @@
Plugins
=======
Pyrogram embeds an **automatic** and lightweight plugin system that is meant to greatly 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**.
Introduction
------------
Prior to the plugin system, pluggable handlers were already possible. For instance, 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 (e.g.: "pyrogram" -> "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?
Creating Plugins
----------------
Setting up your Pyrogram project to accommodate plugins is as easy as creating a folder and putting your files full of
handlers inside.
.. note:: This is the same example application `as shown above <#introduction>`_, written using the 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").run()
The first important thing to note is the ``plugins`` folder, whose name is default and can be changed easily by setting
the ``plugins_dir`` parameter when creating a :obj:`Client <pyrogram.Client>`; you can put any python file in the
plugins folder and each file can contain any decorated function (handlers). Your Pyrogram Client instance (in the
``main.py`` file) 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.
The ``main.py`` script is now at its bare minimum and cleanest state.

View File

@ -0,0 +1,119 @@
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**.
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 (e.g.: "pyrogram" -> "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

@ -14,17 +14,17 @@ by following the instructions at https://pip.pypa.io/en/latest/installing/.
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
-------------
@ -32,21 +32,53 @@ Bleeding Edge
If you want the latest development version of Pyrogram, you can install it straight from the develop_
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.
.. note::
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.
.. 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'
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
.. _develop: http://github.com/pyrogram/pyrogram
.. _develop: http://github.com/pyrogram/pyrogram

View File

@ -170,7 +170,7 @@ class Client(Methods, BaseClient):
workers: int = BaseClient.WORKERS,
workdir: str = BaseClient.WORKDIR,
config_file: str = BaseClient.CONFIG_FILE,
plugins_dir: str or None = BaseClient.PLUGINS_DIR):
plugins_dir: str = None):
super().__init__()
self.session_name = session_name
@ -982,10 +982,7 @@ class Client(Methods, BaseClient):
try:
dirs = os.listdir(self.plugins_dir)
except FileNotFoundError:
if self.plugins_dir == Client.PLUGINS_DIR:
log.info("No plugin loaded: default directory is missing")
else:
log.warning('No plugin loaded: "{}" directory is missing'.format(self.plugins_dir))
log.warning('No plugin loaded: "{}" directory is missing'.format(self.plugins_dir))
else:
plugins_dir = self.plugins_dir.lstrip("./").replace("/", ".")
plugins_count = 0
@ -1009,11 +1006,14 @@ class Client(Methods, BaseClient):
except Exception:
pass
log.warning('Successfully loaded {} plugin{} from "{}"'.format(
plugins_count,
"s" if plugins_count > 1 else "",
self.plugins_dir
))
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()

View File

@ -51,7 +51,6 @@ class BaseClient:
WORKERS = 4
WORKDIR = "."
CONFIG_FILE = "./config.ini"
PLUGINS_DIR = "./plugins"
MEDIA_TYPE_ID = {
0: "thumbnail",

File diff suppressed because it is too large Load Diff

View File

@ -180,6 +180,7 @@ def parse_user(user: types.User) -> pyrogram_types.User or None:
phone_number=user.phone,
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
@ -199,7 +200,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
)
@ -224,7 +226,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")
)

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
@ -58,7 +59,7 @@ class KickChatMember(BaseClient):
user_peer = await self.resolve_peer(user_id)
if isinstance(chat_peer, types.InputPeerChannel):
await self.send(
r = await self.send(
functions.channels.EditBanned(
channel=chat_peer,
user_id=user_peer,
@ -76,11 +77,17 @@ class KickChatMember(BaseClient):
)
)
else:
await self.send(
r = await 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

@ -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>`

View File

@ -57,7 +57,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>`

View File

@ -58,7 +58,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>`

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>`

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>`

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>`

View File

@ -35,7 +35,7 @@ class InputMediaAnimation(InputMedia):
Thumbnails can't be reused and can be only uploaded as a new file.
caption (``str``, *optional*):
Caption of the animation to be sent, 0-200 characters
Caption of the animation to be sent, 0-1024 characters
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`

View File

@ -36,7 +36,7 @@ class InputMediaAudio(InputMedia):
Thumbnails can't be reused and can be only uploaded as a new file.
caption (``str``, *optional*):
Caption of the audio to be sent, 0-200 characters
Caption of the audio to be sent, 0-1024 characters
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`

View File

@ -35,7 +35,7 @@ class InputMediaDocument(InputMedia):
Thumbnails can't be reused and can be only uploaded as a new file.
caption (``str``, *optional*):
Caption of the document to be sent, 0-200 characters
Caption of the document to be sent, 0-1024 characters
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`

View File

@ -31,7 +31,7 @@ class InputMediaPhoto(InputMedia):
Sending photo by a URL is currently unsupported.
caption (``str``, *optional*):
Caption of the photo to be sent, 0-200 characters
Caption of the photo to be sent, 0-1024 characters
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`

View File

@ -37,7 +37,7 @@ class InputMediaVideo(InputMedia):
Thumbnails can't be reused and can be only uploaded as a new file.
caption (``str``, *optional*):
Caption of the video to be sent, 0-200 characters
Caption of the video to be sent, 0-1024 characters
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`

View File

@ -105,7 +105,7 @@ class Message(Object):
Message is a video note, information about the video message.
caption (``str``, *optional*):
Caption for the audio, document, photo, video or voice, 0-200 characters.
Caption for the audio, document, photo, video or voice, 0-1024 characters.
If the message contains caption entities (bold, italic, ...) you can access *caption.markdown* or
*caption.html* to get the marked up caption text. In case there is no caption entity, the fields
will contain the same text as *caption*.

View File

@ -69,6 +69,9 @@ class Chat(Object):
members_count (``int``, *optional*):
Chat members count, for groups and channels only.
restriction_reason (``str``, *optional*):
The reason why this chat might be unavailable to some users.
"""
ID = 0xb0700002
@ -88,7 +91,8 @@ class Chat(Object):
pinned_message=None,
sticker_set_name: str = None,
can_set_sticker_set: bool = None,
members_count: int = None
members_count: int = None,
restriction_reason: str = None
):
self.id = id
self.type = type
@ -104,3 +108,4 @@ class Chat(Object):
self.sticker_set_name = sticker_set_name
self.can_set_sticker_set = can_set_sticker_set
self.members_count = members_count
self.restriction_reason = restriction_reason

View File

@ -61,6 +61,9 @@ class User(Object):
photo (:obj:`ChatPhoto <pyrogram.ChatPhoto>`, *optional*):
User's or bot's current profile photo. Suitable for downloads only.
restriction_reason (``str``, *optional*):
The reason why this bot might be unavailable to some users.
"""
ID = 0xb0700001
@ -79,7 +82,8 @@ class User(Object):
username: str = None,
language_code: str = None,
phone_number: str = None,
photo=None
photo=None,
restriction_reason: str = None
):
self.id = id
self.is_self = is_self
@ -94,3 +98,4 @@ class User(Object):
self.language_code = language_code
self.phone_number = phone_number
self.photo = photo
self.restriction_reason = restriction_reason

View File

@ -173,7 +173,10 @@ setup(
packages=find_packages(exclude=["compiler*"]),
zip_safe=False,
install_requires=read("requirements.txt"),
extras_require={"tgcrypto": ["tgcrypto==1.1.1"]},
extras_require={
"tgcrypto": ["tgcrypto==1.1.1"], # TODO: Remove soon
"fast": ["tgcrypto==1.1.1"],
},
cmdclass={
"clean": Clean,
"generate": Generate