如何实现Discord Bot按钮在程序重启后保持正常交互响应
问题根因
你当前使用的channel.createMessageComponentCollector创建的是进程级临时监听器,生命周期和Bot运行进程强绑定,仅在监听器创建后的当前进程运行阶段生效。Bot重启后内存中所有临时注册的collector会被全部回收,重启前发送的按钮消息没有对应的交互处理逻辑,点击后Discord收不到Bot的合法响应,就会提示interaction failed。
修复方案
移除命令回调内的临时collector逻辑,改用全局持久化的interactionCreate事件统一处理所有按钮交互,该监听需要在Bot启动阶段就完成注册,保证重启后能第一时间接收所有交互事件。
注意:所有交互必须在触发后3秒内返回响应,否则Discord会自动判定交互失败
第一步:注册全局交互监听
把以下代码放到Bot客户端初始化完成的入口位置,确保Bot启动就会加载这段逻辑:
// client为你初始化的discord.js客户端实例 client.on('interactionCreate', async (interaction) => { // 过滤非按钮类型交互,避免和斜杠命令、下拉菜单等其他逻辑冲突 if (!interaction.isButton()) return const { customId } = interaction // 匹配对应按钮的自定义ID做分支处理 switch(customId) { case 'Accept': case 'Tenative': console.log(`Button with id: ${customId} was pressed by ${interaction.user.username}`) await interaction.reply({ content: `Button with id: ${customId} was pressed by ${interaction.user.username}`, ephemeral: true }) break // 后续新增按钮直接在这里加case分支即可 } })
第二步:简化原斜杠命令逻辑
删除原命令里所有和collector相关的代码,仅保留发送按钮消息的逻辑即可,修改后代码如下:
import { MessageActionRow, MessageButton } from "discord.js"; import { ICommand } from "wokcommands"; export default { category: 'testing', description: 'Returns a Button', slash: true, testOnly: true, callback: async ({interaction:msgInt}) =>{ const row = new MessageActionRow() .addComponents( new MessageButton() .setCustomId('Accept') .setEmoji('👍') .setLabel('ACCEPT') .setStyle('SUCCESS') ) .addComponents( new MessageButton() .setCustomId('Tenative') .setEmoji('🤷♂️') .setLabel('TENATIVE') .setStyle('PRIMARY') ) await msgInt.reply({ content: 'Are you sure?', components: [row], }) }, } as ICommand
避坑说明
- 之前自行编写
interactionCreate.ts不生效,优先检查两个点:一是监听事件是否绑定到了正确初始化完成的client实例上,不要在client实例创建完成前注册事件;二是是否缺少交互类型判断,把其他类型的交互(比如斜杠命令)误放到按钮逻辑里处理,导致代码抛错中断。 - 如果按钮点击后的业务逻辑处理时间超过3秒,需要先调用
await interaction.deferReply({ ephemeral: true })做响应占位,等逻辑处理完成后再用editReply发送最终内容,避免触发交互超时报错。 - 如果担心不同功能的按钮customId冲突,可以给ID加前缀做命名空间,比如
testing_Accept、testing_Tenative,监听时用前缀匹配对应功能模块的处理逻辑即可。
内容的提问来源于stack exchange,提问作者Scrappy
相关产品推荐
相关产品推荐

