【问题标题】:using invoke() inside a daily loop在每日循环中使用 invoke()
【发布时间】:2021-08-08 01:13:14
【问题描述】:

我正在使用通用框架从评分最高的答案 here 设置每日重复任务。如果我只想在target_channel_id 中指定的通道中打印内容,这运行良好。

但我正在尝试使用 invoke()called_once_a_day 函数中调用另一个函数(带参数),如下所示:

@bot.command()
async def foo(ctx, *, arg):
    await ctx.send('this works {}'.format(arg))

问题是,foo 中有一个ctx,但called_once_a_day 中没有 ctx。如果我将ctx 添加为called_once_a_day(ctx) 的参数并包含以下行以调用foo

await ctx.invoke(bot.get_command('foo'), arg='whatever')

我收到以下错误:

TypeError: called_once_a_day() missing 1 required positional argument: 'ctx'

我还尝试从 called_once_a_day 中删除 ctx 参数,而是更改调用 invoke() 的方式

await bot.invoke(bot.get_command('foo'), arg='whatever')

然后我得到这个错误:

TypeError: invoke() got an unexpected keyword argument 'arg' 

tldr 我不确定如何在没有ctx 的情况下调用invoke(),但添加ctx 会引发错误。

编辑:

跑步

@tasks.loop(hours = 24) 
async def called_once_a_day(): 
    await ctx.invoke(bot.get_command('foo'), arg='whatever')

抛出以下错误: NameError: name 'ctx' is not defined

【问题讨论】:

  • 第一种方法 (await ctx.invoke(bot.get_command('foo'), arg='whatever')) 对我有效,但 arg 必须是仅关键字参数。
  • 我编辑了我的原始帖子,因为我遇到了这个错误
  • 我看了这个,我同意需要定义一个 ctx 或某种通道目标,但在这种情况下没有 message。如果不必调用另一个命令,只需打印到频道,这对我有用:message_channel = bot.get_channel(id) 后跟await message_channel.send(...)。但是我不能在没有得到错误AttributeError: 'TextChannel' object has no attribute 'invoke' 的情况下做await message_channel.invoke(bot.get_command('foo'), arg='whatever')。我想我需要这样的东西,但我不知道到底是什么。

标签: python discord discord.py


【解决方案1】:

调用命令的主要方式有 3 种:

  1. (新方式)
cmd = bot.get_command('foo')
await cmd(ctx, 'positional argument', keyword='argument')
  1. (老办法)
cmd = bot.get_command('foo')
await ctx.invoke(cmd, 'positional argument', keyword='argument')

小提示:传递位置和关键字参数时要小心


使用这两种方法有 3 个缺点:

  1. 不会触发转换器(类型提示)。您需要将正确的类型传递给参数。
  2. 将绕过检查。您可以使用非所有者调用仅所有者命令,它仍然可以工作(您可以使用 Command.can_run 它将运行所有检查并返回布尔值)
  3. 如果在命令之外调用了ctx.invoke(例如eval),则错误处理程序不会触发。

第三种方法是实际处理所有检查和转换器的方法,尽管您不能自己传递参数(我猜覆盖 ctx.args 会起作用,但没有尝试过)

cmd = bot.get_command('foo')
ctx.command = cmd
await bot.invoke(ctx)

我完全不推荐使用这种方法

【讨论】:

  • 第三种方法仍然会复杂很多,特别是如果您开始使用子命令和所有方法。仅仅替换 .command 属性可能会搞砸一些事情
  • 在您列出的所有 3 种方法中,ctx 是必需的参数。但这又回到了我最初的问题,即如果ctx 不是一个论点该怎么办。在called_once_a_day函数中,没有ctx,加一会报错。
  • 那么根本无法调用命令,最简单的解决方案是创建一个接受通道实例的函数,然后在任务和命令中调用它
  • 另一种解决方案是在命令中启动任务并将 ctx 作为参数传递。
  • 我正在考虑类似于第一个建议的解决方法,只是将另一个版本的函数复制粘贴到循环中,这样就没有什么可调用的了,它只是运行我用指定的频道。您介意详细说明您的第二个建议吗?
【解决方案2】:

有几种方法可以做到这一点,你做哪一种取决于你想要什么。

方法一:你想将命令作为函数调用,忽略所有的钩子、转换器、冷却、权限检查等。

为此,请使用Command.__call__

command = bot.get_command("command name")
await command.__call__(ctx, arg="argument")

方法2:你想调用命令,同时你也想让bot处理checks和converters。

为此,您应该使用Bot.invoke。您应该获取 Context 对象,并将 .command 属性更改为您想要的命令。

command = bot.get_command("command name")
ctx.command = command
await bot.invoke(ctx)

如果您需要更改传递给命令的参数,请使用第一种方法。请注意,它不会处理转换器,因此您必须为命令提供正确的类型。

【讨论】:

  • 我对你的两种方法有同样的问题,因为它们都需要ctx。如果我只是在日常循环之外编写另一个函数,我可以包含 ctx 作为参数并使用提到的任何这些方法来调用另一个命令,完全没有问题。但是一旦我尝试在预定循环的框架内执行此操作,它就会出错。
  • @norinhara 你在这个循环中有消息吗?
  • 不,循环内没有定义消息或通道。这就是您看到的我为编辑添加的内容。
猜你喜欢
  • 2018-02-14
  • 1970-01-01
  • 2017-02-08
  • 1970-01-01
  • 1970-01-01
  • 2013-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多