如何用discord.py让机器人对自身投票消息添加反应并实现超时逻辑?
Discord机器人投票反应与动态超时实现方案
核心实现步骤
- 添加投票反应:发送投票消息后,立即为消息添加指定反应(如✅)
- 动态超时逻辑:设置初始超时,定时检查反应数量,按照规则调整剩余超时时长
- 反应监听与统计:监听反应添加事件,排除机器人自身反应,统计有效票数
- 执行超时操作:投票结束后根据最终票数判断是否执行超时
修改后的完整代码
import asyncio import discord from discord import app_commands @bot.tree.command(name='timeout_vote', description='发起超时投票') @app_commands.checks.has_any_role('verified') @app_commands.describe(timeout='要投票超时的成员', reason='超时理由(可选)') async def timeout_vote(interaction: discord.Interaction, timeout: discord.Member, reason: str = '未提供理由'): # 构建投票嵌入消息 embed = discord.Embed(title='⏱️ 超时投票', description=f'投票目标:{timeout.mention}\n发起者:{interaction.user.mention}\n理由:{reason}') embed.set_author(name=timeout.name, icon_url=timeout.display_avatar.url) embed.set_image(url=timeout.display_avatar.url) # 发送消息并获取消息对象(必须存下来才能后续操作) vote_msg = await interaction.response.send_message(embed=embed) # 添加投票反应 await vote_msg.add_reaction('✅') # 定义反应数对应超时的规则 vote_timeout_map = { 5: 60, # 5票 → 60秒超时 10: 120 # 10票 → 120秒超时 } current_timeout = 300 # 初始超时(5分钟,可自定义) check_interval = 10 # 每10秒检查一次反应数 try: while current_timeout > 0: # 等待反应或检查间隔到期 done, pending = await asyncio.wait( [bot.wait_for('reaction_add', timeout=check_interval)], return_when=asyncio.FIRST_COMPLETED ) # 统计有效票数(减去机器人自己的反应) valid_votes = sum(r.count for r in vote_msg.reactions if r.emoji == '✅') - 1 # 根据当前票数调整超时时间 for vote_count, target_timeout in sorted(vote_timeout_map.items(), reverse=True): if valid_votes >= vote_count: current_timeout = target_timeout break # 减少剩余超时时间 current_timeout -= check_interval # 达到最高票数阈值直接结束等待 if valid_votes >= max(vote_timeout_map.keys()): current_timeout = 0 break # 取消未完成的监听任务 for task in pending: task.cancel() # 投票结束,更新消息并执行后续操作 final_votes = sum(r.count for r in vote_msg.reactions if r.emoji == '✅') - 1 await vote_msg.edit(content=f'投票结束!有效票数:{final_votes}', embed=embed) # 判断是否执行超时 if final_votes >= 5: # 匹配对应超时时长 duration = vote_timeout_map.get(next(k for k in sorted(vote_timeout_map.keys()) if final_votes >= k), 60) await timeout.timeout_for(seconds=duration, reason=reason) await interaction.followup.send(f'{timeout.mention} 因「{reason}」被超时{duration//60}分钟!') else: await interaction.followup.send('投票未达到阈值,超时操作取消。') except asyncio.TimeoutError: # 无反应触发导致超时结束 final_votes = sum(r.count for r in vote_msg.reactions if r.emoji == '✅') - 1 await vote_msg.edit(content=f'投票超时结束!有效票数:{final_votes}', embed=embed) await interaction.followup.send('投票超时,未达到执行条件。') @timeout_vote.error async def timeout_vote_error(interaction: discord.Interaction, error): if isinstance(error, app_commands.MissingAnyRole): await bot.get_channel(1106325554942197992).send('你需要拥有「verified」角色才能使用该命令!')
关键细节说明
- 消息对象获取:必须保存
interaction.response.send_message返回的消息对象,才能对消息添加反应和后续编辑 - 有效票数统计:减去机器人自身的反应,避免把机器人的投票算入有效票数
- 动态超时调整:通过循环定时检查反应数,实时更新剩余超时,确保达到指定票数时自动缩短等待时间
- 错误处理修正:原错误函数参数错误,
app_commands的错误处理第一个参数应为interaction而非ctx
内容的提问来源于stack exchange,提问作者kinsv
相关产品推荐
相关产品推荐

