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

如何让discord.js验证按钮与填写模态框的用户交互?

问题

我正在自学discord.js开发Discord机器人,遇到验证流程的实现问题:

  • 验证频道有requestVerification按钮,点击后弹出模态框
  • 用户提交模态框内容后,数据以Embed形式发送到请求频道,附带审批、拒绝按钮
  • 需求:点击这两个按钮时,要对提交模态框的用户(而非点击按钮的管理员)执行操作(如添加验证角色、发送通知),当前无法关联操作目标用户

现有实现代码:
verificationButtonEvent.js(模态框触发逻辑)

const {
  Events, ActionRowBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, EmbedBuilder, PermissionsBitField
} = require("discord.js");

module.exports = {
  name: Events.InteractionCreate, execute(interaction) {
    if (!interaction.isButton()) return;

    switch (interaction.customId) {
      case "requestVerification": {
        // create the modal
        const verificationModal = new ModalBuilder()
          .setCustomId("verificationModal")
          .setTitle("");

        // verified member role
        const verifiedMemberRole = interaction.guild.roles.cache.get("");

        // nickname text input
        const nicknameInput = new TextInputBuilder()
          .setCustomId("nicknameInput")
          .setLabel()
          .setStyle(TextInputStyle.Short)
          .setPlaceholder()
          .setMaxLength(35)
          .setMinLength(7);

        // source text input
        const sourceInput = new TextInputBuilder()
          .setCustomId("sourceInput")
          .setLabel()
          .setStyle(TextInputStyle.Paragraph)
          .setPlaceholder()
          .setMaxLength(150)
          .setMinLength(10);

        // action rows
        const nicknameRow = new ActionRowBuilder().addComponents(nicknameInput);
        const sourceRow = new ActionRowBuilder().addComponents(sourceInput);
        verificationModal.addComponents(nicknameRow, sourceRow);

        // denied verification message
        const deniedVerificationMessage = new EmbedBuilder()
          .setTitle()
          .setDescription()
          .setColor(0xf08080)
          .setTimestamp()
          .setFooter({
            text: `${interaction.client.user.username}`, iconURL: `${interaction.client.user.avatarURL()}`,
          });

        // check if interaction.member is viable to show the modal
        if (interaction.member.roles.cache.has(verifiedMemberRole)) {
          interaction.reply({embeds: [deniedVerificationMessage], ephemeral: true})
        } else if (interaction.member.permissions.has(PermissionsBitField.Flags.ManageRoles) || interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) {
          interaction.reply({embeds: [deniedVerificationMessage], ephemeral: true})
        } else {
          interaction.showModal(verificationModal);
        }

        break;
      }
    }
  },
};

verificationModalEvent.js(模态框提交处理)

const {Events, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle} = require("discord.js");

module.exports = {
    name: Events.InteractionCreate, async execute(interaction) {
        if (!interaction.isModalSubmit()) return;

        switch (interaction.customId) {
            case "verificationModal": {
                // verified member role
                const verifiedMemberRole = interaction.guild.roles.cache.get("");

                // verification requests channel
                const verificationRequestsChannel = interaction.client.channels.cache.get("");

                // text inputs
                const memberNickname = interaction.fields.getTextInputValue("nicknameInput");
                const memberSource = interaction.fields.getTextInputValue("sourceInput");

                // message that is shown to the user when submitting the modal
                const requestedVerificationEmbed = new EmbedBuilder()
                    .setTitle()
                    .setDescription()
                    .setColor(0xffffb9)
                    .setTimestamp()
                    .setFooter({
                        text: `${interaction.client.user.username}`, iconURL: `${interaction.client.user.avatarURL()}`,
                    });
                    
                // verification request sent to the channel (verificationRequestsChannel)
                const requestVerificationEmbed = new EmbedBuilder()
                    .setTitle()
                    .setDescription()
                    .addFields(
                        {name: "", value: memberNickname, inline: true},
                        {name: "", value: memberSource, inline: true}
                    )
                    .setColor(0xffffb9)
                    .setTimestamp()
                    .setFooter({
                        text: `ID: ${interaction.member.user.id}`, iconURL: `${interaction.client.user.avatarURL()}`,
                    });

                // buttons for approving or denying user's verification
                const requestVerificationButtons = new ActionRowBuilder().addComponents(
                    new ButtonBuilder()
                        .setCustomId("approveVerification")
                        .setLabel("Approve")
                        .setEmoji("✅")
                        .setStyle(ButtonStyle.Success),
                    new ButtonBuilder()
                        .setCustomId("denyVerification")
                        .setLabel("Deny")
                        .setEmoji("❎")
                        .setStyle(ButtonStyle.Danger)
                )

                await verificationRequestsChannel.send({
                    embeds: [requestVerificationEmbed],
                    components: [requestVerificationButtons]
                })

                // await interaction.member.setNickname(memberNickname);
                await interaction.reply({
                    embeds: [requestedVerificationEmbed], ephemeral: true
                })

                break;
            }
        }
    }
}

解决方案

核心思路是将提交用户的ID嵌入到审批/拒绝按钮的customId中,点击按钮时即可提取目标用户身份,执行对应交互操作。

步骤1:修改模态框提交代码,给按钮添加用户ID标识

在verificationModalEvent.js中,修改按钮的customId,拼接提交用户的ID:

// 替换原按钮customId代码
const requestVerificationButtons = new ActionRowBuilder().addComponents(
    new ButtonBuilder()
        .setCustomId(`approveVerification_${interaction.member.user.id}`)
        .setLabel("Approve")
        .setEmoji("✅")
        .setStyle(ButtonStyle.Success),
    new ButtonBuilder()
        .setCustomId(`denyVerification_${interaction.member.user.id}`)
        .setLabel("Deny")
        .setEmoji("❎")
        .setStyle(ButtonStyle.Danger)
)

步骤2:新增审批按钮处理逻辑文件

创建verificationApprovalButtons.js,处理按钮点击事件:

const { Events, EmbedBuilder, PermissionsBitField } = require("discord.js");

module.exports = {
    name: Events.InteractionCreate,
    async execute(interaction) {
        if (!interaction.isButton()) return;

        // 匹配审批/拒绝按钮
        if (!interaction.customId.startsWith("approveVerification_") && !interaction.customId.startsWith("denyVerification_")) return;

        // 权限校验:仅管理员/有权限角色可操作
        if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator) && !interaction.member.permissions.has(PermissionsBitField.Flags.ManageRoles)) {
            return interaction.reply({ content: "无操作权限", ephemeral: true });
        }

        // 提取目标用户ID
        const userId = interaction.customId.split("_")[1];
        const targetMember = await interaction.guild.members.fetch(userId).catch(() => null);
        
        if (!targetMember) {
            return interaction.reply({ content: "目标用户已离开服务器", ephemeral: true });
        }

        const verifiedRole = interaction.guild.roles.cache.get("你的验证角色ID"); // 替换为实际角色ID
        let responseEmbed;

        // 审批逻辑
        if (interaction.customId.startsWith("approveVerification_")) {
            await targetMember.roles.add(verifiedRole);
            // 发送DM通知(失败则忽略)
            await targetMember.send({ content: "你的验证请求已通过!" }).catch(() => {});
            
            responseEmbed = new EmbedBuilder()
                .setTitle("验证通过")
                .setDescription(`已为 <@${userId}> 添加验证角色`)
                .setColor(0x57f287)
                .setTimestamp();
        } 
        // 拒绝逻辑
        else {
            await targetMember.send({ content: "你的验证请求已被拒绝,请检查提交信息是否符合要求。" }).catch(() => {});
            
            responseEmbed = new EmbedBuilder()
                .setTitle("验证拒绝")
                .setDescription(`已拒绝 <@${userId}> 的验证请求`)
                .setColor(0xed4245)
                .setTimestamp();
        }

        // 禁用按钮防止重复操作
        await interaction.update({
            components: [interaction.message.components[0].setComponents(
                interaction.message.components[0].components.map(btn => btn.setDisabled(true))
            )],
            embeds: [responseEmbed]
        });
    }
};

步骤3:注册新事件文件

在机器人启动文件(如index.js)中导入并注册该事件:

const verificationApprovalButtons = require("./events/verificationApprovalButtons");
client.on(Events.InteractionCreate, verificationApprovalButtons.execute);

系统设计优化建议

  1. 用数据库存储请求数据:不要仅依赖Embed Footer存储用户ID,可使用SQLite/MongoDB存储请求ID、用户ID、提交内容、处理状态等,便于追溯和管理。
  2. 完善通知机制:若用户DM关闭,可在服务器专属通知频道发送隐私友好的通知。
  3. 添加请求过期逻辑:对长时间未处理的请求自动标记过期,清理频道冗余内容。
  4. 增加审批留痕:在处理后的Embed中添加审批人信息,便于管理审计。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:42:35