@core-workspace/infoflow-openclaw-plugin 2026.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +989 -0
- package/docs/architecture-data-flow.md +429 -0
- package/docs/architecture.md +423 -0
- package/docs/dev-guide.md +611 -0
- package/index.ts +29 -0
- package/openclaw.plugin.json +138 -0
- package/package.json +40 -0
- package/scripts/deploy.sh +34 -0
- package/skills/infoflow-dev/SKILL.md +88 -0
- package/skills/infoflow-dev/references/api.md +413 -0
- package/src/adapter/inbound/webhook-parser.ts +433 -0
- package/src/adapter/inbound/ws-receiver.ts +226 -0
- package/src/adapter/outbound/reply-dispatcher.ts +281 -0
- package/src/adapter/outbound/target-resolver.ts +109 -0
- package/src/channel/accounts.ts +164 -0
- package/src/channel/channel.ts +364 -0
- package/src/channel/media.ts +365 -0
- package/src/channel/monitor.ts +184 -0
- package/src/channel/outbound.ts +934 -0
- package/src/events.ts +62 -0
- package/src/handler/message-handler.ts +801 -0
- package/src/logging.ts +123 -0
- package/src/runtime.ts +14 -0
- package/src/security/dm-policy.ts +80 -0
- package/src/security/group-policy.ts +271 -0
- package/src/tools/actions/index.ts +456 -0
- package/src/tools/hooks/index.ts +82 -0
- package/src/tools/index.ts +277 -0
- package/src/types.ts +277 -0
- package/src/utils/store/message-store.ts +295 -0
- package/src/utils/token-adapter.ts +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
# Infoflow OpenClaw Plugin
|
|
2
|
+
|
|
3
|
+
[中文](#中文) | [English](#english)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<a id="中文"></a>
|
|
8
|
+
|
|
9
|
+
百度如流 (Infoflow) 企业消息平台 — OpenClaw 频道插件。支持 Webhook 和 WebSocket 两种连接方式,具备图片消息收发、引用回复等能力。
|
|
10
|
+
|
|
11
|
+
## 目录
|
|
12
|
+
|
|
13
|
+
- [快速开始](#快速开始)
|
|
14
|
+
- [配置](#配置)
|
|
15
|
+
- [插件架构](#插件架构)
|
|
16
|
+
- [开发指南](#开发指南)
|
|
17
|
+
- [扩展:Agent Tools](#扩展-agent-tools)
|
|
18
|
+
- [扩展:Agent Hooks](#扩展-agent-hooks)
|
|
19
|
+
- [扩展:Agent Skills](#扩展-agent-skills)
|
|
20
|
+
- [FAQ](#faq)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<a id="快速开始"></a>
|
|
25
|
+
|
|
26
|
+
## 快速开始
|
|
27
|
+
|
|
28
|
+
> 5 步完成部署,从零到机器人上线回消息。
|
|
29
|
+
|
|
30
|
+
### 准备:在如流企业后台创建应用
|
|
31
|
+
|
|
32
|
+
1. 如流企业后台 → 应用管理 → 创建机器人应用
|
|
33
|
+
2. 记录以下 4 个参数:
|
|
34
|
+
|
|
35
|
+
| 参数 | 在哪找 |
|
|
36
|
+
|------|--------|
|
|
37
|
+
| `appKey` | 应用基本信息 |
|
|
38
|
+
| `appSecret` | 应用基本信息 |
|
|
39
|
+
| `checkToken` | 消息推送 → 配置 |
|
|
40
|
+
| `encodingAESKey` | 消息推送 → 配置 |
|
|
41
|
+
|
|
42
|
+
3. 连接方式二选一:
|
|
43
|
+
- **WebSocket**(推荐,无需公网):不用额外配置
|
|
44
|
+
- **Webhook**:在"消息推送"填入 `https://你的域名/webhook/infoflow`
|
|
45
|
+
|
|
46
|
+
### 第一步:安装 OpenClaw
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# macOS / Linux
|
|
50
|
+
curl -fsSL https://openclaw.dev/install.sh | bash
|
|
51
|
+
|
|
52
|
+
# 安装完成后登录(按提示操作)
|
|
53
|
+
openclaw configure
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 第二步:克隆插件
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone ssh://icode.baidu.com:8235/baidu/hi/openclaw_infoflow
|
|
60
|
+
cd openclaw_infoflow
|
|
61
|
+
npm install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 第三步:写配置
|
|
65
|
+
|
|
66
|
+
打开 `~/.openclaw/openclaw.json`,在 `channels` 下加入如流配置:
|
|
67
|
+
|
|
68
|
+
```json5
|
|
69
|
+
{
|
|
70
|
+
"channels": {
|
|
71
|
+
"infoflow": {
|
|
72
|
+
"connectionMode": "websocket",
|
|
73
|
+
"wsGateway": "infoflow-open-gateway.weiyun.baidu.com",
|
|
74
|
+
"apiHost": "https://apiin.im.baidu.com",
|
|
75
|
+
"appKey": "填你的 appKey",
|
|
76
|
+
"appSecret": "填你的 appSecret",
|
|
77
|
+
"checkToken": "填你的 checkToken",
|
|
78
|
+
"encodingAESKey": "填你的 encodingAESKey"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
更多配置项(访问控制、群聊策略、消息格式等)见[配置参考](#配置)。
|
|
85
|
+
|
|
86
|
+
### 第四步:部署插件
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
./scripts/deploy.sh
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 第五步:验证
|
|
93
|
+
|
|
94
|
+
- **私聊**:在如流给机器人发一条消息,应有回复
|
|
95
|
+
- **群聊**:在白名单群 @机器人,应有回复
|
|
96
|
+
- **查日志**:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
openclaw logs | grep infoflow
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 常见问题
|
|
103
|
+
|
|
104
|
+
**机器人不回复?**
|
|
105
|
+
```bash
|
|
106
|
+
openclaw doctor # 检查配置
|
|
107
|
+
openclaw status # 查看各渠道状态
|
|
108
|
+
openclaw logs | grep -i "infoflow\|error"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**改了配置没生效?**
|
|
112
|
+
```bash
|
|
113
|
+
./scripts/deploy.sh # 重新同步并重启
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**群 ID 怎么查?**
|
|
117
|
+
```bash
|
|
118
|
+
openclaw directory groups infoflow
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
<a id="配置"></a>
|
|
124
|
+
|
|
125
|
+
## 配置
|
|
126
|
+
|
|
127
|
+
### 最小配置
|
|
128
|
+
|
|
129
|
+
在 OpenClaw 配置文件中添加:
|
|
130
|
+
|
|
131
|
+
```json5
|
|
132
|
+
{
|
|
133
|
+
channels: {
|
|
134
|
+
infoflow: {
|
|
135
|
+
apiHost: "https://apiin.im.baidu.com",
|
|
136
|
+
checkToken: "your-check-token",
|
|
137
|
+
encodingAESKey: "your-encoding-aes-key",
|
|
138
|
+
appKey: "your-app-key",
|
|
139
|
+
appSecret: "your-app-secret",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 全量配置
|
|
146
|
+
|
|
147
|
+
```json5
|
|
148
|
+
{
|
|
149
|
+
channels: {
|
|
150
|
+
infoflow: {
|
|
151
|
+
// ── 基础连接 ──────────────────────────────────────────
|
|
152
|
+
enabled: true,
|
|
153
|
+
apiHost: "https://apiin.im.baidu.com", // 如流 API 地址(必填)
|
|
154
|
+
connectionMode: "websocket", // "webhook"(默认)或 "websocket"
|
|
155
|
+
wsGateway: "infoflow-open-gateway.weiyun.baidu.com", // WebSocket 网关(websocket 模式专用)
|
|
156
|
+
checkToken: "your-check-token", // 消息验签 Token(必填)
|
|
157
|
+
encodingAESKey: "your-encoding-aes-key", // 消息 AES 加密密钥(必填)
|
|
158
|
+
appKey: "your-app-key", // 应用 Key(必填)
|
|
159
|
+
appSecret: "your-app-secret", // 应用 Secret(必填)
|
|
160
|
+
appAgentId: 12345, // 企业后台应用ID,私聊撤回需要
|
|
161
|
+
robotName: "MyBot", // 机器人显示名,用于群内 @ 检测
|
|
162
|
+
|
|
163
|
+
// ── 访问控制 ──────────────────────────────────────────
|
|
164
|
+
dmPolicy: "allowlist", // "open" / "pairing" / "allowlist"
|
|
165
|
+
allowFrom: ["alice", "bob"], // 私聊白名单(uuapName),dmPolicy="allowlist" 时生效
|
|
166
|
+
groupPolicy: "allowlist", // "open" / "allowlist" / "disabled"
|
|
167
|
+
groupAllowFrom: ["12345678"], // 群聊白名单(群ID字符串),groupPolicy="allowlist" 时生效
|
|
168
|
+
|
|
169
|
+
// ── 群聊回复行为 ───────────────────────────────────────
|
|
170
|
+
replyMode: "mention-and-watch", // "ignore" / "record" / "mention-only" / "mention-and-watch" / "proactive"
|
|
171
|
+
requireMention: false, // 是否强制要求 @ 机器人才触发
|
|
172
|
+
watchMentions: ["alice"], // 监控指定人被 @,机器人代为判断是否回答
|
|
173
|
+
watchRegex: "故障|告警", // 群消息命中正则时触发
|
|
174
|
+
followUp: true, // 机器人回复后时间窗口内免 @ 追问
|
|
175
|
+
followUpWindow: 300, // 跟进时间窗口(秒)
|
|
176
|
+
|
|
177
|
+
// ── 消息格式 ──────────────────────────────────────────
|
|
178
|
+
dmMessageFormat: "markdown", // 私聊格式:"text"(默认)或 "markdown"
|
|
179
|
+
groupMessageFormat: "text", // 群聊格式:"text"(默认)或 "markdown"(不支持引用回复)
|
|
180
|
+
processingHint: true, // 响应慢时发送"⏳ 处理中..."提示
|
|
181
|
+
processingHintDelay: 5, // 发送提示前等待秒数(0 = 立即发送)
|
|
182
|
+
|
|
183
|
+
// ── 多账号 ────────────────────────────────────────────
|
|
184
|
+
defaultAccount: "main",
|
|
185
|
+
accounts: {
|
|
186
|
+
main: { appKey: "...", appSecret: "..." }, // 字段与顶层相同,会合并覆盖
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
// ── 群级别覆盖 ────────────────────────────────────────
|
|
190
|
+
groups: {
|
|
191
|
+
"12345678": {
|
|
192
|
+
replyMode: "proactive",
|
|
193
|
+
systemPrompt: "你是该群的专属助手,回答要简洁。",
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 连接方式
|
|
202
|
+
|
|
203
|
+
插件支持两种连接方式,通过 `connectionMode` 配置切换:
|
|
204
|
+
|
|
205
|
+
| 模式 | 说明 | 适用场景 |
|
|
206
|
+
|------|------|---------|
|
|
207
|
+
| `webhook`(默认) | 如流服务器主动推送 HTTP 请求到你的服务 | 有公网域名或内网穿透 |
|
|
208
|
+
| `websocket` | 插件主动连接如流 WebSocket 长连接 | 无公网域名,本地开发调试 |
|
|
209
|
+
|
|
210
|
+
**Webhook 模式**:在如流企业后台配置 Webhook URL:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
https://your-domain/webhook/infoflow
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**WebSocket 模式**:无需公网域名,适合本地开发:
|
|
217
|
+
|
|
218
|
+
```json5
|
|
219
|
+
{
|
|
220
|
+
channels: {
|
|
221
|
+
infoflow: {
|
|
222
|
+
connectionMode: "websocket",
|
|
223
|
+
// wsGateway: "infoflow-open-gateway.weiyun.baidu.com", // 可选,默认值
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
> WebSocket 模式下 `checkToken` 和 `encodingAESKey` 不用于消息解密(连接本身已认证),但仍建议配置以便随时切回 Webhook。
|
|
230
|
+
|
|
231
|
+
### 配置参考
|
|
232
|
+
|
|
233
|
+
#### 基础连接
|
|
234
|
+
|
|
235
|
+
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|
|
236
|
+
|------|------|:----:|--------|------|
|
|
237
|
+
| `apiHost` | `string` | ✅ | — | 如流 API 地址,例如 `https://apiin.im.baidu.com` |
|
|
238
|
+
| `checkToken` | `string` | ✅ | — | 消息验签 Token,从如流企业后台获取 |
|
|
239
|
+
| `encodingAESKey` | `string` | ✅ | — | 消息 AES 加密密钥,从如流企业后台获取 |
|
|
240
|
+
| `appKey` | `string` | ✅ | — | 应用 Key,从如流企业后台获取 |
|
|
241
|
+
| `appSecret` | `string` | ✅ | — | 应用 Secret,从如流企业后台获取 |
|
|
242
|
+
| `appAgentId` | `number` | — | — | 企业后台"应用ID"(数字),私聊消息撤回需要此字段 |
|
|
243
|
+
| `robotName` | `string` | — | — | 机器人在群里的显示名称,用于检测消息中的 @ 提及 |
|
|
244
|
+
| `connectionMode` | `string` | — | `"webhook"` | 消息接收方式:`"webhook"` 如流主动推送;`"websocket"` 插件主动长连接 |
|
|
245
|
+
| `wsGateway` | `string` | — | `"infoflow-open-gateway.weiyun.baidu.com"` | WebSocket 网关域名,仅 `websocket` 模式使用 |
|
|
246
|
+
| `enabled` | `boolean` | — | `true` | 是否启用该插件 |
|
|
247
|
+
|
|
248
|
+
#### 访问控制
|
|
249
|
+
|
|
250
|
+
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|
|
251
|
+
|------|------|:----:|--------|------|
|
|
252
|
+
| `dmPolicy` | `string` | — | `"open"` | 私聊策略:`"open"` 任何人可触发;`"pairing"` 需配对授权;`"allowlist"` 仅白名单用户 |
|
|
253
|
+
| `allowFrom` | `string[]` | — | `[]` | 私聊白名单(uuapName / 邮箱前缀),`dmPolicy="allowlist"` 时生效 |
|
|
254
|
+
| `groupPolicy` | `string` | — | `"open"` | 群聊策略:`"open"` 任何群可触发;`"allowlist"` 仅白名单群;`"disabled"` 完全禁用群聊 |
|
|
255
|
+
| `groupAllowFrom` | `string[]` | — | `[]` | 群聊白名单(群 ID 数字字符串),`groupPolicy="allowlist"` 时生效 |
|
|
256
|
+
| `requireMention` | `boolean` | — | `false` | 群聊是否强制要求 @ 机器人才触发(与 `replyMode` 配合使用) |
|
|
257
|
+
|
|
258
|
+
#### 回复行为
|
|
259
|
+
|
|
260
|
+
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|
|
261
|
+
|------|------|:----:|--------|------|
|
|
262
|
+
| `replyMode` | `string` | — | `"mention-and-watch"` | 群聊回复策略:`ignore`(丢弃)/ `record`(仅记录)/ `mention-only`(仅被@时)/ `mention-and-watch`(被@或关注人被@时)/ `proactive`(主动参与) |
|
|
263
|
+
| `followUp` | `boolean` | — | `true` | 机器人回复后,时间窗口内自动识别追问,无需再次 @ |
|
|
264
|
+
| `followUpWindow` | `number` | — | `300` | 跟进时间窗口(秒) |
|
|
265
|
+
| `watchMentions` | `string[]` | — | `[]` | 监控指定人员被 @,机器人作为其助手代为判断是否回答,填写 uuapName |
|
|
266
|
+
| `watchRegex` | `string` | — | — | JavaScript 正则表达式,群消息内容命中时触发回复 |
|
|
267
|
+
|
|
268
|
+
#### 消息格式
|
|
269
|
+
|
|
270
|
+
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|
|
271
|
+
|------|------|:----:|--------|------|
|
|
272
|
+
| `dmMessageFormat` | `string` | — | `"text"` | 私聊发出消息的格式:`"text"` 纯文本;`"markdown"` 富文本,支持标题/加粗/列表 |
|
|
273
|
+
| `groupMessageFormat` | `string` | — | `"text"` | 群聊发出消息的格式:`"text"` 纯文本;`"markdown"` 富文本。**注意:`markdown` 格式不支持引用回复** |
|
|
274
|
+
| `processingHint` | `boolean` | — | `true` | LLM 响应较慢时,提前发送"⏳ 处理中..."提示;出错时发"处理出错,请稍后重试" |
|
|
275
|
+
| `processingHintDelay` | `number` | — | `5` | 发送"⏳ 处理中..."前等待的秒数;LLM 在此时间内响应则不发;设为 `0` 立即发送 |
|
|
276
|
+
|
|
277
|
+
#### 多账号与分组
|
|
278
|
+
|
|
279
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
280
|
+
|------|------|:----:|------|
|
|
281
|
+
| `accounts` | `object` | — | 多账号配置,key 为自定义账号 ID,value 字段与顶层配置相同 |
|
|
282
|
+
| `defaultAccount` | `string` | — | 多账号模式下的默认账号 ID |
|
|
283
|
+
| `groups` | `object` | — | 按群覆盖配置,key 为群 ID(数字字符串),字段见下方 |
|
|
284
|
+
|
|
285
|
+
#### 群级别配置 (`groups.<groupId>`)
|
|
286
|
+
|
|
287
|
+
每个群可独立覆盖全局配置,未设置的字段继承顶层配置。
|
|
288
|
+
|
|
289
|
+
| 字段 | 类型 | 说明 |
|
|
290
|
+
|------|------|------|
|
|
291
|
+
| `replyMode` | `string` | 覆盖该群的回复策略 |
|
|
292
|
+
| `watchMentions` | `string[]` | 覆盖该群监控的人员列表 |
|
|
293
|
+
| `watchRegex` | `string` | 覆盖该群的正则匹配规则 |
|
|
294
|
+
| `followUp` | `boolean` | 覆盖该群的跟进开关 |
|
|
295
|
+
| `followUpWindow` | `number` | 覆盖该群的跟进时间窗口(秒) |
|
|
296
|
+
| `systemPrompt` | `string` | 该群专属系统提示词,覆盖全局 Agent 的 system prompt |
|
|
297
|
+
|
|
298
|
+
**配置优先级**:群级别 > 账号级别 > 顶层默认值
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
<a id="插件架构"></a>
|
|
303
|
+
|
|
304
|
+
## 插件架构
|
|
305
|
+
|
|
306
|
+
### 核心模块
|
|
307
|
+
|
|
308
|
+
| 模块 | 文件 | 功能 |
|
|
309
|
+
|------|------|------|
|
|
310
|
+
| **插件入口** | `index.ts` | 注册 Channel、Webhook 路由、Tools、Hooks |
|
|
311
|
+
| **Channel 定义** | `src/channel.ts` | 插件结构、生命周期、Actions |
|
|
312
|
+
| **消息接收(Webhook)** | `src/inbound/webhook-parser.ts` | Webhook 解析、解密、去重 |
|
|
313
|
+
| **消息接收(WebSocket)** | `src/inbound/ws-receiver.ts` | WebSocket 长连接 |
|
|
314
|
+
| **接入管理** | `src/inbound/monitor.ts` | Webhook/WebSocket 启动管理 |
|
|
315
|
+
| **消息处理** | `src/inbound/message-handler.ts` | replyMode 决策、历史注入、LLM 调用 |
|
|
316
|
+
| **消息发送** | `src/outbound/send.ts` | 私聊/群聊发送 API |
|
|
317
|
+
| **回复分发** | `src/outbound/reply-dispatcher.ts` | @mentions 解析、分块、引用回复构造 |
|
|
318
|
+
| **图片处理** | `src/outbound/media.ts` | 图片下载、压缩、Base64(含 SSRF 防护) |
|
|
319
|
+
| **Actions** | `src/outbound/actions.ts` | Channel Actions(send/delete,供 LLM 通过 Channel 调用) |
|
|
320
|
+
| **Agent Tools** | `src/tools/index.ts` | `infoflow_send` / `infoflow_recall`(LLM Function Calling) |
|
|
321
|
+
| **Agent Hooks** | `src/hooks/index.ts` | `before_agent_start` 钩子(如流平台背景知识注入) |
|
|
322
|
+
| **多账号** | `src/config/accounts.ts` | 账号解析、配置合并 |
|
|
323
|
+
| **SDK 适配** | `src/sdk/token-adapter.ts` | Token 管理,封装 SDK TokenManager |
|
|
324
|
+
| **消息存储** | `src/outbound/message-store.ts` | 已发送消息记录(支持撤回) |
|
|
325
|
+
|
|
326
|
+
### 项目结构
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
infoflow-openclaw/
|
|
330
|
+
├── index.ts # 插件入口
|
|
331
|
+
├── openclaw.plugin.json # 插件元数据
|
|
332
|
+
├── skills/ # 随插件分发的 skill 包
|
|
333
|
+
│ └── infoflow-dev/ # 如流开发者指南 skill
|
|
334
|
+
├── src/
|
|
335
|
+
│ ├── channel.ts
|
|
336
|
+
│ ├── logging.ts
|
|
337
|
+
│ ├── runtime.ts
|
|
338
|
+
│ ├── types.ts
|
|
339
|
+
│ ├── config/
|
|
340
|
+
│ │ └── accounts.ts
|
|
341
|
+
│ ├── inbound/
|
|
342
|
+
│ │ ├── monitor.ts
|
|
343
|
+
│ │ ├── webhook-parser.ts
|
|
344
|
+
│ │ ├── ws-receiver.ts
|
|
345
|
+
│ │ └── message-handler.ts
|
|
346
|
+
│ ├── outbound/
|
|
347
|
+
│ │ ├── send.ts
|
|
348
|
+
│ │ ├── reply-dispatcher.ts
|
|
349
|
+
│ │ ├── media.ts
|
|
350
|
+
│ │ ├── actions.ts
|
|
351
|
+
│ │ ├── message-store.ts
|
|
352
|
+
│ │ └── target-resolver.ts
|
|
353
|
+
│ ├── tools/
|
|
354
|
+
│ │ └── index.ts
|
|
355
|
+
│ ├── hooks/
|
|
356
|
+
│ │ └── index.ts
|
|
357
|
+
│ └── sdk/
|
|
358
|
+
│ └── token-adapter.ts
|
|
359
|
+
└── README.md
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
<a id="开发指南"></a>
|
|
365
|
+
|
|
366
|
+
## 开发指南
|
|
367
|
+
|
|
368
|
+
### 前置要求
|
|
369
|
+
|
|
370
|
+
- Node.js >= 18
|
|
371
|
+
- OpenClaw >= 2026.3.2
|
|
372
|
+
|
|
373
|
+
### 本地开发
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
# 修改代码后同步并重启
|
|
377
|
+
rsync -av --delete ./ ~/.openclaw/extensions/infoflow/ \
|
|
378
|
+
--exclude node_modules --exclude dist --exclude .git
|
|
379
|
+
openclaw gateway restart
|
|
380
|
+
|
|
381
|
+
# 查看日志
|
|
382
|
+
tail -f ~/.openclaw/logs/gateway.log | grep -i infoflow
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 常用命令
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
# 查看最近日志(含 DEBUG)
|
|
389
|
+
tail -200 ~/.openclaw/logs/gateway.log | grep DEBUG
|
|
390
|
+
|
|
391
|
+
# 检查 Webhook 是否收到消息
|
|
392
|
+
tail -200 ~/.openclaw/logs/gateway.log | grep "webhook"
|
|
393
|
+
|
|
394
|
+
# 检查消息是否发送成功
|
|
395
|
+
tail -200 ~/.openclaw/logs/gateway.log | grep "sendInfoflowMessage"
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
<a id="扩展-agent-tools"></a>
|
|
401
|
+
|
|
402
|
+
## 扩展:Agent Tools
|
|
403
|
+
|
|
404
|
+
**Tools** 是 LLM Function Calling 的实现,Agent 可主动调用来执行操作。
|
|
405
|
+
|
|
406
|
+
### 内置 Tools
|
|
407
|
+
|
|
408
|
+
#### `infoflow_send` — 主动发消息
|
|
409
|
+
|
|
410
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
411
|
+
|------|------|------|------|
|
|
412
|
+
| `to` | string | ✅ | `"username"` 私聊,`"group:GROUP_ID"` 群聊 |
|
|
413
|
+
| `message` | string | ✅ | 消息正文,支持 Markdown |
|
|
414
|
+
| `atAll` | boolean | — | 群消息中 @全体成员 |
|
|
415
|
+
| `mentionUserIds` | string | — | 逗号分隔的 uuapName(群消息 @指定成员) |
|
|
416
|
+
| `accountId` | string | — | 多账号时指定账号 ID |
|
|
417
|
+
|
|
418
|
+
#### `infoflow_recall` — 撤回消息
|
|
419
|
+
|
|
420
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
421
|
+
|------|------|------|------|
|
|
422
|
+
| `to` | string | ✅ | 格式同 `infoflow_send` |
|
|
423
|
+
| `count` | number | — | 撤回最近 N 条(默认 1) |
|
|
424
|
+
| `messageId` | string | — | 撤回指定消息 ID(优先于 count) |
|
|
425
|
+
| `accountId` | string | — | 多账号时指定账号 ID |
|
|
426
|
+
|
|
427
|
+
### 添加自定义 Tool
|
|
428
|
+
|
|
429
|
+
在 `src/tools/` 下新建文件,在 `src/tools/index.ts` 的 `registerInfoflowTools` 中注册:
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
import { Type, type Static } from "@sinclair/typebox";
|
|
433
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
434
|
+
|
|
435
|
+
const MyToolSchema = Type.Object({
|
|
436
|
+
query: Type.String({ description: "查询关键词" }),
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
export function registerMyTool(api: OpenClawPluginApi) {
|
|
440
|
+
api.registerTool({
|
|
441
|
+
name: "infoflow_my_tool",
|
|
442
|
+
description: "工具说明...",
|
|
443
|
+
parameters: MyToolSchema,
|
|
444
|
+
async execute(_toolCallId, params) {
|
|
445
|
+
const p = params as Static<typeof MyToolSchema>;
|
|
446
|
+
const result = { data: "..." };
|
|
447
|
+
return {
|
|
448
|
+
content: [{ type: "text" as const, text: JSON.stringify(result) }],
|
|
449
|
+
details: result,
|
|
450
|
+
};
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
<a id="扩展-agent-hooks"></a>
|
|
459
|
+
|
|
460
|
+
## 扩展:Agent Hooks
|
|
461
|
+
|
|
462
|
+
**Hooks** 通过 `api.on()` 注册生命周期钩子,在 Agent 启动时向 system prompt 注入背景知识。
|
|
463
|
+
|
|
464
|
+
### 内置 Hook
|
|
465
|
+
|
|
466
|
+
#### `infoflow-intro` — 如流平台背景知识
|
|
467
|
+
|
|
468
|
+
每次 Agent 启动时自动注入,告知 LLM 如流平台的会话类型、消息格式、Bot 能力和访问控制。注入内容被缓存到 system prompt 尾部(prompt caching),不产生额外每轮 token 消耗。
|
|
469
|
+
|
|
470
|
+
### 添加自定义 Hook
|
|
471
|
+
|
|
472
|
+
在 `src/hooks/index.ts` 的 `registerInfoflowHooks` 中添加:
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
api.on("before_agent_start", (_event, ctx) => {
|
|
476
|
+
return {
|
|
477
|
+
appendSystemContext: `
|
|
478
|
+
## 业务规则
|
|
479
|
+
- 工单 SLA:P0 30 分钟,P1 2 小时
|
|
480
|
+
`.trim(),
|
|
481
|
+
};
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**Hook 返回值字段:**
|
|
486
|
+
|
|
487
|
+
| 字段 | 说明 |
|
|
488
|
+
|------|------|
|
|
489
|
+
| `appendSystemContext` | 追加到 system prompt 尾部(推荐,利用 prompt caching) |
|
|
490
|
+
| `prependSystemContext` | 插入到 system prompt 头部 |
|
|
491
|
+
| `systemPrompt` | 完全覆盖 system prompt(慎用) |
|
|
492
|
+
| `modelOverride` | 覆盖模型 |
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
<a id="扩展-agent-skills"></a>
|
|
497
|
+
|
|
498
|
+
## 扩展:Agent Skills
|
|
499
|
+
|
|
500
|
+
**Skills** 是随插件分发的知识包,安装插件时自动安装到用户的 OpenClaw 实例,为 Agent 提供领域专属知识。
|
|
501
|
+
|
|
502
|
+
### 内置 Skill
|
|
503
|
+
|
|
504
|
+
#### `infoflow-dev` — 如流开发者指南
|
|
505
|
+
|
|
506
|
+
包含如流消息 API 参考、鉴权机制、插件配置说明等开发文档。当用户在 Agent 会话中询问如流相关开发问题时自动触发。
|
|
507
|
+
|
|
508
|
+
Skill 文件位于 `skills/infoflow-dev/`,随 `openclaw plugins install` 一并安装。
|
|
509
|
+
|
|
510
|
+
### 添加自定义 Skill
|
|
511
|
+
|
|
512
|
+
在 `skills/` 下新建目录,包含 `SKILL.md`(必须)和可选的 `scripts/`、`references/`、`assets/` 目录:
|
|
513
|
+
|
|
514
|
+
```
|
|
515
|
+
skills/
|
|
516
|
+
└── my-skill/
|
|
517
|
+
├── SKILL.md # Skill 元数据(frontmatter)+ 使用说明
|
|
518
|
+
└── references/
|
|
519
|
+
└── guide.md # 按需加载的参考文档
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
`SKILL.md` 格式:
|
|
523
|
+
|
|
524
|
+
```markdown
|
|
525
|
+
---
|
|
526
|
+
name: my-skill
|
|
527
|
+
description: Skill 描述,说明何时触发(这是触发机制,写清楚触发场景)
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
# 使用说明
|
|
531
|
+
...
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
在 `openclaw.plugin.json` 的 `skills` 字段声明 Skill 目录路径:
|
|
535
|
+
|
|
536
|
+
```json
|
|
537
|
+
{
|
|
538
|
+
"skills": ["./skills/my-skill"]
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
<a id="faq"></a>
|
|
545
|
+
|
|
546
|
+
## FAQ
|
|
547
|
+
|
|
548
|
+
### 1. 如何获取如流配置参数?
|
|
549
|
+
|
|
550
|
+
在如流企业后台:
|
|
551
|
+
1. 进入"应用管理" → 选择你的机器人应用
|
|
552
|
+
2. 获取 `appKey`、`appSecret`、`appAgentId`
|
|
553
|
+
3. 配置 Webhook,获取 `checkToken` 和 `encodingAESKey`
|
|
554
|
+
|
|
555
|
+
### 2. 机器人不回复消息?
|
|
556
|
+
|
|
557
|
+
1. 检查 `replyMode` 是否允许该群触发
|
|
558
|
+
2. 检查日志是否收到 Webhook 请求:
|
|
559
|
+
```bash
|
|
560
|
+
tail -200 ~/.openclaw/logs/gateway.log | grep "webhook"
|
|
561
|
+
```
|
|
562
|
+
3. 检查 `robotName` 是否与如流后台一致(影响 @ 检测)
|
|
563
|
+
4. 检查 `groupPolicy` 是否允许该群
|
|
564
|
+
|
|
565
|
+
### 3. 消息撤回失败?
|
|
566
|
+
|
|
567
|
+
- **时间限制**:如流 API 通常限制 2 分钟内可撤回
|
|
568
|
+
- **缺少参数**:群聊需要 `messageid` + `msgseqid`;私聊需要 `msgkey` + `appAgentId`
|
|
569
|
+
- **未记录**:消息未被记录到 `sent-message-store`(查看 `~/.openclaw/plugins/infoflow/sent-messages/default.json`)
|
|
570
|
+
|
|
571
|
+
### 4. WebSocket 模式和 Webhook 模式有什么区别?
|
|
572
|
+
|
|
573
|
+
| | Webhook | WebSocket |
|
|
574
|
+
|--|---------|-----------|
|
|
575
|
+
| 需要公网域名 | ✅ | ❌ |
|
|
576
|
+
| 消息解密(checkToken/AES) | ✅ 需要 | ❌ 不需要 |
|
|
577
|
+
| 适合场景 | 生产环境 | 本地开发调试 |
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## License
|
|
582
|
+
|
|
583
|
+
MIT
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
<a id="english"></a>
|
|
588
|
+
|
|
589
|
+
# Infoflow OpenClaw Plugin (English)
|
|
590
|
+
|
|
591
|
+
Baidu Infoflow enterprise messaging platform — OpenClaw channel plugin. Supports both Webhook and WebSocket connection modes, with image message handling and quote reply support.
|
|
592
|
+
|
|
593
|
+
## Table of Contents
|
|
594
|
+
|
|
595
|
+
- [Quick Start](#quick-start)
|
|
596
|
+
- [Configuration](#configuration)
|
|
597
|
+
- [Architecture](#architecture)
|
|
598
|
+
- [Development Guide](#development-guide)
|
|
599
|
+
- [Extending: Agent Tools](#extending-agent-tools)
|
|
600
|
+
- [Extending: Agent Hooks](#extending-agent-hooks)
|
|
601
|
+
- [Extending: Agent Skills](#extending-agent-skills)
|
|
602
|
+
- [FAQ (EN)](#faq-en)
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
<a id="quick-start"></a>
|
|
607
|
+
|
|
608
|
+
## Quick Start
|
|
609
|
+
|
|
610
|
+
> 5 steps from zero to a running bot.
|
|
611
|
+
|
|
612
|
+
### Prerequisites: Create an app in Infoflow enterprise console
|
|
613
|
+
|
|
614
|
+
1. Infoflow enterprise console → App Management → Create bot app
|
|
615
|
+
2. Note the following 4 parameters:
|
|
616
|
+
|
|
617
|
+
| Parameter | Where to find |
|
|
618
|
+
|-----------|---------------|
|
|
619
|
+
| `appKey` | App basic info |
|
|
620
|
+
| `appSecret` | App basic info |
|
|
621
|
+
| `checkToken` | Message push → Config |
|
|
622
|
+
| `encodingAESKey` | Message push → Config |
|
|
623
|
+
|
|
624
|
+
3. Choose a connection mode:
|
|
625
|
+
- **WebSocket** (recommended, no public domain needed): no extra setup
|
|
626
|
+
- **Webhook**: set `https://your-domain/webhook/infoflow` in "Message push"
|
|
627
|
+
|
|
628
|
+
### Step 1: Install OpenClaw
|
|
629
|
+
|
|
630
|
+
```bash
|
|
631
|
+
# macOS / Linux
|
|
632
|
+
curl -fsSL https://openclaw.dev/install.sh | bash
|
|
633
|
+
|
|
634
|
+
openclaw configure
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Step 2: Clone the plugin
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
git clone ssh://icode.baidu.com:8235/baidu/hi/openclaw_infoflow
|
|
641
|
+
cd openclaw_infoflow
|
|
642
|
+
npm install
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### Step 3: Write config
|
|
646
|
+
|
|
647
|
+
Open `~/.openclaw/openclaw.json` and add the infoflow channel config:
|
|
648
|
+
|
|
649
|
+
```json5
|
|
650
|
+
{
|
|
651
|
+
"channels": {
|
|
652
|
+
"infoflow": {
|
|
653
|
+
"connectionMode": "websocket",
|
|
654
|
+
"wsGateway": "infoflow-open-gateway.weiyun.baidu.com",
|
|
655
|
+
"apiHost": "https://apiin.im.baidu.com",
|
|
656
|
+
"appKey": "your-app-key",
|
|
657
|
+
"appSecret": "your-app-secret",
|
|
658
|
+
"checkToken": "your-check-token",
|
|
659
|
+
"encodingAESKey": "your-encoding-aes-key"
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
For more options (access control, group policies, message format, etc.) see [Configuration Reference](#configuration).
|
|
666
|
+
|
|
667
|
+
### Step 4: Deploy
|
|
668
|
+
|
|
669
|
+
```bash
|
|
670
|
+
./scripts/deploy.sh
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### Step 5: Verify
|
|
674
|
+
|
|
675
|
+
- **DM**: Send the bot a direct message — it should reply
|
|
676
|
+
- **Group**: @mention the bot in a whitelisted group — it should reply
|
|
677
|
+
- **Logs**: `openclaw logs | grep infoflow`
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
<a id="configuration"></a>
|
|
682
|
+
|
|
683
|
+
## Configuration
|
|
684
|
+
|
|
685
|
+
### Minimal Configuration
|
|
686
|
+
|
|
687
|
+
```json5
|
|
688
|
+
{
|
|
689
|
+
channels: {
|
|
690
|
+
infoflow: {
|
|
691
|
+
apiHost: "https://apiin.im.baidu.com",
|
|
692
|
+
checkToken: "your-check-token",
|
|
693
|
+
encodingAESKey: "your-encoding-aes-key",
|
|
694
|
+
appKey: "your-app-key",
|
|
695
|
+
appSecret: "your-app-secret",
|
|
696
|
+
},
|
|
697
|
+
},
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
### Full Configuration
|
|
702
|
+
|
|
703
|
+
```json5
|
|
704
|
+
{
|
|
705
|
+
channels: {
|
|
706
|
+
infoflow: {
|
|
707
|
+
// ── Connection ────────────────────────────────────────
|
|
708
|
+
enabled: true,
|
|
709
|
+
apiHost: "https://apiin.im.baidu.com", // required
|
|
710
|
+
connectionMode: "websocket", // "webhook" (default) or "websocket"
|
|
711
|
+
wsGateway: "infoflow-open-gateway.weiyun.baidu.com", // websocket mode only
|
|
712
|
+
checkToken: "your-check-token", // required
|
|
713
|
+
encodingAESKey: "your-encoding-aes-key", // required
|
|
714
|
+
appKey: "your-app-key", // required
|
|
715
|
+
appSecret: "your-app-secret", // required
|
|
716
|
+
appAgentId: 12345, // required for DM recall
|
|
717
|
+
robotName: "MyBot", // for @mention detection in groups
|
|
718
|
+
|
|
719
|
+
// ── Access Control ────────────────────────────────────
|
|
720
|
+
dmPolicy: "allowlist", // "open" / "pairing" / "allowlist"
|
|
721
|
+
allowFrom: ["alice", "bob"], // DM whitelist (uuapName), used when dmPolicy="allowlist"
|
|
722
|
+
groupPolicy: "allowlist", // "open" / "allowlist" / "disabled"
|
|
723
|
+
groupAllowFrom: ["12345678"], // Group whitelist (group ID string), used when groupPolicy="allowlist"
|
|
724
|
+
|
|
725
|
+
// ── Reply Behavior ────────────────────────────────────
|
|
726
|
+
replyMode: "mention-and-watch", // "ignore" / "record" / "mention-only" / "mention-and-watch" / "proactive"
|
|
727
|
+
requireMention: false,
|
|
728
|
+
watchMentions: ["alice"], // act as assistant when these users are @mentioned
|
|
729
|
+
watchRegex: "incident|alert", // trigger on regex match in group messages
|
|
730
|
+
followUp: true, // allow follow-up questions without re-mentioning
|
|
731
|
+
followUpWindow: 300, // follow-up window in seconds
|
|
732
|
+
|
|
733
|
+
// ── Message Format ────────────────────────────────────
|
|
734
|
+
dmMessageFormat: "markdown", // "text" (default) or "markdown" for DMs
|
|
735
|
+
groupMessageFormat: "text", // "text" (default) or "markdown" for groups (no quote reply)
|
|
736
|
+
processingHint: true,
|
|
737
|
+
processingHintDelay: 5,
|
|
738
|
+
|
|
739
|
+
// ── Multi-account ─────────────────────────────────────
|
|
740
|
+
defaultAccount: "main",
|
|
741
|
+
accounts: {
|
|
742
|
+
main: { appKey: "...", appSecret: "..." },
|
|
743
|
+
},
|
|
744
|
+
|
|
745
|
+
// ── Per-group Overrides ───────────────────────────────
|
|
746
|
+
groups: {
|
|
747
|
+
"12345678": {
|
|
748
|
+
replyMode: "proactive",
|
|
749
|
+
systemPrompt: "You are the dedicated assistant for this group.",
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
},
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
### Connection Modes
|
|
758
|
+
|
|
759
|
+
| Mode | Description | Use Case |
|
|
760
|
+
|------|-------------|----------|
|
|
761
|
+
| `webhook` (default) | Infoflow server pushes HTTP requests to your service | Public domain or tunnel |
|
|
762
|
+
| `websocket` | Plugin connects to Infoflow WebSocket gateway | No public domain, local dev |
|
|
763
|
+
|
|
764
|
+
**Webhook mode** — configure Webhook URL in Infoflow enterprise console:
|
|
765
|
+
```
|
|
766
|
+
https://your-domain/webhook/infoflow
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**WebSocket mode** — no public domain needed:
|
|
770
|
+
```json5
|
|
771
|
+
{
|
|
772
|
+
channels: {
|
|
773
|
+
infoflow: {
|
|
774
|
+
connectionMode: "websocket",
|
|
775
|
+
// wsGateway: "infoflow-open-gateway.weiyun.baidu.com", // optional
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Configuration Reference
|
|
782
|
+
|
|
783
|
+
#### Connection
|
|
784
|
+
|
|
785
|
+
| Field | Type | Required | Default | Description |
|
|
786
|
+
|-------|------|:--------:|---------|-------------|
|
|
787
|
+
| `apiHost` | `string` | ✅ | — | Infoflow API host, e.g. `https://apiin.im.baidu.com` |
|
|
788
|
+
| `checkToken` | `string` | ✅ | — | Message verification token from Infoflow enterprise console |
|
|
789
|
+
| `encodingAESKey` | `string` | ✅ | — | AES encryption key from Infoflow enterprise console |
|
|
790
|
+
| `appKey` | `string` | ✅ | — | App Key from Infoflow enterprise console |
|
|
791
|
+
| `appSecret` | `string` | ✅ | — | App Secret from Infoflow enterprise console |
|
|
792
|
+
| `appAgentId` | `number` | — | — | App Agent ID (numeric); required for private message recall |
|
|
793
|
+
| `robotName` | `string` | — | — | Bot display name in groups, used for @mention detection |
|
|
794
|
+
| `connectionMode` | `string` | — | `"webhook"` | `"webhook"` (Infoflow pushes to your server) or `"websocket"` (plugin connects to Infoflow) |
|
|
795
|
+
| `wsGateway` | `string` | — | `"infoflow-open-gateway.weiyun.baidu.com"` | WebSocket gateway hostname, only used in `websocket` mode |
|
|
796
|
+
| `enabled` | `boolean` | — | `true` | Whether to enable the plugin |
|
|
797
|
+
|
|
798
|
+
#### Access Control
|
|
799
|
+
|
|
800
|
+
| Field | Type | Required | Default | Description |
|
|
801
|
+
|-------|------|:--------:|---------|-------------|
|
|
802
|
+
| `dmPolicy` | `string` | — | `"open"` | DM policy: `"open"` (anyone); `"pairing"` (requires pairing); `"allowlist"` (whitelist only) |
|
|
803
|
+
| `allowFrom` | `string[]` | — | `[]` | DM whitelist (uuapName / email prefix), effective when `dmPolicy="allowlist"` |
|
|
804
|
+
| `groupPolicy` | `string` | — | `"open"` | Group policy: `"open"` (any group); `"allowlist"` (whitelist only); `"disabled"` (no group messages) |
|
|
805
|
+
| `groupAllowFrom` | `string[]` | — | `[]` | Group whitelist (numeric group ID strings), effective when `groupPolicy="allowlist"` |
|
|
806
|
+
| `requireMention` | `boolean` | — | `false` | Require explicit @ in group messages to trigger |
|
|
807
|
+
|
|
808
|
+
#### Reply Behavior
|
|
809
|
+
|
|
810
|
+
| Field | Type | Required | Default | Description |
|
|
811
|
+
|-------|------|:--------:|---------|-------------|
|
|
812
|
+
| `replyMode` | `string` | — | `"mention-and-watch"` | `ignore` (discard) / `record` (save only) / `mention-only` (when @mentioned) / `mention-and-watch` (default) / `proactive` (always active) |
|
|
813
|
+
| `followUp` | `boolean` | — | `true` | Auto-detect follow-up questions after bot replies (no @ needed) |
|
|
814
|
+
| `followUpWindow` | `number` | — | `300` | Follow-up window in seconds |
|
|
815
|
+
| `watchMentions` | `string[]` | — | `[]` | Watch these users; bot acts as assistant when they are @mentioned |
|
|
816
|
+
| `watchRegex` | `string` | — | — | JavaScript regex; trigger reply when group message content matches |
|
|
817
|
+
|
|
818
|
+
#### Message Format
|
|
819
|
+
|
|
820
|
+
| Field | Type | Required | Default | Description |
|
|
821
|
+
|-------|------|:--------:|---------|-------------|
|
|
822
|
+
| `dmMessageFormat` | `string` | — | `"text"` | Format for private (DM) messages: `"text"` (plain) or `"markdown"` (rich text) |
|
|
823
|
+
| `groupMessageFormat` | `string` | — | `"text"` | Format for group messages: `"text"` (plain) or `"markdown"` (rich text). **Note: `markdown` does not support quote replies** |
|
|
824
|
+
| `processingHint` | `boolean` | — | `true` | Send "⏳ processing..." hint when LLM is slow; send error message on failure |
|
|
825
|
+
| `processingHintDelay` | `number` | — | `5` | Seconds to wait before sending the hint; set to `0` to send immediately |
|
|
826
|
+
|
|
827
|
+
#### Multi-account & Groups
|
|
828
|
+
|
|
829
|
+
| Field | Type | Required | Description |
|
|
830
|
+
|-------|------|:--------:|-------------|
|
|
831
|
+
| `accounts` | `object` | — | Multi-account config; key = account ID, value has same fields as top-level |
|
|
832
|
+
| `defaultAccount` | `string` | — | Default account ID |
|
|
833
|
+
| `groups` | `object` | — | Per-group overrides; key = group ID (numeric string) |
|
|
834
|
+
|
|
835
|
+
Per-group fields: `replyMode`, `watchMentions`, `watchRegex`, `followUp`, `followUpWindow`, `systemPrompt`
|
|
836
|
+
|
|
837
|
+
**Priority**: per-group > account-level > top-level defaults
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
<a id="architecture"></a>
|
|
842
|
+
|
|
843
|
+
## Architecture
|
|
844
|
+
|
|
845
|
+
| Module | File | Description |
|
|
846
|
+
|--------|------|-------------|
|
|
847
|
+
| Entry | `index.ts` | Register channel, webhook route, tools, hooks |
|
|
848
|
+
| Channel | `src/channel.ts` | Plugin structure, lifecycle, actions |
|
|
849
|
+
| Webhook | `src/inbound/webhook-parser.ts` | Parse, decrypt, deduplicate |
|
|
850
|
+
| WebSocket | `src/inbound/ws-receiver.ts` | Long-lived connection |
|
|
851
|
+
| Monitor | `src/inbound/monitor.ts` | Manage Webhook/WebSocket startup |
|
|
852
|
+
| Handler | `src/inbound/message-handler.ts` | replyMode logic, history injection, LLM call |
|
|
853
|
+
| Send | `src/outbound/send.ts` | DM / group message sending |
|
|
854
|
+
| Dispatcher | `src/outbound/reply-dispatcher.ts` | @mentions, chunking, quote reply |
|
|
855
|
+
| Media | `src/outbound/media.ts` | Image download, compress, Base64 (SSRF protection) |
|
|
856
|
+
| Actions | `src/outbound/actions.ts` | Channel actions (send/delete for LLM) |
|
|
857
|
+
| Tools | `src/tools/index.ts` | `infoflow_send` / `infoflow_recall` |
|
|
858
|
+
| Hooks | `src/hooks/index.ts` | `before_agent_start` hook |
|
|
859
|
+
| Accounts | `src/config/accounts.ts` | Multi-account resolution |
|
|
860
|
+
| Token | `src/sdk/token-adapter.ts` | Token management |
|
|
861
|
+
| Store | `src/outbound/message-store.ts` | Sent message store (for recall) |
|
|
862
|
+
|
|
863
|
+
---
|
|
864
|
+
|
|
865
|
+
<a id="development-guide"></a>
|
|
866
|
+
|
|
867
|
+
## Development Guide
|
|
868
|
+
|
|
869
|
+
```bash
|
|
870
|
+
# Sync and restart after code changes
|
|
871
|
+
rsync -av --delete ./ ~/.openclaw/extensions/infoflow/ \
|
|
872
|
+
--exclude node_modules --exclude dist --exclude .git
|
|
873
|
+
openclaw gateway restart
|
|
874
|
+
|
|
875
|
+
# View logs
|
|
876
|
+
tail -f ~/.openclaw/logs/gateway.log | grep -i infoflow
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
<a id="extending-agent-tools"></a>
|
|
882
|
+
|
|
883
|
+
## Extending: Agent Tools
|
|
884
|
+
|
|
885
|
+
**Tools** implement LLM Function Calling — the agent calls them to take actions.
|
|
886
|
+
|
|
887
|
+
#### `infoflow_send` — Send a message
|
|
888
|
+
|
|
889
|
+
| Parameter | Type | Required | Description |
|
|
890
|
+
|-----------|------|----------|-------------|
|
|
891
|
+
| `to` | string | ✅ | `"username"` for DM, `"group:GROUP_ID"` for group |
|
|
892
|
+
| `message` | string | ✅ | Message body (Markdown supported) |
|
|
893
|
+
| `atAll` | boolean | — | @all in group |
|
|
894
|
+
| `mentionUserIds` | string | — | Comma-separated uuapNames to @mention |
|
|
895
|
+
| `accountId` | string | — | Account ID for multi-account setups |
|
|
896
|
+
|
|
897
|
+
#### `infoflow_recall` — Recall messages
|
|
898
|
+
|
|
899
|
+
| Parameter | Type | Required | Description |
|
|
900
|
+
|-----------|------|----------|-------------|
|
|
901
|
+
| `to` | string | ✅ | Same format as `infoflow_send` |
|
|
902
|
+
| `count` | number | — | Recall last N messages (default: 1) |
|
|
903
|
+
| `messageId` | string | — | Recall by message ID (overrides count) |
|
|
904
|
+
| `accountId` | string | — | Account ID for multi-account setups |
|
|
905
|
+
|
|
906
|
+
To add a custom tool, register it inside `registerInfoflowTools` in `src/tools/index.ts`.
|
|
907
|
+
|
|
908
|
+
---
|
|
909
|
+
|
|
910
|
+
<a id="extending-agent-hooks"></a>
|
|
911
|
+
|
|
912
|
+
## Extending: Agent Hooks
|
|
913
|
+
|
|
914
|
+
**Hooks** inject domain knowledge into the agent system prompt via `api.on("before_agent_start", ...)`.
|
|
915
|
+
|
|
916
|
+
The built-in `infoflow-intro` hook injects Infoflow platform context (session types, message formats, bot capabilities, access control) into every agent run. The content is appended to the system prompt and benefits from prompt caching.
|
|
917
|
+
|
|
918
|
+
To add a custom hook, call `api.on(...)` inside `registerInfoflowHooks` in `src/hooks/index.ts`.
|
|
919
|
+
|
|
920
|
+
**Hook return fields:** `appendSystemContext`, `prependSystemContext`, `systemPrompt`, `modelOverride`
|
|
921
|
+
|
|
922
|
+
---
|
|
923
|
+
|
|
924
|
+
<a id="extending-agent-skills"></a>
|
|
925
|
+
|
|
926
|
+
## Extending: Agent Skills
|
|
927
|
+
|
|
928
|
+
**Skills** are knowledge packages bundled with the plugin and auto-installed alongside it. They provide domain-specific context to the Agent.
|
|
929
|
+
|
|
930
|
+
### Built-in Skill
|
|
931
|
+
|
|
932
|
+
#### `infoflow-dev` — Infoflow Developer Guide
|
|
933
|
+
|
|
934
|
+
Contains Infoflow message API reference, authentication, and plugin configuration docs. Automatically triggers when users ask Infoflow-related development questions.
|
|
935
|
+
|
|
936
|
+
Located in `skills/infoflow-dev/`, installed via `openclaw plugins install`.
|
|
937
|
+
|
|
938
|
+
### Adding a Custom Skill
|
|
939
|
+
|
|
940
|
+
Create a directory under `skills/` with a `SKILL.md` and optional resource folders:
|
|
941
|
+
|
|
942
|
+
```
|
|
943
|
+
skills/
|
|
944
|
+
└── my-skill/
|
|
945
|
+
├── SKILL.md
|
|
946
|
+
└── references/
|
|
947
|
+
└── guide.md
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
Declare the skill path in `openclaw.plugin.json`:
|
|
951
|
+
|
|
952
|
+
```json
|
|
953
|
+
{
|
|
954
|
+
"skills": ["./skills/my-skill"]
|
|
955
|
+
}
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
---
|
|
959
|
+
|
|
960
|
+
<a id="faq-en"></a>
|
|
961
|
+
|
|
962
|
+
## FAQ (EN)
|
|
963
|
+
|
|
964
|
+
### Bot not replying?
|
|
965
|
+
|
|
966
|
+
1. Check `replyMode` — confirm the group is allowed to trigger
|
|
967
|
+
2. Check logs for incoming webhook requests
|
|
968
|
+
3. Verify `robotName` matches the bot's display name (affects @ detection)
|
|
969
|
+
4. Check `groupPolicy` allows the group
|
|
970
|
+
|
|
971
|
+
### Message recall failing?
|
|
972
|
+
|
|
973
|
+
- **Time limit**: Infoflow API typically allows recall within 2 minutes
|
|
974
|
+
- **Missing params**: Group recall needs `messageid` + `msgseqid`; DM recall needs `msgkey` + `appAgentId`
|
|
975
|
+
- **Not recorded**: Message may not have been saved to the sent-message store
|
|
976
|
+
|
|
977
|
+
### Webhook vs WebSocket?
|
|
978
|
+
|
|
979
|
+
| | Webhook | WebSocket |
|
|
980
|
+
|--|---------|-----------|
|
|
981
|
+
| Requires public domain | ✅ | ❌ |
|
|
982
|
+
| Message decryption | ✅ needed | ❌ not needed |
|
|
983
|
+
| Best for | Production | Local development |
|
|
984
|
+
|
|
985
|
+
---
|
|
986
|
+
|
|
987
|
+
## License
|
|
988
|
+
|
|
989
|
+
MIT
|