Discord.py抽奖命令计时器失效问题求助
解决Discord.py抽奖命令的倒计时与抽奖逻辑问题
原代码核心问题
- 阻塞的
wait_for调用:原代码中await self.bot.wait_for('reaction_add')会暂停整个函数执行,必须等到有用户添加反应才会继续,导致倒计时完全无法运行 - 剩余时间未实时更新:倒计时循环里一直使用初始计算的天/时/分/秒数值,嵌入消息的剩余时间不会变化
- 用户收集时机错误:提前获取了用户列表,后续添加反应的用户无法被统计到抽奖池中
- 变量名冲突:参数
winners被重新赋值为空列表,导致后续抽奖循环逻辑混乱 - 重复冗余代码:抽奖结束后的逻辑写了两次,增加维护成本
修复后的完整代码
import asyncio import random import datetime import discord from discord import app_commands @app_commands.command(name="giveaway", description="Start a giveaway") @app_commands.guild_only() async def giveaway(self, interaction: discord.Interaction, winners: int, duration: str, prize: str): await interaction.response.defer() embed = discord.Embed(description="**Creating giveaway...**", color=discord.Color.blue()) await interaction.followup.send(embed=embed) msg = await interaction.original_response() # 将时长参数转换为秒数 pos = ["s", "m", "h", "d"] time_dict = {"s": 1, "m": 60, "h": 3600, "d": 3600*24} unit = duration[-1] if unit not in pos: await interaction.followup.send(f'请使用正确的时间单位 (s, m, h, d)') return try: val = int(duration[:-1]) except ValueError: await interaction.followup.send(f'时间数值必须是整数!') return time_in_sec = val * time_dict[unit] # 计算结束时间 end_time = datetime.datetime.utcnow() + datetime.timedelta(seconds=time_in_sec) # 定义格式化剩余时间的辅助函数 def format_remaining_time(seconds): days = seconds // (3600 * 24) hours = (seconds % (3600 * 24)) // 3600 minutes = ((seconds % (3600 * 24)) % 3600) // 60 secs = ((seconds % (3600 * 24)) % 3600) % 60 parts = [] if days > 0: parts.append(f"{days}天") if hours > 0: parts.append(f"{hours}小时") if minutes > 0: parts.append(f"{minutes}分钟") if secs > 0 or len(parts) == 0: parts.append(f"{secs}秒") return " ".join(parts) # 创建初始嵌入消息 embed = discord.Embed(title="🎉 新抽奖开始! 🎉", color=discord.Color.blue()) embed.description = f"**奖品:** {prize}\n**发起者:** {interaction.user.mention}\n**剩余时间:** {format_remaining_time(time_in_sec)}" embed.timestamp = end_time await msg.edit(embed=embed) await msg.add_reaction('\U0001f389') # 倒计时循环:每5秒更新一次剩余时间 while time_in_sec > 0: await asyncio.sleep(5) time_in_sec -= 5 # 更新嵌入消息的剩余时间 embed.description = f"**奖品:** {prize}\n**发起者:** {interaction.user.mention}\n**剩余时间:** {format_remaining_time(time_in_sec)}" await msg.edit(embed=embed) # 倒计时结束,收集所有参与用户 msg = await interaction.channel.fetch_message(msg.id) reaction = msg.reactions[0] users = [user async for user in reaction.users()] # 移除机器人自身的反应 if self.bot.user in users: users.remove(self.bot.user) selected_winners = [] # 抽取获奖者 if len(users) >= winners: for _ in range(winners): winner = random.choice(users) selected_winners.append(winner.mention) users.remove(winner) elif len(users) > 0: # 参与人数不足,抽取所有参与者 selected_winners = [user.mention for user in users] # 更新抽奖结束的嵌入消息 embed = discord.Embed(title="🎉 抽奖结束! 🎉", color=discord.Color.blue()) if not selected_winners: embed.description = f"**奖品:** {prize}\n**无人参与本次抽奖 :( **" await msg.edit(embed=embed) await interaction.channel.send("本次抽奖无人参与!") else: embed.description = f"**奖品:** {prize}\n**获奖者:** {' '.join(selected_winners)}" await msg.edit(embed=embed) await interaction.channel.send(f"恭喜!{' '.join(selected_winners)} 赢得了 {prize}!")
关键修复说明
- 移除阻塞的
wait_for:取消等待单个反应的逻辑,改为倒计时结束后统一收集所有参与用户,确保倒计时正常运行 - 实时计算剩余时间:通过辅助函数每次循环重新计算并格式化剩余时间,保证嵌入消息显示准确
- 延迟收集用户列表:在倒计时结束后再获取反应用户列表,确保所有参与者都被统计
- 解决变量名冲突:将存储获奖者的变量改为
selected_winners,避免和参数winners重名导致逻辑错误 - 简化抽奖逻辑:合并重复的抽奖代码,统一处理参与人数足够、不足和无人参与的情况
- 修复交互响应错误:参数验证失败时改用
interaction.followup.send,避免已defer后调用response.send_message报错 - 优化时间格式化:用辅助函数统一处理时间格式,代码更简洁易维护
内容的提问来源于stack exchange,提问作者Ben Forster
相关产品推荐
相关产品推荐

