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

Discord开发:如何用Modal响应选择菜单命令及解决交互报错

解决方案:Discord斜杠命令中选择菜单触发Modal的实现

方案一:修复组件交互触发Modal的逻辑

你的核心问题是误用了已完成响应的原始斜杠命令交互来调用showModal。正确的做法是使用选择菜单自身的交互对象(代码中的cld)触发Modal——每个组件交互都是独立的,不受原始斜杠交互的响应限制。

修改后的关键代码片段:

collector.on('collect', async (cld) => {
    // 先处理组件交互的响应,避免Discord超时警告
    await cld.deferUpdate();
    if(cld.values[0] === '63960e06ee2ff68635eff3bb'){
        // 改用组件交互对象调用showModal
        await cld.showModal(modal);
    }
})

完整修改后的execute函数:

async execute(interaction, client) {

    const userRoles = interaction.member.roles.cache.map((role) => role.id)
    let allMatches = await matches.find()

    let userMatches = []

    allMatches.forEach(element => {
        userRoles.forEach(role => {
            if (element._team1 === role || element._team2 === role) {
                userMatches.push(element)
            }
        })
    })

    const embed = new EmbedBuilder()
    .setTitle('Select a match')
    .setColor(0x18e1ee)

    const componentsMenu = new ActionRowBuilder().addComponents(
            new StringSelectMenuBuilder()
            .setCustomId('select')
            .setPlaceholder('Nothing selected')               
        )

    userMatches.forEach(element => {
        componentsMenu.components[0].addOptions(
            {
                label: interaction.guild.roles.cache.get(element._team1).name + ' VS ' + interaction.guild.roles.cache.get(element._team2).name,
                value: element.id,
            }
        )
    })

    const modal = new ModalBuilder()
    .setCustomId('test')
    .setTitle('test2')

    const textInput = new TextInputBuilder()
    .setCustomId('test3')
    .setLabel('test4')
    .setRequired(true)
    .setStyle(TextInputStyle.Short)

    modal.addComponents(new ActionRowBuilder().addComponents(textInput))

    const message = await interaction.reply({embeds: [embed], components: [componentsMenu], fetchReply: true})

    const collector = message.createMessageComponentCollector({
        filter: (u) => u.user.id === interaction.user.id,
        time: 60000 // 可选:添加超时时间,避免collector长期存在
    })

    collector.on('collect', async (cld) => {
        await cld.deferUpdate();
        if(cld.values[0] === '63960e06ee2ff68635eff3bb'){
            await cld.showModal(modal);
        }
    })

    collector.on('end', () => {
        // 可选:超时后禁用选择菜单,提示用户重新发起命令
        componentsMenu.components[0].setDisabled(true);
        interaction.editReply({components: [componentsMenu]});
    })
}

方案二:发送普通消息并消除命令错误提示

如果坚持要将选择菜单作为普通消息发送到频道,只需先对斜杠命令做一个私有临时回复(仅用户可见),就能避免Discord弹出未响应命令的错误提示。

修改后的关键代码:

// 先回复斜杠命令,用ephemeral: true让只有用户自己能看到
await interaction.reply({content: '正在生成匹配选择菜单...', ephemeral: true});
// 再发送普通消息到频道
const message = await interaction.channel.send({embeds: [embed], components: [componentsMenu]});

后续的collector逻辑与方案一一致,使用cld.showModal即可。

关键说明

  • Discord的每个交互(斜杠命令、组件点击等)仅能被响应一次,原始斜杠交互用interaction.reply后,不能再用它调用showModal,必须使用组件自身的交互对象。
  • 组件交互必须在3秒内完成响应(deferUpdate、deferReply或直接回复),否则会触发Discord的超时警告,所以一定要添加await cld.deferUpdate()。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 20:20:41