如何为discord.py机器人的经验升级系统添加排名功能并排查代码问题
现有代码可优化点
- 重复IO操作:
level_up函数已经接收了已经读取的users参数,无需再次读取users.json文件,额外增加磁盘IO开销 - 无经验获取冷却:用户短时间内连续发送消息可以重复刷取经验,建议添加最短间隔限制,比如60秒内最多获取一次经验
- 数据结构未按服务器隔离:如果你的机器人服务多个服务器,当前存储逻辑会把所有服务器用户的经验混存,排名也会跨服务器,不符合单服务器排名的需求
- 命名不规范:
level命令的Embed标题错误写为balance,变量名bank_amt和实际存储的等级值完全无关,易维护性差 - 未过滤非当前服务器用户:查询排名时会统计所有在JSON中有记录的用户,包括已经退服的用户和其他服务器的用户
排名功能实现方案
我们新增两个命令:
!lb/!排行榜:展示当前服务器经验等级前10的用户!rank/!排名:查询自己或者@指定用户的当前服务器排名
第一步:添加冷却变量和优化原有逻辑
在client = commands.Bot(command_prefix="!")下方新增冷却存储字典:
# 存储用户上次获得经验的时间,单位秒 cd_time = {} # 经验获取冷却秒数 EXP_CD = 60
修改on_message事件,添加冷却判断,同时删掉level_up里多余的读文件操作:
@client.event async def on_message(message): if not message.author.bot: await open_account_level(message.author) users = await get_level_data() # 冷却判断 now = message.created_at.timestamp() last_get = cd_time.get(message.author.id, 0) if now - last_get >= EXP_CD: await add_experience(users, message.author, 5) await level_up(users, message.author, message) cd_time[message.author.id] = now with open('users.json', 'w') as f: json.dump(users, f) await client.process_commands(message) # 优化后的level_up函数,删掉多余读文件逻辑 async def level_up(users, user, message): experience = users[f'{user.id}']['Exp'] lvl_start = users[f'{user.id}']['Exp Level'] lvl_end = int(experience ** (1 / 4)) if lvl_start < lvl_end: await message.channel.send(f'{user.mention} has leveled up to level {lvl_end}') users[f'{user.id}']['Exp Level'] = lvl_end
第二步:新增排名相关命令
直接在代码末尾(TOKEN定义前)添加以下代码即可:
@client.command(aliases=["排行榜"]) async def lb(ctx, show_num: int = 10): # 最多只展示前20名,避免消息太长 show_num = min(show_num, 20) users = await get_level_data() # 筛选当前服务器的有效用户数据 guild_user_data = [] for user_id, data in users.items(): member = ctx.guild.get_member(int(user_id)) if member: guild_user_data.append((-data["Exp Level"], -data["Exp"], member.name, data["Exp Level"], data["Exp"])) # 按等级降序、同等级经验降序排序 guild_user_data.sort() # 生成展示Embed em = discord.Embed(title=f"{ctx.guild.name} 等级排行榜", color=discord.Color.green()) desc = "" for idx, item in enumerate(guild_user_data[:show_num]): desc += f"{idx+1}. {item[2]} | 等级:{item[3]} | 经验:{item[4]}\n" em.description = desc await ctx.send(embed=em) @client.command(aliases=["排名"]) async def rank(ctx, member: discord.Member = None): # 不指定用户时默认查询自己的排名 if not member: member = ctx.author await open_account_level(member) users = await get_level_data() # 筛选当前服务器的有效用户数据 guild_user_data = [] for user_id, data in users.items(): g_member = ctx.guild.get_member(int(user_id)) if g_member: guild_user_data.append((-data["Exp Level"], -data["Exp"], int(user_id))) # 排序 guild_user_data.sort() # 查找目标用户的排名 rank_idx = None for idx, item in enumerate(guild_user_data): if item[2] == member.id: rank_idx = idx + 1 break # 生成展示Embed user_data = users[str(member.id)] em = discord.Embed(title=f"{member.name} 的排名信息", color=discord.Color.blue()) em.add_field(name="当前排名", value=f"{rank_idx}/{len(guild_user_data)}", inline=False) em.add_field(name="当前等级", value=user_data["Exp Level"], inline=True) em.add_field(name="当前经验", value=user_data["Exp"], inline=True) await ctx.send(embed=em)
如果你需要适配多服务器数据隔离,可以把JSON的存储结构调整为按服务器ID分组,对应修改读写逻辑即可实现多服务器数据互不干扰。
内容的提问来源于stack exchange,提问作者Teutonicknight
相关产品推荐
相关产品推荐

