skip to Main Content

I need to check if a message sender is in a "special" list, to allow only special users to use some commands. I used IDFilter for it. But this "special" list may be changed, and I need these changes to be taken into account. But I found that IDFilter doesn’t consider them.

File test.py:

from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher.filters.builtin import IDFilter

import config

bot = Bot(config.TOKEN)
dp = Dispatcher(bot)

@dp.message_handler(commands=['start'])
async def start_cmd(message: types.Message):
    await message.reply(text='Hello!')

@dp.message_handler(commands=['secret'], user_id=config.mylist) # user_id - the simplest way to use IDFilter. Another way is like IDFilter(config.mylist), but put it before commands argument.
async def secret(message: types.Message):
    await message.answer(text='Hello, this is a secret message only for ids in mylist!!!')

@dp.message_handler(commands=['clear'])
async def clear(message: types.Message):
    config.mylist = []
    await message.reply(f'Mylist is empty now: {", ".join([str(id) for id in config.mylist])}')
    
@dp.message_handler(commands=['add_me'])
async def add_me(message: types.Message):
    config.mylist.append(message.from_user.id)
    await message.answer(f'Your id was added to mylist: {", ".join([str(id) for id in config.mylist])}')

if __name__ == '__main__':
    executor.start_polling(dp)

File config.py:

TOKEN = "<token here>"

mylist = [<my telegram id here>]

Here is the resulting bot:

enter image description here

So even though config.mylist was empty, IDFilter’s verification was successful.

But if I use lambda message: message.from_user.id in config.mylist, the changes of the list are considered.

Why is this happening?

2

Answers


  1. Chosen as BEST ANSWER

    If you will look at aiogram.dispatcher.filters.builtin.py file, and at IDFilter class inside it, you can see that IDFilter on init extract ids from your arguments (config.mylist in my case) and creates copies of them, and then uses the copies for checking.

    So even if config.mylist is changed, the IDFilter doesn't consider this changing, because it has its own ids that were copied from the list, and they don't change.

    So I will use the checking by lambda like lambda message: message.from_user.id in config.mylist.


  2. You might want to implement a decorator for that purpose. For example:

    from functools import wraps
    
    from aiogram import Bot, Dispatcher, executor, types
    
    
    admins = [1, 2, 3]  # Telegram ids of admins (from your config)
    
    bot = Bot(...)
    dp = Dispatcher(bot)
    
    
    def admins_only_handler(handler):
        @wraps(handler)
        async def authorized_handler(message: types.Message, *args, **kwargs):
            if message.from_user.id in admins:
                return await handler(message, *args, **kwargs)
            else:
                await message.reply('You are not authorized to perform this action')
    
        return authorized_handler
    
    # A handler everyone is allowed to use:
    @dp.message_handler(commands=['start'])
    async def start(message: types.Message):
        await message.reply(text='Hello!')
    
    # A handler only for admins:
    @dp.message_handler(commands=['secret'])
    @admins_only_handler
    async def secret(message: types.Message):
        await message.reply(text='Looks like you are an admin!')
    
    
    if __name__ == '__main__':
        executor.start_polling(dp)
    

    Note: the above solution is slightly different from yours conceptually: in the solution with the decorator unauthorized users are restricted from performing an action of the handler but they get to know that such an option exists for admins. With your solution, even the list of protected commands is hidden from your users (if a non-admin user attempts to use an admin command, the corresponding handler is not matched with your lambda, so the bot treats it as if such a command does not exist at all). Your method is generally more secure, but often it is not necessary to hide the list of commands available to the admins.

    More on decorators: https://pythonbasics.org/decorators/

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search