You need to enable JavaScript to run this app. Discord.py开发的Discord机器人无限重复读取用户消息问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

Discord.py开发的Discord机器人无限重复读取用户消息问题咨询

无限重复读取同一条消息的核心根因

所有问题都来自代码逻辑和Discord.py的事件机制不匹配,具体硬伤:

  • 事件回调嵌套死循环:on_message是事件触发式回调——每收到一条新消息,这个函数就会被独立调用一次,执行完就退出等待下一次触发。现在的代码在回调内部写了while(choice != '!end')的死循环,整个循环生命周期里读取的永远是第一次触发这个回调的那条固定message对象,根本接收不到后续用户发的新消息,等于绕着同一条消息反复跑判断逻辑,自然就出现无限重复处理同一条消息的问题。
  • 无效的多前缀判断:代码里写的message.content.startswith('!click' or '!c')、message.content.startswith('!minigame' or '!mg')完全不符合预期。Python中or运算符只要左边表达式为真就直接返回左边的值,'!click'是非空字符串,恒为真,所以这两个判断等价于只匹配!click和!minigame开头的消息,!c、!mg的分支永远触发不了。
  • 错误的循环变量赋值:循环里把choice变量赋值成了await message.reply(...)的返回值——这个返回值是Bot自己发出去的消息对象,不是用户输入的指令文本,所以循环里判断choice != '!end'的条件永远成立,死循环根本退不出去,直接把Discord.py的异步事件循环卡死。
  • 无隔离的全局状态:r、choice这些游戏状态直接用全局变量存储,每次触发on_message就会把r重置为0、choice重置为!idle,既存不住用户的游戏进度,还会被其他用户的消息冲掉全局数据。
  • 未正确启动客户端:代码末尾的client.run没有加括号、没有传入Bot令牌,根本没有实际执行启动逻辑,就算前面逻辑修正了也没法正常运行。
排查思路
  • 第一步:直接删掉on_message内部嵌套的所有while循环,重启Bot观察是否还存在重复处理同一条消息的问题。如果问题消失,就可以确认核心矛盾是循环逻辑和事件回调的耦合问题。
  • 第二步:单独打印测试message.content.startswith('!click' or '!c')的实际返回值,验证字符串判断的逻辑是否符合预期,修正错误的判断写法。
  • 第三步:检查循环内的变量赋值来源,确认存的是用户发送的文本内容,还是Bot自身发送的消息对象,避免把Bot的输出当成用户指令触发逻辑。
  • 第四步:确认客户端启动的代码是否正确调用client.run("你的Bot令牌"),避免没启动就调试逻辑问题。
可落地的解决方案

完全适配Discord.py的事件驱动模型做改造,不需要自己手写循环卡等待输入:

  • 把独立的消息判断作为核心入口:on_message每收到一条新消息,就针对当前这条消息做一次指令判断,处理完直接退出函数,不要在回调内部等待后续用户输入。
  • 按用户/频道维度拆分状态存储:不要用全局变量存游戏数据,用字典结构存每个用户的独立状态,key对应用户ID,value存对应用户的renown数量、游戏阶段,避免数据互相干扰、被重置。
  • 修正多前缀的判断写法:要匹配多个指令前缀时,给startswith传元组参数,比如message.content.startswith(('!click', '!c')),不要用'a' or 'b'的错误写法。

修正后的核心逻辑参考:

# 按用户ID存储独立游戏状态,避免全局数据被干扰
user_game_state = {}

class MyClient(discord.Client):
    async def on_ready(self):
        print(f'Logged in as {self.user}')
        print('------')

    async def on_message(self, message):
        # 跳过所有Bot的消息,避免递归触发处理逻辑
        if message.author.bot:
            return

        user_id = message.author.id
        content = message.content

        # 首次交互的用户初始化游戏状态
        if user_id not in user_game_state:
            user_game_state[user_id] = {"renown": 0}
        user_state = user_game_state[user_id]

        # 逐分支判断当前收到的消息,无嵌套死循环
        if content.startswith('!hello'):
            await message.reply('Hello!', mention_author=True)
        elif content.startswith(('!click', '!c')):
            user_state["renown"] += 1
            await message.reply(f"You have {user_state['renown']} Renown")
        elif content.startswith('!bank'):
            await message.reply(f"You have {user_state['renown']} Renown")
        elif content.startswith(('!minigame', '!mg')):
            await message.reply("Game")
        elif content.startswith('!end'):
            # 清除用户状态,结束当前游戏会话
            del user_game_state[user_id]
            await message.reply("Game ended")

# 正确初始化客户端并传入Bot令牌启动
client = MyClient()
# client.run("替换为你自己的Bot开发令牌")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.02 06:36:05