You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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令牌]')

修复说明

  1. 恢复命令处理逻辑:在on_message末尾添加await bot.process_commands(message),确保!start_quidditch能被解析执行。
  2. 正确修改全局变量:在start_quidditch中声明所有需要修改的全局变量,避免局部变量覆盖全局状态。
  3. 重置目标环列表:在quidditch_game内部初始化目标环,每轮复制新列表操作,防止全局列表被耗尽。
  4. 用View处理组件交互:创建GoalSelect视图类,通过回调函数处理选择菜单的点击和超时逻辑,替代原有的无效事件监听。
  5. 优化玩家加入逻辑:添加重复加入判断,避免同一玩家被多次添加。

内容的提问来源于stack exchange,提问作者rissasux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:00:57