discord.py实现分页排行榜 每页限制展示10位用户
discord.py 排行榜分页改造方案
核心改造逻辑
原实现存在两个明显问题:一是全量加载所有用户数据到单条Embed,很容易触碰Discord单Embed 6000字符的上限导致发送失败;二是用list.index()循环查找排名的时间复杂度过高,用户量过百就会出现明显卡顿。
改造直接按以下思路落地:
- 把排序后的资产数据转成列表结构,用
enumerate直接生成排名,去掉低效的索引查找逻辑 - 按每页10条对排序后的列表做切片,拆分出所有分页数据
- 用discord.py原生的View组件实现翻页按钮,自动处理页码边界、超时禁用逻辑
改造后完整代码
首先在文件顶部补充需要的依赖导入:
import math import json import discord from discord.ext import commands
指令部分实现代码:
@commands.command() @commands.guild_only() @commands.cooldown(1, 3, commands.BucketType.guild) async def leaderboard(self, ctx): # 读取服务器用户数据 with open(f'json/servers/{ctx.guild.id}/User.json', 'r') as f: users = json.load(f) # 计算用户总资产并生成有序列表,避免后续反复遍历查索引 total_amt = [] for user_id, user_data in users.items(): total_assets = user_data['MONEY'] + user_data['BANK'] total_amt.append((user_id, total_assets)) # 按资产从高到低排序 leaderboard = sorted(total_amt, key=lambda item: item[1], reverse=True) # 分页参数配置 per_page = 10 total_pages = math.ceil(len(leaderboard) / per_page) current_page = 0 # 按页码生成对应Embed的工具方法 async def gen_page_embed(page_num: int) -> discord.Embed: page_slice = leaderboard[page_num*per_page : (page_num+1)*per_page] em = discord.Embed(title='Top de Dinero', color=0x96edfd) rank_content = [] # 直接用enumerate生成全局排名,不需要反复查列表索引 for global_rank, (user_id, assets) in enumerate(page_slice, start=page_num*per_page + 1): user_obj = self.bot.get_user(int(user_id)) user_show = user_obj.mention if user_obj else 'None' rank_content.append(f'**{global_rank}.** {user_show} **{assets}**') em.description = '\n'.join(rank_content) em.set_footer(text=f'第 {page_num+1}/{total_pages} 页') return em # 翻页交互视图 class LeaderboardView(discord.ui.View): def __init__(self, timeout=60): super().__init__(timeout=timeout) self.page = 0 async def refresh_page(self, interaction: discord.Interaction): # 自动处理按钮禁用状态:第一页禁用上一页,最后一页禁用下一页 self.children[0].disabled = self.page <= 0 self.children[1].disabled = self.page >= total_pages - 1 new_embed = await gen_page_embed(self.page) await interaction.response.edit_message(embed=new_embed, view=self) @discord.ui.button(label='上一页', style=discord.ButtonStyle.primary, disabled=True) async def prev(self, interaction: discord.Interaction, button: discord.ui.Button): self.page -= 1 await self.refresh_page(interaction) @discord.ui.button(label='下一页', style=discord.ButtonStyle.primary) async def next(self, interaction: discord.Interaction, button: discord.ui.Button): self.page += 1 await self.refresh_page(interaction) # 交互超时后自动禁用所有按钮,避免无效点击 async def on_timeout(self): for child in self.children: child.disabled = True await self.message.edit(view=self) # 发送首页内容 init_embed = await gen_page_embed(current_page) view = LeaderboardView() # 总数据不足10条时直接禁用下一页 if total_pages <= 1: view.children[1].disabled = True view.message = await ctx.send(embed=init_embed, view=view)
注意事项
- 上述实现基于discord.py 2.0+版本,如果你使用1.x旧版本,把按钮交互换成
wait_for监听消息反应即可,核心的分页切片逻辑完全通用 - 单服务器用户量超过1000时,建议把JSON存储换成SQLite等轻量数据库,减少文件读写和全量排序的耗时
- 翻页交互默认超时时间为60秒,可根据需求自行调整View初始化时的timeout参数
内容的提问来源于stack exchange,提问作者Anthony
相关产品推荐
相关产品推荐

