@gloablehive/celphone-wechat-plugin 1.0.0
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/INSTALL.md +231 -0
- package/README.md +259 -0
- package/dist/index-simple.js +9 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +77 -0
- package/dist/mock-server.d.ts +6 -0
- package/dist/mock-server.js +203 -0
- package/dist/openclaw.plugin.json +96 -0
- package/dist/setup-entry.d.ts +9 -0
- package/dist/setup-entry.js +8 -0
- package/dist/src/cache/compactor.d.ts +36 -0
- package/dist/src/cache/compactor.js +154 -0
- package/dist/src/cache/extractor.d.ts +48 -0
- package/dist/src/cache/extractor.js +120 -0
- package/dist/src/cache/index.d.ts +15 -0
- package/dist/src/cache/index.js +16 -0
- package/dist/src/cache/indexer.d.ts +41 -0
- package/dist/src/cache/indexer.js +262 -0
- package/dist/src/cache/manager.d.ts +113 -0
- package/dist/src/cache/manager.js +271 -0
- package/dist/src/cache/message-queue.d.ts +59 -0
- package/dist/src/cache/message-queue.js +147 -0
- package/dist/src/cache/saas-connector.d.ts +94 -0
- package/dist/src/cache/saas-connector.js +289 -0
- package/dist/src/cache/syncer.d.ts +60 -0
- package/dist/src/cache/syncer.js +177 -0
- package/dist/src/cache/types.d.ts +198 -0
- package/dist/src/cache/types.js +43 -0
- package/dist/src/cache/writer.d.ts +81 -0
- package/dist/src/cache/writer.js +461 -0
- package/dist/src/channel.d.ts +65 -0
- package/dist/src/channel.js +334 -0
- package/dist/src/client.d.ts +280 -0
- package/dist/src/client.js +248 -0
- package/index-simple.ts +11 -0
- package/index.ts +89 -0
- package/mock-server.ts +237 -0
- package/openclaw.plugin.json +98 -0
- package/package.json +37 -0
- package/setup-entry.ts +10 -0
- package/src/channel.ts +398 -0
- package/src/client.ts +412 -0
- package/test-cache.ts +260 -0
- package/test-integration.ts +319 -0
- package/tsconfig.json +22 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkPhone WeChat API Client
|
|
3
|
+
*
|
|
4
|
+
* Interfaces with the WorkPhone API for sending/receiving WeChat messages
|
|
5
|
+
* Based on the Apifox API manual: data/apifox-workphone-3/
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface WorkPhoneConfig {
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
apiKey: string;
|
|
11
|
+
accountId?: string;
|
|
12
|
+
wechatAccountId?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface WeChatFriend {
|
|
16
|
+
wechatFriendId: string;
|
|
17
|
+
wechatId: string;
|
|
18
|
+
nickName: string;
|
|
19
|
+
remark: string;
|
|
20
|
+
avatar?: string;
|
|
21
|
+
label?: string[];
|
|
22
|
+
phones?: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface WeChatChatroom {
|
|
26
|
+
wechatChatroomId: string;
|
|
27
|
+
chatroomId: string;
|
|
28
|
+
name: string;
|
|
29
|
+
ownerId: string;
|
|
30
|
+
memberCount: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface WeChatMessage {
|
|
34
|
+
messageId: string;
|
|
35
|
+
msgSvrId?: string;
|
|
36
|
+
wechatId: string;
|
|
37
|
+
content: string;
|
|
38
|
+
type: number;
|
|
39
|
+
timestamp: number;
|
|
40
|
+
isSelf: boolean;
|
|
41
|
+
fromUser?: string;
|
|
42
|
+
toUser?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SendMessageParams {
|
|
46
|
+
wechatAccountId: string;
|
|
47
|
+
toUser: string;
|
|
48
|
+
content: string;
|
|
49
|
+
type?: 'text' | 'image' | 'video' | 'file' | 'link' | 'card';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface SendMessageResult {
|
|
53
|
+
messageId: string;
|
|
54
|
+
msgSvrId?: string;
|
|
55
|
+
success: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface WebhookPayload {
|
|
59
|
+
event: 'message' | 'friend_request' | 'group_invite' | 'system';
|
|
60
|
+
accountId: string;
|
|
61
|
+
wechatAccountId: string;
|
|
62
|
+
message?: WeChatMessage;
|
|
63
|
+
friendRequest?: {
|
|
64
|
+
v1: string;
|
|
65
|
+
scene: number;
|
|
66
|
+
fromUser: string;
|
|
67
|
+
ticket?: string;
|
|
68
|
+
};
|
|
69
|
+
timestamp: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class WorkPhoneWeChatClient {
|
|
73
|
+
private config: WorkPhoneConfig;
|
|
74
|
+
|
|
75
|
+
constructor(config: WorkPhoneConfig) {
|
|
76
|
+
this.config = config;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private async request<T>(
|
|
80
|
+
method: string,
|
|
81
|
+
path: string,
|
|
82
|
+
body?: any,
|
|
83
|
+
query?: Record<string, string>
|
|
84
|
+
): Promise<T> {
|
|
85
|
+
const url = new URL(`${this.config.baseUrl}${path}`);
|
|
86
|
+
if (query) {
|
|
87
|
+
Object.entries(query).forEach(([k, v]) => url.searchParams.append(k, v));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const headers: Record<string, string> = {
|
|
91
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (this.config.accountId) {
|
|
96
|
+
headers['X-Account-Id'] = this.config.accountId;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const response = await fetch(url.toString(), {
|
|
100
|
+
method,
|
|
101
|
+
headers,
|
|
102
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
const error = await response.text();
|
|
107
|
+
throw new Error(`WorkPhone API error: ${response.status} - ${error}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return response.json();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ========== WeChat Account Operations ==========
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get all WeChat accounts for an account
|
|
117
|
+
* GET /api/v1/accounts/{accountId}/wechatAccounts
|
|
118
|
+
*/
|
|
119
|
+
async getWeChatAccounts(accountId: string): Promise<WeChatFriend[]> {
|
|
120
|
+
return this.request('GET', `/api/v1/accounts/${accountId}/wechatAccounts`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get WeChat account info by wechatId
|
|
125
|
+
* GET /wp/api/v1/wechatAccounts/{wechatId}/byWechatId
|
|
126
|
+
*/
|
|
127
|
+
async getWeChatAccountById(wechatId: string): Promise<any> {
|
|
128
|
+
return this.request('GET', `/wp/api/v1/wechatAccounts/${wechatId}/byWechatId`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ========== Friend Operations ==========
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get all friends for a WeChat account
|
|
135
|
+
* GET /wp/api/v1/wechatAccounts/{wechatAccountId}/friends
|
|
136
|
+
*/
|
|
137
|
+
async getFriends(wechatAccountId: string, options?: {
|
|
138
|
+
page?: number;
|
|
139
|
+
pageSize?: number;
|
|
140
|
+
labelId?: string;
|
|
141
|
+
}): Promise<{ list: WeChatFriend[]; total: number }> {
|
|
142
|
+
return this.request('GET', `/wp/api/v1/wechatAccounts/${wechatAccountId}/friends`, undefined, options as any);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Search friends
|
|
147
|
+
* GET /wp/api/v1/wechatFriends
|
|
148
|
+
*/
|
|
149
|
+
async searchFriends(params: {
|
|
150
|
+
accountId?: string;
|
|
151
|
+
wechatAccountId?: string;
|
|
152
|
+
keyword?: string;
|
|
153
|
+
}): Promise<WeChatFriend[]> {
|
|
154
|
+
return this.request('GET', '/wp/api/v1/wechatFriends', undefined, params);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get friend by ID
|
|
159
|
+
* GET /wp/api/v1/wechatFriends/{wechatFriendId}
|
|
160
|
+
*/
|
|
161
|
+
async getFriend(wechatFriendId: string): Promise<WeChatFriend> {
|
|
162
|
+
return this.request('GET', `/wp/api/v1/wechatFriends/${wechatFriendId}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Add friend (send friend request)
|
|
167
|
+
* POST /wp/api/v1/wechatFriends/addFriend
|
|
168
|
+
*/
|
|
169
|
+
async addFriend(params: {
|
|
170
|
+
wechatAccountId: string;
|
|
171
|
+
v1: string; // WeChat ID to add
|
|
172
|
+
verifyMessage?: string;
|
|
173
|
+
scene?: number; // 15: QQๅท, 16: ๅพฎไฟกๅท, 17: ๆๆบๅท, 22: ๆๅๆจ่
|
|
174
|
+
}): Promise<{ success: boolean; taskId?: string }> {
|
|
175
|
+
return this.request('POST', '/wp/api/v1/wechatFriends/addFriend', params);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Accept friend request
|
|
180
|
+
* POST /wp/api/v1/wechatFriends/{wechatFriendId}/acceptFriend
|
|
181
|
+
*/
|
|
182
|
+
async acceptFriend(wechatFriendId: string): Promise<{ success: boolean }> {
|
|
183
|
+
return this.request('POST', `/wp/api/v1/wechatFriends/${wechatFriendId}/acceptFriend`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Update friend remark
|
|
188
|
+
* PUT /wp/api/v1/wechatFriends/{wechatFriendId}/remark
|
|
189
|
+
*/
|
|
190
|
+
async updateFriendRemark(wechatFriendId: string, remark: string): Promise<{ success: boolean }> {
|
|
191
|
+
return this.request('PUT', `/wp/api/v1/wechatFriends/${wechatFriendId}/remark`, { remark });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Update friend labels
|
|
196
|
+
* PUT /wp/api/v1/wechatFriends/{wechatFriendId}/label
|
|
197
|
+
*/
|
|
198
|
+
async updateFriendLabels(wechatFriendId: string, labelIds: string[]): Promise<{ success: boolean }> {
|
|
199
|
+
return this.request('PUT', `/wp/api/v1/wechatFriends/${wechatFriendId}/label`, { labelIds });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ========== Chatroom (Group) Operations ==========
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get all chatrooms for a WeChat account
|
|
206
|
+
* GET /wp/api/v1/wechatAccounts/{wechatAccountId}/chatrooms
|
|
207
|
+
*/
|
|
208
|
+
async getChatrooms(wechatAccountId: string): Promise<WeChatChatroom[]> {
|
|
209
|
+
return this.request('GET', `/wp/api/v1/wechatAccounts/${wechatAccountId}/chatrooms`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get chatroom info by chatroomId
|
|
214
|
+
* GET /wp/api/v1/wechatChatrooms/{wechatChatroomId}
|
|
215
|
+
*/
|
|
216
|
+
async getChatroom(wechatChatroomId: string): Promise<WeChatChatroom> {
|
|
217
|
+
return this.request('GET', `/wp/api/v1/wechatChatrooms/${wechatChatroomId}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Update chatroom name (announcement)
|
|
222
|
+
* PUT /wp/api/v1/wechatChatrooms/{wechatChatroomId}/chatroomName
|
|
223
|
+
*/
|
|
224
|
+
async updateChatroomName(wechatChatroomId: string, name: string): Promise<{ success: boolean }> {
|
|
225
|
+
return this.request('PUT', `/wp/api/v1/wechatChatrooms/${wechatChatroomId}/chatroomName`, { name });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Publish chatroom announcement
|
|
230
|
+
* PUT /api/v1/wechat/chatroom/announcement
|
|
231
|
+
*/
|
|
232
|
+
async publishChatroomAnnouncement(wechatChatroomId: string, content: string): Promise<{ success: boolean }> {
|
|
233
|
+
return this.request('PUT', '/api/v1/wechat/chatroom/announcement', {
|
|
234
|
+
wechatChatroomId,
|
|
235
|
+
content,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ========== Message Operations ==========
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get friend messages (1-on-1 chat)
|
|
243
|
+
* GET /wp/api/v1/wechat/friendMessages
|
|
244
|
+
*/
|
|
245
|
+
async getFriendMessages(params: {
|
|
246
|
+
wechatAccountId: string;
|
|
247
|
+
friendWechatId: string;
|
|
248
|
+
startTime?: number;
|
|
249
|
+
endTime?: number;
|
|
250
|
+
limit?: number;
|
|
251
|
+
offset?: number;
|
|
252
|
+
}): Promise<{ list: WeChatMessage[]; total: number }> {
|
|
253
|
+
return this.request('GET', '/wp/api/v1/wechat/friendMessages', undefined, params as any);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Get chatroom messages (group chat)
|
|
258
|
+
* GET /wp/api/v1/wechat/chatroomMessages
|
|
259
|
+
*/
|
|
260
|
+
async getChatroomMessages(params: {
|
|
261
|
+
wechatAccountId: string;
|
|
262
|
+
chatroomId: string;
|
|
263
|
+
startTime?: number;
|
|
264
|
+
endTime?: number;
|
|
265
|
+
limit?: number;
|
|
266
|
+
offset?: number;
|
|
267
|
+
}): Promise<{ list: WeChatMessage[]; total: number }> {
|
|
268
|
+
return this.request('GET', '/wp/api/v1/wechat/chatroomMessages', undefined, params as any);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Send friend message (1-on-1)
|
|
273
|
+
* POST /wp/api/v1/wechat/friendMessages
|
|
274
|
+
*/
|
|
275
|
+
async sendFriendMessage(params: {
|
|
276
|
+
wechatAccountId: string;
|
|
277
|
+
friendWechatId: string;
|
|
278
|
+
content: string;
|
|
279
|
+
type?: 'text' | 'image' | 'video' | 'file' | 'link';
|
|
280
|
+
}): Promise<SendMessageResult> {
|
|
281
|
+
return this.request('POST', '/wp/api/v1/wechat/friendMessages', params);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Send chatroom message (group)
|
|
286
|
+
* POST /wp/api/v1/wechat/chatroomMessages
|
|
287
|
+
*/
|
|
288
|
+
async sendChatroomMessage(params: {
|
|
289
|
+
wechatAccountId: string;
|
|
290
|
+
chatroomId: string;
|
|
291
|
+
content: string;
|
|
292
|
+
type?: 'text' | 'image' | 'video' | 'file' | 'link';
|
|
293
|
+
atUsers?: string[]; // WeChat IDs to @mention
|
|
294
|
+
}): Promise<SendMessageResult> {
|
|
295
|
+
return this.request('POST', '/wp/api/v1/wechat/chatroomMessages', params);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ========== Moments (ๆๅๅ) Operations ==========
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get moments
|
|
302
|
+
* GET /wp/api/v1/moments
|
|
303
|
+
*/
|
|
304
|
+
async getMoments(params: {
|
|
305
|
+
wechatAccountId: string;
|
|
306
|
+
startTime?: number;
|
|
307
|
+
endTime?: number;
|
|
308
|
+
limit?: number;
|
|
309
|
+
}): Promise<{ list: any[]; total: number }> {
|
|
310
|
+
return this.request('GET', '/wp/api/v1/moments', undefined, params as any);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Publish moment (post to Moments)
|
|
315
|
+
* POST /wp/api/v1/moments
|
|
316
|
+
*/
|
|
317
|
+
async publishMoment(params: {
|
|
318
|
+
wechatAccountId: string;
|
|
319
|
+
content: string;
|
|
320
|
+
images?: string[]; // URLs or paths
|
|
321
|
+
videos?: string[];
|
|
322
|
+
location?: { lat: number; lng: number; name: string };
|
|
323
|
+
}): Promise<{ momentId: string; success: boolean }> {
|
|
324
|
+
return this.request('POST', '/wp/api/v1/moments', params);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Comment on a moment
|
|
329
|
+
* POST /wp/api/v1/moments/{momentId}/comment
|
|
330
|
+
*/
|
|
331
|
+
async commentMoment(momentId: string, content: string, replyCommentId?: string): Promise<{ success: boolean }> {
|
|
332
|
+
return this.request('POST', `/wp/api/v1/moments/${momentId}/comment`, {
|
|
333
|
+
content,
|
|
334
|
+
replyCommentId,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Like a moment
|
|
340
|
+
* POST /wp/api/v1/moments/{momentId}/like
|
|
341
|
+
*/
|
|
342
|
+
async likeMoment(momentId: string): Promise<{ success: boolean }> {
|
|
343
|
+
return this.request('POST', `/wp/api/v1/moments/${momentId}/like`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ========== Media Download ==========
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Download friend message file
|
|
350
|
+
*/
|
|
351
|
+
async downloadFriendFile(messageId: string, wechatTime: number): Promise<Blob> {
|
|
352
|
+
const url = `${this.config.baseUrl}/wp/api/v1/wechat/friendMessage/downloadFile/${messageId}/${wechatTime}`;
|
|
353
|
+
const response = await fetch(url, {
|
|
354
|
+
headers: { 'Authorization': `Bearer ${this.config.apiKey}` },
|
|
355
|
+
});
|
|
356
|
+
if (!response.ok) {
|
|
357
|
+
throw new Error(`Download failed: ${response.status}`);
|
|
358
|
+
}
|
|
359
|
+
return response.blob();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Download chatroom message file
|
|
364
|
+
*/
|
|
365
|
+
async downloadChatroomFile(messageId: string, wechatTime: number): Promise<Blob> {
|
|
366
|
+
const url = `${this.config.baseUrl}/wp/api/v1/wechat/chatroomMessage/downloadFile/${messageId}/${wechatTime}`;
|
|
367
|
+
const response = await fetch(url, {
|
|
368
|
+
headers: { 'Authorization': `Bearer ${this.config.apiKey}` },
|
|
369
|
+
});
|
|
370
|
+
if (!response.ok) {
|
|
371
|
+
throw new Error(`Download failed: ${response.status}`);
|
|
372
|
+
}
|
|
373
|
+
return response.blob();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ========== Webhook Parsing ==========
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Parse webhook payload from WorkPhone callback
|
|
380
|
+
* This should be called by your webhook endpoint handler
|
|
381
|
+
*/
|
|
382
|
+
parseWebhookPayload(payload: any): WebhookPayload | null {
|
|
383
|
+
// WorkPhone webhook format - adjust based on actual webhook docs
|
|
384
|
+
const eventMap: Record<string, WebhookPayload['event']> = {
|
|
385
|
+
'msg': 'message',
|
|
386
|
+
'friend_request': 'friend_request',
|
|
387
|
+
'group_invite': 'group_invite',
|
|
388
|
+
'system': 'system',
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
event: eventMap[payload.event] || 'message',
|
|
393
|
+
accountId: payload.accountId || '',
|
|
394
|
+
wechatAccountId: payload.wechatAccountId || '',
|
|
395
|
+
message: payload.message,
|
|
396
|
+
friendRequest: payload.friendRequest,
|
|
397
|
+
timestamp: payload.timestamp || Date.now(),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Factory function to create a WorkPhone client from OpenClaw config
|
|
404
|
+
*/
|
|
405
|
+
export function createWorkPhoneClient(config: {
|
|
406
|
+
baseUrl: string;
|
|
407
|
+
apiKey: string;
|
|
408
|
+
accountId?: string;
|
|
409
|
+
wechatAccountId?: string;
|
|
410
|
+
}): WorkPhoneWeChatClient {
|
|
411
|
+
return new WorkPhoneWeChatClient(config);
|
|
412
|
+
}
|
package/test-cache.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Script for WeChat Cache System
|
|
3
|
+
* Run: npx tsx test-cache.ts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import {
|
|
9
|
+
createCacheManager,
|
|
10
|
+
CacheManager,
|
|
11
|
+
WeChatAccount,
|
|
12
|
+
WeChatMessage,
|
|
13
|
+
} from './src/cache/index.js';
|
|
14
|
+
|
|
15
|
+
const TEST_CACHE_PATH = '/tmp/wechat-cache-test';
|
|
16
|
+
const ENCODING = 'utf-8';
|
|
17
|
+
|
|
18
|
+
async function cleanup() {
|
|
19
|
+
try {
|
|
20
|
+
await fs.rm(TEST_CACHE_PATH, { recursive: true, force: true });
|
|
21
|
+
} catch {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function test1_CreateCacheManager() {
|
|
25
|
+
console.log('\n๐ Test 1: Create Cache Manager');
|
|
26
|
+
|
|
27
|
+
const accounts: WeChatAccount[] = [
|
|
28
|
+
{
|
|
29
|
+
accountId: 'test-account-001',
|
|
30
|
+
wechatAccountId: 'wp-wechat-001',
|
|
31
|
+
wechatId: 'wxid_test001',
|
|
32
|
+
nickName: 'ๆต่ฏๅฎขๆ',
|
|
33
|
+
enabled: true,
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const manager = createCacheManager({
|
|
38
|
+
basePath: TEST_CACHE_PATH,
|
|
39
|
+
accounts,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await manager.init();
|
|
43
|
+
console.log('โ
Cache manager created and initialized');
|
|
44
|
+
|
|
45
|
+
return manager;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function test2_MessageCaching(manager: CacheManager) {
|
|
49
|
+
console.log('\n๐ Test 2: Message Caching');
|
|
50
|
+
|
|
51
|
+
const message: WeChatMessage = {
|
|
52
|
+
messageId: 'msg-001',
|
|
53
|
+
msgSvrId: 'msg-svr-001',
|
|
54
|
+
accountId: 'test-account-001',
|
|
55
|
+
conversationType: 'friend',
|
|
56
|
+
conversationId: 'wxid_alice',
|
|
57
|
+
senderId: 'wxid_alice',
|
|
58
|
+
content: 'ไฝ ๅฅฝ๏ผๆๆณๅจ่ฏขไบงๅ',
|
|
59
|
+
messageType: 1,
|
|
60
|
+
timestamp: Date.now(),
|
|
61
|
+
isSelf: false,
|
|
62
|
+
direction: 'inbound',
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
await manager.onMessage(message);
|
|
66
|
+
console.log('โ
Message cached:', message.messageId);
|
|
67
|
+
|
|
68
|
+
// Send a reply
|
|
69
|
+
const reply: WeChatMessage = {
|
|
70
|
+
messageId: 'msg-002',
|
|
71
|
+
msgSvrId: 'msg-svr-002',
|
|
72
|
+
accountId: 'test-account-001',
|
|
73
|
+
conversationType: 'friend',
|
|
74
|
+
conversationId: 'wxid_alice',
|
|
75
|
+
senderId: 'wxid_test001',
|
|
76
|
+
content: 'ๆจๅฅฝ๏ผ่ฏท้ฎๆไปไนๅฏไปฅๅธฎๆจ๏ผ',
|
|
77
|
+
messageType: 1,
|
|
78
|
+
timestamp: Date.now() + 1000,
|
|
79
|
+
isSelf: true,
|
|
80
|
+
direction: 'outbound',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
await manager.onMessage(reply);
|
|
84
|
+
console.log('โ
Reply cached:', reply.messageId);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function test3_VerifyFiles() {
|
|
88
|
+
console.log('\n๐ Test 3: Verify Files Created');
|
|
89
|
+
|
|
90
|
+
// Check account directory
|
|
91
|
+
const accountPath = path.join(TEST_CACHE_PATH, 'accounts', 'test-account-001');
|
|
92
|
+
const friendsPath = path.join(accountPath, 'friends');
|
|
93
|
+
|
|
94
|
+
const friends = await fs.readdir(friendsPath);
|
|
95
|
+
console.log('๐ Friends directories:', friends);
|
|
96
|
+
|
|
97
|
+
// Check conversation file
|
|
98
|
+
const convPath = path.join(friendsPath, 'wxid_alice', 'memory');
|
|
99
|
+
try {
|
|
100
|
+
const files = await fs.readdir(convPath);
|
|
101
|
+
console.log('๐ Conversation files:', files);
|
|
102
|
+
|
|
103
|
+
if (files.length > 0) {
|
|
104
|
+
const content = await fs.readFile(path.join(convPath, files[0]), ENCODING);
|
|
105
|
+
console.log('๐ Conversation content preview:');
|
|
106
|
+
console.log(content.slice(0, 500) + '...');
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {
|
|
109
|
+
console.log('โ ๏ธ No conversation files found yet');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check MEMORY.md
|
|
113
|
+
const globalIndex = path.join(TEST_CACHE_PATH, 'MEMORY.md');
|
|
114
|
+
try {
|
|
115
|
+
const indexContent = await fs.readFile(globalIndex, ENCODING);
|
|
116
|
+
console.log('๐ Global MEMORY.md:');
|
|
117
|
+
console.log(indexContent.slice(0, 300) + '...');
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.log('โ ๏ธ No global index yet');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log('โ
File verification complete');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function test4_ProfileOperations(manager: CacheManager) {
|
|
126
|
+
console.log('\n๐ Test 4: Profile Operations');
|
|
127
|
+
|
|
128
|
+
// Update profile
|
|
129
|
+
await manager.updateProfile('test-account-001', 'wxid_alice', {
|
|
130
|
+
remark: 'Alice-ๅฎขๆท',
|
|
131
|
+
tags: ['ๆฝๅจๅฎขๆท', '้ซไปทๅผ'],
|
|
132
|
+
customFields: { region: 'ๅไบฌ' },
|
|
133
|
+
});
|
|
134
|
+
console.log('โ
Profile updated');
|
|
135
|
+
|
|
136
|
+
// Get profile
|
|
137
|
+
const profile = await manager.getProfile('test-account-001', 'wxid_alice');
|
|
138
|
+
console.log('๐ Profile retrieved:', JSON.stringify(profile, null, 2));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function test5_ConnectionStatus(manager: CacheManager) {
|
|
142
|
+
console.log('\n๐ Test 5: Connection Status');
|
|
143
|
+
|
|
144
|
+
const status = manager.getConnectionStatus();
|
|
145
|
+
console.log('๐ Connection status:', JSON.stringify(status, null, 2));
|
|
146
|
+
|
|
147
|
+
const isOnline = manager.isSAASOnline();
|
|
148
|
+
console.log('๐ Is online:', isOnline);
|
|
149
|
+
|
|
150
|
+
const offlineMsg = manager.getOfflineMessage();
|
|
151
|
+
console.log('๐ Offline message:', offlineMsg);
|
|
152
|
+
|
|
153
|
+
console.log('โ
Connection status check complete');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function test6_MultiAccount() {
|
|
157
|
+
console.log('\n๐ Test 6: Multi-Account Support');
|
|
158
|
+
|
|
159
|
+
const accounts: WeChatAccount[] = [
|
|
160
|
+
{
|
|
161
|
+
accountId: 'account-001',
|
|
162
|
+
wechatAccountId: 'wp-001',
|
|
163
|
+
wechatId: 'wxid_001',
|
|
164
|
+
nickName: 'ๅฎขๆ1ๅท',
|
|
165
|
+
enabled: true,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
accountId: 'account-002',
|
|
169
|
+
wechatAccountId: 'wp-002',
|
|
170
|
+
wechatId: 'wxid_002',
|
|
171
|
+
nickName: 'ๅฎขๆ2ๅท',
|
|
172
|
+
enabled: true,
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const manager = createCacheManager({
|
|
177
|
+
basePath: TEST_CACHE_PATH + '-multi',
|
|
178
|
+
accounts,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await manager.init();
|
|
182
|
+
|
|
183
|
+
// Send message to account 1
|
|
184
|
+
await manager.onMessage({
|
|
185
|
+
messageId: 'msg-multi-001',
|
|
186
|
+
accountId: 'account-001',
|
|
187
|
+
conversationType: 'friend',
|
|
188
|
+
conversationId: 'wxid_user1',
|
|
189
|
+
senderId: 'wxid_user1',
|
|
190
|
+
content: 'Message from user 1',
|
|
191
|
+
messageType: 1,
|
|
192
|
+
timestamp: Date.now(),
|
|
193
|
+
isSelf: false,
|
|
194
|
+
direction: 'inbound',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Send message to account 2
|
|
198
|
+
await manager.onMessage({
|
|
199
|
+
messageId: 'msg-multi-002',
|
|
200
|
+
accountId: 'account-002',
|
|
201
|
+
conversationType: 'friend',
|
|
202
|
+
conversationId: 'wxid_user2',
|
|
203
|
+
senderId: 'wxid_user2',
|
|
204
|
+
content: 'Message from user 2',
|
|
205
|
+
messageType: 1,
|
|
206
|
+
timestamp: Date.now(),
|
|
207
|
+
isSelf: false,
|
|
208
|
+
direction: 'inbound',
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Verify separate directories
|
|
212
|
+
const dir1 = path.join(TEST_CACHE_PATH + '-multi', 'accounts', 'account-001', 'friends');
|
|
213
|
+
const dir2 = path.join(TEST_CACHE_PATH + '-multi', 'accounts', 'account-002', 'friends');
|
|
214
|
+
|
|
215
|
+
const users1 = await fs.readdir(dir1);
|
|
216
|
+
const users2 = await fs.readdir(dir2);
|
|
217
|
+
|
|
218
|
+
console.log('๐ Account 1 users:', users1);
|
|
219
|
+
console.log('๐ Account 2 users:', users2);
|
|
220
|
+
|
|
221
|
+
console.log('โ
Multi-account test complete');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function runAllTests() {
|
|
225
|
+
console.log('๐งช WeChat Cache System Tests');
|
|
226
|
+
console.log('='.repeat(50));
|
|
227
|
+
|
|
228
|
+
await cleanup();
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
// Test 1: Create cache manager
|
|
232
|
+
const manager = await test1_CreateCacheManager();
|
|
233
|
+
|
|
234
|
+
// Test 2: Message caching
|
|
235
|
+
await test2_MessageCaching(manager);
|
|
236
|
+
|
|
237
|
+
// Test 3: Verify files
|
|
238
|
+
await test3_VerifyFiles();
|
|
239
|
+
|
|
240
|
+
// Test 4: Profile operations
|
|
241
|
+
await test4_ProfileOperations(manager);
|
|
242
|
+
|
|
243
|
+
// Test 5: Connection status
|
|
244
|
+
await test5_ConnectionStatus(manager);
|
|
245
|
+
|
|
246
|
+
// Test 6: Multi-account
|
|
247
|
+
await test6_MultiAccount();
|
|
248
|
+
|
|
249
|
+
console.log('\n' + '='.repeat(50));
|
|
250
|
+
console.log('๐ All tests passed!');
|
|
251
|
+
console.log('\n๐ Test cache location:', TEST_CACHE_PATH);
|
|
252
|
+
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.error('\nโ Test failed:', error);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Run tests
|
|
260
|
+
runAllTests();
|