diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index 5dfc8cf9..d1834b2a 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -11,8 +11,7 @@ from aiohttp.helpers import sentinel from aiogram.utils.deprecated import renamed_argument from .filters import Command, ContentTypeFilter, ExceptionsFilter, FiltersFactory, HashTag, Regexp, \ RegexpCommandsFilter, StateFilter, Text, IDFilter, AdminFilter, IsReplyFilter, ForwardedMessageFilter, \ - AbstractFilter -from .filters.builtin import IsSenderContact + IsSenderContact, ChatTypeFilter, AbstractFilter from .handler import Handler from .middlewares import MiddlewareManager from .storage import BaseStorage, DELTA, DisabledStorage, EXCEEDED_COUNT, FSMContext, \ @@ -167,6 +166,13 @@ class Dispatcher(DataMixin, ContextInstanceMixin): self.channel_post_handlers, self.edited_channel_post_handlers ]) + filters_factory.bind(ChatTypeFilter, event_handlers=[ + self.message_handlers, + self.edited_message_handlers, + self.channel_post_handlers, + self.edited_channel_post_handlers, + self.callback_query_handlers, + ]) def __del__(self): self.stop_polling() diff --git a/aiogram/dispatcher/filters/__init__.py b/aiogram/dispatcher/filters/__init__.py index edd1959a..5f839662 100644 --- a/aiogram/dispatcher/filters/__init__.py +++ b/aiogram/dispatcher/filters/__init__.py @@ -1,6 +1,7 @@ from .builtin import Command, CommandHelp, CommandPrivacy, CommandSettings, CommandStart, ContentTypeFilter, \ ExceptionsFilter, HashTag, Regexp, RegexpCommandsFilter, StateFilter, \ - Text, IDFilter, AdminFilter, IsReplyFilter, IsSenderContact, ForwardedMessageFilter + Text, IDFilter, AdminFilter, IsReplyFilter, IsSenderContact, ForwardedMessageFilter, \ + ChatTypeFilter from .factory import FiltersFactory from .filters import AbstractFilter, BoundFilter, Filter, FilterNotPassed, FilterRecord, execute_filter, \ check_filters, get_filter_spec, get_filters_spec @@ -33,4 +34,5 @@ __all__ = [ 'execute_filter', 'check_filters', 'ForwardedMessageFilter', + 'ChatTypeFilter', ] diff --git a/aiogram/dispatcher/filters/builtin.py b/aiogram/dispatcher/filters/builtin.py index c59d9b0d..cb1c4815 100644 --- a/aiogram/dispatcher/filters/builtin.py +++ b/aiogram/dispatcher/filters/builtin.py @@ -1,6 +1,7 @@ import inspect import re import typing +import warnings from contextvars import ContextVar from dataclasses import dataclass, field from typing import Any, Dict, Iterable, Optional, Union @@ -9,8 +10,7 @@ from babel.support import LazyProxy from aiogram import types from aiogram.dispatcher.filters.filters import BoundFilter, Filter -from aiogram.types import CallbackQuery, Message, InlineQuery, Poll, ChatType - +from aiogram.types import CallbackQuery, ChatType, InlineQuery, Message, Poll ChatIDArgumentType = typing.Union[typing.Iterable[typing.Union[int, str]], str, int] @@ -691,3 +691,24 @@ class ForwardedMessageFilter(BoundFilter): async def check(self, message: Message): return bool(getattr(message, "forward_date")) is self.is_forwarded + + +class ChatTypeFilter(BoundFilter): + key = 'chat_type' + + def __init__(self, chat_type: typing.Container[ChatType]): + if isinstance(chat_type, str): + chat_type = {chat_type} + + self.chat_type: typing.Set[str] = set(chat_type) + + async def check(self, obj: Union[Message, CallbackQuery]): + if isinstance(obj, Message): + obj = obj.chat + elif isinstance(obj, CallbackQuery): + obj = obj.message.chat + else: + warnings.warn("ChatTypeFilter doesn't support %s as input", type(obj)) + return False + + return obj.type in self.chat_type diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index 4a7287d8..28cc5ed0 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -10,6 +10,7 @@ from .chat_member import ChatMember from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto from .input_file import InputFile +from ..utils.deprecated import deprecated class Chat(base.TelegramObject): @@ -512,6 +513,7 @@ class ChatType(helper.Helper): return obj.type in chat_types @classmethod + @deprecated("This filter was moved to ChatTypeFilter, and will be removed in aiogram v3.0") def is_private(cls, obj) -> bool: """ Check chat is private @@ -522,6 +524,7 @@ class ChatType(helper.Helper): return cls._check(obj, [cls.PRIVATE]) @classmethod + @deprecated("This filter was moved to ChatTypeFilter, and will be removed in aiogram v3.0") def is_group(cls, obj) -> bool: """ Check chat is group @@ -532,6 +535,7 @@ class ChatType(helper.Helper): return cls._check(obj, [cls.GROUP]) @classmethod + @deprecated("This filter was moved to ChatTypeFilter, and will be removed in aiogram v3.0") def is_super_group(cls, obj) -> bool: """ Check chat is super-group @@ -542,6 +546,7 @@ class ChatType(helper.Helper): return cls._check(obj, [cls.SUPER_GROUP]) @classmethod + @deprecated("This filter was moved to ChatTypeFilter, and will be removed in aiogram v3.0") def is_group_or_super_group(cls, obj) -> bool: """ Check chat is group or super-group @@ -552,6 +557,7 @@ class ChatType(helper.Helper): return cls._check(obj, [cls.GROUP, cls.SUPER_GROUP]) @classmethod + @deprecated("This filter was moved to ChatTypeFilter, and will be removed in aiogram v3.0") def is_channel(cls, obj) -> bool: """ Check chat is channel diff --git a/docs/source/dispatcher/filters.rst b/docs/source/dispatcher/filters.rst index 3681dfcb..f53a4c95 100644 --- a/docs/source/dispatcher/filters.rst +++ b/docs/source/dispatcher/filters.rst @@ -149,6 +149,14 @@ ForwardedMessageFilter :show-inheritance: +ChatTypeFilter +------------- + +.. autoclass:: aiogram.dispatcher.filters.filters.ChatTypeFilter + :members: + :show-inheritance: + + Making own filters (Custom filters) =================================== diff --git a/examples/chat_type_filter.py b/examples/chat_type_filter.py new file mode 100644 index 00000000..08bb1858 --- /dev/null +++ b/examples/chat_type_filter.py @@ -0,0 +1,42 @@ +""" +This is an example with usage of ChatTypeFilter +It filters incoming object based on type of its chat type +""" + +import logging + +from aiogram import Bot, Dispatcher, executor, types +from aiogram.dispatcher.handler import SkipHandler +from aiogram.types import ChatType + +API_TOKEN = 'BOT TOKEN HERE' + +# Configure logging +logging.basicConfig(level=logging.INFO) + +# Initialize bot and dispatcher +bot = Bot(token=API_TOKEN) +dp = Dispatcher(bot) + + +@dp.message_handler(chat_type=[ChatType.PRIVATE, ChatType.CHANNEL]) +async def send_welcome(message: types.Message): + """ + This handler will be called when user sends `/start` or `/help` command + """ + await message.reply("Hi!\nI'm hearing your messages in private chats and channels") + + # propagate message to the next handler + raise SkipHandler + + +@dp.message_handler(chat_type=ChatType.PRIVATE) +async def send_welcome(message: types.Message): + """ + This handler will be called when user sends `/start` or `/help` command + """ + await message.reply("Hi!\nI'm hearing your messages only in private chats") + + +if __name__ == '__main__': + executor.start_polling(dp, skip_updates=True)