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.'); } }); }, };
错误原因
- 交互超时未确认:Discord要求所有组件交互(如选择菜单)必须在3秒内给出响应,否则交互会失效,后续调用
reply就会触发Unknown interaction错误。你的代码在收集到选择菜单交互后,直接创建新收集器,未先确认当前交互,导致第一次交互超时失效,二次执行时触发报错。 - 重复使用相同Custom ID:所有频道选择菜单都用
channel_select作为Custom ID,可能导致收集器过滤逻辑混乱,加剧交互处理错误。 - 变量作用域冲突:在
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
相关产品推荐
相关产品推荐

