Discord.js Slash命令重复发旧图致Bot崩溃问题求助
问题根源
每次执行/sendimage命令时,你都在全局的interactionCreate事件上绑定了新的监听器。第二次使用命令后,Bot会累积多个监听器,点击按钮时所有监听器都会触发——每个监听器持有各自的图片、游戏信息变量,因此会重复发送新旧图片;同时,同一个按钮交互被多次回复会触发Discord API的重复回复错误,导致Bot崩溃。
修复方案
移除execute函数内的interaction.client.on('interactionCreate')代码,改用针对单条消息的组件收集器来监听按钮点击,确保每个消息的按钮只对应当前命令的上下文:
const { SlashCommandBuilder, userMention, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('sendimage') .setDescription('send your image to the dm channel') .addAttachmentOption(Option => Option.setName('image').setDescription('your image').setRequired(true)) .addStringOption(Option => Option.setName('gamename').setDescription('what game is this?').setRequired(true)) .addStringOption(Option => Option.setName('twitterusername').setDescription('let us know your twitter (optional)').setRequired(false)), async execute(interaction) { const image = interaction.options.getAttachment('image'); const game = interaction.options.getString('gamename'); const twittername = interaction.options.getString('twitterusername') || "N/A"; await interaction.reply('Image sent, waiting for approval!'); const channel = interaction.client.channels.cache.get('1086283992287957202'); // 保存发送到#sent-images的消息对象 const sentMessage = await channel.send({ files: [image], content: `User: ${userMention(interaction.user.id)}, game: ${game}, twitter: ${twittername}`, components: [ new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('approve') .setLabel('Approve') .setStyle(ButtonStyle.Success), new ButtonBuilder() .setCustomId('deny') .setLabel('Deny') .setStyle(ButtonStyle.Danger) ) ], }); // 创建针对当前消息的组件收集器,只监听按钮交互 const collector = sentMessage.createMessageComponentCollector({ componentType: ComponentType.Button, // 可选:设置超时时间,比如1小时后停止监听 time: 3600000 }); collector.on('collect', async (btnInteraction) => { // 可选:验证操作权限,比如只允许管理员点击 // if (!btnInteraction.member.permissions.has('Administrator')) return btnInteraction.reply({ content: '你没有权限操作', ephemeral: true }); if (btnInteraction.customId === 'approve') { const approvedChannel = interaction.client.channels.cache.get('1086284008704458862'); await approvedChannel.send({ files: [image], content: `User: ${userMention(interaction.user.id)}, game: ${game}, twitter: ${twittername}` }); await btnInteraction.reply('Image approved!'); } else if (btnInteraction.customId === 'deny') { const deniedChannel = interaction.client.channels.cache.get('1086284040715374692'); await deniedChannel.send({ files: [image], content: `User: ${userMention(interaction.user.id)}, game: ${game}, twitter: ${twittername}` }); await btnInteraction.reply('Image denied!'); } // 处理后停止收集器,防止重复操作 collector.stop(); // 禁用按钮,避免再次点击触发错误 await sentMessage.edit({ components: [ new ActionRowBuilder() .addComponents( sentMessage.components[0].components[0].setDisabled(true), sentMessage.components[0].components[1].setDisabled(true) ) ] }); }); collector.on('end', () => { // 超时未处理时自动禁用按钮 sentMessage.edit({ components: [ new ActionRowBuilder() .addComponents( sentMessage.components[0].components[0].setDisabled(true), sentMessage.components[0].components[1].setDisabled(true) ) ] }).catch(() => {}); }); }, };
关键改进点
- 移除全局监听器:不再累积重复的事件监听,彻底避免上下文混乱。
- 单消息独立上下文:每个命令发送的消息都有专属的组件收集器,仅处理自身的按钮交互。
- 防重复机制:处理完按钮点击后立即禁用按钮并停止收集器,杜绝重复操作触发的API错误。
- 代码简化:用
||运算符简化twittername的初始化逻辑,提升可读性。
内容的提问来源于stack exchange,提问作者yetty
相关产品推荐
相关产品推荐

