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 Bot- 使用 YAGPDB bot 的反应投票