Custom-bot 介绍:共同铸造,无限成长的自动化传奇

欢迎来到 Custom-bot 的世界! 这不仅是一个功能强大的 Python 异步机器人框架,更是一个开放、协作的平台,邀请每一位。 它并非封闭的终点,而是你我手中蕴含无限可能、等待共同雕琢的璞玉,是我们一起谱写自动化无限成长传奇的起点。

在这里,“自定义”是核心驱动力。 凭借其高度灵活的 架构,你可以轻松打破协议壁垒,让 Bot 通过 WebSocket(正/反向)、HTTP、MQTT 等多种方式联通所需的服务与平台。 而强大的适配器 (Adapter)插件 (Plugin) 系统,则允许你像锻造传奇装备一般,为 Bot 精准注入独特的命令响应、智能的事件处理和强大的业务逻辑,使其成为满足你特定需求的完美工具。 分享你的适配器和插件,让整个社区受益!

“无限成长”是我们共同的愿景与承诺。成本 构建,拥有处理高并发任务的先天优势。 其asyncio模块化设计保证了核心的稳定与高效,更为功能的迭代与扩展铺就了清晰、便捷的道路。 无论是对接新兴的 AI 服务,集成复杂的业务流程,还是扩展到全新的应用场景,这个框架都能作为我们共同的坚实后盾,与社区的集体智慧一同,不断突破自进化

核心特性一览:

  • 协议无关: 通过适配器轻松支持 HTTP, WebSocket 等多种协议,欢迎贡献新的适配器!

  • 功能无限: 插件化设计,自由添加命令、事件处理和业务逻辑,分享你的创意插件!

  • 异步高效: 基于 ,性能卓越,响应迅速。asyncio

  • 配置驱动:山药

  • 易于管理:内置 Web UI (基于 FastAPI)

  • 部署便捷: 提供 Docker 支持,简化部署和运维流程。

  • 开放协作: 鼓励社区贡献代码、分享经验、共同完善框架。

我们相信,每一个由 Custom-bot 驱动的应用,都有潜力成为一个真正的。 更重要的是,通过传奇大家的共同完善,自定义机器人,成为一个更加强大、稳定和易用的自动化基础设施。成长

欢迎加入我们,一起探索、贡献、学习,共同用 Custom-bot 铸造属于我们的自动化传奇!

Custom-bot 文档结构和主要内容框架

请注意,这是一个,你需要根据你的项目实际实现细节来填充和完善具体内容,特别是“安装”、“使用”、“开发文档”下的具体步骤和 API 细节,“常见问题Q&A”和“指南”需要根据用户反馈和项目特点来编写。文档框架

我将把文档分成几个主要部分,并提供每个部分的核心内容。


文档根目录 (例如 或项目根目录的 及子页面)文档/README.md

README.md (项目根目录)

# Custom-bot: 一个灵活、可扩展的 Python 异步机器人框架

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
<!-- 在这里添加其他徽章,例如构建状态、代码覆盖率等 -->

**作者:** Xinz/ahhhahh (及贡献者)
**当前版本:** 1.0.0-dev (示例)

Custom-bot 是一个使用 Python 和 asyncio 构建的现代化机器人框架。它旨在提供一个模块化、易于扩展的基础,让开发者可以轻松对接不同的聊天平台和协议,并编写自定义的功能插件。

**核心特性:**

*   **异步优先:** 基于 `asyncio`,实现高性能 I/O 处理。
*   **多协议支持:** 通过可插拔的适配器 (Adapter) 对接不同协议 (HTTP Webhook, WebSocket, Telegram, QQ OneBot 等)。
*   **插件化:** 通过处理器 (Handler/Plugin) 轻松添加自定义命令、事件处理和业务逻辑。
*   **Web 管理界面:** 提供图形化界面监控状态、管理配置、查看日志等。
*   **定时任务:** 内置定时任务调度器 (APScheduler)。
*   **配置驱动:** 使用 YAML 文件进行灵活配置。
*   **Docker 支持:** 提供 Dockerfile 和 docker-compose 配置,方便部署。

## 快速开始

**使用 Docker (推荐):**

1.  **克隆仓库:** `git clone <your-repository-url> Custom-bot && cd Custom-bot`
2.  **配置:** 编辑 `config/config.yaml` 文件 (首次运行会自动生成模板),设置管理员、启用适配器和插件等。
3.  **构建并启动:** `docker-compose up --build -d`
4.  **访问 Web UI:** `http://<服务器IP>:9090` (默认端口)

**直接运行:**

1.  **克隆仓库:** `git clone <your-repository-url> Custom-bot && cd Custom-bot`
2.  **创建虚拟环境 (推荐):** `python -m venv .venv && source .venv/bin/activate` (或 Windows 对应命令)
3.  **安装依赖:** `pip install -r requirements.txt`
4.  **首次运行生成配置:** `python main.py` (然后按 Ctrl+C 停止,编辑 `config/config.yaml`)
5.  **启动:** `python main.py`

## 文档导航

*   **[安装指南](docs/安装.md):** 详细的安装和环境配置步骤。
*   **[使用手册](docs/使用.md):** 如何配置、运行和管理 Custom-bot。
*   **[开发文档](docs/开发文档/):** 面向开发者的详细指南。
    *   **[核心开发](docs/开发文档/核心开发.md):** 框架内部架构和原理。
    *   **[适配器开发](docs/开发文档/适配器开发.md):** 如何添加新的协议支持。
    *   **[插件开发](docs/开发文档/插件开发.md):** 如何编写新的功能插件。
    *   **[远程开发 (VSCode)](docs/开发文档/远程开发.md):** (可选) 使用 VSCode Remote - Containers 进行开发的指南。
*   **[常见问题 Q&A](docs/常见问题.md):** 解答使用中可能遇到的问题。
*   **[更新日志](CHANGELOG.md):** (推荐单独文件) 记录版本变更历史。
*   **[帮助与支持](#帮助与支持):** 获取帮助的途径。

## 贡献

欢迎参与 Custom-bot 的开发!请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) (需要创建) 了解贡献指南。

## 帮助与支持

*   **提交 Issue:** 如果您遇到 Bug 或有功能建议,请在 [GitHub Issues](<your-repo-url>/issues) 中提出。
*   **社区讨论:** (可选) 加入我们的 [讨论区/QQ群/Telegram群](<link-to-community>) 进行交流。

## License

Custom-bot 使用 [MIT License](LICENSE) (需要创建 LICENSE 文件)。

请谨慎使用代码。


docs/安装.md

# Custom-bot 安装指南

本文档提供安装 Custom-bot 的详细步骤和环境要求。

## 环境要求

### 方式一:直接运行 Python 代码

*   **Python:** 建议使用 Python 3.8 或更高版本。可以通过 `python --version` 或 `python3 --version` 命令检查。如果未安装,请访问 [Python 官网](https://www.python.org/) 下载并安装。
*   **pip:** Python 包管理器,通常随 Python 一起安装。可以通过 `pip --version` 或 `pip3 --version` 检查。
*   **虚拟环境 (推荐):** 为了隔离项目依赖,强烈建议使用虚拟环境(如 `venv`)。
*   **(可选) Git:** 用于从代码仓库克隆项目。
*   **(可选) Node.js & npm/yarn:** 如果需要执行依赖 Node.js 的插件、定时任务脚本,或者进行前端 Web UI 开发,需要安装它们。可以从 [Node.js 官网](https://nodejs.org/) 下载或使用包管理器(如 apt, yum, brew)或 NodeSource 进行安装。
*   **(可选) 系统依赖:** 某些 Python 库(尤其涉及 C 扩展的)可能需要系统级的编译工具(如 `build-essential`, `gcc`)或特定的开发库(如 `libffi-dev`)。如果在 `pip install` 过程中遇到编译错误,请根据错误提示安装相应的系统依赖。

### 方式二:使用 Docker (推荐)

*   **Docker Engine:** 确保已安装并运行 Docker 服务。版本建议 19.03 或更高。请参考 [Docker 官方文档](https://docs.docker.com/engine/install/) 进行安装。可以通过 `docker --version` 检查。
*   **Docker Compose:** 用于管理多容器 Docker 应用。V2 版本(作为 Docker 插件)通常随 Docker Desktop 或 Docker Engine for Linux 一起安装。可以通过 `docker compose version` (注意没有连字符) 检查。如果是旧的 V1 版本 (`docker-compose --version`) 也可以工作,但建议使用 V2。

## 安装步骤

### 1. 获取代码

使用 Git 克隆项目仓库:

```bash
git clone <your-repository-url> Custom-bot
cd Custom-bot

请谨慎使用代码。

(将 '<your-repository- 替换为实际地址)<您的存储库 url>

如果无法使用 Git,可以下载代码压缩包并解压。

2. 准备运行环境

方式一:直接运行

进入项目目录:

cd Custom-bot

请谨慎使用代码。

创建并激活 Python 虚拟环境 (推荐):

python3 -m venv .venv # 创建 .venv 目录
# Linux/macOS 激活:
source .venv/bin/activate
# Windows (cmd):
# .venv\Scripts\activate.bat
# Windows (PowerShell):
# .\.venv\Scripts\Activate.ps1

请谨慎使用代码。重击

(之后所有 和 命令都在虚拟环境中执行)果仁蟒

安装 Python 依赖:

pip install -r requirements.txt

请谨慎使用代码.

如果在安装过程中遇到错误,请根据错误提示解决(例如安装缺失的系统依赖)。

(可选) 安装 Node.js 依赖 (如果需要):
如果项目包含需要 Node.js 依赖的插件或脚本,或者你需要开发前端:

# 假设 webui 目录包含 package.json
cd webui
npm install # 或者 yarn install
cd .. # 返回项目根目录

请谨慎使用代码。

方式二:使用 Docker

  1. 无需手动安装依赖: Dockerfile 会处理 Python 依赖。 如果需要 Node.js,需要在 Dockerfile 中添加安装步骤。

  2. 确保 Docker 服务正在运行。

3. 初始化配置和目录

方式一:直接运行

首次运行 会自动完成:main.py

python main.py

请谨慎使用代码。

  • 它会检查并创建 , 适配器配置、 、 、 、分贝原木插件, , 'SCR公共脚本 等目录。

  • 如果 文件不存在,它会使用默认模板创建该文件。config/config.yaml

  • 看到提示后,按 停止程序,然后进行下一步的配置。 Ctrl + C 组合键

方式二:使用 Docker

首次运行 'docker-compose up --build 会完成:docker-compose up --build

docker-compose up --build -d # -d 表示后台运行

请谨慎使用代码。

  • Docker 会构建镜像(包括安装 Python 依赖)。

  • 启动容器时, 会检查通过到宿主机的目录。 如果宿主机上对应的目录不存在,Docker 通常会自动创建(但权限可能需要调整)。main.py卷挂载

  • 同样,如果 在挂载点(即宿主机的 目录)不存在,容器内的启动逻辑会创建它。config/config.yamlconfig/

  • 容器启动后,你可以直接编辑宿主机上的 。 config/config.yaml

命令docker 运行

拉取镜像

docker pull sevenxinz/xinz:tagname

以下是一个,它包含了运行 示例 命令docker 运行自定义机器人 通常需要的和端口映射卷挂载。 你需要根据你的实际情况修改其中的路径和端口。

docker run -d \
  --name custom-bot-instance \
  -p 9090:9090 \
  -p 8765:8765 \
  # -p 8088:8088 \ # 如果你需要映射 HTTP Webhook 端口,取消注释并确保端口正确
  # --- 在这里添加其他需要映射的适配器端口 ---
  -v $(pwd)/config:/app/config \
  -v $(pwd)/db:/app/db \
  -v $(pwd)/logs:/app/logs \
  -v $(pwd)/plugins:/app/plugins \
  -v $(pwd)/scripts:/app/scripts \
  -v $(pwd)/public:/app/public \
  -e TZ=Asia/Shanghai \
  --restart unless-stopped \
  sevenxinz/xinz:tagname

请谨慎使用代码

命令详解:

  • docker 运行: 启动一个新容器的命令。

  • -d: Detached 模式,让容器在后台运行。 如果你想在前台看到日志,可以去掉这个参数。

  • --name 自定义机器人实例: 给容器指定一个名字,方便后续管理(例如 ,docker stop 自定义机器人实例docker logs 自定义机器人实例)。 你可以改成你喜欢的名字。

  • - 9090:9090 : 将宿主机的 9090 端口映射到容器的 9090 端口 (Web UI)。 格式是 。 -p <宿主机端口>:<容器端口>

  • - 8765:8765 : 将宿主机的 8765 端口映射到容器的 8765 端口 (反向 WebSocket)。

  • - 8088:8088 (注释掉的): 示例,如果你在 'config.yaml 中启用了 HTTP Webhook 适配器并监听在 8088 端口,你需要取消注释这一行(或修改端口号)。config.yaml

  • -v $(pwd)/config:/app/config: 卷挂载。 将下的 子目录挂载到容器当前宿主机目录配置/app/config 中 目录。

    • $(pwd: 表示当前工作目录 (运行 命令时所在的目录)。 确保你运行命令时位于 docker 运行自定义机器人 项目的根目录下。

    • /app/config 中: 这是 中设置的工作目录 Dockerfile 文件/应用程序 下的 目录。 配置

    • 你需要根据你的实际项目结构调整 这部分路径。 $(pwd)/配置 如果你的配置文件放在别处,需要修改。

  • -v $(pwd)/db:/app/db: 挂载数据库目录,实现数据持久化。

  • -v $(pwd)/logs:/app/logs: 挂载日志目录,方便在宿主机查看日志。

  • -v $(pwd)/插件:/app/plugins: 挂载插件目录,允许在宿主机添加/修改插件。

  • -v $(pwd)/scripts:/app/scripts: 挂载脚本目录,用于定时任务。

  • -v $(pwd)/public:/app/public: 挂载公开资源目录。

  • -e TZ=亚洲/上海: 设置容器内的时区环境变量,确保日志时间等正确。 你可以根据需要修改时区。

  • --restart 除非停止: 设置容器的重启策略。 表示除非手动停止容器,否则容器在退出(例如因为错误或服务器重启)后会自动重启。 其他选项如 除非停止总是 或 。

  • xinz/custom-bot:最新: 指定要使用的 Docker 镜像名称和标签。

使用前的准备:

  1. 确保镜像存在:

    • 如果是你自己构建的,确保已经成功运行了 。 docker build -t xinz/custom-bot:latest 的 .

    • 如果是从 Docker Hub 拉取的,运行 。 docker pull xinz/custom-bot:latest

  2. 准备宿主机目录: 在你(通常是你的项目根目录)下,确保存在 打算运行 命令的目录docker 运行配置, , , , 'p分贝原木插件, , 'pu脚本公共 这些子目录。 如果不存在,需要先创建 ()。 mkdir config 数据库日志 插件 脚本 public

  3. 准备配置文件: 确保 文件存在于宿主机的 config/config.yaml配置 目录下,并且内容是你配置好的。

  4. 检查端口占用: 确保你要映射的宿主机端口(9090, 8765 等)没有被其他程序占用。

  5. 权限问题: 确保 Docker 守护进程(或者运行 的用户,如果未使用 docker 运行须藤)有权限读取和写入你挂载的宿主机目录(特别是 和 分贝原木)。 如果遇到权限错误,参考之前关于 的解决方案(例如权限错误sudo chown -R <UID>: <GID> ./logs ./db<UID>

运行命令:

将上面提供的 命令复制到你的终端,docker 运行确保你当前位于包含 ,, 等子目录的项目根目录下配置分贝原木

注意: docker 运行 命令只用于容器。 之后管理容器,你应该使用:首次创建并启动

  • docker start 自定义机器人实例: 启动已停止的容器。

  • docker stop 自定义机器人实例: 停止正在运行的容器。

  • docker restart 自定义机器人实例: 重启容器。

  • docker logs -f 自定义机器人实例: 查看容器日志。

  • docker rm custom-bot-instance: 删除已停止的容器(数据卷内容会保留)。

4. 配置框架

打开 文件,根据文件内的注释和你自己的需求进行配置:config/config.yaml

  • 设置 。 kernel.admins 的

  • 启用并配置你需要的 (例如 QQ 适配器的端口、路径、Token)。适配器

  • 启用并配置你需要的 (插件)。 处理器

  • (可选) 配置 。 scheduled_tasks

  • (可选) 修改 的端口等设置。对于 Web

重要: 对于 Docker 运行方式,修改配置文件后需要才能生效:重启容器docker-compose restart 自定义机器人

安装和初步配置完成! 现在可以参考 来运行和管理 Custo使用手册

---

**`docs/使用.md`**

```markdown
# Custom-bot 使用手册

本文档介绍如何配置、运行、管理和与 Custom-bot 进行交互。

## 启动与停止

### 方式一:直接运行

*   **启动:**
    1.  进入项目根目录 (`Custom-bot/`)。
    2.  激活 Python 虚拟环境 (如果使用): `source .venv/bin/activate` (或 Windows 对应命令)。
    3.  运行主程序: `python main.py`
    4.  程序将在前台运行,控制台会输出日志。
*   **停止:** 在运行程序的终端中按 `Ctrl+C`。框架会尝试优雅关闭。

### 方式二:使用 Docker

*   **启动:**
    1.  进入项目根目录 (`Custom-bot/`)。
    2.  运行 Docker Compose: `docker-compose up -d` (`-d` 表示后台运行)。
    3.  首次运行或修改了 Dockerfile/requirements.txt 后,使用 `docker-compose up --build -d`。
*   **查看状态:** `docker-compose ps` 查看容器是否正在运行 (`running`)。
*   **查看日志:** `docker-compose logs -f custom-bot` (`-f` 持续跟踪)。按 `Ctrl+C` 停止跟踪。
*   **停止:** `docker-compose down` (停止并移除容器)。
*   **重启:** `docker-compose restart custom-bot` (例如修改配置后需要重启)。

## 配置文件 (`config/config.yaml`)

这是控制 Custom-bot 行为的核心文件。详细配置项说明请参考 [安装指南](安装.md) 或文件内的注释。

**关键配置区域:**

*   `kernel`: 核心设置,如日志级别、全局管理员。
*   `webui`: Web 管理界面开关、地址、端口。
*   `database`: 数据库设置 (目前主要是路径)。
*   `adapters`: 配置和启用协议适配器。**你需要在这里启用至少一个适配器才能让机器人与外界通信。**
*   `handlers`: 配置和启用功能插件。**你需要在这里启用至少一个插件才能让机器人响应命令或事件。**
*   `scheduled_tasks`: 配置定时任务。

**修改配置后,务必重启框架 (直接运行则停止后重开,Docker 则 `docker-compose restart`)。**

## Web 管理界面 (Web UI)

如果 `webui.enabled` 设置为 `true`,你可以通过浏览器访问 Web UI。

*   **默认地址:** `http://<服务器IP>:9090` (端口在 `config.yaml` 中配置)。
*   **功能 (设计目标):**
    *   **仪表盘:** 查看实时系统状态 (CPU, 内存), BOT 运行信息。
    *   **网络配置:** 管理适配器,查看连接状态。
    *   **插件市场 (TODO):** 管理插件的启用/禁用状态(未来可能支持安装/卸载)。
    *   **文件管理 (高风险):** 浏览指定的项目目录 (需要强安全控制)。
    *   **终端 (高风险):** Web Shell (需要极高的安全措施)。
    *   **设置:** 修改部分配置项。
    *   **日志查看:** 查看不同组件的日志。
    *   ... 以及其他管理功能。

**(注意:具体可用功能取决于开发进度。高风险功能默认可能未实现或需要显式安全配置。)**

## 通过聊天平台交互

你需要配置并启用至少一个适配器 (例如 `QQAdapter`) 和至少一个处理器插件 (例如 `基础命令`)。

1.  **配置适配器:** 在 `config.yaml` 中正确配置适配器,例如 QQ 适配器的监听端口、路径、Access Token (如果需要)。
2.  **配置外部客户端:** 配置你的 QQ 客户端 (如 go-cqhttp) 使用反向 WebSocket 连接到 Custom-bot 配置的地址、端口和路径,并设置相同的 Access Token (如果需要)。确保消息格式设置为 `array` 或 `object` (取决于你插件的处理能力,目前示例适配器能接收两者)。
3.  **启动外部客户端和 Custom-bot。**
4.  **发送命令:** 在 QQ 聊天窗口中发送基础命令插件定义的命令,例如:
    *   `时间`
    *   `版本`
    *   `我的id`
    *   (在群里) `群id`
    *   (如果你是管理员) `get system name` (假设存在这个数据)
    *   (如果你是管理员) `set system name 新名字`
    *   (如果你是管理员) `del system name`
    *   (如果你是管理员) `重启`

## 日志文件

日志文件位于项目根目录下的 `logs/` 目录。

*   `kernel.log`: 核心运行日志。
*   `error.log`: 仅记录错误和严重级别的日志。
*   其他日志文件可能由适配器或插件生成。

可以通过 Web UI 查看日志,或者直接在服务器上使用 `cat`, `tail`, `less` 等命令查看。对于 Docker 用户,也可以使用 `docker-compose logs` 命令。

## 目录结构说明 (用户相关)

当你运行 Custom-bot 时,项目根目录下会包含以下对用户比较重要的目录:

*   **`config/`**: 存放核心配置文件 `config.yaml`。**你需要编辑这个文件来配置机器人。**
*   **`db/`**: 存放数据库文件 (例如 TinyDB 的 JSON 文件)。**通常不需要手动编辑。**
*   **`logs/`**: 存放运行时日志。用于排查问题。
*   **`plugins/`**: 存放插件代码。**你需要将下载或编写的插件放入此目录下的二级子目录中。**
*   **`scripts/`**: 存放定时任务脚本。**你需要将编写的定时任务脚本放在这里。**
*   **`public/`**: 存放可通过 Web 访问的公开静态资源。机器人可以将生成的图片等文件放在这里,然后通过 `http://<服务器IP>:<WebUI端口>/public/文件名` 的链接发送。**注意不要放敏感文件。**
*   **`Adapter/`**: (主要面向开发者) 存放协议适配器代码。
*   **`core/`**: (主要面向开发者) 框架核心代码。
*   **`webui/`**: 存放 Web UI 相关文件。
    *   `webui/dist/`: 存放前端构建后的静态文件,由 FastAPI 提供服务。**通常不需要手动修改。**
    *   `webui/src/`: (如果你需要开发前端) 存放前端源代码。
*   `main.py`: 程序主入口。
*   `web_server.py`: Web 服务器实现。
*   `Dockerfile`, `docker-compose.yml`, `.dockerignore`: Docker 相关文件。
*   `requirements.txt`: Python 依赖列表。

## 更新框架

1.  **备份:** 在更新前,**强烈建议备份**您的 `config/`, `db/`, `plugins/`, `scripts/` 目录,以防更新引入不兼容的更改。
2.  **获取新代码:** 使用 `git pull` (如果您使用 Git) 或下载新版本的代码压缩包。
3.  **替换代码:** 用新版本的代码覆盖旧的代码文件(除了您需要保留的配置、数据、插件、脚本目录)。注意查看更新日志,了解是否有重大变更。
4.  **更新依赖:**
    *   **直接运行:** 激活虚拟环境后,运行 `pip install -r requirements.txt --upgrade`。
    *   **Docker:** 运行 `docker-compose build --no-cache custom-bot` 来基于新的 `requirements.txt` 重建镜像。
5.  **重启框架:**
    *   **直接运行:** 停止旧进程,启动新进程 `python main.py`。
    *   **Docker:** `docker-compose down && docker-compose up -d`。
6.  **检查兼容性:** 查看更新日志,确认您的插件、适配器配置或脚本是否需要因框架更新而进行调整。

## 寻求帮助

*   查阅 [常见问题 Q&A](常见问题.md)。
*   在项目的 [GitHub Issues](<your-repo-url>/issues) 中搜索或提问。
*   加入社区讨论 (如果提供)。

请谨慎使用代码。


接下来是面向开发者的文档(核心开发、适配器开发、插件开发)。

4. 核心开发文档 (docs/开发文档/核心开发.md)

# Custom-bot 核心开发文档

**目标读者:** 希望深入理解 Custom-bot 框架内部工作原理、进行框架本身二次开发或排查核心问题的开发者。

## 架构回顾

Custom-bot 采用基于 `asyncio` 的事件驱动和模块化架构。核心组件及其交互关系已在 [使用手册](使用.md) 和项目 `README.md` 中概述。这里重点阐述内部实现细节和开发者需要关注的接口。

## 关键组件实现细节

### Kernel (`core/kernel.py`)

*   **配置加载与管理:**
    *   使用 `ruamel.yaml` 库加载 `config.yaml`,能较好地保留注释和格式(主要在保存时体现)。
    *   `_load_config()` 负责读取文件并存入 `self.config` 字典。
    *   `get_config_value(key_path, default)` 提供安全的点分路径访问配置项的方法,避免因缺少键而抛出 `KeyError`。
*   **动态导入与加载 (`_import_class`, `_load_adapters`, `_load_handlers`):**
    *   `_import_class(class_path, expected_base)` 是核心的动态加载函数:
        *   接收 Python 类路径字符串 (如 `Adapter.qq_adapter.QQAdapter`) 和期望的基类 (如 `BaseAdapter`)。
        *   使用 `importlib.import_module` 动态导入模块。
        *   使用 `getattr` 获取模块中的类对象。
        *   进行类型检查,确保获取到的是类 (`isinstance(obj, type)`) 并且是预期基类的子类 (`issubclass(obj, expected_base)`)。
        *   处理各种导入和属性错误。
    *   `_load_adapters()`: 遍历 `config['adapters']`,对 `enabled: true` 的条目,调用 `_import_class` 加载适配器类,实例化,并调用其 `setup()` 方法,最后存入 `self.adapters` 字典。
    *   `_load_handlers()`:
        *   扫描 `plugins/` 下的二级目录中的 `.py` 文件。
        *   导入每个模块,并使用 `_parse_plugin_meta` 解析模块 Docstring 中的 `@` 元数据。
        *   查找模块中定义的 `BaseHandler` 子类。
        *   根据 `@name` 或类名确定 Handler ID。
        *   结合 `config['handlers']` 中的配置(`enabled`, `config`),实例化启用的 Handler,调用其 `setup()`,并添加到临时列表。
        *   **服务类插件 (`@service: true`)**: 如果元数据标记为服务,即使配置中未启用,也会尝试加载并执行其 `setup()`,但**不会**加入到用于消息处理的 `self.handlers` 列表。
        *   最后,根据 `priority` 对 `handler_instances` 列表进行**降序排序**,存入 `self.handlers`。
*   **生命周期 (`initialize`, `run_forever`, `shutdown`):**
    *   `initialize`: 编排配置加载、日志设置、数据库/调度器启动、组件加载和 Cron 任务调度的顺序。
    *   `run_forever`: 启动消息分发器 (`_message_dispatcher`) 和所有适配器的 `start()` 方法(通过 `_start_adapter_safe`),然后进入一个 `while self.is_running:` 循环,主要依靠 `asyncio.sleep` 和健康检查来维持运行,实际工作由后台任务完成。
    *   `shutdown`: 按安全顺序停止组件:设置 `self.is_running = False` -> 停止调度器 -> 停止所有适配器 (`_stop_adapter_safe`) -> 等待分发器处理完队列 -> 关闭数据库 -> 清理 `wait_input` -> 清理任务引用。
*   **消息流 (`queue_message`, `_message_dispatcher`, `_dispatch_single_message`, `send`):**
    *   `queue_message`: 简单的 `await self.message_queue.put(message)`,带运行状态检查。
    *   `_message_dispatcher`: 核心的消费者循环,处理 `asyncio.Queue`,调用 `_dispatch_single_message`。包含超时逻辑以在关闭时正常退出。
    *   `_dispatch_single_message`:
        1.  检查消息是否满足 `wait_input`。
        2.  如果不满足,按优先级遍历 `self.handlers`。
        3.  对每个 Handler 进行平台、权限、规则检查。
        4.  匹配成功则创建 `Sender` 对象,调用 `handler.handle()`。
        5.  根据返回值决定是否 `break` (停止匹配)。
        6.  包含基本的异常捕获和计时。
    *   `send`: 查找目标 `adapter_id` 对应的实例,调用其 `send()` 方法。
*   **定时任务 (`APScheduler`):**
    *   在 `initialize` 中启动 `AsyncIOScheduler`。
    *   `_schedule_plugin_cron_tasks` 在加载 Handler 后,根据 `@cron` 元数据添加 Job,执行 Handler 的 `execute_cron` 方法。
    *   提供 `schedule_task`, `cancel_task`, `get_scheduled_tasks` 作为公共 API (供 Web UI 或插件使用),其中 `schedule_task` 通过函数路径字符串动态添加任务。
*   **waitInput (`_wait_input_futures`, `register_wait_input`, `_handle_wait_input_timeout`, `satisfy_wait_input`, `clear_wait_input`):** 使用一个字典存储 `wait_key` 到 `(Future, TimerHandle)` 的映射。`register` 时添加记录并设置 `call_later` 定时器。`satisfy` 时找到 Future 设置结果并取消定时器。`_handle_wait_input_timeout` 在超时后设置异常并清理记录。
*   **系统方法 (`check_if_admin`, `request_restart`, `install_dependency`, `run_shell_command`):** 提供一些需要内核权限或状态才能执行的操作。**高风险操作必须在内部实现严格的安全检查和沙箱/隔离机制。** 目前示例代码中安全实现是缺失的。

### 消息 (`core/message.py`)

`InternalMessage` 是一个 `dataclass`,方便创建和访问属性。关键在于其字段设计能够承载来自不同平台的信息,并包含框架流转所需的状态和元数据。适配器负责**填充**它,处理器负责**读取**它,`Sender` 基于它创建交互上下文。

### 适配器基类 (`core/adapters.py`)

`BaseAdapter` 定义了适配器的骨架。子类必须实现核心的生命周期 (`setup`, `start`, `stop`) 和双向通信 (`send`, 以及在 `start` 中实现的接收逻辑并调用 `_push_to_kernel`)。`bridge` 属性提供了可选的扩展点。

### 处理器基类 (`core/handlers.py`)

*   `BaseHandler` 主要定义了接口和元数据处理逻辑。`_compile_rules` 将 `@rule` 字符串编译为 `re.Pattern` 对象。`check_platform`, `check_rule`, `check_admin` 提供了基础的匹配前检查逻辑。`handle` 和 `run_scheduled_task` 是子类必须或应该实现的核心方法。
*   `Sender` 是插件开发的核心交互对象。它通过持有 `kernel`, `message`, `adapter` 的引用,提供了方便且上下文感知的方法,简化了插件与框架底层的交互。

### 数据库 (`core/database.py`)

目前使用 TinyDB 的封装。`_get_table` 方法负责按表名获取(或创建)对应的 JSON 文件和 Table 对象。使用 `asyncio.Lock` 保证应用层对文件的**写操作**是串行的,但读操作可以并发。**注意:这并不是真正的异步 I/O 数据库。** 如果性能或并发写入成为瓶颈,需要替换为真正的异步数据库方案。

## 扩展建议

*   **实现配置热重载:** 监控 `config.yaml` 文件变化,当文件被修改时,触发 Kernel 重新加载配置,并根据配置差异动态地启动、停止或重新配置适配器和处理器。这需要仔细处理状态迁移和资源清理。
*   **插件依赖管理:** 实现更完善的插件依赖声明和自动安装机制(例如在插件元数据中声明 `requirements.txt` 路径或依赖列表),Kernel 在加载插件时自动检查并安装 Python 依赖。
*   **Web UI 功能完善:**
    *   实现完整的认证和授权系统。
    *   为适配器、插件、定时任务提供增删改查的管理界面,并能安全地修改 `config.yaml`。
    *   实现文件管理和 Web 终端的安全版本(例如使用 Docker API 操作容器,限制文件访问范围)。
    *   实现插件市场后端逻辑。
*   **事件总线:** 引入一个更通用的事件总线(例如使用 `asyncio-dispatch` 或自实现),允许组件之间发布和订阅更细粒度的事件,而不仅仅是消息传递。
*   **状态持久化:** 将重要的运行时状态(例如群组监听/回复开关、用户数据等)存储到数据库中,而不是仅存在于内存或配置文件中。
*   **测试覆盖:** 为核心代码、适配器和插件编写全面的单元测试和集成测试。

理解 Kernel 的调度逻辑、消息流程以及各组件的职责是进行核心开发的关键。

请谨慎使用代码。

5. 适配器开发文档 (docs/开发文档/适配器开发.md)

# Custom-bot 适配器开发文档

**目标读者:** 希望为 Custom-bot 添加对新聊天平台、协议或服务接口支持的开发者。

## 什么是适配器 (Adapter)?

适配器是 Custom-bot 框架中连接**外部世界**和**框架内核**的桥梁。它的主要职责是:

1.  **接收:** 监听或连接到外部服务,接收该服务发送的数据(通常是某种协议格式的消息或事件)。
2.  **转换 (入):** 将接收到的外部数据**解析**,并**转换**为框架内部统一的 `InternalMessage` 对象格式。
3.  **推送:** 将转换后的 `InternalMessage` 对象**推送**到框架内核 (Kernel) 的消息队列中,等待后续处理。
4.  **接收 (出):** 从框架内核接收用于发送的 `InternalMessage` 对象(通常由插件通过 `Sender.reply` 发起)。
5.  **转换 (出):** 将 `InternalMessage` 对象**转换**回目标外部服务能够理解的协议格式。
6.  **发送:** 通过已建立的连接或调用外部服务的 API,将转换后的数据**发送**出去。
7.  **生命周期管理:** 处理自身的初始化 (`setup`)、启动 (`start`) 和停止 (`stop`) 逻辑,包括资源管理(如网络连接、后台任务)。

## 开发步骤

### 1. 创建适配器文件

*   在项目根目录下的 `Adapter/` 目录中创建一个新的 Python 文件。
*   文件名应清晰地反映其支持的协议或平台,并使用下划线命名法(例如 `my_protocol_adapter.py`, `telegram_adapter.py`)。

### 2. 定义适配器类

*   在新建的文件中,导入必要的库(例如协议库、`asyncio`、`logging`)以及框架核心类:
    ```python
    import asyncio
    import logging
    # 导入你需要的协议库,例如:
    # import aiohttp
    # import websockets
    # from some_sdk import Client
    from typing import Any, Dict, Optional

    from core.adapters import BaseAdapter
    from core.message import InternalMessage
    ```
*   创建一个新的类,**必须继承**自 `core.adapters.BaseAdapter`。
    ```python
    logger = logging.getLogger(f"适配器.{__name__}") # 使用模块名作为 Logger 名称

    class MyProtocolAdapter(BaseAdapter):
        # ... 类实现 ...
    ```
*   **(可选但推荐) 添加模块级 Docstring 定义元数据:**
    ```python
    """
    # @name         MyProtocol适配器  # 适配器名称 (可选)
    # @description  用于对接 MyProtocol 的适配器示例。
    # @author       Your Name
    # @version      0.1.0
    # @adapter      true             # 标记这是一个适配器 (可选)
    """
    # ... import 和类定义 ...
    ```

### 3. 实现 `__init__` 方法

*   构造函数接收 `kernel`, `adapter_id`, `config` 三个参数。
*   **必须**调用 `super().__init__(kernel, adapter_id, config)` 来初始化基类。
*   从 `config` 字典中读取并存储此适配器运行所需的配置项(例如 API Key, 服务器地址, 端口, Token 等),并进行必要的校验。
*   初始化适配器内部状态变量(例如连接对象、客户端列表、任务引用等)。

    ```python
    class MyProtocolAdapter(BaseAdapter):
        def __init__(self, kernel, adapter_id, config):
            super().__init__(kernel, adapter_id, config)
            # 读取配置,提供默认值
            self.api_key = config.get("api_key")
            self.server_url = config.get("server_url", "default.server.com")
            self.listen_port = int(config.get("listen_port", 12345)) # 确保类型正确
            self.protocol_name = config.get("protocol_name", "my_protocol") # 协议名

            # 校验关键配置
            if not self.api_key:
                 self.logger.error("配置错误:缺少 'api_key'。适配器可能无法正常工作。")
                 # 可以选择抛出异常或设置一个无效状态

            # 初始化内部状态
            self._client = None # 协议客户端实例
            self._listener_task = None # 监听任务
            self._is_connected = False # 连接状态标志
            self._stop_event = asyncio.Event() # 用于优雅停止后台任务
    ```

### 4. 实现 `async def setup(self)` 方法

*   在此方法中执行**连接/监听之前**需要完成的异步准备工作。
*   例如:检查 API Key 有效性、加载证书、初始化数据库连接池(如果适配器需要独立数据库)、预加载数据等。
*   如果不需要异步准备,可以直接 `pass` 或只调用 `await super().setup()`。

    ```python
    async def setup(self):
        await super().setup()
        self.logger.info("正在执行 MyProtocol 适配器的设置...")
        # 示例:异步检查配置有效性或预热缓存
        # await self.validate_api_key()
        self.logger.info("设置完成。")
    ```

### 5. 实现 `async def start(self)` 方法 (核心 - 接收逻辑)

这是适配器**启动**并开始**接收**外部消息的核心方法。

*   **调用基类:** `await super().start()` (虽然基类 `start` 简单,但保持调用是个好习惯)。
*   **启动网络通信:**
    *   **服务器模式 (如反向 WS, HTTP Webhook):** 使用协议库启动监听(例如 `websockets.serve`, `aiohttp.web.TCPSite().start()`)。通常需要将一个连接处理函数(例如 `_connection_handler`)传递给监听器。
    *   **客户端模式 (如正向 WS, MQTT Client):** 使用协议库发起连接(例如 `websockets.connect`, `paho.mqtt.client.connect_async`)。
*   **后台任务:** 网络监听或接收通常是**持续性**的。必须将这些操作放在一个或多个**后台 `asyncio.Task`** 中运行,以避免阻塞 `start()` 方法和整个框架。
    ```python
    self._listener_task = asyncio.create_task(self._run_listener(), name=f"AdapterListener_{self.adapter_id}")
    # 将任务存入 Kernel 的管理列表 (如果需要 Kernel 统一管理)
    # if self.kernel and hasattr(self.kernel, '_running_tasks'):
    #     self.kernel._running_tasks[self._listener_task.get_name()] = self._listener_task
    ```
*   **消息接收与处理循环 (在后台任务中):**
    *   在后台任务(例如 `_run_listener` 或 `_connection_handler`)中,使用 `async for` 或 `while True: await receive_data()` 循环接收原始数据。
    *   **处理连接错误和重连 (客户端模式):** 如果是客户端模式,需要在连接断开或失败时实现重连逻辑,通常包含 `try...except` 和 `asyncio.sleep()`。
    *   **解析数据:** 将收到的原始数据(bytes/str)解析成 Python 对象(例如 JSON -> dict/list)。
    *   **提取信息:** 从解析后的数据和连接上下文中提取 `user_id`, `group_id`, `msg_id`, `text_content` 等。
    *   **创建 `InternalMessage`:** 实例化 `InternalMessage`,并填充所有必要字段。**务必**设置 `adapter_id=self.adapter_id` 和 `protocol=self.protocol_name`。
    *   **推送至 Kernel:** 调用 `await self._push_to_kernel(internal_message)`。
*   **设置运行状态:** 在 `start()` 方法成功启动监听或连接任务后,设置 `self.is_running = True`。如果启动失败,应保持 `self.is_running = False`。

    ```python
    async def start(self):
        await super().start()
        if self.is_running: # 防止重复启动
            return

        # 示例:启动一个后台监听任务 (服务器模式)
        try:
            # 假设 _run_server 是一个包含 websockets.serve 和 wait_closed 的方法
            self._server_task = asyncio.create_task(self._run_server(), name=f"MyProtoServer_{self.adapter_id}")
            # 假设启动成功,设置状态
            self.is_running = True
            self.logger.info("协议服务器已启动。")
        except Exception as e:
            self.logger.error(f"启动协议服务器失败: {e}", exc_info=True)
            self.is_running = False # 启动失败

    async def _run_server(self):
        # 示例:使用 websockets 库
        try:
            async with websockets.serve(self._handle_connection, self.host, self.listen_port):
                self.logger.info(f"服务器正在监听 {self.host}:{self.listen_port}")
                await asyncio.Future() # 保持运行直到被取消
        except OSError as e:
             self.logger.error(f"启动监听失败: {e}")
             self.is_running = False # 更新状态
        except asyncio.CancelledError:
             self.logger.info("服务器监听任务被取消。")
        finally:
             self.is_running = False # 确保退出时状态为 False

    async def _handle_connection(self, websocket, path):
         # --- 在这里实现接收、解析、转换、推送的逻辑 ---
         connection_id = f"{websocket.remote_address[0]}:{websocket.remote_address[1]}"
         self.logger.info(f"新连接: {connection_id} (路径: {path})")
         try:
             async for raw_data in websocket:
                  # 1. 解析 raw_data
                  parsed_data = self.parse_my_protocol_data(raw_data)
                  if not parsed_data: continue
                  # 2. 提取信息
                  user_id = parsed_data.get('sender_id')
                  text = parsed_data.get('text')
                  # ... 其他信息 ...
                  # 3. 创建 InternalMessage
                  msg = InternalMessage(
                       adapter_id=self.adapter_id, protocol=self.protocol_name,
                       connection_id=connection_id, user_id=user_id, text_content=text,
                       parsed_data=parsed_data, raw_data=raw_data
                       # ... 填充其他字段 ...
                  )
                  # 4. 推送到 Kernel
                  await self._push_to_kernel(msg)
         except websockets.exceptions.ConnectionClosed:
              self.logger.info(f"连接 {connection_id} 已关闭。")
         except Exception as e:
              self.logger.error(f"处理连接 {connection_id} 时出错: {e}", exc_info=True)
         finally:
              # 清理此连接相关的资源 (如果需要)
              pass
    ```

### 6. 实现 `async def stop(self)` 方法 (核心)

负责**优雅地**停止适配器并释放所有资源。

*   **调用基类:** `await super().stop()`。
*   **设置停止标志:** (如果使用了类似 `_stop_event` 的机制) `self._stop_event.set()`,通知后台任务退出循环。
*   **关闭网络连接:**
    *   **服务器模式:** 调用服务器对象的关闭方法(例如 `self._server.close()`),并等待其完全关闭 (`await self._server.wait_closed()` 或等待关联的任务结束)。主动关闭所有已连接的客户端 (`await client.close()`)。
    *   **客户端模式:** 调用客户端对象的断开连接方法 (`await self._client.disconnect()` 或 `await self._connection.close()`)。
*   **取消后台任务:** 取消在 `start()` 中创建的所有 `asyncio.Task` (`self._listener_task.cancel()`),并使用 `await asyncio.wait_for()` 或 `await asyncio.gather()` 等待它们结束(捕获 `CancelledError`)。
*   **清理资源:** 关闭文件句柄、数据库连接(如果适配器自己管理的话)、清除内部状态(如客户端列表 `self._clients.clear()`)。
*   **设置运行状态:** 确保最后设置 `self.is_running = False`。

### 7. 实现 `async def send(self, message: InternalMessage)` 方法 (核心 - 发送逻辑)

负责将 Kernel 传来的 `InternalMessage` 发送出去。

*   **确定目标:**
    *   **服务器模式:** 通常需要从 `message.connection_id` 字段获取目标客户端的标识符,并从内部状态(如 `self._clients` 字典)中找到对应的连接对象。如果 `connection_id` 为空或无效,应记录警告或错误,并返回 `False`。
    *   **客户端模式:** 目标通常是固定的、已连接的服务器。直接使用 `self._client` 或 `self._connection` 对象发送。
*   **转换消息格式:**
    *   根据目标协议的要求,将 `InternalMessage` 中的信息(主要是 `message.text_content`, `message.attachments`, `message.parsed_data`)转换为协议所需的格式(例如 JSON 字符串、XML、特定结构的 bytes、或者调用 SDK 提供的方法)。
    *   处理附件:可能需要将 `attachments` 列表中的 URL 或本地路径转换为协议特定的表示方式(例如 CQ 码、上传文件获取 ID 等)。
*   **发送数据:**
    *   使用协议库提供的发送方法(例如 `websocket.send()`, `http_client.post()`, `mqtt_client.publish()`)将转换后的数据发送出去。
    *   处理发送过程中可能发生的错误(例如连接已断开、网络错误、API 错误),并返回 `False`。
*   **返回值:** 发送成功(或至少已尝试发送)返回 `True`,失败返回 `False`。

    ```python
    async def send(self, message: InternalMessage) -> bool:
        # 示例:服务器模式
        target_conn_id = message.connection_id
        if not target_conn_id or target_conn_id not in self._clients:
            self.logger.warning(f"发送失败:找不到目标连接 {target_conn_id}")
            return False

        websocket = self._clients.get(target_conn_id)
        if not websocket or not websocket.open:
             self.logger.warning(f"发送失败:连接 {target_conn_id} 已关闭或无效")
             if target_conn_id in self._clients: del self._clients[target_conn_id] # 清理
             return False

        # 转换消息为 MyProtocol 格式 (假设是 JSON)
        try:
            payload = self.convert_to_my_protocol(message)
            if payload is None: return False # 无法转换
            data_to_send = json.dumps(payload, ensure_ascii=False)
        except Exception as convert_err:
             self.logger.error(f"转换消息 {message.internal_id} 为协议格式时出错: {convert_err}")
             return False

        # 发送数据
        try:
            await websocket.send(data_to_send)
            self.logger.debug(f"已向 {target_conn_id} 发送消息 {message.internal_id}")
            return True
        except websockets.exceptions.ConnectionClosed:
             self.logger.warning(f"发送到 {target_conn_id} 失败:连接已关闭")
             if target_conn_id in self._clients: del self._clients[target_conn_id]
             return False
        except Exception as send_err:
             self.logger.error(f"向 {target_conn_id} 发送消息时出错: {send_err}", exc_info=True)
             return False
    ```

### 8. (可选) 实现 Bridge 和 get_status

*   **Bridge:** 如果协议提供了一些独特的功能(例如获取群成员列表、修改用户资料等),可以通过重写 `@property def bridge(self)` 来暴露一个包含这些异步方法的对象或字典,供插件通过 `sender.bridge` 调用。
*   **get_status:** 重写 `def get_status(self)` 方法,返回一个包含更详细运行时状态的字典,例如连接状态、客户端数量、错误计数等,方便 Web UI 展示。

### 9. 配置适配器

最后,在 `config/config.yaml` 的 `adapters` 部分添加该适配器的配置,包括 `enabled: true`, `class:` (指向你的类) 和 `config:` 下的特定参数。

## 适配器开发注意事项

*   **完全异步:** 适配器中的所有 I/O 操作(网络、文件读写等)都必须是异步的,使用 `async` 和 `await`。避免任何长时间阻塞事件循环的操作。
*   **错误处理:** 对网络错误、协议错误、解析错误、发送错误等进行健壮的处理。客户端适配器应包含自动重连逻辑。
*   **资源管理:** 确保在 `stop()` 方法中正确关闭连接、取消任务、释放所有资源,避免内存泄漏或句柄泄漏。
*   **日志记录:** 使用 `self.logger` 记录清晰的日志,方便调试和监控。
*   **协议细节:** 深入理解你要对接的协议规范或 SDK 文档,确保消息的正确解析和构建。
*   **InternalMessage 映射:** 仔细设计外部协议数据与 `InternalMessage` 字段之间的映射关系,尽量保留原始信息,同时提供标准化的字段供插件使用。

通过遵循这些步骤和原则,您可以为 Custom-bot 添加对各种新协议和服务的支持。

请谨慎使用代码。


接下来是最后一部分:插件开发文档。

这是面向开发者的。 插件(处理器/Handler)开发文档


6. 插件开发文档 (docs/开发文档/插件开发.md)

# Custom-bot 插件开发文档

**目标读者:** 希望为 Custom-bot 添加新功能、命令、事件响应或与其他服务集成的开发者。这是扩展 Custom-bot 功能最常用的方式。

## 什么是插件 (Handler)?

插件,在 Custom-bot 框架内部也称为处理器 (Handler),是负责执行具体**业务逻辑**的模块。它们接收由内核 (Kernel) 分发过来的标准化内部消息 (`InternalMessage`),根据预设的规则和条件进行判断,并执行相应的操作,例如:

*   响应用户通过聊天发送的命令(如查询天气、讲笑话)。
*   处理特定类型的事件(如用户加入群聊、收到好友请求)。
*   与外部 API 或服务进行交互(如获取新闻、翻译文本)。
*   操作数据库(如记录用户积分、存储设置)。
*   执行定时任务(如每日签到提醒、定期清理数据)。
*   修改或控制机器人的行为。

## 开发步骤

### 1. 创建插件文件

*   **位置规则:** 插件 Python 文件 (`.py`) **必须** 放置在项目根目录下 `plugins/` 文件夹内的 **二级子目录** 中。
    *   这个二级子目录通常用于**组织和分类**插件,例如可以按功能命名 (`娱乐`, `管理`, `工具`) 或按作者命名 (`官方插件`, `张三的插件`)。
    *   **示例:**
        *   `plugins/娱乐/猜数字.py` (会被加载)
        *   `plugins/管理/权限检查.py` (会被加载)
        *   `plugins/官方插件/基础命令.py` (会被加载)
        *   `plugins/根目录插件.py` (**不会**被加载)
        *   `plugins/娱乐/utils/helper.py` (**不会**作为插件加载,但可以被同目录插件导入使用)
*   **命名:** 文件名建议使用小写和下划线(例如 `weather_query.py`),并能清晰反映插件功能。文件名(去除 `.py`)或稍后定义的类名可以作为配置中的默认 Handler ID。
*   **`__init__.py`:** 确保每个二级子目录(例如 `plugins/娱乐/`)下都有一个空的 `__init__.py` 文件,这样 Python 才能将该目录识别为一个包。

### 2. 定义元数据 (模块级 Docstring)

在插件 `.py` 文件的**最顶部**,使用多行字符串 `"""..."""`(模块级 Docstring)来定义插件的元数据。这些元数据使用 `@key value` 的格式,由 Kernel 在加载时解析。

```python
"""
# --- 插件元数据 ---
# @name         天气查询          # [必需] 插件名称,也是 config.yaml 中 handlers 的默认 ID
# @description  查询指定城市的天气信息。 # [推荐] 插件功能描述
# @author       你的名字或昵称     # [推荐] 作者信息
# @version      1.1.0             # [推荐] 插件版本
# @origin       可选的来源        # 例如:社区、官方
# @platform     all               # [可选] 适用的平台 (all 或空格分隔列表: qqbot_reverse_ws tg_bot)
# @rule         ^天气\s+([\u4e00-\u9fa5]+)$  # [必需,除非service] 触发规则(正则),可多个@rule。捕获组会作为参数
# @rule         ^weather\s+(\w+)$
# @priority     100               # [可选] 优先级 (数字越大越高, 默认 0)
# @admin        false             # [可选] 是否仅管理员可触发 (true/false, 默认 false)
# @disable      false             # [可选] 是否默认禁用 (true/false, 默认 false)
# @cron         0 30 7 * * *      # [可选] 定时任务表达式 (6位含秒), 执行 run_scheduled_task
# @service      false             # [可选] 是否为服务插件 (true/false), 服务插件通常无 rule, 启动时执行 setup
# @public       false             # [预留] 是否公开发布
# @Copyright    (可选) 版权信息
"""

# --- 你的 Python 代码从这里开始 ---
import asyncio
import logging
# ... 其他导入 ...
from core.handlers import BaseHandler, Sender, HandlerMatch, CONTINUE_MATCHING
# ...

请谨慎使用代码。

关键元数据解释:

  • @name (必需): 插件的易读名称,也是 中 部分默认使用的 ID。 **必须保config.yaml处理器必须保证唯一性。

  • @description (推荐): 描述插件的功能。

  • @rule (必需,除非是 的插件): 定义触发插件 @service:true处理 方法的。 可以定义多个正则表达式@rule,只要消息的文本内容 () 匹配其中规则即可触发(还需满足平台和权限条件)。message.text_content任何一个

    • 正则表达式中的 会被提取出来,作为参数传递给 捕获组 (...)处理 方法(可以通过 获取)。 sender.param(索引)

  • @platform (可选): 限制此插件只在指定平台的消息上触发。 值可以是适配器的 ID (如 ) 或协议名称 (如 `qqbot_reverse_wsonebot_rws),用空格分隔多个。 省略或为 则

  • @priority (可选): 整数,决定匹配优先级。 Kernel 会按优先级尝试匹配插件。 如果一个高优先级插件处理了消息并且从高到低没有返回 ('next'),则低优先级的插件即使规则匹配也不会被执行。CONTINUE_MATCHING

  • @admin (可选): 布尔值。 如果为 ,则只有被配置为管理员的用户(在 `kernel真kernel.admins 的 或插件特定 中)发送的消息匹配 config.admins@rule 时,才会触发 方法。 处理

  • @disable (可选): 布尔值。 如果为 ,则无论 中如何配置,此插件都**不会真config.yaml不会被加载。 主要用于临时禁用插件而无需修改配置文件。

  • @cron (可选): 如果提供标准的 6 位 Cron 表达式(含秒),Kernel 会使用 APScheduler 定时调用此插件的 方法。 run_scheduled_task()

  • @service (可选): 布尔值。 如果为 ,此插件通常没有 `@rul真@rule,主要用于在框架启动时执行一次 方法(例如启动后台任务、注册全局监听器等)。 服务类插件设置 ()不会参与普通的消息处理流程(即 方法不会被消息触发)。 处理

3. 编写插件代码

  • 导入依赖: 导入所需的 Python 库以及框架核心类:

    import asyncio
    import logging
    import aiohttp # 示例:用于发起 HTTP 请求
    from typing import TYPE_CHECKING, Optional
    
    # 核心类导入
    from core.handlers import BaseHandler, Sender, HandlerMatch, CONTINUE_MATCHING
    if TYPE_CHECKING: # 类型检查时导入,避免循环
        from core.message import InternalMessage

    content_copydownload

    请谨慎使用代码。蟒

创建处理器类: 创建一个继承自 的类。 类名建议与文件名或 相关。 core.handlers.BaseHandler@name

logger = logging.getLogger(f"插件.{__name__}") # 使用模块名创建 Logger

class WeatherQueryPlugin(BaseHandler):
    # ...

请谨慎使用代码。

实现 方法 (可选):async def setup(自身)

  • 在此方法中执行插件初始化时需要的一次性异步操作。

  • 例如:读取插件的特定配置 ()、初始化 API 客户端、加载模型、连接数据库等。self.config

async def setup(self):
    await super().setup()
    self.api_key = self.config.get("weather_api_key") # 从 config.yaml 读取配置
    if not self.api_key:
         self.logger.warning("未配置天气 API Key,插件功能可能受限。")
    self.http_session = aiohttp.ClientSession() # 创建共享的 HTTP session
    self.logger.info("天气查询插件已设置完成。")

请谨慎使用代码。

  • 实现 方法 (核心):async def handle(self, sender: Sender, message: InternalMessage, match: HandlerMatch)

获取参数: 使用 (索引从 1 开始) 获取 正则表达式中匹配到的捕获组内容。sender.param(索引)@rule

city_name = sender.param(1) # 假设第一个捕获组是城市名
if not city_name:
     await sender.reply("请输入要查询的城市名称。")
     return # 处理完毕,停止匹配

请谨慎使用代码。

执行逻辑: 调用 API、查询数据库、进行计算等。所有 I/O 操作必须使用 。 等待

try:
    # 假设有一个异步方法获取天气
    weather_info = await self._get_weather(city_name)
    if weather_info:
        reply_text = f"{city_name}的天气:{weather_info}"
    else:
        reply_text = f"未能查询到 {city_name} 的天气信息。"
except Exception as e:
    self.logger.error(f"查询天气时出错: {e}", exc_info=True)
    reply_text = "查询天气时发生错误,请稍后再试。"

请谨慎使用代码。

发送回复: 使用 发送结果给用户。 可以发送文本、图片、或其他类型(取决于适配器支持)。 sender.reply()

await sender.reply(reply_text)
# 示例:发送图片
# await sender.reply({
#     'type': 'image',
#     'path': 'https://example.com/weather.png' # 图片 URL 或本地路径
# })

请谨慎使用代码。

  • 交互式操作 (可选): 使用 可以等待用户的下一步输入,实现多轮对话。sender.waitInput()

  • 调用系统方法 (可选): 通过 , ,sender.db_get()sender.db_set()sender.sleep() 等方法与框架交互。

  • 返回值: 决定是否让其他插件继续处理这条消息。

    • 返回 无 (或不返回): 停止匹配。

    • 返回 CONTINUE_MATCHING (或 ): 继续匹配。 '下一个'

  • 实现 方法 (如果定义了 ):async def run_scheduled_task(个体经营)@cron

    • 在此方法中编写定时执行的逻辑。

可以通过 访问内核实例,例如发送推送消息:self.kernel 内核

async def run_scheduled_task(self):
    self.logger.info("执行每日天气推送任务...")
    # 假设从数据库获取用户订阅的城市
    # subscribed_users = await self.kernel.db.get("weather_subscriptions", "all_users", [])
    # for user_id, city in subscribed_users:
    #     weather_info = await self._get_weather(city)
    #     if weather_info:
    #         # 需要知道用户来自哪个平台才能推送
    #         # 这部分逻辑比较复杂,可能需要额外存储用户信息
    #         await self.kernel.push_message(
    #             target_platform="qqbot_reverse_ws", # 需要知道平台
    #             target_user_id=user_id,
    #             text_content=f"早上好!{city} 今日天气: {weather_info}"
    #         )

请谨慎使用代码。

  • (可选) 实现 方法:async def teardown(自营) 虽然基类没有定义,但你可以在 Kernel 关闭时(如果 Kernel 支持调用插件的 teardown)执行清理操作,例如关闭 。 aiohttp 的客户端会话

导出插件类: 在文件末尾添加(注意:这是 Python 赋值,不是 Node.js 的导出):

# Kernel 会自动查找并加载这个类
# module.exports = YourHandlerClassName # 这是错误的!Python 不需要显式导出
# 只需要定义好类即可

请谨慎使用代码。

(Kernel 的加载逻辑会自动查找文件中定义的 子类)BaseHandler (基础处理程序)

4. 配置插件 (config/config.yaml)

  • 在 部分添加或修改对应插件 ID 的条目。处理器:

  • 设置 。 启用:true

  • 在 下添加插件 或 方法中需要读取的配置项。配置:设置 ()手柄()

5. 测试插件

  1. 重启框架:'docker-compdocker-compose restart 自定义机器人 (或 )。 Python main.py

  2. 检查日志: 查看 Kernel 日志,确认插件是否成功加载。

  3. 触发插件: 通过配置的适配器发送能够匹配插件 的消息。 @rule

  4. 观察行为: 查看机器人的回复是否符合预期,检查日志中是否有错误信息。

  5. 调试: 使用 添加更多调试日志,或者使用 VSCode Remote - Containers 等工具进行断点调试。logger.debug()

Sender 对象详解

发射机 对象是插件与框架交互的主要接口,在 方法中由 Kernel 传入。 以下是一些常用方法:手柄()

  • 获取信息:

    • sender.get_msg(): 获取消息文本 ()。 message.text_content

    • sender.get_msg_id(): 获取平台原始消息 ID。

    • sender.get_user_id(), : 获取用户信息。 sender.get_user_name()

    • sender.get_group_id(), : 获取群组信息 (私聊时可能为 None)。 sender.get_group_name()

    • sender.get_platform(): 获取来源适配器 ID。

    • sender.get_protocol(): 获取来源协议名称。

    • sender.param(索引): 获取 正则匹配的第 @rule指数 个捕获组 (从 1 开始)。

    • sender.msg: 直接访问触发的 对象,获取更完整的信息 (内部消息sender.msg.parsed_data, 等)。 sender.msg.附件

  • 发送消息:

    • await sender.reply(内容, ...): 发送回复消息。 可以是字符串或包含类型/路径的字典。 可以指定 内容user_id, , 'quote_msggroup_idquote_msg_id

  • 消息操作:

    • await sender.delete_message(*msg_ids, wait=0): 请求适配器删除指定 ID 的消息(通常用于撤回机器人自己发的消息)。

  • 交互流程:

    • await sender.waitInput(超时=30,callback=无): 等待该用户的下一次输入。 返回包含新消息的 对象,发射机没有。 可用于在收到输入时立即处理并决定回调'再')。

    • 等待 sender.again(reply_content): 和返回 ''ag回复()'再' 的语法糖,用于 的回调。 waitInput

  • 权限检查:

    • await sender.is_admin(): 检查当前用户是否是管理员。

  • 数据库操作:

    • await sender.db_get(表,键,默认=无)

    • await sender.db_set(表、键、值)

    • await sender.db_delete(表,键)

    • await sender.db_keys(表)

  • 系统功能:

    • 等待 sender.sleep(秒): 异步休眠。

    • sender.get_time(格式): 获取格式化时间。

    • sender.config: (只读) 访问 Kernel 加载的全局配置字典。

    • sender.get_config_value(key_path,默认): 安全地获取嵌套配置项。

    • 等待 sender.inline(text): 模拟当前用户发送一条新消息。

    • (高风险)'等待sender.kernel.install_等待 sender.kernel.install_dependency(...), '等待 sender.kernel.run_s: 调用 Kernel 提供的受控方法执行高风险操作 (需要 Kernel 实现安全检查)。等待 sender.kernel.run_shell_command(...)

  • 适配器桥 ():发件人.bridge

    • 如果来源适配器实现了 属性,可以通过 桥sender.bridge.your_method() 调用适配器提供的特定功能。

详细的方法签名和用法请参考 中 核心/handlers.py发射机 类的代码和注释。

调试技巧

  • 日志: 在插件代码中使用 , ,logger.debug()logger.info()logger.warning(), 记录关键步骤和变量值。 将 Kernel 的日志级别 (logger.error()config.yaml -> kernel.log_level) 设为 可以看到最详细的输出。 调试

  • 打印(): 在开发初期或简单调试时, 也可以快速输出信息到控制台。 打印()

  • 检查 对象:消息 在 方法开头打印 处理消息 对象本身 () 或其 (print(消息)__dict__打印(message.__dict__)),可以了解适配器传递过来的所有信息。

  • 单元测试: 为插件的核心逻辑编写单元测试,模拟 和 发射机内部消息 对象进行测试。

  • VSCode 远程 - 容器: 如果使用 Docker,强烈推荐使用 VSCode 的 Remote - Containers 扩展。 它允许你在容器内进行开发和调试,可以直接在插件代码中设置断点、检查变量、单步执行等,极大地提高了调试效率。 参考 文档 (需要创建)。 远程开发 (VSCode)

通过这些步骤和工具,你可以高效地为 Custom-bot 开发各种强大的功能插件。

---

至此,主要的文档框架和内容已经提供。您需要根据项目的实际进展和特性,不断填充、更新和完善这些文档。

请谨慎使用代码