Discord机器人报AttributeError: 'NoneType' object无keys属性求解
报错排查与修复方案
根因定位
触发'NoneType' object has no attribute 'keys'的直接原因是:你调用.keys()方法的db对象实际值为None,并非正常初始化的Replit内置数据库实例,因此不存在对应方法。
分步排查修复
- 确认运行环境合规
Replit DB仅在Replit平台原生运行环境下会自动初始化,本地运行、迁移到其他平台部署时,from replit import db导入的db对象默认是None,会直接触发该报错。如果确认在Replit内运行,先在平台Shell执行pip install --upgrade replit升级依赖,旧版本replit包在数据库服务未就绪时不会抛出明确连接错误,只会返回None值。 - 修复代码本身的语法/缩进/逻辑错误
你贴出的代码存在大量Python语法层面的问题,会直接导致运行时逻辑异常:- 全文件缩进完全错位:Python是缩进敏感语言,函数定义、事件回调、条件判断的缩进错位会导致作用域混乱,部分逻辑无法正常执行
- 方法拼写错误:
update_encouragements函数中encouragements.appen()拼写错误,正确方法名是append - 指令匹配不一致:删除指令判断用
msg.startswith("del"),但后续拆分参数用的是"$del"分隔符,会触发索引越界错误 - 参数类型错误:
delete_encouragement传入的索引是字符串类型,未转换为整数就直接做列表下标判断,会触发类型错误
- 增加DB操作兜底逻辑
不要直接调用db.keys()做存在性判断,先判断db对象是否有效,首次运行数据库为空时提前初始化默认键。修复后的核心可运行代码参考:
import discord import requests import json import random import os from replit import db # 优先从环境变量读TOKEN,不要硬编码 TOKEN = os.getenv("DISCORD_TOKEN") # 注意要开消息内容权限,否则机器人读不到用户发的消息 intents = discord.Intents.default() intents.message_content = True client = discord.Client(intents=intents) sad_words = ["sad", "depressed", "unhappy", "angry", "miserable", "depressing"] starter_encouragements = ["Cheer up!", "Hang in there.", "You are a great person!"] # 初始化DB兜底 if db is not None and "encouragements" not in db.keys(): db["encouragements"] = [] def get_quote(): response = requests.get("https://zenquotes.io/api/random") json_data = json.loads(response.text) quote = f"{json_data[0]['q']} - {json_data[0]['a']}" return quote def update_encouragements(encouraging_message): if db is None: return encouragements = db["encouragements"] encouragements.append(encouraging_message) db["encouragements"] = encouragements def delete_encouragement(index): if db is None: return encouragements = db["encouragements"] if len(encouragements) > index: del encouragements[index] db["encouragements"] = encouragements @client.event async def on_ready(): print(f'We have logged in as {client.user}') @client.event async def on_message(message): if message.author == client.user: return msg = message.content if msg.startswith('inspire'): quote = get_quote() await message.channel.send(quote) options = starter_encouragements.copy() if db is not None and "encouragements" in db.keys(): options.extend(db["encouragements"]) if any(word in msg.lower() for word in sad_words): await message.channel.send(random.choice(options)) if msg.startswith("$new"): encouraging_message = msg.split("$new ",1)[1] update_encouragements(encouraging_message) await message.channel.send("New encouraging message added.") # 统一指令前缀为$ if msg.startswith("$del"): encouragements = [] if db is not None and "encouragements" in db.keys(): try: index = int(msg.split("$del ", 1)[1]) delete_encouragement(index) encouragements = db["encouragements"] except: await message.channel.send("Invalid index, please input a valid number") return await message.channel.send(encouragements) client.run(TOKEN)
Discord机器人高效开发实用技巧
- 正式开发优先使用
discord.py的commands扩展框架,不要裸写on_message做指令判断。框架自带参数解析、指令别名、权限校验、错误捕获能力,能减少大量重复逻辑 - 所有数据库操作、第三方接口调用都要加
try/except异常捕获,Replit DB、励志语录接口都存在网络波动可能,不加捕获会导致机器人直接崩溃离线 - 敏感凭证比如机器人TOKEN不要硬编码在代码里,用Replit左侧的Secrets功能存为环境变量,代码中通过
os.getenv()读取,避免TOKEN泄露被人恶意控制机器人 - 开发阶段开启discord.py的debug日志,能直接看到事件触发、接口调用、权限错误的详细栈信息,比逐行加print排查效率高很多
- 关键词匹配逻辑加小写转换(比如把用户消息先转成
msg.lower()再匹配sad_words),避免用户发大写开头的Sad、DEPRESSED时无法触发响应;不要用简单的子串包含判断,后续可以改成正则整词匹配,减少误触发 - 自定义话术增删功能加权限校验,只允许服务器管理员操作,避免普通用户添加恶意、违规内容导致机器人被举报封禁
- 新版discord.py需要手动在开发者后台开启消息内容Intent,同时代码里声明对应intents配置,否则机器人读不到服务器内用户发的普通消息,这是新手最容易踩的权限坑
内容的提问来源于stack exchange,提问作者NJI8
相关产品推荐
相关产品推荐

