2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-28 12:57:52 +00:00

Add a way to stop iterating through handlers

Closes #125
This commit is contained in:
Dan 2019-01-02 18:11:22 +01:00
parent f440b1f969
commit 1960b00280
11 changed files with 206 additions and 59 deletions

View File

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

View File

@ -0,0 +1,148 @@
More on Updates
===============
Here we'll show some advanced usages when working with updates.
.. note::
This page makes use of Handlers and Filters to show you how to handle updates.
Learn more at `Update Handling <UpdateHandling.html>`_ and `Using Filters <UsingFilters.html>`_.
Handler Groups
--------------
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored.
In order to process the same update more than once, you can register your handler in a different group.
Groups are identified by a number (number 0 being the default) and are sorted, that is, a lower group number has a
higher priority.
For example, in:
.. code-block:: python
@app.on_message(Filters.text | Filters.sticker)
def text_or_sticker(client, message):
print("Text or Sticker")
@app.on_message(Filters.text)
def just_text(client, message):
print("Just Text")
``just_text`` is never executed because ``text_or_sticker`` already handles texts. To enable it, simply register the
function using a different group:
.. code-block:: python
@app.on_message(Filters.text, group=1)
def just_text(client, message):
print("Just Text")
Or, if you want ``just_text`` to be fired *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``):
.. code-block:: python
@app.on_message(Filters.text, group=-1)
def just_text(client, message):
print("Just Text")
With :meth:`add_handler() <pyrogram.Client.add_handler>` (without decorators) the same can be achieved with:
.. code-block:: python
app.add_handler(MessageHandler(just_text, Filters.text), -1)
Update propagation
------------------
Registering multiple handlers, each in a different group, becomes useful when you want to handle the same update more
than once. Any incoming update will be sequentially processed by all of your registered functions by respecting the
groups priority policy described above. Even in case any handler raises an unhandled exception, Pyrogram will still
continue to propagate the same update to the next groups until all the handlers are done. Example:
.. code-block:: python
@app.on_message(Filters.private)
def _(client, message):
print(0)
@app.on_message(Filters.private, group=1)
def _(client, message):
print(1 / 0) # Unhandled exception: ZeroDivisionError
@app.on_message(Filters.private, group=2)
def _(client, message):
print(2)
All these handlers will handle the same kind of messages, that are, messages sent or received in private chats.
The output for each incoming update will therefore be:
.. code-block:: text
0
ZeroDivisionError: division by zero
2
Stop Propagation
^^^^^^^^^^^^^^^^
In order to prevent further propagation of an update in the dispatching phase, you can do *one* of the following:
- Call the update's bound-method ``.stop_propagation()`` (preferred way).
- Manually ``raise StopPropagation`` error (more suitable for raw updates only).
.. note::
Note that ``.stop_propagation()`` is just an elegant and intuitive way to raise a ``StopPropagation`` error;
this means that any code coming *after* calling it won't be executed as your function just raised a custom exception
to signal the dispatcher not to propagate the update anymore.
Example with ``stop_propagation()``:
.. code-block:: python
@app.on_message(Filters.private)
def _(client, message):
print(0)
@app.on_message(Filters.private, group=1)
def _(client, message):
print(1)
message.stop_propagation()
@app.on_message(Filters.private, group=2)
def _(client, message):
print(2)
Example with ``raise StopPropagation``:
.. code-block:: python
from pyrogram import StopPropagation
@app.on_message(Filters.private)
def _(client, message):
print(0)
@app.on_message(Filters.private, group=1)
def _(client, message):
print(1)
raise StopPropagation
@app.on_message(Filters.private, group=2)
def _(client, message):
print(2)
The handler in group number 2 will never be executed because the propagation was stopped before. The output of both
examples will be:
.. code-block:: text
0
1

View File

@ -5,7 +5,8 @@ For a finer grained control over what kind of messages will be allowed or not in
:class:`Filters <pyrogram.Filters>`.
.. note::
This section makes use of Handlers to handle updates. Learn more at `Update Handling <UpdateHandling.html>`_.
This page makes use of Handlers to show you how to handle updates.
Learn more at `Update Handling <UpdateHandling.html>`_.
- This example will show you how to **only** handle messages containing an :obj:`Audio <pyrogram.Audio>` object and
ignore any other message:
@ -99,45 +100,6 @@ More handlers using different filters can also live together.
def from_pyrogramchat(client, message):
print("New message in @PyrogramChat")
Handler Groups
--------------
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored.
In order to process the same message more than once, you can register your handler in a different group.
Groups are identified by a number (number 0 being the default) and are sorted. This means that a lower group number has
a higher priority.
For example, in:
.. code-block:: python
@app.on_message(Filters.text | Filters.sticker)
def text_or_sticker(client, message):
print("Text or Sticker")
@app.on_message(Filters.text)
def just_text(client, message):
print("Just Text")
``just_text`` is never executed because ``text_or_sticker`` already handles texts. To enable it, simply register the
function using a different group:
.. code-block:: python
@app.on_message(Filters.text, group=1)
def just_text(client, message):
print("Just Text")
or, if you want ``just_text`` to be fired *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``):
.. code-block:: python
@app.on_message(Filters.text, group=-1)
def just_text(client, message):
print("Just Text")
Custom Filters
--------------

View File

@ -32,7 +32,7 @@ from .client.types import (
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus,
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove,
Poll, PollOption, ChatPreview
Poll, PollOption, ChatPreview, StopPropagation
)
from .client import (
Client, ChatAction, ParseMode, Emoji,

View File

@ -134,24 +134,29 @@ class Dispatcher:
parsed_update, handler_type = parser(update, users, chats)
for group in self.groups.values():
for handler in group:
args = None
try:
for handler in group:
args = None
if isinstance(handler, RawUpdateHandler):
args = (update, users, chats)
elif isinstance(handler, handler_type):
if handler.check(parsed_update):
args = (parsed_update,)
if isinstance(handler, RawUpdateHandler):
args = (update, users, chats)
elif isinstance(handler, handler_type):
if handler.check(parsed_update):
args = (parsed_update,)
if args is None:
continue
if args is None:
continue
try:
handler.callback(self.client, *args)
except StopIteration:
raise
except Exception as e:
log.error(e, exc_info=True)
try:
handler.callback(self.client, *args)
except Exception as e:
log.error(e, exc_info=True)
finally:
break
except StopIteration:
break
except Exception as e:
log.error(e, exc_info=True)

View File

@ -37,3 +37,4 @@ from .user_and_chats import (
Chat, ChatMember, ChatMembers, ChatPhoto,
Dialog, Dialogs, User, UserStatus, ChatPreview
)
from .update import StopPropagation

View File

@ -22,10 +22,11 @@ from struct import pack
import pyrogram
from pyrogram.api import types
from ..pyrogram_type import PyrogramType
from ..update import Update
from ..user_and_chats import User
class CallbackQuery(PyrogramType):
class CallbackQuery(PyrogramType, Update):
"""This object represents an incoming callback query from a callback button in an inline keyboard.
If the button that originated the query was attached to a message sent by the bot, the field message
will be present. If the button was attached to a message sent via the bot (in inline mode),

View File

@ -26,11 +26,12 @@ from .location import Location
from .message_entity import MessageEntity
from ..messages_and_media.photo import Photo
from ..pyrogram_type import PyrogramType
from ..update import Update
from ..user_and_chats.chat import Chat
from ..user_and_chats.user import User
class Message(PyrogramType):
class Message(PyrogramType, Update):
"""This object represents a message.
Args:

View File

@ -22,10 +22,11 @@ import pyrogram
from pyrogram.api import types
from .message import Message
from ..pyrogram_type import PyrogramType
from ..update import Update
from ..user_and_chats import Chat
class Messages(PyrogramType):
class Messages(PyrogramType, Update):
"""This object represents a chat's messages.
Args:

View File

@ -0,0 +1,26 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 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/>.
class StopPropagation(StopIteration):
pass
class Update:
def stop_propagation(self):
raise StopPropagation

View File

@ -20,9 +20,10 @@ import pyrogram
from pyrogram.api import types
from ..pyrogram_type import PyrogramType
from ..update import Update
class UserStatus(PyrogramType):
class UserStatus(PyrogramType, Update):
"""This object represents a User status (Last Seen privacy).
.. note::