Skip to content

Commit 71e74da

Browse files
committed
Make filters and/or-able using bitwise operators.
See associated PR for more info.
1 parent 5285f63 commit 71e74da

4 files changed

Lines changed: 176 additions & 64 deletions

File tree

telegram/ext/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
from .commandhandler import CommandHandler
2727
from .handler import Handler
2828
from .inlinequeryhandler import InlineQueryHandler
29-
from .messagehandler import MessageHandler, Filters
29+
from .messagehandler import MessageHandler
30+
from .filters import Filters
3031
from .regexhandler import RegexHandler
3132
from .stringcommandhandler import StringCommandHandler
3233
from .stringregexhandler import StringRegexHandler

telegram/ext/filters.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/usr/bin/env python
2+
#
3+
# A library that provides a Python interface to the Telegram Bot API
4+
# Copyright (C) 2015-2016
5+
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU Lesser Public License as published by
9+
# the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# This program is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU Lesser Public License for more details.
16+
#
17+
# You should have received a copy of the GNU Lesser Public License
18+
# along with this program. If not, see [http://www.gnu.org/licenses/].
19+
""" This module contains the MessageHandler class """
20+
21+
22+
class BaseFilter(object):
23+
"""Base class for all Message Filters"""
24+
25+
def __call__(self, message):
26+
raise NotImplementedError('Please implement a call method in your filter.')
27+
28+
def __and__(self, other):
29+
return MergedFilter(self, and_filter=other)
30+
31+
def __or__(self, other):
32+
return MergedFilter(self, or_filter=other)
33+
34+
35+
class MergedFilter(BaseFilter):
36+
"""Represents a filter consisting of two other filters."""
37+
38+
def __init__(self, base_filter, and_filter=None, or_filter=None):
39+
self.base_filter = base_filter
40+
self.and_filter = and_filter
41+
self.or_filter = or_filter
42+
43+
def __call__(self, message):
44+
if self.and_filter:
45+
return self.base_filter(message) and self.and_filter(message)
46+
elif self.or_filter:
47+
return self.base_filter(message) or self.or_filter(message)
48+
49+
50+
class Filters(object):
51+
"""
52+
Convenient namespace (class) & methods for the filter funcs of the
53+
MessageHandler class.
54+
"""
55+
56+
class Text(BaseFilter):
57+
58+
def __call__(self, message):
59+
return bool(message.text and not message.text.startswith('/'))
60+
61+
text = Text()
62+
63+
class Command(BaseFilter):
64+
65+
def __call__(self, message):
66+
return bool(message.text and message.text.startswith('/'))
67+
68+
command = Command()
69+
70+
class Audio(BaseFilter):
71+
72+
def __call__(self, message):
73+
return bool(message.audio)
74+
75+
audio = Audio()
76+
77+
class Document(BaseFilter):
78+
79+
def __call__(self, message):
80+
return bool(message.document)
81+
82+
document = Document()
83+
84+
class Photo(BaseFilter):
85+
86+
def __call__(self, message):
87+
return bool(message.photo)
88+
89+
photo = Photo()
90+
91+
class Sticker(BaseFilter):
92+
93+
def __call__(self, message):
94+
return bool(message.sticker)
95+
96+
sticker = Sticker()
97+
98+
class Video(BaseFilter):
99+
100+
def __call__(self, message):
101+
return bool(message.video)
102+
103+
video = Video()
104+
105+
class Voice(BaseFilter):
106+
107+
def __call__(self, message):
108+
return bool(message.voice)
109+
110+
voice = Voice()
111+
112+
class Contact(BaseFilter):
113+
114+
def __call__(self, message):
115+
return bool(message.contact)
116+
117+
contact = Contact()
118+
119+
class Location(BaseFilter):
120+
121+
def __call__(self, message):
122+
return bool(message.location)
123+
124+
location = Location()
125+
126+
class Venue(BaseFilter):
127+
128+
def __call__(self, message):
129+
return bool(message.venue)
130+
131+
venue = Venue()
132+
133+
class StatusUpdate(BaseFilter):
134+
135+
def __call__(self, message):
136+
return bool(message.new_chat_member or message.left_chat_member
137+
or message.new_chat_title or message.new_chat_photo
138+
or message.delete_chat_photo or message.group_chat_created
139+
or message.supergroup_chat_created or message.channel_chat_created
140+
or message.migrate_to_chat_id or message.migrate_from_chat_id
141+
or message.pinned_message)
142+
143+
status_update = StatusUpdate()
144+
145+
class Forwarded(BaseFilter):
146+
147+
def __call__(self, message):
148+
return bool(message.forward_date)
149+
150+
forwarded = Forwarded()

telegram/ext/messagehandler.py

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,69 +23,6 @@
2323
from telegram.utils.deprecate import deprecate
2424

2525

26-
class Filters(object):
27-
"""
28-
Convenient namespace (class) & methods for the filter funcs of the
29-
MessageHandler class.
30-
"""
31-
32-
@staticmethod
33-
def text(message):
34-
return message.text and not message.text.startswith('/')
35-
36-
@staticmethod
37-
def command(message):
38-
return message.text and message.text.startswith('/')
39-
40-
@staticmethod
41-
def audio(message):
42-
return bool(message.audio)
43-
44-
@staticmethod
45-
def document(message):
46-
return bool(message.document)
47-
48-
@staticmethod
49-
def photo(message):
50-
return bool(message.photo)
51-
52-
@staticmethod
53-
def sticker(message):
54-
return bool(message.sticker)
55-
56-
@staticmethod
57-
def video(message):
58-
return bool(message.video)
59-
60-
@staticmethod
61-
def voice(message):
62-
return bool(message.voice)
63-
64-
@staticmethod
65-
def contact(message):
66-
return bool(message.contact)
67-
68-
@staticmethod
69-
def location(message):
70-
return bool(message.location)
71-
72-
@staticmethod
73-
def venue(message):
74-
return bool(message.venue)
75-
76-
@staticmethod
77-
def status_update(message):
78-
return bool(message.new_chat_member or message.left_chat_member or message.new_chat_title
79-
or message.new_chat_photo or message.delete_chat_photo
80-
or message.group_chat_created or message.supergroup_chat_created
81-
or message.channel_chat_created or message.migrate_to_chat_id
82-
or message.migrate_from_chat_id or message.pinned_message)
83-
84-
@staticmethod
85-
def forwarded(message):
86-
return bool(message.forward_date)
87-
88-
8926
class MessageHandler(Handler):
9027
"""
9128
Handler class to handle telegram messages. Messages are Telegram Updates

tests/test_filters.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,30 @@ def test_filters_status_update(self):
150150
self.assertTrue(Filters.status_update(self.message))
151151
self.message.pinned_message = None
152152

153+
def test_and_filters(self):
154+
# For now just test with forwarded as that's the only one that makes sense
155+
# That'll change when we get a entities filter
156+
self.message.text = 'test'
157+
self.message.forward_date = True
158+
self.assertTrue((Filters.text & Filters.forwarded)(self.message))
159+
self.message.text = '/test'
160+
self.assertFalse((Filters.text & Filters.forwarded)(self.message))
161+
self.message.text = 'test'
162+
self.message.forward_date = None
163+
self.assertFalse((Filters.text & Filters.forwarded)(self.message))
164+
165+
def test_or_filters(self):
166+
# For now just test with forwarded as that's the only one that makes sense
167+
# That'll change when we get a entities filter
168+
self.message.text = 'test'
169+
self.assertTrue((Filters.text | Filters.status_update)(self.message))
170+
self.message.group_chat_created = True
171+
self.assertTrue((Filters.text | Filters.status_update)(self.message))
172+
self.message.text = None
173+
self.assertTrue((Filters.text | Filters.status_update)(self.message))
174+
self.message.group_chat_created = False
175+
self.assertFalse((Filters.text | Filters.status_update)(self.message))
176+
153177

154178
if __name__ == '__main__':
155179
unittest.main()

0 commit comments

Comments
 (0)