diff --git a/README.md b/README.md index 4531610..3a94151 100644 --- a/README.md +++ b/README.md @@ -6,29 +6,37 @@ ## 架构 ``` -QQ 客户端 服务器 - │ │ - │ QQ 协议 │ - ▼ │ -NapCat(Docker) │ ← 第2步部署 - │ OneBot HTTP │ - ├── POST 消息 → qqrebot (:25580) ← 第3步部署 - │ ├── openclaw_bridge 插件 - │ │ │ - │ │ ▼ - │ │ OpenClaw Gateway (:18789) ← 已有 - │ │ │ - │ │ ▼ - │ │ 新建的 qq-agent ← 第5步创建 - │ │ │ - ◄── 回复/操作 ←─── qqrebot ◄─────┘ - -NapCat Docker 端口映射: - 6099/tcp ← HTTP API(脚本发消息/管理都走这个端口) - 3001/tcp ← WebSocket(qqrebot 内部使用这个通道) - 9000/tcp ← WebUI 管理面板 +QQ 用户 →→→ NapCat(Docker:6099) + │ + │ HTTP POST(事件推送) + ▼ + qqrebot(HTTP Server :25580) ← 消息接收 + │ + ├── 执行 openclaw_bridge 插件 + │ │ + │ │ HTTP POST /v1/chat/completions + │ ▼ + │ OpenClaw Gateway(:18789) + │ │ + │ ▼ + │ qq-agent(AI 模型) + │ │ + ◄── 返回回复文本 ──┘ + │ + │ HTTP POST(send_private_msg / send_group_msg) + ▼ + NapCat(:6099) → 回复发送到 QQ ``` +**关键通信链路(全是 HTTP,无 WebSocket):** + +| 链路 | 方向 | 协议 | 端口 | +|---|---|---|---| +| NapCat → qqrebot | 事件推送 | HTTP POST | `qqrebot:25580` | +| 插件 → OpenClaw | AI 请求 | HTTP POST | `127.0.0.1:18789` | +| 插件 → NapCat | 发送回复 | HTTP POST | `napcat:6099` | +| 脚本 → NapCat | 发消息/管理 | HTTP POST | `napcat:6099` | + ## 前提条件 | 组件 | 状态 | 由本仓库部署 | @@ -52,24 +60,27 @@ NapCat Docker 端口映射: |---|---|---| | `ADMIN_QQ` | 管理员的 QQ 号(白名单使用) | `123456789` | | `GATEWAY_TOKEN` | OpenClaw Gateway 的认证 Token | 读取 `~/.openclaw/openclaw.json` 的 `gateway.auth.token` | -| `NAP CAT_HOST` | 服务器 IP(NapCat 所在机器) | `192.168.1.100` | +| `NAP CAT_HOST` | NapCat 服务器地址(可含端口) | `127.0.0.1`(同机)或 `192.168.1.100:6099` | +| `QQREBOT_HOST` | qqrebot 服务器地址(NapCat 侧看到的) | `127.0.0.1`(同机)或 `172.17.0.1:25580`(Docker 宿主机) | | `NEW_AGENT_ID` | 新 agent 的唯一 ID | `qq-agent` | | `SHARED_DIR` | NapCat 与宿主机共享目录路径 | `/opt/napcat_shared` | +> `NAP CAT_HOST` 和 `QQREBOT_HOST` 可能不同:当 NapCat 和 qqrebot 运行在同一台机器上时,NapCat 用 Docker 内部地址访问宿主机 qqrebot(如 `172.17.0.1:25580`),而脚本用外部地址访问 NapCat(如 `127.0.0.1:6099`)。 + --- ### 第2步:通过 Docker 部署 NapCat -NapCat 接收 QQ 消息并通过 OneBot HTTP 协议转发给下游(qqrebot)。 +NapCat 是 QQ 协议实现,接收 QQ 消息并通过 **HTTP POST** 推送到 qqrebot。 #### 2.1 创建目录结构 ```bash -# 共享文件目录(用于发文件到 QQ) +# 共享文件目录(NapCat Docker 内发文件时读取) SHARED_DIR="${SHARED_DIR:-/opt/napcat_shared}" mkdir -p "${SHARED_DIR}" -# NapCat 配置目录 +# NapCat 配置目录 + QQ 登录数据持久化 mkdir -p /opt/napcat/config mkdir -p /opt/napcat/qq_data ``` @@ -81,8 +92,6 @@ docker run -d \ --name napcat \ --restart unless-stopped \ -p 6099:6099 \ - -p 3001:3001 \ - -p 9000:9000 \ -v /opt/napcat/config:/app/napcat/config \ -v /opt/napcat/qq_data:/app/.config/QQ \ -v "${SHARED_DIR}:/app/napcat/share" \ @@ -92,32 +101,34 @@ docker run -d \ 端口说明: -| 端口 | 用途 | -|---|---| -| 6099 | OneBot HTTP API(脚本发消息/管理用) | -| 3001 | OneBot WebSocket(qqrebot 接收消息用) | -| 9000 | WebUI 管理面板(首次本地扫码登录用) | +| 端口 | 是否必开 | 用途 | +|---|---|---| +| `6099` | ✅ | OneBot HTTP API(脚本发消息/群管理/取数据都用这个端口) | +| `3001` | ❌ | WebSocket Server(本教程用 HTTP POST 推送,不需要) | +| `9000` | 按需 | WebUI 管理面板(首次扫码登录可临时用) | + +如果机器防扫描严格,**只开 6099** 即可;WebUI 按需用完即关。 #### 2.3 首次登录配置 -> NapCat 首次启动需要扫码登录 QQ 账号。登录后自动持久化 `/opt/napcat/qq_data`。 +> NapCat 首次启动需要扫码登录 QQ 账号。登录数据持久化在 `/opt/napcat/qq_data`。 ```bash -# 查看登录二维码 +# 查看登录二维码(日志中会打印链接) docker logs napcat 2>&1 | grep -o 'http[s]*://[^ ]*\.png' -# 或用浏览器打开 http://<服务器IP>:9000/webui/扫码登录 +# 或用浏览器打开 http://<服务器IP>:9000/webui 扫码登录 # 等待登录成功 docker logs napcat -f | grep "登录成功" ``` -#### 2.4 配置 OneBot HTTP 端点 +#### 2.4 配置事件推送(HTTP POST → qqrebot) -登录成功后,通过 WebUI 或直接编辑配置文件配置 HTTP 上报地址: +NapCat 需要将所有消息事件通过 HTTP POST 推送到 qqrebot(端口 `25580`)。 -```bash -# 编辑 NapCat OneBot 配置 -cat > /opt/napcat/config/onebot11.json << 'EOF' +编辑 `/opt/napcat/config/onebot11.json`: + +```json { "http": { "enable": true, @@ -126,26 +137,34 @@ cat > /opt/napcat/config/onebot11.json << 'EOF' "enableHttpPost": false, "enableHttpHeartbeat": false }, + "httpPost": [ + { + "url": "http://${QQREBOT_HOST}/", + "secret": "" + } + ], "ws": { - "enable": true, - "host": "0.0.0.0", - "port": 3001 - }, - "reverseWs": { "enable": false }, "heartbeat": { - "enable": true, - "interval": 30000 + "enable": false }, "enableLocalFile2Url": true } -EOF - -docker restart napcat ``` -> **注意**:NapCat 通过 `reverseWs` 或 WebSocket 推送消息给 qqrebot。本配置使用 WebSocket Server 模式,qqrebot 作为客户端连接 `ws://napcat_host:3001`。 +> **关键说明:** +> - `httpPost` 数组中的 URL 是 NapCat 向 qqrebot 推送消息事件的目标地址 +> - 如果 NapCat 和 qqrebot 在**同一台宿主机上**,`url` 通常设置为 `http://172.17.0.1:25580/`(Docker 默认网关地址) +> - 如果**不同机器**,填 qqrebot 机器可达的真实 IP +> - `ws.enable = false` — 本教程不用 WebSocket +> - `http.enableHttpPost = false` — httpPost 由专门的 `httpPost` 数组控制 + +应用配置: + +```bash +docker restart napcat +``` #### 2.5 验证 NapCat @@ -161,33 +180,38 @@ curl -s http://localhost:6099/get_version_info | python3 -m json.tool ### 第3步:部署 qqrebot(消息接收器) -从 [chatrebot1.0 release](https://jianfgit.xyz/jianf/chat_rebot-connect-with-onebot-standard-/src/tag/chatrebot1.0/) 下载 qqrebot 框架: +qqrebot 是 HTTP 服务器,NapCat 将 QQ 消息 POST 到 qqrebot,qqrebot 加载插件处理。 + +#### 3.1 安装 qqrebot + +从 [chatrebot1.0 release](https://jianfgit.xyz/jianf/chat_rebot-connect-with-onebot-standard-/src/tag/chatrebot1.0/) 下载: ```bash -# 下载并解压 wget https://jianfgit.xyz/jianf/chat_rebot-connect-with-onebot-standard-/archive/chatrebot1.0.tar.gz tar xzf chatrebot1.0.tar.gz mv chat_rebot-connect-with-onebot-standard- /opt/qqrebot cd /opt/qqrebot ``` -**配置 `config/config.toml`:** +#### 3.2 配置 + +编辑 `config/config.toml`: ```toml [app] list_port = 25580 -send_url = "http://${NAP CAT_HOST}:6099" # NapCat HTTP 端口 6099 +send_url = "http://${NAP CAT_HOST}:6099" # → 插件回复时调 NapCat HTTP API 用 [rebot] -id = "" # 不需要 bot_id +id = "" [plugins] dir = ["plugins"] ``` -**注意**:NapCat 的 HTTP API 端口是 `6099`(不是 go-cqhttp 的 `25570`),所有脚本中的 `CQHTTP_URL` 最终也要替换为 `http://${NAP CAT_HOST}:6099`。 +> **`send_url`**:qqrebot 框架用它作为 NapCat HTTP API 的基础地址。插件中的 `ctx.group.url` 来自此值,后续所有 `send_private_msg` / `send_group_msg` 调用都走这个地址。如果 NapCat 和 qqrebot 同机,填 `http://127.0.0.1:6099`。 -**创建 systemd 服务:** +#### 3.3 创建 systemd 服务 ```ini # /etc/systemd/system/qqrebot.service @@ -220,25 +244,21 @@ systemctl enable --now qqrebot 本仓库即 openclaw_bridge 插件的源码,需打包后部署到 qqrebot。 -**在目标机器上执行打包(因含 C 扩展,必须在此机器上执行):** +**打包(必须在目标主机上执行,因含 C 扩展):** ```bash cd /path/to/chatrebot_aireply_plug - -# 安装依赖 pip install -r requirements.txt - -# 打包 python3 package.py ``` -**将生成的 ZIP 复制到 qqrebot 插件目录:** +**部署:** ```bash cp dist/openclaw_bridge.zip /opt/qqrebot/plugins/ ``` -**创建插件配置文件 `/opt/qqrebot/config/openclawbridge/config.toml`:** +**创建插件配置 `/opt/qqrebot/config/openclawbridge/config.toml`:** ```toml [openclaw] @@ -255,7 +275,7 @@ agent_id = "${NEW_AGENT_ID}" systemctl restart qqrebot ``` -**验证插件已加载:** +**验证插件加载:** ```bash journalctl -u qqrebot -f | grep OpenClawBridge @@ -276,7 +296,6 @@ Config loaded: url=http://127.0.0.1:18789, allowed=123456789 AGENT_ID="${NEW_AGENT_ID}" AGENT_WORKSPACE="/home/program/${AGENT_ID}-workspace" -# 创建 workspace 目录 mkdir -p "${AGENT_WORKSPACE}/scripts" mkdir -p "${AGENT_WORKSPACE}/skills" mkdir -p "${AGENT_WORKSPACE}/self-workplace" @@ -284,7 +303,7 @@ mkdir -p "${AGENT_WORKSPACE}/memory" mkdir -p "${AGENT_WORKSPACE}/files" ``` -添加 agent 配置项到 OpenClaw 的 `agents.list`: +添加 agent 配置项到 `agents.list`: ```json { @@ -295,7 +314,7 @@ mkdir -p "${AGENT_WORKSPACE}/files" } ``` -> **注意**:通过 `GET /api/config/all` 获取当前配置,找到 `agents.list` 数组,push 上述对象,然后通过 `POST /api/config/set` 写入。或者直接编辑 `~/.openclaw/openclaw.json` 文件后重启 Gateway。 +> 通过 `GET /api/config/all` 获取当前配置,找到 `agents.list` 数组执行 push,然后 `POST /api/config/set`。或直接编辑 `~/.openclaw/openclaw.json` 后重启 Gateway。 --- @@ -315,35 +334,31 @@ for SKILL in qq-messenger qq-management qq-resolver qq-napcat-extras mc-query ni done ``` -**替换脚本中的占位符:** +**替换占位符(关键!必须执行):** ```bash cd "${AGENT_WORKSPACE}/scripts" -# 替换 NapCat HTTP API 地址 → 端口 6099 +# NapCat HTTP API 地址(端口 6099,非 25570) sed -i "s|YOUR_NAP CAT_HOST:25570|${NAP CAT_HOST}:6099|g" *.py sed -i "s|YOUR_NAP CAT_HOST|${NAP CAT_HOST}:6099|g" *.py sed -i "s|YOUR_NAPCAT_HOST:25570|${NAP CAT_HOST}:6099|g" *.py sed -i "s|YOUR_NAPCAT_HOST|${NAP CAT_HOST}:6099|g" *.py -# 替换管理员 QQ +# 管理员 QQ sed -i "s|YOUR_ADMIN_QQ|${ADMIN_QQ}|g" *.py -# 替换 Gateway Token +# Gateway Token sed -i "s|YOUR_GATEWAY_TOKEN|${GATEWAY_TOKEN}|g" *.py -# 替换工作区路径 +# 工作区路径 sed -i "s|YOUR_WORKSPACE_PATH|${AGENT_WORKSPACE}|g" *.py -# 替换共享目录(Docker 挂载的 shared 目录) +# 共享目录 sed -i "s|YOUR_SHARED_DIR|${SHARED_DIR}|g" *.py - -# 替换群 ID 占位符(可选:群 ID 通常按需传入,但 DEFAULT_GID 需用户指定) -# sed -i "s|YOUR_GROUP_ID|${DEFAULT_GROUP_ID}|g" *.py -# sed -i "s|YOUR_BOT_QQ|${BOT_QQ}|g" *.py ``` -同样处理 `skills/` 目录下的 SKILL.md 文件中的 `YOUR_WORKSPACE_PATH` 和 `YOUR_ADMIN_QQ`。 +同样处理 `skills/` 目录下的 SKILL.md 文件。 --- @@ -356,11 +371,8 @@ openclaw gateway restart 确认新 agent 已加载: ```bash -# 查看代理列表 openclaw config get agents.list - -# 或在日志中确认 -journalctl -u openclaw -f | grep "agent.${NEW_AGENT_ID}" +# 或 journalctl -u openclaw -f | grep "agent.${NEW_AGENT_ID}" ``` --- @@ -374,59 +386,43 @@ journalctl -u openclaw -f | grep "agent.${NEW_AGENT_ID}" --- -## 脚本适配说明(NapCat vs go-cqhttp) +## 脚本适配说明 -如果从 go-cqhttp 迁移到 NapCat,或者混合使用,以下脚本需要关注差异点: - -### 端口差异 +### 端口对照 | 组件 | go-cqhttp | NapCat(Docker) | |---|---|---| | HTTP API 端口 | `25570` | **6099** | -| WebSocket 端口 | `25570` | **3001** | -| qqrebot 配置中 `send_url` | `http://host:25570` | `http://host:6099` | +| WebSocket | `25570`(兼用) | 不启用(本教程) | -> 所有脚本中的 `CQHTTP_URL = "http://YOUR_NAP CAT_HOST:25570"` 在替换时会自动改为 `6099`。 +> 脚本中 `CQHTTP_URL = "http://YOUR_NAP CAT_HOST:25570"` 替换后变为 `http://:6099`。 -### 文件发送适配 +### 文件发送与共享目录 -#### 跨主机文件访问 - -NapCat 运行在 Docker 中,宿主机文件不能直接在 NapCat 内部访问。需要用 Docker volume 映射: +NapCat 运行在 Docker 内部,宿主机文件路径不能直接访问。解决方式: ``` -宿主机 ${SHARED_DIR}/ ⇔ NapCat 容器内 /app/napcat/share/ +宿主机 ${SHARED_DIR}/ ⇔ Docker volume → 容器内 /app/napcat/share/ ``` -受影响的脚本: - -| 脚本 | 依赖 | 影响说明 | +| 脚本 | 关联 | 说明 | |---|---|---| -| `qq_send_file.py` | 文件先复制到 `TARGET_DIR`,再由 NapCat 读取 | 需确保 `TARGET_DIR` = `SHARED_DIR` | -| `qq_ocr_image.py` | NapCat 回退时需文件在共享目录 | 需复制到 `SHARED_DIR` | -| `qq_get_file.py` | NapCat 返回文件 URL 或 base64 | 自动处理,不需要共享目录 | +| `qq_send_file.py` | 使用 `TARGET_DIR` | 发文件前复制到共享目录,NapCat 从 `/app/napcat/share/` 读取 | +| `qq_ocr_image.py` | 提到 NapCat 回退 | 只在本地 Tesseract 不可用时走 NapCat,此时需文件在共享目录 | +| `qq_get_file.py` | 无依赖 | NapCat 返回 base64 或 URL,自动下载,不依赖共享目录 | +| `qq_upload_group_file.py` | 无依赖 | 使用 base64:// 编码直接上传,不依赖共享目录 | -#### 文件上传方式对比 - -| 方式 | 支持 NapCat | 说明 | -|---|---|---| -| `upload_group_file` API | ✅ | NapCat 原生支持,底层走共享目录 | -| `base64://` 方式 | ✅ | 小文件(<10MB)直接编码嵌入,无需共享目录 | -| 文件路径引用 | ✅ | 文件路径需在 NapCat 容器内可访问 | - -`qq_upload_group_file.py` 使用 base64 方式上传,可以跨主机工作不依赖共享目录,但大文件可能有性能问题。 - -### 脚本变量速查 +### 变量速查 | 占位符 | 替换值 | 出现位置 | |---|---|---| -| `YOUR_NAP CAT_HOST:25570` | `${NAP CAT_HOST}:6099` | 所有 py 脚本 | -| `YOUR_NAP CAT_HOST` | `${NAP CAT_HOST}:6099` | 所有 py 脚本 | -| `YOUR_NAPCAT_HOST` | `${NAP CAT_HOST}:6099` | 部分 py 脚本 | -| `YOUR_ADMIN_QQ` | `${ADMIN_QQ}` | py 脚本 + SKILL.md | -| `YOUR_GATEWAY_TOKEN` | `${GATEWAY_TOKEN}` | py 脚本 | +| `YOUR_NAP CAT_HOST:25570` | `${NAP CAT_HOST}:6099` | 所有脚本 | +| `YOUR_NAP CAT_HOST` | `${NAP CAT_HOST}:6099` | 所有脚本 | +| `YOUR_NAPCAT_HOST` | `${NAP CAT_HOST}:6099` | 部分脚本 | +| `YOUR_ADMIN_QQ` | `${ADMIN_QQ}` | 脚本 + SKILL.md | +| `YOUR_GATEWAY_TOKEN` | `${GATEWAY_TOKEN}` | 脚本 | | `YOUR_SHARED_DIR` | `${SHARED_DIR}` | `qq_send_file.py`、`qq_ocr_image.py` | -| `YOUR_WORKSPACE_PATH` | `${AGENT_WORKSPACE}` | SKILL.md + py 脚本 | +| `YOUR_WORKSPACE_PATH` | `${AGENT_WORKSPACE}` | SKILL.md + 脚本 | --- @@ -464,9 +460,7 @@ NapCat 运行在 Docker 中,宿主机文件不能直接在 NapCat 内部访问 ### 安全上报 -当非管理员触发高危词时,插件自动: -1. 通过 OneBot API 给 `allowed_sender` 发送告警消息 -2. 告警内容:用户 QQ、所在群、触发的原文摘要 +当非管理员触发高危词时,插件自动通过 NapCat HTTP API 给 `allowed_sender` 发送告警(调用 `send_private_msg`)。 ### 错误脱敏 @@ -477,10 +471,10 @@ NapCat 运行在 Docker 中,宿主机文件不能直接在 NapCat 内部访问 | 配置项 | 必填 | 说明 | |---|---|---| | `gateway_url` | ✅ | OpenClaw Gateway 地址 | -| `gateway_token` | ✅ | Gateway 认证 Token(从用户处获取) | -| `allowed_sender` | ✅ | 管理员 QQ 号(从用户处获取) | +| `gateway_token` | ✅ | Gateway 认证 Token | +| `allowed_sender` | ✅ | 管理员 QQ 号 | | `model` | ✅ | Agent 模型 ID(格式:`openclaw/${AGENT_ID}`) | -| `agent_id` | ✅ | Agent ID(与 `agents.list` 中一致) | +| `agent_id` | ✅ | Agent ID(与 `agents.list` 一致) | ## 目录结构 @@ -496,12 +490,12 @@ chatrebot_aireply_plug/ │ ├── plugin_modules.py # BasePlugin + MessageContext │ └── user_module.py # User/Group 封装 ├── scripts/ # ✅ 部署时复制到 agent workspace -│ ├── qq_send_msg.py # 发送消息(端口 6099) -│ ├── qq_send_file.py # 发送文件/图片(共享目录映射) +│ ├── qq_send_msg.py # 发送消息(调 NapCat :6099) +│ ├── qq_send_file.py # 发送文件/图片 │ ├── qq_get_groups.py # 获取群列表 │ ├── qq_get_friends.py # 获取好友列表 │ ├── qq_group_manage.py # 群管理(踢/禁言/设管理) -│ ├── ... # 详见技能文档 +│ └── ... ├── skills/ # ✅ 部署时复制到 agent skills/ │ ├── qq-messenger/SKILL.md │ ├── qq-management/SKILL.md @@ -509,17 +503,17 @@ chatrebot_aireply_plug/ │ ├── qq-napcat-extras/SKILL.md │ ├── mc-query/SKILL.md │ └── ... -├── dependence.py # 依赖安装工具 -├── package.py # ZIP 打包器 -├── packup.sh / packup.bat # 一键打包脚本 -├── test.py # 本地测试 -├── requirements.txt # Python 依赖 -└── README.md # 本文件 +├── dependence.py +├── package.py +├── packup.sh / packup.bat +├── test.py +├── requirements.txt +└── README.md ``` ## 自定义安全词库 -编辑 `src/process.py` 中的 `HIGH_RISK_WORDS` 列表: +编辑 `src/process.py` 中的 `HIGH_RISK_WORDS` 列表后重新打包部署重启: ```python HIGH_RISK_WORDS = { @@ -530,8 +524,6 @@ HIGH_RISK_WORDS = { } ``` -添加后重新打包、部署、重启 qqrebot。 - ## 许可证 MIT