@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 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