Update filters: Make the name argument optional

This commit is contained in:
Dan 2019-06-28 11:11:59 +02:00
parent 506253e506
commit 155580649a
2 changed files with 92 additions and 96 deletions

View File

@ -24,7 +24,7 @@ button:
app.send_message(
"username", # Change this to your username or id
"Pyrogram's custom filter test",
"Pyrogram custom filter test",
reply_markup=InlineKeyboardMarkup(
[[InlineKeyboardButton("Press me", "pyrogram")]]
)
@ -33,61 +33,54 @@ button:
Basic Filters
-------------
For this basic filter we will be using only the first two parameters of :meth:`~pyrogram.Filters.create`.
For this basic filter we will be using only the first parameter of :meth:`~pyrogram.Filters.create`.
The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries
containing "Pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data
equals to ``"Pyrogram"``.
containing "pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data
equals to ``"pyrogram"``.
.. code-block:: python
static_data = Filters.create(
name="StaticdData",
func=lambda flt, query: query.data == "Pyrogram"
)
static_data_filter = Filters.create(lambda _, query: query.data == "pyrogram")
The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same
could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope:
could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter scope:
.. code-block:: python
def func(flt, query):
return query.data == "Pyrogram"
def func(_, query):
return query.data == "pyrogram"
static_data = Filters.create(
name="StaticData",
func=func
)
static_data_filter = Filters.create(func)
The filter usage remains the same:
.. code-block:: python
@app.on_callback_query(static_data)
@app.on_callback_query(static_data_filter)
def pyrogram_data(_, query):
query.answer("it works!")
Filters with Arguments
----------------------
A much cooler filter would be one that accepts "Pyrogram" or any other data as argument at usage time.
A dynamic filter like this will make use of the third parameter of :meth:`~pyrogram.Filters.create`.
A much cooler filter would be one that accepts "pyrogram" or any other data as argument at usage time.
A dynamic filter like this will make use of named arguments for the :meth:`~pyrogram.Filters.create` method.
This is how a dynamic custom filter looks like:
.. code-block:: python
def dynamic_data(data):
def dynamic_data_filter(data):
return Filters.create(
name="DynamicData",
func=lambda flt, query: flt.data == query.data,
data=data # "data" kwarg is accessed with "flt.data"
lambda flt, query: flt.data == query.data,
data=data # "data" kwarg is accessed with "flt.data" above
)
And its usage:
.. code-block:: python
@app.on_callback_query(dynamic_data("Pyrogram"))
@app.on_callback_query(dynamic_data_filter("pyrogram"))
def pyrogram_data(_, query):
query.answer("it works!")

View File

@ -17,190 +17,193 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import re
from typing import Callable
from .filter import Filter
from ..types.bots_and_keyboards import InlineKeyboardMarkup, ReplyKeyboardMarkup
CUSTOM_FILTER_NAME = "CustomFilter"
def create(name: str, func: callable, **kwargs) -> type:
"""Create a Filter.
def create(func: Callable, name: str = None, **kwargs) -> Filter:
"""Easily create a custom filter.
Custom filters give you extra control over which updates are allowed or not to be processed by your handlers.
Parameters:
name (``str``):
Your filter's name. Can be anything you like.
func (``callable``):
A function that accepts two arguments *(filter, update)* and returns a Boolean: True if the update should be
handled, False otherwise.
The "update" argument type will vary depending on which `Handler <Handlers.html>`_ is coming from.
For example, in a :obj:`MessageHandler` the update type will be
a :obj:`Message`; in a :obj:`CallbackQueryHandler` the
update type will be a :obj:`CallbackQuery`. Your function body can then access the
incoming update and decide whether to allow it or not.
A function that accepts two positional arguments *(filter, update)* and returns a boolean: True if the
update should be handled, False otherwise. The *filter* argument refers to the filter itself and can be used
to access keyword arguments (read below). The *update* argument type will vary depending on which
`Handler <handlers>`_ is coming from. For example, in a :obj:`MessageHandler` the *update* argument will be
a :obj:`Message`; in a :obj:`CallbackQueryHandler` the *update* will be a :obj:`CallbackQuery`. Your
function body can then access the incoming update attributes and decide whether to allow it or not.
name (``str``, *optional*):
Your filter's name. Can be anything you like.
Defaults to "CustomFilter".
**kwargs (``any``, *optional*):
Any keyword argument you would like to pass. Useful for custom filters that accept parameters (e.g.:
:meth:`~Filters.command`, :meth:`~Filters.regex`).
Any keyword argument you would like to pass. Useful when creating parameterized custom filters, such as
:meth:`~Filters.command` or :meth:`~Filters.regex`.
"""
# TODO: unpack kwargs using **kwargs into the dict itself. For Python 3.5+ only
d = {"__call__": func}
d.update(kwargs)
return type(name, (Filter,), d)()
return type(name or CUSTOM_FILTER_NAME, (Filter,), d)()
class Filters:
"""This class provides access to all library-defined Filters available in Pyrogram.
The Filters listed here are intended to be used with the :obj:`MessageHandler` only.
The Filters listed here are currently intended to be used with the :obj:`MessageHandler` only.
At the moment, if you want to filter updates coming from different `Handlers <Handlers.html>`_ you have to create
your own filters with :meth:`~Filters.create` and use them in the same way.
"""
create = create
me = create("Me", lambda _, m: bool(m.from_user and m.from_user.is_self))
me = create(lambda _, m: bool(m.from_user and m.from_user.is_self), "MeFilter")
"""Filter messages generated by you yourself."""
bot = create("Bot", lambda _, m: bool(m.from_user and m.from_user.is_bot))
bot = create(lambda _, m: bool(m.from_user and m.from_user.is_bot), "BotFilter")
"""Filter messages coming from bots."""
incoming = create("Incoming", lambda _, m: not m.outgoing)
incoming = create(lambda _, m: not m.outgoing, "IncomingFilter")
"""Filter incoming messages. Messages sent to your own chat (Saved Messages) are also recognised as incoming."""
outgoing = create("Outgoing", lambda _, m: m.outgoing)
outgoing = create(lambda _, m: m.outgoing, "OutgoingFilter")
"""Filter outgoing messages. Messages sent to your own chat (Saved Messages) are not recognized as outgoing."""
text = create("Text", lambda _, m: bool(m.text))
text = create(lambda _, m: bool(m.text), "TextFilter")
"""Filter text messages."""
reply = create("Reply", lambda _, m: bool(m.reply_to_message))
reply = create(lambda _, m: bool(m.reply_to_message), "ReplyFilter")
"""Filter messages that are replies to other messages."""
forwarded = create("Forwarded", lambda _, m: bool(m.forward_date))
forwarded = create(lambda _, m: bool(m.forward_date), "ForwardedFilter")
"""Filter messages that are forwarded."""
caption = create("Caption", lambda _, m: bool(m.caption))
caption = create(lambda _, m: bool(m.caption), "CaptionFilter")
"""Filter media messages that contain captions."""
edited = create("Edited", lambda _, m: bool(m.edit_date))
edited = create(lambda _, m: bool(m.edit_date), "EditedFilter")
"""Filter edited messages."""
audio = create("Audio", lambda _, m: bool(m.audio))
audio = create(lambda _, m: bool(m.audio), "AudioFilter")
"""Filter messages that contain :obj:`Audio` objects."""
document = create("Document", lambda _, m: bool(m.document))
document = create(lambda _, m: bool(m.document), "DocumentFilter")
"""Filter messages that contain :obj:`Document` objects."""
photo = create("Photo", lambda _, m: bool(m.photo))
photo = create(lambda _, m: bool(m.photo), "PhotoFilter")
"""Filter messages that contain :obj:`Photo` objects."""
sticker = create("Sticker", lambda _, m: bool(m.sticker))
sticker = create(lambda _, m: bool(m.sticker), "StickerFilter")
"""Filter messages that contain :obj:`Sticker` objects."""
animation = create("Animation", lambda _, m: bool(m.animation))
animation = create(lambda _, m: bool(m.animation), "AnimationFilter")
"""Filter messages that contain :obj:`Animation` objects."""
game = create("Game", lambda _, m: bool(m.game))
game = create(lambda _, m: bool(m.game), "GameFilter")
"""Filter messages that contain :obj:`Game` objects."""
video = create("Video", lambda _, m: bool(m.video))
video = create(lambda _, m: bool(m.video), "VideoFilter")
"""Filter messages that contain :obj:`Video` objects."""
media_group = create("MediaGroup", lambda _, m: bool(m.media_group_id))
media_group = create(lambda _, m: bool(m.media_group_id), "MediaGroupFilter")
"""Filter messages containing photos or videos being part of an album."""
voice = create("Voice", lambda _, m: bool(m.voice))
voice = create(lambda _, m: bool(m.voice), "VoiceFilter")
"""Filter messages that contain :obj:`Voice` note objects."""
video_note = create("VideoNote", lambda _, m: bool(m.video_note))
video_note = create(lambda _, m: bool(m.video_note), "VideoNoteFilter")
"""Filter messages that contain :obj:`VideoNote` objects."""
contact = create("Contact", lambda _, m: bool(m.contact))
contact = create(lambda _, m: bool(m.contact), "ContactFilter")
"""Filter messages that contain :obj:`Contact` objects."""
location = create("Location", lambda _, m: bool(m.location))
location = create(lambda _, m: bool(m.location), "LocationFilter")
"""Filter messages that contain :obj:`Location` objects."""
venue = create("Venue", lambda _, m: bool(m.venue))
venue = create(lambda _, m: bool(m.venue), "VenueFilter")
"""Filter messages that contain :obj:`Venue` objects."""
web_page = create("WebPage", lambda _, m: m.web_page)
web_page = create(lambda _, m: m.web_page, "WebPageFilter")
"""Filter messages sent with a webpage preview."""
poll = create("Poll", lambda _, m: m.poll)
poll = create(lambda _, m: m.poll, "PollFilter")
"""Filter messages that contain :obj:`Poll` objects."""
private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
private = create(lambda _, m: bool(m.chat and m.chat.type == "private"), "PrivateFilter")
"""Filter messages sent in private chats."""
group = create("Group", lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}))
group = create(lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}), "GroupFilter")
"""Filter messages sent in group or supergroup chats."""
channel = create("Channel", lambda _, m: bool(m.chat and m.chat.type == "channel"))
channel = create(lambda _, m: bool(m.chat and m.chat.type == "channel"), "ChannelFilter")
"""Filter messages sent in channels."""
new_chat_members = create("NewChatMembers", lambda _, m: bool(m.new_chat_members))
new_chat_members = create(lambda _, m: bool(m.new_chat_members), "NewChatMembersFilter")
"""Filter service messages for new chat members."""
left_chat_member = create("LeftChatMember", lambda _, m: bool(m.left_chat_member))
left_chat_member = create(lambda _, m: bool(m.left_chat_member), "LeftChatMemberFilter")
"""Filter service messages for members that left the chat."""
new_chat_title = create("NewChatTitle", lambda _, m: bool(m.new_chat_title))
new_chat_title = create(lambda _, m: bool(m.new_chat_title), "NewChatTitleFilter")
"""Filter service messages for new chat titles."""
new_chat_photo = create("NewChatPhoto", lambda _, m: bool(m.new_chat_photo))
new_chat_photo = create(lambda _, m: bool(m.new_chat_photo), "NewChatPhotoFilter")
"""Filter service messages for new chat photos."""
delete_chat_photo = create("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo))
delete_chat_photo = create(lambda _, m: bool(m.delete_chat_photo), "DeleteChatPhotoFilter")
"""Filter service messages for deleted photos."""
group_chat_created = create("GroupChatCreated", lambda _, m: bool(m.group_chat_created))
group_chat_created = create(lambda _, m: bool(m.group_chat_created), "GroupChatCreatedFilter")
"""Filter service messages for group chat creations."""
supergroup_chat_created = create("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created))
supergroup_chat_created = create(lambda _, m: bool(m.supergroup_chat_created), "SupergroupChatCreatedFilter")
"""Filter service messages for supergroup chat creations."""
channel_chat_created = create("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created))
channel_chat_created = create(lambda _, m: bool(m.channel_chat_created), "ChannelChatCreatedFilter")
"""Filter service messages for channel chat creations."""
migrate_to_chat_id = create("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id))
migrate_to_chat_id = create(lambda _, m: bool(m.migrate_to_chat_id), "MigrateToChatIdFilter")
"""Filter service messages that contain migrate_to_chat_id."""
migrate_from_chat_id = create("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id))
migrate_from_chat_id = create(lambda _, m: bool(m.migrate_from_chat_id), "MigrateFromChatIdFilter")
"""Filter service messages that contain migrate_from_chat_id."""
pinned_message = create("PinnedMessage", lambda _, m: bool(m.pinned_message))
pinned_message = create(lambda _, m: bool(m.pinned_message), "PinnedMessageFilter")
"""Filter service messages for pinned messages."""
game_high_score = create("GameHighScore", lambda _, m: bool(m.game_high_score))
game_high_score = create(lambda _, m: bool(m.game_high_score), "GameHighScoreFilter")
"""Filter service messages for game high scores."""
reply_keyboard = create("ReplyKeyboard", lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup))
reply_keyboard = create(lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup), "ReplyKeyboardFilter")
"""Filter messages containing reply keyboard markups"""
inline_keyboard = create("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup))
inline_keyboard = create(lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup), "InlineKeyboardFilter")
"""Filter messages containing inline keyboard markups"""
mentioned = create("Mentioned", lambda _, m: bool(m.mentioned))
mentioned = create(lambda _, m: bool(m.mentioned), "MentionedFilter")
"""Filter messages containing mentions"""
via_bot = create("ViaBot", lambda _, m: bool(m.via_bot))
via_bot = create(lambda _, m: bool(m.via_bot), "ViaBotFilter")
"""Filter messages sent via inline bots"""
service = create("Service", lambda _, m: bool(m.service))
service = create(lambda _, m: bool(m.service), "ServiceFilter")
"""Filter service messages.
A service message contains any of the following fields set: *left_chat_member*,
*new_chat_title*, *new_chat_photo*, *delete_chat_photo*, *group_chat_created*, *supergroup_chat_created*,
*channel_chat_created*, *migrate_to_chat_id*, *migrate_from_chat_id*, *pinned_message*, *game_score*.
"""
media = create("Media", lambda _, m: bool(m.media))
media = create(lambda _, m: bool(m.media), "MediaFilter")
"""Filter media messages.
A media message contains any of the following fields set: *audio*, *document*, *photo*, *sticker*, *video*,
*animation*, *voice*, *video_note*, *contact*, *location*, *venue*, *poll*.
"""
@ -253,17 +256,17 @@ class Filters:
commands = {c if case_sensitive else c.lower() for c in commands}
prefixes = set(prefix) if prefix else {""}
return create("Command", func=func, c=commands, p=prefixes, s=separator, cs=case_sensitive)
return create(func, "CommandFilter", c=commands, p=prefixes, s=separator, cs=case_sensitive)
@staticmethod
def regex(pattern, flags: int = 0):
"""Filter messages that match a given RegEx pattern.
"""Filter message texts or captions that match a given regular expression pattern.
Parameters:
pattern (``str``):
The RegEx pattern as string, it will be applied to the text of a message. When a pattern matches,
all the `Match Objects <https://docs.python.org/3/library/re.html#match-objects>`_
are stored in the *matches* field of the :obj:`Message` itself.
The RegEx pattern as string, it will be applied to the text or the caption of a message. When a pattern
matches, all the `Match Objects <https://docs.python.org/3/library/re.html#match-objects>`_ are stored
in the *matches* field of the :obj:`Message` itself.
flags (``int``, *optional*):
RegEx flags.
@ -273,7 +276,7 @@ class Filters:
m.matches = [i for i in _.p.finditer(m.text or m.caption or "")]
return bool(m.matches)
return create("Regex", f, p=re.compile(pattern, flags))
return create(f, "RegexFilter", p=re.compile(pattern, flags))
# noinspection PyPep8Naming
class user(Filter, set):
@ -285,7 +288,7 @@ class Filters:
Parameters:
users (``int`` | ``str`` | ``list``):
Pass one or more user ids/usernames to filter users.
For you yourself, "me" or "self" can be used as well.
For you yourself, "me" or "self" can be used as well.
Defaults to None (no users).
"""
@ -342,12 +345,12 @@ class Filters:
@staticmethod
def callback_data(data: str or bytes):
"""Filter callback queries for their data.
Parameters:
data (``str`` | ``bytes``):
Pass the data you want to filter for.
"""
return create("CallbackData", lambda flt, cb: cb.data == flt.data, data=data)
return create(lambda flt, cb: cb.data == flt.data, "CallbackDataFilter", data=data)
dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162))
dan = create(lambda _, m: bool(m.from_user and m.from_user.id == 23122162), "DanFilter")