Aiogram内置的IDFilter无法与更新值一起使用。

huangapple go评论100阅读模式
英文:

Aiogram builtin IDFilter doesn't work with updating values

问题

我需要检查消息发送者是否在一个"特殊"列表中,以允许只有特殊用户使用一些命令。我使用了IDFilter来实现这个功能。但是这个"特殊"列表可能会发生变化,我需要让这些变化生效。但我发现IDFilter并没有考虑这些变化。

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>]

这就是生成的机器人:

Aiogram内置的IDFilter无法与更新值一起使用。

所以,即使config.mylist为空,IDFilter的验证也成功。

但是如果我使用lambda message: message.from_user.id in config.mylist,那么列表的更改会被考虑在内。

为什么会发生这种情况?

英文:

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=[&#39;start&#39;])
async def start_cmd(message: types.Message):
    await message.reply(text=&#39;Hello!&#39;)

@dp.message_handler(commands=[&#39;secret&#39;], 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=&#39;Hello, this is a secret message only for ids in mylist!!!&#39;)

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

if __name__ == &#39;__main__&#39;:
    executor.start_polling(dp)

File config.py:

TOKEN = &quot;&lt;token here&gt;&quot;

mylist = [&lt;my telegram id here&gt;]

Here is the resulting bot:

Aiogram内置的IDFilter无法与更新值一起使用。

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?

答案1

得分: 1

你可能想要为此目的实现一个装饰器。例如:

from functools import wraps

from aiogram import Bot, Dispatcher, executor, types

admins = [1, 2, 3]  # Telegram 管理员的用户 ID(从您的配置文件中获取)

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('您无权执行此操作')

    return authorized_handler

# 所有人都可以使用的处理程序:
@dp.message_handler(commands=['start'])
async def start(message: types.Message):
    await message.reply(text='你好!')

# 仅供管理员使用的处理程序:
@dp.message_handler(commands=['secret'])
@admins_only_handler
async def secret(message: types.Message):
    await message.reply(text='看起来你是一个管理员!')

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

注意:上述解决方案在概念上与您的略有不同:在使用装饰器的解决方案中,未经授权的用户被限制执行某个操作,但他们会知道这种操作/命令是为管理员开放的。而使用您的解决方案,即使是受限的命令列表也对用户隐藏(如果非管理员用户尝试使用管理员命令,您的 lambda 不会匹配相应处理程序,因此机器人会将其视为根本不存在)。您的方法通常更安全,但通常不必隐藏管理员可用命令的列表。

更多关于装饰器的信息:https://pythonbasics.org/decorators/

英文:

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(&#39;You are not authorized to perform this action&#39;)

    return authorized_handler

# A handler everyone is allowed to use:
@dp.message_handler(commands=[&#39;start&#39;])
async def start(message: types.Message):
    await message.reply(text=&#39;Hello!&#39;)

# A handler only for admins:
@dp.message_handler(commands=[&#39;secret&#39;])
@admins_only_handler
async def secret(message: types.Message):
    await message.reply(text=&#39;Looks like you are an admin!&#39;)


if __name__ == &#39;__main__&#39;:
    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 but they get to know that this sort of action/command 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 by 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/

答案2

得分: 0

如果你查看 aiogram.dispatcher.filters.builtin.py 文件,并查看其中的 IDFilter 类,你会发现在初始化时,IDFilter会从你的参数中提取id(在我的情况下是config.mylist)并创建它们的副本,然后使用这些副本进行检查。

因此,即使config.mylist发生了变化,IDFilter也不会考虑这种变化,因为它有自己从列表中复制的id,它们不会改变。

所以我会使用lambda进行检查,就像这样:lambda message: message.from_user.id in config.mylist

英文:

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 these 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.

huangapple
  • 本文由 发表于 2023年8月5日 05:49:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76839259.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定