Discord.js v14中String Select Collector交互失败问题求助
Discord.js v14 选择菜单触发“Interaction Failed”问题解决
核心问题分析
- 未完成交互回应:Discord要求所有组件交互必须在3秒内通过
reply/deferReply/update等方法确认,仅执行console.log会导致平台判定交互超时,触发错误。 - CustomID 设计不合理:当前使用
client.user.id(机器人自身ID)生成组件ID,会导致所有用户的组件ID完全一致,可能引发过滤器匹配冲突,且无法区分不同用户的交互。
修复后的完整代码
import { ActionRowBuilder, TextInputBuilder, TextInputStyle, Message, ButtonBuilder, ButtonStyle, ComponentType, ModalBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, } from "discord.js"; import CustomClient from "@/bot/classes/client"; const teams = [...]; export default { run: async (client: CustomClient, interaction: Message) => { try { // 使用消息发起者的ID生成唯一组件标识 const userId = interaction.author.id; const unlockButton = new ButtonBuilder() .setCustomId(`unlock-${userId}`) .setStyle(ButtonStyle.Primary) .setLabel("🔓 Clique para acessar o canal..."); const unlockButtonReply = await interaction.reply({ components: [ new ActionRowBuilder<ButtonBuilder>().setComponents(unlockButton), ], }); const unlockButtonReplyCollector = unlockButtonReply.createMessageComponentCollector({ componentType: ComponentType.Button, filter: (i) => i.customId === `unlock-${userId}` && i.user.id === userId, time: 120_000, }); unlockButtonReplyCollector.on( "collect", async (unlockButtonInteraction) => { const emailInput = new TextInputBuilder() .setCustomId(`email-${userId}`) .setLabel("Digite seu email") .setRequired(true) .setMinLength(5) .setStyle(TextInputStyle.Short); const modalEmail = new ModalBuilder() .setCustomId(`modal-${userId}`) .setTitle("Liberação do Canal de Notícias") .setComponents([ new ActionRowBuilder<TextInputBuilder>().addComponents( emailInput ), ]); await unlockButtonInteraction.showModal(modalEmail); const modalEmailInteraction = await unlockButtonInteraction.awaitModalSubmit({ filter: (i) => i.customId === `modal-${userId}` && i.user.id === userId, time: 120_000, }); const email = modalEmailInteraction.fields.getField( `email-${userId}` ); const username = modalEmailInteraction.user.username; const teamSelectReply = await modalEmailInteraction.reply({ components: [ new ActionRowBuilder<StringSelectMenuBuilder>().setComponents( new StringSelectMenuBuilder() .setCustomId(`team-${userId}`) .setPlaceholder("Selecione o seu time do coração!") .setMinValues(1) .setMaxValues(1) .addOptions( teams.map((time) => new StringSelectMenuOptionBuilder() .setLabel(time.label) .setValue(time.value) ) ) ), ], ephemeral: true, // 可选:设置为临时消息仅当前用户可见 }); try { const teamSelectInteraction = await teamSelectReply.awaitMessageComponent({ componentType: ComponentType.StringSelect, filter: (i) => i.customId === `team-${userId}` && i.user.id === userId, time: 120000, }); // 必须完成交互回应,这里选择更新原消息 await teamSelectInteraction.deferUpdate(); // 也可以发送确认消息: // await teamSelectInteraction.reply({ content: `你选择了团队:${teamSelectInteraction.values[0]}`, ephemeral: true }); const team = teamSelectInteraction.values[0]; console.log(team); } catch (error) { console.error("选择菜单交互超时或出错:", error); } } ); } catch (error) { console.log("全局执行错误:", error); } }, };
关键修复说明
- 添加交互确认:在获取选择菜单交互后,调用
deferUpdate()完成平台要求的交互回应,避免超时错误。 - 唯一组件ID:用
interaction.author.id替换client.user.id生成组件CustomID,同时在过滤器中添加用户ID校验,确保交互仅对发起者生效。 - 优化异步逻辑:将
.then().catch()改为try/catch结构,提升代码可读性和错误处理能力。
内容的提问来源于stack exchange,提问作者Luis Henrique Valgoi
相关产品推荐
相关产品推荐

