diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index 39eb7810..0da5f621 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -9,7 +9,7 @@ import aiohttp from aiohttp.helpers import sentinel from .filters import Command, ContentTypeFilter, ExceptionsFilter, FiltersFactory, HashTag, Regexp, \ - RegexpCommandsFilter, StateFilter, Text + RegexpCommandsFilter, StateFilter, Text, IdFilter from .handler import Handler from .middlewares import MiddlewareManager from .storage import BaseStorage, DELTA, DisabledStorage, EXCEEDED_COUNT, FSMContext, \ @@ -114,6 +114,11 @@ class Dispatcher(DataMixin, ContextInstanceMixin): filters_factory.bind(ExceptionsFilter, event_handlers=[ self.errors_handlers ]) + filters_factory.bind(IdFilter, event_handlers=[ + self.message_handlers, self.edited_message_handlers, + self.channel_post_handlers, self.edited_channel_post_handlers, + self.callback_query_handlers, self.inline_query_handlers + ]) def __del__(self): self.stop_polling() diff --git a/aiogram/dispatcher/filters/__init__.py b/aiogram/dispatcher/filters/__init__.py index 2ae959cf..eb4a5a52 100644 --- a/aiogram/dispatcher/filters/__init__.py +++ b/aiogram/dispatcher/filters/__init__.py @@ -1,5 +1,5 @@ from .builtin import Command, CommandHelp, CommandPrivacy, CommandSettings, CommandStart, ContentTypeFilter, \ - ExceptionsFilter, HashTag, Regexp, RegexpCommandsFilter, StateFilter, Text + ExceptionsFilter, HashTag, Regexp, RegexpCommandsFilter, StateFilter, Text, IdFilter from .factory import FiltersFactory from .filters import AbstractFilter, BoundFilter, Filter, FilterNotPassed, FilterRecord, execute_filter, \ check_filters, get_filter_spec, get_filters_spec @@ -23,6 +23,7 @@ __all__ = [ 'Regexp', 'StateFilter', 'Text', + 'IdFilter', 'get_filter_spec', 'get_filters_spec', 'execute_filter', diff --git a/aiogram/dispatcher/filters/builtin.py b/aiogram/dispatcher/filters/builtin.py index 7c8caaa8..9826c63b 100644 --- a/aiogram/dispatcher/filters/builtin.py +++ b/aiogram/dispatcher/filters/builtin.py @@ -503,3 +503,66 @@ class ExceptionsFilter(BoundFilter): return True except: return False + + +class IdFilter(Filter): + + def __init__(self, + user_id: Optional[Union[Iterable[Union[int, str]], str, int]] = None, + chat_id: Optional[Union[Iterable[Union[int, str]], str, int]] = None, + ): + """ + :param user_id: + :param chat_id: + """ + if user_id is None and chat_id is None: + raise ValueError("Both user_id and chat_id can't be None") + + self.user_id = None + self.chat_id = None + if user_id: + if isinstance(user_id, Iterable): + self.user_id = list(map(int, user_id)) + else: + self.user_id = [int(user_id), ] + if chat_id: + if isinstance(chat_id, Iterable): + self.chat_id = list(map(int, chat_id)) + else: + self.chat_id = [int(chat_id), ] + + @classmethod + def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]: + result = {} + if 'user_id' in full_config: + result['user_id'] = full_config.pop('user_id') + + if 'chat_id' in full_config: + result['chat_id'] = full_config.pop('chat_id') + + return result + + async def check(self, obj: Union[Message, CallbackQuery, InlineQuery]): + if isinstance(obj, Message): + user_id = obj.from_user.id + chat_id = obj.chat.id + elif isinstance(obj, CallbackQuery): + user_id = obj.from_user.id + chat_id = None + if obj.message is not None: + # if the button was sent with message + chat_id = obj.message.chat.id + elif isinstance(obj, InlineQuery): + user_id = obj.from_user.id + chat_id = None + else: + return False + + if self.user_id and self.chat_id: + return user_id in self.user_id and chat_id in self.chat_id + elif self.user_id: + return user_id in self.user_id + elif self.chat_id: + return chat_id in self.chat_id + + return False diff --git a/docs/source/dispatcher/filters.rst b/docs/source/dispatcher/filters.rst index d103ac36..af03e163 100644 --- a/docs/source/dispatcher/filters.rst +++ b/docs/source/dispatcher/filters.rst @@ -111,6 +111,14 @@ ExceptionsFilter :show-inheritance: +IdFilter +---------------- + +.. autoclass:: aiogram.dispatcher.filters.builtin.IdFilter + :members: + :show-inheritance: + + Making own filters (Custom filters) =================================== diff --git a/examples/id_filter_example.py b/examples/id_filter_example.py new file mode 100644 index 00000000..64dc3b3f --- /dev/null +++ b/examples/id_filter_example.py @@ -0,0 +1,38 @@ +from aiogram import Bot, Dispatcher, executor, types +from aiogram.dispatcher.handler import SkipHandler + +API_TOKEN = 'API_TOKE_HERE' +bot = Bot(token=API_TOKEN) +dp = Dispatcher(bot) + +user_id_to_test = None # todo: Set id here +chat_id_to_test = user_id_to_test + + +@dp.message_handler(user_id=user_id_to_test) +async def handler1(msg: types.Message): + await bot.send_message(msg.chat.id, + "Hello, checking with user_id=") + raise SkipHandler + + +@dp.message_handler(chat_id=chat_id_to_test) +async def handler2(msg: types.Message): + await bot.send_message(msg.chat.id, + "Hello, checking with chat_id=") + raise SkipHandler + + +@dp.message_handler(user_id=user_id_to_test, chat_id=chat_id_to_test) +async def handler3(msg: types.Message): + await bot.send_message(msg.chat.id, + "Hello from user= & chat_id=") + + +@dp.message_handler(user_id=[user_id_to_test, 123]) # todo: add second id here +async def handler4(msg: types.Message): + print("Checked user_id with list!") + + +if __name__ == '__main__': + executor.start_polling(dp)