@wecom/wecom-openclaw-plugin 2026.3.30 → 2026.3.31-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 +251 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +113 -0
- package/dist/src/accounts.d.ts +57 -0
- package/dist/src/accounts.js +222 -0
- package/dist/src/agent/api-client.d.ts +94 -0
- package/dist/src/agent/api-client.js +292 -0
- package/dist/src/agent/handler.d.ts +63 -0
- package/dist/src/agent/handler.js +562 -0
- package/dist/src/agent/index.d.ts +5 -0
- package/dist/src/agent/index.js +5 -0
- package/dist/src/agent/webhook.d.ts +23 -0
- package/dist/src/agent/webhook.js +226 -0
- package/dist/src/agent/xml.d.ts +21 -0
- package/dist/src/agent/xml.js +41 -0
- package/dist/src/channel.js +564 -0
- package/dist/src/chat-queue.d.ts +31 -0
- package/dist/src/chat-queue.js +45 -0
- package/dist/src/const.d.ts +46 -0
- package/dist/src/const.js +157 -0
- package/dist/src/dm-policy.js +84 -0
- package/dist/src/dynamic-agent.d.ts +37 -0
- package/dist/src/dynamic-agent.js +60 -0
- package/dist/src/dynamic-routing.d.ts +75 -0
- package/dist/src/dynamic-routing.js +124 -0
- package/dist/src/group-policy.js +126 -0
- package/dist/src/http.d.ts +26 -0
- package/dist/src/http.js +93 -0
- package/dist/src/interface.d.ts +0 -2
- package/dist/src/interface.js +4 -0
- package/dist/src/mcp/index.js +6 -0
- package/dist/src/mcp/interceptors/biz-error.js +64 -0
- package/dist/src/mcp/interceptors/index.js +66 -0
- package/dist/src/mcp/interceptors/msg-media.js +133 -0
- package/dist/src/mcp/interceptors/smartpage-create.js +132 -0
- package/dist/src/mcp/interceptors/smartpage-export.js +87 -0
- package/dist/src/mcp/interceptors/types.js +7 -0
- package/dist/src/mcp/schema.js +99 -0
- package/dist/src/mcp/tool.js +202 -0
- package/dist/src/mcp/transport.js +410 -0
- package/dist/src/media-handler.js +122 -0
- package/dist/src/media-uploader.js +333 -0
- package/dist/src/message-parser.js +129 -0
- package/dist/src/message-sender.d.ts +18 -0
- package/dist/src/message-sender.js +120 -0
- package/dist/src/monitor.js +875 -0
- package/dist/src/onboarding.js +175 -0
- package/dist/src/openclaw-compat.d.ts +47 -0
- package/dist/src/openclaw-compat.js +477 -0
- package/dist/src/reqid-store.js +83 -0
- package/dist/src/routing.d.ts +24 -0
- package/dist/src/routing.js +34 -0
- package/dist/src/runtime.js +10 -0
- package/dist/src/shared/command-auth.d.ts +23 -0
- package/dist/src/shared/command-auth.js +73 -0
- package/dist/src/shared/xml-parser.d.ts +46 -0
- package/dist/src/shared/xml-parser.js +210 -0
- package/dist/src/state-dir-resolve.d.ts +2 -0
- package/dist/src/state-dir-resolve.js +9 -0
- package/dist/src/state-manager.js +216 -0
- package/dist/src/target.d.ts +35 -0
- package/dist/src/target.js +67 -0
- package/dist/src/template-card-manager.d.ts +55 -0
- package/dist/src/template-card-manager.js +205 -0
- package/dist/src/template-card-parser.js +647 -0
- package/dist/src/timeout.js +36 -0
- package/dist/src/types/account.d.ts +29 -0
- package/dist/src/types/account.js +4 -0
- package/dist/src/types/config.d.ts +100 -0
- package/dist/src/types/config.js +7 -0
- package/dist/src/types/constants.d.ts +42 -0
- package/dist/src/types/constants.js +42 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/message.d.ts +238 -0
- package/dist/src/types/message.js +5 -0
- package/dist/src/utils.d.ts +39 -0
- package/dist/src/utils.js +70 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +24 -0
- package/dist/src/webhook/command-auth.d.ts +47 -0
- package/dist/src/webhook/command-auth.js +96 -0
- package/dist/src/webhook/gateway.d.ts +36 -0
- package/dist/src/webhook/gateway.js +227 -0
- package/dist/src/webhook/handler.d.ts +18 -0
- package/dist/src/webhook/handler.js +420 -0
- package/dist/src/webhook/helpers.d.ts +156 -0
- package/dist/src/webhook/helpers.js +833 -0
- package/dist/src/webhook/http.d.ts +26 -0
- package/dist/src/webhook/http.js +94 -0
- package/dist/src/webhook/index.d.ts +11 -0
- package/dist/src/webhook/index.js +14 -0
- package/dist/src/webhook/media.d.ts +29 -0
- package/dist/src/webhook/media.js +88 -0
- package/dist/src/webhook/monitor.d.ts +59 -0
- package/dist/src/webhook/monitor.js +1185 -0
- package/dist/src/webhook/state.d.ts +220 -0
- package/dist/src/webhook/state.js +491 -0
- package/dist/src/webhook/target.d.ts +41 -0
- package/dist/src/webhook/target.js +132 -0
- package/dist/src/webhook/types.d.ts +347 -0
- package/dist/src/webhook/types.js +33 -0
- package/package.json +15 -7
- package/dist/index.cjs.js +0 -4953
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.esm.js +0 -4926
- package/dist/index.esm.js.map +0 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
**WeCom channel plugin for [OpenClaw](https://github.com/openclaw)** — by the Tencent WeCom team.
|
|
21
21
|
|
|
22
|
-
> A
|
|
22
|
+
> A channel plugin powered by WeCom. Supports **Bot mode** (WebSocket long-polling) and **Agent mode** (HTTP webhook with XML encrypted callbacks). Direct messages, group chats, streaming replies, and proactive messaging.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -28,13 +28,15 @@
|
|
|
28
28
|
|
|
29
29
|
## ✨ Features
|
|
30
30
|
|
|
31
|
-
- 🔗 WebSocket
|
|
31
|
+
- 🔗 **Dual-mode**: Bot (WebSocket) and Agent (HTTP webhook) can run independently or together
|
|
32
32
|
- 💬 Supports both direct messages (DM) and group chat
|
|
33
33
|
- 📤 Proactive messaging to specific users or groups
|
|
34
34
|
- 🖼️ Receives and processes image and file messages with automatic downloading
|
|
35
|
-
- ⏳ Streaming replies with "thinking" placeholder messages
|
|
35
|
+
- ⏳ Streaming replies with "thinking" placeholder messages (Bot mode)
|
|
36
|
+
- 🔐 Agent mode: AES-256-CBC encrypted XML callbacks with SHA1 signature verification
|
|
36
37
|
- 📝 Markdown formatting support for replies
|
|
37
38
|
- 🔒 Built-in access control: DM Policy (pairing / open / allowlist / disabled) and Group Policy (open / allowlist / disabled)
|
|
39
|
+
- 👥 Multi-account support: run multiple WeCom accounts with independent bot/agent configs
|
|
38
40
|
- ⚡ Auto heartbeat keep-alive and reconnection (up to 100 reconnect attempts)
|
|
39
41
|
- 🧙 Interactive CLI setup wizard
|
|
40
42
|
|
|
@@ -89,7 +91,16 @@ openclaw config set channels.wecom.enabled true
|
|
|
89
91
|
openclaw gateway restart
|
|
90
92
|
```
|
|
91
93
|
|
|
92
|
-
###
|
|
94
|
+
### Mode Overview
|
|
95
|
+
|
|
96
|
+
The plugin supports two connection modes that can be used independently or together:
|
|
97
|
+
|
|
98
|
+
| Mode | Connection | Message Format | Use Case |
|
|
99
|
+
|------|-----------|---------------|----------|
|
|
100
|
+
| **Bot** (智能体) | WebSocket long-polling | JSON | Quick setup, streaming replies |
|
|
101
|
+
| **Agent** (自建应用) | HTTP webhook callbacks | XML | Enterprise apps, API-driven messaging |
|
|
102
|
+
|
|
103
|
+
### Bot Mode Configuration
|
|
93
104
|
|
|
94
105
|
| Config Path | Description | Options | Default |
|
|
95
106
|
|---|---|---|---|
|
|
@@ -103,6 +114,141 @@ openclaw gateway restart
|
|
|
103
114
|
| `channels.wecom.groupAllowFrom` | Group allowlist (group IDs) | — | `[]` |
|
|
104
115
|
| `channels.wecom.sendThinkingMessage` | Send "thinking" placeholder | `true` / `false` | `true` |
|
|
105
116
|
|
|
117
|
+
### Agent Mode Configuration
|
|
118
|
+
|
|
119
|
+
Agent mode uses HTTP webhook callbacks with XML encrypted messages. You need to configure the callback URL in the WeCom admin console under "API Receive" settings.
|
|
120
|
+
|
|
121
|
+
#### Prerequisites
|
|
122
|
+
|
|
123
|
+
1. Create a self-built app in [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps)
|
|
124
|
+
2. In the app settings, go to "API Receive" (API接收) and configure:
|
|
125
|
+
- **URL**: `https://<your-gateway-host>/plugins/wecom/agent` (or `/plugins/wecom/agent/<accountId>` for multi-account)
|
|
126
|
+
- **Token**: auto-generated or custom
|
|
127
|
+
- **EncodingAESKey**: auto-generated or custom
|
|
128
|
+
3. Note down the **CorpID**, **CorpSecret**, **Token**, and **EncodingAESKey**
|
|
129
|
+
|
|
130
|
+
#### Quick Setup
|
|
131
|
+
|
|
132
|
+
```shell
|
|
133
|
+
openclaw config set channels.wecom.agent.corpId <YOUR_CORP_ID>
|
|
134
|
+
openclaw config set channels.wecom.agent.corpSecret <YOUR_CORP_SECRET>
|
|
135
|
+
openclaw config set channels.wecom.agent.agentId <YOUR_AGENT_ID>
|
|
136
|
+
openclaw config set channels.wecom.agent.token <YOUR_CALLBACK_TOKEN>
|
|
137
|
+
openclaw config set channels.wecom.agent.encodingAESKey <YOUR_ENCODING_AES_KEY>
|
|
138
|
+
openclaw config set channels.wecom.enabled true
|
|
139
|
+
openclaw gateway restart
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### JSON Configuration
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"channels": {
|
|
147
|
+
"wecom": {
|
|
148
|
+
"enabled": true,
|
|
149
|
+
"agent": {
|
|
150
|
+
"corpId": "ww1234567890abcdef",
|
|
151
|
+
"corpSecret": "your-corp-secret",
|
|
152
|
+
"agentId": 1000002,
|
|
153
|
+
"token": "your-callback-token",
|
|
154
|
+
"encodingAESKey": "your-encoding-aes-key-43-chars"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Agent Config Reference
|
|
162
|
+
|
|
163
|
+
| Config Path | Description | Required |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `channels.wecom.agent.corpId` | Enterprise Corp ID | Yes |
|
|
166
|
+
| `channels.wecom.agent.corpSecret` | App secret | Yes |
|
|
167
|
+
| `channels.wecom.agent.agentId` | App agent ID | No (needed for proactive messaging) |
|
|
168
|
+
| `channels.wecom.agent.token` | Callback verification token | Yes |
|
|
169
|
+
| `channels.wecom.agent.encodingAESKey` | Callback encryption key (43 chars) | Yes |
|
|
170
|
+
|
|
171
|
+
#### Webhook Paths
|
|
172
|
+
|
|
173
|
+
| Path | Description |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `/plugins/wecom/agent` | Recommended path (single account) |
|
|
176
|
+
| `/plugins/wecom/agent/<accountId>` | Multi-account path |
|
|
177
|
+
| `/wecom/agent` | Legacy compatible path |
|
|
178
|
+
|
|
179
|
+
### Outbound Delivery (Bot WS → Agent HTTP Fallback)
|
|
180
|
+
|
|
181
|
+
The plugin uses a **Bot-first, Agent-fallback** strategy for outbound message delivery:
|
|
182
|
+
|
|
183
|
+
1. **Bot WebSocket available** → send via WS (supports markdown, streaming)
|
|
184
|
+
2. **Bot WS unavailable** → automatically fallback to **Agent HTTP API** (`cgi-bin/message/send`)
|
|
185
|
+
|
|
186
|
+
This means:
|
|
187
|
+
- **Agent-only accounts** (no Bot configured) can still send proactive messages, cron deliveries, and broadcasts
|
|
188
|
+
- **Target formats** like `party:1`, `tag:Ops`, `user:zhangsan` are fully supported in both paths
|
|
189
|
+
- No manual switching needed — the plugin handles fallback transparently
|
|
190
|
+
|
|
191
|
+
### Using Both Modes Together
|
|
192
|
+
|
|
193
|
+
Bot and Agent can run simultaneously on the same account. Bot handles WebSocket streaming; Agent handles HTTP webhook callbacks with API-driven replies.
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"channels": {
|
|
198
|
+
"wecom": {
|
|
199
|
+
"enabled": true,
|
|
200
|
+
"botId": "your-bot-id",
|
|
201
|
+
"secret": "your-bot-secret",
|
|
202
|
+
"agent": {
|
|
203
|
+
"corpId": "ww1234567890abcdef",
|
|
204
|
+
"corpSecret": "your-corp-secret",
|
|
205
|
+
"agentId": 1000002,
|
|
206
|
+
"token": "your-callback-token",
|
|
207
|
+
"encodingAESKey": "your-encoding-aes-key-43-chars"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Multi-Account Configuration
|
|
215
|
+
|
|
216
|
+
Use `accounts` to configure multiple WeCom accounts, each with optional bot and/or agent sub-configs:
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"channels": {
|
|
221
|
+
"wecom": {
|
|
222
|
+
"enabled": true,
|
|
223
|
+
"accounts": {
|
|
224
|
+
"main": {
|
|
225
|
+
"bot": {
|
|
226
|
+
"botId": "bot-id-1",
|
|
227
|
+
"secret": "secret-1"
|
|
228
|
+
},
|
|
229
|
+
"agent": {
|
|
230
|
+
"corpId": "ww1234567890abcdef",
|
|
231
|
+
"corpSecret": "secret-a",
|
|
232
|
+
"agentId": 1000002,
|
|
233
|
+
"token": "token-a",
|
|
234
|
+
"encodingAESKey": "aes-key-a"
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"support": {
|
|
238
|
+
"agent": {
|
|
239
|
+
"corpId": "ww1234567890abcdef",
|
|
240
|
+
"corpSecret": "secret-b",
|
|
241
|
+
"agentId": 1000003,
|
|
242
|
+
"token": "token-b",
|
|
243
|
+
"encodingAESKey": "aes-key-b"
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
106
252
|
---
|
|
107
253
|
|
|
108
254
|
## 🔒 Access Control
|
|
@@ -198,6 +344,107 @@ In addition to the group allowlist, you can restrict which members within a grou
|
|
|
198
344
|
|
|
199
345
|
---
|
|
200
346
|
|
|
347
|
+
## ⏰ Cronjob (Scheduled Tasks)
|
|
348
|
+
|
|
349
|
+
The plugin supports scheduled message delivery via OpenClaw's built-in Cron service. Cron jobs run through the **Agent outbound** channel, so Agent mode must be configured.
|
|
350
|
+
|
|
351
|
+
### Target Formats
|
|
352
|
+
|
|
353
|
+
The `delivery.to` field supports the following target formats:
|
|
354
|
+
|
|
355
|
+
| Format | Target | Example |
|
|
356
|
+
|--------|--------|---------|
|
|
357
|
+
| `party:<id>` | Department (all members) | `party:1` (root dept = all employees) |
|
|
358
|
+
| `dept:<id>` | Department (alias) | `dept:5` |
|
|
359
|
+
| `tag:<id>` | Tag group | `tag:Ops` |
|
|
360
|
+
| `user:<id>` | Specific user | `user:zhangsan` |
|
|
361
|
+
| `group:<id>` | External group chat | `group:wr123abc` |
|
|
362
|
+
| `chat:<id>` | Group chat (alias) | `chat:wc456def` |
|
|
363
|
+
| Pure number | Auto-detected as department | `1` → `party:1` |
|
|
364
|
+
| `wr...` / `wc...` | Auto-detected as group chat | `wr123` → `chatid` |
|
|
365
|
+
| Other string | Auto-detected as user | `zhangsan` → `touser` |
|
|
366
|
+
|
|
367
|
+
### Method 1: CLI (Recommended — takes effect immediately)
|
|
368
|
+
|
|
369
|
+
```shell
|
|
370
|
+
openclaw cron add \
|
|
371
|
+
--name "daily-report" \
|
|
372
|
+
--agent main \
|
|
373
|
+
--cron "0 9 * * 1-5" \
|
|
374
|
+
--message "Good morning! Here is your daily briefing." \
|
|
375
|
+
--deliver \
|
|
376
|
+
--delivery-channel wecom \
|
|
377
|
+
--delivery-to "party:1"
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Common CLI commands:
|
|
381
|
+
|
|
382
|
+
```shell
|
|
383
|
+
openclaw cron list # List all cron jobs
|
|
384
|
+
openclaw cron enable <id> # Enable a job
|
|
385
|
+
openclaw cron disable <id> # Disable a job
|
|
386
|
+
openclaw cron remove <id> # Remove a job
|
|
387
|
+
openclaw cron run <id> # Manually trigger a job
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Method 2: Edit `jobs.json` (requires gateway restart)
|
|
391
|
+
|
|
392
|
+
File path: `~/.openclaw/cron/jobs.json`
|
|
393
|
+
|
|
394
|
+
```json
|
|
395
|
+
{
|
|
396
|
+
"version": 1,
|
|
397
|
+
"jobs": [
|
|
398
|
+
{
|
|
399
|
+
"id": "daily-report",
|
|
400
|
+
"name": "Daily Report",
|
|
401
|
+
"agentId": "main",
|
|
402
|
+
"enabled": true,
|
|
403
|
+
"schedule": { "kind": "cron", "expr": "0 9 * * 1-5", "tz": "Asia/Shanghai" },
|
|
404
|
+
"sessionTarget": "isolated",
|
|
405
|
+
"wakeMode": "now",
|
|
406
|
+
"payload": {
|
|
407
|
+
"kind": "agentTurn",
|
|
408
|
+
"message": "Generate today's briefing and send it.",
|
|
409
|
+
"deliver": true,
|
|
410
|
+
"channel": "wecom",
|
|
411
|
+
"to": "party:1"
|
|
412
|
+
},
|
|
413
|
+
"delivery": {
|
|
414
|
+
"mode": "announce",
|
|
415
|
+
"channel": "wecom",
|
|
416
|
+
"to": "party:1",
|
|
417
|
+
"accountId": "main"
|
|
418
|
+
},
|
|
419
|
+
"state": {}
|
|
420
|
+
}
|
|
421
|
+
]
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
After editing, restart the gateway:
|
|
426
|
+
|
|
427
|
+
```shell
|
|
428
|
+
openclaw gateway restart
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Method 3: Create via chat (takes effect immediately)
|
|
432
|
+
|
|
433
|
+
You can ask the AI agent directly in a WeCom conversation:
|
|
434
|
+
|
|
435
|
+
> "Create a scheduled task: send a daily briefing to the entire company at 9am every weekday"
|
|
436
|
+
|
|
437
|
+
The agent will call the Cron API to create the job — no restart needed.
|
|
438
|
+
|
|
439
|
+
### Notes
|
|
440
|
+
|
|
441
|
+
- Cron jobs use the **Agent outbound** path — Agent mode (`corpId` / `corpSecret` / `agentId`) must be configured.
|
|
442
|
+
- The server IP must be in the WeCom trusted IP allowlist, or configure `egressProxyUrl` for a fixed egress proxy.
|
|
443
|
+
- Jobs created via CLI or chat API take effect immediately. Manual edits to `jobs.json` require `openclaw gateway restart`.
|
|
444
|
+
- For multi-account setups, set `delivery.accountId` to the target account (e.g. `"main"`, `"support"`).
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
201
448
|
## 📦 Update
|
|
202
449
|
|
|
203
450
|
```shell
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { OpenClawPluginApi } from
|
|
2
|
-
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
|
3
2
|
declare const plugin: {
|
|
4
3
|
id: string;
|
|
5
4
|
name: string;
|
|
@@ -7,5 +6,4 @@ declare const plugin: {
|
|
|
7
6
|
configSchema: Record<string, unknown>;
|
|
8
7
|
register(api: OpenClawPluginApi): void;
|
|
9
8
|
};
|
|
10
|
-
|
|
11
|
-
export { plugin as default };
|
|
9
|
+
export default plugin;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { emptyPluginConfigSchema } from "./src/openclaw-compat.js";
|
|
2
|
+
import { wecomPlugin } from "./src/channel.js";
|
|
3
|
+
import { createWeComMcpTool } from "./src/mcp/index.js";
|
|
4
|
+
import { setWeComRuntime } from "./src/runtime.js";
|
|
5
|
+
import { CHANNEL_ID, WEBHOOK_PATHS } from "./src/const.js";
|
|
6
|
+
import { handleWecomAgentWebhookRequest } from "./src/agent/webhook.js";
|
|
7
|
+
import { handleWecomWebhookRequest } from "./src/webhook/index.js";
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// 需要自动注入 tools.alsoAllow 的工具名列表
|
|
10
|
+
// ============================================================================
|
|
11
|
+
const REQUIRED_ALSO_ALLOW_TOOLS = ["wecom_mcp"];
|
|
12
|
+
/**
|
|
13
|
+
* 确保 tools.alsoAllow 中包含插件所需的工具名。
|
|
14
|
+
*
|
|
15
|
+
* 逻辑:
|
|
16
|
+
* 1. 读取当前配置
|
|
17
|
+
* 2. 检查 tools.alsoAllow 中是否已包含所需工具
|
|
18
|
+
* 3. 如有缺失,合并写入配置文件
|
|
19
|
+
*
|
|
20
|
+
* 幂等操作——重复调用不会产生副作用。
|
|
21
|
+
* 若 tools.allow 已显式设置(与 alsoAllow 互斥),则跳过注入并打印提示。
|
|
22
|
+
*/
|
|
23
|
+
async function ensureToolsAlsoAllow(api) {
|
|
24
|
+
try {
|
|
25
|
+
const cfg = api.runtime.config.loadConfig();
|
|
26
|
+
const tools = cfg.tools ?? {};
|
|
27
|
+
// 如果用户显式配置了 tools.allow(全量白名单),则 alsoAllow 与之互斥,
|
|
28
|
+
// 此时不应自动追加 alsoAllow,避免产生校验冲突
|
|
29
|
+
if (tools.allow && tools.allow.length > 0) {
|
|
30
|
+
const missing = REQUIRED_ALSO_ALLOW_TOOLS.filter((t) => !tools.allow.includes(t));
|
|
31
|
+
if (missing.length > 0) {
|
|
32
|
+
console.warn(`[wecom] tools.allow 已显式设置,无法自动注入 alsoAllow。` +
|
|
33
|
+
`请手动将 ${JSON.stringify(missing)} 加入 tools.allow。`);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const existing = tools.alsoAllow ?? [];
|
|
38
|
+
const missing = REQUIRED_ALSO_ALLOW_TOOLS.filter((t) => !existing.includes(t));
|
|
39
|
+
if (missing.length === 0) {
|
|
40
|
+
// 所有工具已在白名单中,无需操作
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const merged = [...existing, ...missing];
|
|
44
|
+
const nextConfig = {
|
|
45
|
+
...cfg,
|
|
46
|
+
tools: {
|
|
47
|
+
...tools,
|
|
48
|
+
alsoAllow: merged,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
await api.runtime.config.writeConfigFile(nextConfig);
|
|
52
|
+
console.log(`[wecom] 已自动将 ${JSON.stringify(missing)} 加入 tools.alsoAllow`);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// 配置写入失败不应阻断 Gateway 启动
|
|
56
|
+
console.error(`[wecom] 自动注入 tools.alsoAllow 失败:`, err instanceof Error ? err.message : String(err));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const plugin = {
|
|
60
|
+
id: "wecom-openclaw-plugin",
|
|
61
|
+
name: "企业微信",
|
|
62
|
+
description: "企业微信 OpenClaw 插件",
|
|
63
|
+
configSchema: emptyPluginConfigSchema(),
|
|
64
|
+
register(api) {
|
|
65
|
+
setWeComRuntime(api.runtime);
|
|
66
|
+
api.registerChannel({ plugin: wecomPlugin });
|
|
67
|
+
// 注册 wecom_mcp:通过 HTTP 直接调用企业微信 MCP Server
|
|
68
|
+
api.registerTool(createWeComMcpTool(), { name: "wecom_mcp" });
|
|
69
|
+
// 注册 Agent 模式 HTTP 路由(prefix match 覆盖带 accountId 的子路径)
|
|
70
|
+
api.registerHttpRoute({
|
|
71
|
+
path: WEBHOOK_PATHS.AGENT_PLUGIN,
|
|
72
|
+
handler: handleWecomAgentWebhookRequest,
|
|
73
|
+
auth: "plugin",
|
|
74
|
+
match: "prefix",
|
|
75
|
+
});
|
|
76
|
+
api.registerHttpRoute({
|
|
77
|
+
path: WEBHOOK_PATHS.AGENT,
|
|
78
|
+
handler: handleWecomAgentWebhookRequest,
|
|
79
|
+
auth: "plugin",
|
|
80
|
+
match: "prefix",
|
|
81
|
+
});
|
|
82
|
+
// ── 注册 bot Webhook HTTP 路由 ────────────────────────────────────────
|
|
83
|
+
// 前缀匹配模式,同时覆盖 /plugins/wecom 和 /wecom 路径
|
|
84
|
+
const webhookRoutes = [WEBHOOK_PATHS.BOT_PLUGIN, WEBHOOK_PATHS.BOT_ALT, WEBHOOK_PATHS.BOT];
|
|
85
|
+
for (const routePath of webhookRoutes) {
|
|
86
|
+
api.registerHttpRoute({
|
|
87
|
+
path: routePath,
|
|
88
|
+
handler: handleWecomWebhookRequest,
|
|
89
|
+
auth: "plugin",
|
|
90
|
+
match: "prefix",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// ── Gateway 启动时自动确保 tools.alsoAllow 包含 wecom_mcp ──────────
|
|
94
|
+
// 在 gateway_start 阶段检测并写入,保证插件安装/更新后首次启动即生效
|
|
95
|
+
// api.on("gateway_start", async () => {
|
|
96
|
+
// await ensureToolsAlsoAllow(api);
|
|
97
|
+
// });
|
|
98
|
+
// 注入媒体发送指令和文件大小限制提示词(仅对企业微信 channel 生效)
|
|
99
|
+
api.on("before_prompt_build", (_event, ctx) => {
|
|
100
|
+
// 只在企业微信 channel 的会话中注入,避免影响其他 channel 插件
|
|
101
|
+
if (ctx?.channelId !== CHANNEL_ID) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
appendSystemContext: [
|
|
106
|
+
"重要:涉及发送图片/视频/语音/文件给用户时,请务必使用 `MEDIA:` 指令。详见 wecom-send-media 这个 skill(技能)。",
|
|
107
|
+
"重要:当需要向用户发送结构化卡片消息(如通知、投票、按钮选择等)时,请在回复中直接输出 JSON 代码块(```json ... ```),其中 card_type 字段标明卡片类型。详见 wecom-send-template-card 技能。"
|
|
108
|
+
].join("\n"),
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
export default plugin;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { WeComConfig, WeComAccountConfig, ResolvedWeComAccount } from "./utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* 企业微信多账号配置结构(扩展 WeComConfig)
|
|
5
|
+
*/
|
|
6
|
+
export interface WeComMultiAccountConfig extends WeComConfig {
|
|
7
|
+
/** 默认账号 ID */
|
|
8
|
+
defaultAccount?: string;
|
|
9
|
+
/** 多账号配置 */
|
|
10
|
+
accounts?: Record<string, WeComAccountConfig>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 判断是否为多账号模式(即配置中存在 accounts 字段)。
|
|
14
|
+
* 用于区分单账号/多账号模式的分支判断,替代 `accountId === DEFAULT_ACCOUNT_ID` 的不可靠判断。
|
|
15
|
+
*/
|
|
16
|
+
export declare function hasMultiAccounts(cfg: OpenClawConfig): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 列出所有企业微信账号 ID。
|
|
19
|
+
* 如果没有 accounts 字段,返回 [DEFAULT_ACCOUNT_ID] 以向后兼容。
|
|
20
|
+
*/
|
|
21
|
+
export declare function listWeComAccountIds(cfg: OpenClawConfig): string[];
|
|
22
|
+
/**
|
|
23
|
+
* 解析默认账号 ID。
|
|
24
|
+
*
|
|
25
|
+
* 优先级:
|
|
26
|
+
* 1. 显式设置的 defaultAccount
|
|
27
|
+
* 2. 包含 DEFAULT_ACCOUNT_ID 的账号列表
|
|
28
|
+
* 3. 字母序第一个账号
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveDefaultWeComAccountId(cfg: OpenClawConfig): string;
|
|
31
|
+
/**
|
|
32
|
+
* 解析单个企业微信账号的完整配置。
|
|
33
|
+
*
|
|
34
|
+
* 支持:
|
|
35
|
+
* - 显式指定 accountId → 使用该 accountId
|
|
36
|
+
* - 未指定 → 使用默认账号
|
|
37
|
+
* - 单账号模式(无 accounts 字段) → 直接读取顶层配置
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveWeComAccountMulti(params: {
|
|
40
|
+
cfg: OpenClawConfig;
|
|
41
|
+
accountId?: string | null;
|
|
42
|
+
}): ResolvedWeComAccount;
|
|
43
|
+
/**
|
|
44
|
+
* 列出所有已启用且已配置凭据的账号。
|
|
45
|
+
*/
|
|
46
|
+
export declare function listEnabledWeComAccounts(cfg: OpenClawConfig): ResolvedWeComAccount[];
|
|
47
|
+
/**
|
|
48
|
+
* 写入企业微信账户配置(自动区分单账号/多账号模式)。
|
|
49
|
+
*
|
|
50
|
+
* - 单账号模式(无 accounts 字段):写入顶层 channels.wecom
|
|
51
|
+
* - 多账号模式:写入 channels.wecom.accounts[accountId]
|
|
52
|
+
*
|
|
53
|
+
* @param cfg 当前全局配置
|
|
54
|
+
* @param updates 要写入的部分配置字段
|
|
55
|
+
* @param accountId 目标账号 ID(默认写入默认账号)
|
|
56
|
+
*/
|
|
57
|
+
export declare function setWeComAccountMulti(cfg: OpenClawConfig, updates: Partial<WeComConfig>, accountId?: string): OpenClawConfig;
|