From 71e74da0a2b2e70f91e9aad6d73066249fdfc084 Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Wed, 14 Sep 2016 19:29:15 +0200 Subject: [PATCH 01/12] Make filters and/or-able using bitwise operators. See associated PR for more info. --- telegram/ext/__init__.py | 3 +- telegram/ext/filters.py | 150 +++++++++++++++++++++++++++++++++ telegram/ext/messagehandler.py | 63 -------------- tests/test_filters.py | 24 ++++++ 4 files changed, 176 insertions(+), 64 deletions(-) create mode 100644 telegram/ext/filters.py diff --git a/telegram/ext/__init__.py b/telegram/ext/__init__.py index 7706431843a..6180134758f 100644 --- a/telegram/ext/__init__.py +++ b/telegram/ext/__init__.py @@ -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 Filters from .regexhandler import RegexHandler from .stringcommandhandler import StringCommandHandler from .stringregexhandler import StringRegexHandler diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py new file mode 100644 index 00000000000..9c44f21b647 --- /dev/null +++ b/telegram/ext/filters.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2016 +# Leandro Toledo de Souza +# +# 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 MessageHandler class """ + + +class BaseFilter(object): + """Base class for all Message Filters""" + + def __call__(self, message): + raise NotImplementedError('Please implement a call method in your filter.') + + def __and__(self, other): + return MergedFilter(self, and_filter=other) + + def __or__(self, other): + return MergedFilter(self, or_filter=other) + + +class MergedFilter(BaseFilter): + """Represents a filter consisting of two other filters.""" + + def __init__(self, base_filter, and_filter=None, or_filter=None): + self.base_filter = base_filter + self.and_filter = and_filter + self.or_filter = or_filter + + def __call__(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) + + +class Filters(object): + """ + Convenient namespace (class) & methods for the filter funcs of the + MessageHandler class. + """ + + class Text(BaseFilter): + + def __call__(self, message): + return bool(message.text and not message.text.startswith('/')) + + text = Text() + + class Command(BaseFilter): + + def __call__(self, message): + return bool(message.text and message.text.startswith('/')) + + command = Command() + + class Audio(BaseFilter): + + def __call__(self, message): + return bool(message.audio) + + audio = Audio() + + class Document(BaseFilter): + + def __call__(self, message): + return bool(message.document) + + document = Document() + + class Photo(BaseFilter): + + def __call__(self, message): + return bool(message.photo) + + photo = Photo() + + class Sticker(BaseFilter): + + def __call__(self, message): + return bool(message.sticker) + + sticker = Sticker() + + class Video(BaseFilter): + + def __call__(self, message): + return bool(message.video) + + video = Video() + + class Voice(BaseFilter): + + def __call__(self, message): + return bool(message.voice) + + voice = Voice() + + class Contact(BaseFilter): + + def __call__(self, message): + return bool(message.contact) + + contact = Contact() + + class Location(BaseFilter): + + def __call__(self, message): + return bool(message.location) + + location = Location() + + class Venue(BaseFilter): + + def __call__(self, message): + return bool(message.venue) + + venue = Venue() + + class StatusUpdate(BaseFilter): + + def __call__(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 __call__(self, message): + return bool(message.forward_date) + + forwarded = Forwarded() diff --git a/telegram/ext/messagehandler.py b/telegram/ext/messagehandler.py index 03208fa1041..273e8c34a7d 100644 --- a/telegram/ext/messagehandler.py +++ b/telegram/ext/messagehandler.py @@ -23,69 +23,6 @@ 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) - - class MessageHandler(Handler): """ Handler class to handle telegram messages. Messages are Telegram Updates diff --git a/tests/test_filters.py b/tests/test_filters.py index b023700afd5..b9f54aeb010 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -150,6 +150,30 @@ def test_filters_status_update(self): self.assertTrue(Filters.status_update(self.message)) self.message.pinned_message = None + def test_and_filters(self): + # For now just test with forwarded as that's the only one that makes sense + # That'll change when we get a entities filter + self.message.text = 'test' + self.message.forward_date = True + self.assertTrue((Filters.text & Filters.forwarded)(self.message)) + self.message.text = '/test' + self.assertFalse((Filters.text & Filters.forwarded)(self.message)) + self.message.text = 'test' + self.message.forward_date = None + self.assertFalse((Filters.text & Filters.forwarded)(self.message)) + + def test_or_filters(self): + # For now just test with forwarded as that's the only one that makes sense + # That'll change when we get a entities filter + self.message.text = 'test' + self.assertTrue((Filters.text | Filters.status_update)(self.message)) + self.message.group_chat_created = True + self.assertTrue((Filters.text | Filters.status_update)(self.message)) + self.message.text = None + self.assertTrue((Filters.text | Filters.status_update)(self.message)) + self.message.group_chat_created = False + self.assertFalse((Filters.text | Filters.status_update)(self.message)) + if __name__ == '__main__': unittest.main() From 2161681131aa9d3fcee1a49953fff61c62ecb5dd Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sat, 24 Sep 2016 18:20:32 +0200 Subject: [PATCH 02/12] Use filter method instead of __call__ __call__ is scary looking for users wanted to create their own filters. Also allows us to put additional logic in __call__ if we want in the future. --- telegram/ext/filters.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 9c44f21b647..a23f008591d 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -23,7 +23,7 @@ class BaseFilter(object): """Base class for all Message Filters""" def __call__(self, message): - raise NotImplementedError('Please implement a call method in your filter.') + self.filter(message) def __and__(self, other): return MergedFilter(self, and_filter=other) @@ -31,6 +31,9 @@ def __and__(self, 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.""" @@ -40,7 +43,7 @@ def __init__(self, base_filter, and_filter=None, or_filter=None): self.and_filter = and_filter self.or_filter = or_filter - def __call__(self, message): + def filter(self, message): if self.and_filter: return self.base_filter(message) and self.and_filter(message) elif self.or_filter: @@ -55,84 +58,84 @@ class Filters(object): class Text(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.text and not message.text.startswith('/')) text = Text() class Command(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.text and message.text.startswith('/')) command = Command() class Audio(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.audio) audio = Audio() class Document(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.document) document = Document() class Photo(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.photo) photo = Photo() class Sticker(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.sticker) sticker = Sticker() class Video(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.video) video = Video() class Voice(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.voice) voice = Voice() class Contact(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.contact) contact = Contact() class Location(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.location) location = Location() class Venue(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.venue) venue = Venue() class StatusUpdate(BaseFilter): - def __call__(self, message): + 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 @@ -144,7 +147,7 @@ def __call__(self, message): class Forwarded(BaseFilter): - def __call__(self, message): + def filter(self, message): return bool(message.forward_date) forwarded = Forwarded() From 61596400e18de8d3ecf37ecc2f443329e49e0e4f Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sat, 24 Sep 2016 18:56:54 +0200 Subject: [PATCH 03/12] __call__ should return the result Also add tests with both & and |. --- telegram/ext/filters.py | 2 +- tests/test_filters.py | 39 ++++++++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index b17c65cc97b..5a529b05224 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -23,7 +23,7 @@ class BaseFilter(object): """Base class for all Message Filters""" def __call__(self, message): - self.filter(message) + return self.filter(message) def __and__(self, other): return MergedFilter(self, and_filter=other) diff --git a/tests/test_filters.py b/tests/test_filters.py index 24a1abe0fd6..fc7f99715af 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -23,14 +23,11 @@ import sys import unittest from datetime import datetime - import functools -from telegram import MessageEntity - sys.path.append('.') -from telegram import Message, User, Chat +from telegram import Message, User, Chat, MessageEntity from telegram.ext import Filters from tests.base import BaseTest @@ -40,6 +37,7 @@ class FiltersTest(BaseTest, unittest.TestCase): def setUp(self): self.message = Message(0, User(0, "Testuser"), datetime.now(), Chat(0, 'private')) + self.e = functools.partial(MessageEntity, offset=0, length=0) def test_filters_text(self): self.message.text = 'test' @@ -155,23 +153,19 @@ def test_filters_status_update(self): self.message.pinned_message = None def test_entities_filter(self): - e = functools.partial(MessageEntity, offset=0, length=0) - - self.message.entities = [e(MessageEntity.MENTION)] + self.message.entities = [self.e(MessageEntity.MENTION)] self.assertTrue(Filters.entity(MessageEntity.MENTION)(self.message)) self.message.entities = [] self.assertFalse(Filters.entity(MessageEntity.MENTION)(self.message)) - self.message.entities = [e(MessageEntity.BOLD)] + self.message.entities = [self.e(MessageEntity.BOLD)] self.assertFalse(Filters.entity(MessageEntity.MENTION)(self.message)) - self.message.entities = [e(MessageEntity.BOLD), e(MessageEntity.MENTION)] + self.message.entities = [self.e(MessageEntity.BOLD), self.e(MessageEntity.MENTION)] self.assertTrue(Filters.entity(MessageEntity.MENTION)(self.message)) def test_and_filters(self): - # For now just test with forwarded as that's the only one that makes sense - # That'll change when we get a entities filter self.message.text = 'test' self.message.forward_date = True self.assertTrue((Filters.text & Filters.forwarded)(self.message)) @@ -181,9 +175,16 @@ def test_and_filters(self): self.message.forward_date = None self.assertFalse((Filters.text & Filters.forwarded)(self.message)) + self.message.text = 'test' + self.message.forward_date = True + self.message.entities = [self.e(MessageEntity.MENTION)] + self.assertTrue((Filters.text & Filters.forwarded & Filters.entity(MessageEntity.MENTION))( + self.message)) + self.message.entities = [self.e(MessageEntity.BOLD)] + self.assertFalse((Filters.text & Filters.forwarded & Filters.entity(MessageEntity.MENTION) + )(self.message)) + def test_or_filters(self): - # For now just test with forwarded as that's the only one that makes sense - # That'll change when we get a entities filter self.message.text = 'test' self.assertTrue((Filters.text | Filters.status_update)(self.message)) self.message.group_chat_created = True @@ -193,6 +194,18 @@ def test_or_filters(self): self.message.group_chat_created = False self.assertFalse((Filters.text | Filters.status_update)(self.message)) + def test_and_or_filters(self): + self.message.text = 'test' + self.message.forward_date = True + self.assertTrue((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)) + )(self.message)) + self.message.forward_date = False + self.assertFalse((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION) + ))(self.message)) + self.message.entities = [self.e(MessageEntity.MENTION)] + self.assertTrue((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)) + )(self.message)) + if __name__ == '__main__': unittest.main() From 3244417f6178c21d9ce9814dd3a0ad019967e1de Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sun, 25 Sep 2016 00:30:04 +0200 Subject: [PATCH 04/12] Add docs for filters. --- docs/source/telegram.ext.filters.rst | 7 +++ docs/source/telegram.ext.rst | 1 + telegram/ext/__init__.py | 4 +- telegram/ext/filters.py | 85 ++++++++++++++++------------ tests/test_filters.py | 2 +- 5 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 docs/source/telegram.ext.filters.rst diff --git a/docs/source/telegram.ext.filters.rst b/docs/source/telegram.ext.filters.rst new file mode 100644 index 00000000000..a04a2d1c13b --- /dev/null +++ b/docs/source/telegram.ext.filters.rst @@ -0,0 +1,7 @@ +telegram.ext.filters module +=========================== + +.. automodule:: telegram.ext.filters + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.rst b/docs/source/telegram.ext.rst index 51c652e4f07..3fa83b1b143 100644 --- a/docs/source/telegram.ext.rst +++ b/docs/source/telegram.ext.rst @@ -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 diff --git a/telegram/ext/__init__.py b/telegram/ext/__init__.py index 6180134758f..451354abe58 100644 --- a/telegram/ext/__init__.py +++ b/telegram/ext/__init__.py @@ -27,7 +27,7 @@ from .handler import Handler from .inlinequeryhandler import InlineQueryHandler from .messagehandler import MessageHandler -from .filters import Filters +from .filters import BaseFilter, Filters from .regexhandler import RegexHandler from .stringcommandhandler import StringCommandHandler from .stringregexhandler import StringRegexHandler @@ -36,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') diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 5a529b05224..fabbe087093 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -16,11 +16,31 @@ # # 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 """ +""" This module contains the Filters for use with the MessageHandler class """ class BaseFilter(object): - """Base class for all Message Filters""" + """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) @@ -52,88 +72,87 @@ def filter(self, message): class Filters(object): """ - Convenient namespace (class) & methods for the filter funcs of the - MessageHandler class. + Predefined filters for use with the `filter` argument of :class:`telegram.ext.MessageHandler`. """ - class Text(BaseFilter): + class _Text(BaseFilter): def filter(self, message): return bool(message.text and not message.text.startswith('/')) - text = Text() + text = _Text() - class Command(BaseFilter): + class _Command(BaseFilter): def filter(self, message): return bool(message.text and message.text.startswith('/')) - command = Command() + command = _Command() - class Audio(BaseFilter): + class _Audio(BaseFilter): def filter(self, message): return bool(message.audio) - audio = Audio() + audio = _Audio() - class Document(BaseFilter): + class _Document(BaseFilter): def filter(self, message): return bool(message.document) - document = Document() + document = _Document() - class Photo(BaseFilter): + class _Photo(BaseFilter): def filter(self, message): return bool(message.photo) - photo = Photo() + photo = _Photo() - class Sticker(BaseFilter): + class _Sticker(BaseFilter): def filter(self, message): return bool(message.sticker) - sticker = Sticker() + sticker = _Sticker() - class Video(BaseFilter): + class _Video(BaseFilter): def filter(self, message): return bool(message.video) - video = Video() + video = _Video() - class Voice(BaseFilter): + class _Voice(BaseFilter): def filter(self, message): return bool(message.voice) - voice = Voice() + voice = _Voice() - class Contact(BaseFilter): + class _Contact(BaseFilter): def filter(self, message): return bool(message.contact) - contact = Contact() + contact = _Contact() - class Location(BaseFilter): + class _Location(BaseFilter): def filter(self, message): return bool(message.location) - location = Location() + location = _Location() - class Venue(BaseFilter): + class _Venue(BaseFilter): def filter(self, message): return bool(message.venue) - venue = Venue() + venue = _Venue() - class StatusUpdate(BaseFilter): + class _StatusUpdate(BaseFilter): def filter(self, message): return bool(message.new_chat_member or message.left_chat_member @@ -143,16 +162,16 @@ def filter(self, message): or message.migrate_to_chat_id or message.migrate_from_chat_id or message.pinned_message) - status_update = StatusUpdate() + status_update = _StatusUpdate() - class Forwarded(BaseFilter): + class _Forwarded(BaseFilter): def filter(self, message): return bool(message.forward_date) - forwarded = Forwarded() + forwarded = _Forwarded() - class Entity(BaseFilter): + class entity(BaseFilter): """Filters messages to only allow those which have a :class:`telegram.MessageEntity` where their `type` matches `entity_type`. @@ -168,7 +187,3 @@ def __init__(self, entity_type): def filter(self, message): return any([entity.type == self.entity_type for entity in message.entities]) - -# We don't initialize since this filter accepts arguments. - - entity = Entity diff --git a/tests/test_filters.py b/tests/test_filters.py index fc7f99715af..a7208968e07 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """ -This module contains a object that represents Tests for MessageHandler.Filters +This module contains a object that represents Tests for Filters for use with MessageHandler """ import sys From 79e065a730dc8b1df93b99a200229813b892be46 Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sun, 25 Sep 2016 16:31:06 +0200 Subject: [PATCH 05/12] Add __str__ and __repr__ to MergedFilter. --- telegram/ext/filters.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index fabbe087093..d2672212ac6 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -69,6 +69,13 @@ def filter(self, message): elif self.or_filter: return self.base_filter(message) or self.or_filter(message) + def __str__(self): + return ("").format(self.base_filter, "and" if self.and_filter else "or", + self.and_filter or self.or_filter) + + __repr__ = __str__ + class Filters(object): """ From 79bdfe4c5d4a5659c3ff2dc7e725ab95e7029e7b Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Thu, 29 Sep 2016 19:10:22 +0200 Subject: [PATCH 06/12] Allow filters to be passed without list. Also deprecates actually using a list. --- telegram/ext/messagehandler.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/telegram/ext/messagehandler.py b/telegram/ext/messagehandler.py index 4895c59619f..b222de6d45f 100644 --- a/telegram/ext/messagehandler.py +++ b/telegram/ext/messagehandler.py @@ -17,6 +17,7 @@ # 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 @@ -30,12 +31,10 @@ class MessageHandler(Handler): 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. @@ -57,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)): @@ -66,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 From ca5e3146c6578d9e2bc482ee090a863eafbfa573 Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Fri, 14 Oct 2016 10:32:12 +0200 Subject: [PATCH 07/12] Fix docstring according to Jannes' commentns. --- telegram/ext/filters.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index d2672212ac6..2a56bfb65f9 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -26,7 +26,7 @@ class BaseFilter(object): And: - >>> (Filters.text & Filters.entity(MENTION) + >>> (Filters.text & Filters.entity(MENTION)) Or: @@ -34,7 +34,7 @@ class BaseFilter(object): Also works with more than two filters: - >>> (Filters.text & (Filters.entity(URL |Filters.entity(TEXT_LINK)))) + >>> (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` @@ -56,7 +56,13 @@ def filter(self, message): class MergedFilter(BaseFilter): - """Represents a filter consisting of two other filters.""" + """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): self.base_filter = base_filter From c626044a3033432c778984d879467c3acc461c39 Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sat, 15 Oct 2016 22:58:55 +0200 Subject: [PATCH 08/12] Add "all" filter Since and empty list cannot (in the future, currently only deprecated) be used. --- telegram/ext/filters.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 2a56bfb65f9..4b433810748 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -88,6 +88,13 @@ 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): From f99b2f8f3b7215c95ff10921a23e7a0bb704421d Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sat, 15 Oct 2016 22:59:41 +0200 Subject: [PATCH 09/12] Inprove coverage --- tests/test_filters.py | 20 +++++++++++++++++++- tests/test_updater.py | 7 +++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index a7208968e07..8a6056699cb 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -28,7 +28,7 @@ sys.path.append('.') from telegram import Message, User, Chat, MessageEntity -from telegram.ext import Filters +from telegram.ext import Filters, BaseFilter from tests.base import BaseTest @@ -206,6 +206,24 @@ def test_and_or_filters(self): self.assertTrue((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)) )(self.message)) + self.assertRegex( + str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), + r" and " + r" or " + r">>") + + def test_faulty_custom_filter(self): + + class _CustomFilter(BaseFilter): + pass + + custom = _CustomFilter() + + with self.assertRaises(NotImplementedError): + (custom & Filters.text)(self.message) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_updater.py b/tests/test_updater.py index a269378eb2f..cbc2a69bc88 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -182,7 +182,7 @@ def test_editedMessageHandler(self): self._setup_updater('Test', edited=True) d = self.updater.dispatcher from telegram.ext import Filters - handler = MessageHandler([Filters.text], self.telegramHandlerEditedTest, allow_edited=True) + handler = MessageHandler(Filters.text, self.telegramHandlerEditedTest, allow_edited=True) d.addHandler(handler) self.updater.start_polling(0.01) sleep(.1) @@ -190,8 +190,7 @@ def test_editedMessageHandler(self): # Remove handler d.removeHandler(handler) - handler = MessageHandler( - [Filters.text], self.telegramHandlerEditedTest, allow_edited=False) + handler = MessageHandler(Filters.text, self.telegramHandlerEditedTest, allow_edited=False) d.addHandler(handler) self.reset() @@ -201,7 +200,7 @@ def test_editedMessageHandler(self): def test_addTelegramMessageHandlerMultipleMessages(self): self._setup_updater('Multiple', 100) - self.updater.dispatcher.add_handler(MessageHandler([], self.telegramHandlerTest)) + self.updater.dispatcher.add_handler(MessageHandler(Filters.all, self.telegramHandlerTest)) self.updater.start_polling(0.0) sleep(2) self.assertEqual(self.received_message, 'Multiple') From 29e0cc64e99c94ed00266e1a71189d8a6aaa5d9c Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sat, 15 Oct 2016 23:29:46 +0200 Subject: [PATCH 10/12] Fix py2 compat --- tests/test_filters.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index 8a6056699cb..22b18e0be43 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -206,13 +206,22 @@ def test_and_or_filters(self): self.assertTrue((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)) )(self.message)) - self.assertRegex( - str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), - r" and " - r" or " - r">>") + try: + self.assertRegex( + str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), + r" and " + r" or " + r">>") + except AttributeError: + self.assertRegexpMatches( + str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), + r" and " + r" or " + r">>") def test_faulty_custom_filter(self): From 5408c23e3306bb253428d6de644b6247359d753b Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sun, 16 Oct 2016 12:48:45 +0200 Subject: [PATCH 11/12] assertRegexMatches still exists for now. Use it. --- tests/test_filters.py | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index 22b18e0be43..a648e02dfd0 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -206,22 +206,13 @@ def test_and_or_filters(self): self.assertTrue((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)) )(self.message)) - try: - self.assertRegex( - str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), - r" and " - r" or " - r">>") - except AttributeError: - self.assertRegexpMatches( - str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), - r" and " - r" or " - r">>") + self.assertRegexpMatches( + str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), + r" and " + r" or " + r">>") def test_faulty_custom_filter(self): From 39832d2f6b848c3a9fc9ea3c85615e11e7c89808 Mon Sep 17 00:00:00 2001 From: Jacob Bom Date: Sun, 16 Oct 2016 13:19:42 +0200 Subject: [PATCH 12/12] __str__ behaves differntly on py2 apparently. --- tests/test_filters.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index a648e02dfd0..c1963ada251 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -208,11 +208,10 @@ def test_and_or_filters(self): self.assertRegexpMatches( str((Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION)))), - r" and " - r" or " - r">>") + r" and or " + r">>") def test_faulty_custom_filter(self): 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