在深入 NoneBot2 的内部机制和高级特性之前,我们首先需要理解它的核心设计理念、基本构成以及如何搭建一个可以运行的环境。
NoneBot2 是一个现代化的 Python 异步机器人框架。它基于 Python 3.7+ 的 asyncio
和类型提示 (Type Hints),提供了高度模块化和可扩展的结构,旨在简化聊天机器人的开发过程。
核心特性:
asyncio
,能够高效处理大量并发的网络请求和事件,这对于需要同时与多个用户或平台交互的聊天机器人至关重要。nb-cli
) 快速创建项目和插件,同时其高度模块化的设计也允许开发者进行深度定制。设计理念:
NoneBot2 的设计深受现代 Web 框架(如 FastAPI)的影响,强调代码的简洁性、可读性和可维护性。它试图将复杂的机器人交互逻辑抽象为一系列清晰定义的组件和流程。
与 NoneBot v1 的区别:
如果你之前接触过 NoneBot v1,你会发现 NoneBot2 是一个完全重写的版本,它们之间不兼容。NoneBot2 带来了诸多改进,包括:
nb-cli
等工具。在深入代码之前,我们先对 NoneBot2 的几个核心组件有一个初步的认识:
Driver (驱动器):
Adapter (适配器):
Bot 对象:
Event (事件):
Matcher (事件响应器):
Plugin (插件):
Message (消息) 与 MessageSegment (消息段):
Message
对象来表示要发送或接收的消息内容。Message
可以由多个 MessageSegment
组成。每个 MessageSegment
代表消息的一部分,如文本、图片、@某人、表情等。这种结构使得构造复杂格式的消息变得容易。Rule (规则):
Permission (权限):
依赖注入 (Dependency Injection):
nb-cli
(NoneBot Command Line Interface):
下图简要展示了这些核心组件之间的大致关系:
这个流程图概括了用户消息从平台到 NoneBot2 处理再返回平台的过程。
现在,我们将一步步搭建一个基础的 NoneBot2 应用。
1.3.1 环境要求
python -m pip install --upgrade pip
)。venv
(Python 内置) 和 conda
。使用 venv
创建虚拟环境 (以 Windows 为例,Linux/macOS 命令类似):
# 1. 在你的项目文件夹(例如 D:\my_nonebot_project)打开命令行
cd /d D:\my_nonebot_project # 进入项目目录
# 2. 创建虚拟环境,环境名通常为 .venv 或 venv
python -m venv .venv # 创建名为 .venv 的虚拟环境
# 3. 激活虚拟环境
# Windows (cmd):
# .venv\Scripts\activate.bat
# Windows (PowerShell):
# .venv\Scripts\Activate.ps1
# (如果PowerShell提示执行策略问题,可能需要先执行 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser)
# Linux/macOS (bash/zsh):
# source .venv/bin/activate
# 激活成功后,命令行提示符前通常会显示 (.venv)
后续所有 pip install
命令都应在激活的虚拟环境中执行。
1.3.2 安装 nb-cli
nb-cli
是 NoneBot2 的官方命令行工具,它极大地简化了项目的创建、依赖管理和插件开发。
在激活的虚拟环境中执行:
pip install nb-cli # 安装nb-cli
安装完成后,可以通过 nb --help
查看 nb-cli
的可用命令。
1.3.3 使用 nb-cli
创建项目
nb-cli
提供了项目脚手架,可以快速生成一个基础的 NoneBot2 项目结构。
在你的工作目录下(例如 D:\my_nonebot_projects
,注意不是上面创建的 .venv
目录,而是一个用于存放多个机器人的父目录,或者直接在 D:\
下面创建新的项目文件夹),执行:
nb create # 运行创建项目命令
nb-cli
会引导你完成项目的初始化配置,通常会询问以下问题:
my_awesome_bot
。src
(将插件放在 my_awesome_bot/src/plugins
目录下) 或者直接放在项目根目录下的 plugins
文件夹。选择 src
更符合现代 Python 项目结构。FastAPI (Recommended)
: 将 NoneBot2 与 FastAPI Web 框架集成,通常用于通过 HTTP 接收事件。这是最常用的选项。Quart
: 另一个异步 Web 框架,类似 Flask。aiohttp
: 一个异步 HTTP 客户端/服务器框架。websockets
: 如果你的适配器主要通过 WebSocket 通信且不需要完整的 HTTP 服务器。FastAPI
。OneBot V11
是一个很好的起点,因为它可以连接到广泛使用的 Go-CQHttp 等 QQ 机器人客户端。No
。如果还没有,可以选择 Yes
让 nb-cli
帮你创建。Yes
。完成这些步骤后,nb-cli
会为你生成项目文件结构,并安装必要的依赖。
项目结构概览 (以选择 FastAPI 和 OneBot V11 为例):
my_awesome_bot/
├── .env # 环境变量配置文件 (开发环境,通常不提交到git)
├── .env.dev # 开发环境的 .env 文件模板或实际配置
├── .env.prod # 生产环境的 .env 文件模板或实际配置 (可能没有)
├── .gitattributes # Git 属性文件
├── .gitignore # Git 忽略文件配置
├── bot.py # NoneBot2 应用入口文件
├── nb_cli.toml # nb-cli 工具的配置文件 (管理插件、适配器等)
├── pyproject.toml # Python 项目配置文件 (PEP 518, 包含项目元数据、依赖等)
├── README.md # 项目说明文件
└── src/ # (如果选择将插件放在src下)
└── plugins/ # 存放你的插件
└── __init__.py
我们来简单看一下几个关键文件:
bot.py
: 这是 NoneBot2 应用的入口。它负责初始化驱动器、加载适配器、加载插件等。
# bot.py (简化示例)
import nonebot # 导入nonebot核心库
from nonebot.adapters.onebot.v11 import Adapter as ONEBOT_V11Adapter # 导入你选择的适配器,这里是OneBot V11
# 初始化 NoneBot
nonebot.init() # 执行NoneBot的初始化配置,会读取.env等配置文件
# 注册适配器
driver = nonebot.get_driver() # 获取全局唯一的Driver实例
driver.register_adapter(ONEBOT_V11Adapter) # 向Driver注册OneBot V11适配器,使其能够处理该平台的事件
# 在这里加载插件
# nonebot.load_builtin_plugins("echo") # 例如,加载一个内置的echo插件 (如果存在)
nonebot.load_plugins("src/plugins") # 加载src/plugins目录下的所有插件
# 如果你的插件直接放在项目根目录的 plugins 文件夹,则是 nonebot.load_plugins("plugins")
if __name__ == "__main__": # 如果直接运行这个bot.py文件
# nonebot.run(host="127.0.0.1", port=8080) # 方式一:直接用nonebot.run()启动,它会使用默认的ASGI服务器配置
# host 和 port 参数通常在 .env 文件中配置
pass # 通常我们使用 nb run 命令来启动,它会处理更多细节
# 在实际项目中,启动通常由 `nb run` 命令处理,该命令会读取 `pyproject.toml` 和 `nb_cli.toml`
# 以及 .env 文件中的配置来启动合适的 ASGI 服务器 (如 Uvicorn) 并运行 NoneBot 应用。
pyproject.toml
: 定义了项目的依赖(如 nonebot2
, 你选择的适配器包等)和构建系统信息。nb-cli
会帮助管理这个文件。
# pyproject.toml (部分示例)
[tool.poetry] # 如果使用Poetry作为包管理工具 (nb-cli默认可能使用pdm或pip)
name = "my_awesome_bot"
version = "0.1.0"
description = ""
authors = ["Your Name "] # 你的名字和邮箱
[tool.poetry.dependencies]
python = "^3.8" # 指定Python版本要求
nonebot2 = "^2.0.0" # NoneBot2核心库
nonebot-adapter-onebot = "^2.0.0" # OneBot适配器 (包名可能略有不同,以实际生成为准)
# ... 其他驱动器或适配器依赖,如 fastapi, uvicorn ...
[build-system]
requires = ["poetry-core>=1.0.0"] # 构建系统要求
build-backend = "poetry.core.masonry.api" # 构建后端
# 如果是pdm生成的,结构会类似但使用pdm的配置方式
# [project]
# name = "my_awesome_bot"
# version = "0.1.0"
# dependencies = [
# "nonebot2>=2.0.0",
# "nonebot-adapter-onebot>=2.0.0",
# ]
.env
系列文件: 用于配置环境变量。NoneBot2 和适配器会从中读取配置。
.env
: 通用配置,通常不直接修改,作为模板。.env.dev
: 开发环境配置,nb-cli
会优先加载这个文件。.env.prod
: 生产环境配置。.env.dev
):ENVIRONMENT=dev # 当前环境
HOST=127.0.0.1 # NoneBot HTTP服务器监听的IP地址 (FastAPI驱动器使用)
PORT=8080 # NoneBot HTTP服务器监听的端口
LOG_LEVEL=DEBUG # 日志级别
# 超级用户QQ号,多个用逗号隔开
SUPERUSERS=["123456789", "987654321"]
# 命令起始符,例如 "/" 或 "" (空字符串表示不需要特定起始符)
COMMAND_START=["/", "!", ""]
# ... 其他适配器或插件可能需要的配置 ...
# OneBot V11 适配器特有的配置 (如果使用反向 WebSocket 连接 Go-CQHttp)
# ONEBOT_ACCESS_TOKEN=your_access_token # 如果Go-CQHttp设置了access_token
# ONEBOT_API_ROOTS='{"default": "ws://127.0.0.1:6700/api"}' # Go-CQHttp API WebSocket 地址
# ONEBOT_EVENT_ENABLED=true # 启用事件上报 (通常Go-CQHttp通过HTTP上报,这里指NoneBot是否处理来自WS的事件)
非常重要: .env
文件中可能包含敏感信息(如API密钥、token、超级用户QQ号等),不应该将包含真实敏感信息的 .env
文件提交到公共 Git 仓库。通常 .gitignore
文件会包含 .env.*
(除了 .env.example
或 .env.template
这类模板文件)。
1.3.4 配置 OneBot 适配器和 Go-CQHttp (示例)
如果你选择了 OneBot V11
适配器,你需要一个实现了 OneBot V11 协议的客户端来连接到 QQ 平台。目前最流行的是 Go-CQHttp。
Go-CQHttp 的安装与配置简要步骤:
下载 Go-CQHttp: 从其 GitHub Releases 页面 (https://github.com/Mrs4s/go-cqhttp/releases) 下载对应你操作系统的最新版本。
首次运行生成配置文件:
go-cqhttp.exe
)。3
(反向 WebSocket 通信)。config.yml
(或 config.hjson
等) 配置文件。编辑 config.yml
:
uin
: 填入你的机器人 QQ 号。password
: 填入你的机器人 QQ 密码。如果开启了设备锁或不希望明文密码,可以留空,首次登录时会提示扫码或短信验证。encrypt_password
(可选): 是否加密存储密码。account
部分的核心配置:account: # 账号相关
uin: 123456789 # 你的机器人QQ号
password: '' # 密码,为空则每次启动时输入或扫码
encrypt_password: false # 是否加密密码
# ... 其他配置 ...
relogin: # 重连相关设置
delay: 3
interval: 3
max_times: 0
# ... 其他部分 ...
servers:
# HTTP 通信设置 (go-cqhttp 作为 HTTP 服务器)
# 如果 NoneBot2 使用正向连接到 go-cqhttp 的 HTTP API (不推荐,事件上报麻烦)
# - http:
# host: 127.0.0.1
# port: 5700 # go-cqhttp 的 HTTP API 服务端口
# timeout: 5
# long_polling: # 是否开启长轮询
# enabled: false # 关闭长轮询,因为我们主要用反向WS
# max_timeout: 0
# middlewares:
# <<: *default # 引用默认中间件
# post_urls: # 事件上报的目标URL (如果NoneBot是HTTP服务器)
# # - http://127.0.0.1:8080/onebot/v11/event # 假设NoneBot在8080端口监听
# secret: '' # 上报数据签名密钥,保持与 NoneBot 配置一致
# filters_path: '' # 过滤配置文件路径
# 反向 WebSocket 通信设置 (go-cqhttp 作为 WebSocket 客户端连接到 NoneBot2)
# 这是推荐的方式,NoneBot2 作为 WebSocket 服务器
- ws-reverse:
# 反向WS设置 Universal Path
universal: ws://127.0.0.1:8080/onebot/v11/ws # NoneBot2 监听的 WebSocket 地址
# 注意: 上面的地址是 go-cqhttp 要连接的 NoneBot2 的地址。
# NoneBot2 的 HOST 和 PORT 在 .env 文件中配置。
# 路径 `/onebot/v11/ws` 是 OneBot V11 适配器在 FastAPI 驱动下通常的默认路径。
# 如果你修改了 NoneBot2 的 HOST/PORT 或适配器路径,这里也要相应修改。
reconnect_interval: 3000 # 重连间隔,单位毫秒
middlewares:
<<: *default # 引用默认中间件
# api_format: json # API请求和响应的格式 (string 或 json,默认string)
# event_format: json # 事件上报的格式 (string 或 json,默认string)
servers
配置中,我们主要配置 ws-reverse
(反向 WebSocket)。universal
: 指向你的 NoneBot2 应用监听的 WebSocket 地址。如果你的 NoneBot2 (bot.py
或通过 nb run
) 运行在 127.0.0.1:8080
,并且 OneBot V11 适配器的 WebSocket 路径是 /onebot/v11/ws
(通常是默认的),那么这里就填 ws://127.0.0.1:8080/onebot/v11/ws
。ONEBOT_ACCESS_TOKEN
,Go-CQHttp 的 ws-reverse
部分也需要配置对应的 access_token
(在 universal
URL后加 ?access_token=xxx
,或者有些版本的 Go-CQHttp 可能有专门的 access_token
字段,具体查阅其文档)。配置 NoneBot2 的 .env.dev
文件 (对应反向 WebSocket):
HOST
和 PORT
配置正确,例如 HOST=127.0.0.1
和 PORT=8080
。.env
中为适配器配置 ONEBOT_API_ROOTS
(因为是 Go-CQHttp 主动连过来)。access_token
,则在 .env.dev
中添加:ONEBOT_ACCESS_TOKEN=your_secret_token # 这个token需要和Go-CQHttp那边配置的一致
启动顺序:
先启动 NoneBot2 应用:
在你的 NoneBot2 项目目录 (my_awesome_bot
) 下,激活虚拟环境后,执行:
nb run # 启动NoneBot2应用
如果一切顺利,你会看到类似 Uvicorn 服务器启动的日志,提示 NoneBot2 正在监听指定的 HOST 和 PORT (例如 http://127.0.0.1:8080
),并且 WebSocket 端点 (如 /onebot/v11/ws
) 也已准备好。
再启动 Go-CQHttp:
运行 go-cqhttp.exe
。它会尝试连接到你在 config.yml
中为 ws-reverse
配置的 NoneBot2 WebSocket 地址。
Bot 123456789 connected
)。此时,你的 NoneBot2 应用就已经通过 Go-CQHttp 连接到了 QQ 平台。
1.3.5 编写你的第一个插件
现在我们来编写一个简单的 “hello” 插件。
创建插件文件:
在你的插件目录 (例如 my_awesome_bot/src/plugins/
) 下创建一个新的 Python 文件,例如 hello.py
。
编写插件代码 (hello.py
):
from nonebot import on_command # 导入 on_command 事件响应器装饰器
from nonebot.adapters.onebot.v11 import Bot as OneBotV11Bot # 导入OneBot V11的Bot类型,用于类型提示
from nonebot.adapters.onebot.v11 import MessageEvent as OneBotV11MessageEvent # 导入OneBot V11的消息事件类型
from nonebot.matcher import Matcher # 导入Matcher类型,用于类型提示
from nonebot.params import CommandArg # 导入CommandArg用于获取命令参数
from nonebot.typing import T_State # 导入T_State用于类型提示
from nonebot.adapters.onebot.v11 import Message # 导入Message用于构造回复消息
# 使用 on_command 装饰器创建一个命令处理器
# "hello" 是命令的名称,当用户发送类似 "/hello" 或 "hello" (取决于COMMAND_START配置) 的消息时会触发
# aliases 参数可以设置命令的别名,例如 {"你好"},则发送 "你好" 也能触发
# priority 参数设置事件响应器的优先级,数字越小优先级越高
hello_matcher = on_command("hello", aliases={
"你好", "hi"}, priority=10) # 创建一个命令匹配器,匹配 "hello", "你好", "hi"
# 定义一个处理函数,当 hello_matcher 匹配成功时会被调用
# 使用类型提示来获取依赖注入的对象
@hello_matcher.handle() # 将此函数注册为 hello_matcher 的处理器
async def handle_hello(
bot: OneBotV11Bot, # 通过类型提示注入OneBotV11Bot实例,代表当前事件对应的机器人
event: OneBotV11MessageEvent, # 通过类型提示注入OneBotV11MessageEvent实例,代表当前接收到的消息事件
matcher: Matcher, # 通过类型提示注入当前的Matcher实例
state: T_State, # 通过类型提示注入当前Matcher的状态字典
args: Message = CommandArg() # 通过CommandArg获取命令的参数部分 (即 "hello" 后面的文本)
): # 这是一个异步函数
user_id = event.get_user_id() # 从事件对象中获取发送者的QQ号
user_name = event.sender.nickname or user_id # 获取发送者的昵称,如果昵称为空则使用QQ号
if args.extract_plain_text().strip(): # 检查命令是否带有参数 (去除首尾空格后是否为空)
# extract_plain_text() 从Message对象中提取纯文本内容
# strip() 去除字符串两端的空白字符
target_name = args.extract_plain_text().strip() # 获取参数作为目标名字
reply_message = Message(f"Hello, {
target_name}! 我是 {
bot.self_id}。很高兴认识你,{
user_name}!") # 构造回复消息
# bot.self_id 是机器人自身的QQ号
else: # 如果命令没有参数
reply_message = Message(f"Hello, {
user_name}! 我是 {
bot.self_id}。有什么可以帮你的吗?") # 构造不同的回复消息
# 使用 matcher.send() 发送回复消息
# matcher.send() 会自动将消息发送到触发该事件的上下文中(例如,私聊回复给用户,群聊回复到群里)
await matcher.send(reply_message) # 异步发送回复消息
# 如果希望在处理完这个事件后,这个matcher不再继续接收后续处理(如果有其他handle或got),可以return
# await matcher.finish(reply_message) # finish会发送消息并结束当前事件处理流程
# 你还可以为同一个Matcher添加更多的处理步骤或更复杂的逻辑
# 例如,使用 matcher.got() 来获取用户下一步的输入
# @hello_matcher.got("name_prompt", prompt="你叫什么名字呢?") # 定义一个获取 "name_prompt" 状态的步骤,并发送提示
# async def got_name(bot: OneBotV11Bot, event: OneBotV11MessageEvent, state: T_State):
# user_provided_name = state["name_prompt"].extract_plain_text() # 从状态中获取用户输入的名字
# await hello_matcher.send(f"原来你叫 {user_provided_name} 啊!")
确保插件被加载:
检查你的 bot.py
文件,确保 nonebot.load_plugins("src/plugins")
(或对应的插件目录) 这一行存在并且路径正确。nb-cli
生成的项目通常会自动配置好。
重新启动 NoneBot2:
如果你之前正在运行 nb run
,先按 Ctrl+C
停止它,然后再次运行 nb run
。NoneBot2 会自动发现并加载新的 hello.py
插件。
测试:
在 QQ 中向你的机器人发送 /hello
、hello
、你好
、hi
或者 /hello Bot
等命令,看看它是否按预期回复。
COMMAND_START
在 .env.dev
中设置了 ""
(空字符串),那么直接发送 hello
就能触发。"/"
,那么需要发送 /hello
。这只是一个非常基础的插件。NoneBot2 的强大之处在于其灵活的事件匹配、状态管理、依赖注入以及丰富的 API,使得构建复杂功能的插件成为可能。
调试技巧:
.env.dev
中设置 LOG_LEVEL=DEBUG
可以获取更详细的日志。print()
调试: 在插件代码中适当地使用 print()
(或者更好的 logger.debug()
) 来输出变量值或执行流程,帮助理解代码行为。launch.json
来调试 NoneBot2 应用。通常是配置为运行 nb run
命令或直接运行 bot.py
(但推荐前者)。// .vscode/launch.json 示例 (用于通过 nb run 启动调试)
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: NoneBot2 (nb run)", // 配置名称
"type": "python", // 调试器类型
"request": "launch", // 请求类型
"module": "nb_cli", // 要运行的模块
"args": [ // 传递给 nb_cli 的参数
"run",
// "--reload" // 如果需要热重载 (开发时方便,但调试单步可能不稳)
],
"console": "integratedTerminal", // 在VS Code集成终端中运行
"justMyCode": true, // 调试时是否只进入用户代码 (可以设为false来看库代码)
"envFile": "${workspaceFolder}/.env.dev" // 指定环境变量文件
}
]
}
在 VS Code 中打开你的项目文件夹,然后转到“运行和调试”视图,选择这个配置并启动调试。你就可以在插件代码中设置断点进行调试了。
要真正掌握NoneBot2并开发出复杂、高效的机器人,理解其核心概念和内部工作原理至关重要。本章将深入探讨构成NoneBot2框架基石的各个组件。
2.1.1 什么是驱动器?
在NoneBot2中,驱动器 (Driver) 是整个框架的动力核心和事件循环的管理者。它负责以下关键任务:
asyncio
构建的异步框架。驱动器内部维护着一个事件循环 (event loop),所有的异步任务、网络通信、定时任务等都在这个事件循环中调度和执行。.env
文件中定义的配置)。SIGINT
、SIGTERM
),以实现优雅停机。2.1.2 内置驱动器与选择
NoneBot2本身不直接实现事件循环和Web服务器功能,而是依赖于成熟的第三方ASGI(Asynchronous Server Gateway Interface)服务器和异步库。常见的驱动器(实际上是ASGI服务器与NoneBot2的集成)包括:
nonebot.drivers.fastapi
: 基于 FastAPI
和 Uvicorn
。这是官方推荐且最常用的驱动器,功能完善,性能优秀,易于扩展(例如,可以方便地添加自定义的HTTP接口)。nonebot.drivers.quart
: 基于 Quart
。Quart
是一个与 Flask
API 兼容的异步Web框架。在项目初始化时(通常是通过 nb-cli
),你会选择一个驱动器。选择不同的驱动器可能会影响到底层Web服务器的行为和某些高级配置选项。
2.1.3 驱动器的工作流程简述
nb run
或直接执行 bot.py
时,驱动器被实例化并启动。FastAPI
驱动器,它会启动一个ASGI服务器(如Uvicorn),监听指定的端口。Event
)。Matcher
) 根据预设的规则匹配这些事件,并执行相应的处理函数。Bot
对象调用适配器提供的API来发送回复或执行其他操作。2.1.4 驱动器配置示例 (以 FastAPI
+ Uvicorn
为例)
通常,驱动器的基本配置(如主机和端口)在项目根目录下的 .env
或 .env.*
文件中完成。
# .env.prod
HOST=0.0.0.0 # 监听的主机地址,0.0.0.0 表示监听所有可用网络接口
PORT=8080 # 监听的端口
DEBUG=false # 是否开启调试模式,生产环境建议关闭
SUPERUSERS=["123456789"] # 超级用户的ID列表
NICKNAME=["小N"] # 机器人的昵称
COMMAND_START=["/", "!", "!"] # 命令起始符
这些配置项会被NoneBot2的配置系统加载,并传递给驱动器。
在 bot.py
文件中,我们通常这样获取驱动器并运行NoneBot2:
# bot.py
import nonebot
from nonebot.adapters.onebot.v11 import Adapter as ONEBOT_V11Adapter # 导入OneBot V11适配器,具体适配器根据你的需求选择
# 初始化 NoneBot
nonebot.init() # 进行NoneBot的初始化,加载配置等
# 注册适配器
driver = nonebot.get_driver() # 获取全局驱动器实例
driver.register_adapter(ONEBOT_V11Adapter) # 向驱动器注册OneBot V11适配器,使其能够处理来自OneBot兼容平台的消息
# 加载插件
nonebot.load_builtin_plugins("echo") # 加载内置的echo插件作为示例
nonebot.load_plugins("my_awesome_bot/plugins") # 从指定目录加载自定义插件
if __name__ == "__main__":
# nonebot.run() # 以默认配置运行NoneBot,通常由nb-cli管理
# 如果你想更细致地控制Uvicorn的启动参数,可以这样做:
config = driver.config # 获取驱动器的配置对象
# 注意:直接在bot.py中用uvicorn.run运行的方式,在较新版本的nb-cli管理的项目中可能不常见,
# 通常是nb-cli通过读取pyproject.toml中的配置来启动。
# 但为了理解,这里展示一个概念性的Uvicorn启动方式(实际项目中请优先使用nb-cli)
if hasattr(config, "host") and hasattr(config, "port"): # 检查配置中是否有host和port属性
if __name__ == "__main__": # 确保只在直接运行此脚本时执行
nonebot.run(host=config.host, port=config.port) # 使用配置中的host和port运行NoneBot
else: # 如果配置中没有host和port
if __name__ == "__main__": # 确保只在直接运行此脚本时执行
nonebot.run() # 使用默认参数运行NoneBot
代码解释:
import nonebot
: 导入NoneBot2的核心库。from nonebot.adapters.onebot.v11 import Adapter as ONEBOT_V11Adapter
: 导入你选择的适配器。这里以OneBot V11为例,它是连接如Go-CQHTTP等QQ机器人客户端的常用适配器。nonebot.init()
: 初始化NoneBot2框架。这一步会加载配置文件(.env
系列文件)、设置日志等。这是启动NoneBot2应用的第一步,必须调用。driver = nonebot.get_driver()
: 获取当前激活的驱动器实例。在nonebot.init()
之后,全局驱动器实例就已经创建好了。driver.register_adapter(ONEBOT_V11Adapter)
: 向驱动器注册一个适配器。只有注册过的适配器才能接收和处理来自对应平台的消息。你可以注册多个不同的适配器以同时支持多个聊天平台。nonebot.load_builtin_plugins("echo")
: 加载NoneBot2内置的插件。echo
是一个简单的示例插件。nonebot.load_plugins("my_awesome_bot/plugins")
: 从指定的目录加载用户自定义的插件。my_awesome_bot/plugins
应该是存放你插件模块的文件夹路径。if __name__ == "__main__":
: 确保以下代码只在直接运行 bot.py
时执行,而不是在被导入时执行。config = driver.config
: 获取驱动器加载的配置对象,其中包含了从 .env
文件中读取的 HOST
和 PORT
等信息。hasattr(config, "host") and hasattr(config, "port")
: 检查配置对象中是否存在 host
和 port
属性。nonebot.run(host=config.host, port=config.port)
: 调用 nonebot.run()
启动NoneBot2应用,并明确指定ASGI服务器(如Uvicorn)监听的主机和端口。如果 .env
文件中没有配置,则会使用默认值。深入理解驱动器与ASGI:
ASGI (Asynchronous Server Gateway Interface) 是 WSGI (Web Server Gateway Interface) 的异步继任者。它定义了异步Python Web服务器、框架和应用之间通信的标准接口。
uvloop
和 httptools
构建。当使用 nonebot.drivers.fastapi
时,底层通常就是 Uvicorn 在运行。驱动器扮演的角色是将NoneBot2的事件处理逻辑与这些ASGI服务器无缝集成。当一个聊天平台通过HTTP(或其他协议,适配器会处理转换)向你的机器人发送一个事件时:
因此,驱动器是NoneBot2与外部世界(通过网络和聊天平台)沟通的咽喉。它的稳定性和效率直接影响到整个机器人的性能和响应能力。
驱动器的生命周期钩子 (Lifecycle Hooks)
NoneBot2的驱动器提供了一些生命周期钩子函数,允许你在驱动器启动、关闭等不同阶段执行自定义逻辑。这对于需要在应用启动时初始化资源(如数据库连接池)或在应用关闭时清理资源非常有用。
import nonebot
from nonebot import get_driver # 导入get_driver函数
from nonebot.drivers import Driver # 导入Driver基类,用于类型注解
driver: Driver = get_driver() # 获取全局驱动器实例,并进行类型注解
@driver.on_startup # 注册一个驱动器启动时执行的钩子函数
async def _(): # 定义一个异步钩子函数
print("驱动器已启动!正在初始化应用资源...") # 打印启动信息
# 在这里可以进行一些异步的初始化操作,例如:
# await initialize_database_connection()
# await load_some_configurations_from_remote()
print("应用资源初始化完成。") # 打印初始化完成信息
@driver.on_shutdown # 注册一个驱动器关闭时执行的钩子函数
async def _(): # 定义一个异步钩子函数
print("驱动器即将关闭!正在清理应用资源...") # 打印关闭信息
# 在这里可以进行一些异步的清理操作,例如:
# await close_database_connection()
# await release_external_resources()
print("应用资源清理完成。") # 打印清理完成信息
# ... 其他的 bot.py 内容,如适配器注册、插件加载等
# nonebot.init() 和 nonebot.run() 的调用保持不变
代码解释:
from nonebot.drivers import Driver
: 导入 Driver
基类,主要用于类型注解,帮助IDE和静态分析工具理解 driver
变量的类型。driver: Driver = get_driver()
: 获取驱动器实例,并明确其类型。@driver.on_startup
: 这是一个装饰器,用于注册一个异步函数,该函数将在驱动器成功启动后、开始接收外部请求之前被调用。async def _():
: 定义一个异步函数作为启动钩子。函数名可以是任意合法的Python标识符,这里用 _
表示我们不关心函数名本身。@driver.on_shutdown
: 这是一个装饰器,用于注册一个异步函数,该函数将在驱动器接收到关闭信号、停止服务之后,实际退出之前被调用。理解驱动器是理解NoneBot2如何与外界交互、如何管理其内部异步任务的基础。虽然日常插件开发中可能不常直接操作驱动器的底层细节,但对其工作方式有一个清晰的认识,有助于排查问题和进行更高级的定制。
2.2.1 什么是适配器?
适配器 (Adapter) 是NoneBot2中连接具体聊天平台和NoneBot2核心框架的桥梁。每个聊天平台(如QQ、Telegram、Discord、微信、钉钉等)都有其自己的一套API接口、数据格式和事件类型。适配器的核心职责就是:
Event
对象结构。这使得插件开发者可以用一套相对一致的方式处理来自不同平台的事件。Bot
) 的提供: 为每个连接到平台的机器人账号(或实例)创建一个对应的 Bot
对象。插件通过这个 Bot
对象来调用特定平台的API(例如,发送消息、获取用户信息等)。Bot
对象的方法,供插件开发者方便使用。简单来说,如果没有适配器,NoneBot2就无法理解来自QQ的消息,也无法将回复发送到Telegram群组。适配器是实现跨平台兼容性的关键。
2.2.2 适配器的种类与选择
NoneBot2社区和官方维护了众多适配器,以支持各种流行的聊天平台。一些常见的适配器包括:
nonebot-adapter-onebot
: 支持 OneBot 标准。OneBot 是一个旨在统一不同聊天机器人平台API的开放标准。
nonebot-adapter-telegram
: 支持 Telegram Bot API。nonebot-adapter-discord
: 支持 Discord Bot API。nonebot-adapter-feishu
: 支持飞书 (Lark) 机器人。nonebot-adapter-dingtalk
: 支持钉钉机器人。nonebot-adapter-console
: 一个特殊的适配器,用于在命令行终端进行调试,模拟聊天交互。nonebot-adapter-red
: 适配 Chronocat 项目下的 QQ Red协议 实现。nonebot-adapter-satori
: 一个新兴的、旨在提供更现代和统一机器人协议的适配器。选择哪个适配器取决于你的目标聊天平台。你可以在一个NoneBot2项目中同时使用多个适配器,让你的机器人同时服务于不同的平台。
2.2.3 适配器的工作流程
bot.py
中,通过 driver.register_adapter()
将适配器类注册到驱动器。Event
对象的子类实例。这个 Event
对象包含了事件类型、来源信息(用户ID、群组ID)、消息内容、时间戳等关键信息,并可能包含特定于平台的额外字段。Bot
对象的创建与关联:
Bot
对象实例。这个 Bot
对象通常会包含一个 self_id
(机器人自身的ID) 和调用平台API所需的信息(如access token)。Event
对象中会包含一个 bot
属性,指向产生该事件的 Bot
对象。这样,插件在处理事件时,就能知道应该使用哪个 Bot
实例来回复消息或执行操作。Event
对象通过驱动器提交给NoneBot2的核心事件处理流程,供各个插件的 Matcher
进行匹配和处理。await bot.send(event, "你好")
)时,它实际上是调用了与当前事件关联的 Bot
对象的方法。Bot
对象的方法内部会根据调用参数,构造出符合平台API要求的请求数据(如JSON payload)。2.2.4 适配器的配置
适配器的配置通常也在 .env
或 .env.*
文件中进行。不同的适配器有不同的配置项。
示例:OneBot V11 适配器配置
如果你的Go-CQHTTP客户端配置了反向WebSocket服务,监听在 ws://127.0.0.1:8080/onebot/v11/ws
,并且设置了 access-token
为 your_secret_token
,那么你可能需要在 .env
文件中添加如下配置:
# .env (或 .env.dev, .env.prod)
ONEBOT_WS_URLS=["ws://127.0.0.1:8080/onebot/v11/ws"] # OneBot 反向WebSocket连接地址列表
ONEBOT_ACCESS_TOKEN=your_secret_token # OneBot 连接的 Access Token (如果设置了)
代码解释:
ONEBOT_WS_URLS
: 一个JSON格式的字符串列表,指定了NoneBot2需要连接的OneBot V11反向WebSocket服务器地址。可以配置多个地址,NoneBot2会尝试连接它们。ONEBOT_ACCESS_TOKEN
: 如果你的OneBot实现(如Go-CQHTTP)配置了访问令牌,你需要在这里提供。示例:Telegram 适配器配置
# .env (或 .env.dev, .env.prod)
TELEGRAM_BOT_TOKEN=your_telegram_bot_token # Telegram Bot 的 API Token
TELEGRAM_API_SERVER=https://api.telegram.org/ # Telegram API 服务器地址 (通常不需要修改)
TELEGRAM_PROXY=http://127.0.0.1:10809 # 如果需要通过代理连接Telegram,则配置代理地址
代码解释:
TELEGRAM_BOT_TOKEN
: 从BotFather获取到的你的Telegram机器人的唯一Token。TELEGRAM_API_SERVER
: Telegram API的官方服务器地址。一般情况下不需要修改。TELEGRAM_PROXY
: 如果你的运行环境无法直接访问Telegram API(例如,在中国大陆),你需要配置一个HTTP或SOCKS5代理服务器地址。这些配置项由相应的适配器在初始化时读取和使用。
2.2.5 Bot
对象:与平台交互的句柄
Bot
对象是适配器为每个机器人实例(或账号)创建的,代表了NoneBot2与该机器人实例在特定平台上的连接。它是插件调用平台API的直接入口。
每个 Bot
对象通常至少包含以下关键信息:
self_id
: 机器人自身在平台上的唯一标识符(例如,QQ号、Telegram Bot ID)。adapter
: 指向创建该 Bot
对象的适配器实例。send(event: Event, message: Message, **kwargs)
: 发送消息。send_private_msg(user_id: int, message: Message, **kwargs)
: 发送私聊消息(特定适配器可能有更具体的方法)。send_group_msg(group_id: int, message: Message, **kwargs)
: 发送群消息(特定适配器可能有更具体的方法)。示例:在事件响应器中使用 Bot
对象
from nonebot import on_message # 导入on_message事件响应器
from nonebot.adapters.onebot.v11 import Bot as OBV11Bot # 导入OneBot V11的Bot类型
from nonebot.adapters.onebot.v11 import MessageEvent as OBV11MessageEvent # 导入OneBot V11的消息事件类型
from nonebot.adapters.onebot.v11 import MessageSegment # 导入OneBot V11的消息段类型
matcher = on_message() # 创建一个处理所有消息事件的响应器
@matcher.handle() # 定义事件处理函数
async def handle_message(bot: OBV11Bot, event: OBV11MessageEvent): # 注入Bot和Event对象
user_id = event.get_user_id() # 从事件中获取发送者QQ号
message_type = event.message_type # 获取消息类型 (private, group)
raw_message = event.get_plaintext() # 获取纯文本消息内容
# 使用Bot对象发送回复
try:
if message_type == "private": # 如果是私聊消息
await bot.send_private_msg(user_id=int(user_id), message=f"收到你的私聊消息: {
raw_message}") # 调用Bot的send_private_msg方法发送私聊回复
elif message_type == "group": # 如果是群聊消息
group_id = event.group_id # 获取群号
if group_id: # 确保group_id存在
await bot.send_group_msg(group_id=group_id, message=MessageSegment.at(user_id) + f" 收到你在群里的消息: {
raw_message}") # 调用Bot的send_group_msg方法发送群聊回复,并@发送者
# 通用发送接口,更推荐使用上面的具体接口
# await bot.send(event, f"已收到: {raw_message}", at_sender=True) # 使用通用的send方法发送回复,at_sender=True表示在群聊中会自动@事件的发送者
# 示例:获取群成员信息 (仅当事件为群消息且Bot支持时)
if message_type == "group" and hasattr(bot, "get_group_member_info"): # 检查bot对象是否有get_group_member_info方法
member_info = await bot.get_group_member_info(group_id=event.group_id, user_id=int(user_id)) # 调用API获取群成员信息
if member_info: # 如果成功获取到信息
await bot.send(event, f"你的群名片是: {
member_info.get('card', '无')}") # 发送用户的群名片信息
except Exception as e: # 捕获可能发生的异常
print(f"发送消息或调用API时出错: {
e}") # 打印错误信息
代码解释:
from nonebot.adapters.onebot.v11 import Bot as OBV11Bot, MessageEvent as OBV11MessageEvent, MessageSegment
: 从OneBot V11适配器导入 Bot
类型、MessageEvent
类型以及用于构建复杂消息的 MessageSegment
。在实际编码中,使用具体的类型注解(如 bot: OBV11Bot
)可以获得更好的IDE支持和代码可读性。async def handle_message(bot: OBV11Bot, event: OBV11MessageEvent):
: 事件处理函数通过依赖注入自动获取当前的 Bot
实例和触发事件的 Event
实例。event.get_user_id()
: MessageEvent
对象提供的便捷方法,用于获取消息发送者的用户ID。event.message_type
: MessageEvent
对象的属性,指示消息的类型(如 "private"
, "group"
)。event.group_id
: 如果是群消息事件,此属性包含群ID。await bot.send_private_msg(...)
和 await bot.send_group_msg(...)
: 调用 Bot
对象上由OneBot V11适配器提供的、特定于平台的方法来发送私聊和群聊消息。MessageSegment.at(user_id)
: 使用 MessageSegment
构建一个@某人的消息段。hasattr(bot, "get_group_member_info")
: 检查当前的 Bot
对象是否具有 get_group_member_info
方法。这是一个良好的实践,因为并非所有OneBot实现都完整支持所有API。await bot.get_group_member_info(...)
: 调用平台API获取更详细的信息。2.2.6 自己编写适配器(高级)
虽然大多数情况下我们会使用社区提供的成熟适配器,但如果你需要连接一个NoneBot2尚不支持的专有平台,或者想对现有平台的通信方式进行深度定制,你可以考虑编写自己的适配器。
编写适配器通常需要实现以下几个核心部分:
nonebot.adapters.Adapter
类。_setup()
方法: 在驱动器加载适配器时调用,用于进行初始化配置。Bot
子类: 定义一个继承自 nonebot.adapters.Bot
的类,封装该平台特定的API调用。Event
子类,继承自 nonebot.events.Event
。Message
和 MessageSegment
子类,继承自 nonebot.internal.adapter.Message
和 nonebot.internal.adapter.MessageSegment
。这是一个相对高级的主题,需要对NoneBot2的内部架构、异步编程以及目标平台的API有深入的理解。
适配器是NoneBot2生态系统的重要组成部分,它们极大地扩展了NoneBot2的应用范围,使其能够轻松接入各种聊天平台。理解适配器的工作原理有助于我们更好地配置机器人、排查平台通信问题,以及在需要时进行更深层次的定制。
2.3.1 什么是事件?
在NoneBot2的语境下,事件 是指由聊天平台产生并传递给机器人的各类信息的总称。这些信息标志着某些事情的发生,机器人需要对这些事情做出响应。常见的事件类型包括:
每个具体的聊天平台可能会有自己独特的事件分类和命名方式。适配器的重要职责之一就是将这些平台特定的事件数据,转换为NoneBot2内部统一的、或至少是遵循一定规范的事件对象。
2.3.2 NoneBot2 中的 Event
基类与继承体系
NoneBot2 定义了一个基础的 Event
类 (nonebot.events.Event
),所有特定平台的事件类型都应该直接或间接地继承自这个基类。这样做的好处是:
isinstance()
或类型注解来判断和处理不同类型的事件。nonebot.events.Event
基类本身包含了一些所有事件都可能具有的通用属性,例如:
time: int
: 事件发生的时间戳 (通常是 Unix 时间戳,秒级)。self_id: int | str
: 收到事件的机器人QQ号或标识。post_type: str
: 事件类型的大分类,例如 “message”, “notice”, “request”, “meta_event”。这个字段在OneBot标准中非常重要。特定适配器的事件定义
每个适配器会基于 nonebot.events.Event
定义一套符合其对应平台规范的事件类。以广泛使用的 nonebot-adapter-onebot
(特别是V11版本) 为例,它定义了非常详细的事件继承体系:
nonebot.adapters.onebot.v11.Event
: OneBot V11适配器的事件基类,继承自 nonebot.events.Event
。它会添加一些OneBot V11特有的通用字段,如 sub_type
(事件子类型)等。MessageEvent
(nonebot.adapters.onebot.v11.events.MessageEvent
): 消息事件的基类,继承自 OneBotV11Event
。
PrivateMessageEvent
: 私聊消息事件。GroupMessageEvent
: 群聊消息事件。NoticeEvent
(nonebot.adapters.onebot.v11.events.NoticeEvent
): 通知事件的基类。
GroupUploadNoticeEvent
: 群文件上传事件。GroupAdminNoticeEvent
: 群管理员变动事件 (设置/取消管理员)。GroupDecreaseNoticeEvent
: 群成员减少事件 (主动退群、被踢)。GroupIncreaseNoticeEvent
: 群成员增加事件 (被邀请、主动加入)。GroupBanNoticeEvent
: 群禁言事件。FriendAddNoticeEvent
: 好友添加事件。GroupRecallNoticeEvent
: 群消息撤回事件。FriendRecallNoticeEvent
: 好友消息撤回事件。NotifyEvent
(通常指戳一戳等):提醒事件。
PokeNotifyEvent
: 戳一戳事件。RequestEvent
(nonebot.adapters.onebot.v11.events.RequestEvent
): 请求事件的基类。
FriendRequestEvent
: 加好友请求事件。GroupRequestEvent
: 加群请求/邀请事件。MetaEvent
(nonebot.adapters.onebot.v11.events.MetaEvent
): 元事件的基类。
LifecycleMetaEvent
: 生命周期元事件 (如 connect
事件,表示与OneBot客户端连接成功)。HeartbeatMetaEvent
: 心跳元事件。其他适配器的事件示例 (概念性)
TelegramEvent(Event)
MessageEvent(TelegramEvent)
: 包含 chat_id
, from_user
, text
, photo
, video
等字段。CallbackQueryEvent(TelegramEvent)
: 用户点击了inline keyboard按钮的事件。ChatMemberUpdatedEvent(TelegramEvent)
: 聊天成员状态更新事件。DiscordEvent(Event)
MessageCreateEvent(DiscordEvent)
: 消息创建事件,包含 channel_id
, author
, content
, attachments
等。GuildMemberAddEvent(DiscordEvent)
: 服务器成员加入事件。InteractionCreateEvent(DiscordEvent)
: 交互事件 (如Slash Command, Button Click)。2.3.3 事件对象的核心属性与方法
让我们更详细地看一下一个典型的 MessageEvent
(以OneBot V11为例) 可能包含哪些重要的属性和方法,这些是插件开发者最常接触到的:
# 假设我们有一个从OneBot V11适配器接收到的GroupMessageEvent对象实例 event
# event: nonebot.adapters.onebot.v11.events.GroupMessageEvent
# --- 通用 Event 属性 (继承自 nonebot.events.Event 或其上层) ---
event_time = event.time # 事件发生的时间戳 (int), 例如: 1678886400
robot_self_id = event.self_id # 机器人自身的QQ号 (int), 例如: 10001
event_post_type = event.post_type # 事件类型 (str), 对于消息事件通常是 "message"
# --- OneBot V11 Event 特有属性 (继承自 nonebot.adapters.onebot.v11.Event) ---
# event_sub_type = event.sub_type # OneBot v11 中的子类型,但MessageEvent通常用message_type区分
# --- MessageEvent 特有属性 (继承自 nonebot.adapters.onebot.v11.events.MessageEvent) ---
message_id = event.message_id # 消息ID (int), 例如: 12345
real_id = event.real_id # 消息真实ID (int),某些情况下与message_id不同或更有用
user_id = event.user_id # 发送者QQ号 (int), 例如: 98765
event_message_type = event.message_type # 消息类型 (str), "private" 或 "group"
sender = event.sender # 发送者信息 (Sender 对象), 包含更详细的发送者资料
# sender.user_id (int): 发送者QQ号
# sender.nickname (str): 发送者昵称
# sender.sex (str): 性别, "male", "female", "unknown"
# sender.age (int): 年龄
# sender.card (str, 仅群聊): 群名片
# sender.area (str, 仅群聊): 地区
# sender.level (str, 仅群聊): 等级
# sender.role (str, 仅群聊): 角色, "owner", "admin", "member"
# sender.title (str, 仅群聊): 专属头衔
raw_message = event.raw_message # 原始消息内容 (str), 未经处理的原始字符串
font = event.font # 字体 (int), 消息使用的字体,通常为0
message = event.message # 消息内容 (Message 对象), 经过NoneBot2封装的消息段列表
to_me = event.to_me # 消息是否是发给我的 (bool), 例如是否以@机器人开头或机器人昵称开头
# --- GroupMessageEvent 特有属性 (继承自 nonebot.adapters.onebot.v11.events.GroupMessageEvent) ---
if isinstance(event, GroupMessageEvent): # 确保是群消息事件
group_id = event.group_id # 群号 (int), 例如: 10086
anonymous = event.anonymous # 匿名信息 (Optional[Anonymous]), 如果是匿名消息则包含匿名信息,否则为None
# if anonymous:
# anonymous_id = anonymous.id # 匿名用户ID (int)
# anonymous_name = anonymous.name # 匿名用户名称 (str)
# anonymous_flag = anonymous.flag # 匿名消息flag (str)
# --- Event 对象提供的便捷方法 (由 nonebot.events.Event 或其子类提供) ---
# 这些方法非常常用,因为它们提供了跨适配器获取基本信息的统一接口(如果适配器实现了它们)
# 获取消息的纯文本内容
plain_text = event.get_plaintext() # (str) 例如用户发送 "@机器人 你好呀[图片]",这里会得到 "你好呀"
# 注意: get_plaintext() 会去除CQ码等非文本内容,以及消息开头的@机器人部分 (如果to_me为True)
# 获取消息主体 (Message对象,但通常会去除开头的@机器人部分)
message_body = event.get_message() # (Message)
# 获取用户ID (str)
# 对于OneBot V11, event.user_id已经是int, 但get_user_id()返回str以保持通用性
str_user_id = event.get_user_id() # (str) "98765"
# 获取会话ID (str),用于唯一标识一个对话场景 (例如,私聊是用户ID,群聊是群ID)
# 这对于区分不同聊天窗口的会话状态非常重要
session_id = event.get_session_id() # (str)
# 对于私聊: f"private_{user_id}"
# 对于群聊: f"group_{group_id}"
# 如果是频道(Guild)等更复杂的场景,适配器会定义相应的session_id格式
# 获取事件的简要描述 (str),用于日志等
event_description = event.get_event_description() # (str) 例如: 'Message[12345] from User[98765] in Group[10086] "你好呀[图片]"'
# 获取事件名称 (str)
event_name = event.get_event_name() # (str) 例如: "message.group.normal"
# 判断事件是否是发给机器人的
is_tome = event.is_tome() # (bool), 与 event.to_me 类似,但可能是更通用的判断
# 获取 Bot 对象
current_bot = event.get_bot() # (Bot) 返回与此事件关联的 Bot 实例
代码解释:
event
是一个 GroupMessageEvent
实例。time
, self_id
, post_type
是几乎所有事件都有的基础信息。message_id
, user_id
, message_type
, sender
, raw_message
, message
, to_me
对于处理消息至关重要。
raw_message
: 包含了CQ码等平台特定格式的原始字符串。message
: 是一个 nonebot.adapters.onebot.v11.Message
对象(或对应适配器的Message类型),它是一个由多个 MessageSegment
组成的列表。这使得可以方便地处理图文混合、@某人等复杂消息。我们后续会详细讲解 Message
和 MessageSegment
。sender
: 包含了发送者的详细信息,非常有用。group_id
标识了消息来源的群。anonymous
用于判断是否是匿名消息。get_plaintext()
: 获取纯文本,通常是你最关心的用户输入内容。get_message()
: 获取 Message
对象,但通常会去除消息开头对机器人的称呼 (如 @机器人
)。get_user_id()
: 统一获取用户ID的字符串形式。get_session_id()
: 获取会话ID,对于需要区分不同聊天上下文(如私聊A、群聊B)的场景非常关键。get_event_description()
和 get_event_name()
: 主要用于日志记录和调试。is_tome()
: 判断消息是否明确指向机器人。get_bot()
: 获取与此事件关联的 Bot
对象实例,这样处理函数就可以直接使用这个 bot
来调用API发送回复等。NoticeEvent
和 RequestEvent
的属性示例 (OneBot V11)
GroupIncreaseNoticeEvent
(群成员增加):
event.notice_type
: “group_increase”event.sub_type
: “approve” (管理员同意入群), “invite” (被邀请入群)event.group_id
: 群号event.user_id
: 加入群的用户的QQ号event.operator_id
: 操作者的QQ号 (例如,邀请者的QQ号,或同意申请的管理员QQ号)FriendRequestEvent
(加好友请求):
event.request_type
: “friend”event.user_id
: 请求加好友的用户的QQ号event.comment
: 验证信息/附言event.flag
: 请求flag,用于后续同意或拒绝请求时使用理解不同事件类型的特有属性对于编写能正确响应各种平台行为的插件至关重要。通常,你需要查阅对应适配器的文档来了解其支持的所有事件类型及其详细字段。
2.3.4 事件的生命周期与处理流程(初步概览)
一个事件从产生到被插件处理,大致经历以下步骤:
Event
子类。Event
对象的各个属性中。Bot
对象实例(代表接收此事件的机器人账号)。通常 Bot
对象是在适配器与平台建立连接时创建并维护的。Event
对象通过 nonebot.adapters.Adapter.post_event(event: Event)
方法提交给NoneBot2的核心事件分发机制。Matcher
(事件响应器)的列表。Matcher
都有其自己的触发规则 (rule)、权限检查 (permission) 和处理函数 (handler)。Matcher
的规则进行匹配。Matcher
是否关心这类事件(例如,只处理 MessageEvent
)。Matcher
定义的自定义逻辑条件(例如,消息是否以特定命令开头)。Matcher
的类型检查、规则检查和权限检查,那么该 Matcher
对应的处理函数 (handler) 就会被调用。Event
对象(以及关联的 Bot
对象等)会通过依赖注入的方式传递给这个处理函数。Matcher
可以有多个处理函数,它们会按顺序执行。Matcher.skip()
, Matcher.reject()
, Matcher.finish()
)来控制 Matcher
的后续行为。Matcher
可以设置优先级 (priority
)。高优先级的 Matcher
会先进行匹配。Matcher
可以设置是否阻断事件 (block=True
)。如果一个设置了 block=True
的 Matcher
成功处理了一个事件,那么该事件通常不会再被后续同优先级或低优先级的 Matcher
处理(除非特殊配置)。Matcher
处理完毕(或者事件被阻断)后,事件可能会经过全局事件后处理器。后处理器可以进行一些收尾工作,如记录最终处理结果等。Bot
对象调用了平台的API(如发送消息),那么这个API调用会通过适配器转换成平台特定的请求,并发送回聊天平台。这个流程是一个高度概括。我们将在后续章节中详细探讨 Matcher
、Rule
、Permission
、Handler
、依赖注入等核心机制。
2.3.5 在插件中与事件交互
在插件的事件处理函数中,你会经常与 Event
对象打交道。
from nonebot import on_command, require # 导入on_command用于创建命令响应器, require用于确保插件加载顺序
from nonebot.log import logger # 导入日志记录器
from nonebot.typing import T_State # 导入状态字典类型
from nonebot.adapters.onebot.v11 import Bot as OBV11Bot # 导入OneBot V11的Bot类型
from nonebot.adapters.onebot.v11 import MessageEvent as OBV11MessageEvent # 导入OneBot V11的消息事件类型
from nonebot.adapters.onebot.v11 import GroupMessageEvent as OBV11GroupMessageEvent # 导入OneBot V11的群消息事件类型
from nonebot.adapters.onebot.v11 import PrivateMessageEvent as