Python Discord魁地奇Bot无法响应!start_quidditch命令求助
问题分析与修复方案
核心问题1:自定义on_message事件阻断命令处理
自定义on_message事件后,必须手动调用await bot.process_commands(message),否则Bot不会解析!start_quidditch这类命令——这是命令无响应的直接原因。
核心问题2:全局变量被局部覆盖
在start_quidditch函数里,直接写scores = {}会创建局部变量,而非修改全局的同名变量。必须添加global声明才能重置全局状态。
核心问题3:目标环列表未重置
goal_rings是全局列表,每次游戏中执行goal_rings.remove(selected_ring)会减少元素,后续游戏会因列表为空报错,得每次启动游戏时重新初始化。
核心问题4:组件使用方式错误
discord.py已弃用components参数,需改用View类承载选择菜单;同时不存在on_select_option/on_select_timeout这类全局事件,必须通过视图的回调函数处理交互。
核心问题5:选择菜单交互未生效
原代码依赖未定义的事件处理选择,导致游戏卡在while player not in goal_attempts:的死循环里,得用视图实现完整的选择交互逻辑。
修复后的完整代码
import discord from discord.ext import commands, tasks import random import asyncio intents = discord.Intents.all() intents.typing = False intents.presences = False bot = commands.Bot(command_prefix='!', intents=intents) # 全局状态变量 scores = {} goal_attempts = {} players = [] snitch_active = False attempts_per_player = 3 game_started = False @bot.event async def on_ready(): print(f'{bot.user.name} 已连接到Discord!') @bot.command(name='start_quidditch') async def start_quidditch(ctx): global game_started, snitch_active, scores, goal_attempts, players if game_started: await ctx.send("已有一场魁地奇比赛正在进行中!") return game_started = True # 重置全局状态 scores = {} goal_attempts = {} players = [] snitch_active = False embed = discord.Embed( title="魁地奇比赛", description="点击⚡反应加入游戏!比赛将在120秒后开始。", color=discord.Color.gold() ) message = await ctx.send(embed=embed) await message.add_reaction("⚡") await asyncio.sleep(120) await ctx.send("魁地奇比赛开始!") await quidditch_game(ctx) @bot.event async def on_reaction_add(reaction, user): global game_started, players if user.bot: return message = reaction.message if message.author == bot.user and str(reaction.emoji) == "⚡" and not game_started: if user not in players: players.append(user) await message.remove_reaction("⚡", user) await message.channel.send(f"{user.mention} 已加入游戏!") # 定义选择菜单视图 class GoalSelect(discord.ui.View): def __init__(self, player, correct_ring): super().__init__(timeout=30) self.player = player self.correct_ring = correct_ring self.result = None @discord.ui.select( placeholder="选择一个球环尝试得分", options=[ discord.SelectOption(label="Ring 1"), discord.SelectOption(label="Ring 2"), discord.SelectOption(label="Ring 3") ] ) async def select_callback(self, interaction: discord.Interaction, select: discord.ui.Select): if interaction.user != self.player: await interaction.response.send_message("这不是你的选择菜单!", ephemeral=True) return self.result = select.values[0] if self.result == self.correct_ring: points = random.randint(10, 50) scores[self.player] = scores.get(self.player, 0) + points await interaction.response.send_message(f"成功得分!获得 {points} 分!", ephemeral=True) else: await interaction.response.send_message("得分失败!", ephemeral=True) self.stop() async def on_timeout(self): await self.message.edit(content=f"{self.player.mention} 超时未选择,本轮不得分。", view=None) self.result = "timeout" self.stop() async def quidditch_game(ctx): global players, goal_attempts, snitch_active, scores, game_started goal_rings = ["Ring 1", "Ring 2", "Ring 3"] # 每次游戏重新初始化 if len(players) < 2: embed = discord.Embed( title="魁地奇比赛", description="玩家数量不足,至少需要2名玩家才能开始游戏。", color=discord.Color.gold() ) await ctx.send(embed=embed) game_started = False return snitch_active = True await ctx.send("金色飞贼已出现!聊天时有概率捕获它获得额外分数!") for _ in range(attempts_per_player): random.shuffle(players) current_rings = goal_rings.copy() # 每轮复制新的环列表 for player in players: selected_ring = random.choice(current_rings) current_rings.remove(selected_ring) view = GoalSelect(player, selected_ring) msg = await ctx.send(f"{player.mention},选择一个球环尝试得分:", view=view) view.message = msg await view.wait() # 记录尝试结果 goal_attempts[player] = view.result await ctx.send("所有得分尝试已完成!") # 初始化未得分玩家的分数 for player in players: if player not in scores: scores[player] = 0 snitch_active = False await display_leaderboard(ctx) game_started = False @bot.command(name='score') async def score(ctx): await display_leaderboard(ctx) async def display_leaderboard(ctx): if scores: sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True) leaderboard_text = "魁地奇排行榜:\n" for rank, (user, points) in enumerate(sorted_scores, 1): leaderboard_text += f"{rank}. {user.name}: {points} 分\n" embed = discord.Embed( title="魁地奇排行榜", description=leaderboard_text, color=discord.Color.gold() ) await ctx.send(embed=embed) winner = sorted_scores[0][0] winner_embed = discord.Embed( title="冠军", description=f"获胜者是 {winner.mention},获得 {sorted_scores[0][1]} 分!", color=discord.Color.gold() ) await ctx.send(embed=winner_embed) else: embed = discord.Embed( title="魁地奇排行榜", description="暂无分数记录,使用 `!start_quidditch` 开始一场比赛吧。", color=discord.Color.gold() ) await ctx.send(embed=embed) @bot.event async def on_message(message): global snitch_active, scores if message.author.bot: return if snitch_active and random.random() < 0.05: snitch_points = random.randint(10, 50) scores[message.author] = scores.get(message.author, 0) + snitch_points embed = discord.Embed( title="捕获金色飞贼!", description=f"{message.author.mention} 捕获了金色飞贼,获得 {snitch_points} 分!", color=discord.Color.gold() ) await message.channel.send(embed=embed) snitch_active = False # 必须添加这行,否则命令无法处理 await bot.process_commands(message) bot.run('[你的Bot令牌]')
修复说明
- 恢复命令处理逻辑:在
on_message末尾添加await bot.process_commands(message),确保!start_quidditch能被解析执行。 - 正确修改全局变量:在
start_quidditch中声明所有需要修改的全局变量,避免局部变量覆盖全局状态。 - 重置目标环列表:在
quidditch_game内部初始化目标环,每轮复制新列表操作,防止全局列表被耗尽。 - 用View处理组件交互:创建
GoalSelect视图类,通过回调函数处理选择菜单的点击和超时逻辑,替代原有的无效事件监听。 - 优化玩家加入逻辑:添加重复加入判断,避免同一玩家被多次添加。
内容的提问来源于stack exchange,提问作者rissasux
相关产品推荐
相关产品推荐

