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

Discord.py如何检测频道内特定消息是否存在 处理四子棋面板被删异常

解决方案

你的思路完全可行,以下是两种可直接落地的实现方案:

方案1:单局游戏内异步监听双事件(适配单局/少局运行场景)

无需修改全局事件逻辑,直接在现有游戏循环中调整,通过asyncio.wait同时监听「用户添加反应」和「面板消息被删除」两个事件,哪个先触发就优先处理哪个:

import asyncio

turn = 0 
game_over = False
# 此处的board_msg是你发送的四子棋面板消息对象,需要提前保存
board_msg_id = board_msg.id

while not game_over:
    try:
        # 定义两个待监听的任务
        wait_reaction = asyncio.create_task(client.wait_for("reaction_add", check=check))
        wait_delete = asyncio.create_task(client.wait_for(
            "raw_message_delete", 
            check=lambda payload: payload.message_id == board_msg_id
        ))

        # 等待任意一个任务完成
        done, pending = await asyncio.wait(
            [wait_reaction, wait_delete],
            return_when=asyncio.FIRST_COMPLETED
        )

        # 取消未完成的任务,避免内存泄漏
        for task in pending:
            task.cancel()

        # 判断触发的是哪个事件
        if wait_delete in done:
            # 面板被删除,结束游戏
            await board_msg.channel.send("Board was not found")
            game_over = True
            continue
        
        # 否则是用户添加反应事件,走原有逻辑
        reaction, user = wait_reaction.result()
        if turn == 0:
            # 原有玩家1的落子逻辑
            if winning_move():
                game_over = True
        else:
            # 原有玩家2的落子逻辑
            if winning_move():
                game_over = True
        
    except Exception as e:
        # 异常兜底处理
        game_over = True

方案2:全局事件关联游戏实例(适配多局同时运行场景)

如果机器人要同时支持多频道多局游戏,推荐用全局事件监听+游戏实例映射的方式实现:

  1. 先定义全局字典存储活跃游戏关联关系
# key为面板消息ID,value为对应游戏的game_over状态(用列表包裹保证可修改)
active_games = {}
  1. 发送完游戏面板消息后,把当前游戏存入全局字典
# 发送面板代码省略,得到board_msg对象
active_games[board_msg.id] = [False] # 列表第0位存储game_over状态
game_over_ref = active_games[board_msg.id]
  1. 全局监听消息删除事件
@client.event
async def on_raw_message_delete(payload):
    if payload.message_id in active_games:
        # 获取对应游戏的状态引用,设置为结束
        game_over_ref = active_games[payload.message_id]
        game_over_ref[0] = True
        # 发送提示,可从payload中获取频道ID发消息
        channel = client.get_channel(payload.channel_id)
        if channel:
            await channel.send("Board was not found")
        # 从活跃字典中移除
        del active_games[payload.message_id]
  1. 原有游戏循环的判断条件改为读取引用值
while not game_over_ref[0]:
    # 原有逻辑不变

注意事项

  • 所有游戏结束场景(正常获胜、平局、主动退出、面板删除)都要及时把对应消息ID从active_games字典中移除,避免内存泄漏
  • 优先使用raw_message_delete事件而非message_delete,前者不受客户端消息缓存限制,消息未存入缓存时删除也能正常触发,稳定性更高
  • 可在每次处理用户反应前加兜底校验:尝试fetch_message获取面板消息,获取失败直接结束游戏,避免极端场景漏判

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 16:15:08