discord.py警告系统warn命令触发KeyError报错问题排查
报错根因
触发KeyError: '472100676407656448'是因为你在累加用户警告数时,没有提前校验该用户ID是否存在于warns.json存储的字典中:第一次给某个用户发警告时,字典里没有对应用户ID的键,直接执行+=1操作就会抛出键不存在的错误。
除此之外你的代码还有几个会影响运行的隐性问题:
warns_check函数直接返回了全量警告字典,没有提取单个用户的警告数值,调用warnings命令时会输出所有用户的警告数据,不符合预期remove_warn函数同样没有做键存在校验,也没有限制扣减后的警告数不能小于0,会触发同类KeyError,或者出现负数警告数的逻辑错误- 没有处理
warns.json文件不存在、内容为空的初始化场景,第一次运行代码读文件就会报错 - 三个工具函数传入的
ctx参数全程没有被使用,属于冗余传参 - 没有处理用户关闭私信、操作对象是机器人/操作者本人、扣减警告数为负数等边界场景,容易触发异常
修复后代码
import json import os import discord from discord.ext import commands # 首次运行自动初始化警告存储文件 if not os.path.exists('warns.json'): with open('warns.json', 'w', encoding='utf-8') as f: json.dump({}, f) def save_warn(member: discord.Member): with open('warns.json', 'r', encoding='utf-8') as f: warns = json.load(f) user_id = str(member.id) # 无该用户警告记录时默认从0开始累加 warns[user_id] = warns.get(user_id, 0) + 1 with open('warns.json', 'w', encoding='utf-8') as f: json.dump(warns, f, indent=2) def remove_warn(member: discord.Member, amount: int): with open('warns.json', 'r', encoding='utf-8') as f: warns = json.load(f) user_id = str(member.id) current_warns = warns.get(user_id, 0) # 扣减后警告数最小为0,不出现负数 warns[user_id] = max(current_warns - amount, 0) with open('warns.json', 'w', encoding='utf-8') as f: json.dump(warns, f, indent=2) def warns_check(member: discord.Member): with open('warns.json', 'r', encoding='utf-8') as f: warns = json.load(f) user_id = str(member.id) # 仅返回当前查询用户的警告数,无记录时返回0 return warns.get(user_id, 0) @bot.command() @commands.has_permissions(kick_members=True) async def warn(ctx, member: discord.Member, *, reason): # 拦截无效操作 if member.bot or member == ctx.author: await ctx.send("无法对该用户执行警告操作") return save_warn(member) dm = await bot.fetch_user(member.id) em = discord.Embed(title="服务器警告", description=f"来源服务器: {ctx.guild.name}\n警告原因: {reason}") # 处理用户关闭私信的场景,避免抛错 try: await dm.send(embed=em) except discord.Forbidden: pass await ctx.send(f"已给 {member.mention} 记1次警告,原因:{reason}") @bot.command() @commands.has_permissions(kick_members=True) async def warnings(ctx, member: discord.Member): warn_count = warns_check(member) await ctx.send(f"{member.name} 当前共有 {warn_count} 次警告。") @bot.command() @commands.has_permissions(kick_members=True) async def rmwarn(ctx, member: discord.Member, amount: int): if amount <= 0: await ctx.send("扣除的警告数值必须大于0") return remove_warn(member, amount) await ctx.send(f"已移除 {member.name} 的 {amount} 次警告!")
调整说明
- 所有文件读写加了
encoding='utf-8',避免不同系统下中文乱码 - 写入json时加了
indent=2参数,生成的存储文件格式清晰,方便手动修改调整 - 补全了所有边界场景的判断,避免无意义的报错
- 移除了工具函数中冗余的
ctx传参,减少不必要的参数传递
内容的提问来源于stack exchange,提问作者Monster png
相关产品推荐
相关产品推荐

