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

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 });
        }
    }
}

关键修改说明

  1. 替换模态框调用对象:将await interaction.showModal(modal)改为await userInteraction.showModal(modal),userInteraction是选择菜单触发的全新交互,未被回复/延迟,符合Discord交互规则。
  2. 修复无效ActionRow:删除含纯字符串的firstActionRow,改用将测试ID嵌入模态框自定义ID的方式传递参数,后续处理模态框提交时,可通过interaction.customId.split('-')[2]提取测试ID。
  3. 优化错误处理:在catch块中判断原交互状态,避免触发二次回复错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 02:05:58