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

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条分割,通过「上一页/下一页」按钮切换页面:

  1. 分割封禁列表:
const banArray = Array.from(banList.values());
const itemsPerPage = 5;
const totalPages = Math.ceil(banArray.length / itemsPerPage);
let currentPage = 0;
  1. 封装分页嵌入生成函数:
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();
}
  1. 封装分页按钮生成逻辑:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:02:03