From 472ed8e355e797181c299a72a9de9429a732e7b2 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Wed, 11 Apr 2018 03:16:48 +0200 Subject: [PATCH] Document the new features --- compiler/api/compiler.py | 2 +- docs/source/pyrogram/Filters.rst | 6 + docs/source/pyrogram/InputMedia.rst | 6 - docs/source/pyrogram/InputMediaPhoto.rst | 6 + docs/source/pyrogram/InputMediaVideo.rst | 6 + docs/source/pyrogram/MessageHandler.rst | 6 + docs/source/pyrogram/RawUpdateHandler.rst | 6 + docs/source/pyrogram/index.rst | 29 ++- pyrogram/__init__.py | 2 +- pyrogram/client/client.py | 38 +++- pyrogram/client/dispatcher/dispatcher.py | 6 +- pyrogram/client/filters/filters.py | 177 +++++++++++++----- pyrogram/client/handler/message_handler.py | 31 --- pyrogram/client/handler/raw_update_handler.py | 24 --- .../client/{handler => handlers}/__init__.py | 4 +- .../client/{handler => handlers}/handler.py | 0 pyrogram/client/handlers/handlers.py | 93 +++++++++ 17 files changed, 322 insertions(+), 120 deletions(-) create mode 100644 docs/source/pyrogram/Filters.rst delete mode 100644 docs/source/pyrogram/InputMedia.rst create mode 100644 docs/source/pyrogram/InputMediaPhoto.rst create mode 100644 docs/source/pyrogram/InputMediaVideo.rst create mode 100644 docs/source/pyrogram/MessageHandler.rst create mode 100644 docs/source/pyrogram/RawUpdateHandler.rst delete mode 100644 pyrogram/client/handler/message_handler.py delete mode 100644 pyrogram/client/handler/raw_update_handler.py rename pyrogram/client/{handler => handlers}/__init__.py (86%) rename pyrogram/client/{handler => handlers}/handler.py (100%) create mode 100644 pyrogram/client/handlers/handlers.py diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index 3d5af589..250de451 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -297,7 +297,7 @@ def start(): "{}: {}{}".format( arg_name, "``optional`` ".format(flag_number) if is_optional else "", - get_docstring_arg_type(arg_type) + get_docstring_arg_type(arg_type, is_pyrogram_type=c.namespace == "pyrogram") ) ) diff --git a/docs/source/pyrogram/Filters.rst b/docs/source/pyrogram/Filters.rst new file mode 100644 index 00000000..083bd64a --- /dev/null +++ b/docs/source/pyrogram/Filters.rst @@ -0,0 +1,6 @@ +Filters +======= + +.. autoclass:: pyrogram.Filters + :members: + :undoc-members: diff --git a/docs/source/pyrogram/InputMedia.rst b/docs/source/pyrogram/InputMedia.rst deleted file mode 100644 index c637bdc0..00000000 --- a/docs/source/pyrogram/InputMedia.rst +++ /dev/null @@ -1,6 +0,0 @@ -InputMedia -========== - -.. autoclass:: pyrogram.InputMedia - :members: - :undoc-members: diff --git a/docs/source/pyrogram/InputMediaPhoto.rst b/docs/source/pyrogram/InputMediaPhoto.rst new file mode 100644 index 00000000..abc3f456 --- /dev/null +++ b/docs/source/pyrogram/InputMediaPhoto.rst @@ -0,0 +1,6 @@ +InputMediaPhoto +=============== + +.. autoclass:: pyrogram.InputMediaPhoto + :members: + :undoc-members: diff --git a/docs/source/pyrogram/InputMediaVideo.rst b/docs/source/pyrogram/InputMediaVideo.rst new file mode 100644 index 00000000..de9c480b --- /dev/null +++ b/docs/source/pyrogram/InputMediaVideo.rst @@ -0,0 +1,6 @@ +InputMediaVideo +=============== + +.. autoclass:: pyrogram.InputMediaVideo + :members: + :undoc-members: diff --git a/docs/source/pyrogram/MessageHandler.rst b/docs/source/pyrogram/MessageHandler.rst new file mode 100644 index 00000000..de908bd3 --- /dev/null +++ b/docs/source/pyrogram/MessageHandler.rst @@ -0,0 +1,6 @@ +MessageHandler +============== + +.. autoclass:: pyrogram.MessageHandler + :members: + :undoc-members: diff --git a/docs/source/pyrogram/RawUpdateHandler.rst b/docs/source/pyrogram/RawUpdateHandler.rst new file mode 100644 index 00000000..3d74a34b --- /dev/null +++ b/docs/source/pyrogram/RawUpdateHandler.rst @@ -0,0 +1,6 @@ +\RawUpdateHandler +================ + +.. autoclass:: pyrogram.RawUpdateHandler + :members: + :undoc-members: diff --git a/docs/source/pyrogram/index.rst b/docs/source/pyrogram/index.rst index 160766eb..7701fd60 100644 --- a/docs/source/pyrogram/index.rst +++ b/docs/source/pyrogram/index.rst @@ -9,9 +9,36 @@ the same parameters as well, thus offering a familiar look to Bot developers. .. toctree:: Client + MessageHandler + RawUpdateHandler + Filters ChatAction ParseMode - InputMedia Error +Types +----- + +.. toctree:: + ../types/pyrogram/User + ../types/pyrogram/Chat + ../types/pyrogram/Message + ../types/pyrogram/MessageEntity + ../types/pyrogram/PhotoSize + ../types/pyrogram/Audio + ../types/pyrogram/Document + ../types/pyrogram/Video + ../types/pyrogram/Voice + ../types/pyrogram/VideoNote + ../types/pyrogram/Contact + ../types/pyrogram/Location + ../types/pyrogram/Venue + ../types/pyrogram/UserProfilePhotos + ../types/pyrogram/ChatPhoto + ../types/pyrogram/ChatMember + InputMediaPhoto + InputMediaVideo + ../types/pyrogram/Sticker + + .. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index fbb73eb4..d5e26b43 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -34,5 +34,5 @@ from .client.input_media_photo import InputMediaPhoto from .client.input_media_video import InputMediaVideo from .client.input_phone_contact import InputPhoneContact from .client import Emoji -from .client.handler import MessageHandler, RawUpdateHandler +from .client.handlers import MessageHandler, RawUpdateHandler from .client.filters import Filters diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 6545e9a2..7e8eb791 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -51,7 +51,6 @@ from pyrogram.session import Auth, Session from pyrogram.session.internals import MsgId from . import message_parser from .dispatcher import Dispatcher -from .handler import Handler from .input_media import InputMedia from .style import Markdown, HTML from .utils import decode @@ -204,6 +203,19 @@ class Client: self.update_handler = None def on_message(self, 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 + MessageHandler. + + Args: + filters (:obj:`Filters `): + Pass one or more filters to allow only a subset of messages to be passed + in your function. + + group (``int``, optional): + The group identifier, defaults to 0. + """ + def decorator(func): self.add_handler(pyrogram.MessageHandler(func, filters), group) return func @@ -211,13 +223,35 @@ class Client: return decorator def on_raw_update(self, 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 + RawUpdateHandler. + + Args: + group (``int``, optional): + The group identifier, defaults to 0. + """ + def decorator(func): self.add_handler(pyrogram.RawUpdateHandler(func), group) return func return decorator - def add_handler(self, handler: Handler, group: int = 0): + def add_handler(self, handler, group: int = 0): + """Use this method to register an event handler. + + You can register multiple handlers, but at most one handler within a group + will be used for a single event. To handle the same event more than once, register + your handler using a different group id (lower group id == higher priority). + + Args: + handler (:obj:`Handler `): + The handler to be registered. + + group (``int``, optional): + The group identifier, defaults to 0. + """ self.dispatcher.add_handler(handler, group) def start(self): diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py index 62086acd..2710c4c0 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/dispatcher/dispatcher.py @@ -25,9 +25,7 @@ from threading import Thread import pyrogram from pyrogram.api import types from .. import message_parser -from ..handler import ( - Handler, RawUpdateHandler -) +from ..handlers import RawUpdateHandler log = logging.getLogger(__name__) @@ -62,7 +60,7 @@ class Dispatcher: for _ in range(self.workers): self.updates.put(None) - def add_handler(self, handler: Handler, group: int): + def add_handler(self, handler, group: int): if group not in self.groups: self.groups[group] = [] self.groups = OrderedDict(sorted(self.groups.items())) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index 6058b0ce..ac2ecf67 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -29,10 +29,71 @@ def build(name: str, func: callable, **kwargs) -> type: class Filters: + """This class provides access to all the Filters available in Pyrogram. + It is intended to be used when adding an handler.""" + text = build("Text", lambda _, m: bool(m.text and not m.text.startswith("/"))) + """Filter text messages.""" + + reply = build("Reply", lambda _, m: bool(m.reply_to_message)) + """Filter messages that are replies to other messages.""" + + forwarded = build("Forwarded", lambda _, m: bool(m.forward_date)) + """Filter messages that are forwarded.""" + + caption = build("Caption", lambda _, m: bool(m.caption)) + """Filter media messages that contain captions.""" + + edited = build("Edited", lambda _, m: bool(m.edit_date)) + """Filter edited messages.""" + + audio = build("Audio", lambda _, m: bool(m.audio)) + """Filter messages that contain an :obj:`Audio `.""" + + document = build("Document", lambda _, m: bool(m.document)) + """Filter messages that contain a :obj:`Document `.""" + + photo = build("Photo", lambda _, m: bool(m.photo)) + """Filter messages that contain a :obj:`Photo `.""" + + sticker = build("Sticker", lambda _, m: bool(m.sticker)) + """Filter messages that contain a :obj:`Sticker `.""" + + video = build("Video", lambda _, m: bool(m.video)) + """Filter messages that contain a :obj:`Video `.""" + + voice = build("Voice", lambda _, m: bool(m.voice)) + """Filter messages that contain a :obj:`Voice ` note.""" + + video_note = build("Voice", lambda _, m: bool(m.video_note)) + """Filter messages that contain a :obj:`VideoNote `.""" + + contact = build("Contact", lambda _, m: bool(m.contact)) + """Filter messages that contain a :obj:`Contact `.""" + + location = build("Location", lambda _, m: bool(m.location)) + """Filter messages that contain a :obj:`Location `.""" + + venue = build("Venue", lambda _, m: bool(m.venue)) + """Filter messages that contain a :obj:`Venue `.""" + + private = build("Private", lambda _, m: bool(m.chat.type == "private")) + """Filter messages sent in private chats.""" + + group = build("Group", lambda _, m: bool(m.chat.type in {"group", "supergroup"})) + """Filter messages sent in group or supergroup chats.""" + + channel = build("Channel", lambda _, m: bool(m.chat.type == "channel")) + """Filter messages sent in channels.""" @staticmethod def command(command: str or list): + """Filter commands, i.e.: text messages starting with '/'. + + Args: + command (``str`` | ``list``): + The command or list of commands as strings the filter should look for. + """ return build( "Command", lambda _, m: bool( @@ -47,28 +108,17 @@ class Filters: ) ) - reply = build("Reply", lambda _, m: bool(m.reply_to_message)) - forwarded = build("Forwarded", lambda _, m: bool(m.forward_date)) - caption = build("Caption", lambda _, m: bool(m.caption)) - edited = build("Edited", lambda _, m: bool(m.edit_date)) - - audio = build("Audio", lambda _, m: bool(m.audio)) - document = build("Document", lambda _, m: bool(m.document)) - photo = build("Photo", lambda _, m: bool(m.photo)) - sticker = build("Sticker", lambda _, m: bool(m.sticker)) - video = build("Video", lambda _, m: bool(m.video)) - voice = build("Voice", lambda _, m: bool(m.voice)) - video_note = build("Voice", lambda _, m: bool(m.video_note)) - contact = build("Contact", lambda _, m: bool(m.contact)) - location = build("Location", lambda _, m: bool(m.location)) - venue = build("Venue", lambda _, m: bool(m.venue)) - - private = build("Private", lambda _, m: bool(m.chat.type == "private")) - group = build("Group", lambda _, m: bool(m.chat.type in {"group", "supergroup"})) - channel = build("Channel", lambda _, m: bool(m.chat.type == "channel")) - @staticmethod def regex(pattern, flags: int = 0): + """Filter messages that match a given RegEx pattern. + + Args: + pattern (``str``): + The RegEx pattern. + + flags (``int``, optional): + RegEx flags. + """ return build( "Regex", lambda _, m: bool(_.p.search(m.text or "")), p=re.compile(pattern, flags) @@ -76,6 +126,12 @@ class Filters: @staticmethod def user(user: int or str or list): + """Filter messages coming from specific users. + + Args: + user (``int`` | ``str`` | ``list``): + The user or list of user IDs (int) or usernames (str) the filter should look for. + """ return build( "User", lambda _, m: bool(m.from_user @@ -91,6 +147,12 @@ class Filters: @staticmethod def chat(chat: int or str or list): + """Filter messages coming from specific chats. + + Args: + chat (``int`` | ``str`` | ``list``): + The chat or list of chat IDs (int) or usernames (str) the filter should look for. + """ return build( "Chat", lambda _, m: bool(m.chat @@ -104,32 +166,53 @@ class Filters: ) ) - class _Service(Filter): - new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members)) - left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member)) - new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title)) - new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo)) - delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo)) - group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created)) - supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created)) - channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created)) - migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id)) - migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id)) - pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message)) + new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members)) + """Filter service messages for new chat members.""" - def __call__(self, message): - return bool( - self.new_chat_members(message) - or self.left_chat_member(message) - or self.new_chat_title(message) - or self.new_chat_photo(message) - or self.delete_chat_photo(message) - or self.group_chat_created(message) - or self.supergroup_chat_created(message) - or self.channel_chat_created(message) - or self.migrate_to_chat_id(message) - or self.migrate_from_chat_id(message) - or self.pinned_message(message) - ) + left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member)) + """Filter service messages for members that left the chat.""" - service = _Service() + new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title)) + """Filter service messages for new chat titles.""" + + new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo)) + """Filter service messages for new chat photos.""" + + delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo)) + """Filter service messages for deleted photos.""" + + group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created)) + """Filter service messages for group chat creations.""" + + supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created)) + """Filter service messages for supergroup chat creations.""" + + channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created)) + """Filter service messages for channel chat creations.""" + + migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id)) + """Filter service messages that contain migrate_to_chat_id.""" + + migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id)) + """Filter service messages that contain migrate_from_chat_id.""" + + pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message)) + """Filter service messages for pinned messages.""" + + service = build( + "Service", + lambda _, m: bool( + _.new_chat_members(m) + or _.left_chat_member(m) + or _.new_chat_title(m) + or _.new_chat_photo(m) + or _.delete_chat_photo(m) + or _.group_chat_created(m) + or _.supergroup_chat_created(m) + or _.channel_chat_created(m) + or _.migrate_to_chat_id(m) + or _.migrate_from_chat_id(m) + or _.pinned_m(m) + ) + ) + """Filter all service messages""" diff --git a/pyrogram/client/handler/message_handler.py b/pyrogram/client/handler/message_handler.py deleted file mode 100644 index fdbba245..00000000 --- a/pyrogram/client/handler/message_handler.py +++ /dev/null @@ -1,31 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# 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 . - -from .handler import Handler - - -class MessageHandler(Handler): - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) - - def check(self, message): - return ( - self.filters(message) - if self.filters - else True - ) diff --git a/pyrogram/client/handler/raw_update_handler.py b/pyrogram/client/handler/raw_update_handler.py deleted file mode 100644 index 758606db..00000000 --- a/pyrogram/client/handler/raw_update_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# 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 . - -from .handler import Handler - - -class RawUpdateHandler(Handler): - def __init__(self, callback: callable): - super().__init__(callback) diff --git a/pyrogram/client/handler/__init__.py b/pyrogram/client/handlers/__init__.py similarity index 86% rename from pyrogram/client/handler/__init__.py rename to pyrogram/client/handlers/__init__.py index be0bfb56..d9c48359 100644 --- a/pyrogram/client/handler/__init__.py +++ b/pyrogram/client/handlers/__init__.py @@ -16,6 +16,4 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from .handler import Handler -from .message_handler import MessageHandler -from .raw_update_handler import RawUpdateHandler +from .handlers import MessageHandler, RawUpdateHandler diff --git a/pyrogram/client/handler/handler.py b/pyrogram/client/handlers/handler.py similarity index 100% rename from pyrogram/client/handler/handler.py rename to pyrogram/client/handlers/handler.py diff --git a/pyrogram/client/handlers/handlers.py b/pyrogram/client/handlers/handlers.py new file mode 100644 index 00000000..ca43282f --- /dev/null +++ b/pyrogram/client/handlers/handlers.py @@ -0,0 +1,93 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# 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 . + +from .handler import Handler + + +class MessageHandler(Handler): + """The Message handler class. It is used to handle text, media and service messages coming from + any chat (private, group, channel). + + Args: + callback (``callable``): + Pass a function that will be called when a new Message arrives. It takes *(client, message)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`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 `): + The Client itself, useful when you want to call other API methods inside the message handler. + + message (:obj:`Message `): + The received message. + """ + + def __init__(self, callback: callable, filters=None): + super().__init__(callback, filters) + + def check(self, message): + return ( + self.filters(message) + if self.filters + else True + ) + + +class RawUpdateHandler(Handler): + """The Raw Update handler class. It is used to handle raw updates. + + Args: + callback (``callable``): + A function that will be called when a new update is received from the server. It takes + *(client, update, users, chats)* as positional arguments (look at the section below for + a detailed description). + + Other Parameters: + client (:class:`Client `): + The Client itself, useful when you want to call other API methods inside the update handler. + + update (``Update``): + The received update, which can be one of the many single Updates listed in the *updates* + field you see in the :obj:`Update ` type. + + users (``dict``): + Dictionary of all :obj:`User ` mentioned in the update. + You can access extra info about the user (such as *first_name*, *last_name*, etc...) by using + the IDs you find in the *update* argument (e.g.: *users[1768841572]*). + + chats (``dict``): + Dictionary of all :obj:`Chat ` and + :obj:`Channel ` mentioned in the update. + You can access extra info about the chat (such as *title*, *participants_count*, etc...) + by using the IDs you find in the *update* argument (e.g.: *chats[1701277281]*). + + Note: + The following Empty or Forbidden types may exist inside the *users* and *chats* dictionaries. + They mean you have been blocked by the user or banned from the group/channel. + + - :obj:`UserEmpty ` + - :obj:`ChatEmpty ` + - :obj:`ChatForbidden ` + - :obj:`ChannelForbidden ` + """ + + def __init__(self, callback: callable): + super().__init__(callback)