@searchfe/openclaw-baiduapp 0.1.0-beta.1

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,212 @@
1
+ # OpenClaw 接入百度App 配置指南
2
+
3
+ 本指南帮助你配置百度App渠道,并接入 OpenClaw。
4
+
5
+ > **⚠️ 重要提示**:Baidu App 插件专注于**文本私聊场景**,不支持群聊和媒体消息(图片/语音/文件)。
6
+
7
+ ## 功能概览
8
+
9
+ | 功能 | 状态 |
10
+ | :------------- | :--: |
11
+ | 文本消息 | ✅ |
12
+ | 主动发送消息 | ✅ |
13
+ | 私聊 | ✅ |
14
+ | 群聊 | ❌ |
15
+ | 图片/语音/文件 | ❌ |
16
+ | Webhook 回调 | ✅ |
17
+ | 多账户 | ✅ |
18
+ | 访问控制策略 | ✅ |
19
+
20
+ ## 前置条件
21
+
22
+ 1. 百度App 开发者账号
23
+ 2. 公网可访问的 HTTPS 服务器(用于接收回调)
24
+ 3. OpenClaw 已安装并运行
25
+
26
+ ---
27
+
28
+ ## 步骤一:安装插件
29
+
30
+ ### 方式一:从 npm 安装
31
+
32
+ ```bash
33
+ openclaw plugins install @searchfe/openclaw-baiduapp
34
+ ```
35
+
36
+ ---
37
+
38
+ ## 步骤二:获取百度平台凭证
39
+
40
+ <!-- TODO: 补充百度平台注册具体步骤和截图 -->
41
+
42
+ 在百度App 开发者平台注册应用后,获取以下信息:
43
+
44
+ - **App Key**:应用标识
45
+ - **App Secret**:应用密钥
46
+ - **Token**:消息校验 Token
47
+ - **EncodingAESKey**:消息加密密钥(43 位字符)
48
+
49
+ ---
50
+
51
+ ## 步骤三:配置 OpenClaw
52
+
53
+ ### 使用命令行配置
54
+
55
+ ```bash
56
+ openclaw config set channels.baidu-app.enabled true
57
+ openclaw config set channels.baidu-app.webhookPath /baidu-app
58
+ openclaw config set channels.baidu-app.token your-token
59
+ openclaw config set channels.baidu-app.encodingAESKey your-43-char-encoding-aes-key
60
+ openclaw config set channels.baidu-app.appKey your-app-key
61
+ openclaw config set channels.baidu-app.appSecret your-app-secret
62
+ ```
63
+
64
+ ### 或直接编辑配置文件
65
+
66
+ 编辑 `~/.openclaw/openclaw.json`:
67
+
68
+ ```json
69
+ {
70
+ "channels": {
71
+ "baidu-app": {
72
+ "enabled": true,
73
+ "webhookPath": "/baidu-app",
74
+ "token": "your-token",
75
+ "encodingAESKey": "your-43-char-encoding-aes-key",
76
+ "appKey": "your-app-key",
77
+ "appSecret": "your-app-secret"
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### 配置说明
84
+
85
+ | 字段 | 必填 | 说明 |
86
+ | :--------------- | :--: | :------------------------------ |
87
+ | `enabled` | ✅ | 是否启用该渠道 |
88
+ | `webhookPath` | ✅ | 回调路径,需与百度平台配置一致 |
89
+ | `token` | ✅ | 消息校验 Token |
90
+ | `encodingAESKey` | ✅ | 消息加密密钥(43 位) |
91
+ | `appKey` | ✅ | 应用 App Key(主动发送必需) |
92
+ | `appSecret` | ✅ | 应用 App Secret(主动发送必需) |
93
+ | `welcomeText` | ❌ | 用户首次进入时的欢迎语 |
94
+ | `dmPolicy` | ❌ | 私聊策略(默认 `pairing`) |
95
+ | `allowFrom` | ❌ | 私聊白名单用户 ID 列表 |
96
+
97
+ ### 环境变量支持
98
+
99
+ 默认账户的凭证也可以通过环境变量设置:
100
+
101
+ | 环境变量 | 对应配置 |
102
+ | :--------------------------- | :--------------- |
103
+ | `BAIDU_APP_TOKEN` | `token` |
104
+ | `BAIDU_APP_ENCODING_AES_KEY` | `encodingAESKey` |
105
+ | `BAIDU_APP_KEY` | `appKey` |
106
+ | `BAIDU_APP_SECRET` | `appSecret` |
107
+
108
+ ---
109
+
110
+ ## 步骤四:重启 Gateway
111
+
112
+ ```bash
113
+ openclaw gateway restart
114
+ ```
115
+
116
+ ---
117
+
118
+ ## 步骤五:验证配置
119
+
120
+ 1. 在百度平台配置 Webhook 回调地址,指向你的 OpenClaw Gateway
121
+ 2. 平台验证通过后,发送一条测试消息
122
+ 3. 查看 OpenClaw 日志确认消息接收和回复
123
+
124
+ ---
125
+
126
+ ## 高级配置
127
+
128
+ ### 访问控制
129
+
130
+ ```json
131
+ {
132
+ "channels": {
133
+ "baidu-app": {
134
+ "enabled": true,
135
+ "dmPolicy": "open",
136
+ "allowFrom": []
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ | 字段 | 说明 |
143
+ | :---------- | :--------------------------------------------------------------------------------------- |
144
+ | `dmPolicy` | 私聊策略:`open`(任何人)/ `pairing`(配对)/ `allowlist`(白名单)/ `disabled`(禁用) |
145
+ | `allowFrom` | 私聊白名单用户 ID 列表(当 `dmPolicy` 为 `allowlist` 时生效) |
146
+
147
+ ### 多账户配置
148
+
149
+ ```json
150
+ {
151
+ "channels": {
152
+ "baidu-app": {
153
+ "enabled": true,
154
+ "accounts": {
155
+ "bot1": {
156
+ "webhookPath": "/baidu-app-1",
157
+ "token": "token-1",
158
+ "encodingAESKey": "key-1",
159
+ "appKey": "app-key-1",
160
+ "appSecret": "secret-1"
161
+ },
162
+ "bot2": {
163
+ "webhookPath": "/baidu-app-2",
164
+ "token": "token-2",
165
+ "encodingAESKey": "key-2",
166
+ "appKey": "app-key-2",
167
+ "appSecret": "secret-2"
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ ```
174
+
175
+ > 提示:多账号共用同一路径/Token 时,系统会使用第一个匹配的账号。建议每个账号使用独立的 `webhookPath`。
176
+
177
+ ---
178
+
179
+ ## 常见问题
180
+
181
+ ### Q: 保存配置时提示验证失败?
182
+
183
+ 1. 检查 OpenClaw 是否已启动并监听正确端口
184
+ 2. 确认 `webhookPath` 与平台 URL 路径一致
185
+ 3. 确认 `token` 和 `encodingAESKey` 与平台配置完全一致
186
+ 4. 确认服务器公网可访问
187
+
188
+ ### Q: 消息接收成功但发送失败?
189
+
190
+ 1. 检查 `appKey`、`appSecret` 是否正确
191
+ 2. 查看 OpenClaw 日志获取详细错误信息
192
+
193
+ ### Q: 如何使用 target 发送消息?
194
+
195
+ ```bash
196
+ # 使用 user: 前缀
197
+ send user:userId123 你好
198
+
199
+ # 使用 baidu-app: 完整前缀
200
+ send baidu-app:user:userId123 你好
201
+
202
+ # 指定账户
203
+ send user:userId123@bot1 你好
204
+ ```
205
+
206
+ ---
207
+
208
+ ## 开发验证
209
+
210
+ ```bash
211
+ pnpm test
212
+ ```
@@ -0,0 +1,434 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+
3
+ type BaiduAppDmPolicy = 'open' | 'pairing' | 'allowlist' | 'disabled';
4
+ interface BaiduAppAccountConfig {
5
+ name?: string;
6
+ enabled?: boolean;
7
+ webhookPath?: string;
8
+ token?: string;
9
+ encodingAESKey?: string;
10
+ receiveId?: string;
11
+ appKey?: string;
12
+ appSecret?: string;
13
+ welcomeText?: string;
14
+ dmPolicy?: BaiduAppDmPolicy;
15
+ allowFrom?: string[];
16
+ }
17
+ type BaiduAppConfig = BaiduAppAccountConfig & {
18
+ accounts?: Record<string, BaiduAppAccountConfig>;
19
+ defaultAccount?: string;
20
+ };
21
+ interface ResolvedBaiduAppAccount {
22
+ accountId: string;
23
+ name?: string;
24
+ enabled: boolean;
25
+ configured: boolean;
26
+ token?: string;
27
+ encodingAESKey?: string;
28
+ receiveId: string;
29
+ appKey?: string;
30
+ appSecret?: string;
31
+ canSendActive: boolean;
32
+ config: BaiduAppAccountConfig;
33
+ }
34
+ interface BaiduAppSendTarget {
35
+ userId: string;
36
+ }
37
+ interface AccessTokenCacheEntry {
38
+ token: string;
39
+ expiresAt: number;
40
+ }
41
+ interface BaiduAppInboundBase {
42
+ MsgId?: string;
43
+ msgid?: string;
44
+ aibotid?: string;
45
+ response_url?: string;
46
+ from?: {
47
+ userid?: string;
48
+ appkey?: string;
49
+ };
50
+ FromUserName?: string;
51
+ ToUserName?: string;
52
+ CreateTime?: number;
53
+ MsgType?: string;
54
+ msgtype?: string;
55
+ BotID?: number;
56
+ }
57
+ type BaiduAppInboundText = BaiduAppInboundBase & {
58
+ msgtype: 'text';
59
+ MsgType?: 'text';
60
+ text?: {
61
+ content?: string;
62
+ };
63
+ Content?: string;
64
+ quote?: unknown;
65
+ };
66
+ type BaiduAppInboundEvent = BaiduAppInboundBase & {
67
+ msgtype: 'event';
68
+ MsgType?: 'event';
69
+ create_time?: number;
70
+ Event?: string;
71
+ EventKey?: string;
72
+ event?: {
73
+ eventtype?: string;
74
+ [key: string]: unknown;
75
+ };
76
+ };
77
+ type BaiduAppInboundMessage = BaiduAppInboundText | BaiduAppInboundEvent | (BaiduAppInboundBase & Record<string, unknown>);
78
+
79
+ declare const DEFAULT_ACCOUNT_ID = "default";
80
+ interface PluginConfig {
81
+ session?: {
82
+ store?: unknown;
83
+ };
84
+ channels?: {
85
+ 'baidu-app'?: BaiduAppConfig;
86
+ };
87
+ }
88
+
89
+ declare const baiduAppPlugin: {
90
+ id: string;
91
+ meta: {
92
+ id: "baidu-app";
93
+ label: "Baidu App";
94
+ selectionLabel: "Baidu Search AI (百度搜索对话式AI)";
95
+ docsPath: "/channels/baidu-app";
96
+ docsLabel: "baidu-app";
97
+ blurb: "百度搜索对话式AI,支持主动发送消息";
98
+ aliases: readonly ["baidu-ai", "百度搜索AI"];
99
+ order: 85;
100
+ };
101
+ capabilities: {
102
+ chatTypes: readonly ["direct"];
103
+ media: boolean;
104
+ reactions: boolean;
105
+ threads: boolean;
106
+ edit: boolean;
107
+ reply: boolean;
108
+ polls: boolean;
109
+ activeSend: boolean;
110
+ };
111
+ configSchema: {
112
+ schema: {
113
+ type: string;
114
+ additionalProperties: boolean;
115
+ properties: {
116
+ name: {
117
+ type: string;
118
+ };
119
+ enabled: {
120
+ type: string;
121
+ };
122
+ webhookPath: {
123
+ type: string;
124
+ };
125
+ token: {
126
+ type: string;
127
+ };
128
+ encodingAESKey: {
129
+ type: string;
130
+ };
131
+ receiveId: {
132
+ type: string;
133
+ };
134
+ appKey: {
135
+ type: string;
136
+ };
137
+ appSecret: {
138
+ type: string;
139
+ };
140
+ welcomeText: {
141
+ type: string;
142
+ };
143
+ dmPolicy: {
144
+ type: string;
145
+ enum: string[];
146
+ };
147
+ allowFrom: {
148
+ type: string;
149
+ items: {
150
+ type: string;
151
+ };
152
+ };
153
+ defaultAccount: {
154
+ type: string;
155
+ };
156
+ accounts: {
157
+ type: string;
158
+ additionalProperties: {
159
+ type: string;
160
+ additionalProperties: boolean;
161
+ properties: {
162
+ name: {
163
+ type: string;
164
+ };
165
+ enabled: {
166
+ type: string;
167
+ };
168
+ webhookPath: {
169
+ type: string;
170
+ };
171
+ token: {
172
+ type: string;
173
+ };
174
+ encodingAESKey: {
175
+ type: string;
176
+ };
177
+ receiveId: {
178
+ type: string;
179
+ };
180
+ appKey: {
181
+ type: string;
182
+ };
183
+ appSecret: {
184
+ type: string;
185
+ };
186
+ welcomeText: {
187
+ type: string;
188
+ };
189
+ dmPolicy: {
190
+ type: string;
191
+ enum: string[];
192
+ };
193
+ allowFrom: {
194
+ type: string;
195
+ items: {
196
+ type: string;
197
+ };
198
+ };
199
+ };
200
+ };
201
+ };
202
+ };
203
+ };
204
+ };
205
+ reload: {
206
+ configPrefixes: string[];
207
+ };
208
+ config: {
209
+ listAccountIds: (cfg: PluginConfig) => string[];
210
+ resolveAccount: (cfg: PluginConfig, accountId?: string) => ResolvedBaiduAppAccount;
211
+ defaultAccountId: (cfg: PluginConfig) => string;
212
+ setAccountEnabled: (params: {
213
+ cfg: PluginConfig;
214
+ accountId?: string;
215
+ enabled: boolean;
216
+ }) => PluginConfig;
217
+ deleteAccount: (params: {
218
+ cfg: PluginConfig;
219
+ accountId?: string;
220
+ }) => PluginConfig;
221
+ isConfigured: (account: ResolvedBaiduAppAccount) => boolean;
222
+ describeAccount: (account: ResolvedBaiduAppAccount) => {
223
+ accountId: string;
224
+ name: string | undefined;
225
+ enabled: boolean;
226
+ configured: boolean;
227
+ canSendActive: boolean;
228
+ webhookPath: string;
229
+ };
230
+ resolveAllowFrom: (params: {
231
+ cfg: PluginConfig;
232
+ accountId?: string;
233
+ }) => string[];
234
+ formatAllowFrom: (params: {
235
+ allowFrom: Array<string | number>;
236
+ }) => string[];
237
+ };
238
+ directory: {
239
+ canResolve: (params: {
240
+ target: string;
241
+ }) => boolean;
242
+ resolveTarget: (params: {
243
+ cfg: PluginConfig;
244
+ target: string;
245
+ }) => {
246
+ channel: string;
247
+ accountId?: string;
248
+ to: string;
249
+ } | null;
250
+ resolveTargets: (params: {
251
+ cfg: PluginConfig;
252
+ targets: string[];
253
+ }) => Array<{
254
+ channel: string;
255
+ accountId?: string;
256
+ to: string;
257
+ }>;
258
+ getTargetFormats: () => string[];
259
+ };
260
+ outbound: {
261
+ deliveryMode: string;
262
+ sendText: (params: {
263
+ cfg: PluginConfig;
264
+ accountId?: string;
265
+ to: string;
266
+ text: string;
267
+ options?: {
268
+ markdown?: boolean;
269
+ };
270
+ }) => Promise<{
271
+ channel: string;
272
+ ok: boolean;
273
+ messageId: string;
274
+ error?: Error;
275
+ }>;
276
+ };
277
+ gateway: {
278
+ startAccount: (ctx: {
279
+ cfg: PluginConfig;
280
+ runtime?: unknown;
281
+ abortSignal?: AbortSignal;
282
+ accountId: string;
283
+ setStatus?: (status: Record<string, unknown>) => void;
284
+ log?: {
285
+ info: (msg: string) => void;
286
+ error: (msg: string) => void;
287
+ };
288
+ }) => Promise<void>;
289
+ stopAccount: (ctx: {
290
+ accountId: string;
291
+ setStatus?: (status: Record<string, unknown>) => void;
292
+ }) => Promise<void>;
293
+ };
294
+ };
295
+
296
+ interface PluginRuntime {
297
+ log?: (msg: string) => void;
298
+ error?: (msg: string) => void;
299
+ channel?: {
300
+ routing?: {
301
+ resolveAgentRoute?: (params: {
302
+ cfg: unknown;
303
+ channel: string;
304
+ accountId?: string;
305
+ peer: {
306
+ kind: string;
307
+ id: string;
308
+ };
309
+ }) => {
310
+ sessionKey: string;
311
+ accountId: string;
312
+ agentId?: string;
313
+ };
314
+ };
315
+ reply?: {
316
+ dispatchReplyFromConfig?: (params: {
317
+ ctx: unknown;
318
+ cfg: unknown;
319
+ dispatcher?: unknown;
320
+ replyOptions?: unknown;
321
+ }) => Promise<{
322
+ queuedFinal: boolean;
323
+ counts: {
324
+ final: number;
325
+ };
326
+ }>;
327
+ dispatchReplyWithBufferedBlockDispatcher?: (params: {
328
+ ctx: unknown;
329
+ cfg: unknown;
330
+ dispatcherOptions: {
331
+ deliver: (payload: {
332
+ text?: string;
333
+ }) => Promise<void>;
334
+ onError?: (err: unknown, info: {
335
+ kind: string;
336
+ }) => void;
337
+ };
338
+ }) => Promise<void>;
339
+ finalizeInboundContext?: (ctx: unknown) => unknown;
340
+ createReplyDispatcher?: (params: unknown) => unknown;
341
+ createReplyDispatcherWithTyping?: (params: unknown) => {
342
+ dispatcher: unknown;
343
+ replyOptions?: unknown;
344
+ markDispatchIdle?: () => void;
345
+ };
346
+ resolveHumanDelayConfig?: (cfg: unknown, agentId?: string) => unknown;
347
+ resolveEnvelopeFormatOptions?: (cfg: unknown) => unknown;
348
+ formatAgentEnvelope?: (params: {
349
+ channel: string;
350
+ from: string;
351
+ previousTimestamp?: number;
352
+ envelope?: unknown;
353
+ body: string;
354
+ }) => string;
355
+ };
356
+ session?: {
357
+ resolveStorePath?: (store: unknown, params: {
358
+ agentId?: string;
359
+ }) => string | undefined;
360
+ readSessionUpdatedAt?: (params: {
361
+ storePath?: string;
362
+ sessionKey: string;
363
+ }) => number | null;
364
+ recordInboundSession?: (params: {
365
+ storePath: string;
366
+ sessionKey: string;
367
+ ctx: unknown;
368
+ onRecordError?: (err: unknown) => void;
369
+ }) => Promise<void>;
370
+ };
371
+ text?: {
372
+ resolveMarkdownTableMode?: (params: {
373
+ cfg: unknown;
374
+ channel: string;
375
+ accountId?: string;
376
+ }) => unknown;
377
+ convertMarkdownTables?: (text: string, mode: unknown) => string;
378
+ };
379
+ };
380
+ system?: {
381
+ enqueueSystemEvent?: (message: string, options?: unknown) => void;
382
+ };
383
+ [key: string]: unknown;
384
+ }
385
+ declare function setBaiduAppRuntime(next: PluginRuntime): void;
386
+ declare function getBaiduAppRuntime(): PluginRuntime;
387
+
388
+ declare function getAccessToken(account: ResolvedBaiduAppAccount): Promise<string>;
389
+ declare function clearAccessTokenCache(account: ResolvedBaiduAppAccount): void;
390
+ declare function clearAllAccessTokenCache(): void;
391
+ interface SendMessageResult {
392
+ ok: boolean;
393
+ errcode?: number;
394
+ errmsg?: string;
395
+ invaliduser?: string;
396
+ msgid?: string;
397
+ }
398
+ declare function sendBaiduAppMessage(account: ResolvedBaiduAppAccount, target: BaiduAppSendTarget, message: string): Promise<SendMessageResult>;
399
+
400
+ interface SendMessageOptions {
401
+ text?: string;
402
+ }
403
+ interface SendResult {
404
+ ok: boolean;
405
+ msgid?: string;
406
+ error?: string;
407
+ }
408
+ declare function normalizeTarget(target: string, type: 'user'): string;
409
+ declare function parseTarget(target: string): BaiduAppSendTarget;
410
+ declare function sendBaiduDM(account: ResolvedBaiduAppAccount, userId: string, options: SendMessageOptions): Promise<SendResult>;
411
+ declare function sendBaidu(account: ResolvedBaiduAppAccount, target: string, options: SendMessageOptions): Promise<SendResult>;
412
+
413
+ interface MoltbotPluginApi {
414
+ registerChannel: (opts: {
415
+ plugin: unknown;
416
+ }) => void;
417
+ registerHttpHandler?: (handler: (req: IncomingMessage, res: ServerResponse) => Promise<boolean> | boolean) => void;
418
+ runtime?: unknown;
419
+ [key: string]: unknown;
420
+ }
421
+
422
+ declare const plugin: {
423
+ id: string;
424
+ name: string;
425
+ description: string;
426
+ configSchema: {
427
+ type: string;
428
+ additionalProperties: boolean;
429
+ properties: {};
430
+ };
431
+ register(api: MoltbotPluginApi): void;
432
+ };
433
+
434
+ export { type AccessTokenCacheEntry, type BaiduAppConfig, type BaiduAppDmPolicy, type BaiduAppInboundMessage, type BaiduAppSendTarget, DEFAULT_ACCOUNT_ID, type MoltbotPluginApi, type ResolvedBaiduAppAccount, type SendMessageOptions, type SendResult, baiduAppPlugin, clearAccessTokenCache, clearAllAccessTokenCache, plugin as default, getAccessToken, getBaiduAppRuntime, normalizeTarget, parseTarget, sendBaidu, sendBaiduAppMessage, sendBaiduDM, setBaiduAppRuntime };