如何扩展 client.command 的功能?

How do you extend the functionality of client.command?

我正在尝试添加 client.commandclientdiscord.ext.commands.Bot 实例)的错误、args、kwargs 和函数内容的处理。 基本上,我试图让我的所有命令都具有某些共同的自定义行为。

我最初的想法是制作一个 returns 装饰器的函数,用 client.command.

装饰它的包装器

但是我遇到的最大的问题是client.command(...)返回的装饰器的参数处理完全依赖于被装饰函数的参数和注解的排列方式,也就是说一个wrapper其参数如下

async def wrapper(*args, **kwargs):

将接收原始参数。那将意味着我必须自己处理包装器内的所有内容,这首先破坏了使用 discord.ext.commands 的全部意义。

阅读 PEP 3107,我试图想出一个解决方法。下面是代码草图,删掉了与问题无关的部分:

from discord.ext import commands as c
import discord as d
client = c.Bot(command_prefix = "insert_prefix_here$")



def command(*args, **kwargs):
    def decorator(func):
        command_name = kwargs.setdefault("name", func.__name__)

        async def wrapper(ctx, *args, **kwargs):
            # do stuff with func and command_name

        # ...eh? this down here is the dirtiest thing i've ever seen
        wrapper.__defaults__ = func.__defaults__
        wrapper.__annotations__ = func.__annotations__

        wrapper = client.command(*args, **kwargs)(wrapper)


        @wrapper.error
        async def wrapper_error(ctx, error):
            # do stuff with ctx, error, command_name, ... etc

        return wrapper

    return decorator

# insert commands with @command(...) decorator here

我简单的想到了"tricking"client.command(...)返回的装饰器,认为包装器的参数结构与被装饰函数的参数结构相同,通过设置包装器的__default____annotations__ 属性到修饰函数的属性。

是的,我完全知道这是一个可怕的想法,而且不是经过深思熟虑的想法(甚至行不通)。所以才发的,说明我方向不行

有什么建议吗?

有没有一种我完全不知道的更简单的方法来做这样的事情?

我是否应该自己从头构建一个 command 装饰器并坚持使用 discord.Client 而不是尝试添加到 client.command

我认为您根本不需要扩展 Command 的功能。相反,您可以使用机器人范围的 on_command_erroron_command_completion 事件来提供您正在寻找的功能。

唯一的问题是 return 值。最简单的方法可能是分配一个未使用的 ctx 属性,而不是尝试捕获 return 值(您也可以使用 return 值引发自定义错误)

from discord.commands.ext import Bot, BadArgument, MissingRequiredArgument
import sys

bot = Bot("!")

@bot.command()
async def some_command(ctx):
    ctx.return_value = 1

@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, BadArgument):
        await ctx.send("That's a bad argument")
    elif isinstance(error, MissingRequiredArgument):
        await ctx.send("You're missing an argument")
    else:
        # This is what the standard on_command_error does
        print('Ignoring exception in command {}:'.format(context.command), file=sys.stderr)
        traceback.print_exception(type(exception), exception, exception.__traceback__, file=sys.stderr)

@bot.event
async def on_command_completion(ctx):
    await ctx.send(str(ctx.return_value))

bot.run("TOKEN")