Discord.js报错InteractionAlreadyReplied:选择菜单后无法发送模态框
Discord机器人选择菜单后弹出模态框报错:InteractionAlreadyReplied
问题根源
错误核心是调用了错误的交互对象展示模态框:原斜杠命令交互(interaction)已经通过interaction.reply()发送过回复,此时再用它调用showModal会触发InteractionAlreadyReplied报错。正确做法是使用选择菜单触发的新交互对象(userInteraction)来展示模态框。
另外代码存在隐藏问题:const firstActionRow = new ActionRowBuilder().addComponents(idTestu);直接将字符串selectedTestId加入ActionRow无效,ActionRow仅支持Discord官方消息组件(如TextInput、SelectMenu),不能添加纯文本。若需传递测试ID,建议嵌入模态框自定义ID或通过后续交互关联。
修正后的代码
} else if (testOption === 'upravit') { const database = JSON.parse(fs.readFileSync('events/testy.json', 'utf8')); const currentDate = new Date(); const currentDateString = currentDate.toISOString().slice(0, 10).replace(/-/g, ''); // Format to YYYYMMDD const options = database.entries .filter(entry => { return entry.datum >= currentDateString; }) .map(entry => { const entryDate = new Date( entry.datum.substring(0, 4), entry.datum.substring(4, 6) - 1, // Month is zero-based entry.datum.substring(6, 8) ); const formattedDate = entryDate.toLocaleDateString('cs-CZ', { day: '2-digit', month: '2-digit', year: 'numeric' }); return { label: entry.predmet, description: `${entry.tema}, naplánováno na ${formattedDate}`, value: entry.id.toString() }; }); const select = new StringSelectMenuBuilder() .setCustomId('pick-test') .setPlaceholder('Testy') .addOptions(options); const row = new ActionRowBuilder() .addComponents(select); try { await interaction.reply({ content: 'Vyber test pro upravení.', components: [row], }); const userInteraction = await interaction.channel.awaitMessageComponent({ filter: i => i.customId === 'pick-test' && i.user.id === interaction.user.id, time: 60000 }); if (!userInteraction) { await interaction.followUp({ content: 'Vypršel čas na výběr.' }); return; } const selectedTestId = userInteraction.values[0]; // 将测试ID嵌入模态框自定义ID,方便后续提交时提取 const modal = new ModalBuilder() .setCustomId(`test-edit-${selectedTestId}`) .setTitle('Upravení testu'); const newTema = new TextInputBuilder() .setLabel('Nové téma testu') .setCustomId('newTema-testu') .setRequired(false) .setPlaceholder(`(nepovinné)`) .setStyle(TextInputStyle.Short); const newDate = new TextInputBuilder() .setCustomId('newDate-testu') .setLabel('Nové datum testu') .setStyle(TextInputStyle.Short) .setPlaceholder(`ve formátu YYYYMMDD (nepovinné)`) .setRequired(false) .setMaxLength(8); // 移除无效的纯文本ActionRow,改用模态框自定义ID传递测试ID const secondActionRow = new ActionRowBuilder().addComponents(newTema); const thirdActionRow = new ActionRowBuilder().addComponents(newDate); modal.addComponents(secondActionRow, thirdActionRow); // 关键:使用选择菜单的交互对象展示模态框 await userInteraction.showModal(modal); } catch (error) { console.error(error); // 避免二次回复报错,先判断交互状态 if (interaction.replied || interaction.deferred) { await interaction.followUp({ content: 'Nastala chyba při zpracování příkazu.', ephemeral: true }); } else { await interaction.reply({ content: 'Nastala chyba při zpracování příkazu.', ephemeral: true }); } } }
关键修改说明
- 替换模态框调用对象:将
await interaction.showModal(modal)改为await userInteraction.showModal(modal),userInteraction是选择菜单触发的全新交互,未被回复/延迟,符合Discord交互规则。 - 修复无效ActionRow:删除含纯字符串的
firstActionRow,改用将测试ID嵌入模态框自定义ID的方式传递参数,后续处理模态框提交时,可通过interaction.customId.split('-')[2]提取测试ID。 - 优化错误处理:在catch块中判断原交互状态,避免触发二次回复错误。
内容的提问来源于stack exchange,提问作者Nicolas
相关产品推荐
相关产品推荐

