Discord Bot命令读取JSON文件等级数据的问题排查
问题说明
你已知晓JSON不适合作为生产数据库,本次仅为测试JSON格式的应用可行性,不会长期使用JSON存储数据。本次开发目标是适配多服务器的Discord等级机器人,需要实现用户查询自身等级的命令,用户ID、等级、累计经验值存储在本地users.json文件中,当前自行编写的代码无法正常运行,以下是具体问题排查、修复方案和通用开发优化建议。
代码问题排查
你的代码存在以下直接导致运行失败的问题:
- 命令参数定义错误:
!level命令的函数除了框架自动传入的上下文ctx外,额外定义了users/user/channel三个参数,Discord命令框架会默认将用户输入的命令内容按顺序匹配这些参数,用户仅输入!level时会直接报参数缺失错误。实际上ctx.author就是调用命令的用户,ctx.channel就是当前命令所在频道,不需要额外作为参数声明。 - 缺少命令处理调用:自定义
on_message事件监听后,没有添加await client.process_commands(message),会导致所有前缀命令被事件拦截,根本不会触发命令逻辑,这是!level无响应的核心原因之一。 - 重复调用
client.run():代码末尾写了两次client.run(),第一次调用后程序就会进入阻塞状态,第二次代码永远不会执行,两种读取环境变量的逻辑重复,保留一个即可。 - 缩进错误:
on_message事件中更新用户发言冷却时间的member_messages[message.author.id] = current_time被错误缩进在json.dump的文件写入代码块内,逻辑位置错误,会导致冷却时间更新异常。 - 缺少机器人消息过滤:
on_message事件没有判断消息发送者是否为机器人,会导致机器人自己发消息时给自己涨经验,逻辑错误。 - 缺少意图配置:新版discord.py默认关闭特权意图,没有配置
intents的情况下,on_member_join、消息内容相关事件根本无法被接收,机器人无法正常工作。 - 缺少异常和边界判断:
- 第一次运行时如果
users.json不存在,直接读取会报文件不存在错误 - 查询等级时如果用户没有在JSON中存储数据,
level_information为None,直接调用.get('level')会抛出属性错误导致命令崩溃 - 读写文件没有指定
utf-8编码,Windows环境下容易出现乱码或读取失败
- 第一次运行时如果
- 无用导入和笔误:导入了
random模块但全程未使用;add_experience函数的形参exprience拼写错误(正确为experience),虽不影响运行但属于代码瑕疵。
修复后完整代码
from discord.ext import commands from time import time import json import discord import os # 配置意图,需要提前在Discord开发者后台开启对应特权意图 intents = discord.Intents.default() intents.members = True intents.message_content = True client = commands.Bot(command_prefix="!", intents=intents) # 启动时初始化/加载用户数据到内存,避免频繁读写文件 if not os.path.exists("users.json"): with open("users.json", "w", encoding="utf-8") as f: json.dump({}, f) with open("users.json", "r", encoding="utf-8") as f: users_data = json.load(f) # 发言冷却记录 member_messages = {} async def update_data(user): """更新用户数据,不存在则初始化""" user_id = str(user.id) if user_id not in users_data: users_data[user_id] = { "experience": 0, "level": 1 } async def add_experience(user, exp): """给用户增加经验""" user_id = str(user.id) users_data[user_id]["experience"] += exp async def level_up(user, channel): """检查升级并发送通知""" user_id = str(user.id) experience = users_data[user_id]["experience"] level_start = users_data[user_id]["level"] level_end = int(experience ** (1/4)) if level_start < level_end: await channel.send(f"{user.mention} Has Leveled Up! They Have Leveled Up To Level {level_end}!") users_data[user_id]["level"] = level_end # 数据变更后写回文件 with open("users.json", "w", encoding="utf-8") as f: json.dump(users_data, f, indent=2) @client.listen() async def on_ready(): print("Bot Ready.") print(f"Logged In As: {client.user}") @client.listen() async def on_member_join(member): await update_data(member) with open("users.json", "w", encoding="utf-8") as f: json.dump(users_data, f, indent=2) @client.listen() async def on_message(message): # 过滤机器人自己发的消息 if message.author.bot: return # 必须加这行,否则前缀命令无法正常触发 await client.process_commands(message) global member_messages user_id = message.author.id if user_id not in member_messages: member_messages[user_id] = 0 current_time = time() # 2秒发言冷却,防止刷经验 cooldown_time = current_time - 2 if member_messages[user_id] <= cooldown_time: await update_data(message.author) await add_experience(message.author, 5) await level_up(message.author, message.channel) member_messages[user_id] = current_time @client.command(name="level") async def get_level(ctx): """查询自身等级命令""" user_id = str(ctx.author.id) # 确保用户数据存在 await update_data(ctx.author) level = users_data[user_id]["level"] exp = users_data[user_id]["experience"] await ctx.send(f"{ctx.author.mention} Is Level {level}, Current EXP: {exp}.") # 按自己的环境变量配置选一种即可 client.run(os.getenv("Token"))
Discord机器人通用开发优化建议
- 提前配置特权意图:在Discord开发者后台对应机器人的「Bot」页面,下拉找到「Privileged Gateway Intents」板块,开启
Server Members Intent和Message Content Intent,否则成员事件、消息读取事件都无法正常接收,代码里的intents配置要和后台开启的选项对应。 - 减少高频文件IO:不要每次消息触发、每次命令调用都开关读写JSON文件,启动时把数据加载到内存,数据变更时再写回文件,也可以加定时自动存盘逻辑,避免程序意外崩溃丢失数据,同时降低磁盘占用。
- 多服务器数据隔离:如果需要每个服务器的等级独立不互通,存储用户数据的key不要只用用户ID,要改成
{服务器ID}_{用户ID}的格式,避免跨服数据互通不符合需求。 - 添加全局错误处理:给命令、事件都加异常捕获,比如JSON文件损坏、权限不足无法发送消息的情况,不要让机器人直接崩溃,输出错误日志方便排查问题。
- 冷却逻辑优化:当前用内存字典存发言冷却,机器人重启后冷却会重置,如果对冷却要求严格可以把冷却时间也持久化存储;命令类的冷却可以直接用discord.py自带的
@commands.cooldown()装饰器实现,不用自己手写逻辑。 - 调整等级公式:当前用经验开四次方的升级公式,后期升级需要的经验会指数级上涨,可以根据需求调整成线性升级(比如每级固定需要100经验)或者自定义成长曲线,用户体验更好。
- 增加权限校验:后续添加管理类命令(比如给用户加经验、重置等级)的时候,一定要加权限判断,只允许服务器管理员使用这类命令,避免普通用户恶意修改数据。
内容的提问来源于stack exchange,提问作者Broski Man
相关产品推荐
相关产品推荐

