You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 01:05:37