@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.
Files changed (45) hide show
  1. package/INSTALL.md +231 -0
  2. package/README.md +259 -0
  3. package/dist/index-simple.js +9 -0
  4. package/dist/index.d.ts +16 -0
  5. package/dist/index.js +77 -0
  6. package/dist/mock-server.d.ts +6 -0
  7. package/dist/mock-server.js +203 -0
  8. package/dist/openclaw.plugin.json +96 -0
  9. package/dist/setup-entry.d.ts +9 -0
  10. package/dist/setup-entry.js +8 -0
  11. package/dist/src/cache/compactor.d.ts +36 -0
  12. package/dist/src/cache/compactor.js +154 -0
  13. package/dist/src/cache/extractor.d.ts +48 -0
  14. package/dist/src/cache/extractor.js +120 -0
  15. package/dist/src/cache/index.d.ts +15 -0
  16. package/dist/src/cache/index.js +16 -0
  17. package/dist/src/cache/indexer.d.ts +41 -0
  18. package/dist/src/cache/indexer.js +262 -0
  19. package/dist/src/cache/manager.d.ts +113 -0
  20. package/dist/src/cache/manager.js +271 -0
  21. package/dist/src/cache/message-queue.d.ts +59 -0
  22. package/dist/src/cache/message-queue.js +147 -0
  23. package/dist/src/cache/saas-connector.d.ts +94 -0
  24. package/dist/src/cache/saas-connector.js +289 -0
  25. package/dist/src/cache/syncer.d.ts +60 -0
  26. package/dist/src/cache/syncer.js +177 -0
  27. package/dist/src/cache/types.d.ts +198 -0
  28. package/dist/src/cache/types.js +43 -0
  29. package/dist/src/cache/writer.d.ts +81 -0
  30. package/dist/src/cache/writer.js +461 -0
  31. package/dist/src/channel.d.ts +65 -0
  32. package/dist/src/channel.js +334 -0
  33. package/dist/src/client.d.ts +280 -0
  34. package/dist/src/client.js +248 -0
  35. package/index-simple.ts +11 -0
  36. package/index.ts +89 -0
  37. package/mock-server.ts +237 -0
  38. package/openclaw.plugin.json +98 -0
  39. package/package.json +37 -0
  40. package/setup-entry.ts +10 -0
  41. package/src/channel.ts +398 -0
  42. package/src/client.ts +412 -0
  43. package/test-cache.ts +260 -0
  44. package/test-integration.ts +319 -0
  45. 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();