python - Discord bot 添加并等待问答游戏的反应
问题描述
我正在使用 discordpy 编写一个不和谐的测验机器人。机器人会发送一条消息,其中包含问题和 4 个可能的答案。该机器人还使用表情符号 1️⃣、2️⃣、3️⃣ 和 4️⃣ 添加对他的消息的反应。这个想法是,机器人等待 30 秒,让人们点击一个反应。如果点击的反应是正确/错误的答案,机器人会回复正确或错误。一旦有人回答,机器人也应该停止等待新的反应。Aka:一旦有人点击了 4 个反应表情符号之一,机器人应该回复,而不是处理任何未来对此消息的反应。
目前,我让机器人发送消息(嵌入)并向其添加反应表情符号。但是,从人们那里获得结果是我遇到的问题。
一方面,由于某种原因,机器人似乎仍然被他自己的反应触发,即使我在检查功能中排除了这一点。(或者我是这么认为的)。
总的来说,我希望为此采用一种结构良好的方法。我熟悉所有 api 调用/事件,例如on_message()
and 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")
我怎样才能做到这一点?
解决方案
您的代码中有几个错误和一些不准确之处;首先,我将列出它们,然后我将向您展示我认为设置此类命令的最佳方式。请注意,以下一些不是实际的修复,而是组织代码的更有效方法。
- 你应该使用装饰器来定义机器人命令,而不是使用如下函数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")
那么当然你应该定义如何识别正确答案以及如何通知用户。如果您需要对我写的内容进行澄清,请随时对此答案发表评论,我很乐意为您解答。
推荐阅读
- spring - jhipster keycloak 集成始终重定向到 localhost:8080
- c# - c# 中的 {0,8:c} 是什么意思?
- java - HP TestExportTool 不断询问依赖关系
- r - 带有 %in% 子句的元组列表(多列)中的子集
- android - Android NDK 套接字连接()在 3g 上失败时返回 0
- bash - 如何将特定文件与 makefile 同步
- oracle - SQL*Plus 脚本中列出的许多 sql 文件的顺序执行顺序
- objective-c - 由于 FBSDKCoreKit 位码错误,FacebookCore 无法链接
- node.js - Redux 状态仅在注销后更新
- java - ElasticSearch 中的复杂 Lucene 查询