@wecom/wecom-openclaw-plugin 2026.3.31-beta.0 → 2026.4.1-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.
Files changed (209) hide show
  1. package/README.md +4 -251
  2. package/dist/cjs/index.js +45 -0
  3. package/dist/cjs/index.js.map +1 -0
  4. package/dist/cjs/src/channel.js +339 -0
  5. package/dist/cjs/src/channel.js.map +1 -0
  6. package/dist/cjs/src/const.js +137 -0
  7. package/dist/cjs/src/const.js.map +1 -0
  8. package/dist/cjs/src/dm-policy.js +89 -0
  9. package/dist/cjs/src/dm-policy.js.map +1 -0
  10. package/dist/cjs/src/group-policy.js +133 -0
  11. package/dist/cjs/src/group-policy.js.map +1 -0
  12. package/dist/cjs/src/mcp/interceptors/biz-error.js +70 -0
  13. package/dist/cjs/src/mcp/interceptors/biz-error.js.map +1 -0
  14. package/dist/cjs/src/mcp/interceptors/index.js +73 -0
  15. package/dist/cjs/src/mcp/interceptors/index.js.map +1 -0
  16. package/dist/cjs/src/mcp/interceptors/msg-media.js +159 -0
  17. package/dist/cjs/src/mcp/interceptors/msg-media.js.map +1 -0
  18. package/dist/cjs/src/mcp/interceptors/smartpage-create.js +157 -0
  19. package/dist/cjs/src/mcp/interceptors/smartpage-create.js.map +1 -0
  20. package/dist/cjs/src/mcp/interceptors/smartpage-export.js +93 -0
  21. package/dist/cjs/src/mcp/interceptors/smartpage-export.js.map +1 -0
  22. package/dist/cjs/src/mcp/schema.js +104 -0
  23. package/dist/cjs/src/mcp/schema.js.map +1 -0
  24. package/dist/cjs/src/mcp/tool.js +208 -0
  25. package/dist/cjs/src/mcp/tool.js.map +1 -0
  26. package/dist/cjs/src/mcp/transport.js +417 -0
  27. package/dist/cjs/src/mcp/transport.js.map +1 -0
  28. package/dist/cjs/src/media-handler.js +129 -0
  29. package/dist/cjs/src/media-handler.js.map +1 -0
  30. package/dist/cjs/src/media-uploader.js +284 -0
  31. package/dist/cjs/src/media-uploader.js.map +1 -0
  32. package/dist/cjs/src/message-parser.js +134 -0
  33. package/dist/cjs/src/message-parser.js.map +1 -0
  34. package/dist/cjs/src/message-sender.js +89 -0
  35. package/dist/cjs/src/message-sender.js.map +1 -0
  36. package/dist/cjs/src/monitor.js +991 -0
  37. package/dist/cjs/src/monitor.js.map +1 -0
  38. package/dist/cjs/src/onboarding.js +182 -0
  39. package/dist/cjs/src/onboarding.js.map +1 -0
  40. package/dist/cjs/src/openclaw-compat.js +400 -0
  41. package/dist/cjs/src/openclaw-compat.js.map +1 -0
  42. package/dist/cjs/src/reqid-store.js +88 -0
  43. package/dist/cjs/src/reqid-store.js.map +1 -0
  44. package/dist/cjs/src/runtime.js +16 -0
  45. package/dist/cjs/src/runtime.js.map +1 -0
  46. package/dist/cjs/src/state-dir-resolve.js +35 -0
  47. package/dist/cjs/src/state-dir-resolve.js.map +1 -0
  48. package/dist/cjs/src/state-manager.js +158 -0
  49. package/dist/cjs/src/state-manager.js.map +1 -0
  50. package/dist/cjs/src/template-card-parser.js +641 -0
  51. package/dist/cjs/src/template-card-parser.js.map +1 -0
  52. package/dist/cjs/src/timeout.js +42 -0
  53. package/dist/cjs/src/timeout.js.map +1 -0
  54. package/dist/cjs/src/utils.js +60 -0
  55. package/dist/cjs/src/utils.js.map +1 -0
  56. package/dist/cjs/src/version.js +26 -0
  57. package/dist/cjs/src/version.js.map +1 -0
  58. package/dist/esm/index.js +41 -0
  59. package/dist/esm/index.js.map +1 -0
  60. package/dist/esm/src/channel.js +337 -0
  61. package/dist/esm/src/channel.js.map +1 -0
  62. package/dist/{src → esm/src}/const.js +30 -77
  63. package/dist/esm/src/const.js.map +1 -0
  64. package/dist/{src → esm/src}/dm-policy.js +9 -6
  65. package/dist/esm/src/dm-policy.js.map +1 -0
  66. package/dist/{src → esm/src}/group-policy.js +7 -3
  67. package/dist/esm/src/group-policy.js.map +1 -0
  68. package/dist/{src → esm/src}/mcp/interceptors/biz-error.js +6 -2
  69. package/dist/esm/src/mcp/interceptors/biz-error.js.map +1 -0
  70. package/dist/{src → esm/src}/mcp/interceptors/index.js +10 -6
  71. package/dist/esm/src/mcp/interceptors/index.js.map +1 -0
  72. package/dist/{src → esm/src}/mcp/interceptors/msg-media.js +10 -6
  73. package/dist/esm/src/mcp/interceptors/msg-media.js.map +1 -0
  74. package/dist/{src → esm/src}/mcp/interceptors/smartpage-create.js +6 -2
  75. package/dist/esm/src/mcp/interceptors/smartpage-create.js.map +1 -0
  76. package/dist/{src → esm/src}/mcp/interceptors/smartpage-export.js +6 -2
  77. package/dist/esm/src/mcp/interceptors/smartpage-export.js.map +1 -0
  78. package/dist/{src → esm/src}/mcp/schema.js +4 -1
  79. package/dist/esm/src/mcp/schema.js.map +1 -0
  80. package/dist/{src → esm/src}/mcp/tool.js +8 -4
  81. package/dist/esm/src/mcp/tool.js.map +1 -0
  82. package/dist/{src → esm/src}/mcp/transport.js +16 -15
  83. package/dist/esm/src/mcp/transport.js.map +1 -0
  84. package/dist/{src → esm/src}/media-handler.js +10 -6
  85. package/dist/esm/src/media-handler.js.map +1 -0
  86. package/dist/{src → esm/src}/media-uploader.js +10 -64
  87. package/dist/esm/src/media-uploader.js.map +1 -0
  88. package/dist/{src → esm/src}/message-parser.js +4 -1
  89. package/dist/esm/src/message-parser.js.map +1 -0
  90. package/dist/{src → esm/src}/message-sender.js +11 -46
  91. package/dist/esm/src/message-sender.js.map +1 -0
  92. package/dist/{src → esm/src}/monitor.js +282 -151
  93. package/dist/esm/src/monitor.js.map +1 -0
  94. package/dist/{src → esm/src}/onboarding.js +26 -22
  95. package/dist/esm/src/onboarding.js.map +1 -0
  96. package/dist/{src → esm/src}/openclaw-compat.js +27 -143
  97. package/dist/esm/src/openclaw-compat.js.map +1 -0
  98. package/dist/{src → esm/src}/reqid-store.js +7 -4
  99. package/dist/esm/src/reqid-store.js.map +1 -0
  100. package/dist/{src → esm/src}/runtime.js +5 -2
  101. package/dist/esm/src/runtime.js.map +1 -0
  102. package/dist/esm/src/state-dir-resolve.js +13 -0
  103. package/dist/esm/src/state-dir-resolve.js.map +1 -0
  104. package/dist/{src → esm/src}/state-manager.js +16 -84
  105. package/dist/esm/src/state-manager.js.map +1 -0
  106. package/dist/{src → esm/src}/template-card-parser.js +8 -17
  107. package/dist/esm/src/template-card-parser.js.map +1 -0
  108. package/dist/{src → esm/src}/timeout.js +5 -2
  109. package/dist/esm/src/timeout.js.map +1 -0
  110. package/dist/{src → esm/src}/utils.js +10 -24
  111. package/dist/esm/src/utils.js.map +1 -0
  112. package/dist/esm/src/version.js +23 -0
  113. package/dist/esm/src/version.js.map +1 -0
  114. package/dist/esm/types/index.d.ts +9 -0
  115. package/dist/{src → esm/types/src}/const.d.ts +0 -46
  116. package/dist/{src → esm/types/src}/interface.d.ts +2 -0
  117. package/dist/{src → esm/types/src}/message-sender.d.ts +0 -18
  118. package/dist/{src → esm/types/src}/openclaw-compat.d.ts +0 -49
  119. package/dist/esm/types/src/state-dir-resolve.d.ts +2 -0
  120. package/dist/{src → esm/types/src}/utils.d.ts +0 -39
  121. package/dist/esm/types/src/version.d.ts +2 -0
  122. package/dist/index.d.ts +4 -2
  123. package/package.json +7 -15
  124. package/dist/index.js +0 -113
  125. package/dist/src/accounts.d.ts +0 -57
  126. package/dist/src/accounts.js +0 -222
  127. package/dist/src/agent/api-client.d.ts +0 -94
  128. package/dist/src/agent/api-client.js +0 -292
  129. package/dist/src/agent/handler.d.ts +0 -63
  130. package/dist/src/agent/handler.js +0 -532
  131. package/dist/src/agent/index.d.ts +0 -5
  132. package/dist/src/agent/index.js +0 -5
  133. package/dist/src/agent/webhook.d.ts +0 -23
  134. package/dist/src/agent/webhook.js +0 -226
  135. package/dist/src/agent/xml.d.ts +0 -21
  136. package/dist/src/agent/xml.js +0 -41
  137. package/dist/src/channel.js +0 -564
  138. package/dist/src/chat-queue.d.ts +0 -31
  139. package/dist/src/chat-queue.js +0 -45
  140. package/dist/src/http.d.ts +0 -26
  141. package/dist/src/http.js +0 -93
  142. package/dist/src/interface.js +0 -4
  143. package/dist/src/mcp/index.js +0 -6
  144. package/dist/src/mcp/interceptors/types.js +0 -7
  145. package/dist/src/shared/command-auth.d.ts +0 -23
  146. package/dist/src/shared/command-auth.js +0 -73
  147. package/dist/src/shared/xml-parser.d.ts +0 -46
  148. package/dist/src/shared/xml-parser.js +0 -210
  149. package/dist/src/target.d.ts +0 -35
  150. package/dist/src/target.js +0 -67
  151. package/dist/src/template-card-manager.d.ts +0 -55
  152. package/dist/src/template-card-manager.js +0 -205
  153. package/dist/src/types/account.d.ts +0 -29
  154. package/dist/src/types/account.js +0 -4
  155. package/dist/src/types/config.d.ts +0 -80
  156. package/dist/src/types/config.js +0 -7
  157. package/dist/src/types/constants.d.ts +0 -42
  158. package/dist/src/types/constants.js +0 -42
  159. package/dist/src/types/index.d.ts +0 -7
  160. package/dist/src/types/index.js +0 -5
  161. package/dist/src/types/message.d.ts +0 -238
  162. package/dist/src/types/message.js +0 -5
  163. package/dist/src/version.d.ts +0 -2
  164. package/dist/src/version.js +0 -24
  165. package/dist/src/webhook/command-auth.d.ts +0 -47
  166. package/dist/src/webhook/command-auth.js +0 -96
  167. package/dist/src/webhook/gateway.d.ts +0 -36
  168. package/dist/src/webhook/gateway.js +0 -227
  169. package/dist/src/webhook/handler.d.ts +0 -18
  170. package/dist/src/webhook/handler.js +0 -420
  171. package/dist/src/webhook/helpers.d.ts +0 -156
  172. package/dist/src/webhook/helpers.js +0 -833
  173. package/dist/src/webhook/http.d.ts +0 -26
  174. package/dist/src/webhook/http.js +0 -94
  175. package/dist/src/webhook/index.d.ts +0 -11
  176. package/dist/src/webhook/index.js +0 -14
  177. package/dist/src/webhook/media.d.ts +0 -29
  178. package/dist/src/webhook/media.js +0 -88
  179. package/dist/src/webhook/monitor.d.ts +0 -59
  180. package/dist/src/webhook/monitor.js +0 -1147
  181. package/dist/src/webhook/state.d.ts +0 -220
  182. package/dist/src/webhook/state.js +0 -491
  183. package/dist/src/webhook/target.d.ts +0 -41
  184. package/dist/src/webhook/target.js +0 -132
  185. package/dist/src/webhook/types.d.ts +0 -347
  186. package/dist/src/webhook/types.js +0 -33
  187. /package/dist/{src → esm/types/src}/channel.d.ts +0 -0
  188. /package/dist/{src → esm/types/src}/dm-policy.d.ts +0 -0
  189. /package/dist/{src → esm/types/src}/group-policy.d.ts +0 -0
  190. /package/dist/{src → esm/types/src}/mcp/index.d.ts +0 -0
  191. /package/dist/{src → esm/types/src}/mcp/interceptors/biz-error.d.ts +0 -0
  192. /package/dist/{src → esm/types/src}/mcp/interceptors/index.d.ts +0 -0
  193. /package/dist/{src → esm/types/src}/mcp/interceptors/msg-media.d.ts +0 -0
  194. /package/dist/{src → esm/types/src}/mcp/interceptors/smartpage-create.d.ts +0 -0
  195. /package/dist/{src → esm/types/src}/mcp/interceptors/smartpage-export.d.ts +0 -0
  196. /package/dist/{src → esm/types/src}/mcp/interceptors/types.d.ts +0 -0
  197. /package/dist/{src → esm/types/src}/mcp/schema.d.ts +0 -0
  198. /package/dist/{src → esm/types/src}/mcp/tool.d.ts +0 -0
  199. /package/dist/{src → esm/types/src}/mcp/transport.d.ts +0 -0
  200. /package/dist/{src → esm/types/src}/media-handler.d.ts +0 -0
  201. /package/dist/{src → esm/types/src}/media-uploader.d.ts +0 -0
  202. /package/dist/{src → esm/types/src}/message-parser.d.ts +0 -0
  203. /package/dist/{src → esm/types/src}/monitor.d.ts +0 -0
  204. /package/dist/{src → esm/types/src}/onboarding.d.ts +0 -0
  205. /package/dist/{src → esm/types/src}/reqid-store.d.ts +0 -0
  206. /package/dist/{src → esm/types/src}/runtime.d.ts +0 -0
  207. /package/dist/{src → esm/types/src}/state-manager.d.ts +0 -0
  208. /package/dist/{src → esm/types/src}/template-card-parser.d.ts +0 -0
  209. /package/dist/{src → esm/types/src}/timeout.d.ts +0 -0
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 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.
22
+ > A bot plugin powered by WeCom AI Bot WebSocket persistent connections. Supports direct messages & group chats, streaming replies, and proactive messaging.
23
23
 
24
24
  ---
25
25
 
@@ -28,15 +28,13 @@
28
28
 
29
29
  ## ✨ Features
30
30
 
31
- - 🔗 **Dual-mode**: Bot (WebSocket) and Agent (HTTP webhook) can run independently or together
31
+ - 🔗 WebSocket persistent connection for stable communication
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 (Bot mode)
36
- - 🔐 Agent mode: AES-256-CBC encrypted XML callbacks with SHA1 signature verification
35
+ - ⏳ Streaming replies with "thinking" placeholder messages
37
36
  - 📝 Markdown formatting support for replies
38
37
  - 🔒 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
40
38
  - ⚡ Auto heartbeat keep-alive and reconnection (up to 100 reconnect attempts)
41
39
  - 🧙 Interactive CLI setup wizard
42
40
 
@@ -91,16 +89,7 @@ openclaw config set channels.wecom.enabled true
91
89
  openclaw gateway restart
92
90
  ```
93
91
 
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
92
+ ### Configuration Reference
104
93
 
105
94
  | Config Path | Description | Options | Default |
106
95
  |---|---|---|---|
@@ -114,141 +103,6 @@ The plugin supports two connection modes that can be used independently or toget
114
103
  | `channels.wecom.groupAllowFrom` | Group allowlist (group IDs) | — | `[]` |
115
104
  | `channels.wecom.sendThinkingMessage` | Send "thinking" placeholder | `true` / `false` | `true` |
116
105
 
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
-
252
106
  ---
253
107
 
254
108
  ## 🔒 Access Control
@@ -344,107 +198,6 @@ In addition to the group allowlist, you can restrict which members within a grou
344
198
 
345
199
  ---
346
200
 
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
-
448
201
  ## 📦 Update
449
202
 
450
203
  ```shell
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var openclawCompat = require('./src/openclaw-compat.js');
6
+ var channel = require('./src/channel.js');
7
+ var tool = require('./src/mcp/tool.js');
8
+ require('@wecom/aibot-node-sdk');
9
+ var _const = require('./src/const.js');
10
+ require('./src/version.js');
11
+ var runtime = require('./src/runtime.js');
12
+
13
+ const plugin = {
14
+ id: "wecom-openclaw-plugin",
15
+ name: "企业微信",
16
+ description: "企业微信 OpenClaw 插件",
17
+ configSchema: openclawCompat.emptyPluginConfigSchema(),
18
+ register(api) {
19
+ runtime.setWeComRuntime(api.runtime);
20
+ api.registerChannel({ plugin: channel.wecomPlugin });
21
+ // 注册 wecom_mcp:通过 HTTP 直接调用企业微信 MCP Server
22
+ api.registerTool(tool.createWeComMcpTool(), { name: "wecom_mcp" });
23
+ // ── Gateway 启动时自动确保 tools.alsoAllow 包含 wecom_mcp ──────────
24
+ // 在 gateway_start 阶段检测并写入,保证插件安装/更新后首次启动即生效
25
+ // api.on("gateway_start", async () => {
26
+ // await ensureToolsAlsoAllow(api);
27
+ // });
28
+ // 注入媒体发送指令和文件大小限制提示词(仅对企业微信 channel 生效)
29
+ api.on("before_prompt_build", (_event, ctx) => {
30
+ // 只在企业微信 channel 的会话中注入,避免影响其他 channel 插件
31
+ if (ctx?.channelId !== _const.CHANNEL_ID) {
32
+ return;
33
+ }
34
+ return {
35
+ appendSystemContext: [
36
+ "重要:涉及发送图片/视频/语音/文件给用户时,请务必使用 `MEDIA:` 指令。详见 wecom-send-media 这个 skill(技能)。",
37
+ "重要:当需要向用户发送结构化卡片消息(如通知、投票、按钮选择等)时,请在回复中直接输出 JSON 代码块(```json ... ```),其中 card_type 字段标明卡片类型。详见 wecom-send-template-card 技能。"
38
+ ].join("\n"),
39
+ };
40
+ });
41
+ },
42
+ };
43
+
44
+ exports.default = plugin;
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../index.ts"],"sourcesContent":["import type { OpenClawPluginApi } from \"openclaw/plugin-sdk/core\";\nimport { emptyPluginConfigSchema } from \"./src/openclaw-compat.js\";\nimport { wecomPlugin } from \"./src/channel.js\";\nimport { createWeComMcpTool } from \"./src/mcp\";\nimport { setWeComRuntime } from \"./src/runtime.js\";\nimport { CHANNEL_ID } from \"./src/const.js\";\n\n// ============================================================================\n// 需要自动注入 tools.alsoAllow 的工具名列表\n// ============================================================================\n\nconst REQUIRED_ALSO_ALLOW_TOOLS = [\"wecom_mcp\"] as const;\n\n/**\n * 确保 tools.alsoAllow 中包含插件所需的工具名。\n *\n * 逻辑:\n * 1. 读取当前配置\n * 2. 检查 tools.alsoAllow 中是否已包含所需工具\n * 3. 如有缺失,合并写入配置文件\n *\n * 幂等操作——重复调用不会产生副作用。\n * 若 tools.allow 已显式设置(与 alsoAllow 互斥),则跳过注入并打印提示。\n */\nasync function ensureToolsAlsoAllow(api: OpenClawPluginApi): Promise<void> {\n try {\n const cfg = api.runtime.config.loadConfig();\n const tools = cfg.tools ?? {};\n\n // 如果用户显式配置了 tools.allow(全量白名单),则 alsoAllow 与之互斥,\n // 此时不应自动追加 alsoAllow,避免产生校验冲突\n if (tools.allow && tools.allow.length > 0) {\n const missing = REQUIRED_ALSO_ALLOW_TOOLS.filter(\n (t) => !tools.allow!.includes(t),\n );\n if (missing.length > 0) {\n console.warn(\n `[wecom] tools.allow 已显式设置,无法自动注入 alsoAllow。` +\n `请手动将 ${JSON.stringify(missing)} 加入 tools.allow。`,\n );\n }\n return;\n }\n\n const existing = tools.alsoAllow ?? [];\n const missing = REQUIRED_ALSO_ALLOW_TOOLS.filter(\n (t) => !existing.includes(t),\n );\n\n if (missing.length === 0) {\n // 所有工具已在白名单中,无需操作\n return;\n }\n\n const merged = [...existing, ...missing];\n const nextConfig = {\n ...cfg,\n tools: {\n ...tools,\n alsoAllow: merged,\n },\n };\n\n await api.runtime.config.writeConfigFile(nextConfig);\n console.log(\n `[wecom] 已自动将 ${JSON.stringify(missing)} 加入 tools.alsoAllow`,\n );\n } catch (err) {\n // 配置写入失败不应阻断 Gateway 启动\n console.error(\n `[wecom] 自动注入 tools.alsoAllow 失败:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n}\n\nconst plugin = {\n id: \"wecom-openclaw-plugin\",\n name: \"企业微信\",\n description: \"企业微信 OpenClaw 插件\",\n configSchema: emptyPluginConfigSchema(),\n register(api: OpenClawPluginApi) {\n\n setWeComRuntime(api.runtime);\n api.registerChannel({ plugin: wecomPlugin });\n\n // 注册 wecom_mcp:通过 HTTP 直接调用企业微信 MCP Server\n api.registerTool(createWeComMcpTool(), { name: \"wecom_mcp\" });\n\n // ── Gateway 启动时自动确保 tools.alsoAllow 包含 wecom_mcp ──────────\n // 在 gateway_start 阶段检测并写入,保证插件安装/更新后首次启动即生效\n // api.on(\"gateway_start\", async () => {\n // await ensureToolsAlsoAllow(api);\n // });\n\n // 注入媒体发送指令和文件大小限制提示词(仅对企业微信 channel 生效)\n api.on(\"before_prompt_build\", (_event, ctx) => {\n // 只在企业微信 channel 的会话中注入,避免影响其他 channel 插件\n if (ctx?.channelId !== CHANNEL_ID) {\n return;\n }\n return {\n appendSystemContext: [\n \"重要:涉及发送图片/视频/语音/文件给用户时,请务必使用 `MEDIA:` 指令。详见 wecom-send-media 这个 skill(技能)。\",\n \"重要:当需要向用户发送结构化卡片消息(如通知、投票、按钮选择等)时,请在回复中直接输出 JSON 代码块(```json ... ```),其中 card_type 字段标明卡片类型。详见 wecom-send-template-card 技能。\"\n ].join(\"\\n\"),\n };\n });\n },\n};\n\nexport default plugin;\n"],"names":["emptyPluginConfigSchema","setWeComRuntime","wecomPlugin","createWeComMcpTool","CHANNEL_ID"],"mappings":";;;;;;;;;;;;AA4EA,MAAM,MAAM,GAAG;AACb,IAAA,EAAE,EAAE,uBAAuB;AAC3B,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,WAAW,EAAE,kBAAkB;IAC/B,YAAY,EAAEA,sCAAuB,EAAE;AACvC,IAAA,QAAQ,CAAC,GAAsB,EAAA;AAE7B,QAAAC,uBAAe,CAAC,GAAG,CAAC,OAAO,CAAC;QAC5B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAEC,mBAAW,EAAE,CAAC;;AAG5C,QAAA,GAAG,CAAC,YAAY,CAACC,uBAAkB,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;;;;;;;QAS7D,GAAG,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,MAAM,EAAE,GAAG,KAAI;;AAE5C,YAAA,IAAI,GAAG,EAAE,SAAS,KAAKC,iBAAU,EAAE;gBACjC;YACF;YACA,OAAO;AACL,gBAAA,mBAAmB,EAAE;oBACnB,6EAA6E;oBAC7E;iBACD,CAAC,IAAI,CAAC,IAAI,CAAC;aACb;AACH,QAAA,CAAC,CAAC;IACJ,CAAC;;;;;"}
@@ -0,0 +1,339 @@
1
+ 'use strict';
2
+
3
+ var openclawCompat = require('./openclaw-compat.js');
4
+ var runtime = require('./runtime.js');
5
+ var monitor = require('./monitor.js');
6
+ var stateManager = require('./state-manager.js');
7
+ var onboarding = require('./onboarding.js');
8
+ var utils = require('./utils.js');
9
+ var _const = require('./const.js');
10
+ var mediaUploader = require('./media-uploader.js');
11
+
12
+ /**
13
+ * 使用 SDK 的 sendMessage 主动发送企业微信消息
14
+ * 无需依赖 reqId,直接向指定会话推送消息
15
+ */
16
+ async function sendWeComMessage({ to, content, accountId, }) {
17
+ const resolvedAccountId = accountId ?? openclawCompat.DEFAULT_ACCOUNT_ID;
18
+ // 从 to 中提取 chatId(格式是 "${CHANNEL_ID}:chatId" 或直接是 chatId)
19
+ const channelPrefix = new RegExp(`^${_const.CHANNEL_ID}:`, "i");
20
+ const chatId = to.replace(channelPrefix, "");
21
+ // 获取 WSClient 实例
22
+ const wsClient = stateManager.getWeComWebSocket(resolvedAccountId);
23
+ if (!wsClient) {
24
+ throw new Error(`WSClient not connected for account ${resolvedAccountId}`);
25
+ }
26
+ // 使用 SDK 的 sendMessage 主动发送 markdown 消息
27
+ const result = await wsClient.sendMessage(chatId, {
28
+ msgtype: 'markdown',
29
+ markdown: { content },
30
+ });
31
+ const messageId = result?.headers?.req_id ?? `wecom-${Date.now()}`;
32
+ return {
33
+ channel: _const.CHANNEL_ID,
34
+ messageId,
35
+ chatId,
36
+ };
37
+ }
38
+ // 企业微信频道元数据
39
+ const meta = {
40
+ id: _const.CHANNEL_ID,
41
+ label: "企业微信",
42
+ selectionLabel: "企业微信 (WeCom)",
43
+ detailLabel: "企业微信智能机器人",
44
+ docsPath: `/channels/${_const.CHANNEL_ID}`,
45
+ docsLabel: _const.CHANNEL_ID,
46
+ blurb: "企业微信智能机器人接入插件",
47
+ systemImage: "message.fill",
48
+ };
49
+ const wecomPlugin = {
50
+ id: _const.CHANNEL_ID,
51
+ meta: {
52
+ ...meta,
53
+ quickstartAllowFrom: true,
54
+ },
55
+ pairing: {
56
+ idLabel: "wecomUserId",
57
+ normalizeAllowEntry: (entry) => entry.replace(new RegExp(`^(${_const.CHANNEL_ID}|user):`, "i"), "").trim(),
58
+ notifyApproval: async ({ cfg, id }) => {
59
+ // sendWeComMessage({
60
+ // to: id,
61
+ // content: " pairing approved",
62
+ // accountId: cfg.accountId,
63
+ // });
64
+ // Pairing approved for user
65
+ },
66
+ },
67
+ setupWizard: onboarding.wecomSetupWizard,
68
+ setup: onboarding.wecomSetupAdapter,
69
+ capabilities: {
70
+ chatTypes: ["direct", "group"],
71
+ reactions: false,
72
+ threads: false,
73
+ media: true,
74
+ nativeCommands: false,
75
+ blockStreaming: true,
76
+ },
77
+ reload: { configPrefixes: [`channels.${_const.CHANNEL_ID}`] },
78
+ config: {
79
+ // 列出所有账户 ID(最小实现只支持默认账户)
80
+ listAccountIds: () => [openclawCompat.DEFAULT_ACCOUNT_ID],
81
+ // 解析账户配置
82
+ resolveAccount: (cfg) => utils.resolveWeComAccount(cfg),
83
+ // 获取默认账户 ID
84
+ defaultAccountId: () => openclawCompat.DEFAULT_ACCOUNT_ID,
85
+ // 设置账户启用状态
86
+ setAccountEnabled: ({ cfg, enabled }) => {
87
+ const wecomConfig = (cfg.channels?.[_const.CHANNEL_ID] ?? {});
88
+ return {
89
+ ...cfg,
90
+ channels: {
91
+ ...cfg.channels,
92
+ [_const.CHANNEL_ID]: {
93
+ ...wecomConfig,
94
+ enabled,
95
+ },
96
+ },
97
+ };
98
+ },
99
+ // 删除账户
100
+ deleteAccount: ({ cfg }) => {
101
+ const wecomConfig = (cfg.channels?.[_const.CHANNEL_ID] ?? {});
102
+ const { botId, secret, ...rest } = wecomConfig;
103
+ return {
104
+ ...cfg,
105
+ channels: {
106
+ ...cfg.channels,
107
+ [_const.CHANNEL_ID]: rest,
108
+ },
109
+ };
110
+ },
111
+ // 检查是否已配置
112
+ isConfigured: (account) => Boolean(account.botId?.trim() && account.secret?.trim()),
113
+ // 描述账户信息
114
+ describeAccount: (account) => ({
115
+ accountId: account.accountId,
116
+ name: account.name,
117
+ enabled: account.enabled,
118
+ configured: Boolean(account.botId?.trim() && account.secret?.trim()),
119
+ botId: account.botId,
120
+ websocketUrl: account.websocketUrl,
121
+ }),
122
+ // 解析允许来源列表
123
+ resolveAllowFrom: ({ cfg }) => {
124
+ const account = utils.resolveWeComAccount(cfg);
125
+ return (account.config.allowFrom ?? []).map((entry) => String(entry));
126
+ },
127
+ // 格式化允许来源列表
128
+ formatAllowFrom: ({ allowFrom }) => allowFrom
129
+ .map((entry) => String(entry).trim())
130
+ .filter(Boolean),
131
+ },
132
+ security: {
133
+ resolveDmPolicy: ({ account }) => {
134
+ const basePath = `channels.${_const.CHANNEL_ID}.`;
135
+ return {
136
+ policy: account.config.dmPolicy ?? "open",
137
+ allowFrom: account.config.allowFrom ?? [],
138
+ policyPath: `${basePath}dmPolicy`,
139
+ allowFromPath: basePath,
140
+ approveHint: openclawCompat.formatPairingApproveHint(_const.CHANNEL_ID),
141
+ normalizeEntry: (raw) => raw.replace(new RegExp(`^${_const.CHANNEL_ID}:`, "i"), "").trim(),
142
+ };
143
+ },
144
+ collectWarnings: ({ account, cfg }) => {
145
+ const warnings = [];
146
+ // DM 策略警告
147
+ const dmPolicy = account.config.dmPolicy ?? "open";
148
+ if (dmPolicy === "open") {
149
+ const hasWildcard = (account.config.allowFrom ?? []).some((entry) => String(entry).trim() === "*");
150
+ if (!hasWildcard) {
151
+ warnings.push(`- 企业微信私信:dmPolicy="open" 但 allowFrom 未包含 "*"。任何人都可以发消息,但允许列表为空可能导致意外行为。建议设置 channels.${_const.CHANNEL_ID}.allowFrom=["*"] 或使用 dmPolicy="pairing"。`);
152
+ }
153
+ }
154
+ // 群组策略警告
155
+ const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
156
+ const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "open";
157
+ // const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({
158
+ // providerConfigPresent: true,
159
+ // groupPolicy: account.config.groupPolicy,
160
+ // defaultGroupPolicy,
161
+ // });
162
+ if (groupPolicy === "open") {
163
+ warnings.push(`- 企业微信群组:groupPolicy="open" 允许所有群组中的成员触发。设置 channels.${_const.CHANNEL_ID}.groupPolicy="allowlist" + channels.${_const.CHANNEL_ID}.groupAllowFrom 来限制群组。`);
164
+ }
165
+ return warnings;
166
+ },
167
+ },
168
+ messaging: {
169
+ normalizeTarget: (target) => {
170
+ const trimmed = target.trim();
171
+ if (!trimmed)
172
+ return undefined;
173
+ return trimmed;
174
+ },
175
+ targetResolver: {
176
+ looksLikeId: (id) => {
177
+ const trimmed = id?.trim();
178
+ return Boolean(trimmed);
179
+ },
180
+ hint: "<userId|groupId>",
181
+ },
182
+ },
183
+ directory: {
184
+ self: async () => null,
185
+ listPeers: async () => [],
186
+ listGroups: async () => [],
187
+ },
188
+ outbound: {
189
+ deliveryMode: "gateway",
190
+ chunker: (text, limit) => runtime.getWeComRuntime().channel.text.chunkMarkdownText(text, limit),
191
+ textChunkLimit: _const.TEXT_CHUNK_LIMIT,
192
+ sendText: async ({ to, text, accountId }) => {
193
+ return sendWeComMessage({ to, content: text, accountId: accountId ?? undefined });
194
+ },
195
+ sendMedia: async ({ to, text, mediaUrl, mediaLocalRoots, accountId }) => {
196
+ const resolvedAccountId = accountId ?? openclawCompat.DEFAULT_ACCOUNT_ID;
197
+ const channelPrefix = new RegExp(`^${_const.CHANNEL_ID}:`, "i");
198
+ const chatId = to.replace(channelPrefix, "");
199
+ // 获取 WSClient 实例
200
+ const wsClient = stateManager.getWeComWebSocket(resolvedAccountId);
201
+ if (!wsClient) {
202
+ throw new Error(`WSClient not connected for account ${resolvedAccountId}`);
203
+ }
204
+ // 如果没有 mediaUrl,fallback 为纯文本
205
+ if (!mediaUrl) {
206
+ return sendWeComMessage({ to, content: text || "", accountId: resolvedAccountId });
207
+ }
208
+ const result = await mediaUploader.uploadAndSendMedia({
209
+ wsClient,
210
+ mediaUrl,
211
+ chatId,
212
+ mediaLocalRoots,
213
+ });
214
+ if (result.rejected) {
215
+ return sendWeComMessage({ to, content: `⚠️ ${result.rejectReason}`, accountId: resolvedAccountId });
216
+ }
217
+ if (!result.ok) {
218
+ // 上传/发送失败,fallback 为文本 + URL
219
+ const fallbackContent = text
220
+ ? `${text}\n📎 ${mediaUrl}`
221
+ : `📎 ${mediaUrl}`;
222
+ return sendWeComMessage({ to, content: fallbackContent, accountId: resolvedAccountId });
223
+ }
224
+ // 如有伴随文本,额外发送一条 markdown
225
+ if (text) {
226
+ await sendWeComMessage({ to, content: text, accountId: resolvedAccountId });
227
+ }
228
+ // 如果有降级说明,额外发送提示
229
+ if (result.downgradeNote) {
230
+ await sendWeComMessage({ to, content: `ℹ️ ${result.downgradeNote}`, accountId: resolvedAccountId });
231
+ }
232
+ return {
233
+ channel: _const.CHANNEL_ID,
234
+ messageId: result.messageId,
235
+ chatId,
236
+ };
237
+ },
238
+ },
239
+ status: {
240
+ defaultRuntime: {
241
+ accountId: openclawCompat.DEFAULT_ACCOUNT_ID,
242
+ running: false,
243
+ lastStartAt: null,
244
+ lastStopAt: null,
245
+ lastError: null,
246
+ },
247
+ collectStatusIssues: (accounts) => accounts.flatMap((entry) => {
248
+ const accountId = String(entry.accountId ?? openclawCompat.DEFAULT_ACCOUNT_ID);
249
+ const enabled = entry.enabled !== false;
250
+ const configured = entry.configured === true;
251
+ if (!enabled) {
252
+ return [];
253
+ }
254
+ const issues = [];
255
+ if (!configured) {
256
+ issues.push({
257
+ channel: _const.CHANNEL_ID,
258
+ accountId,
259
+ kind: "config",
260
+ message: "企业微信机器人 ID 或 Secret 未配置",
261
+ fix: "Run: openclaw channels add wecom --bot-id <id> --secret <secret>",
262
+ });
263
+ }
264
+ return issues;
265
+ }),
266
+ buildChannelSummary: ({ snapshot }) => ({
267
+ configured: snapshot.configured ?? false,
268
+ running: snapshot.running ?? false,
269
+ lastStartAt: snapshot.lastStartAt ?? null,
270
+ lastStopAt: snapshot.lastStopAt ?? null,
271
+ lastError: snapshot.lastError ?? null,
272
+ }),
273
+ probeAccount: async () => {
274
+ return { ok: true, status: 200 };
275
+ },
276
+ buildAccountSnapshot: ({ account, runtime }) => {
277
+ const configured = Boolean(account.botId?.trim() &&
278
+ account.secret?.trim());
279
+ return {
280
+ accountId: account.accountId,
281
+ name: account.name,
282
+ enabled: account.enabled,
283
+ configured,
284
+ running: runtime?.running ?? false,
285
+ lastStartAt: runtime?.lastStartAt ?? null,
286
+ lastStopAt: runtime?.lastStopAt ?? null,
287
+ lastError: runtime?.lastError ?? null,
288
+ };
289
+ },
290
+ },
291
+ gateway: {
292
+ startAccount: async (ctx) => {
293
+ const account = ctx.account;
294
+ // 启动 WebSocket 监听
295
+ return monitor.monitorWeComProvider({
296
+ account,
297
+ config: ctx.cfg,
298
+ runtime: ctx.runtime,
299
+ abortSignal: ctx.abortSignal,
300
+ setStatus: ctx.setStatus,
301
+ });
302
+ },
303
+ logoutAccount: async ({ cfg }) => {
304
+ const nextCfg = { ...cfg };
305
+ const wecomConfig = (cfg.channels?.[_const.CHANNEL_ID] ?? {});
306
+ const nextWecom = { ...wecomConfig };
307
+ let cleared = false;
308
+ let changed = false;
309
+ if (nextWecom.botId || nextWecom.secret) {
310
+ delete nextWecom.botId;
311
+ delete nextWecom.secret;
312
+ cleared = true;
313
+ changed = true;
314
+ }
315
+ if (changed) {
316
+ if (Object.keys(nextWecom).length > 0) {
317
+ nextCfg.channels = { ...nextCfg.channels, [_const.CHANNEL_ID]: nextWecom };
318
+ }
319
+ else {
320
+ const nextChannels = { ...nextCfg.channels };
321
+ delete nextChannels[_const.CHANNEL_ID];
322
+ if (Object.keys(nextChannels).length > 0) {
323
+ nextCfg.channels = nextChannels;
324
+ }
325
+ else {
326
+ delete nextCfg.channels;
327
+ }
328
+ }
329
+ await runtime.getWeComRuntime().config.writeConfigFile(nextCfg);
330
+ }
331
+ const resolved = utils.resolveWeComAccount(changed ? nextCfg : cfg);
332
+ const loggedOut = !resolved.botId && !resolved.secret;
333
+ return { cleared, envToken: false, loggedOut };
334
+ },
335
+ },
336
+ };
337
+
338
+ exports.wecomPlugin = wecomPlugin;
339
+ //# sourceMappingURL=channel.js.map