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

Discord.js MAL榜单分页按钮异常问题求助

问题诊断

你的分页按钮问题主要源于三个核心问题:

  1. 按钮交互时未正确传递/获取当前页面状态,导致页码计算逻辑错误
  2. 未使用interaction.update()更新原消息,而是采用了错误的响应方式,导致Embed无法替换
  3. 未对页码边界做限制,且按钮未根据当前页码做禁用控制
修复步骤
  1. 给按钮自定义ID携带当前页码参数,确保点击时能准确获取当前页
  2. 修正页码计算逻辑,添加边界限制(上不低于1,下不超过最大页数)
  3. 使用interaction.update()直接更新原消息的Embed和组件
  4. 根据当前页码自动禁用无效按钮(第一页禁用上一页,最后一页禁用下一页)
完整修复代码示例

命令核心代码

const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const { fetchMALTopAnime } = require('./your-scraper-module'); // 替换为你的爬虫模块路径

module.exports = {
    data: new SlashCommandBuilder()
        .setName('top')
        .setDescription('查看MAL Top10动漫榜单')
        .addIntegerOption(option =>
            option.setName('page')
                .setDescription('页码(1-5)')
                .setMinValue(1)
                .setMaxValue(5)),
    async execute(interaction) {
        let currentPage = interaction.options.getInteger('page') || 1;
        const maxPages = 5; // MAL Top50,每页10条,共5页

        // 获取当前页榜单数据
        const animeList = await fetchMALTopAnime(currentPage);
        if (!animeList) return interaction.reply('获取榜单失败,请稍后重试');

        // 构建榜单Embed
        const buildEmbed = (page) => {
            const embed = new EmbedBuilder()
                .setTitle(`MAL Top10动漫榜单 - 第${page}/${maxPages}页`)
                .setColor('#FF9EAA')
                .setDescription(
                    animeList.map((anime, idx) => 
                        `${(page-1)*10 + idx + 1}. **${anime.title}**\n评分: ${anime.score}`
                    ).join('\n\n')
                );
            return embed;
        };

        // 构建分页按钮行
        const buildButtonRow = (page) => {
            const prevBtn = new ButtonBuilder()
                .setCustomId(`top_prev_${page}`)
                .setLabel('上一页')
                .setStyle(ButtonStyle.Secondary)
                .setDisabled(page === 1);

            const nextBtn = new ButtonBuilder()
                .setCustomId(`top_next_${page}`)
                .setLabel('下一页')
                .setStyle(ButtonStyle.Secondary)
                .setDisabled(page === maxPages);

            return new ActionRowBuilder().addComponents(prevBtn, nextBtn);
        };

        // 发送初始消息
        const replyMsg = await interaction.reply({
            embeds: [buildEmbed(currentPage)],
            components: [buildButtonRow(currentPage)],
            fetchReply: true
        });

        // 创建按钮交互收集器(1分钟超时)
        const collector = replyMsg.createMessageComponentCollector({ time: 60000 });

        collector.on('collect', async (btnInteraction) => {
            // 仅允许命令发起者操作按钮
            if (btnInteraction.user.id !== interaction.user.id) {
                return btnInteraction.reply({ content: '只有发起命令的用户可以操作按钮!', ephemeral: true });
            }

            // 解析按钮ID中的当前页码
            const [_, action, pageStr] = btnInteraction.customId.split('_');
            let newPage = parseInt(pageStr);

            // 计算新页码并限制边界
            if (action === 'prev') {
                newPage = Math.max(newPage - 1, 1);
            } else if (action === 'next') {
                newPage = Math.min(newPage + 1, maxPages);
            }

            // 获取新页面数据
            const newAnimeList = await fetchMALTopAnime(newPage);
            if (!newAnimeList) {
                return btnInteraction.reply({ content: '获取新页面数据失败', ephemeral: true });
            }

            // 更新原消息的Embed和按钮
            await btnInteraction.update({
                embeds: [buildEmbed(newPage)],
                components: [buildButtonRow(newPage)]
            });
        });

        // 收集器超时后禁用所有按钮
        collector.on('end', () => {
            const disabledRow = new ActionRowBuilder().addComponents(
                new ButtonBuilder().setCustomId('top_prev_disabled').setLabel('上一页').setStyle(ButtonStyle.Secondary).setDisabled(true),
                new ButtonBuilder().setCustomId('top_next_disabled').setLabel('下一页').setStyle(ButtonStyle.Secondary).setDisabled(true)
            );
            interaction.editReply({ components: [disabledRow] }).catch(() => {});
        });
    },
};

爬虫函数优化(确保分页数据正确)

const axios = require('axios');
const cheerio = require('cheerio');

async function fetchMALTopAnime(page = 1) {
    try {
        const limit = (page - 1) * 10;
        const response = await axios.get(`https://myanimelist.net/topanime.php?limit=${limit}`);
        const $ = cheerio.load(response.data);

        const animeList = [];
        $('.ranking-list').each((idx, el) => {
            const title = $(el).find('.hoverinfo_trigger.fw-b.fl-l').text().trim();
            const score = $(el).find('.score-label').text().trim();
            animeList.push({ title, score });
        });

        return animeList.slice(0, 10); // 确保仅返回当前页的10条数据
    } catch (err) {
        console.error('MAL榜单爬取失败:', err);
        return null;
    }
}

module.exports = { fetchMALTopAnime };
关键修复说明
  • 自定义ID带页码:按钮ID包含当前页码,避免交互时丢失页面状态,彻底解决上一页跳1、下一页无响应的问题
  • 页码边界控制:用Math.max和Math.min确保页码始终在1~5范围内,防止越界
  • 使用update更新消息:直接修改原消息内容,而非发送新消息,解决Embed无法更新的问题
  • 按钮状态控制:自动禁用无效按钮,避免用户点击无意义操作
  • 权限验证:仅允许命令发起者操作按钮,防止无关用户干扰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:15:21