From 7c113f5c75db162caa871f715074f03afc76c31c Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Sun, 24 Apr 2022 17:17:35 +0530 Subject: [PATCH] Expand and Adjust `filters.{Document, Sticker}` (#2922) Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> --- telegram/ext/filters.py | 98 ++++++++++++++++++++++++++++++++--------- tests/test_filters.py | 32 +++++++++++--- 2 files changed, 104 insertions(+), 26 deletions(-) diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index b20d6a0a9..2e47e50c1 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -52,7 +52,6 @@ __all__ = ( 'Chat', 'ChatType', 'Command', - 'DOCUMENT', 'Dice', 'Document', 'Entity', @@ -70,7 +69,7 @@ __all__ = ( 'POLL', 'REPLY', 'Regex', - 'STICKER', + 'Sticker', 'SUCCESSFUL_PAYMENT', 'SenderChat', 'StatusUpdate', @@ -819,7 +818,7 @@ class ChatType: # A convenience namespace for Chat types. Use these filters like: ``filters.ChatType.CHANNEL`` or ``filters.ChatType.SUPERGROUP`` etc. - Note: + Caution: ``filters.ChatType`` itself is *not* a filter, but just a convenience namespace. """ @@ -1084,18 +1083,30 @@ class Dice(_Dice): """Dice messages with the emoji 🎰. Matches any dice value.""" -class Document(MessageFilter): +class Document: """ Subset for messages containing a document/file. Examples: Use these filters like: ``filters.Document.MP3``, - ``filters.Document.MimeType("text/plain")`` etc. Or use just ``filters.DOCUMENT`` for all - document messages. + ``filters.Document.MimeType("text/plain")`` etc. Or just use ``filters.Document.ALL`` for + all document messages. + + Caution: + ``filters.Document`` itself is *not* a filter, but just a convenience namespace. """ __slots__ = () + class _All(MessageFilter): + __slots__ = () + + def filter(self, message: Message) -> bool: + return bool(message.document) + + ALL = _All(name="filters.Document.ALL") + """Messages that contain a :attr:`telegram.Message.document`.""" + class Category(MessageFilter): """Filters documents by their category in the mime-type attribute. @@ -1252,13 +1263,6 @@ class Document(MessageFilter): ZIP = MimeType(mimetypes.types_map['.zip']) """Use as ``filters.Document.ZIP``.""" - def filter(self, message: Message) -> bool: - return bool(message.document) - - -DOCUMENT = Document(name="filters.DOCUMENT") -"""Shortcut for :class:`telegram.ext.filters.Document()`.""" - class Entity(MessageFilter): """ @@ -1665,7 +1669,7 @@ class StatusUpdate: Use these filters like: ``filters.StatusUpdate.NEW_CHAT_MEMBERS`` etc. Or use just ``filters.StatusUpdate.ALL`` for all status update messages. - Note: + Caution: ``filters.StatusUpdate`` itself is *not* a filter, but just a convenience namespace. """ @@ -1860,15 +1864,69 @@ class StatusUpdate: """ -class _Sticker(MessageFilter): +class Sticker: + """Filters messages which contain a sticker. + + Examples: + Use this filter like: ``filters.Sticker.VIDEO``. Or, just use ``filters.Sticker.ALL`` for + any type of sticker. + + Caution: + ``filters.Sticker`` itself is *not* a filter, but just a convenience namespace. + """ + __slots__ = () - def filter(self, message: Message) -> bool: - return bool(message.sticker) + class _All(MessageFilter): + __slots__ = () + def filter(self, message: Message) -> bool: + return bool(message.sticker) -STICKER = _Sticker(name="filters.STICKER") -"""Messages that contain :attr:`telegram.Message.sticker`.""" + ALL = _All(name="filters.Sticker.ALL") + """Messages that contain :attr:`telegram.Message.sticker`.""" + + class _Animated(MessageFilter): + __slots__ = () + + def filter(self, message: Message) -> bool: + return bool(message.sticker) and bool(message.sticker.is_animated) # type: ignore + + ANIMATED = _Animated(name="filters.Sticker.ANIMATED") + """Messages that contain :attr:`telegram.Message.sticker` and + :attr:`is animated `. + + .. versionadded:: 14.0 + """ + + class _Static(MessageFilter): + __slots__ = () + + def filter(self, message: Message) -> bool: + return bool(message.sticker) and ( + not bool(message.sticker.is_animated) # type: ignore[union-attr] + and not bool(message.sticker.is_video) # type: ignore[union-attr] + ) + + STATIC = _Static(name="filters.Sticker.STATIC") + """Messages that contain :attr:`telegram.Message.sticker` and is a static sticker, i.e. does + not contain :attr:`telegram.Sticker.is_animated` or :attr:`telegram.Sticker.is_video`. + + .. versionadded:: 14.0 + """ + + class _Video(MessageFilter): + __slots__ = () + + def filter(self, message: Message) -> bool: + return bool(message.sticker) and bool(message.sticker.is_video) # type: ignore + + VIDEO = _Video(name="filters.Sticker.VIDEO") + """Messages that contain :attr:`telegram.Message.sticker` and is a + :attr:`video sticker `. + + .. versionadded:: 14.0 + """ class _SuccessfulPayment(MessageFilter): @@ -1940,7 +1998,7 @@ class UpdateType: Use these filters like: ``filters.UpdateType.MESSAGE`` or ``filters.UpdateType.CHANNEL_POSTS`` etc. - Note: + Caution: ``filters.UpdateType`` itself is *not* a filter, but just a convenience namespace. """ diff --git a/tests/test_filters.py b/tests/test_filters.py index 853460730..eb99ece17 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -20,7 +20,17 @@ import datetime import pytest -from telegram import Message, User, Chat, MessageEntity, Document, Update, Dice, CallbackQuery +from telegram import ( + Message, + User, + Chat, + MessageEntity, + Document, + Update, + Dice, + CallbackQuery, + Sticker, +) from telegram.ext import filters import inspect import re @@ -583,9 +593,9 @@ class TestFilters: assert filters.AUDIO.check_update(update) def test_filters_document(self, update): - assert not filters.DOCUMENT.check_update(update) + assert not filters.Document.ALL.check_update(update) update.message.document = 'test' - assert filters.DOCUMENT.check_update(update) + assert filters.Document.ALL.check_update(update) def test_filters_document_type(self, update): update.message.document = Document( @@ -816,9 +826,19 @@ class TestFilters: assert filters.PHOTO.check_update(update) def test_filters_sticker(self, update): - assert not filters.STICKER.check_update(update) - update.message.sticker = 'test' - assert filters.STICKER.check_update(update) + assert not filters.Sticker.ALL.check_update(update) + update.message.sticker = Sticker('1', 'uniq', 1, 2, False, False) + assert filters.Sticker.ALL.check_update(update) + assert filters.Sticker.STATIC.check_update(update) + update.message.sticker.is_animated = True + assert filters.Sticker.ANIMATED.check_update(update) + assert not filters.Sticker.VIDEO.check_update(update) + assert not filters.Sticker.STATIC.check_update(update) + update.message.sticker.is_animated = False + update.message.sticker.is_video = True + assert not filters.Sticker.ANIMATED.check_update(update) + assert not filters.Sticker.STATIC.check_update(update) + assert filters.Sticker.VIDEO.check_update(update) def test_filters_video(self, update): assert not filters.VIDEO.check_update(update)