Как вы расширяете функциональность client.command?

#python #discord.py #discord.py-rewrite

#python #discord.py

Вопрос:

Я пытаюсь добавить к обработке ошибок аргументы, kwargs и функциональные элементы client.command ( client являясь discord.ext.commands.Bot экземпляром). По сути, я пытаюсь сделать так, чтобы все мои команды имели определенное общее поведение.

Моя первоначальная идея состояла в том, чтобы создать функцию, которая возвращает декоратор, который украшает свою оболочку с помощью client.command .

Однако самая большая проблема, с которой я столкнулся, заключается в том, что обработка параметров декоратора, возвращаемого client.command(...) , полностью зависит от способа расположения параметров и аннотаций оформленной функции, что означает, что оболочка с ее параметрами, подобными этому

 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
  

Я кратко подумал о том, чтобы «обмануть» декоратора, возвращаемого client.command(...) , заставив его думать, что структура параметров оболочки такая же, как у оформленной функции, установив атрибуты оболочки __default__ и __annotations__ на атрибуты оформленной функции.

Да, я полностью осознаю, что это ужасная и не очень хорошо продуманная идея (и она даже не работает). Вот почему я опубликовал это, это означает, что мое направление никуда не годится.

Есть предложения?

Есть ли гораздо более простой способ сделать что-то подобное, о котором я совершенно не знал?

Должен ли я просто создать command декоратор самостоятельно с нуля и придерживаться discord.Client вместо того, чтобы пытаться добавить к client.command ?

Комментарии:

1. Какие именно изменения вы хотите внести? Может быть возможно создать подкласс Command и передать ваш подкласс в client.command с cls аргументом.

2. Чтобы быть более конкретным, прямо сейчас я пытаюсь обработать BadArgument , MissingRequiredArgument ситуацию, когда возникает другое исключение, и ситуацию, когда исключения не возникает (и что-то сделать с возвращаемым значением). Я хочу, чтобы все команды делали это одинаково.

Ответ №1:

Я не думаю, что вам вообще нужно расширять функциональность Command . Вместо этого у вас могут быть боты on_command_error и on_command_completion события, которые обеспечивают функциональность, которую вы ищете.

Единственная проблема заключается в возвращаемом значении. Вероятно, самый простой способ — назначить неиспользуемый атрибут ctx вместо того, чтобы пытаться получить возвращаемое значение (вы также можете вызвать пользовательскую ошибку с возвращаемым значением)

 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")