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

Discord机器人setnotifs命令二次执行报错:Unknown interaction求助

关于Discord机器人setnotifs命令二次执行报错DiscordAPIError[10062]的问题

我正在开发一款可发送开播类通知的Discord机器人,创建了setnotifs命令用于配置通知发送频道。首次执行该命令一切正常,但连续第二次执行时,终端出现如下错误:

DiscordAPIError[10062]: Unknown interaction
    at handleErrors (E:\Bot Discord\Francis le Bot\node_modules\@discordjs\rest\dist\index.js:722:13)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async BurstHandler.runRequest (E:\Bot Discord\Francis le Bot\node_modules\@discordjs\rest\dist\index.js:826:23)
    at async _REST.request (E:\Bot Discord\Francis le Bot\node_modules\@discordjs\rest\dist\index.js:1266:22)
    at async StringSelectMenuInteraction.reply (E:\Bot Discord\Francis le Bot\node_modules\discord.js\src\structures\interfaces\InteractionResponses.js:111:5)
    at async InteractionCollector.<anonymous> (E:\Bot Discord\Francis le Bot\commands\utils\setnotifs.js:173:29) {
  requestBody: {
    files: [],
    json: {
      type: 4,
      data: {
        content: 'Les notifications TikTok ont bien été configurées ! ✅',
        tts: false,
        nonce: undefined,
        embeds: undefined,
        components: undefined,
        username: undefined,
        avatar_url: undefined,
        allowed_mentions: undefined,
        flags: undefined,
        message_reference: undefined,
        attachments: undefined,
        sticker_ids: undefined,
        thread_name: undefined
      }
    }
  },
  rawError: { message: 'Unknown interaction', code: 10062 },
  code: 10062,
  status: 404,
  method: 'POST',
  url: 'https://discord.com/api/v10/interactions/1237745806162001970/aW50ZXJhY3Rpb246MTIzNzc0NTgwNjE2MjAwMTk3MDo0REczcDN6Q1FLMGZLeFpLOVJ5UFFvWGRJeVp3TFZWS01tcjBQNk1FT0p2TDkwQUtPTFd2TU45QUZHYTBpb2lDVU5RVEc0d1Z1c0JKUzNha1dpdjFOR2tqOVdhdm9VYlA0SXVwZ0xKeXJaMk54YXlXTjltZ3c0VWVoOUduV0ZvOA/callback'
}

已尝试使用interaction.deferReply()和editReply方法但无效,相关代码如下:

const { SlashCommandBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ActionRowBuilder, ChannelType, ChannelSelectMenuBuilder } = require('discord.js');
const fs = require('fs'); 
const configFile = './config.json';


module.exports = {
    data: new SlashCommandBuilder()
        .setName('setnotifs')
        .setDescription('Définir dans quel salon envoyer les notifications relatives à Killame')
        .setDMPermission(false),

    async run(interaction, client) {
        const select = new StringSelectMenuBuilder()
            .setCustomId('notification_select')
            .setPlaceholder('Sélectionnez une option')
            .addOptions([
                new StringSelectMenuOptionBuilder()
                    .setLabel('🔴 Twitch')
                    .setDescription('Paramétrer les notifs Twitch')
                    .setValue('twitch'),
                new StringSelectMenuOptionBuilder()
                    .setLabel('🎥 Youtube rediff')
                    .setDescription('Paramétrer les notifs de la chaîne rediff')
                    .setValue('youtuber'),
                new StringSelectMenuOptionBuilder()
                    .setLabel('🎥 Youtube')
                    .setDescription('Paramétrer les notifications Youtube')
                    .setValue('youtube'),
                new StringSelectMenuOptionBuilder()
                    .setLabel('📱 Tiktok')
                    .setDescription('Paramétrer les notifications TikTok')
                    .setValue('tiktok')
            ]);

        const row = new ActionRowBuilder()
        .addComponents(select);

        await interaction.reply({
            content: 'Quelles notifications voulez-vous configurer ? 🤔',
            components: [row]
        });

        const filter = (interaction) => interaction.isStringSelectMenu() && interaction.customId === 'notification_select';
        const collector = interaction.channel.createMessageComponentCollector({ filter, time: 15000 }); 

        collector.on('collect', async (interaction) => {
            const selectedValue = interaction.values[0];

            switch (selectedValue) {
                case 'twitch':

                    const select = new ChannelSelectMenuBuilder()
                    .setCustomId('channel_select')
                    .setPlaceholder('Choisisez un channel')
                    .setMinValues(1)
                    .setMaxValues(1)
                    .setChannelTypes(ChannelType.GuildText)
                    

                    const row = new ActionRowBuilder()
                    .addComponents(select);
                
                    await interaction.reply({
                        content: 'Dans quel salon voulez-vous que les notifs soient envoyées ? 🤔',
                        components: [row]
                    });

                    const filter = (interaction) => interaction.customId === 'channel_select';
                    const collector = interaction.channel.createMessageComponentCollector({ filter, time: 15000}) 

                    collector.on('collect', async (interaction) => {
                        const selectedChannelId = interaction.values[0];

                        const config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
                        config.discord.notificationChannelTwitch = selectedChannelId;
                        fs.writeFileSync(configFile, JSON.stringify(config, null, 2));

                        await interaction.reply('Les notifications Twitch ont bien été configurées ! ✅');
                    });


                    break;
                case 'youtuber':

                const select1 = new ChannelSelectMenuBuilder()
                .setCustomId('channel_select')
                .setPlaceholder('Choisisez un channel')
                .setMinValues(1)
                .setMaxValues(1)
                .setChannelTypes(ChannelType.GuildText)

                    const row1 = new ActionRowBuilder()
                    .addComponents(select1);

                    await interaction.reply({
                        content: 'Dans quel salon voulez-vous que les notifs soient envoyées ? 🤔',
                        components: [row1]
                    }); 
                


                    const filter1 = (interaction) => interaction.customId === 'channel_select';
                    const collector1 = interaction.channel.createMessageComponentCollector({ filter1, time: 15000}) 

                    collector1.on('collect', async (interaction) => {
                        const selectedChannelId = interaction.values[0];

                        const config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
                        config.discord.notificationChannelYtbRedif = selectedChannelId;
                        fs.writeFileSync(configFile, JSON.stringify(config, null, 2));

                        await interaction.reply('Les notifications Youtube Rediff ont bien été configurées ! ✅');
                    });
                    break;
                case 'youtube':
                    
                const select3 = new ChannelSelectMenuBuilder()
                .setCustomId('channel_select')
                .setPlaceholder('Choisisez un channel')
                .setMinValues(1)
                .setMaxValues(1)
                .setChannelTypes(ChannelType.GuildText)

                    const row3 = new ActionRowBuilder()
                    .addComponents(select3);
                
                    await interaction.reply({
                        content: 'Dans quel salon voulez-vous que les notifs soient envoyées ? 🤔',
                        components: [row3]
                    });

                    const filter3 = (interaction) => interaction.customId === 'channel_select';
                    const collector3 = interaction.channel.createMessageComponentCollector({ filter3, time: 15000}) 

                    collector3.on('collect', async (interaction) => {
                        const selectedChannelId = interaction.values[0];

                        const config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
                        config.discord.notificationChannelYtb = selectedChannelId;
                        fs.writeFileSync(configFile, JSON.stringify(config, null, 2));

                        await interaction.reply('Les notifications Youtube ont bien été configurées ! ✅');
                    });
                    break;
                case 'tiktok':
                                        
                    const select2 = new ChannelSelectMenuBuilder()
                    .setCustomId('channel_select')
                    .setPlaceholder('Choisisez un channel')
                    .setMinValues(1)
                    .setMaxValues(1)
                    .setChannelTypes(ChannelType.GuildText)
    
                        const row2 = new ActionRowBuilder()
                        .addComponents(select2)
                    
                        await interaction.reply({
                            content: 'Dans quel salon voulez-vous que les notifs soient envoyées ? 🤔',
                            components: [row2]
                        }); 
                        
    
                        const filter2 = (interaction) => interaction.customId === 'channel_select';
                        const collector2 = interaction.channel.createMessageComponentCollector({ filter2, time: 15000}) 
    
                        collector2.on('collect', async (interaction) => {
                            const selectedChannelId = interaction.values[0];
        
                            const config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
                            config.discord.notificationChannelTikTok = selectedChannelId;
                            fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
    
                            await interaction.reply('Les notifications TikTok ont bien été configurées ! ✅');
                        });
                        break;             
                default:
                    break;
            }
        });

        collector.on('end', (collected) => {
            if (collected.size === 0) {
                interaction.followUp('Aucune interactions détectée, merci de recommencer.');
            }
        });
    },
};

错误原因

  1. 交互超时未确认:Discord要求所有组件交互(如选择菜单)必须在3秒内给出响应,否则交互会失效,后续调用reply就会触发Unknown interaction错误。你的代码在收集到选择菜单交互后,直接创建新收集器,未先确认当前交互,导致第一次交互超时失效,二次执行时触发报错。
  2. 重复使用相同Custom ID:所有频道选择菜单都用channel_select作为Custom ID,可能导致收集器过滤逻辑混乱,加剧交互处理错误。
  3. 变量作用域冲突:在collect回调中重新定义filter、collector等变量,覆盖外部同名变量,导致逻辑执行异常。

解决方法

1. 及时确认组件交互

对每个组件交互,先通过deferReply或update确认,避免超时。例如收集到第一个选择菜单交互时,先调用deferReply,再更新原消息展示频道选择菜单。

2. 为组件设置唯一Custom ID

给不同平台的频道选择菜单设置唯一ID,比如twitch_channel_select、tiktok_channel_select,避免收集器混淆。

3. 修复变量作用域问题

不要在回调中重新定义与外部同名的变量,将内部的filter改为channelFilter、collector改为channelCollector,避免覆盖外部变量。

修正后的核心代码

collector.on('collect', async (selectInteraction) => {
    const selectedValue = selectInteraction.values[0];

    // 先确认交互,防止超时
    await selectInteraction.deferReply({ ephemeral: true });

    // 为每个平台的频道选择菜单设置唯一ID
    const channelSelectId = `${selectedValue}_channel_select`;
    const channelSelect = new ChannelSelectMenuBuilder()
        .setCustomId(channelSelectId)
        .setPlaceholder('Choisisez un channel')
        .setMinValues(1)
        .setMaxValues(1)
        .setChannelTypes(ChannelType.GuildText);

    const row = new ActionRowBuilder().addComponents(channelSelect);

    // 更新之前的回复,展示频道选择菜单
    await selectInteraction.editReply({
        content: 'Dans quel salon voulez-vous que les notifs soient envoyées ? 🤔',
        components: [row]
    });

    // 使用唯一ID过滤交互
    const channelFilter = (interaction) => interaction.customId === channelSelectId;
    const channelCollector = selectInteraction.channel.createMessageComponentCollector({ channelFilter, time: 15000 });

    channelCollector.on('collect', async (channelInteraction) => {
        await channelInteraction.deferReply({ ephemeral: true });
        const selectedChannelId = channelInteraction.values[0];

        // 读取并更新配置
        const config = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
        switch(selectedValue) {
            case 'twitch':
                config.discord.notificationChannelTwitch = selectedChannelId;
                break;
            case 'youtuber':
                config.discord.notificationChannelYtbRedif = selectedChannelId;
                break;
            case 'youtube':
                config.discord.notificationChannelYtb = selectedChannelId;
                break;
            case 'tiktok':
                config.discord.notificationChannelTikTok = selectedChannelId;
                break;
        }
        fs.writeFileSync(configFile, JSON.stringify(config, null, 2));

        // 编辑回复告知配置完成
        await channelInteraction.editReply(`Les notifications ${selectedValue.charAt(0).toUpperCase() + selectedValue.slice(1)} ont bien été configurées ! ✅`);
    });

    channelCollector.on('end', (collected) => {
        if (collected.size === 0) {
            selectInteraction.followUp('Aucune interactions détectée, merci de recommencer.', { ephemeral: true });
        }
    });
});

额外优化建议

  • 使用fs.promises.readFile和fs.promises.writeFile异步方法代替同步读写,避免阻塞事件循环。
  • 为文件操作添加try-catch错误处理,防止文件读写失败导致机器人崩溃。

内容的提问来源于stack exchange,提问作者user23326371

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 13:45:54