Compare commits
47 Commits
main-merge
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e47256c292 | ||
|
|
b1668f51fb | ||
|
|
de591dc2c7 | ||
|
|
32e4a404e7 | ||
|
|
f440a22123 | ||
|
|
272c1d897a | ||
|
|
f0568ba18b | ||
|
|
f5a414dda6 | ||
|
|
29f3dc7058 | ||
|
|
2d4db470eb | ||
|
|
2c06146539 | ||
|
|
e2164f2bb7 | ||
|
|
a13fec2e2e | ||
|
|
1493a47ea0 | ||
|
|
70813a723f | ||
|
|
d3706386f7 | ||
|
|
b39c41f4de | ||
|
|
956e22c161 | ||
|
|
8ccb231fda | ||
|
|
251a2f0e99 | ||
|
|
1205ecc86e | ||
|
|
cd6b30b358 | ||
|
|
bde6c22363 | ||
|
|
8cc6e034bf | ||
|
|
e48a035b07 | ||
|
|
87611a6326 | ||
|
|
8b4704f649 | ||
|
|
0a6b0fe73a | ||
|
|
611b4f4323 | ||
|
|
0c22a89c6a | ||
|
|
ca4d7d9ea5 | ||
|
|
6c0b8058ff | ||
|
|
15ea067d8d | ||
|
|
a35413ef7b | ||
|
|
cf0cc0d626 | ||
|
|
5b9068fcf4 | ||
|
|
571e176f44 | ||
|
|
d7a5ed6e0d | ||
|
|
98c7a598d8 | ||
|
|
4fefa8f15b | ||
|
|
31926bac28 | ||
|
|
7db7b7e2d6 | ||
|
|
d6c55d61f4 | ||
|
|
eda0579750 | ||
|
|
24200f1a5f | ||
|
|
484db3f90c | ||
|
|
efcf627213 |
261
README.bak.md
Normal file
261
README.bak.md
Normal file
@@ -0,0 +1,261 @@
|
||||
<div align="center">
|
||||
|
||||
# QQ Bot Channel Plugin for Openclaw(Clawdbot/Moltbot)
|
||||
|
||||
QQ 开放平台 Bot API 的 Openclaw 渠道插件,支持 C2C 私聊、群聊 @消息、频道消息。
|
||||
|
||||
[](https://www.npmjs.com/package/@sliverp/qqbot)
|
||||
[](./LICENSE)
|
||||
[](https://bot.q.qq.com/wiki/)
|
||||
[](https://github.com/sliverp/openclaw)
|
||||
[](https://nodejs.org/)
|
||||
[](https://www.typescriptlang.org/)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 📸 使用示例
|
||||
<div align="center">
|
||||
<img width="400" alt="使用示例" src="https://github.com/user-attachments/assets/6f1704ab-584b-497e-8937-96f84ce2958f" />
|
||||
<img width="670" height="396" alt="Clipboard_Screenshot_1770366319" src="https://github.com/user-attachments/assets/e21e9292-fb93-41a7-81fe-39eeefe3b01d" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
- 🔒 **多场景支持** - C2C 私聊、群聊 @消息、频道消息、频道私信
|
||||
- 🖼️ **富媒体消息** - 支持图片收发、文件发送
|
||||
- ⏰ **定时推送** - 支持定时任务到时后主动推送
|
||||
- 🔗 **URL 无限制** - 私聊可直接发送 URL
|
||||
- ⌨️ **输入状态** - Bot 正在输入中状态提示
|
||||
- 🔄 **热更新** - 支持 npm 方式安装和热更新
|
||||
- 📝 **Markdown** - 支持 Markdown 格式
|
||||
- 📝 **Command** - 支持Openclaw原生命令
|
||||
|
||||
|
||||
---
|
||||
|
||||
## ⭐ Star 趋势
|
||||
<div align="center">
|
||||
<img width="666" height="464" alt="star-history-202626 (1)" src="https://github.com/user-attachments/assets/01d123b4-f2a7-45b9-b2ed-b7a344497b4a" />
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📦 安装
|
||||
|
||||
### 方式一:腾讯云 Lighthouse 镜像(最简单)
|
||||
|
||||
[](https://cloud.tencent.com/product/lighthouse)
|
||||
|
||||
直接使用预装好的腾讯云 Lighthouse 镜像,开箱即用,无需手动安装配置。
|
||||
|
||||
### 方式二:npm 安装(推荐)
|
||||
|
||||
```bash
|
||||
openclaw plugins install @sliverp/qqbot@1.3.7
|
||||
```
|
||||
|
||||
### 方式三:源码安装
|
||||
|
||||
```bash
|
||||
git clone https://github.com/sliverp/qqbot.git && cd qqbot
|
||||
clawdbot plugins install .
|
||||
```
|
||||
|
||||
> 💡 安装过程需要一些时间,尤其是小内存机器,请耐心等待
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 配置
|
||||
|
||||
### 1. 获取 QQ 机器人凭证
|
||||
|
||||
1. 访问 [QQ 开放平台](https://q.qq.com/)
|
||||
2. 创建机器人应用
|
||||
3. 获取 `AppID` 和 `AppSecret`(ClientSecret)
|
||||
4. Token 格式:`AppID:AppSecret`
|
||||
|
||||
### 2. 添加配置
|
||||
|
||||
**交互式配置:**
|
||||
|
||||
```bash
|
||||
clawdbot channels add
|
||||
# 选择 qqbot,按提示输入 Token
|
||||
```
|
||||
|
||||
**命令行配置:**
|
||||
|
||||
```bash
|
||||
clawdbot channels add --channel qqbot --token "AppID:AppSecret"
|
||||
```
|
||||
|
||||
### 3. 手动编辑配置(可选)
|
||||
|
||||
编辑 `~/.clawdbot/clawdbot.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"qqbot": {
|
||||
"enabled": true,
|
||||
"appId": "你的AppID",
|
||||
"clientSecret": "你的AppSecret"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 🚀 使用
|
||||
|
||||
### 启动服务
|
||||
|
||||
```bash
|
||||
# 后台启动
|
||||
clawdbot gateway restart
|
||||
|
||||
# 前台启动(查看日志)
|
||||
clawdbot gateway --port 18789 --verbose
|
||||
```
|
||||
|
||||
### CLI 配置向导
|
||||
|
||||
```bash
|
||||
clawdbot onboard
|
||||
# 选择 QQ Bot 进行交互式配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
- **群消息**:需要在群内 @机器人 才能触发回复
|
||||
- **沙箱模式**:新创建的机器人默认在沙箱模式,需要添加测试用户
|
||||
|
||||
---
|
||||
|
||||
## 🔄 升级
|
||||
|
||||
### npm 热更新
|
||||
|
||||
```bash
|
||||
npx -y @sliverp/qqbot@1.3.7 upgrade
|
||||
```
|
||||
|
||||
> 热更新后无需重新配置 AppId 和 AppSecret。该方式Openclaw和Node.js会占用大量内存,小内存机器优先建议使用源码方式热更新
|
||||
|
||||
### 源码热更新
|
||||
|
||||
```bash
|
||||
git clone https://github.com/sliverp/qqbot.git && cd qqbot
|
||||
|
||||
# 运行升级脚本
|
||||
bash ./scripts/upgrade.sh
|
||||
|
||||
# 重新安装
|
||||
clawdbot plugins install .
|
||||
|
||||
# 重新配置
|
||||
clawdbot channels add --channel qqbot --token "AppID:AppSecret"
|
||||
|
||||
# 重启网关
|
||||
clawdbot gateway restart
|
||||
```
|
||||
|
||||
升级脚本会自动清理旧版本和配置。
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 📚 版本历史
|
||||
|
||||
<details>
|
||||
<summary><b>v1.4.0</b></summary>
|
||||
|
||||
- 支持 Markdown 格式
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.3.13 - 2026.02.06</b></summary>
|
||||
|
||||
- ✨ 支持Openclawd内置指令“/compact" , "/new"等(注意,/reset等命令有危险性,非常不建议把Bot拉入群聊)
|
||||
- 🐛 修复在一些情况下”正在输入“不生效的问题
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.3.0 - 2026.02.03</b></summary>
|
||||
|
||||
- ✨ 支持图片收发等功能
|
||||
- ✨ 支持定时任务到时后主动推送
|
||||
- ✨ 支持使用 npm 等方式安装和升级
|
||||
- 🐛 优化一些已知问题
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.5 - 2026.02.02</b></summary>
|
||||
|
||||
- ✨ 解除 URL 发送限制
|
||||
- ✨ 更新 Bot 正在输入中状态
|
||||
- ✨ 提供主动推送能力
|
||||
- 🐛 优化一些已知问题
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.2 - 2026.01.31</b></summary>
|
||||
|
||||
- ✨ 支持发送文件
|
||||
- ✨ 支持 openclaw、moltbot 命令行
|
||||
- 🐛 修复 health 检查提示问题
|
||||
- 🐛 修复文件发送后 clawdbot 无法读取的问题
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.2.1</b></summary>
|
||||
|
||||
- 🐛 解决长时间使用会断联的问题
|
||||
- 🐛 解决频繁重连的问题
|
||||
- ✨ 增加大模型调用失败后的提示消息
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>v1.1.0</b></summary>
|
||||
|
||||
- 🐛 解决 URL 被拦截的问题
|
||||
- 🐛 解决多轮消息发送失败的问题
|
||||
- 🐛 修复部分图片无法接收的问题
|
||||
- ✨ 增加支持 onboard 配置方式
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🔗 相关链接
|
||||
|
||||
- [QQ 机器人官方文档](https://bot.q.qq.com/wiki/)
|
||||
- [QQ 开放平台](https://q.qq.com/)
|
||||
- [API v2 文档](https://bot.q.qq.com/wiki/develop/api-v2/)
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT
|
||||
277
README.md
277
README.md
@@ -1,226 +1,125 @@
|
||||
# QQ Bot Channel Plugin for Moltbot
|
||||
# QQ
|
||||
|
||||
QQ 开放平台Bot API 的 Moltbot 渠道插件,支持 C2C 私聊、群聊 @消息、频道消息。
|
||||
QQ is a widely-used instant messaging platform that provides various communication capabilities such as text, voice, images, and files. It supports collaborative scenarios like group chats and channels, making it suitable for both personal communication and team collaboration.
|
||||
|
||||
## 功能特性
|
||||
This integration method connects OpenClaw with a QQ Bot. It utilizes the platform's long-connection event subscription mechanism to receive message and event callbacks, enabling stable and secure message exchange and automation capability integration without exposing a public webhook address.
|
||||
|
||||
- **多场景支持**:C2C 单聊、QQ 群 @消息、频道公开消息、频道私信
|
||||
- **自动重连**:WebSocket 断连后自动重连,支持 Session Resume
|
||||
- **消息去重**:自动管理 `msg_seq`,支持对同一消息多次回复
|
||||
- **系统提示词**:可配置自定义系统提示词注入到 AI 请求
|
||||
- **错误提示**:AI 无响应时自动提示用户检查配置
|
||||
# Step 1: Install the QQ Bot Plugin
|
||||
|
||||
## 使用示例:
|
||||
<img width="952" height="582" alt="image" src="https://github.com/user-attachments/assets/a16d582b-708c-473e-b3a2-e0c4c503a0c8" />
|
||||
Install via the OpenClaw plugins command.
|
||||
|
||||
## 版本更新
|
||||
**使用`openclaw plugins list`来产看插件版本,建议使用最新版的版本**
|
||||
<img width="902" height="248" alt="Clipboard_Screenshot_1769739939" src="https://github.com/user-attachments/assets/d6f37458-900c-4de9-8fdc-f8e6bf5c7ee5" />
|
||||
```
|
||||
openclaw plugins install @sliverp/qqbot@latest
|
||||
```
|
||||
|
||||
### 1.3.0(即将更新)
|
||||
- 支持图片收发等功能
|
||||
<img width="924" height="428" alt="Clipboard_Screenshot_1770112572" src="https://github.com/user-attachments/assets/80f38ae9-dc40-4545-ad17-e7e254064cf4" />
|
||||
Install from source code:
|
||||
|
||||
- 支持定时任务到时后主动推送
|
||||
<img width="930" height="288" alt="Clipboard_Screenshot_1770112539" src="https://github.com/user-attachments/assets/9674cda0-91e9-4860-8dcc-bc50007862a2" />
|
||||
|
||||
|
||||
### 1.2.5-2026.02.02
|
||||
- 解除URL发送限制,现在可以直接在私聊发送URL
|
||||
<img width="886" height="276" alt="Clipboard_Screenshot_1770092858" src="https://github.com/user-attachments/assets/c660949e-28a5-4e5f-abc2-77f0a2c67bad" />
|
||||
- 更新Bot正在输入中状态
|
||||
<img width="740" height="212" alt="Clipboard_Screenshot_1770091969" src="https://github.com/user-attachments/assets/47835c4b-ccd2-4782-aaa6-b873cb58f7d7" />
|
||||
- 提供主动推送能力(目前AI还不知道怎么调用主动推送,相关完整Skill能力将在后续版本更新)
|
||||
- 优化一些已知问题
|
||||
- 优化未收到未收到大模型响应时的提示信息
|
||||
|
||||
|
||||
### 1.2.2-2026.01.31
|
||||
- 支持发送文件
|
||||
- 支持openclaw、moltbot命令行
|
||||
- 修复[health]检查提示: [health] refresh failed: Cannot read properties of undefined (reading 'appId')的问题(不影响使用)
|
||||
- 修复文件发送后clawdbot无法读取的问题
|
||||
|
||||
### 1.2.1
|
||||
- 解决了长时间使用会断联的问题
|
||||
- 解决了频繁重连的问题
|
||||
- 增加了大模型调用失败后的提示消息
|
||||
|
||||
|
||||
### 1.1.0
|
||||
- 解决了一些url会被拦截的问题
|
||||
- 解决了多轮消息会发送失败的问题
|
||||
- 修复了部分图片无法接受的问题
|
||||
- 增加支持onboard的方式配置AppId 和 AppSecret
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
在插件目录下执行:
|
||||
|
||||
```bash
|
||||
```
|
||||
git clone https://github.com/sliverp/qqbot.git && cd qqbot
|
||||
clawdbot plugins install . # 这一步会有点久,需要安装一些依赖。稍微耐心等待一下,尤其是小内存机器
|
||||
openclaw plugins install .
|
||||
```
|
||||
|
||||
## 配置
|
||||
# Step 2: Create a QQ Bot
|
||||
|
||||
### 1. 获取 QQ 机器人凭证
|
||||
## 1. Register on the QQ Open Platform
|
||||
|
||||
1. 访问 [QQ 开放平台](https://q.qq.com/)
|
||||
2. 创建机器人应用
|
||||
3. 获取 `AppID` 和 `AppSecret`(ClientSecret)
|
||||
4. Token 格式为 `AppID:AppSecret`,例如 `102146862:Xjv7JVhu7KXkxANbp3HVjxCRgvAPeuAQ`
|
||||
Go to the official website of the Tencent QQ Open Platform. You cannot log in directly with your personal QQ account by default; you need to register a new QQ Open Platform account.
|
||||
<img width="2140" height="1004" alt="1" src="https://github.com/user-attachments/assets/d76a780c-5040-43fb-ac41-5808f975ae4b" />
|
||||
|
||||
### 2. 添加配置
|
||||
After the initial registration, follow the platform's instructions to set up a super administrator.
|
||||
|
||||
#### 方式一:交互式配置
|
||||
<img width="2556" height="1744" alt="2" src="https://github.com/user-attachments/assets/ad0a54d5-6997-4f52-ae8f-bea71aa11c30" />
|
||||
After successfully scanning the QR code with your mobile QQ, proceed to the next step to fill in the relevant entity information.
|
||||
|
||||
```bash
|
||||
clawdbot channels add
|
||||
# 选择 qqbot,按提示输入 Token
|
||||
Using "Individual" as an example here, follow the prompts to enter your name, ID number, phone number, and verification code, then click continue to proceed to the facial recognition step.
|
||||
<img width="2544" height="1744" alt="3" src="https://github.com/user-attachments/assets/b85c11f8-5627-4e08-b522-b38c4929bcb6" />
|
||||
|
||||
Use your mobile QQ to scan the QR code for facial recognition.
|
||||
<img width="2542" height="1272" alt="4" src="https://github.com/user-attachments/assets/d0db5539-56ef-4189-930f-595348892bef" />
|
||||
|
||||
Once the facial recognition review is approved, you can log in to the QQ Open Platform.
|
||||
<img width="2356" height="1308" alt="5" src="https://github.com/user-attachments/assets/c1875b27-fefc-4a1c-81ef-863da8b15ec6" />
|
||||
|
||||
## 2. Create a QQ Bot
|
||||
|
||||
On the QQ Open Platform's QQ Bot page, you can create a bot.
|
||||
<img width="2334" height="1274" alt="6" src="https://github.com/user-attachments/assets/8389c38d-6662-46d0-ae04-92af374b61ef" />
|
||||
<img width="2316" height="1258" alt="7" src="https://github.com/user-attachments/assets/15cfe57a-0404-4b02-85fe-42a22cf96d01" />
|
||||
|
||||
After the QQ Bot is created, you can select it and click to enter the management page.
|
||||
<img width="3002" height="1536" alt="8" src="https://github.com/user-attachments/assets/7c0c7c69-29db-457f-974a-4aa52ebd7973" />
|
||||
|
||||
On the QQ Bot management page, obtain the current bot's AppID and AppSecret, copy them, and save them to your personal notepad or memo (please ensure data security and do not leak them). They will be needed later in "Step 3: Configuring OpenClaw".
|
||||
|
||||
Note: For security reasons, the QQ Bot's AppSecret is not stored in plain text. If you view it for the first time or forget it, you need to regenerate it.
|
||||
<img width="2970" height="1562" alt="9" src="https://github.com/user-attachments/assets/c7fc3094-2840-4780-a202-47b2c2b74e50" />
|
||||
<img width="1258" height="594" alt="10" src="https://github.com/user-attachments/assets/4445bede-e7d5-4927-9821-039e7ad8f1f5" />
|
||||
|
||||
## 3. Sandbox Configuration
|
||||
|
||||
On the QQ Bot's "Development Management" page, in the "Sandbox Configuration" section, set up private chat (select "Configure in Message List").
|
||||
|
||||
You can configure this according to your own usage scenario, or you can complete the subsequent steps and then return to this step to operate.
|
||||
|
||||
⚠️ Note:
|
||||
The QQ Bot created here does not need to be published and made available to all QQ users. It can be used for personal (sandbox) debugging and experience.
|
||||
The QQ Open Platform does not support "Configuration in QQ Groups" for bots; it only supports private chat with the QQ Bot.
|
||||
<img width="1904" height="801" alt="11" src="https://github.com/user-attachments/assets/f3940a87-aae7-4c89-8f9a-c94fb52cd3ea" />
|
||||
|
||||
Note: When selecting "Configure in Message List", you need to first add members, and then use the QQ scan code of that member to add the bot.
|
||||
<img width="2582" height="484" alt="12" src="https://github.com/user-attachments/assets/5631fe76-2205-4b1e-b463-75fa3a397464" />
|
||||
Note here that after successfully adding a member, you still need to use QQ scan code to add the bot.
|
||||
|
||||
<img width="2286" height="1324" alt="13" src="https://github.com/user-attachments/assets/cbf379be-ef6e-4391-8cb1-67c08aad2d43" />
|
||||
At this point, after adding the bot to your QQ account, you still cannot have a normal conversation with it. You will receive a prompt saying "The bot has gone to Mars, please try again later." This is because the QQ bot has not yet been connected to the OpenClaw application.
|
||||
|
||||
You need to proceed with the following steps to configure the QQ bot's AppID and AppSecret for the OpenClaw application.
|
||||
|
||||
<img width="872" height="1052" alt="14" src="https://github.com/user-attachments/assets/0c02aaf6-6cf9-419c-a6ab-36398d73c5ba" />
|
||||
|
||||
(Optional) You can also add more members by referring to the previous steps: First, add a new member in the member management page, then add the member in the sandbox configuration page. After that, the new member can add this QQ bot by scanning the QR code with QQ.
|
||||
<img width="3006" height="1504" alt="15" src="https://github.com/user-attachments/assets/cecef3a6-0596-4da0-8b92-8d67b8f3cdca" />
|
||||
<img width="2902" height="1394" alt="16" src="https://github.com/user-attachments/assets/eb98ffce-490f-402c-8b0c-af7ede1b1303" />
|
||||
<img width="1306" height="672" alt="17" src="https://github.com/user-attachments/assets/799056e3-82a6-44bc-9e3d-9c840faafa41" />
|
||||
|
||||
# Step 3: Configure OpenClaw
|
||||
|
||||
## Method 1: Configure via Wizard (Recommended)
|
||||
|
||||
Add the qqbot channel and input the AppID and AppSecret obtained in Step 2.
|
||||
|
||||
```
|
||||
openclaw channels add --channel qqbot --token "AppID:AppSecret"
|
||||
```
|
||||
|
||||
#### 方式二:命令行配置
|
||||
## Method 2: Configure via Configuration File
|
||||
|
||||
```bash
|
||||
clawdbot channels add --channel qqbot --token "AppID:AppSecret"
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
clawdbot channels add --channel qqbot --token "102146862:xxxxxxxx"
|
||||
```
|
||||
|
||||
### 3. 手动编辑配置(可选)
|
||||
|
||||
也可以直接编辑 `~/.clawdbot/clawdbot.json`:
|
||||
Edit ~/.openclaw/openclaw.json:
|
||||
|
||||
``` json
|
||||
{
|
||||
"channels": {
|
||||
"qqbot": {
|
||||
"enabled": true,
|
||||
"appId": "你的AppID",
|
||||
"clientSecret": "你的AppSecret",
|
||||
"systemPrompt": "你是一个友好的助手"
|
||||
"appId": "Your AppID",
|
||||
"clientSecret": "Your AppSecret"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Step 4: Start and Test
|
||||
|
||||
|
||||
## 配置项说明
|
||||
|
||||
| 配置项 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| `appId` | string | 是 | QQ 机器人 AppID |
|
||||
| `clientSecret` | string | 是* | AppSecret,与 `clientSecretFile` 二选一 |
|
||||
| `clientSecretFile` | string | 是* | AppSecret 文件路径 |
|
||||
| `enabled` | boolean | 否 | 是否启用,默认 `true` |
|
||||
| `name` | string | 否 | 账户显示名称 |
|
||||
| `systemPrompt` | string | 否 | 自定义系统提示词 |
|
||||
|
||||
## 支持的消息类型
|
||||
|
||||
| 事件类型 | 说明 | Intent |
|
||||
|----------|------|--------|
|
||||
| `C2C_MESSAGE_CREATE` | C2C 单聊消息 | `1 << 25` |
|
||||
| `GROUP_AT_MESSAGE_CREATE` | 群聊 @机器人消息 | `1 << 25` |
|
||||
| `AT_MESSAGE_CREATE` | 频道 @机器人消息 | `1 << 30` |
|
||||
| `DIRECT_MESSAGE_CREATE` | 频道私信 | `1 << 12` |
|
||||
|
||||
## 使用
|
||||
|
||||
### 启动
|
||||
|
||||
后台启动
|
||||
```bash
|
||||
clawdbot gateway restart
|
||||
```
|
||||
|
||||
前台启动, 方便试试查看日志
|
||||
```bash
|
||||
clawdbot gateway --port 18789 --verbose
|
||||
```
|
||||
|
||||
### CLI 配置向导
|
||||
|
||||
```bash
|
||||
clawdbot onboard
|
||||
# 选择 QQ Bot 进行交互式配置
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **群消息**:需要在群内 @机器人 才能触发回复
|
||||
2. **沙箱模式**:新创建的机器人默认在沙箱模式,需要添加测试用户
|
||||
|
||||
## 升级
|
||||
|
||||
如果需要升级插件,先运行升级脚本清理旧版本:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/sliverp/qqbot.git && cd qqbot
|
||||
|
||||
# 运行升级脚本(清理旧版本和配置)
|
||||
bash ./scripts/upgrade.sh
|
||||
|
||||
# 重新安装插件
|
||||
clawdbot plugins install . # 这一步会有点久,需要安装一些依赖。稍微耐心等待一下,尤其是小内存机器
|
||||
|
||||
# 重新配置
|
||||
clawdbot channels add --channel qqbot --token "AppID:AppSecret"
|
||||
|
||||
# 重启网关
|
||||
clawdbot gateway restart
|
||||
```
|
||||
|
||||
升级脚本会自动:
|
||||
- 删除 `~/.clawdbot/extensions/qqbot` 目录
|
||||
- 清理 `clawdbot.json` 中的 qqbot 相关配置
|
||||
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 编译
|
||||
npm run build
|
||||
|
||||
# 监听模式
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 文件结构
|
||||
## 1. Start the gateway
|
||||
|
||||
```
|
||||
qqbot/
|
||||
├── index.ts # 入口文件
|
||||
├── src/
|
||||
│ ├── api.ts # QQ Bot API 封装
|
||||
│ ├── channel.ts # Channel Plugin 定义
|
||||
│ ├── config.ts # 配置解析
|
||||
│ ├── gateway.ts # WebSocket 网关
|
||||
│ ├── onboarding.ts # CLI 配置向导
|
||||
│ ├── outbound.ts # 出站消息处理
|
||||
│ ├── runtime.ts # 运行时状态
|
||||
│ └── types.ts # 类型定义
|
||||
├── scripts/
|
||||
│ └── upgrade.sh # 升级脚本
|
||||
├── package.json
|
||||
└── tsconfig.json
|
||||
openclaw gateway
|
||||
```
|
||||
|
||||
## 相关链接
|
||||
## 2. Chat with the QQbot in QQ
|
||||
|
||||
- [QQ 机器人官方文档](https://bot.q.qq.com/wiki/)
|
||||
- [QQ 开放平台](https://q.qq.com/)
|
||||
- [API v2 文档](https://bot.q.qq.com/wiki/develop/api-v2/)
|
||||
<img width="990" height="984" alt="18" src="https://github.com/user-attachments/assets/b2776c8b-de72-4e37-b34d-e8287ce45de1" />
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
# Other Language README
|
||||
[简体中文](README.zh.md)
|
||||
115
README.zh.md
Normal file
115
README.zh.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# QQ
|
||||
QQ 是一款覆盖广泛用户群体的即时通讯平台,提供文字、语音、图片、文件等多种沟通能力,并支持群聊、频道等协作场景,适用于个人交流与团队协同。
|
||||
|
||||
该接入方式可将 OpenClaw 与 QQ Bot 进行连接,通过平台的长连接事件订阅机制接收消息与事件回调,从而在不对外暴露公网 Webhook 地址的情况下实现稳定、安全的消息收发与自动化能力集成。
|
||||
|
||||
# 步骤1:安装QQ Bot插件
|
||||
|
||||
OpenClaw plugins命令安装
|
||||
|
||||
```
|
||||
openclaw plugins install @sliverp/qqbot@latest
|
||||
```
|
||||
|
||||
使用源码安装:
|
||||
|
||||
```
|
||||
git clone https://github.com/sliverp/qqbot.git && cd qqbot
|
||||
openclaw plugins install .
|
||||
```
|
||||
|
||||
# 步骤2:创建QQ Bot
|
||||
## 1.注册QQ开放平台
|
||||
前往腾讯QQ开放平台官网,默认无法使用您的QQ账号直接登录,需要新注册QQ开放平台账号。
|
||||
<img width="2140" height="1004" alt="1" src="https://github.com/user-attachments/assets/d76a780c-5040-43fb-ac41-5808f975ae4b" />
|
||||
|
||||
首次注册之后,可以按照QQ开放平台的指引设置超级管理员。
|
||||
|
||||
<img width="2556" height="1744" alt="2" src="https://github.com/user-attachments/assets/ad0a54d5-6997-4f52-ae8f-bea71aa11c30" />
|
||||
手机QQ扫码成功后,继续下一步填写主体相关信息。
|
||||
|
||||
此处以“个人”为例,按照指引依次输入姓名、身份证号、手机号、验证码,点击继续进入下一步人脸认证。
|
||||
<img width="2544" height="1744" alt="3" src="https://github.com/user-attachments/assets/b85c11f8-5627-4e08-b522-b38c4929bcb6" />
|
||||
|
||||
使用手机QQ扫码进行人脸认证。
|
||||
<img width="2542" height="1272" alt="4" src="https://github.com/user-attachments/assets/d0db5539-56ef-4189-930f-595348892bef" />
|
||||
|
||||
人脸识别审核通过后,即可登录进入QQ开放平台。
|
||||
<img width="2356" height="1308" alt="5" src="https://github.com/user-attachments/assets/c1875b27-fefc-4a1c-81ef-863da8b15ec6" />
|
||||
|
||||
## 2.创建一个QQBot机器人
|
||||
在QQ开放平台的QQ机器人页面,可以创建机器人。
|
||||
<img width="2334" height="1274" alt="6" src="https://github.com/user-attachments/assets/8389c38d-6662-46d0-ae04-92af374b61ef" />
|
||||
<img width="2316" height="1258" alt="7" src="https://github.com/user-attachments/assets/15cfe57a-0404-4b02-85fe-42a22cf96d01" />
|
||||
|
||||
QQ机器人创建完成之后,可选择机器人点击进入管理页面。
|
||||
<img width="3002" height="1536" alt="8" src="https://github.com/user-attachments/assets/7c0c7c69-29db-457f-974a-4aa52ebd7973" />
|
||||
|
||||
在QQ机器人管理页面获取当前机器人的AppID和AppSecret,复制并将其保存到个人记事本或备忘录中(请注意数据安全,勿泄露),后续在“步骤3中配置OpenClaw“中需要使用。
|
||||
|
||||
注意:出于安全考虑,QQ机器人的AppSecret不支持明文保存,首次查看或忘记AppSecret需要重新生成。
|
||||
<img width="2970" height="1562" alt="9" src="https://github.com/user-attachments/assets/c7fc3094-2840-4780-a202-47b2c2b74e50" />
|
||||
<img width="1258" height="594" alt="10" src="https://github.com/user-attachments/assets/4445bede-e7d5-4927-9821-039e7ad8f1f5" />
|
||||
|
||||
## 3.沙箱配置
|
||||
在QQ机器人的“开发管理”页面,在“沙箱配置”中,设置单独聊天(选择“在消息列表配置”)。
|
||||
|
||||
您可以按照自己的使用场景进行配置,也可以完成后续步骤之后再回到本步骤进行操作。
|
||||
|
||||
⚠️ 注意:
|
||||
此处已创建的QQ机器人无需进行发布上架对所有QQ用户公开使用,在开发者私人的(沙箱)调试下使用体验即可。
|
||||
QQ开放平台不支持机器人的“在QQ群配置”操作,只支持单独和QQ机器人聊天。
|
||||
<img width="1904" height="801" alt="11" src="https://github.com/user-attachments/assets/f3940a87-aae7-4c89-8f9a-c94fb52cd3ea" />
|
||||
|
||||
注意:选择“在消息列表配置”时,需要先添加成员,再通过该成员的QQ扫码来添加机器人。
|
||||
<img width="2582" height="484" alt="12" src="https://github.com/user-attachments/assets/5631fe76-2205-4f1e-b463-75fa3a397464" />
|
||||
此处注意添加成员成功之后,还需要使用QQ扫码添加
|
||||
|
||||
<img width="2286" height="1324" alt="13" src="https://github.com/user-attachments/assets/cbf379be-ef6e-4391-8cb1-67c08aad2d43" />
|
||||
此时您的QQ账号添加机器人之后,还不能与该机器人正常进行对话,会提示“该机器人去火星了,稍后再试吧”,因为QQ机器人此时尚未与OpenClaw应用打通。
|
||||
|
||||
您需要继续后面的步骤,为OpenClaw应用配置QQ机器人的AppID和AppSecret。
|
||||
|
||||
<img width="872" height="1052" alt="14" src="https://github.com/user-attachments/assets/0c02aaf6-6cf9-419c-a6ab-36398d73c5ba" />
|
||||
|
||||
(可选)您也可以参考前述步骤添加更多成员:首先在成员管理页面中添加新成员,然后在沙箱配置页面中添加成员,之后新成员即可通过QQ扫码添加该QQ机器人。
|
||||
<img width="3006" height="1504" alt="15" src="https://github.com/user-attachments/assets/cecef3a6-0596-4da0-8b92-8d67b8f3cdca" />
|
||||
<img width="2902" height="1394" alt="16" src="https://github.com/user-attachments/assets/eb98ffce-490f-402c-8b0c-af7ede1b1303" />
|
||||
<img width="1306" height="672" alt="17" src="https://github.com/user-attachments/assets/799056e3-82a6-44bc-9e3d-9c840faafa41" />
|
||||
|
||||
# 步骤3: 配置OpenClaw
|
||||
## 方式一: 通过Wizard配置(推荐)
|
||||
|
||||
添加qqbot channel 并将步骤2中获取的AppID和AppSecret
|
||||
```
|
||||
openclaw channels add --channel qqbot --token "AppID:AppSecret"
|
||||
```
|
||||
## 方式二:通过配置文件配置
|
||||
|
||||
编辑 ~/.openclaw/openclaw.json:
|
||||
``` json
|
||||
{
|
||||
"channels": {
|
||||
"qqbot": {
|
||||
"enabled": true,
|
||||
"appId": "你的AppID",
|
||||
"clientSecret": "你的AppSecret"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# 步骤4:启动与测试
|
||||
|
||||
## 1.启动gateway
|
||||
|
||||
```
|
||||
openclaw gateway
|
||||
```
|
||||
|
||||
## 2.在QQ中与QQbot 对话
|
||||
|
||||
<img width="990" height="984" alt="18" src="https://github.com/user-attachments/assets/b2776c8b-de72-4e37-b34d-e8287ce45de1" />
|
||||
|
||||
# 其他语言 README
|
||||
[英文](README.md)
|
||||
227
bin/qqbot-cli.js
Normal file
227
bin/qqbot-cli.js
Normal file
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* QQBot CLI - 用于升级和管理 QQBot 插件
|
||||
*
|
||||
* 用法:
|
||||
* npx @sliverp/qqbot upgrade # 升级插件
|
||||
* npx @sliverp/qqbot install # 安装插件
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, readFileSync, writeFileSync, rmSync } from 'fs';
|
||||
import { homedir } from 'os';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// 获取包的根目录
|
||||
const PKG_ROOT = join(__dirname, '..');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0];
|
||||
|
||||
// 检测使用的是 clawdbot 还是 openclaw
|
||||
function detectInstallation() {
|
||||
const home = homedir();
|
||||
if (existsSync(join(home, '.openclaw'))) {
|
||||
return 'openclaw';
|
||||
}
|
||||
if (existsSync(join(home, '.clawdbot'))) {
|
||||
return 'clawdbot';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 清理旧版本插件,返回旧的 qqbot 配置
|
||||
function cleanupInstallation(appName) {
|
||||
const home = homedir();
|
||||
const appDir = join(home, `.${appName}`);
|
||||
const configFile = join(appDir, `${appName}.json`);
|
||||
const extensionDir = join(appDir, 'extensions', 'qqbot');
|
||||
|
||||
let oldQqbotConfig = null;
|
||||
|
||||
console.log(`\n>>> 处理 ${appName} 安装...`);
|
||||
|
||||
// 1. 先读取旧的 qqbot 配置
|
||||
if (existsSync(configFile)) {
|
||||
try {
|
||||
const config = JSON.parse(readFileSync(configFile, 'utf8'));
|
||||
if (config.channels?.qqbot) {
|
||||
oldQqbotConfig = { ...config.channels.qqbot };
|
||||
console.log('已保存旧的 qqbot 配置');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('读取配置文件失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 删除旧的扩展目录
|
||||
if (existsSync(extensionDir)) {
|
||||
console.log(`删除旧版本插件: ${extensionDir}`);
|
||||
rmSync(extensionDir, { recursive: true, force: true });
|
||||
} else {
|
||||
console.log('未找到旧版本插件目录,跳过删除');
|
||||
}
|
||||
|
||||
// 3. 清理配置文件中的 qqbot 相关字段
|
||||
if (existsSync(configFile)) {
|
||||
console.log('清理配置文件中的 qqbot 字段...');
|
||||
try {
|
||||
const config = JSON.parse(readFileSync(configFile, 'utf8'));
|
||||
|
||||
// 删除 channels.qqbot
|
||||
if (config.channels?.qqbot) {
|
||||
delete config.channels.qqbot;
|
||||
console.log(' - 已删除 channels.qqbot');
|
||||
}
|
||||
|
||||
// 删除 plugins.entries.qqbot
|
||||
if (config.plugins?.entries?.qqbot) {
|
||||
delete config.plugins.entries.qqbot;
|
||||
console.log(' - 已删除 plugins.entries.qqbot');
|
||||
}
|
||||
|
||||
// 删除 plugins.installs.qqbot
|
||||
if (config.plugins?.installs?.qqbot) {
|
||||
delete config.plugins.installs.qqbot;
|
||||
console.log(' - 已删除 plugins.installs.qqbot');
|
||||
}
|
||||
|
||||
writeFileSync(configFile, JSON.stringify(config, null, 2));
|
||||
console.log('配置文件已更新');
|
||||
} catch (err) {
|
||||
console.error('清理配置文件失败:', err.message);
|
||||
}
|
||||
} else {
|
||||
console.log(`未找到配置文件: ${configFile}`);
|
||||
}
|
||||
|
||||
return oldQqbotConfig;
|
||||
}
|
||||
|
||||
// 执行命令并继承 stdio
|
||||
function runCommand(cmd, args = []) {
|
||||
try {
|
||||
execSync([cmd, ...args].join(' '), { stdio: 'inherit' });
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 升级命令
|
||||
function upgrade() {
|
||||
console.log('=== QQBot 插件升级脚本 ===');
|
||||
|
||||
let foundInstallation = null;
|
||||
let savedConfig = null;
|
||||
const home = homedir();
|
||||
|
||||
// 检查 openclaw
|
||||
if (existsSync(join(home, '.openclaw'))) {
|
||||
savedConfig = cleanupInstallation('openclaw');
|
||||
foundInstallation = 'openclaw';
|
||||
}
|
||||
|
||||
// 检查 clawdbot
|
||||
if (existsSync(join(home, '.clawdbot'))) {
|
||||
const clawdbotConfig = cleanupInstallation('clawdbot');
|
||||
if (!savedConfig) savedConfig = clawdbotConfig;
|
||||
foundInstallation = 'clawdbot';
|
||||
}
|
||||
|
||||
if (!foundInstallation) {
|
||||
console.log('\n未找到 clawdbot 或 openclaw 安装目录');
|
||||
console.log('请确认已安装 clawdbot 或 openclaw');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('\n=== 清理完成 ===');
|
||||
|
||||
// 自动安装插件
|
||||
console.log('\n[1/2] 安装新版本插件...');
|
||||
runCommand(foundInstallation, ['plugins', 'install', '@sliverp/qqbot']);
|
||||
|
||||
// 自动配置通道(使用保存的 appId 和 clientSecret)
|
||||
console.log('\n[2/2] 配置机器人通道...');
|
||||
if (savedConfig?.appId && savedConfig?.clientSecret) {
|
||||
const token = `${savedConfig.appId}:${savedConfig.clientSecret}`;
|
||||
console.log(`使用已保存的配置: appId=${savedConfig.appId}`);
|
||||
runCommand(foundInstallation, ['channels', 'add', '--channel', 'qqbot', '--token', `"${token}"`]);
|
||||
|
||||
// 恢复其他配置项(如 markdownSupport)
|
||||
if (savedConfig.markdownSupport !== undefined) {
|
||||
runCommand(foundInstallation, ['config', 'set', 'channels.qqbot.markdownSupport', String(savedConfig.markdownSupport)]);
|
||||
}
|
||||
} else {
|
||||
console.log('未找到已保存的 qqbot 配置,请手动配置:');
|
||||
console.log(` ${foundInstallation} channels add --channel qqbot --token "AppID:AppSecret"`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n=== 升级完成 ===');
|
||||
console.log(`\n可以运行以下命令前台运行启动机器人:`);
|
||||
console.log(` ${foundInstallation} gateway stop && ${foundInstallation} gateway --port 18789 --verbose`);
|
||||
}
|
||||
|
||||
// 安装命令
|
||||
function install() {
|
||||
console.log('=== QQBot 插件安装 ===');
|
||||
|
||||
const cmd = detectInstallation();
|
||||
if (!cmd) {
|
||||
console.log('未找到 clawdbot 或 openclaw 安装');
|
||||
console.log('请先安装 openclaw 或 clawdbot');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`\n使用 ${cmd} 安装插件...`);
|
||||
runCommand(cmd, ['plugins', 'install', '@sliverp/qqbot']);
|
||||
|
||||
console.log('\n=== 安装完成 ===');
|
||||
console.log('\n请配置机器人通道:');
|
||||
console.log(` ${cmd} channels add --channel qqbot --token "AppID:AppSecret"`);
|
||||
}
|
||||
|
||||
// 显示帮助
|
||||
function showHelp() {
|
||||
console.log(`
|
||||
QQBot CLI - QQ机器人插件管理工具
|
||||
|
||||
用法:
|
||||
npx @sliverp/qqbot <命令>
|
||||
|
||||
命令:
|
||||
upgrade 清理旧版本插件(升级前执行)
|
||||
install 安装插件到 openclaw/clawdbot
|
||||
|
||||
示例:
|
||||
npx @sliverp/qqbot upgrade
|
||||
npx @sliverp/qqbot install
|
||||
`);
|
||||
}
|
||||
|
||||
// 主入口
|
||||
switch (command) {
|
||||
case 'upgrade':
|
||||
upgrade();
|
||||
break;
|
||||
case 'install':
|
||||
install();
|
||||
break;
|
||||
case '-h':
|
||||
case '--help':
|
||||
case 'help':
|
||||
showHelp();
|
||||
break;
|
||||
default:
|
||||
if (command) {
|
||||
console.log(`未知命令: ${command}`);
|
||||
}
|
||||
showHelp();
|
||||
process.exit(command ? 1 : 0);
|
||||
}
|
||||
1153
package-lock.json
generated
1153
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,9 +1,23 @@
|
||||
{
|
||||
"name": "qqbot",
|
||||
"version": "1.3.0",
|
||||
"name": "@sliverp/qqbot",
|
||||
"version": "1.4.2",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"bin": {
|
||||
"qqbot": "./bin/qqbot-cli.js"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"bin",
|
||||
"src",
|
||||
"skills",
|
||||
"index.ts",
|
||||
"tsconfig.json",
|
||||
"openclaw.plugin.json",
|
||||
"clawdbot.plugin.json",
|
||||
"moltbot.plugin.json"
|
||||
],
|
||||
"clawdbot": {
|
||||
"extensions": ["./index.ts"]
|
||||
},
|
||||
@@ -33,5 +47,6 @@
|
||||
"clawdbot": "*",
|
||||
"moltbot": "*",
|
||||
"openclaw": "*"
|
||||
}
|
||||
},
|
||||
"homepage": "https://github.com/sliverp/qqbot"
|
||||
}
|
||||
|
||||
@@ -120,6 +120,20 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
|
||||
configured: Boolean(account?.appId && account?.clientSecret),
|
||||
tokenSource: account?.secretSource,
|
||||
}),
|
||||
// 关键:解析 allowFrom 配置,用于命令授权
|
||||
resolveAllowFrom: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string }) => {
|
||||
const account = resolveQQBotAccount(cfg, accountId);
|
||||
const allowFrom = account.config?.allowFrom ?? [];
|
||||
console.log(`[qqbot] resolveAllowFrom: accountId=${accountId}, allowFrom=${JSON.stringify(allowFrom)}`);
|
||||
return allowFrom.map((entry: string | number) => String(entry));
|
||||
},
|
||||
// 格式化 allowFrom 条目(移除 qqbot: 前缀,统一大写)
|
||||
formatAllowFrom: ({ allowFrom }: { allowFrom: Array<string | number> }) =>
|
||||
allowFrom
|
||||
.map((entry: string | number) => String(entry).trim())
|
||||
.filter(Boolean)
|
||||
.map((entry: string) => entry.replace(/^qqbot:/i, ""))
|
||||
.map((entry: string) => entry.toUpperCase()), // QQ openid 是大写的
|
||||
},
|
||||
setup: {
|
||||
// 新增:规范化账户 ID
|
||||
@@ -159,18 +173,6 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
|
||||
});
|
||||
},
|
||||
},
|
||||
// 新增:消息目标解析
|
||||
messaging: {
|
||||
normalizeTarget: (target) => {
|
||||
// 支持格式: qqbot:openid, qqbot:group:xxx, openid, group:xxx
|
||||
const normalized = target.replace(/^qqbot:/i, "");
|
||||
return { ok: true, to: normalized };
|
||||
},
|
||||
targetResolver: {
|
||||
looksLikeId: (id) => /^[A-F0-9]{32}$/i.test(id) || id.startsWith("group:") || id.startsWith("channel:"),
|
||||
hint: "<openid> or group:<groupOpenid>",
|
||||
},
|
||||
},
|
||||
outbound: {
|
||||
deliveryMode: "direct",
|
||||
chunker: chunkText,
|
||||
|
||||
@@ -76,7 +76,7 @@ export function resolveQQBotAccount(
|
||||
allowFrom: qqbot?.allowFrom,
|
||||
systemPrompt: qqbot?.systemPrompt,
|
||||
imageServerBaseUrl: qqbot?.imageServerBaseUrl,
|
||||
markdownSupport: qqbot?.markdownSupport,
|
||||
markdownSupport: qqbot?.markdownSupport ?? true,
|
||||
};
|
||||
appId = qqbot?.appId ?? "";
|
||||
} else {
|
||||
@@ -128,11 +128,16 @@ export function applyQQBotAccountConfig(
|
||||
const next = { ...cfg };
|
||||
|
||||
if (accountId === DEFAULT_ACCOUNT_ID) {
|
||||
// 如果没有设置过 allowFrom,默认设置为 ["*"]
|
||||
const existingConfig = (next.channels?.qqbot as QQBotChannelConfig) || {};
|
||||
const allowFrom = existingConfig.allowFrom ?? ["*"];
|
||||
|
||||
next.channels = {
|
||||
...next.channels,
|
||||
qqbot: {
|
||||
...(next.channels?.qqbot as Record<string, unknown> || {}),
|
||||
enabled: true,
|
||||
allowFrom,
|
||||
...(input.appId ? { appId: input.appId } : {}),
|
||||
...(input.clientSecret
|
||||
? { clientSecret: input.clientSecret }
|
||||
@@ -144,6 +149,10 @@ export function applyQQBotAccountConfig(
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// 如果没有设置过 allowFrom,默认设置为 ["*"]
|
||||
const existingAccountConfig = (next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {};
|
||||
const allowFrom = existingAccountConfig.allowFrom ?? ["*"];
|
||||
|
||||
next.channels = {
|
||||
...next.channels,
|
||||
qqbot: {
|
||||
@@ -154,6 +163,7 @@ export function applyQQBotAccountConfig(
|
||||
[accountId]: {
|
||||
...((next.channels?.qqbot as QQBotChannelConfig)?.accounts?.[accountId] || {}),
|
||||
enabled: true,
|
||||
allowFrom,
|
||||
...(input.appId ? { appId: input.appId } : {}),
|
||||
...(input.clientSecret
|
||||
? { clientSecret: input.clientSecret }
|
||||
|
||||
@@ -2,7 +2,7 @@ import WebSocket from "ws";
|
||||
import path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent, GroupMessageEvent } from "./types.js";
|
||||
import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, startBackgroundTokenRefresh, stopBackgroundTokenRefresh } from "./api.js";
|
||||
import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage, clearTokenCache, sendC2CImageMessage, sendGroupImageMessage, initApiConfig, startBackgroundTokenRefresh, stopBackgroundTokenRefresh, sendC2CInputNotify } from "./api.js";
|
||||
import { loadSession, saveSession, clearSession, type SessionState } from "./session-store.js";
|
||||
import { recordKnownUser, flushKnownUsers } from "./known-users.js";
|
||||
import { getQQBotRuntime } from "./runtime.js";
|
||||
@@ -412,6 +412,13 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
||||
direction: "inbound",
|
||||
});
|
||||
|
||||
try{
|
||||
await sendC2CInputNotify(accessToken, event.senderId, event.messageId, 60);
|
||||
log?.info(`[qqbot:${account.accountId}] Sent input notify to ${event.senderId}`);
|
||||
}catch(err){
|
||||
log?.error(`[qqbot:${account.accountId}] sendC2CInputNotify error: ${err}`);
|
||||
}
|
||||
|
||||
const isGroup = event.type === "guild" || event.type === "group";
|
||||
const peerId = event.type === "guild" ? `channel:${event.channelId}`
|
||||
: event.type === "group" ? `group:${event.groupOpenid}`
|
||||
@@ -445,13 +452,14 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
||||
- 群组 group_openid: ${event.groupOpenid}` : ""}
|
||||
|
||||
【定时提醒能力】
|
||||
你可以帮助用户设置定时提醒。使用 openclaw cron 命令:
|
||||
你可以帮助用户设置定时提醒。使用exec工具来来运行bash命令 openclaw cron 命令而不是直接使用 "cron" 工具:
|
||||
|
||||
示例:5分钟后提醒用户喝水
|
||||
\`\`\`bash
|
||||
openclaw cron add \\
|
||||
--name "提醒喝水-${event.senderName || "用户"}" \\
|
||||
--at "5m" \\
|
||||
--session isolated \\
|
||||
--message "💧 该喝水啦!" \\
|
||||
--deliver \\
|
||||
--channel qqbot \\
|
||||
@@ -468,6 +476,7 @@ openclaw cron add \\
|
||||
- \`--tz "Asia/Shanghai"\`: 周期任务务必设置时区
|
||||
- \`--delete-after-run\`: 一次性任务必须添加此参数
|
||||
- \`--message\`: 消息内容(必填,不能为空!这是定时提醒触发时直接发送给用户的内容)
|
||||
- \`--session isolated\` 独立会话任务
|
||||
|
||||
⚠️ 重要注意事项:
|
||||
1. --at 参数格式:相对时间用 \`5m\`、\`1h\` 等(不要加 + 号!);绝对时间用完整 ISO 格式
|
||||
@@ -490,7 +499,8 @@ openclaw cron add \\
|
||||
⚠️ 注意:
|
||||
- 必须使用 <qqimg>路径</qqimg> 格式
|
||||
- 本地路径必须是绝对路径,支持 png、jpg、jpeg、gif、webp 格式
|
||||
- 图片文件/URL 必须有效,否则发送失败`;
|
||||
- 图片文件/URL 必须有效,否则发送失败
|
||||
- Markdown格式下,也必须使用该方式发送图片`;
|
||||
|
||||
const systemPrompts = [builtinPrompt];
|
||||
if (account.systemPrompt) {
|
||||
@@ -566,10 +576,15 @@ openclaw cron add \\
|
||||
}
|
||||
|
||||
const userContent = event.content + attachmentInfo;
|
||||
const messageBody = `【系统提示】\n${systemPrompts.join("\n")}\n\n【用户输入】\n${userContent}`;
|
||||
let messageBody = `【系统提示】\n${systemPrompts.join("\n")}\n\n【用户输入】\n${userContent}`;
|
||||
|
||||
if(userContent.startsWith("/")){ // 保留Openclaw原始命令
|
||||
messageBody = userContent
|
||||
}
|
||||
log?.info(`[qqbot:${account.accountId}] messageBody: ${messageBody}`);
|
||||
|
||||
const body = pluginRuntime.channel.reply.formatInboundEnvelope({
|
||||
channel: "QQBot",
|
||||
channel: "qqbot",
|
||||
from: event.senderName ?? event.senderId,
|
||||
timestamp: new Date(event.timestamp).getTime(),
|
||||
body: messageBody,
|
||||
@@ -588,6 +603,14 @@ openclaw cron add \\
|
||||
: `qqbot:c2c:${event.senderId}`;
|
||||
const toAddress = fromAddress;
|
||||
|
||||
// 计算命令授权状态
|
||||
// allowFrom: ["*"] 表示允许所有人,否则检查 senderId 是否在 allowFrom 列表中
|
||||
const allowFromList = account.config?.allowFrom ?? [];
|
||||
const allowAll = allowFromList.length === 0 || allowFromList.some((entry: string) => entry === "*");
|
||||
const commandAuthorized = allowAll || allowFromList.some((entry: string) =>
|
||||
entry.toUpperCase() === event.senderId.toUpperCase()
|
||||
);
|
||||
|
||||
const ctxPayload = pluginRuntime.channel.reply.finalizeInboundContext({
|
||||
Body: body,
|
||||
RawBody: event.content,
|
||||
@@ -608,6 +631,7 @@ openclaw cron add \\
|
||||
QQChannelId: event.channelId,
|
||||
QQGuildId: event.guildId,
|
||||
QQGroupOpenid: event.groupOpenid,
|
||||
CommandAuthorized: commandAuthorized,
|
||||
});
|
||||
|
||||
// 发送消息的辅助函数,带 token 过期重试
|
||||
@@ -808,7 +832,7 @@ openclaw cron add \\
|
||||
log?.info(`[qqbot:${account.accountId}] Sent image via <qqimg> tag: ${imagePath.slice(0, 60)}...`);
|
||||
} catch (err) {
|
||||
log?.error(`[qqbot:${account.accountId}] Failed to send image from <qqimg>: ${err}`);
|
||||
await sendErrorMessage(`发送图片失败: ${err}`);
|
||||
await sendErrorMessage(`图片发送失败,图片似乎不存在哦,图片路径:${imagePath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1186,11 +1210,6 @@ openclaw cron add \\
|
||||
textWithoutImages = textWithoutImages.replace(match[0], "").trim();
|
||||
}
|
||||
|
||||
// 处理文本中的 URL 点号(防止被 QQ 解析为链接)
|
||||
if (textWithoutImages) {
|
||||
textWithoutImages = textWithoutImages.replace(/([a-zA-Z0-9])\.([a-zA-Z0-9])/g, "$1_$2");
|
||||
}
|
||||
|
||||
try {
|
||||
// 发送图片(通过富媒体 API)
|
||||
for (const imageUrl of imageUrls) {
|
||||
@@ -1253,7 +1272,9 @@ openclaw cron add \\
|
||||
}
|
||||
},
|
||||
},
|
||||
replyOptions: {},
|
||||
replyOptions: {
|
||||
disableBlockStreaming: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 等待分发完成或超时
|
||||
|
||||
@@ -21,6 +21,7 @@ interface QQBotChannelConfig {
|
||||
clientSecretFile?: string;
|
||||
name?: string;
|
||||
imageServerBaseUrl?: string;
|
||||
allowFrom?: string[];
|
||||
accounts?: Record<string, {
|
||||
enabled?: boolean;
|
||||
appId?: string;
|
||||
@@ -28,6 +29,7 @@ interface QQBotChannelConfig {
|
||||
clientSecretFile?: string;
|
||||
name?: string;
|
||||
imageServerBaseUrl?: string;
|
||||
allowFrom?: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -137,6 +139,7 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
|
||||
qqbot: {
|
||||
...(next.channels?.qqbot as Record<string, unknown> || {}),
|
||||
enabled: true,
|
||||
allowFrom: resolvedAccount.config?.allowFrom ?? ["*"],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -200,6 +203,9 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
|
||||
).trim();
|
||||
}
|
||||
|
||||
// 默认允许所有人执行命令(用户无感知)
|
||||
const allowFrom: string[] = resolvedAccount.config?.allowFrom ?? ["*"];
|
||||
|
||||
// 应用配置
|
||||
if (appId && clientSecret) {
|
||||
if (accountId === DEFAULT_ACCOUNT_ID) {
|
||||
@@ -212,6 +218,7 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
|
||||
enabled: true,
|
||||
appId,
|
||||
clientSecret,
|
||||
allowFrom,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -230,6 +237,7 @@ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecr
|
||||
enabled: true,
|
||||
appId,
|
||||
clientSecret,
|
||||
allowFrom,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user