Discord.js v14 /unban命令开发:嵌入展示与分页按钮实现问题
实现Discord.js v14的分页式/unban斜杠命令
你的需求完全可以实现,下面分步骤解决当前问题并完成分页和解封按钮功能:
一、修复当前仅显示单个用户的问题
你当前代码里硬编码取UserTag[0]和UserID[0],自然只能显示第一个用户;若之前尝试拼接数组时写法错误,会导致仅显示最后一条数据。正确的做法是遍历封禁列表,把所有用户信息拼接成多行文本:
// 替换原有的description设置逻辑 const userList = banList.map(user => `- ${user.user.tag} -> ${user.user.id}`).join('\n'); embed.setColor('Random') .setTitle(`Banned Users in '${guild.name}'`) .setDescription(userList || '暂无封禁用户') .setTimestamp();
二、实现分页功能(每页5位用户)
将封禁列表按每页5条分割,通过「上一页/下一页」按钮切换页面:
- 分割封禁列表:
const banArray = Array.from(banList.values()); const itemsPerPage = 5; const totalPages = Math.ceil(banArray.length / itemsPerPage); let currentPage = 0;
- 封装分页嵌入生成函数:
function generatePageEmbed(page) { const start = page * itemsPerPage; const end = start + itemsPerPage; const pageItems = banArray.slice(start, end); const userList = pageItems.map(user => `- ${user.user.tag} -> ${user.user.id}`).join('\n'); return new EmbedBuilder() .setColor('Random') .setTitle(`Banned Users in '${guild.name}'`) .setDescription(userList || '暂无封禁用户') .setFooter({ text: `第 ${page + 1}/${totalPages} 页` }) .setTimestamp(); }
- 封装分页按钮生成逻辑:
function generatePaginationButtons(page) { const prevButton = new ButtonBuilder() .setCustomId('unban_prev') .setLabel('上一页') .setStyle(ButtonStyle.Primary) .setDisabled(page === 0); const nextButton = new ButtonBuilder() .setCustomId('unban_next') .setLabel('下一页') .setStyle(ButtonStyle.Primary) .setDisabled(page === totalPages - 1); return new ActionRowBuilder().addComponents(prevButton, nextButton); }
三、实现单个用户的解封按钮
为每页的用户生成独立的解封按钮,注意Discord限制每行最多5个按钮,需按规则拆分按钮行:
function generateUnbanButtons(page) { const start = page * itemsPerPage; const end = start + itemsPerPage; const pageItems = banArray.slice(start, end); const buttonRows = []; let currentRow = new ActionRowBuilder(); pageItems.forEach((user, index) => { const unbanButton = new ButtonBuilder() .setCustomId(`unban_${user.user.id}`) .setLabel(`解封 ${user.user.tag}`) .setStyle(ButtonStyle.Danger); currentRow.addComponents(unbanButton); // 每5个按钮或最后一个按钮时,结束当前行 if ((index + 1) % 5 === 0 || index === pageItems.length - 1) { buttonRows.push(currentRow); currentRow = new ActionRowBuilder(); } }); return buttonRows; }
四、整合逻辑并处理按钮交互
将上述模块整合,通过消息组件收集器处理按钮点击事件,实现动态更新:
完整的execute函数示例:
async execute(interaction, client) { const { guild } = interaction; const banList = await guild.bans.fetch(); const banArray = Array.from(banList.values()); const itemsPerPage = 5; let totalPages = Math.ceil(banArray.length / itemsPerPage); let currentPage = 0; // 封装页面更新函数 const updateMessage = async (page) => { const embed = generatePageEmbed(page); const unbanRows = generateUnbanButtons(page); const paginationRow = generatePaginationButtons(page); await interaction.editReply({ embeds: [embed], components: [...unbanRows, paginationRow] }); }; // 生成初始内容 const initialEmbed = generatePageEmbed(currentPage); const initialUnbanRows = generateUnbanButtons(currentPage); const initialPaginationRow = generatePaginationButtons(currentPage); const reply = await interaction.reply({ embeds: [initialEmbed], components: [...initialUnbanRows, initialPaginationRow], fetchReply: true }); // 创建交互收集器,仅允许命令发起者操作,1分钟后超时 const collector = reply.createMessageComponentCollector({ time: 60000, filter: i => i.user.id === interaction.user.id }); collector.on('collect', async i => { if (i.customId === 'unban_prev') { currentPage--; await updateMessage(currentPage); await i.deferUpdate(); } else if (i.customId === 'unban_next') { currentPage++; await updateMessage(currentPage); await i.deferUpdate(); } else if (i.customId.startsWith('unban_')) { // 处理解封操作 const userId = i.customId.split('_')[1]; try { await guild.members.unban(userId); await i.reply({ content: `已成功解封用户 <@${userId}>`, ephemeral: true }); // 更新封禁列表和页面 const newBanList = await guild.bans.fetch(); banArray.splice(banArray.findIndex(u => u.user.id === userId), 1); totalPages = Math.ceil(banArray.length / itemsPerPage); // 当前页为空时自动跳转到上一页 if (banArray.slice(currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage).length === 0 && currentPage > 0) { currentPage--; } await updateMessage(currentPage); } catch (error) { await i.reply({ content: `解封失败:${error.message}`, ephemeral: true }); } } }); // 收集器超时后禁用所有按钮 collector.on('end', () => { reply.edit({ components: reply.components.map(row => row.setComponents(row.components.map(btn => btn.setDisabled(true))) ) }).catch(() => {}); }); // 辅助函数:生成分页嵌入 function generatePageEmbed(page) { const start = page * itemsPerPage; const end = start + itemsPerPage; const pageItems = banArray.slice(start, end); const userList = pageItems.map(user => `- ${user.user.tag} -> ${user.user.id}`).join('\n'); return new EmbedBuilder() .setColor('Random') .setTitle(`Banned Users in '${guild.name}'`) .setDescription(userList || '暂无封禁用户') .setFooter({ text: `第 ${page + 1}/${totalPages} 页` }) .setTimestamp(); } // 辅助函数:生成分页按钮 function generatePaginationButtons(page) { const prevButton = new ButtonBuilder() .setCustomId('unban_prev') .setLabel('上一页') .setStyle(ButtonStyle.Primary) .setDisabled(page === 0); const nextButton = new ButtonBuilder() .setCustomId('unban_next') .setLabel('下一页') .setStyle(ButtonStyle.Primary) .setDisabled(page === totalPages - 1); return new ActionRowBuilder().addComponents(prevButton, nextButton); } // 辅助函数:生成解封按钮 function generateUnbanButtons(page) { const start = page * itemsPerPage; const end = start + itemsPerPage; const pageItems = banArray.slice(start, end); const buttonRows = []; let currentRow = new ActionRowBuilder(); pageItems.forEach((user, index) => { const unbanButton = new ButtonBuilder() .setCustomId(`unban_${user.user.id}`) .setLabel(`解封 ${user.user.tag}`) .setStyle(ButtonStyle.Danger); currentRow.addComponents(unbanButton); if ((index + 1) % 5 === 0 || index === pageItems.length - 1) { buttonRows.push(currentRow); currentRow = new ActionRowBuilder(); } }); return buttonRows; } }
关键注意事项
- 确保Discord.js版本为v14.x,且导入
ActionRowBuilder:
const { SlashCommandBuilder, EmbedBuilder, PermissionsBitField, ButtonBuilder, ButtonStyle, ActionRowBuilder } = require('discord.js');
- 交互收集器限制仅命令发起者操作,避免无关用户误操作;超时后自动禁用按钮,减少无效状态。
- 解封成功后会自动更新封禁列表和页面,保持数据同步。
内容的提问来源于stack exchange,提问作者Joe Mama
相关产品推荐
相关产品推荐

