Skip to content

Make filters and/or-able using bitwise operators. #411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Oct 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/source/telegram.ext.filters.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
telegram.ext.filters module
===========================

.. automodule:: telegram.ext.filters
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/source/telegram.ext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Submodules
telegram.ext.commandhandler
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.filters
telegram.ext.regexhandler
telegram.ext.stringcommandhandler
telegram.ext.stringregexhandler
Expand Down
5 changes: 3 additions & 2 deletions telegram/ext/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
from .commandhandler import CommandHandler
from .handler import Handler
from .inlinequeryhandler import InlineQueryHandler
from .messagehandler import MessageHandler, Filters
from .messagehandler import MessageHandler
from .filters import BaseFilter, Filters
from .regexhandler import RegexHandler
from .stringcommandhandler import StringCommandHandler
from .stringregexhandler import StringRegexHandler
Expand All @@ -35,5 +36,5 @@

__all__ = ('Dispatcher', 'JobQueue', 'Job', 'Updater', 'CallbackQueryHandler',
'ChosenInlineResultHandler', 'CommandHandler', 'Handler', 'InlineQueryHandler',
'MessageHandler', 'Filters', 'RegexHandler', 'StringCommandHandler',
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler')
209 changes: 209 additions & 0 deletions telegram/ext/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2016
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the Filters for use with the MessageHandler class """


class BaseFilter(object):
"""Base class for all Message Filters

Subclassing from this class filters to be combined using bitwise operators:

And:

>>> (Filters.text & Filters.entity(MENTION))

Or:

>>> (Filters.audio | Filters.video)

Also works with more than two filters:

>>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK)))

If you want to create your own filters create a class inheriting from this class and implement
a `filter` method that returns a boolean: `True` if the message should be handled, `False`
otherwise. Note that the filters work only as class instances, not actual class objects
(so remember to initialize your filter classes).
"""

def __call__(self, message):
return self.filter(message)

def __and__(self, other):
return MergedFilter(self, and_filter=other)

def __or__(self, other):
return MergedFilter(self, or_filter=other)

def filter(self, message):
raise NotImplementedError


class MergedFilter(BaseFilter):
"""Represents a filter consisting of two other filters.

Args:
base_filter: Filter 1 of the merged filter
and_filter: Optional filter to "and" with base_filter. Mutually exclusive with or_filter.
or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter.
"""

def __init__(self, base_filter, and_filter=None, or_filter=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a docstring indicating that and_filter and or_filter are mutually exclusive

self.base_filter = base_filter
self.and_filter = and_filter
self.or_filter = or_filter

def filter(self, message):
if self.and_filter:
return self.base_filter(message) and self.and_filter(message)
elif self.or_filter:
return self.base_filter(message) or self.or_filter(message)

def __str__(self):
return ("<telegram.ext.filters.MergedFilter consisting of"
" {} {} {}>").format(self.base_filter, "and" if self.and_filter else "or",
self.and_filter or self.or_filter)

__repr__ = __str__


class Filters(object):
"""
Predefined filters for use with the `filter` argument of :class:`telegram.ext.MessageHandler`.
"""

class _All(BaseFilter):

def filter(self, message):
return True

all = _All()

class _Text(BaseFilter):

def filter(self, message):
return bool(message.text and not message.text.startswith('/'))

text = _Text()

class _Command(BaseFilter):

def filter(self, message):
return bool(message.text and message.text.startswith('/'))

command = _Command()

class _Audio(BaseFilter):

def filter(self, message):
return bool(message.audio)

audio = _Audio()

class _Document(BaseFilter):

def filter(self, message):
return bool(message.document)

document = _Document()

class _Photo(BaseFilter):

def filter(self, message):
return bool(message.photo)

photo = _Photo()

class _Sticker(BaseFilter):

def filter(self, message):
return bool(message.sticker)

sticker = _Sticker()

class _Video(BaseFilter):

def filter(self, message):
return bool(message.video)

video = _Video()

class _Voice(BaseFilter):

def filter(self, message):
return bool(message.voice)

voice = _Voice()

class _Contact(BaseFilter):

def filter(self, message):
return bool(message.contact)

contact = _Contact()

class _Location(BaseFilter):

def filter(self, message):
return bool(message.location)

location = _Location()

class _Venue(BaseFilter):

def filter(self, message):
return bool(message.venue)

venue = _Venue()

class _StatusUpdate(BaseFilter):

def filter(self, message):
return bool(message.new_chat_member or message.left_chat_member
or message.new_chat_title or message.new_chat_photo
or message.delete_chat_photo or message.group_chat_created
or message.supergroup_chat_created or message.channel_chat_created
or message.migrate_to_chat_id or message.migrate_from_chat_id
or message.pinned_message)

status_update = _StatusUpdate()

class _Forwarded(BaseFilter):

def filter(self, message):
return bool(message.forward_date)

forwarded = _Forwarded()

class entity(BaseFilter):
"""Filters messages to only allow those which have a :class:`telegram.MessageEntity`
where their `type` matches `entity_type`.

Args:
entity_type: Entity type to check for. All types can be found as constants
in :class:`telegram.MessageEntity`.

Returns: function to use as filter
"""

def __init__(self, entity_type):
self.entity_type = entity_type

def filter(self, message):
return any([entity.type == self.entity_type for entity in message.entities])
103 changes: 16 additions & 87 deletions telegram/ext/messagehandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,105 +17,24 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
""" This module contains the MessageHandler class """
import warnings

from .handler import Handler
from telegram import Update
from telegram.utils.deprecate import deprecate


class Filters(object):
"""
Convenient namespace (class) & methods for the filter funcs of the
MessageHandler class.
"""

@staticmethod
def text(message):
return message.text and not message.text.startswith('/')

@staticmethod
def command(message):
return message.text and message.text.startswith('/')

@staticmethod
def audio(message):
return bool(message.audio)

@staticmethod
def document(message):
return bool(message.document)

@staticmethod
def photo(message):
return bool(message.photo)

@staticmethod
def sticker(message):
return bool(message.sticker)

@staticmethod
def video(message):
return bool(message.video)

@staticmethod
def voice(message):
return bool(message.voice)

@staticmethod
def contact(message):
return bool(message.contact)

@staticmethod
def location(message):
return bool(message.location)

@staticmethod
def venue(message):
return bool(message.venue)

@staticmethod
def status_update(message):
return bool(message.new_chat_member or message.left_chat_member or message.new_chat_title
or message.new_chat_photo or message.delete_chat_photo
or message.group_chat_created or message.supergroup_chat_created
or message.channel_chat_created or message.migrate_to_chat_id
or message.migrate_from_chat_id or message.pinned_message)

@staticmethod
def forwarded(message):
return bool(message.forward_date)

@staticmethod
def entity(entity_type):
"""Filters messages to only allow those which have a :class:`telegram.MessageEntity`
where their `type` matches `entity_type`.

Args:
entity_type: Entity type to check for. All types can be found as constants
in :class:`telegram.MessageEntity`.

Returns: function to use as filter
"""

def entities_filter(message):
return any([entity.type == entity_type for entity in message.entities])

return entities_filter


class MessageHandler(Handler):
"""
Handler class to handle telegram messages. Messages are Telegram Updates
that do not contain a command. They might contain text, media or status
updates.

Args:
filters (list[function]): A list of filter functions. Standard filters
can be found in the Filters class above.
| Each `function` takes ``Update`` as arg and returns ``bool``.
| All messages that match at least one of those filters will be
accepted. If ``bool(filters)`` evaluates to ``False``, messages are
not filtered.
filters (telegram.ext.BaseFilter): A filter inheriting from
:class:`telegram.filters.BaseFilter`. Standard filters can be found in
:class:`telegram.filters.Filters`. Filters can be combined using bitwise
operators (& for and, | for or).
callback (function): A function that takes ``bot, update`` as
positional arguments. It will be called when the ``check_update``
has determined that an update should be processed by this handler.
Expand All @@ -137,6 +56,13 @@ def __init__(self,
self.filters = filters
self.allow_edited = allow_edited

# We put this up here instead of with the rest of checking code
# in check_update since we don't wanna spam a ton
if isinstance(self.filters, list):
warnings.warn('Using a list of filters in MessageHandler is getting '
'deprecated, please use bitwise operators (& and |) '
'instead. More info: https://git.io/vPTbc.')

def check_update(self, update):
if (isinstance(update, Update)
and (update.message or update.edited_message and self.allow_edited)):
Expand All @@ -146,7 +72,10 @@ def check_update(self, update):

else:
message = update.message or update.edited_message
res = any(func(message) for func in self.filters)
if isinstance(self.filters, list):
res = any(func(message) for func in self.filters)
else:
res = self.filters(message)

else:
res = False
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy