discord.py 如何实现分步Bot教程的next触发功能并避免on_message冲突
Discord 分步教程Next触发功能实现方案
原代码存在的核心问题
- 将
@client.event装饰的on_message嵌套在命令内部,每执行一次pytutorial命令就会重复注册一次全局消息监听,多次触发后会出现多套监听逻辑同时运行的问题 - 注册的
on_message没有调用client.process_commands(message)把消息流转给命令处理模块,会直接拦停所有其他机器人命令的触发逻辑,导致其他功能完全失效 - 没有做用户级的进度隔离:只要频道里出现带"next"的消息,不管是谁发的、是不是正在参与教程,都会推送下一个步骤,完全没有会话边界
优化后的完整实现逻辑
- 将所有教程步骤抽为结构化常量
把分散在代码里的步骤Embed内容统一抽成可遍历的列表,后续新增步骤只需要往列表里追加内容即可,不需要改动核心逻辑:import discord from discord.ext import commands intents = discord.Intents.default() intents.message_content = True client = commands.Bot(command_prefix="!", intents=intents) # 教程步骤结构化存储 TUTORIAL_STEPS = [ { "title": "Help for Wolf-Hosting Bot", "description": "panel.wolf-hosting.nl", "color": 0x0400ff, "thumbnail": "https://cdn.discordapp.com/icons/807978904727191572/0704feeefd7a91c1abb2c37f9c9cc4f4.webp?size=256", "fields": [ ("Welcome to the Wolf Hosting python tutorial!", "We are first going to start with chosing an editor. I prefer using VSCode.", True), ('Type "next" for the next step.', "Don't include the \"\"\"", True), ("Download VSCode:", "https://code.visualstudio.com/download", False) ] }, { "title": "Help for Wolf-Hosting Bot", "description": "panel.wolf-hosting.nl", "color": 0x0400ff, "thumbnail": "https://cdn.discordapp.com/icons/807978904727191572/0704feeefd7a91c1abb2c37f9c9cc4f4.webp?size=256", "fields": [ ("Installing discord.py", 'First click on Terminal in VSCode. Type "py -3 -m pip install -U discord.py".', True), ('Type "next" for the next step.', "Don't include the \"\"\"", True), ("More advanced explaining:", "https://discordpy.readthedocs.io/en/latest/intro.html#installing", False) ] } ] # 存储每个参与教程的用户当前进度,键为用户ID,值为当前步骤索引 user_tutorial_progress = {} - 全局消息监听加上会话校验与命令放行逻辑
把on_message事件移到全局作用域,避免重复注册,同时加上进度校验和命令放行:@client.event async def on_message(message): # 跳过机器人自身发送的消息,避免循环触发 if message.author.bot: return user_id = message.author.id # 仅对正在参与教程的用户触发Next逻辑 if user_id in user_tutorial_progress: # 严格匹配用户发送的内容为"next",避免误触发 if message.content.strip().lower() == "next": current_step = user_tutorial_progress[user_id] # 走到最后一步时清除用户进度,结束教程 if current_step >= len(TUTORIAL_STEPS) - 1: del user_tutorial_progress[user_id] await message.channel.send("你已经完成了全部教程步骤") else: # 取出下一步的内容生成Embed并发送 next_step = current_step + 1 step_info = TUTORIAL_STEPS[next_step] embed = discord.Embed( title=step_info["title"], description=step_info["description"], color=step_info["color"] ) embed.set_thumbnail(url=step_info["thumbnail"]) for field_name, field_value, inline in step_info["fields"]: embed.add_field(name=field_name, value=field_value, inline=inline) await message.channel.send(embed=embed) # 更新用户进度 user_tutorial_progress[user_id] = next_step # 关键:必须将消息传递给命令处理流程,否则其他命令会失效 await client.process_commands(message) - 改造pytutorial启动命令
去掉命令内部嵌套的事件逻辑,仅保留启动教程、初始化用户进度的功能:@client.command() async def pytutorial(ctx): # 初始化用户的教程进度为第一步 user_tutorial_progress[ctx.author.id] = 0 # 发送第一步的教程内容 first_step = TUTORIAL_STEPS[0] embed = discord.Embed( title=first_step["title"], description=first_step["description"], color=first_step["color"] ) embed.set_thumbnail(url=first_step["thumbnail"]) for field_name, field_value, inline in first_step["fields"]: embed.add_field(name=field_name, value=field_value, inline=inline) await ctx.send(embed=embed)
可选优化方向
- 增加超时逻辑:对长时间未操作的用户自动清除进度,避免无效的进度记录堆积
- 增加中断逻辑:当用户在教程过程中触发其他机器人命令时,自动清除该用户的教程进度,避免流程冲突
- 改用Discord原生的按钮交互替代文字触发:通过给Embed加Next按钮的方式,避免用户输入错字、误发内容的问题,交互体验也更顺畅
内容的提问来源于stack exchange,提问作者ChiMC
相关产品推荐
相关产品推荐

