Discord bot 添加并等待问答游戏的反应

Posted

技术标签:

【中文标题】Discord bot 添加并等待问答游戏的反应【英文标题】:Discord bot adding and waiting for reactions for a quiz game 【发布时间】:2021-06-04 11:26:18 【问题描述】:

我正在使用 discordpy 编写一个不和谐的测验机器人。 机器人发送一条包含问题和 4 个可能答案的消息。 该机器人还使用表情符号 1️⃣、2️⃣、3️⃣ 和 4️⃣ 添加对他的消息的反应。 这个想法是,机器人会等待 30 秒,让人们点击一个反应。如果点击的反应是正确/错误的答案,机器人会回复正确或错误。一旦有人回答,机器人也应该停止等待新的反应。 Aka:一旦用户点击了 4 个反应表情符号之一,机器人应该回复,而不是处理任何未来对此消息的反应。

目前,我让机器人发送消息(嵌入)并向其添加反应表情符号。但是,从人们那里获得结果是我遇到的问题。

一方面,由于某种原因,机器人似乎仍然会被自己的反应触发,即使我在检查功能中排除了这一点。 (或者我是这么认为的)。

总的来说,我希望为此采用一种结构良好的方法。我熟悉所有 api 调用/事件,例如 on_message()on_reaction_add(),但我无法正确地将所有内容放在一起。

这是我目前所拥有的:

@commands.command(name="quiz")
    async def on_command_quiz(ctx):

        #if ctx.message.author.bot:
        #    return   

        print("Quiz command!")
        quiz = QuizGame()

        # Send quiz
        reply = await ctx.message.channel.send(embed=quiz.format())

        # Add reply emojis
        for x in range(0, len(quiz.quiz_answers)):
            await reply.add_reaction(Utils.get_number_emoji_by_number(x + 1))
        print("Correct Answer:", quiz.quiz_correct_answer)

        # Evaluate replies
        async def check_answer(reaction, user):        
            emojis = ["1️⃣","2️⃣","3️⃣","4️⃣"]
            return user != ctx.message.author and str(reaction.emoji) in emojis

        # Wait for replies
        try:
            reaction, user = await bot.wait_for('reaction_add', timeout=30.0, check=check_answer)
        except asyncio.TimeoutError:
            print("Timeout")
        else:
            if user != ctx.message.author:
                if str(reaction.emoji) == "1️⃣":
                    print("1")
                elif str(reaction.emoji) == "2️⃣":
                    print("2")
                elif str(reaction.emoji) == "3️⃣":
                    print("3")
                elif str(reaction.emoji) == "4️⃣":
                    print("4")
                else:
                    print("Unknown reaction")

我怎样才能做到这一点?

【问题讨论】:

【参考方案1】:

您的代码中有几个错误和一些不准确之处;首先,我将列出它们,然后我将向您展示我认为设置此类命令的最佳方式。 请注意,以下一些不是实际的修复,而是组织代码的更有效方法。

-你应该使用decorators来定义机器人命令,而不是使用像on_command这样的函数:

@bot.command()
async def quiz(ctx)

-ctx 类已经提供了channel 属性,所以ctx.message.channel 有点多余,请改用ctx.channel

同样适用于ctx.message.author

-如果答案的数量始终相同,那么您可以通过非常简单的for循环添加数字表情符号(另外,无需调用Utils来获取相关表情符号):

for emoji in ["1️⃣","2️⃣","3️⃣","4️⃣"]:
    reply.add_reaction(emoji)

-check_answer 函数也是多余的,逻辑上也是错误的。

这是多余的,因为不需要验证反应表情符号是否是 4 个可用表情符号之一,因为无论如何它都会在稍后的 try 块中确定。

这在逻辑上是错误的,因为如果添加反应的用户与命令的作者匹配,它应该返回True,而不是相反(你会注意到这也会阻止机器人被自己的反应触发)。

那么,函数就不需要异步了。

def check_answer(reaction, user):
    return user == ctx.author

-最后,整个 try-except-else 块在这里并没有真正起作用。为了让机器人在特定用户的第一个反应或 30 秒超时到期之前保持响应,您应该将 try-except 块集成到无限 while 循环

while True:
    try:
        reaction, user = await bot.wait_for("reaction_add", timeout=30, check=check_answer)
    
        # The following line is optional: it removes the reaction added by the user 
        # to let them react again with the same emoji; not really necessary in your case, 
        # but very helpful if your bot would still be responsive after the first reaction.
        await reply.remove_reaction(reaction, user) 

        # Here goes the if-else block of reactions.

    except asyncio.TimeoutError:
        print("Timeout")

请记住,在 try 块中的某处,当操作完成时,您必须使用 break 语句停止循环,否则它将无限期地继续。

我也在开发一个 Discord 机器人,现在还是个初学者,所以我希望我能够解释清楚。 无论如何,总结一下,这是我个人如何实现该命令的示例:

@bot.command()
async def quiz(ctx):

    print("Quiz command!")
    quiz = QuizGame()

    reply = await ctx.send(embed=quiz.format())

    emojis = ["1️⃣","2️⃣","3️⃣","4️⃣"]
    for emoji in emojis:
        await reply.add_reaction(emoji)

    def check_answer(reaction, user):
        return user == ctx.author

    while True:
        try:
            reaction, user = await bot.wait_for("reaction_add", timeout=30, check=check_answer)
            await reply.remove_reaction(reaction, user)

            # Shorter representation of that if-else block.
            if reaction.emoji in emojis:
                print(emojis.index(reaction.emoji) + 1)
                break
            else:
                print("Unknown reaction")

        except asyncio.TimeoutError:
            print("Timeout")

那么你当然应该定义如何识别正确答案以及如何通知用户。 如果您需要对我写的内容进行澄清,请随时对此答案发表评论,我很乐意为您解答。

【讨论】:

谢谢。这帮助了很多人。如果你不介意的话,一个快速的侧面问题。在定义自己的命令时,我可以为它们设置名称。但是,它们中的空格似乎会导致执行错误的功能。例如,如果您有一个属性名称为“test”的命令函数和一个属性为“test hello”的命令函数,由于某种原因,第二个函数总是被忽略,而只运行第一个函数。 发生这种情况是因为 name 属性表示函数的名称,并且由于无法定义名称中包含空格字符的函数,因此该字符与以下所有字符一起被丢弃.在您的情况下,获得的字符串与另一个函数匹配,否则代码将引发NameError 还有一些方法可以定义一个名称中带有特殊字符的函数,但是这意味着会弄乱主脚本的全局符号表,这是非常不鼓励的,真的不值得。我的机器人也有多个单词命令,例如将前缀更改为名称一个,我将它们定义为“change-prefix”或“changeprefix”。 我认为在我的情况下最好只使用一个单词作为命令名称,第二个单词作为参数?然后,根据论点是什么,做不同的事情。 这绝对是一个很好的解决方法。【参考方案2】:

您并没有真正忽略机器人的反应,至少从我在代码中看到的情况来看。 你可以试试下面的方法:

            try:
                reaction, user = await self.bot.wait_for('reaction_add', timeout=15)
                while user == self.bot.user:
                    reaction, user = await self.bot.wait_for('reaction_add', timeout=15)
                if str(reaction.emoji) == "YourEmoji":

check 函数可以是:

        reactions = "YourReactions"

        def check1(reaction, user):
            return user == ctx.author and str(reaction.emoji) in [reactions]

这里我们检查反应是否来自命令的作者,并检查表情符号是否在reactions“列表”中。

【讨论】:

感谢您的回复。我现在注意到一些奇怪的行为。当您的方法与一直等待的循环一起使用时,它有时会被重复调用,直到超时。有没有更好的方法来做到这一点。基本上每个人都应该能够做出反应,但机器人本身的东西应该被忽略。并且只是在一个while循环中执行它会导致超时并挂在机器人中嗯

以上是关于Discord bot 添加并等待问答游戏的反应的主要内容,如果未能解决你的问题,请参考以下文章

如何制作随机反应游戏(discord.py)

Discord.JS 如何等待会员反应

Discord Bot- 使用 YAGPDB bot 的反应投票

角色反应 Discord Bot

Discord bot 添加对消息 discord.py 的反应(无自定义表情符号)

如何向 discord.js bot v12 添加验证