@cloudbase/agent-adapter-wx 0.0.18 → 0.0.19
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 +165 -117
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,35 +5,62 @@ WeChat adapter for AG-Kit - Forward LLM messages to WeChat platforms
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
✅ **Multi-Platform Support**: WeChat Mini Program, Official Account (Service/Subscription), Enterprise WeChat Customer Service
|
|
8
|
+
✅ **Express Handler**: Ready-to-use `createWxMessageHandler` for Express servers
|
|
9
|
+
✅ **Agent Adapter**: `WeChatAgent` wraps any AG-UI agent with WeChat integration
|
|
10
|
+
✅ **CloudBase History**: `WeChatHistoryManager` with CloudBase database storage
|
|
8
11
|
✅ **Automatic Token Management**: Built-in caching and refresh mechanism
|
|
9
|
-
✅ **Type-Safe**: Full TypeScript support
|
|
10
|
-
✅ **
|
|
11
|
-
✅ **Batch Operations**: Send multiple messages at once
|
|
12
|
-
✅ **Async Support**: Fire-and-forget message sending
|
|
12
|
+
✅ **Type-Safe**: Full TypeScript support
|
|
13
|
+
✅ **Async/Sync Reply**: Supports both sync HTTP response and async message sending
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
16
17
|
```bash
|
|
17
18
|
npm install @cloudbase/agent-adapter-wx
|
|
18
19
|
# or
|
|
19
|
-
yarn add @cloudbase/agent-adapter-wx
|
|
20
|
-
# or
|
|
21
20
|
pnpm add @cloudbase/agent-adapter-wx
|
|
22
21
|
```
|
|
23
22
|
|
|
24
23
|
## Quick Start
|
|
25
24
|
|
|
25
|
+
### Express Server Handler
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import express from "express";
|
|
29
|
+
import { createWxMessageHandler, WeChatAgent } from "@cloudbase/agent-adapter-wx";
|
|
30
|
+
|
|
31
|
+
const app = express();
|
|
32
|
+
app.use(express.json());
|
|
33
|
+
|
|
34
|
+
// Create handler with agent factory
|
|
35
|
+
const wxHandler = createWxMessageHandler(
|
|
36
|
+
async ({ request, options }) => {
|
|
37
|
+
const agent = new WeChatAgent({
|
|
38
|
+
agent: yourBaseAgent,
|
|
39
|
+
wechatConfig: {
|
|
40
|
+
platform: WeChatPlatform.SERVICE,
|
|
41
|
+
appId: process.env.WECHAT_APP_ID!,
|
|
42
|
+
appSecret: process.env.WECHAT_APP_SECRET!,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
return { agent };
|
|
46
|
+
},
|
|
47
|
+
{ logger: console }
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
app.post("/wx/message", wxHandler);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Direct Message Sending
|
|
54
|
+
|
|
26
55
|
```typescript
|
|
27
56
|
import { WeChatSender, WeChatPlatform } from "@cloudbase/agent-adapter-wx";
|
|
28
57
|
|
|
29
|
-
// Initialize sender
|
|
30
58
|
const sender = new WeChatSender({
|
|
31
59
|
platform: WeChatPlatform.MINI_APP,
|
|
32
60
|
appId: "your-app-id",
|
|
33
61
|
appSecret: "your-app-secret",
|
|
34
62
|
});
|
|
35
63
|
|
|
36
|
-
// Send a message
|
|
37
64
|
await sender.send({
|
|
38
65
|
toUser: "user-openid",
|
|
39
66
|
message: {
|
|
@@ -43,38 +70,89 @@ await sender.send({
|
|
|
43
70
|
});
|
|
44
71
|
```
|
|
45
72
|
|
|
46
|
-
##
|
|
73
|
+
## Core Components
|
|
47
74
|
|
|
48
|
-
###
|
|
75
|
+
### createWxMessageHandler
|
|
76
|
+
|
|
77
|
+
Express route handler for WeChat message webhook. Handles the complete message flow:
|
|
78
|
+
|
|
79
|
+
1. Parse incoming WeChat message
|
|
80
|
+
2. Validate message type (text/voice)
|
|
81
|
+
3. Handle retry logic for unverified accounts (11-second rule)
|
|
82
|
+
4. Process "继续" command for async replies
|
|
83
|
+
5. Run agent and return response
|
|
49
84
|
|
|
50
85
|
```typescript
|
|
51
|
-
import {
|
|
86
|
+
import { createWxMessageHandler } from "@cloudbase/agent-adapter-wx";
|
|
52
87
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
platform: WeChatPlatform.MINI_APP,
|
|
56
|
-
appId: process.env.WECHAT_APP_ID!,
|
|
57
|
-
appSecret: process.env.WECHAT_APP_SECRET!,
|
|
88
|
+
const handler = createWxMessageHandler(createAgent, {
|
|
89
|
+
logger: console, // optional logger
|
|
58
90
|
});
|
|
59
91
|
|
|
60
|
-
|
|
61
|
-
|
|
92
|
+
app.post("/wx/callback", handler);
|
|
93
|
+
```
|
|
62
94
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
**Bot ID Resolution Priority:**
|
|
96
|
+
1. `SCF_FUNCTIONNAME` environment variable
|
|
97
|
+
2. Parsed from request URL via `utils.parseBotId()`
|
|
98
|
+
3. Parsed from `HOSTNAME` environment variable
|
|
99
|
+
4. Fallback: `"agent-id"`
|
|
100
|
+
|
|
101
|
+
### WeChatAgent
|
|
102
|
+
|
|
103
|
+
Wraps any AG-UI `AbstractAgent` with WeChat-specific functionality:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { WeChatAgent, WeChatPlatform, WeChatSendMode } from "@cloudbase/agent-adapter-wx";
|
|
107
|
+
|
|
108
|
+
const wechatAgent = new WeChatAgent({
|
|
109
|
+
agent: yourBaseAgent,
|
|
110
|
+
wechatConfig: {
|
|
111
|
+
platform: WeChatPlatform.SERVICE,
|
|
112
|
+
appId: "your-app-id",
|
|
113
|
+
appSecret: "your-app-secret",
|
|
114
|
+
sendMode: WeChatSendMode.AITOOLS, // or WeChatSendMode.LOCAL
|
|
69
115
|
},
|
|
70
|
-
recommendQuestions: [
|
|
71
|
-
"What about tomorrow?",
|
|
72
|
-
"Show me the forecast",
|
|
73
|
-
"Temperature details",
|
|
74
|
-
],
|
|
116
|
+
recommendQuestions: ["Question 1", "Question 2"],
|
|
75
117
|
});
|
|
76
118
|
```
|
|
77
119
|
|
|
120
|
+
### WeChatHistoryManager
|
|
121
|
+
|
|
122
|
+
CloudBase database storage for chat history (singleton pattern):
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { WeChatHistoryManager } from "@cloudbase/agent-adapter-wx";
|
|
126
|
+
|
|
127
|
+
// Initialize (requires TCB_ENV or CLOUDBASE_ENV)
|
|
128
|
+
const historyManager = WeChatHistoryManager.getInstance({
|
|
129
|
+
envId: "your-cloudbase-env", // or set TCB_ENV env var
|
|
130
|
+
collectionName: "wx_chat_history", // default
|
|
131
|
+
botId: "your-bot-id",
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Save to history
|
|
135
|
+
await historyManager.saveToHistory({
|
|
136
|
+
messageId: "msg-123",
|
|
137
|
+
role: "user",
|
|
138
|
+
content: "Hello",
|
|
139
|
+
threadId: "conversation-id",
|
|
140
|
+
createdAt: Date.now(),
|
|
141
|
+
metadata: {
|
|
142
|
+
sender: "user-openid",
|
|
143
|
+
triggerSrc: "WXService",
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Get previous reply
|
|
148
|
+
const previous = await historyManager.getPreviousReply(
|
|
149
|
+
"conversation-id",
|
|
150
|
+
"msg-123"
|
|
151
|
+
);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Usage Examples
|
|
155
|
+
|
|
78
156
|
### Enterprise WeChat Customer Service
|
|
79
157
|
|
|
80
158
|
```typescript
|
|
@@ -98,124 +176,94 @@ await sender.send({
|
|
|
98
176
|
### Batch Sending
|
|
99
177
|
|
|
100
178
|
```typescript
|
|
101
|
-
const
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
toUser: "user2",
|
|
108
|
-
message: { content: "Message 2", type: "text" as const },
|
|
109
|
-
},
|
|
110
|
-
];
|
|
111
|
-
|
|
112
|
-
const results = await sender.sendBatch(messages);
|
|
113
|
-
console.log(results);
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Async Sending (Fire and Forget)
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
// Send without waiting for response
|
|
120
|
-
sender.sendAsync({
|
|
121
|
-
toUser: "user-openid",
|
|
122
|
-
message: {
|
|
123
|
-
content: "Notification message",
|
|
124
|
-
type: "text",
|
|
125
|
-
},
|
|
126
|
-
});
|
|
179
|
+
const results = await sender.sendBatch([
|
|
180
|
+
{ toUser: "user1", message: { content: "Message 1", type: "text" } },
|
|
181
|
+
{ toUser: "user2", message: { content: "Message 2", type: "text" } },
|
|
182
|
+
]);
|
|
127
183
|
```
|
|
128
184
|
|
|
129
185
|
## API Reference
|
|
130
186
|
|
|
131
|
-
### WeChatSender
|
|
132
|
-
|
|
133
|
-
#### Constructor
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
new WeChatSender(config: WeChatConfig)
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
#### Methods
|
|
140
|
-
|
|
141
|
-
- `send(options: SendMessageOptions, originMsg?: any): Promise<WeChatAPIResponse>`
|
|
142
|
-
- `sendBatch(messages: SendMessageOptions[]): Promise<WeChatAPIResponse[]>`
|
|
143
|
-
- `sendAsync(options: SendMessageOptions, originMsg?: any): Promise<void>`
|
|
144
|
-
- `clearCache(): void`
|
|
145
|
-
- `getConfig(): WeChatConfig`
|
|
146
|
-
|
|
147
187
|
### Types
|
|
148
188
|
|
|
149
189
|
#### WeChatPlatform
|
|
150
190
|
|
|
151
191
|
```typescript
|
|
152
192
|
enum WeChatPlatform {
|
|
153
|
-
CUSTOM_SERVICE = "WXCustomerService",
|
|
154
|
-
MINI_APP = "
|
|
155
|
-
SERVICE = "WXService",
|
|
156
|
-
SUBSCRIPTION = "WXSubscription",
|
|
193
|
+
CUSTOM_SERVICE = "WXCustomerService", // 企业微信客服
|
|
194
|
+
MINI_APP = "WXMiniApp", // 微信小程序
|
|
195
|
+
SERVICE = "WXService", // 微信服务号
|
|
196
|
+
SUBSCRIPTION = "WXSubscription", // 微信订阅号
|
|
157
197
|
}
|
|
158
198
|
```
|
|
159
199
|
|
|
160
|
-
####
|
|
200
|
+
#### WeChatSendMode
|
|
161
201
|
|
|
162
202
|
```typescript
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
appSecret: string;
|
|
167
|
-
openKfId?: string;
|
|
168
|
-
tokenCacheTTL?: number; // default: 7200000 (2 hours)
|
|
203
|
+
enum WeChatSendMode {
|
|
204
|
+
LOCAL = "local", // Use WeChatSender (local development)
|
|
205
|
+
AITOOLS = "aitools", // Use aitools SDK (cloud function)
|
|
169
206
|
}
|
|
170
207
|
```
|
|
171
208
|
|
|
172
|
-
####
|
|
209
|
+
#### HistoryEntry
|
|
173
210
|
|
|
174
211
|
```typescript
|
|
175
|
-
interface
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
212
|
+
interface HistoryEntry {
|
|
213
|
+
id?: string;
|
|
214
|
+
messageId: string;
|
|
215
|
+
role: "user" | "assistant" | "system";
|
|
216
|
+
content?: string;
|
|
217
|
+
threadId: string;
|
|
218
|
+
createdAt: number;
|
|
219
|
+
botId?: string;
|
|
220
|
+
runId?: string;
|
|
221
|
+
needAsyncReply?: boolean;
|
|
222
|
+
status?: string;
|
|
223
|
+
type?: string;
|
|
224
|
+
metadata?: {
|
|
225
|
+
sender?: string;
|
|
226
|
+
triggerSrc?: string;
|
|
227
|
+
originMsg?: string;
|
|
228
|
+
replyTo?: string;
|
|
229
|
+
reply?: string;
|
|
230
|
+
image?: string;
|
|
231
|
+
recommendQuestions?: string[];
|
|
181
232
|
};
|
|
182
|
-
recommendQuestions?: string[];
|
|
183
|
-
msgId?: string;
|
|
184
233
|
}
|
|
185
234
|
```
|
|
186
235
|
|
|
187
|
-
|
|
236
|
+
### WeChatSender Methods
|
|
188
237
|
|
|
189
|
-
|
|
190
|
-
-
|
|
191
|
-
-
|
|
238
|
+
- `send(options, originMsg?): Promise<WeChatAPIResponse>`
|
|
239
|
+
- `sendBatch(messages): Promise<WeChatAPIResponse[]>`
|
|
240
|
+
- `sendAsync(options, originMsg?): Promise<void>`
|
|
241
|
+
- `clearCache(): void`
|
|
192
242
|
|
|
193
|
-
###
|
|
194
|
-
- Uses customer service message API
|
|
195
|
-
- User must follow the account
|
|
243
|
+
### WeChatHistoryManager Methods
|
|
196
244
|
|
|
197
|
-
|
|
198
|
-
-
|
|
199
|
-
-
|
|
245
|
+
- `getInstance(config?): WeChatHistoryManager` - Get singleton instance
|
|
246
|
+
- `getPreviousReply(threadId, messageId?): Promise<HistoryEntry | null>`
|
|
247
|
+
- `saveToHistory(record): Promise<void>`
|
|
248
|
+
- `updateContent(threadId, messageId, content): Promise<void>`
|
|
200
249
|
|
|
201
|
-
##
|
|
250
|
+
## Environment Variables
|
|
202
251
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
});
|
|
209
|
-
} catch (error) {
|
|
210
|
-
console.error("Failed to send message:", error);
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## License
|
|
252
|
+
| Variable | Description |
|
|
253
|
+
|----------|-------------|
|
|
254
|
+
| `TCB_ENV` or `CLOUDBASE_ENV` | CloudBase environment ID (required for history) |
|
|
255
|
+
| `SCF_FUNCTIONNAME` | Cloud function name (used as bot ID) |
|
|
256
|
+
| `HOSTNAME` | Container hostname (fallback for bot ID) |
|
|
215
257
|
|
|
216
|
-
|
|
258
|
+
## Platform-Specific Notes
|
|
217
259
|
|
|
218
|
-
|
|
260
|
+
| Platform | Trigger Source | Async Reply |
|
|
261
|
+
|----------|---------------|-------------|
|
|
262
|
+
| Mini Program | `WXMiniApp` | Always async |
|
|
263
|
+
| Service Account | `WXService` | Based on verification |
|
|
264
|
+
| Subscription | `WXSubscription` | Based on verification |
|
|
265
|
+
| Enterprise CS | `WXCustomerService` | Always async |
|
|
219
266
|
|
|
220
|
-
|
|
267
|
+
## License
|
|
221
268
|
|
|
269
|
+
ISC
|
package/dist/index.js
CHANGED
|
@@ -350,6 +350,20 @@ function createWxMessageHandler(createAgent, options) {
|
|
|
350
350
|
);
|
|
351
351
|
let skipAI = false;
|
|
352
352
|
let isEnd = false;
|
|
353
|
+
const isUnverifiedAccount = ["WXSubscription", "WXService"].includes(triggerSrc) && !wxVerify;
|
|
354
|
+
if (!isUnverifiedAccount) {
|
|
355
|
+
const existingUserMsg = await agent.getPreviousReply(
|
|
356
|
+
msgData.conversation,
|
|
357
|
+
msgData.recordId
|
|
358
|
+
);
|
|
359
|
+
if (existingUserMsg) {
|
|
360
|
+
logger.info?.("[WX] Duplicate callback detected, skipping:", {
|
|
361
|
+
recordId: msgData.recordId,
|
|
362
|
+
conversation: msgData.conversation
|
|
363
|
+
});
|
|
364
|
+
return res.json({});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
353
367
|
const msgType = extractMsgType(callbackData);
|
|
354
368
|
const validation = validateMessageType(msgType);
|
|
355
369
|
if (!validation.isValid) {
|