如何让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);
系统设计优化建议
- 用数据库存储请求数据:不要仅依赖Embed Footer存储用户ID,可使用SQLite/MongoDB存储请求ID、用户ID、提交内容、处理状态等,便于追溯和管理。
- 完善通知机制:若用户DM关闭,可在服务器专属通知频道发送隐私友好的通知。
- 添加请求过期逻辑:对长时间未处理的请求自动标记过期,清理频道冗余内容。
- 增加审批留痕:在处理后的Embed中添加审批人信息,便于管理审计。
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

