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
相关产品推荐
相关产品推荐

