NoneBot2 插件编写与加载全指南:插件结构、nb-cli 创建与七种加载方式详解

📅 发布时间:2026/9/27 10:12:54
NoneBot2 插件编写与加载全指南:插件结构、nb-cli 创建与七种加载方式详解
后端即时通讯【免费下载链接】nonebot2跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python项目地址https://gitcode.com/gh_mirrors/no/nonebot2点击查看免费下载本篇教程聚焦 NoneBot2 插件开发的起点——插件是什么、如何创建、如何加载。你将学会区分单文件插件与包插件、通过nb plugin create或手动方式创建插件并掌握load_plugin、load_plugins、load_all_plugins、load_from_json、load_from_toml以及内置插件加载等全部加载接口的适用场景与底层实现。读完即可在真实项目中独立组织插件目录并正确接入入口文件bot.py。插件结构插件本质上是一个特殊的 Python 模块在 NoneBot 中插件即是 Python 的一个模块module。NoneBot 会在导入时对这些模块做一些特殊处理使其成为一个真正的插件。插件之间应尽量减少耦合可以进行有限制的相互调用NoneBot 能够正确解析插件间的依赖关系。从源码实现来看这一特殊处理发生在 nonebot/plugin/manager.py 的PluginLoader.exec_module中模块执行前框架会先通过_new_plugin创建Plugin对象并挂载为模块的__plugin__属性随后进入插件上下文_current_plugin再真正执行模块代码若执行抛出异常则调用_revert_plugin回滚注册避免留下脏数据。因此一个普通.py文件在导入后只要其__plugin__属性存在且为Plugin实例就会被识别为插件——这就是插件即模块的底层依据。单文件插件一个普通的.py文件即可以作为一个插件。例如创建一个foo.py文件 plugins └── foo.py这个时候模块foo已经可以被称为一个插件了尽管它还什么都没做。包插件一个包含__init__.py的文件夹即是一个常规 Python 包package。例如创建一个foo文件夹 plugins └── foo └── __init__.py这个时候包foo同样是一个合法的插件插件内容可以在__init__.py文件中编写。仓库佐证pkgutil.iter_modules扫描插件目录时单文件插件与包含__init__.py的包插件都会被纳入搜索在 nonebot/utils.py 的path_to_module_name中若路径文件名是__init__则取父目录作为模块名这正是包插件能被正确转换为模块名的原因。创建插件nb-cli 交互式创建与手动创建创建插件可以通过nb-cli命令从完整模板创建也可以手动新建空白文件。通过以下命令创建一个名为weather的插件$ nb plugin create [?] 插件名称: weather [?] 使用嵌套插件? (y/N) N [?] 请输入插件存储位置: awesome_bot/pluginsnb-cli会在awesome_bot/plugins目录下创建一个名为weather的文件夹其中包含的文件将在后续章节中用到 awesome-bot ├── .venv ├── awesome_bot │ └── plugins │ └── weather │ ├── __init__.py │ └── config.py ├── .env.prod ├── pyproject.toml └── README.md生成出的config.py是插件专属配置的声明文件用于配合get_plugin_config从全局配置中提取插件所需配置项见 nonebot/plugin/init.py后续编写插件业务逻辑时可以在__init__.py中导入使用。基于 bootstrap 模板项目的修改如果在之前的快速上手章节中已经使用bootstrap模板创建了项目那么需要做出如下修改在项目目录中创建一个两层文件夹awesome_bot/plugins awesome-bot ├── .venv ├── awesome_bot │ └── plugins ├── .env.prod ├── pyproject.toml └── README.md修改pyproject.toml文件中的nonebot配置项在plugin_dirs中添加awesome_bot/plugins[tool.nonebot] plugin_dirs [awesome_bot/plugins]plugin_dirs数组正是后续load_from_toml读取[tool.nonebot]Table 时所依赖的字段它声明了机器人要扫描的本地插件目录。基于手动创建项目的修改如果在之前的创建项目章节中手动创建了相关文件那么需要做出如下修改在项目目录中创建一个两层文件夹awesome_bot/plugins awesome-bot ├── awesome_bot │ └── plugins └── bot.py修改bot.py文件中的加载插件部分取消注释或者添加如下代码# 在这里加载插件 nonebot.load_builtin_plugins(echo) # 内置插件 nonebot.load_plugins(awesome_bot/plugins) # 本地插件加载插件时机、约束与七种加载接口加载时机与必须遵守的约束加载插件是在机器人入口文件中完成的需要在框架初始化之后、运行之前进行即位于nonebot.init()与nonebot.run()之间import nonebot nonebot.init() # 加载插件 nonebot.run():::danger[警告] 请勿在插件被加载前import插件模块这会导致 NoneBot 无法将其转换为插件而出现意料之外的情况。 :::这条警告的根源在PluginLoader.create_module与PluginLoader.exec_module的实现逻辑nonebot/plugin/manager.py如果模块早已被普通import放入sys.modules则加载器会直接复用已存在的模块而跳过创建插件对象的步骤导致该模块没有__plugin__属性最终在load_plugin中抛出Module ... is not loaded as a plugin!错误。此外还需注意加载的插件模块名称插件文件名或文件夹名不能相同且每一个插件只能被加载一次重复加载将会导致异常。这对应 nonebot/plugin/manager.py 中_prepare_plugins的去重校验——无论是独立插件名还是目录扫描出的插件只要插件标识符已存在就会抛出Plugin already exists: xxx! Check your plugin name。如果你使用nb-cli管理插件那么可以跳过本节nb-cli会自动处理加载如果使用自定义的入口文件bot.py则需要手动加载。加载插件的方式有多种但底层的加载逻辑是一致的所有接口最终都汇聚到PluginManager与importlib以下是为加载插件提供的几种方式。load_plugin加载单个插件通过点分割模块名称或使用pathlib的Path对象来加载插件通常用于加载第三方插件或者项目插件。例如from pathlib import Path nonebot.load_plugin(path.to.your.plugin) # 加载第三方插件 nonebot.load_plugin(Path(./path/to/your/plugin.py)) # 加载项目插件:::warning[注意] 本地插件的路径应该为相对机器人**入口文件通常为 bot.py**可导入的例如在项目plugins目录下。 :::源码实现上nonebot/plugin/load.py传入Path时先经path_to_module_name转换为点分模块名再交由PluginManager加载。仓库测试 tests/test_plugin/test_load.py 同时验证了模块名加载与路径加载两条路径并确认加载不存在的插件会返回None。load_plugins加载目录下所有插件加载传入插件目录中的所有插件通常用于加载一系列本地编写的项目插件。例如nonebot.load_plugins(src/plugins, path/to/your/plugins):::warning[注意] 插件目录应该为相对机器人**入口文件通常为 bot.py**可导入的例如在项目plugins目录下。 :::底层通过PluginManager(search_pathplugin_dir)扫描目录注意实现细节nonebot/plugin/manager.py以_开头的文件或文件夹不会被导入。这一点在测试中得到印证assert plugin._hidden not in sys.modules见 tests/test_plugin/test_load.py——仓库的tests/plugins/_hidden.py正是用来验证下划线前缀插件会被忽略。load_all_plugins混合加载这种加载方式是以上两种方式的混合加载所有传入的插件模块名称以及所有给定目录下的插件。例如nonebot.load_all_plugins([path.to.your.plugin], [path/to/your/plugins])签名对应源码load_all_plugins(module_path: Iterable[str], plugin_dir: Iterable[str])nonebot/plugin/load.py它把独立插件与目录插件统一交给一个PluginManager处理。load_from_json从 JSON 文件加载通过 JSON 文件加载插件是load_all_plugins的 JSON 变种通过读取 JSON 文件中的plugins字段和plugin_dirs字段进行加载。例如{ plugins: [path.to.your.plugin], plugin_dirs: [path/to/your/plugins] }nonebot.load_from_json(plugin_config.json, encodingutf-8)源码会校验 JSON 顶层必须是 dict且plugins、plugin_dirs均为列表nonebot/plugin/load.py否则抛出TypeError或AssertionError。仓库中的测试样例 tests/plugins.json 与校验测试见 tests/test_plugin/test_load.py非法 JSON 会触发TypeError。:::tip[提示] 如果 JSON 配置文件中的字段无法满足你的需求可以使用load_all_plugins方法自行读取配置来加载插件。 :::load_from_toml从 TOML 文件加载通过 TOML 文件加载插件是load_all_plugins的 TOML 变种通过读取 TOML 文件中的[tool.nonebot]Table 中的plugin_dirsArray 与[tool.nonebot.plugins]Table 中的多个 Array 进行加载。例如[tool.nonebot] plugin_dirs [path/to/your/plugins] [tool.nonebot.plugins] local [path.to.your.plugin] # 本地插件等非插件商店来源的插件 nonebot-plugin-someplugin [nonebot_plugin_someplugin] # 插件商店来源的插件nonebot.load_from_toml(plugin_config.toml, encodingutf-8)源码解析逻辑nonebot/plugin/load.py值得留意若 TOML 中没有[tool.nonebot]Table直接抛出ValueError: Cannot find [tool.nonebot] in given toml file!测试见 tests/test_plugin/test_load.py[tool.nonebot]下既支持新版plugins作为 Table[tool.nonebot.plugins]下的多个 Array按来源分组也兼容旧版plugins作为 Array 的格式——旧格式会输出警告Legacy project format found! Upgrade withnb upgrade-format.仓库测试样例 tests/plugins.toml新版分组格式与 tests/plugins.legacy.toml旧版数组格式对两者均有覆盖。:::tip[提示] 如果 TOML 配置文件中的字段无法满足你的需求可以使用load_all_plugins方法自行读取配置来加载插件。 :::load_builtin_plugin加载单个内置插件加载一个内置插件传入的插件名必须为 NoneBot 内置插件。该方法是load_plugin的封装。例如nonebot.load_builtin_plugin(echo)源码实现nonebot/plugin/load.py等价于load_plugin(fnonebot.plugins.{name})即把内置插件当作nonebot.plugins包下的模块加载。仓库内置插件目录 nonebot/plugins/echo.py 定义了/echo命令——它通过on_command(echo, to_me())注册响应器并回复消息内容。load_builtin_plugins加载多个内置插件加载传入插件列表中的所有内置插件。例如nonebot.load_builtin_plugins(echo, single_session)源码实现nonebot/plugin/load.py等价于load_all_plugins([fnonebot.plugins.{p} for p in plugins], [])。第二个内置插件 nonebot/plugins/single_session.py 是唯一会话插件——加载后自动生效通过event_preprocessor限制同一会话内同时只能运行一个响应器。其他加载方式以上是面向入口文件的全部加载接口。除此之外插件加载机制还覆盖两个进阶场景可参考官方文档深入了解跨插件访问通过require(name)声明依赖并获取其他插件模块实现见 nonebot/plugin/load.py详见跨插件访问嵌套插件子插件以父插件标识符:子插件名的形式注册测试nested:nested_subplugin见 tests/test_plugin/test_load.py详见嵌套插件。加载流程小结与自检清单所有加载接口最终都经由PluginManagernonebot/plugin/manager.py完成先_prepare_plugins搜索并缓存可用插件含重名校验再由load_plugin触发importlib导入导入过程被注册在sys.meta_path首位的PluginFinder拦截改用PluginLoader执行模块并注入__plugin__属性最终插件进入全局注册表_pluginsnonebot/plugin/init.py可通过get_loaded_plugins()/get_plugin()查询。完成本教程后建议按以下清单自查插件目录如awesome_bot/plugins已创建且相对入口文件可导入插件是普通.py文件或含__init__.py的包且插件名不与已加载插件重名入口文件中nonebot.init()之后、nonebot.run()之前调用加载接口未在任何插件加载前手动import插件模块目录内以_开头的文件/文件夹不会被当作插件加载这是特性不是 bug。赞分享后端即时通讯【免费下载链接】nonebot2跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python项目地址https://gitcode.com/gh_mirrors/no/nonebot2点击查看免费下载相关推荐NoneBot2 嵌套插件编写与加载子插件完全指南NoneBot2 嵌套插件编写与加载子插件完全指南 嵌套插件是 NoneBot2 提供的一种插件组织机制一个插件可以包含其他插件父插件通过调用框架的加载插后端即时通讯NoneBot2插件开发指南从创建到加载全流程解析NoneBot2插件开发指南从创建到加载全流程解析 前言 NoneBot2作为一款优秀的Python异步机器人框架其插件系统是功能扩展的核心。本文将全面讲解后端即时通讯NoneBot2插件开发指南从创建到加载全流程解析NoneBot2插件开发指南从创建到加载全流程解析 前言 NoneBot2作为一款优秀的Python异步机器人框架其插件系统是整个框架的核心功能之一。本文将后端即时通讯上一篇ng-zorro-antd List 组件完全指南从基础列表到栅格、加载更多与虚拟滚动下一篇3步搞定抖音无水印视频下载完整指南让你永久保存高清原创内容创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考