@yanhaidao/wecom 2.2.5 → 2.2.28
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/.claude/settings.local.json +11 -0
- package/.github/workflows/release.yml +56 -0
- package/CLAUDE.md +238 -0
- package/GOVERNANCE.md +26 -0
- package/LICENSE +7 -0
- package/README.md +279 -87
- package/assets/01.bot-add.png +0 -0
- package/assets/01.bot-setp2.png +0 -0
- package/assets/02.agent.add.png +0 -0
- package/assets/02.agent.api-set.png +0 -0
- package/changelog/v2.2.28.md +70 -0
- package/compat-single-account.md +118 -0
- package/package.json +11 -3
- package/src/accounts.ts +17 -55
- package/src/agent/api-client.ts +8 -3
- package/src/agent/handler.event-filter.test.ts +50 -0
- package/src/agent/handler.ts +162 -139
- package/src/channel.config.test.ts +147 -0
- package/src/channel.lifecycle.test.ts +234 -0
- package/src/channel.ts +90 -140
- package/src/config/accounts.resolve.test.ts +38 -0
- package/src/config/accounts.ts +257 -22
- package/src/config/index.ts +6 -0
- package/src/config/routing.test.ts +88 -0
- package/src/config/routing.ts +26 -0
- package/src/config/schema.ts +52 -4
- package/src/config-schema.ts +5 -41
- package/src/dynamic-agent.account-scope.test.ts +17 -0
- package/src/dynamic-agent.ts +178 -0
- package/src/gateway-monitor.ts +200 -0
- package/src/monitor/state.queue.test.ts +1 -1
- package/src/monitor/state.ts +1 -1
- package/src/monitor/types.ts +1 -1
- package/src/monitor.active.test.ts +6 -3
- package/src/monitor.inbound-filter.test.ts +63 -0
- package/src/monitor.ts +482 -53
- package/src/monitor.webhook.test.ts +288 -3
- package/src/outbound.test.ts +130 -0
- package/src/outbound.ts +38 -9
- package/src/shared/command-auth.ts +4 -2
- package/src/shared/xml-parser.test.ts +21 -1
- package/src/shared/xml-parser.ts +18 -0
- package/src/types/account.ts +43 -14
- package/src/types/config.ts +51 -2
- package/src/types/index.ts +3 -0
- package/src/types.ts +29 -147
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Release & Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
# 🌟 必须与 npm 设置的 Environment 一致
|
|
12
|
+
environment: release
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write # 发布 Release
|
|
15
|
+
id-token: write # 获取 OIDC 身份令牌
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout Code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Node.js
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
# npm Trusted Publishing (OIDC) requires npm >=11.5.1.
|
|
24
|
+
# Use Node 24 to get modern npm in hosted runners.
|
|
25
|
+
node-version: '24'
|
|
26
|
+
registry-url: 'https://registry.npmjs.org'
|
|
27
|
+
|
|
28
|
+
- name: Show runtime versions
|
|
29
|
+
run: |
|
|
30
|
+
node -v
|
|
31
|
+
npm -v
|
|
32
|
+
|
|
33
|
+
- name: Ensure npm trusted publishing support
|
|
34
|
+
run: |
|
|
35
|
+
npm i -g npm@^11.5.1
|
|
36
|
+
npm -v
|
|
37
|
+
|
|
38
|
+
- name: Install Dependencies
|
|
39
|
+
run: npm install --legacy-peer-deps || npm install
|
|
40
|
+
|
|
41
|
+
- name: Build Plugin
|
|
42
|
+
run: npm run build --if-present
|
|
43
|
+
|
|
44
|
+
# 🚀 自动发布到 npm (使用 OIDC 自动授权,无需 Token)
|
|
45
|
+
- name: Publish to npm
|
|
46
|
+
run: npm publish --access public
|
|
47
|
+
|
|
48
|
+
# 自动同步发布 GitHub Release
|
|
49
|
+
- name: Create GitHub Release
|
|
50
|
+
uses: softprops/action-gh-release@v2
|
|
51
|
+
with:
|
|
52
|
+
body_path: changelog/${{ github.ref_name }}.md
|
|
53
|
+
draft: false
|
|
54
|
+
prerelease: false
|
|
55
|
+
env:
|
|
56
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is an **OpenClaw Channel Plugin** for WeCom (企业微信 / WeChat Work). It enables AI bot integration with enterprise WeChat through a dual-mode architecture.
|
|
8
|
+
|
|
9
|
+
- **Package**: `@yanhaidao/wecom`
|
|
10
|
+
- **Type**: ES Module (NodeNext)
|
|
11
|
+
- **Entry**: `index.ts`
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
### Dual-Mode Design (Bot + Agent)
|
|
16
|
+
|
|
17
|
+
The plugin implements a unique dual-mode architecture:
|
|
18
|
+
|
|
19
|
+
| Mode | Purpose | Webhook Path | Capabilities |
|
|
20
|
+
|------|---------|--------------|--------------|
|
|
21
|
+
| **Bot** (智能体) | Real-time streaming chat | `/wecom`, `/wecom/bot` | Streaming responses, low latency, text/image only |
|
|
22
|
+
| **Agent** (自建应用) | Fallback & broadcast | `/wecom/agent` | File sending, broadcasts, long tasks (>6min) |
|
|
23
|
+
|
|
24
|
+
**Key Design Principle**: Bot is preferred for conversations; Agent is used as fallback when Bot cannot deliver (files, timeouts) or for proactive broadcasts.
|
|
25
|
+
|
|
26
|
+
### Core Components
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
index.ts # Plugin entry - registers channel and HTTP handlers
|
|
30
|
+
src/
|
|
31
|
+
channel.ts # ChannelPlugin implementation, lifecycle management
|
|
32
|
+
monitor.ts # Core webhook handler, message flow, stream state
|
|
33
|
+
runtime.ts # Runtime state singleton
|
|
34
|
+
http.ts # HTTP client with undici + proxy support
|
|
35
|
+
crypto.ts # AES-CBC encryption/decryption for webhooks
|
|
36
|
+
media.ts # Media file download/decryption
|
|
37
|
+
outbound.ts # Outbound message adapter
|
|
38
|
+
target.ts # Target resolution (user/party/tag/chat)
|
|
39
|
+
dynamic-agent.ts # Dynamic agent routing (per-user/per-group isolation)
|
|
40
|
+
agent/
|
|
41
|
+
api-client.ts # WeCom API client with AccessToken caching
|
|
42
|
+
handler.ts # XML webhook handler for Agent mode
|
|
43
|
+
config/
|
|
44
|
+
schema.ts # Zod schemas for configuration
|
|
45
|
+
monitor/
|
|
46
|
+
state.ts # StreamStore and ActiveReplyStore with TTL pruning
|
|
47
|
+
types/constants.ts # API endpoints and limits
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Stream State Management
|
|
51
|
+
|
|
52
|
+
The plugin uses a sophisticated stream state system (`src/monitor/state.ts`):
|
|
53
|
+
|
|
54
|
+
- **StreamStore**: Manages message streams with 6-minute timeout window
|
|
55
|
+
- **ActiveReplyStore**: Tracks `response_url` for proactive pushes
|
|
56
|
+
- **Pending Queue**: Debounces rapid messages (500ms default)
|
|
57
|
+
- **Message Deduplication**: Uses `msgid` to prevent duplicate processing
|
|
58
|
+
|
|
59
|
+
### Token Management
|
|
60
|
+
|
|
61
|
+
Agent mode uses automatic AccessToken caching (`src/agent/api-client.ts`):
|
|
62
|
+
- Token cached with 60-second refresh buffer
|
|
63
|
+
- Automatic retry on expiration
|
|
64
|
+
- Thread-safe refresh deduplication
|
|
65
|
+
|
|
66
|
+
## Development Commands
|
|
67
|
+
|
|
68
|
+
### Testing
|
|
69
|
+
|
|
70
|
+
This project uses **Vitest**. Tests extend from a base config at `../../vitest.config.ts`:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Run all tests
|
|
74
|
+
npx vitest --config vitest.config.ts
|
|
75
|
+
|
|
76
|
+
# Run specific test file
|
|
77
|
+
npx vitest --config vitest.config.ts src/crypto.test.ts
|
|
78
|
+
|
|
79
|
+
# Run tests matching pattern
|
|
80
|
+
npx vitest --config vitest.config.ts --testNamePattern="should encrypt"
|
|
81
|
+
|
|
82
|
+
# Watch mode
|
|
83
|
+
npx vitest --config vitest.config.ts --watch
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Test files are located alongside source files with `.test.ts` suffix:
|
|
87
|
+
- `src/crypto.test.ts`
|
|
88
|
+
- `src/monitor.integration.test.ts`
|
|
89
|
+
- `src/monitor/state.queue.test.ts`
|
|
90
|
+
- etc.
|
|
91
|
+
|
|
92
|
+
### Type Checking
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx tsc --noEmit
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Build
|
|
99
|
+
|
|
100
|
+
The plugin is loaded directly as TypeScript by OpenClaw. No build step is required for development, but type checking is recommended.
|
|
101
|
+
|
|
102
|
+
## Configuration Schema
|
|
103
|
+
|
|
104
|
+
Configuration is validated via Zod (`src/config/schema.ts`):
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
{
|
|
108
|
+
enabled: boolean,
|
|
109
|
+
bot: {
|
|
110
|
+
token: string, // Bot webhook token
|
|
111
|
+
encodingAESKey: string, // AES encryption key
|
|
112
|
+
receiveId: string?, // Optional receive ID
|
|
113
|
+
streamPlaceholderContent: string?, // "Thinking..."
|
|
114
|
+
welcomeText: string?,
|
|
115
|
+
dm: { policy, allowFrom }
|
|
116
|
+
},
|
|
117
|
+
agent: {
|
|
118
|
+
corpId: string,
|
|
119
|
+
corpSecret: string,
|
|
120
|
+
agentId: number,
|
|
121
|
+
token: string, // Callback token
|
|
122
|
+
encodingAESKey: string, // Callback AES key
|
|
123
|
+
welcomeText: string?,
|
|
124
|
+
dm: { policy, allowFrom }
|
|
125
|
+
},
|
|
126
|
+
network: {
|
|
127
|
+
egressProxyUrl: string? // For dynamic IP scenarios
|
|
128
|
+
},
|
|
129
|
+
media: {
|
|
130
|
+
maxBytes: number? // Default 25MB
|
|
131
|
+
},
|
|
132
|
+
dynamicAgents: {
|
|
133
|
+
enabled: boolean? // Enable per-user/per-group agents
|
|
134
|
+
dmCreateAgent: boolean? // Create agent per DM user
|
|
135
|
+
groupEnabled: boolean? // Enable for group chats
|
|
136
|
+
adminUsers: string[]? // Admin users (bypass dynamic routing)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Dynamic Agent Routing
|
|
142
|
+
|
|
143
|
+
When `dynamicAgents.enabled` is `true`, the plugin automatically creates isolated Agent instances for each user/group:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Enable dynamic agents
|
|
147
|
+
openclaw config set channels.wecom.dynamicAgents.enabled true
|
|
148
|
+
|
|
149
|
+
# Configure admin users (use main agent)
|
|
150
|
+
openclaw config set channels.wecom.dynamicAgents.adminUsers '["admin1","admin2"]'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Generated Agent ID format**: `wecom-{type}-{peerId}`
|
|
154
|
+
- DM: `wecom-dm-zhangsan`
|
|
155
|
+
- Group: `wecom-group-wr123456`
|
|
156
|
+
|
|
157
|
+
Dynamic agents are automatically added to `agents.list` in the config file.
|
|
158
|
+
|
|
159
|
+
## Key Technical Details
|
|
160
|
+
|
|
161
|
+
### Webhook Security
|
|
162
|
+
|
|
163
|
+
- **Signature Verification**: HMAC-SHA256 with token
|
|
164
|
+
- **Encryption**: AES-CBC with PKCS#7 padding (32-byte blocks)
|
|
165
|
+
- **Paths**: `/wecom` (legacy), `/wecom/bot`, `/wecom/agent`
|
|
166
|
+
|
|
167
|
+
### Timeout Handling
|
|
168
|
+
|
|
169
|
+
Bot mode has a 6-minute window (360s) for streaming responses. The plugin:
|
|
170
|
+
- Tracks deadline: `createdAt + 6 * 60 * 1000`
|
|
171
|
+
- Switches to Agent fallback at `deadline - 30s` margin
|
|
172
|
+
- Sends DM via Agent for remaining content
|
|
173
|
+
|
|
174
|
+
### Media Handling
|
|
175
|
+
|
|
176
|
+
- **Inbound**: Decrypts WeCom encrypted media URLs
|
|
177
|
+
- **Outbound Images**: Base64 encoded via `msg_item` in stream
|
|
178
|
+
- **Outbound Files**: Requires Agent mode, sent via `media/upload` + `message/send`
|
|
179
|
+
- **Max Size**: 25MB default (configurable via `channels.wecom.media.maxBytes`)
|
|
180
|
+
|
|
181
|
+
### Proxy Support
|
|
182
|
+
|
|
183
|
+
For servers with dynamic IPs (common error: `60020 not allow to access from your ip`):
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
openclaw config set channels.wecom.network.egressProxyUrl "http://proxy.company.local:3128"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Testing Notes
|
|
190
|
+
|
|
191
|
+
- Tests use Vitest with `../../vitest.config.ts` as base
|
|
192
|
+
- Integration tests mock WeCom API responses
|
|
193
|
+
- Crypto tests verify AES encryption round-trips
|
|
194
|
+
- Monitor tests cover stream state transitions and queue behavior
|
|
195
|
+
|
|
196
|
+
## Common Patterns
|
|
197
|
+
|
|
198
|
+
### Adding a New Message Type Handler
|
|
199
|
+
|
|
200
|
+
1. Update `buildInboundBody()` in `src/monitor.ts` to parse the message
|
|
201
|
+
2. Add type definitions in `src/types/message.ts`
|
|
202
|
+
3. Update `processInboundMessage()` if media handling is needed
|
|
203
|
+
|
|
204
|
+
### Agent API Calls
|
|
205
|
+
|
|
206
|
+
Always use `api-client.ts` methods which handle token management:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { sendText, uploadMedia } from "./agent/api-client.js";
|
|
210
|
+
|
|
211
|
+
// Token is automatically cached and refreshed
|
|
212
|
+
await sendText({ agent, toUser: "userid", text: "Hello" });
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Stream Content Updates
|
|
216
|
+
|
|
217
|
+
Use `streamStore.updateStream()` for thread-safe updates:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
streamStore.updateStream(streamId, (state) => {
|
|
221
|
+
state.content = "new content";
|
|
222
|
+
state.finished = true;
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Dependencies
|
|
227
|
+
|
|
228
|
+
- `undici`: HTTP client with proxy support
|
|
229
|
+
- `fast-xml-parser`: XML parsing for Agent callbacks
|
|
230
|
+
- `zod`: Configuration validation
|
|
231
|
+
- `openclaw`: Peer dependency (>=2026.2.24)
|
|
232
|
+
|
|
233
|
+
## WeCom API Endpoints Used
|
|
234
|
+
|
|
235
|
+
- `GET_TOKEN`: `https://qyapi.weixin.qq.com/cgi-bin/gettoken`
|
|
236
|
+
- `SEND_MESSAGE`: `https://qyapi.weixin.qq.com/cgi-bin/message/send`
|
|
237
|
+
- `UPLOAD_MEDIA`: `https://qyapi.weixin.qq.com/cgi-bin/media/upload`
|
|
238
|
+
- `DOWNLOAD_MEDIA`: `https://qyapi.weixin.qq.com/cgi-bin/media/get`
|
package/GOVERNANCE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Governance
|
|
2
|
+
|
|
3
|
+
This document clarifies project ownership and collaboration expectations so contributors can work together with fewer ambiguities.
|
|
4
|
+
|
|
5
|
+
## 1. Upstream ownership
|
|
6
|
+
- This repository is the **Upstream / Source of Truth** for the OpenClaw WeCom plugin.
|
|
7
|
+
- **Author & Lead Maintainer:** YanHaidao (GitHub: YanHaidao).
|
|
8
|
+
|
|
9
|
+
## 2. Co-maintenance model
|
|
10
|
+
- Tencent Cloud contributors are welcome as **co-maintainers** for code, docs, testing, and cloud deployment adaptation.
|
|
11
|
+
- Tencent Cloud may host an official mirror repository for sync and downstream integration needs.
|
|
12
|
+
|
|
13
|
+
## 3. Decision-making
|
|
14
|
+
- We prefer discussion and consensus on non-trivial changes.
|
|
15
|
+
- If consensus is not reached in time, the Lead Maintainer makes the final upstream decision for roadmap, architecture, and release direction.
|
|
16
|
+
|
|
17
|
+
## 4. Contribution workflow
|
|
18
|
+
- Non-trivial changes should be proposed via Pull Request.
|
|
19
|
+
- Keep change scope clear, include test notes when relevant, and document behavior changes.
|
|
20
|
+
|
|
21
|
+
## 5. Mirrors and downstream adaptations
|
|
22
|
+
- Mirrors may carry downstream patches (for example deployment integration or cloud templates).
|
|
23
|
+
- Mirrors/downstream repositories should keep attribution in README or NOTICE:
|
|
24
|
+
- Upstream source: this repository
|
|
25
|
+
- Author: YanHaidao
|
|
26
|
+
- Co-maintained with Tencent Cloud contributors
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License (ISC)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|