wechat-ilink-client 0.1.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.
@@ -0,0 +1,593 @@
1
+ import { EventEmitter } from "node:events";
2
+
3
+ //#region src/api/types.d.ts
4
+ /**
5
+ * WeChat iLink bot protocol types.
6
+ *
7
+ * Reverse-engineered from @tencent-weixin/openclaw-weixin.
8
+ * The backend API uses JSON over HTTP; byte fields are base64 strings in JSON.
9
+ */
10
+ declare const UploadMediaType: {
11
+ readonly IMAGE: 1;
12
+ readonly VIDEO: 2;
13
+ readonly FILE: 3;
14
+ readonly VOICE: 4;
15
+ };
16
+ type UploadMediaType = (typeof UploadMediaType)[keyof typeof UploadMediaType];
17
+ declare const MessageType: {
18
+ readonly NONE: 0;
19
+ readonly USER: 1;
20
+ readonly BOT: 2;
21
+ };
22
+ type MessageType = (typeof MessageType)[keyof typeof MessageType];
23
+ declare const MessageItemType: {
24
+ readonly NONE: 0;
25
+ readonly TEXT: 1;
26
+ readonly IMAGE: 2;
27
+ readonly VOICE: 3;
28
+ readonly FILE: 4;
29
+ readonly VIDEO: 5;
30
+ };
31
+ type MessageItemType = (typeof MessageItemType)[keyof typeof MessageItemType];
32
+ declare const MessageState: {
33
+ readonly NEW: 0;
34
+ readonly GENERATING: 1;
35
+ readonly FINISH: 2;
36
+ };
37
+ type MessageState = (typeof MessageState)[keyof typeof MessageState];
38
+ declare const TypingStatus: {
39
+ readonly TYPING: 1;
40
+ readonly CANCEL: 2;
41
+ };
42
+ type TypingStatus = (typeof TypingStatus)[keyof typeof TypingStatus];
43
+ /** Metadata attached to every outgoing CGI request. */
44
+ interface BaseInfo {
45
+ channel_version?: string;
46
+ }
47
+ /** CDN media reference; aes_key is base64-encoded bytes in JSON. */
48
+ interface CDNMedia {
49
+ /** Encrypted parameters for CDN download/upload. */
50
+ encrypt_query_param?: string;
51
+ /** Base64-encoded AES-128 key. */
52
+ aes_key?: string;
53
+ /** 0 = only encrypt fileid, 1 = packed thumb/mid info. */
54
+ encrypt_type?: number;
55
+ }
56
+ interface TextItem {
57
+ text?: string;
58
+ }
59
+ interface ImageItem {
60
+ /** Original image CDN reference. */
61
+ media?: CDNMedia;
62
+ /** Thumbnail CDN reference. */
63
+ thumb_media?: CDNMedia;
64
+ /** Raw AES-128 key as hex string (16 bytes); preferred over media.aes_key for inbound decryption. */
65
+ aeskey?: string;
66
+ url?: string;
67
+ mid_size?: number;
68
+ thumb_size?: number;
69
+ thumb_height?: number;
70
+ thumb_width?: number;
71
+ hd_size?: number;
72
+ }
73
+ interface VoiceItem {
74
+ media?: CDNMedia;
75
+ /** Voice encoding: 1=pcm, 2=adpcm, 3=feature, 4=speex, 5=amr, 6=silk, 7=mp3, 8=ogg-speex */
76
+ encode_type?: number;
77
+ bits_per_sample?: number;
78
+ /** Sample rate in Hz. */
79
+ sample_rate?: number;
80
+ /** Duration in milliseconds. */
81
+ playtime?: number;
82
+ /** Speech-to-text content (if available). */
83
+ text?: string;
84
+ }
85
+ interface FileItem {
86
+ media?: CDNMedia;
87
+ file_name?: string;
88
+ md5?: string;
89
+ /** File size as string. */
90
+ len?: string;
91
+ }
92
+ interface VideoItem {
93
+ media?: CDNMedia;
94
+ video_size?: number;
95
+ play_length?: number;
96
+ video_md5?: string;
97
+ thumb_media?: CDNMedia;
98
+ thumb_size?: number;
99
+ thumb_height?: number;
100
+ thumb_width?: number;
101
+ }
102
+ interface RefMessage {
103
+ message_item?: MessageItem;
104
+ /** Summary text. */
105
+ title?: string;
106
+ }
107
+ interface MessageItem {
108
+ type?: number;
109
+ create_time_ms?: number;
110
+ update_time_ms?: number;
111
+ is_completed?: boolean;
112
+ msg_id?: string;
113
+ ref_msg?: RefMessage;
114
+ text_item?: TextItem;
115
+ image_item?: ImageItem;
116
+ voice_item?: VoiceItem;
117
+ file_item?: FileItem;
118
+ video_item?: VideoItem;
119
+ }
120
+ interface WeixinMessage {
121
+ seq?: number;
122
+ message_id?: number;
123
+ from_user_id?: string;
124
+ to_user_id?: string;
125
+ client_id?: string;
126
+ create_time_ms?: number;
127
+ update_time_ms?: number;
128
+ delete_time_ms?: number;
129
+ session_id?: string;
130
+ group_id?: string;
131
+ /** 1 = USER, 2 = BOT */
132
+ message_type?: number;
133
+ /** 0 = NEW, 1 = GENERATING, 2 = FINISH */
134
+ message_state?: number;
135
+ item_list?: MessageItem[];
136
+ /** Conversation context token — must be echoed verbatim in replies. */
137
+ context_token?: string;
138
+ }
139
+ interface GetUpdatesReq {
140
+ /** Full context buf cached locally; send "" on first request. */
141
+ get_updates_buf?: string;
142
+ base_info?: BaseInfo;
143
+ }
144
+ interface GetUpdatesResp {
145
+ ret?: number;
146
+ /** Error code from server (e.g. -14 = session timeout). */
147
+ errcode?: number;
148
+ errmsg?: string;
149
+ msgs?: WeixinMessage[];
150
+ /** Full context buf to cache locally and send on next request. */
151
+ get_updates_buf?: string;
152
+ /** Server-suggested timeout (ms) for the next long-poll. */
153
+ longpolling_timeout_ms?: number;
154
+ }
155
+ interface SendMessageReq {
156
+ msg?: WeixinMessage;
157
+ base_info?: BaseInfo;
158
+ }
159
+ interface SendMessageResp {
160
+ ret?: number;
161
+ errmsg?: string;
162
+ }
163
+ interface GetUploadUrlReq {
164
+ filekey?: string;
165
+ /** See UploadMediaType: 1=IMAGE, 2=VIDEO, 3=FILE, 4=VOICE */
166
+ media_type?: number;
167
+ to_user_id?: string;
168
+ /** Original file plaintext size. */
169
+ rawsize?: number;
170
+ /** Original file plaintext MD5 (hex). */
171
+ rawfilemd5?: string;
172
+ /** Ciphertext size after AES-128-ECB encryption. */
173
+ filesize?: number;
174
+ /** Thumbnail plaintext size (IMAGE/VIDEO). */
175
+ thumb_rawsize?: number;
176
+ /** Thumbnail plaintext MD5 (IMAGE/VIDEO). */
177
+ thumb_rawfilemd5?: string;
178
+ /** Thumbnail ciphertext size (IMAGE/VIDEO). */
179
+ thumb_filesize?: number;
180
+ /** Skip thumbnail upload URL. */
181
+ no_need_thumb?: boolean;
182
+ /** AES key (hex). */
183
+ aeskey?: string;
184
+ base_info?: BaseInfo;
185
+ }
186
+ interface GetUploadUrlResp {
187
+ /** Original image upload encrypted parameters. */
188
+ upload_param?: string;
189
+ /** Thumbnail upload encrypted parameters. */
190
+ thumb_upload_param?: string;
191
+ }
192
+ interface GetConfigReq {
193
+ ilink_user_id?: string;
194
+ context_token?: string;
195
+ base_info?: BaseInfo;
196
+ }
197
+ interface GetConfigResp {
198
+ ret?: number;
199
+ errmsg?: string;
200
+ /** Base64-encoded typing ticket for sendTyping. */
201
+ typing_ticket?: string;
202
+ }
203
+ interface SendTypingReq {
204
+ ilink_user_id?: string;
205
+ typing_ticket?: string;
206
+ /** 1 = typing, 2 = cancel typing */
207
+ status?: number;
208
+ base_info?: BaseInfo;
209
+ }
210
+ interface SendTypingResp {
211
+ ret?: number;
212
+ errmsg?: string;
213
+ }
214
+ interface QRCodeResponse {
215
+ /** Opaque QR code identifier (pass to get_qrcode_status). */
216
+ qrcode: string;
217
+ /** URL that renders/encodes the QR image. */
218
+ qrcode_img_content: string;
219
+ }
220
+ interface QRCodeStatusResponse {
221
+ status: "wait" | "scaned" | "confirmed" | "expired";
222
+ bot_token?: string;
223
+ /** Bot account identifier (e.g. "hex@im.bot"). */
224
+ ilink_bot_id?: string;
225
+ /** API base URL returned on successful login. */
226
+ baseurl?: string;
227
+ /** User ID of the person who scanned the QR code. */
228
+ ilink_user_id?: string;
229
+ }
230
+ //#endregion
231
+ //#region src/api/client.d.ts
232
+ declare const DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
233
+ declare const CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
234
+ /** Default bot_type value. */
235
+ declare const DEFAULT_BOT_TYPE = "3";
236
+ interface ApiClientOptions {
237
+ baseUrl?: string;
238
+ cdnBaseUrl?: string;
239
+ token?: string;
240
+ /** Channel version string sent as base_info.channel_version. */
241
+ channelVersion?: string;
242
+ /** Optional SKRouteTag header. */
243
+ routeTag?: string;
244
+ }
245
+ declare class ApiClient {
246
+ readonly baseUrl: string;
247
+ readonly cdnBaseUrl: string;
248
+ private token?;
249
+ private channelVersion;
250
+ private routeTag?;
251
+ constructor(opts?: ApiClientOptions);
252
+ /** Update the bearer token (after QR login). */
253
+ setToken(token: string): void;
254
+ getToken(): string | undefined;
255
+ private buildBaseInfo;
256
+ private buildHeaders;
257
+ /**
258
+ * POST JSON to an iLink API endpoint with timeout + abort.
259
+ */
260
+ private apiFetch;
261
+ /**
262
+ * Long-poll for new messages. Returns empty response on client-side timeout
263
+ * (normal for long-poll).
264
+ */
265
+ getUpdates(getUpdatesBuf: string, timeoutMs?: number): Promise<GetUpdatesResp>;
266
+ /** Send a message downstream. */
267
+ sendMessage(req: SendMessageReq): Promise<void>;
268
+ /** Get a pre-signed CDN upload URL. */
269
+ getUploadUrl(req: GetUploadUrlReq): Promise<GetUploadUrlResp>;
270
+ /** Fetch bot config (includes typing_ticket) for a given user. */
271
+ getConfig(ilinkUserId: string, contextToken?: string): Promise<GetConfigResp>;
272
+ /** Send a typing indicator. */
273
+ sendTyping(req: SendTypingReq): Promise<void>;
274
+ /** Fetch a new QR code for bot login. */
275
+ getQRCode(botType?: string): Promise<QRCodeResponse>;
276
+ /**
277
+ * Long-poll the QR code scan status.
278
+ * Returns `{ status: "wait" }` on client-side timeout.
279
+ */
280
+ pollQRCodeStatus(qrcode: string): Promise<QRCodeStatusResponse>;
281
+ }
282
+ //#endregion
283
+ //#region src/auth/qr-login.d.ts
284
+ interface LoginResult {
285
+ connected: boolean;
286
+ botToken?: string;
287
+ accountId?: string;
288
+ baseUrl?: string;
289
+ userId?: string;
290
+ message: string;
291
+ }
292
+ interface QRLoginOptions {
293
+ /** Maximum time to wait for QR scan (ms). Default: 480_000 (8 min). */
294
+ timeoutMs?: number;
295
+ /** bot_type parameter (default: "3"). */
296
+ botType?: string;
297
+ /** Maximum number of QR code refreshes on expiry. Default: 3. */
298
+ maxRefreshes?: number;
299
+ /**
300
+ * Called when a QR code URL is available (initial and on refresh).
301
+ * The caller is responsible for rendering the QR code.
302
+ */
303
+ onQRCode?: (qrcodeUrl: string) => void | Promise<void>;
304
+ /** Called when status changes. */
305
+ onStatus?: (status: QRCodeStatusResponse["status"]) => void;
306
+ /** AbortSignal for cancellation. */
307
+ signal?: AbortSignal;
308
+ }
309
+ /**
310
+ * Run the full QR code login flow. Returns a LoginResult.
311
+ *
312
+ * The library does NOT render QR codes — use `opts.onQRCode` to receive
313
+ * the QR code URL and handle display yourself.
314
+ */
315
+ declare function loginWithQRCode(api: ApiClient, opts?: QRLoginOptions): Promise<LoginResult>;
316
+ //#endregion
317
+ //#region src/media/download.d.ts
318
+ interface DownloadedMedia {
319
+ /** Decrypted file content. */
320
+ data: Buffer;
321
+ /** Media type hint: "image", "voice", "file", "video". */
322
+ kind: "image" | "voice" | "file" | "video";
323
+ /** Original filename (file items only). */
324
+ fileName?: string;
325
+ }
326
+ /**
327
+ * Download and decrypt media from a single MessageItem.
328
+ * Returns null if the item has no downloadable media.
329
+ */
330
+ declare function downloadMediaFromItem(item: MessageItem, cdnBaseUrl: string): Promise<DownloadedMedia | null>;
331
+ //#endregion
332
+ //#region src/media/upload.d.ts
333
+ interface UploadedFileInfo {
334
+ filekey: string;
335
+ /** CDN download encrypted_query_param (fill into CDNMedia.encrypt_query_param). */
336
+ downloadEncryptedQueryParam: string;
337
+ /** AES-128-ECB key, hex-encoded; convert to base64 for CDNMedia.aes_key. */
338
+ aeskey: string;
339
+ /** Plaintext file size in bytes. */
340
+ fileSize: number;
341
+ /** Ciphertext file size in bytes (after AES-128-ECB + PKCS7). */
342
+ fileSizeCiphertext: number;
343
+ }
344
+ /** Upload a local image file to the WeChat CDN. */
345
+ declare function uploadImage(params: {
346
+ filePath: string;
347
+ toUserId: string;
348
+ api: ApiClient;
349
+ cdnBaseUrl: string;
350
+ }): Promise<UploadedFileInfo>;
351
+ /** Upload a local video file to the WeChat CDN. */
352
+ declare function uploadVideo(params: {
353
+ filePath: string;
354
+ toUserId: string;
355
+ api: ApiClient;
356
+ cdnBaseUrl: string;
357
+ }): Promise<UploadedFileInfo>;
358
+ /** Upload a local file attachment to the WeChat CDN. */
359
+ declare function uploadFile(params: {
360
+ filePath: string;
361
+ toUserId: string;
362
+ api: ApiClient;
363
+ cdnBaseUrl: string;
364
+ }): Promise<UploadedFileInfo>;
365
+ //#endregion
366
+ //#region src/monitor.d.ts
367
+ /** Error code returned by the server when the bot session has expired. */
368
+ declare const SESSION_EXPIRED_ERRCODE = -14;
369
+ interface MonitorOptions {
370
+ /** Long-poll timeout in ms. Server may override via longpolling_timeout_ms. */
371
+ longPollTimeoutMs?: number;
372
+ /** AbortSignal for stopping the loop. */
373
+ signal?: AbortSignal;
374
+ /**
375
+ * Called once at startup to load a previously persisted sync cursor.
376
+ * Return the cursor string, or undefined/empty to start fresh.
377
+ */
378
+ loadSyncBuf?: () => string | undefined | Promise<string | undefined>;
379
+ /**
380
+ * Called after each successful getUpdates with the new cursor value.
381
+ * The caller can persist this for resume across restarts.
382
+ */
383
+ saveSyncBuf?: (buf: string) => void | Promise<void>;
384
+ }
385
+ interface MonitorCallbacks {
386
+ /** Called for each inbound message. */
387
+ onMessage: (msg: WeixinMessage) => void | Promise<void>;
388
+ /** Called when getUpdates returns an error response. */
389
+ onError?: (err: Error) => void;
390
+ /** Called when the bot session has expired (errcode -14). */
391
+ onSessionExpired?: () => void;
392
+ /** Called after each successful getUpdates response. */
393
+ onPoll?: (resp: GetUpdatesResp) => void;
394
+ }
395
+ /**
396
+ * Start the long-poll monitor loop. Runs until the AbortSignal fires.
397
+ */
398
+ declare function startMonitor(api: ApiClient, opts: MonitorOptions, callbacks: MonitorCallbacks): Promise<void>;
399
+ //#endregion
400
+ //#region src/client.d.ts
401
+ interface WeChatClientOptions extends ApiClientOptions {
402
+ /** Account ID. Set after login if not provided. */
403
+ accountId?: string;
404
+ }
405
+ interface WeChatClientEvents {
406
+ message: [msg: WeixinMessage];
407
+ error: [err: Error];
408
+ sessionExpired: [];
409
+ poll: [resp: GetUpdatesResp];
410
+ }
411
+ /**
412
+ * Normalize a raw account ID (e.g. "hex@im.bot") to a safe key
413
+ * (e.g. "hex-im-bot").
414
+ */
415
+ declare function normalizeAccountId(raw: string): string;
416
+ declare class WeChatClient extends EventEmitter {
417
+ readonly api: ApiClient;
418
+ private accountId?;
419
+ private abortController?;
420
+ /** In-process cache: userId -> contextToken (echoed from getUpdates). */
421
+ private contextTokens;
422
+ constructor(opts?: WeChatClientOptions);
423
+ getAccountId(): string | undefined;
424
+ /** Get the cached context token for a user (needed for sending replies). */
425
+ getContextToken(userId: string): string | undefined;
426
+ /**
427
+ * Run the QR code login flow. On success, configures the API client
428
+ * with the new token and sets the accountId.
429
+ *
430
+ * The library does NOT render QR codes. Use `opts.onQRCode` to receive
431
+ * the QR code URL and handle display yourself.
432
+ *
433
+ * The library does NOT persist credentials. The caller should save
434
+ * `result.botToken`, `result.accountId`, and `result.baseUrl` themselves.
435
+ */
436
+ login(opts?: QRLoginOptions): Promise<LoginResult>;
437
+ /**
438
+ * Start the long-poll monitor loop. Emits "message" events for each
439
+ * inbound message.
440
+ *
441
+ * Sync buf persistence is opt-in via `opts.loadSyncBuf` / `opts.saveSyncBuf`.
442
+ *
443
+ * Call `stop()` to terminate.
444
+ */
445
+ start(opts?: Omit<MonitorOptions, "accountId">): Promise<void>;
446
+ /** Stop the long-poll monitor loop. */
447
+ stop(): void;
448
+ /**
449
+ * Send a text message. Uses the cached context token for the target user.
450
+ * Pass an explicit contextToken to override.
451
+ */
452
+ sendText(to: string, text: string, contextToken?: string): Promise<string>;
453
+ /**
454
+ * Upload a local file and send it as the appropriate media type.
455
+ * (image/*, video/*, or file attachment based on MIME type.)
456
+ */
457
+ sendMedia(to: string, filePath: string, caption?: string, contextToken?: string): Promise<string>;
458
+ /**
459
+ * Send an already-uploaded image.
460
+ */
461
+ sendUploadedImage(to: string, uploaded: UploadedFileInfo, caption?: string, contextToken?: string): Promise<string>;
462
+ /**
463
+ * Send an already-uploaded video.
464
+ */
465
+ sendUploadedVideo(to: string, uploaded: UploadedFileInfo, caption?: string, contextToken?: string): Promise<string>;
466
+ /**
467
+ * Send an already-uploaded file attachment.
468
+ */
469
+ sendUploadedFile(to: string, fileName: string, uploaded: UploadedFileInfo, caption?: string, contextToken?: string): Promise<string>;
470
+ /**
471
+ * Send a "typing" indicator to the user. Requires a typing_ticket
472
+ * (obtained from getConfig).
473
+ */
474
+ sendTyping(userId: string, typingTicket: string, status?: "typing" | "cancel"): Promise<void>;
475
+ /**
476
+ * Get the typing ticket for a user (calls getConfig).
477
+ */
478
+ getTypingTicket(userId: string, contextToken?: string): Promise<string>;
479
+ uploadImage(filePath: string, toUserId: string): Promise<UploadedFileInfo>;
480
+ uploadVideo(filePath: string, toUserId: string): Promise<UploadedFileInfo>;
481
+ uploadFile(filePath: string, toUserId: string): Promise<UploadedFileInfo>;
482
+ /**
483
+ * Download and decrypt a media item from an inbound message.
484
+ */
485
+ downloadMedia(item: MessageItem): Promise<DownloadedMedia | null>;
486
+ /** Extract the text body from a WeixinMessage. */
487
+ static extractText(msg: WeixinMessage): string;
488
+ /** Check if a message item is a media type. */
489
+ static isMediaItem(item: MessageItem): boolean;
490
+ }
491
+ //#endregion
492
+ //#region src/media/send.d.ts
493
+ /**
494
+ * Send a text message. contextToken is required (echoed from getUpdates).
495
+ */
496
+ declare function sendText(api: ApiClient, to: string, text: string, contextToken: string): Promise<string>;
497
+ /**
498
+ * Send an image message with a previously uploaded file.
499
+ */
500
+ declare function sendImage(api: ApiClient, to: string, uploaded: UploadedFileInfo, contextToken: string, caption?: string): Promise<string>;
501
+ /**
502
+ * Send a video message with a previously uploaded file.
503
+ */
504
+ declare function sendVideo(api: ApiClient, to: string, uploaded: UploadedFileInfo, contextToken: string, caption?: string): Promise<string>;
505
+ /**
506
+ * Send a file attachment with a previously uploaded file.
507
+ */
508
+ declare function sendFileMessage(api: ApiClient, to: string, fileName: string, uploaded: UploadedFileInfo, contextToken: string, caption?: string): Promise<string>;
509
+ /**
510
+ * Upload and send a local file as a media message. Routing by MIME type:
511
+ * - video/* -> video message
512
+ * - image/* -> image message
513
+ * - else -> file attachment
514
+ */
515
+ declare function sendMediaFile(api: ApiClient, to: string, filePath: string, contextToken: string, caption?: string): Promise<string>;
516
+ //#endregion
517
+ //#region src/cdn/aes-ecb.d.ts
518
+ /** Encrypt a buffer with AES-128-ECB (PKCS7 padding). */
519
+ declare function encryptAesEcb(plaintext: Buffer, key: Buffer): Buffer;
520
+ /** Decrypt a buffer with AES-128-ECB (PKCS7 padding). */
521
+ declare function decryptAesEcb(ciphertext: Buffer, key: Buffer): Buffer;
522
+ /** Compute AES-128-ECB ciphertext size (PKCS7 pads to 16-byte boundary). */
523
+ declare function aesEcbPaddedSize(plaintextSize: number): number;
524
+ //#endregion
525
+ //#region src/cdn/cdn-download.d.ts
526
+ /**
527
+ * Parse CDNMedia.aes_key into a raw 16-byte AES key.
528
+ *
529
+ * Two encodings are observed:
530
+ * - base64(raw 16 bytes) -> images (aes_key from media field)
531
+ * - base64(hex string of 16 bytes) -> file / voice / video
532
+ *
533
+ * In the second case, base64-decoding yields 32 ASCII hex chars which must
534
+ * then be parsed as hex to recover the actual 16-byte key.
535
+ */
536
+ declare function parseAesKey(aesKeyBase64: string): Buffer;
537
+ /**
538
+ * Download and AES-128-ECB decrypt a CDN media file. Returns plaintext Buffer.
539
+ */
540
+ declare function downloadAndDecrypt(encryptedQueryParam: string, aesKeyBase64: string, cdnBaseUrl: string): Promise<Buffer>;
541
+ /**
542
+ * Download plain (unencrypted) bytes from the CDN.
543
+ */
544
+ declare function downloadPlain(encryptedQueryParam: string, cdnBaseUrl: string): Promise<Buffer>;
545
+ //#endregion
546
+ //#region src/cdn/cdn-upload.d.ts
547
+ /**
548
+ * Upload one buffer to the WeChat CDN with AES-128-ECB encryption.
549
+ * Returns the download encrypted_query_param from the CDN `x-encrypted-param` header.
550
+ */
551
+ declare function uploadBufferToCdn(params: {
552
+ buf: Buffer;
553
+ uploadParam: string;
554
+ filekey: string;
555
+ cdnBaseUrl: string;
556
+ aeskey: Buffer;
557
+ }): Promise<{
558
+ downloadParam: string;
559
+ }>;
560
+ //#endregion
561
+ //#region src/cdn/cdn-url.d.ts
562
+ /**
563
+ * CDN URL construction for WeChat CDN upload/download.
564
+ */
565
+ /** Build a CDN download URL from encrypt_query_param. */
566
+ declare function buildCdnDownloadUrl(encryptedQueryParam: string, cdnBaseUrl: string): string;
567
+ /** Build a CDN upload URL from upload_param and filekey. */
568
+ declare function buildCdnUploadUrl(params: {
569
+ cdnBaseUrl: string;
570
+ uploadParam: string;
571
+ filekey: string;
572
+ }): string;
573
+ //#endregion
574
+ //#region src/util/mime.d.ts
575
+ /** Get MIME type from filename extension. Defaults to "application/octet-stream". */
576
+ declare function getMimeFromFilename(filename: string): string;
577
+ /** Get file extension from MIME type. Defaults to ".bin". */
578
+ declare function getExtensionFromMime(mimeType: string): string;
579
+ /** Get file extension from Content-Type header or URL path. Defaults to ".bin". */
580
+ declare function getExtensionFromContentTypeOrUrl(contentType: string | null, url: string): string;
581
+ //#endregion
582
+ //#region src/util/random.d.ts
583
+ /**
584
+ * Generate a prefixed unique ID: `{prefix}:{timestamp}-{8-char hex}`.
585
+ */
586
+ declare function generateId(prefix: string): string;
587
+ /**
588
+ * Generate a temporary file name: `{prefix}-{timestamp}-{8-char hex}{ext}`.
589
+ */
590
+ declare function tempFileName(prefix: string, ext: string): string;
591
+ //#endregion
592
+ export { ApiClient, type ApiClientOptions, type BaseInfo, type CDNMedia, CDN_BASE_URL, DEFAULT_BASE_URL, DEFAULT_BOT_TYPE, type DownloadedMedia, type FileItem, type GetConfigReq, type GetConfigResp, type GetUpdatesReq, type GetUpdatesResp, type GetUploadUrlReq, type GetUploadUrlResp, type ImageItem, type LoginResult, type MessageItem, MessageItemType, MessageState, MessageType, type MonitorCallbacks, type MonitorOptions, type QRCodeResponse, type QRCodeStatusResponse, type QRLoginOptions, type RefMessage, SESSION_EXPIRED_ERRCODE, type SendMessageReq, type SendMessageResp, type SendTypingReq, type SendTypingResp, type TextItem, TypingStatus, UploadMediaType, type UploadedFileInfo, type VideoItem, type VoiceItem, WeChatClient, type WeChatClientEvents, type WeChatClientOptions, type WeixinMessage, aesEcbPaddedSize, buildCdnDownloadUrl, buildCdnUploadUrl, decryptAesEcb, downloadAndDecrypt, downloadMediaFromItem, downloadPlain, encryptAesEcb, generateId, getExtensionFromContentTypeOrUrl, getExtensionFromMime, getMimeFromFilename, loginWithQRCode, normalizeAccountId, parseAesKey, sendFileMessage, sendImage, sendMediaFile, sendText, sendVideo, startMonitor, tempFileName, uploadBufferToCdn, uploadFile, uploadImage, uploadVideo };
593
+ //# sourceMappingURL=index.d.mts.map