--- sidebar_position: 0 description: 在商店发布自己的插件 --- # 发布插件 import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; NoneBot 为开发者提供了分享插件的官方商店。本指南囊括**从创建项目到发布到 PyPI,最终提交商店审核**的全过程。 :::warning 警告 如果你的插件只是满足自用需求,则完全可以选择**不发布插件**。发布插件**不是**使用插件的必要条件。 NoneBot 社区对于插件有一定质量要求,对于不符合要求的插件,社区成员将会要求修改,直至符合要求后才能通过审核;如果长期未更新修改,社区将会关闭当前请求,之后如需发布请重新提交发布插件请求。相应的要求会在本章节以下部分介绍。 ::: :::tip 提示 本章节仅包含插件发布流程指导,插件开发请查阅前述章节。 ::: ## 准备工作 ### 插件命名规范 NoneBot 插件使用下述命名规范: - 对于**项目名**,建议统一以 `nonebot-plugin-` 开头,之后为拟定的插件名字,词间用横杠 `-` 分隔; - **项目名**用于代码仓库名称、PyPI 包的发布名称等; - 本文使用 `nonebot-plugin-{your-plugin-name}` 为例。 - 对于**模块名**,建议与**项目名**一致,但词间用下划线 `_` 分隔,即统一以 `nonebot_plugin_` 开头,之后为拟定的插件名字; - **模块名**用于程序导入使用,应为插件文件(夹)的名称; - 本文使用 `nonebot_plugin_{your_plugin_name}` 为例。 ### 项目结构 :::tip 提示 本段所述的项目结构仅作推荐,不做强制要求。 ::: 插件程序本身结构可参考[插件结构](../tutorial/create-plugin.md#插件结构)一节,唯一区别在于,插件包可以直接处于项目顶层。 插件项目的一种组织结构如下: ```tree 📦 nonebot-plugin-{your-plugin-name} ├── 📂 nonebot_plugin_{your_plugin_name} │ ├── 📜 __init__.py │ └── 📜 config.py ├── 📜 pyproject.toml └── 📜 README.md ``` 功能开发可以在 `__init__.py` 中进行或在包内部创建其他模块并在 `__init__.py` 中导入。 ### 从项目模板开始 为降低新手门槛,我们提供三条清晰、完整、可复制的发布路径。 :::tip 提示 你只需选择一条与你习惯一致的路径,**完整跟随即可成功发布**。无需在不同工具间切换或猜测配置。 ::: NoneBot 生态目前有如下插件项目模板: - [RF-Tar-Railt/nonebot-plugin-template](https://github.com/RF-Tar-Railt/nonebot-plugin-template) 此路径使用 **PDM** 项目管理器,符合 PEP 621 标准,自动化程度高。 - [fllesser/nonebot-plugin-template](https://github.com/fllesser/nonebot-plugin-template) 此路径使用 **uv** 项目管理器和 **PoeThePoet** 任务运行器,构建速度快,适合追求效率的开发者。 - [A-kirami/nonebot-plugin-template](https://github.com/A-kirami/nonebot-plugin-template) 此路径使用 **Poetry** 项目管理器,适合熟悉传统 Python 生态的开发者。 #### 1. 创建项目 1. 访问上述三个模板之一。 2. 点击 **“Use this template”** → **“Create a new repository”**。 3. 仓库名称填写:`nonebot-plugin-{your-plugin-name}`(此部分以 `nonebot-plugin-weather` 为例)。 4. 点击 **“Create repository from template”**。 #### 2. 配置发布权限 1. 进入新仓库 → **Settings** → **Actions** → **General**。 2. 在 **Workflow permissions** 下,勾选 **“Read and write permissions”** → 点击 **Save**。 #### 3. 全局替换项目信息 在仓库中点击 **“Add file”** → **“Create new file”**,创建一个空文件 `LICENSE`,选择开源协议并提交(此操作会触发工作流)。 然后在本地克隆仓库,使用编辑器对以下内容进行**全局替换**: :::tip 提示 此部分以“天气插件”为例,实际的替换内容应该根据你所创建的插件名称等相应调整。 ::: | 原内容 | 替换为 | | ------------------------------ | ---------------------------------- | | `nonebot-plugin-template` | `nonebot-plugin-weather` | | `nonebot_plugin_template` | `nonebot_plugin_weather` | | `` | `天气查询` | | `` | `查询指定城市的实时天气与未来预报` | | `` | `你的GitHub用户名` | | `` | `你的邮箱` | #### 4. 安装依赖与开发 ```bash # 安装 PDM(若未安装) curl -sSL https://pdm-project.org/install-pdm.py | python3 - # 安装项目依赖(自动创建虚拟环境) pdm sync # 添加新依赖(如 httpx) pdm add httpx ``` ```bash # 安装 uv(Windows) powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # 安装 uv(macOS/Linux) curl -LsSf https://astral.sh/uv/install.sh | sh # 安装所有依赖(含 dev) uv sync --all-groups -p 3.12 # 添加新依赖 uv add httpx ``` ```bash # 安装 Poetry(推荐方式) curl -sSL https://install.python-poetry.org | python3 - # 安装项目依赖 poetry install # 添加新依赖 poetry add httpx ``` #### 5. 更新版本并发布 [bump-my-version](https://github.com/callowayproject/bump-my-version) 是一个功能强大、可配置的 Python 项目版本更新工具,支持自动提交到 Git 等 VCS。 ```bash # 安装 bump-my-version pdm add --dev bump-my-version # 更新 patch 版本 pdm run bump patch # 推送 tag 触发发布 git push origin --tags ``` ```bash # 更新 patch 版本 uv run poe bump patch # 推送 tag 触发发布 git push origin --tags ``` ```bash # 安装 bump-my-version poetry add --dev bump-my-version # 更新 patch 版本 poetry run bump patch # 推送 tag 触发发布 git push origin --tags ``` 需要安装 PDM 插件 [pdm-bump](https://github.com/carstencodes/pdm-bump)。 ```bash # 安装 pdm-bump pdm self add pdm-bump # 更新 patch 版本 pdm bump patch # 推送 tag 触发发布 git push origin --tags ``` ```bash # 更新 patch 版本 uv version --bump patch # 创建相应提交与标签 git add pyproject.toml git commit -m "chore: release v0.1.1" # 替换为实际的版本号 git tag v0.1.1 # 替换为实际的版本号 # 推送 tag 触发发布 git push origin --tags ``` ```bash # 更新版本(自动提交并打标签) poetry version patch # 推送 tag 触发发布 git push origin --tags ``` 手动更新 `pyproject.toml` 中的 `version` 字段,然后推送 tag 触发发布工作流 ```bash git add pyproject.toml git commit -m "chore: release v0.1.1" # 替换为实际的版本号 git tag v0.1.1 # 替换为实际的版本号 git push origin --tags ``` 推送 `v*` 标签后,模板提供的 GitHub Actions 工作流将自动构建并发布到 PyPI。 #### 6. 发布到 [PyPI](https://pypi.org) 不同模板使用的发布方式可能不同,具体配置流程参考对应模板的详细使用指南。 根据选用的构建系统,在项目的 `pyproject.toml` 中填入必要信息后进行构建与发布。 :::tip 提示 不同构建工具的使用可能存在差别。本文仅以 [`pdm`](https://pdm-project.org/zh/latest/), [`poetry`](https://python-poetry.org/docs/), [`setuptools`](https://setuptools.pypa.io/en/latest/) 构建系统**本地构建与发布**为示例讲解,其余构建/管理工具等和自动化构建的用法请读者自行探索。 ::: ```bash poetry publish --build # 构建并发布 # 等效于以下两个命令 poetry build # 只构建 poetry publish # 只发布先前的构建 ``` ```bash pdm publish # 构建并发布 # 等效于以下两个命令 pdm build # 只构建 pdm publish --no-build # 只发布先前的构建 ``` ```bash pip install build twine # 安装通用构建与发布工具 python -m build --sdist --wheel . # 只构建 twine upload dist/* # 只发布先前的构建 ``` :::tip 提示 发布前建议自行测试构建包是否可用,避免遗漏代码文件或资源文件等问题。 ::: ## 基本要求 无论你选择哪条路径,以下内容**必须**完成,否则无法通过商店自动检查: ### 能够正确加载 插件包必须能够被 NoneBot 正确加载,在商店审核中会通过 **NoneFlow** 自动化加载测试进行。 #### 依赖其他插件 如果插件依赖其他插件提供的功能,则**必须**在代码中使用 `require()` 来引入该插件,然后才能 `import` 该插件提供的功能。具体细节参阅[跨插件访问](../advanced/requiring.md)。 使用示例如下: ```python title=nonebot_plugin_weather/__init__.py from nonebot import require require("nonebot_plugin_apscheduler") from nonebot_plugin_apscheduler import scheduler ``` #### 不能零配置加载的插件 如果插件需要必要配置项才能正常导入,则**必须**在商店提交表单中填写必要的配置项内容。 但一种更好的做法是,**将插件设计为零配置即可加载**(允许缺少必要配置项时插件仍能正常导入,但不执行需要相应配置项的功能),尤其是对于一些必要配置含有敏感信息(如密钥、Token、API Key 等)的插件。这样可以避免在商店提交表单时填写敏感信息的风险。 ### 插件元数据 插件包**必须**填写元数据才能通过 **NoneFlow** 自动化检查。 下面是一个示例: ```python title=nonebot_plugin_weather/__init__.py from nonebot.plugin import PluginMetadata from .config import Config __plugin_meta__ = PluginMetadata( # 基本信息(必填) name="天气查询", # 插件名称 description="查询指定城市的实时天气与未来预报", # 插件介绍 usage="发送【天气 城市名】获取天气信息", # 插件用法 # 发布额外信息 type="application", # 插件分类 # 发布必填,当前有效类型有:`library`(为其他插件编写提供功能),`application`(向机器人用户提供功能)。 homepage="https://github.com/你的用户名/nonebot-plugin-weather", # 发布必填。 config=Config, # 插件配置项类,如果有配置类则必须填写。 supported_adapters={"~onebot.v11"}, # 支持的适配器集合,其中 `~` 在此处代表前缀 `nonebot.adapters.`,其余适配器亦按此格式填写。 # 若插件只使用了 NoneBot 基本抽象,应显式填写 None,否则应该列出插件支持的适配器。 ) ``` :::caution 注意 `__plugin_meta__` 变量**必须**处于插件最外层(如 `__init__.py` 中),否则无法正常识别。 一般做法是在 `__init__.py` 中定义 `__plugin_meta__`。 ::: #### 继承其他插件支持的适配器 如果你的插件依赖于其他插件提供的支持功能,而其他插件可能支持更少的适配器,这时就应该使用 [inherit_supported_adapters()](../api/plugin/load#inherit-supported-adapters) 函数来继承其他插件支持的适配器。 示例用法如下: ```python title=nonebot_plugin_weather/__init__.py from nonebot import require from nonebot.plugin import PluginMetadata, inherit_supported_adapters from .config import Config require("nonebot_plugin_alconna") # 必须先 require 才能被 inherit_supported_adapters 处理 __plugin_meta__ = PluginMetadata( name="天气查询", description="查询指定城市的实时天气与未来预报", usage="发送【天气 城市名】获取天气信息", type="application", homepage="https://github.com/你的用户名/nonebot-plugin-weather", config=Config, supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna"), # 继承 nonebot_plugin_alconna 插件的适配器支持列表 ) ``` ### 准备项目主页 通常可以使用 GitHub 项目页面作为项目主页,在 `README.md` 文件中编写插件介绍等内容。 内容大致包括: - 插件功能介绍; - 安装方法 - **必须**有 NB-CLI 方式安装 - 可选依赖可以给出其他安装方式 - **不得**使用旧式的 `bot.py` 配置 - 插件配置项(如 `Config` 类字段,若无可跳过) - 插件设置的触发规则(若无可跳过) - 插件的其它用法(按需编写) - 效果图、权限说明(按需编写) ## 质量要求 以下内容**强烈建议**完成,否则社区成员将会要求修改: ### 依赖管理原则 - **必须**包含 `nonebot2`。 - **必须**将插件直接使用的适配器加入依赖列表,如:使用 OneBot 适配器的插件应添加 `nonebot-adapter-onebot` 依赖; - **禁止**使用 `==` 锁定单一版本,使用 `>=` 或 `~=`。 - **禁止**添加 `nonebot`(V1)作为依赖。 - 所有在代码中 `import` 的第三方库,必须在 `pyproject.toml` 的 `dependencies` 中列出。 ### 避免误用同步操作 NoneBot 是一个异步框架,插件中**禁止**使用任何可能阻塞事件循环的同步操作,例如: - 同步 HTTP 请求(如 `requests` 库); **推荐**操作(以 `httpx` 为例): ```python import httpx async with httpx.AsyncClient() as client: response = await client.get("https://api.example.com/data") # 异步操作,不阻塞机器人 ``` **禁止**操作: ```python import requests requests.get("https://api.example.com/data") # 同步操作,会阻塞机器人 ``` - 其他可能长时间运行阻塞事件循环的操作。 ### 本地文件存储 如果插件需要在本地存储数据、配置或缓存文件,**必须**使用 [`nonebot-plugin-localstore`](https://github.com/nonebot/plugin-localstore) 管理,具体细节参阅[本地存储](../best-practice/data-storing.md)章节。 参考示例: ```python title=nonebot_plugin_weather/__init__.py from pathlib import Path from nonebot import require require("nonebot_plugin_localstore") import nonebot_plugin_localstore as store # 获取插件缓存文件(夹)路径 weather_cache_dir: Path = store.get_plugin_cache_dir() weather_cache_file: Path = store.get_plugin_cache_file("cache.json") # 获取插件配置文件(夹)路径 weather_config_dir: Path = store.get_plugin_config_dir() weather_config_file: Path = store.get_plugin_config_file("config.toml") # 获取插件数据文件(夹)路径 weather_data_dir: Path = store.get_plugin_data_dir() weather_data_file: Path = store.get_plugin_data_file("resource-index.json") ``` ## 商店审核 ### 提交申请 完成在 PyPI 的插件发布流程后,前往[商店](/store/plugins)页面,切换到插件页签,点击 **发布插件** 按钮。 在弹出的插件信息提交表单内,填入您所要发布的相应插件信息。请注意,如果插件需要必要配置项才能正常导入,请在“插件配置项”中填写必要的内容(请勿填写密钥等敏感信息)。 完成填写后,点击 **发布** 按钮,这将自动跳转 NoneBot 仓库内的“发布插件”页面。确认信息无误后点击页面下方的 `Submit new issue` 按钮进行最终提交即可。 ### 等待插件审核 插件发布 Issue 创建后,将会经过 **NoneFlow Bot** 的自动前置检查,以确保插件信息正确无误、插件能被正确加载。 :::tip 提示 若插件检查未通过或信息有误,**不必**关闭当前 Issue。只需更新插件并上传到 PyPI/修改信息后勾选插件测试勾选框即可重新触发插件检查。 ::: 之后,NoneBot 的维护者和一些志愿者会初步检查插件代码,帮助减少该插件的问题。 完成这些步骤后,您的插件将会被自动合并到[商店](/store/plugins),而您也将成为 [**NoneBot 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors)的一员。