discord.py 2.0自定义装饰器导致命令无法注册求助
问题:discord.py 2.0自定义装饰器导致命令无法被机器人识别
我基于discord.py 2.0开发机器人,为了避免在每个新命令中重复编写用户注册检查逻辑,把该逻辑封装成了自定义装饰器command_author_check。但给命令添加这个装饰器后,命令完全无法被机器人识别。
命令类代码
from discord.ext import commands from main import db from scr.phrases_func import dict_to_str from scr.cfg import phrases from scr.command_author_check import command_author_check class GeneralCommands(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot @commands.command() async def info(self, ctx): auth = ctx.message.author if db.check_user(auth): if db.get_user(auth).permissions_level >= 20: await auth.send(ctx.message.author.mention + phrases.master_greeting + dict_to_str(phrases.master_commands)) else: await auth.send(ctx.message.author.mention + phrases.slaves_greeting + dict_to_str(phrases.slaves_commands)) else: await auth.send(ctx.message.author.mention + phrases.general_greeting + dict_to_str(phrases.slaves_commands)) @commands.command() @command_author_check() async def test(self, ctx): await ctx.send("ПОЛУЧИЛОСЬ") async def setup(bot: commands.Bot): await bot.add_cog(GeneralCommands(bot))
装饰器代码
from main import db from scr.cfg import phrases from scr import phrases_func def command_author_check(permissions_level: int = 1): print("command_author_check") def decorator(func): print("decorator") async def wrapper(ctx, *args, **kwargs): print("wrapper") auth = ctx.message.author if db.check_user(auth): if db.get_user(auth).permissions_level >= permissions_level: return await func(ctx, *args, **kwargs) else: await auth.send(ctx.message.author.mention + phrases.slaves_greeting + phrases_func.dict_to_str(phrases.slaves_commands)) else: await auth.send(ctx.message.author.mention + phrases.general_greeting + phrases_func.dict_to_str(phrases.slaves_commands)) return wrapper return decorator
解决方法
问题出在两个核心点:装饰器顺序错误,以及自定义装饰器未保留原函数元数据。
1. 调整装饰器顺序
discord.py的@commands.command()装饰器必须最后应用(即放在最靠近函数定义的位置)。它的作用是将函数注册为机器人命令,如果先使用它再用自定义装饰器,机器人只会注册未被包装的原函数,包装后的逻辑不会被关联到命令上,自然无法被识别。
修正后的命令装饰顺序:
@command_author_check() @commands.command() async def test(self, ctx): await ctx.send("ПОЛУЧИЛОСЬ")
2. 保留原函数元数据
自定义装饰器的wrapper函数会覆盖原函数的名称等元数据,而discord.py依赖这些元数据确定命令名称。需要用functools.wraps装饰wrapper,把原函数的元数据复制过来,确保机器人能正确识别命令名。
修正后的装饰器代码:
from functools import wraps from main import db from scr.cfg import phrases from scr import phrases_func def command_author_check(permissions_level: int = 1): print("command_author_check") def decorator(func): print("decorator") @wraps(func) # 保留原函数元数据,确保命令名正确识别 async def wrapper(ctx, *args, **kwargs): print("wrapper") auth = ctx.message.author if db.check_user(auth): if db.get_user(auth).permissions_level >= permissions_level: return await func(ctx, *args, **kwargs) else: await auth.send(ctx.message.author.mention + phrases.slaves_greeting + phrases_func.dict_to_str(phrases.slaves_commands)) else: await auth.send(ctx.message.author.mention + phrases.general_greeting + phrases_func.dict_to_str(phrases.slaves_commands)) return wrapper return decorator
完成以上两处修改后,test命令就能被机器人正常识别并执行了。
内容的提问来源于stack exchange,提问作者FARAON
相关产品推荐
相关产品推荐

