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

如何用Hikari/Lightbulb实现长Embed消息分页及按钮翻页?

实现Hikari-Lightbulb分页Embed与按钮切换

实现步骤与代码

1. 前置准备

确保已安装hikari和hikari-lightbulb依赖,机器人已开启组件交互权限。

2. 完整代码实现

import hikari
import lightbulb
from lightbulb import plugin

plugin = plugin.Plugin("FruitPagination")

# 每页显示条目数,可根据Discord Embed字段上限(25个)调整
PER_PAGE = 10

@plugin.command
@lightbulb.option('type', 'fruit type', modifier=lightbulb.OptionModifier.CONSUME_REST)
@lightbulb.command('fruit', 'available fruit')
@lightbulb.implements(lightbulb.SlashCommand)
async def lan(ctx: lightbulb.Context) -> None:
    if ctx.options.type != 'fru':
        await ctx.respond("仅支持查询'fru'类型的水果")
        return

    # 读取文件内容,自动关闭文件
    loglist = []
    try:
        with open("fru.txt", "r", encoding="utf8") as file:
            for line in file.readlines():
                line = line.strip()
                if line:  # 跳过空行
                    loglist.append(line)
    except FileNotFoundError:
        await ctx.respond("未找到fru.txt文件")
        return

    if not loglist:
        await ctx.respond("当前没有可显示的水果数据")
        return

    # 拆分内容为分页
    pages = [loglist[i:i+PER_PAGE] for i in range(0, len(loglist), PER_PAGE)]
    total_pages = len(pages)

    # 生成指定页码的Embed
    def get_page_embed(page_num: int) -> hikari.Embed:
        embed = hikari.Embed(title=f"Fruits - 第 {page_num+1}/{total_pages} 页", description="--------------------")
        for item in pages[page_num]:
            embed.add_field(name="-----", value=f"`{item}`", inline=False)
        return embed

    # 生成指定页码的控制按钮
    def get_page_buttons(page_num: int) -> list[hikari.api.MessageActionRowBuilder]:
        prev_disabled = page_num == 0
        next_disabled = page_num == total_pages - 1

        prev_btn = hikari.Button(
            style=hikari.ButtonStyle.PRIMARY,
            label="上一页",
            custom_id=f"fruit_prev_{ctx.user.id}_{page_num}",
            is_disabled=prev_disabled
        )
        next_btn = hikari.Button(
            style=hikari.ButtonStyle.PRIMARY,
            label="下一页",
            custom_id=f"fruit_next_{ctx.user.id}_{page_num}",
            is_disabled=next_disabled
        )

        return [hikari.MessageActionRowBuilder().add_button(prev_btn).add_button(next_btn)]

    # 发送初始分页消息
    await ctx.respond(embed=get_page_embed(0), components=get_page_buttons(0))

# 监听按钮交互事件
@plugin.listener(hikari.InteractionCreateEvent)
async def on_button_click(event: hikari.InteractionCreateEvent) -> None:
    # 仅处理组件按钮交互
    if not isinstance(event.interaction, hikari.ComponentInteraction):
        return

    interaction = event.interaction
    custom_id = interaction.custom_id

    # 过滤非水果分页的按钮
    if not custom_id.startswith("fruit_"):
        return

    # 解析自定义ID中的参数:操作类型、用户ID、当前页码
    parts = custom_id.split("_")
    if len(parts) != 4:
        return

    action, user_id_str, current_page_str = parts[1], parts[2], parts[3]
    try:
        user_id = int(user_id_str)
        current_page = int(current_page_str)
    except ValueError:
        return

    # 仅允许触发命令的用户操作按钮
    if interaction.user.id != user_id:
        await interaction.create_response(
            hikari.ResponseType.MESSAGE_CREATE,
            content="你无权操作此分页按钮",
            flags=hikari.MessageFlag.EPHEMERAL
        )
        return

    # 重新读取数据(如需优化可缓存数据)
    loglist = []
    with open("fru.txt", "r", encoding="utf8") as file:
        for line in file.readlines():
            line = line.strip()
            if line:
                loglist.append(line)
    pages = [loglist[i:i+PER_PAGE] for i in range(0, len(loglist), PER_PAGE)]
    total_pages = len(pages)

    # 计算新页码
    new_page = current_page
    if action == "prev" and current_page > 0:
        new_page -= 1
    elif action == "next" and current_page < total_pages - 1:
        new_page += 1
    else:
        await interaction.create_response(hikari.ResponseType.DEFERRED_MESSAGE_UPDATE)
        return

    # 生成新页面的Embed和按钮
    new_embed = hikari.Embed(title=f"Fruits - 第 {new_page+1}/{total_pages} 页", description="--------------------")
    for item in pages[new_page]:
        new_embed.add_field(name="-----", value=f"`{item}`", inline=False)

    new_prev_disabled = new_page == 0
    new_next_disabled = new_page == total_pages - 1

    new_prev_btn = hikari.Button(
        style=hikari.ButtonStyle.PRIMARY,
        label="上一页",
        custom_id=f"fruit_prev_{user_id}_{new_page}",
        is_disabled=new_prev_disabled
    )
    new_next_btn = hikari.Button(
        style=hikari.ButtonStyle.PRIMARY,
        label="下一页",
        custom_id=f"fruit_next_{user_id}_{new_page}",
        is_disabled=new_next_disabled
    )

    new_components = [hikari.MessageActionRowBuilder().add_button(new_prev_btn).add_button(new_next_btn)]

    # 更新分页消息
    await interaction.edit_response(embed=new_embed, components=new_components)

# 注册插件
def load(bot: lightbulb.BotApp) -> None:
    bot.add_plugin(plugin)

关键说明

  • 分页逻辑:通过列表切片将内容按指定数量拆分页面,适配Discord Embed的字段限制。
  • 权限控制:自定义ID携带用户ID,确保只有触发命令的用户能操作对应分页,避免跨用户误操作。
  • 组件更新:点击按钮后通过edit_response更新消息内容,实现无刷新分页切换。
  • 异常处理:包含文件不存在、空数据等场景的处理,提升机器人稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 03:54:28