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

Discord JS多模态交互报错及方法缺失问题求助

问题解决:Discord连续模态框员工申请流程

错误1:ValidationError: Expected the value to be a string or number

原因

代码中question1文本输入框未设置setStyle()方法,TextInputStyle是必填枚举参数,未传入时会使用undefined,触发Shapeshift验证失败。

修复

给question1补充样式设置:

const question1 = new TextInputBuilder()
  .setCustomId('question1')
  .setLabel('Do you have a working mic?')
  .setPlaceholder('Yes or no')
  .setStyle(TextInputStyle.Short) // 新增此行
  .setRequired(true);

错误2:连续模态框交互逻辑问题

关于i.awaitModalSubmit()不存在

该方法属于discord.js v14+的客户端级收集器方法,正确调用方式为client.awaitModalSubmit(),而非直接在交互对象上调用。

关于ModalSubmitInteraction无showModal()方法

Discord交互规则限制:模态框必须由用户主动触发的交互(如按钮、选择菜单)展示,ModalSubmitInteraction是用户提交模态框后的回调对象,无法直接调用showModal。必须先完成当前交互的响应(回复/延迟回复),再通过其他交互触发下一个模态框。

重构后的完整实现代码

collector.on('collect', async i => {
  if (i.customId !== 'applications') return;

  // 定义所有文本输入框(修复question1的style问题)
  const roleInput = new TextInputBuilder()
    .setCustomId('roleInput')
    .setLabel('Role')
    .setPlaceholder('Developer, Moderator, Builder, etc.')
    .setStyle(TextInputStyle.Short)
    .setRequired(true);

  const ignInput = new TextInputBuilder()
    .setCustomId('ignInput')
    .setLabel('Minecraft Username')
    .setStyle(TextInputStyle.Short)
    .setRequired(true)
    .setPlaceholder('Username');

  const question1 = new TextInputBuilder()
    .setCustomId('question1')
    .setLabel('Do you have a working mic?')
    .setPlaceholder('Yes or no')
    .setStyle(TextInputStyle.Short) // 修复此处
    .setRequired(true);

  const question2 = new TextInputBuilder()
    .setCustomId('question3')
    .setLabel('How much time can you spend per week?')
    .setStyle(TextInputStyle.Short)
    .setRequired(true)
    .setPlaceholder('___ hours');

  const question3 = new TextInputBuilder()
    .setCustomId('question4')
    .setLabel('Why should we choose you?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question4 = new TextInputBuilder()
    .setCustomId('question5')
    .setLabel('Why do you want to be staff?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question5 = new TextInputBuilder()
    .setCustomId('question6')
    .setLabel('What staff experience do you have?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question6 = new TextInputBuilder()
    .setCustomId('question7')
    .setLabel('What would you do if someone spammed chat?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question7 = new TextInputBuilder()
    .setCustomId('question8')
    .setLabel('What if someone impersonated staff?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question8 = new TextInputBuilder()
    .setCustomId('question9')
    .setLabel('What if someone was bigoted in chat?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question9 = new TextInputBuilder()
    .setCustomId('question10')
    .setLabel('What if someone were hacking/exploiting?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question10 = new TextInputBuilder()
    .setCustomId('question11')
    .setLabel('What if someone doxxed another user?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question11 = new TextInputBuilder()
    .setCustomId('question12')
    .setLabel('What if you saw someone abusing staff?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(true)
    .setPlaceholder('Paragraph');

  const question12 = new TextInputBuilder()
    .setCustomId('question13')
    .setLabel('Anything else you want us to know?')
    .setStyle(TextInputStyle.Paragraph)
    .setRequired(false)
    .setPlaceholder('Paragraph');

  // 构建三个模态框
  const modal1 = new ModalBuilder()
    .setTitle('Staff Application - Part 1')
    .setCustomId('applicationModal1')
    .addComponents(
      new ActionRowBuilder().addComponents(roleInput),
      new ActionRowBuilder().addComponents(ignInput),
      new ActionRowBuilder().addComponents(question1),
      new ActionRowBuilder().addComponents(question2),
      new ActionRowBuilder().addComponents(question3)
    );

  const modal2 = new ModalBuilder()
    .setTitle('Staff Application - Part 2')
    .setCustomId('applicationModal2')
    .addComponents(
      new ActionRowBuilder().addComponents(question4),
      new ActionRowBuilder().addComponents(question5),
      new ActionRowBuilder().addComponents(question6),
      new ActionRowBuilder().addComponents(question7),
      new ActionRowBuilder().addComponents(question8)
    );

  const modal3 = new ModalBuilder()
    .setTitle('Staff Application - Part 3')
    .setCustomId('applicationModal3')
    .addComponents(
      new ActionRowBuilder().addComponents(question9),
      new ActionRowBuilder().addComponents(question10),
      new ActionRowBuilder().addComponents(question11),
      new ActionRowBuilder().addComponents(question12)
    );

  // 存储用户的申请数据
  const applicationData = {};

  // 展示第一个模态框
  await i.showModal(modal1);

  // 监听第一个模态框提交
  const firstSubmit = await client.awaitModalSubmit({
    filter: int => int.customId === 'applicationModal1' && int.user.id === i.user.id,
    time: 600000 // 10分钟超时
  }).catch(() => null);

  if (!firstSubmit) {
    return i.followUp({ content: '申请超时,请重新发起。', ephemeral: true });
  }

  // 保存第一部分数据
  applicationData.role = firstSubmit.fields.getTextInputValue('roleInput');
  applicationData.ign = firstSubmit.fields.getTextInputValue('ignInput');
  applicationData.q1 = firstSubmit.fields.getTextInputValue('question1');
  applicationData.q2 = firstSubmit.fields.getTextInputValue('question3');
  applicationData.q3 = firstSubmit.fields.getTextInputValue('question4');

  // 回复第一个提交,然后触发第二个模态框
  await firstSubmit.deferReply({ ephemeral: true });
  // 创建临时按钮触发第二个模态框
  const nextButton = new ButtonBuilder()
    .setCustomId('nextToModal2')
    .setLabel('继续填写第二部分')
    .setStyle(ButtonStyle.Primary);

  await firstSubmit.followUp({
    components: [new ActionRowBuilder().addComponents(nextButton)],
    ephemeral: true
  });

  // 监听按钮点击,展示第二个模态框
  const buttonClick2 = await client.awaitMessageComponent({
    filter: int => int.customId === 'nextToModal2' && int.user.id === i.user.id,
    time: 600000
  }).catch(() => null);

  if (!buttonClick2) {
    return firstSubmit.followUp({ content: '申请超时,请重新发起。', ephemeral: true });
  }

  await buttonClick2.showModal(modal2);

  // 监听第二个模态框提交
  const secondSubmit = await client.awaitModalSubmit({
    filter: int => int.customId === 'applicationModal2' && int.user.id === i.user.id,
    time: 600000
  }).catch(() => null);

  if (!secondSubmit) {
    return buttonClick2.followUp({ content: '申请超时,请重新发起。', ephemeral: true });
  }

  // 保存第二部分数据
  applicationData.q4 = secondSubmit.fields.getTextInputValue('question5');
  applicationData.q5 = secondSubmit.fields.getTextInputValue('question6');
  applicationData.q6 = secondSubmit.fields.getTextInputValue('question7');
  applicationData.q7 = secondSubmit.fields.getTextInputValue('question8');
  applicationData.q8 = secondSubmit.fields.getTextInputValue('question9');

  // 回复第二个提交,触发第三个模态框
  await secondSubmit.deferReply({ ephemeral: true });
  const nextButton3 = new ButtonBuilder()
    .setCustomId('nextToModal3')
    .setLabel('继续填写第三部分')
    .setStyle(ButtonStyle.Primary);

  await secondSubmit.followUp({
    components: [new ActionRowBuilder().addComponents(nextButton3)],
    ephemeral: true
  });

  // 监听按钮点击,展示第三个模态框
  const buttonClick3 = await client.awaitMessageComponent({
    filter: int => int.customId === 'nextToModal3' && int.user.id === i.user.id,
    time: 600000
  }).catch(() => null);

  if (!buttonClick3) {
    return secondSubmit.followUp({ content: '申请超时,请重新发起。', ephemeral: true });
  }

  await buttonClick3.showModal(modal3);

  // 监听第三个模态框提交
  const thirdSubmit = await client.awaitModalSubmit({
    filter: int => int.customId === 'applicationModal3' && int.user.id === i.user.id,
    time: 600000
  }).catch(() => null);

  if (!thirdSubmit) {
    return buttonClick3.followUp({ content: '申请超时,请重新发起。', ephemeral: true });
  }

  // 保存第三部分数据
  applicationData.q9 = thirdSubmit.fields.getTextInputValue('question10');
  applicationData.q10 = thirdSubmit.fields.getTextInputValue('question11');
  applicationData.q11 = thirdSubmit.fields.getTextInputValue('question12');
  applicationData.q12 = thirdSubmit.fields.getTextInputValue('question13');

  // 完成申请,回复用户
  await thirdSubmit.reply({ content: '你的申请已提交,正在审核中。', ephemeral: true });

  // 构建并发送审核嵌入
  const applicationEmbed = new EmbedBuilder()
    .setTitle('员工申请')
    .setDescription(`申请人:${i.user}`)
    .setAuthor({ name: i.user.tag, iconURL: i.user.displayAvatarURL() })
    .setThumbnail(i.user.displayAvatarURL())
    .addFields(
      { name: '申请职位', value: applicationData.role },
      { name: 'Minecraft用户名', value: applicationData.ign },
      { name: '你是否有可用的麦克风?', value: applicationData.q1 },
      { name: '每周可投入时长', value: applicationData.q2 },
      { name: '我们为什么选择你?', value: applicationData.q3 },
      { name: '你为什么想成为员工?', value: applicationData.q4 },
      { name: '你有过哪些员工经验?', value: applicationData.q5 },
      { name: '遇到刷屏用户你会怎么做?', value: applicationData.q6 },
      { name: '遇到冒充员工的用户你会怎么做?', value: applicationData.q7 },
      { name: '遇到发表仇恨言论的用户你会怎么做?', value: applicationData.q8 },
      { name: '遇到作弊/利用漏洞的用户你会怎么做?', value: applicationData.q9 },
      { name: '遇到人肉其他用户的用户你会怎么做?', value: applicationData.q10 },
      { name: '遇到滥用员工权限的人你会怎么做?', value: applicationData.q11 },
      { name: '其他想告诉我们的内容', value: applicationData.q12 || '无' }
    )
    .setColor(0xc50549);

  await applicationChannel.send({ embeds: [applicationEmbed] });
  console.log('收到新的员工申请');
});

代码优化点

  1. 修复了question1缺失样式的验证错误。
  2. 使用客户端级收集器替代嵌套的全局监听器,避免内存泄漏和重复触发。
  3. 遵循Discord交互规则,通过临时按钮触发后续模态框,解决ModalSubmitInteraction无法直接展示模态框的问题。
  4. 统一存储用户输入数据,避免跨交互引用字段的混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:30:54