Discord.js v13 Bot无法处理同一频道内多用户同时触发的按钮交互问题
Discord带按钮的冒险系统多用户同频道使用方案
原问题描述
有没有办法让带按钮的命令支持两名用户在同一频道内同时使用?
我开发了一个冒险系统,目前进展顺利,但发现同一频道内仅支持单用户使用该系统。若其他用户在同频道触发,点击按钮时会立刻抛出"DiscordAPIError: Unknown Interaction"错误。我尝试过在按钮的custom ID末尾添加消息作者ID适配,但不知为何并未生效,目前完全没有头绪。
根据我的判断,问题应该和多按钮实例无关,但我想不到其他可能的诱因。我的过滤器代码如下:
const filter = async (interaction) => { if (interaction.user.id === message.author.id) return true; await interaction.deferReply() await interaction.editReply({ content: `**${interaction.user.username}**, You can't use this button!`, ephemeral: true}) return false }
按钮点击监听代码大致如下:
GameCollect.on('collect', async ButtonInteraction => { ButtonInteraction.deferUpdate() // My code })
我已经尝试过的解决方案:
- 删除所有点击收集器中的
ButtonInteraction.deferUpdate()代码。 - 将按钮的custom ID末尾拼接消息作者ID,并适配了对应的业务逻辑。
- (调侃:急得头撞键盘,也没用... 开玩笑的)
如果有需要,我可以把完整代码上传到repl方便定位问题,我认为其他逻辑不会影响这部分功能,应该只需要添加少量代码/微调就能支持同频道两人同时使用。
编辑补充:我已上传完整源码方便定位问题,也可以查看最新回答下的评论了解我的测试详情,找到解决方案我会同步更新。
声明:我花了一个多月都没找到可行解决方案,现在基本决定放弃用按钮实现该功能了,还是感谢所有提供帮助的人。
问题根因
报错的核心诱因有两个:
- 你使用了全局共享的
GameCollect收集器监听,每次新用户触发冒险时,新的收集器配置会覆盖旧的监听逻辑,导致先触发的冒险实例的按钮交互找不到对应的处理逻辑,直接抛出未知交互错误。 - 交互处理逻辑没有添加
await关键字,异步操作未完成就结束了交互生命周期,同时filter内对非允许用户的交互响应逻辑存在时序问题,未完成回复就直接返回false导致交互未被正确消费。
修复方案
1. 为每一场冒险独立创建专属收集器
不要复用全局收集器,每次用户触发冒险命令、生成带按钮的游戏消息后,单独为这条消息创建独立的组件收集器,不同冒险实例的收集器互相隔离,互不干扰:
// 每次用户触发冒险、生成游戏消息后执行 const gameMessage = await channel.send({ /* 你的消息内容+按钮配置 */ }) // 为当前这条游戏消息创建专属收集器 const currentGameCollector = gameMessage.createMessageComponentCollector({ time: 7200000, // 自定义游戏超时时间 filter: generateFilter(message.author.id, secondUserId) // 传入允许参与的两个用户ID }) // 仅监听当前这场冒险的按钮交互 currentGameCollector.on('collect', async interaction => { // 必须加await等待defer完成,避免交互提前过期 await interaction.deferUpdate() // 你的游戏业务逻辑 }) // 游戏结束后销毁收集器,避免残留监听 currentGameCollector.on('end', () => { // 可选:将游戏消息的按钮设为禁用状态 })
2. 修正过滤器逻辑
调整filter的判断条件和响应逻辑,确保所有交互都被正确响应:
// 生成适配当前冒险的过滤器,传入两个允许参与的用户ID const generateFilter = (hostId, guestId) => async (interaction) => { if (interaction.user.id === hostId || interaction.user.id === guestId) return true // 直接用reply不用deferReply+editReply,减少异步层级 await interaction.reply({ content: `**${interaction.user.username}**, 你不能使用这个按钮!`, ephemeral: true }) return false }
3. 可选优化:custom ID 适配规则
如果你依然要使用custom ID区分用户,格式建议统一为[动作类型]_[用户ID],在全局交互监听中先拆分custom ID,匹配到对应的游戏实例再处理,也可以解决多实例冲突问题。
内容的提问来源于stack exchange,提问作者Neokat
相关产品推荐
相关产品推荐

