diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index 24e13801..95a6d8d9 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, IdFilter + RegexpCommandsFilter, StateFilter, Text, IDFilter, AdminFilter from .handler import Handler from .middlewares import MiddlewareManager from .storage import BaseStorage, DELTA, DisabledStorage, EXCEEDED_COUNT, FSMContext, \ @@ -128,7 +128,15 @@ class Dispatcher(DataMixin, ContextInstanceMixin): filters_factory.bind(ExceptionsFilter, event_handlers=[ self.errors_handlers, ]) - filters_factory.bind(IdFilter, event_handlers=[ + filters_factory.bind(AdminFilter, 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, + ]) + filters_factory.bind(IDFilter, event_handlers=[ self.message_handlers, self.edited_message_handlers, self.channel_post_handlers, diff --git a/aiogram/dispatcher/filters/__init__.py b/aiogram/dispatcher/filters/__init__.py index 374bcf2a..277db03a 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, IdFilter + ExceptionsFilter, HashTag, Regexp, RegexpCommandsFilter, StateFilter, Text, IDFilter, AdminFilter from .factory import FiltersFactory from .filters import AbstractFilter, BoundFilter, Filter, FilterNotPassed, FilterRecord, execute_filter, \ check_filters, get_filter_spec, get_filters_spec @@ -23,7 +23,8 @@ __all__ = [ 'Regexp', 'StateFilter', 'Text', - 'IdFilter', + 'IDFilter', + 'AdminFilter', 'get_filter_spec', 'get_filters_spec', 'execute_filter', diff --git a/aiogram/dispatcher/filters/builtin.py b/aiogram/dispatcher/filters/builtin.py index 5b50c23c..e15b98de 100644 --- a/aiogram/dispatcher/filters/builtin.py +++ b/aiogram/dispatcher/filters/builtin.py @@ -9,7 +9,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 +from aiogram.types import CallbackQuery, Message, InlineQuery, Poll, ChatType class Command(Filter): @@ -510,7 +510,7 @@ class ExceptionsFilter(BoundFilter): return False -class IdFilter(Filter): +class IDFilter(Filter): def __init__(self, user_id: Optional[Union[Iterable[Union[int, str]], str, int]] = None, @@ -571,3 +571,57 @@ class IdFilter(Filter): return chat_id in self.chat_id return False + + +class AdminFilter(Filter): + """ + Checks if user is admin in a chat. + If is_chat_admin is not set, the filter will check in the current chat (correct only for messages). + is_chat_admin is required for InlineQuery. + """ + + def __init__(self, is_chat_admin: Optional[Union[Iterable[Union[int, str]], str, int, bool]] = None): + self._check_current = False + self._chat_ids = None + + if is_chat_admin is False: + raise ValueError("is_chat_admin cannot be False") + + if is_chat_admin: + if isinstance(is_chat_admin, bool): + self._check_current = is_chat_admin + if isinstance(is_chat_admin, Iterable): + self._chat_ids = list(is_chat_admin) + else: + self._chat_ids = [is_chat_admin] + else: + self._check_current = True + + @classmethod + def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]: + result = {} + + if "is_chat_admin" in full_config: + result["is_chat_admin"] = full_config.pop("is_chat_admin") + + return result + + async def check(self, obj: Union[Message, CallbackQuery, InlineQuery]) -> bool: + user_id = obj.from_user.id + + if self._check_current: + if isinstance(obj, Message): + message = obj + elif isinstance(obj, CallbackQuery) and obj.message: + message = obj.message + else: + return False + if ChatType.is_private(message): # there is no admin in private chats + return False + chat_ids = [message.chat.id] + else: + chat_ids = self._chat_ids + + admins = [member.user.id for chat_id in chat_ids for member in await obj.bot.get_chat_administrators(chat_id)] + + return user_id in admins diff --git a/docs/source/dispatcher/filters.rst b/docs/source/dispatcher/filters.rst index af03e163..059a4f06 100644 --- a/docs/source/dispatcher/filters.rst +++ b/docs/source/dispatcher/filters.rst @@ -111,10 +111,18 @@ ExceptionsFilter :show-inheritance: -IdFilter +IDFilter ---------------- -.. autoclass:: aiogram.dispatcher.filters.builtin.IdFilter +.. autoclass:: aiogram.dispatcher.filters.builtin.IDFilter + :members: + :show-inheritance: + + +AdminFilter +---------------- + +.. autoclass:: aiogram.dispatcher.filters.builtin.AdminFilter :members: :show-inheritance: diff --git a/examples/admin_filter_example.py b/examples/admin_filter_example.py new file mode 100644 index 00000000..ec8746bb --- /dev/null +++ b/examples/admin_filter_example.py @@ -0,0 +1,33 @@ +import logging + +from aiogram import Bot, Dispatcher, types, executor + +API_TOKEN = 'API_TOKEN_HERE' + + +logging.basicConfig(level=logging.DEBUG) + +bot = Bot(token=API_TOKEN) +dp = Dispatcher(bot=bot) + + +# checks specified chat +@dp.message_handler(is_chat_admin=-1001241113577) +async def handle_specified(msg: types.Message): + await msg.answer("You are an admin of the specified chat!") + + +# checks multiple chats +@dp.message_handler(is_chat_admin=[-1001241113577, -320463906]) +async def handle_multiple(msg: types.Message): + await msg.answer("You are an admin of multiple chats!") + + +# checks current chat +@dp.message_handler(is_chat_admin=True) +async def handler3(msg: types.Message): + await msg.answer("You are an admin of the current chat!") + + +if __name__ == '__main__': + executor.start_polling(dp)