Python Discord机器人运行报错 命令无法更新对应服务器JSON数据库
问题定位与修复指南
核心问题排查
- 专属JSON文件路径生成逻辑完全错误:现有代码拼接文件路径用的是
str(bot.guilds) + ".json",转义后会生成包含机器人所有已加入服务器列表的字符串,对应路径完全无效,系统无法找到可读写的文件,直接触发IO类报错。正确的路径生成逻辑需要从命令触发的上下文ctx.guild.id获取当前服务器的唯一ID,每个服务器对应一个以自身ID命名的独立JSON文件。 - Discord.py 版本API适配错误:现有代码使用的是已停止维护的discord.py 0.x版本接口,当前主流的1.x/2.x版本中,不存在
client.send_message、ctx.quote、ctx.timestamp、ctx.content这些废弃属性,运行时会直接报属性不存在的错误。 - 发送消息统一用上下文自带的
ctx.send()方法,替代原有的client.send_message(message.channel, 内容)写法 - 命令输入内容直接通过Discord.py内置的参数解析功能捕获,不需要手动从消息字符串里截取、替换命令前缀
- 操作人信息、消息时间戳分别对应上下文的
ctx.author、ctx.message.created_at属性,不需要从旧版上下文中提取 - 新增服务器的文件初始化兜底缺失:新接入的服务器对应的JSON存储文件不存在时,直接以只读模式打开会直接报错,需要增加文件存在性判断,不存在时自动初始化符合结构要求的空JSON文件再读写。
- 随机数生成逻辑有边界漏洞:
randomquote命令里的随机数范围写为random.randint(1, len(parsed_json["quotes"]) -1),当服务器没有任何语录时,会生成从1到-1的无效范围,直接触发数值范围错误,需要增加空数据判断分支。 - 手动命令前缀匹配可靠性差:现有代码通过字符串替换手动识别命令、截取参数,很容易因为用户输入带多余空格、自定义前缀不匹配导致参数提取错误,直接使用Discord.py内置的命令参数解析可以完全规避这类问题。
修复后的可运行参考代码
import json import random from pathlib import Path from discord.ext import commands # 初始化机器人,设置命令触发前缀 bot = commands.Bot(command_prefix="!") # 工具函数:根据服务器ID获取专属数据文件路径 def get_guild_db_path(guild_id: int) -> Path: return Path(f"./guild_data/{guild_id}.json") # 工具函数:读取对应服务器的数据库,文件不存在时自动初始化空结构 def read_guild_db(guild_id: int) -> dict: db_path = get_guild_db_path(guild_id) db_path.parent.mkdir(exist_ok=True) if not db_path.exists(): default_data = {"quotes": {}} with open(db_path, "w", encoding="utf-8") as f: json.dump(default_data, f, ensure_ascii=False, indent=2) return default_data with open(db_path, "r", encoding="utf-8") as f: return json.load(f) # 工具函数:将修改后的数据库内容写回对应服务器的JSON文件 def write_guild_db(guild_id: int, data: dict): db_path = get_guild_db_path(guild_id) with open(db_path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) @bot.command() async def addquote(ctx, *, quote_content: str): db = read_guild_db(ctx.guild.id) next_quote_id = str(len(db["quotes"]) + 1) db["quotes"][next_quote_id] = { "content": quote_content, "addedby": str(ctx.author), "add_date": str(ctx.message.created_at.date()) } write_guild_db(ctx.guild.id, db) await ctx.send(f"已添加编号为 {next_quote_id} 的语录") @bot.command() async def addquotename(ctx, name: str, *, quote_content: str): db = read_guild_db(ctx.guild.id) next_quote_id = str(len(db["quotes"]) + 1) db["quotes"][next_quote_id] = { "name": name, "content": quote_content, "addedby": str(ctx.author), "add_date": str(ctx.message.created_at.date()) } write_guild_db(ctx.guild.id, db) await ctx.send(f"已添加名为 {name} 的语录") @bot.command() async def randomquote(ctx): db = read_guild_db(ctx.guild.id) if not db["quotes"]: await ctx.send("当前服务器暂未存储任何语录") return quote_num = random.choice(list(db["quotes"].keys())) quote = db["quotes"][quote_num] await ctx.send(f"随机语录 {quote_num}:{quote['content']}") @bot.command() async def quoteaddedby(ctx, quote_num: int): db = read_guild_db(ctx.guild.id) quote_id = str(quote_num) if quote_id not in db["quotes"]: await ctx.send(f"不存在编号为 {quote_num} 的语录,当前服务器共有 {len(db['quotes'])} 条语录") return author = db["quotes"][quote_id]["addedby"] await ctx.send(f"编号为 {quote_num} 的语录添加者为:{author}") # 运行时替换为自己的机器人Token即可启动 # bot.run("你的机器人API Token")
注意:启动前需要提前在机器人运行的开发者后台开启对应的Message Content Intent权限,否则无法正常响应命令。
内容的提问来源于stack exchange,提问作者André Peixinho
相关产品推荐
相关产品推荐

