@lanbaolu/dsh-wechat-bridge 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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/cordis.patch.yml +12 -0
  4. package/docs/feasibility-plan.md +62 -0
  5. package/lib/bridge/commands/handlers.js +155 -0
  6. package/lib/bridge/commands/handlers.js.map +1 -0
  7. package/lib/bridge/commands/router.js +52 -0
  8. package/lib/bridge/commands/router.js.map +1 -0
  9. package/lib/bridge/config.js +40 -0
  10. package/lib/bridge/config.js.map +1 -0
  11. package/lib/bridge/constants.js +14 -0
  12. package/lib/bridge/constants.js.map +1 -0
  13. package/lib/bridge/dsh-client.js +100 -0
  14. package/lib/bridge/dsh-client.js.map +1 -0
  15. package/lib/bridge/logger.js +75 -0
  16. package/lib/bridge/logger.js.map +1 -0
  17. package/lib/bridge/main.js +546 -0
  18. package/lib/bridge/main.js.map +1 -0
  19. package/lib/bridge/pending-queue.js +55 -0
  20. package/lib/bridge/pending-queue.js.map +1 -0
  21. package/lib/bridge/session.js +84 -0
  22. package/lib/bridge/session.js.map +1 -0
  23. package/lib/bridge/store.js +38 -0
  24. package/lib/bridge/store.js.map +1 -0
  25. package/lib/bridge/wechat/accounts.js +50 -0
  26. package/lib/bridge/wechat/accounts.js.map +1 -0
  27. package/lib/bridge/wechat/api.js +197 -0
  28. package/lib/bridge/wechat/api.js.map +1 -0
  29. package/lib/bridge/wechat/cdn.js +45 -0
  30. package/lib/bridge/wechat/cdn.js.map +1 -0
  31. package/lib/bridge/wechat/crypto.js +17 -0
  32. package/lib/bridge/wechat/crypto.js.map +1 -0
  33. package/lib/bridge/wechat/login.js +139 -0
  34. package/lib/bridge/wechat/login.js.map +1 -0
  35. package/lib/bridge/wechat/media.js +138 -0
  36. package/lib/bridge/wechat/media.js.map +1 -0
  37. package/lib/bridge/wechat/monitor.js +104 -0
  38. package/lib/bridge/wechat/monitor.js.map +1 -0
  39. package/lib/bridge/wechat/send.js +172 -0
  40. package/lib/bridge/wechat/send.js.map +1 -0
  41. package/lib/bridge/wechat/sync-buf.js +11 -0
  42. package/lib/bridge/wechat/sync-buf.js.map +1 -0
  43. package/lib/bridge/wechat/types.js +35 -0
  44. package/lib/bridge/wechat/types.js.map +1 -0
  45. package/lib/bridge/wechat/upload.js +111 -0
  46. package/lib/bridge/wechat/upload.js.map +1 -0
  47. package/lib/client/Panel.js +205 -0
  48. package/lib/client/Panel.js.map +1 -0
  49. package/lib/client/index.js +11 -0
  50. package/lib/client/index.js.map +1 -0
  51. package/lib/client.js +376 -0
  52. package/lib/client.js.map +1 -0
  53. package/lib/index.js +828 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/types/bridge/commands/handlers.d.ts +18 -0
  56. package/lib/types/bridge/commands/router.d.ts +26 -0
  57. package/lib/types/bridge/config.d.ts +7 -0
  58. package/lib/types/bridge/constants.d.ts +10 -0
  59. package/lib/types/bridge/dsh-client.d.ts +39 -0
  60. package/lib/types/bridge/logger.d.ts +13 -0
  61. package/lib/types/bridge/main.d.ts +1 -0
  62. package/lib/types/bridge/pending-queue.d.ts +10 -0
  63. package/lib/types/bridge/session.d.ts +22 -0
  64. package/lib/types/bridge/store.d.ts +11 -0
  65. package/lib/types/bridge/wechat/accounts.d.ts +14 -0
  66. package/lib/types/bridge/wechat/api.d.ts +32 -0
  67. package/lib/types/bridge/wechat/cdn.d.ts +2 -0
  68. package/lib/types/bridge/wechat/crypto.d.ts +4 -0
  69. package/lib/types/bridge/wechat/login.d.ts +31 -0
  70. package/lib/types/bridge/wechat/media.d.ts +20 -0
  71. package/lib/types/bridge/wechat/monitor.d.ts +10 -0
  72. package/lib/types/bridge/wechat/send.d.ts +6 -0
  73. package/lib/types/bridge/wechat/sync-buf.d.ts +2 -0
  74. package/lib/types/bridge/wechat/types.d.ts +135 -0
  75. package/lib/types/bridge/wechat/upload.d.ts +11 -0
  76. package/lib/types/client/Panel.d.ts +3 -0
  77. package/lib/types/client/index.d.ts +9 -0
  78. package/lib/types/index.d.ts +39 -0
  79. package/package.json +92 -0
@@ -0,0 +1,139 @@
1
+ import { DEFAULT_BASE_URL, saveAccount } from './accounts.js';
2
+ import { logger } from '../logger.js';
3
+ const QR_CODE_URL = `${DEFAULT_BASE_URL}/ilink/bot/get_bot_qrcode?bot_type=3`;
4
+ const QR_STATUS_URL = `${DEFAULT_BASE_URL}/ilink/bot/get_qrcode_status`;
5
+ const POLL_INTERVAL_MS = 3_000;
6
+ function sleep(ms) {
7
+ return new Promise((resolve) => setTimeout(resolve, ms));
8
+ }
9
+ /** Phase 1: Request a QR code for login. Returns the URL and ID. */
10
+ export async function startQrLogin() {
11
+ logger.info('Requesting QR code');
12
+ const res = await fetch(QR_CODE_URL);
13
+ if (!res.ok) {
14
+ throw new Error(`Failed to get QR code: HTTP ${res.status}`);
15
+ }
16
+ const data = (await res.json());
17
+ if (data.ret !== 0 || !data.qrcode_img_content || !data.qrcode) {
18
+ throw new Error(`Failed to get QR code (ret=${data.ret})`);
19
+ }
20
+ logger.info('QR code obtained', { qrcodeId: data.qrcode });
21
+ return {
22
+ qrcodeUrl: data.qrcode_img_content,
23
+ qrcodeId: data.qrcode,
24
+ };
25
+ }
26
+ /**
27
+ * Check one QR-code status without blocking. Saves the account on success.
28
+ * This is the non-blocking building block used both by the terminal setup
29
+ * loop and by the Web settings panel polling.
30
+ */
31
+ export async function checkQrStatus(qrcodeId) {
32
+ const url = `${QR_STATUS_URL}?qrcode=${encodeURIComponent(qrcodeId)}`;
33
+ logger.debug('Checking QR status', { qrcodeId });
34
+ const controller = new AbortController();
35
+ const timer = setTimeout(() => controller.abort(), 5_000);
36
+ let res;
37
+ try {
38
+ res = await fetch(url, { signal: controller.signal });
39
+ }
40
+ catch (e) {
41
+ clearTimeout(timer);
42
+ const isTimeout = e?.name === 'AbortError' || e?.code === 'ETIMEDOUT';
43
+ return {
44
+ status: 'error',
45
+ message: isTimeout ? 'QR poll timed out' : (e?.message ?? String(e)),
46
+ retryable: true,
47
+ };
48
+ }
49
+ clearTimeout(timer);
50
+ if (!res.ok) {
51
+ return {
52
+ status: 'error',
53
+ message: `Failed to check QR status: HTTP ${res.status}`,
54
+ retryable: false,
55
+ };
56
+ }
57
+ const data = (await res.json());
58
+ logger.debug('QR status response', { status: data.status });
59
+ switch (data.status) {
60
+ case 'wait':
61
+ case 'scaned':
62
+ return { status: data.status };
63
+ case 'confirmed': {
64
+ if (!data.bot_token || !data.ilink_bot_id || !data.ilink_user_id) {
65
+ return {
66
+ status: 'error',
67
+ message: 'QR confirmed but missing required fields in response',
68
+ retryable: false,
69
+ };
70
+ }
71
+ const accountData = {
72
+ botToken: data.bot_token,
73
+ accountId: data.ilink_bot_id,
74
+ baseUrl: data.baseurl || DEFAULT_BASE_URL,
75
+ userId: data.ilink_user_id,
76
+ createdAt: new Date().toISOString(),
77
+ };
78
+ saveAccount(accountData);
79
+ logger.info('QR login successful', { accountId: accountData.accountId });
80
+ return { status: 'confirmed', account: accountData };
81
+ }
82
+ case 'expired': {
83
+ logger.info('QR code expired');
84
+ return { status: 'expired', message: 'QR code expired' };
85
+ }
86
+ default: {
87
+ logger.warn('Unknown QR status', { status: data.status, retmsg: data.retmsg });
88
+ // Surface error to user for known failure statuses
89
+ const status = data.status ?? '';
90
+ if (status && (status.includes('not_support') ||
91
+ status.includes('version') ||
92
+ status.includes('forbid') ||
93
+ status.includes('reject') ||
94
+ status.includes('cancel'))) {
95
+ return {
96
+ status: 'error',
97
+ message: `二维码扫描失败: ${data.retmsg || status}`,
98
+ retryable: false,
99
+ };
100
+ }
101
+ if (data.retmsg) {
102
+ return {
103
+ status: 'error',
104
+ message: `二维码扫描失败: ${data.retmsg}`,
105
+ retryable: false,
106
+ };
107
+ }
108
+ // Unknown but not fatal: keep waiting.
109
+ return { status: 'wait' };
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * Phase 2: Wait for the user to scan and confirm the QR code.
115
+ * Throws on expiry so the caller can regenerate the QR image.
116
+ * Returns the full AccountData on success.
117
+ */
118
+ export async function waitForQrScan(qrcodeId) {
119
+ while (true) {
120
+ const result = await checkQrStatus(qrcodeId);
121
+ switch (result.status) {
122
+ case 'confirmed':
123
+ return result.account;
124
+ case 'expired':
125
+ throw new Error(result.message);
126
+ case 'error':
127
+ if (result.retryable) {
128
+ logger.info('QR status check failed, retrying', { message: result.message });
129
+ await sleep(POLL_INTERVAL_MS);
130
+ continue;
131
+ }
132
+ throw new Error(result.message);
133
+ default:
134
+ break;
135
+ }
136
+ await sleep(POLL_INTERVAL_MS);
137
+ }
138
+ }
139
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/bridge/wechat/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import fs from 'node:fs';
4
+ import { MessageItemType } from './types.js';
5
+ import { downloadAndDecrypt } from './cdn.js';
6
+ import { logger } from '../logger.js';
7
+ function detectMimeType(data) {
8
+ if (data[0] === 0x89 && data[1] === 0x50)
9
+ return 'image/png';
10
+ if (data[0] === 0xFF && data[1] === 0xD8)
11
+ return 'image/jpeg';
12
+ if (data[0] === 0x47 && data[1] === 0x49)
13
+ return 'image/gif';
14
+ if (data[0] === 0x52 && data[1] === 0x49)
15
+ return 'image/webp';
16
+ if (data[0] === 0x42 && data[1] === 0x4D)
17
+ return 'image/bmp';
18
+ return 'image/jpeg'; // fallback
19
+ }
20
+ /**
21
+ * Extract AES key and encrypt_query_param from an ImageItem,
22
+ * supporting both the old cdn_media format and the newer flat format.
23
+ */
24
+ function getImageCdnData(imageItem) {
25
+ // Old format: cdn_media.aes_key + cdn_media.encrypt_query_param
26
+ if (imageItem.cdn_media?.aes_key && imageItem.cdn_media?.encrypt_query_param) {
27
+ return {
28
+ aesKey: imageItem.cdn_media.aes_key,
29
+ encryptQueryParam: imageItem.cdn_media.encrypt_query_param,
30
+ };
31
+ }
32
+ // New format: aeskey + media.encrypt_query_param
33
+ // Use media.aes_key (base64-of-hex) over aeskey (raw hex) since downloadAndDecrypt expects base64
34
+ if (imageItem.media?.encrypt_query_param && (imageItem.media.aes_key || imageItem.aeskey)) {
35
+ return {
36
+ aesKey: imageItem.media.aes_key ?? imageItem.aeskey,
37
+ encryptQueryParam: imageItem.media.encrypt_query_param,
38
+ };
39
+ }
40
+ logger.warn('Image item has no usable CDN data', {
41
+ hasCdnMedia: !!imageItem.cdn_media,
42
+ hasAeskey: !!imageItem.aeskey,
43
+ hasMedia: !!imageItem.media,
44
+ });
45
+ return null;
46
+ }
47
+ /**
48
+ * Download a CDN image, decrypt it, and save it to a local temp file.
49
+ * Returns the absolute local path, or null on failure.
50
+ */
51
+ export async function downloadImage(item) {
52
+ const imageItem = item.image_item;
53
+ if (!imageItem) {
54
+ return null;
55
+ }
56
+ const cdnData = getImageCdnData(imageItem);
57
+ if (!cdnData) {
58
+ return null;
59
+ }
60
+ try {
61
+ const decrypted = await downloadAndDecrypt(cdnData.encryptQueryParam, cdnData.aesKey);
62
+ const mimeType = detectMimeType(decrypted);
63
+ const ext = mimeType.split('/')[1] || 'png';
64
+ const tmpDir = path.join(os.tmpdir(), 'dsh-wechat-bridge');
65
+ fs.mkdirSync(tmpDir, { recursive: true });
66
+ const filePath = path.join(tmpDir, `img-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`);
67
+ fs.writeFileSync(filePath, decrypted);
68
+ logger.info('Image downloaded and saved', { path: filePath, size: decrypted.length });
69
+ return filePath;
70
+ }
71
+ catch (err) {
72
+ const msg = err instanceof Error ? err.message : String(err);
73
+ logger.warn('Failed to download image', { error: msg });
74
+ return null;
75
+ }
76
+ }
77
+ export function extractText(item) {
78
+ if (item.text_item?.text)
79
+ return item.text_item.text;
80
+ if (item.voice_item?.text)
81
+ return item.voice_item.text;
82
+ if (item.file_item?.file_name)
83
+ return `[用户发送了文件: ${item.file_item.file_name}]`;
84
+ if (item.type === MessageItemType.VIDEO)
85
+ return '[用户发送了视频]';
86
+ return '';
87
+ }
88
+ /**
89
+ * Find the first IMAGE type item in a list.
90
+ */
91
+ export function extractFirstImageUrl(items) {
92
+ return items?.find((item) => item.type === MessageItemType.IMAGE);
93
+ }
94
+ /**
95
+ * Find the first FILE type item in a list.
96
+ */
97
+ export function extractFirstFileItem(items) {
98
+ return items?.find((item) => item.type === MessageItemType.FILE);
99
+ }
100
+ /**
101
+ * Download a CDN file, decrypt it, and save to a temp directory.
102
+ * Returns the local file path, or null on failure.
103
+ */
104
+ export async function downloadFile(item) {
105
+ const fileItem = item.file_item;
106
+ if (!fileItem)
107
+ return null;
108
+ let aesKey;
109
+ let encryptQueryParam;
110
+ if (fileItem.media?.encrypt_query_param) {
111
+ encryptQueryParam = fileItem.media.encrypt_query_param;
112
+ aesKey = fileItem.media.aes_key;
113
+ }
114
+ else if (fileItem.cdn_media?.encrypt_query_param) {
115
+ encryptQueryParam = fileItem.cdn_media.encrypt_query_param;
116
+ aesKey = fileItem.cdn_media.aes_key;
117
+ }
118
+ if (!encryptQueryParam || !aesKey) {
119
+ logger.warn('File item has no usable CDN data');
120
+ return null;
121
+ }
122
+ try {
123
+ const decrypted = await downloadAndDecrypt(encryptQueryParam, aesKey);
124
+ const tmpDir = path.join(os.tmpdir(), 'dsh-wechat-bridge');
125
+ fs.mkdirSync(tmpDir, { recursive: true });
126
+ const fileName = fileItem.file_name || `file-${Date.now()}.bin`;
127
+ const filePath = path.join(tmpDir, fileName);
128
+ fs.writeFileSync(filePath, decrypted);
129
+ logger.info('File downloaded and saved', { path: filePath, size: decrypted.length, name: fileName });
130
+ return filePath;
131
+ }
132
+ catch (err) {
133
+ const msg = err instanceof Error ? err.message : String(err);
134
+ logger.warn('Failed to download file', { error: msg });
135
+ return null;
136
+ }
137
+ }
138
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/bridge/wechat/media.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { loadSyncBuf, saveSyncBuf } from './sync-buf.js';
2
+ import { logger } from '../logger.js';
3
+ const SESSION_EXPIRED_ERRCODE = -14;
4
+ const SESSION_EXPIRED_PAUSE_MS = 60 * 60 * 1000; // 1 hour
5
+ const BACKOFF_THRESHOLD = 3;
6
+ const BACKOFF_LONG_MS = 30_000;
7
+ const BACKOFF_SHORT_MS = 3_000;
8
+ export function createMonitor(api, callbacks) {
9
+ const controller = new AbortController();
10
+ let stopped = false;
11
+ const recentMsgIds = new Set();
12
+ const MAX_MSG_IDS = 1000;
13
+ async function run() {
14
+ let consecutiveFailures = 0;
15
+ while (!controller.signal.aborted) {
16
+ try {
17
+ const buf = loadSyncBuf();
18
+ logger.debug('Polling for messages', { hasBuf: buf.length > 0 });
19
+ const resp = await api.getUpdates(buf || undefined);
20
+ if (resp.ret === SESSION_EXPIRED_ERRCODE) {
21
+ logger.warn('Session expired, pausing for 1 hour');
22
+ callbacks.onSessionExpired();
23
+ await sleep(SESSION_EXPIRED_PAUSE_MS, controller.signal);
24
+ consecutiveFailures = 0;
25
+ continue;
26
+ }
27
+ if (resp.ret !== undefined && resp.ret !== 0) {
28
+ logger.warn('getUpdates returned error', { ret: resp.ret, retmsg: resp.retmsg });
29
+ }
30
+ // Save the new sync buffer regardless of ret
31
+ if (resp.get_updates_buf) {
32
+ saveSyncBuf(resp.get_updates_buf);
33
+ }
34
+ // Process messages (with deduplication)
35
+ const messages = resp.msgs ?? [];
36
+ if (messages.length > 0) {
37
+ logger.info('Received messages', { count: messages.length });
38
+ for (const msg of messages) {
39
+ // Skip already-processed messages
40
+ if (msg.message_id && recentMsgIds.has(msg.message_id)) {
41
+ continue;
42
+ }
43
+ if (msg.message_id) {
44
+ recentMsgIds.add(msg.message_id);
45
+ if (recentMsgIds.size > MAX_MSG_IDS) {
46
+ // Evict oldest half (Set iterates in insertion order)
47
+ const iter = recentMsgIds.values();
48
+ const toDelete = [];
49
+ for (let i = 0; i < MAX_MSG_IDS / 2; i++) {
50
+ const { value } = iter.next();
51
+ if (value !== undefined)
52
+ toDelete.push(value);
53
+ }
54
+ for (const id of toDelete)
55
+ recentMsgIds.delete(id);
56
+ }
57
+ }
58
+ // Fire-and-forget: don't block the polling loop on message processing
59
+ // This allows permission responses (y/n) to be received while a query is running
60
+ callbacks.onMessage(msg).catch((err) => {
61
+ const msg2 = err instanceof Error ? err.message : String(err);
62
+ logger.error('Error processing message', { error: msg2, messageId: msg.message_id });
63
+ });
64
+ }
65
+ }
66
+ consecutiveFailures = 0;
67
+ }
68
+ catch (err) {
69
+ if (controller.signal.aborted) {
70
+ break;
71
+ }
72
+ consecutiveFailures++;
73
+ const errorMsg = err instanceof Error ? err.message : String(err);
74
+ logger.error('Monitor error', { error: errorMsg, consecutiveFailures });
75
+ const backoff = consecutiveFailures >= BACKOFF_THRESHOLD ? BACKOFF_LONG_MS : BACKOFF_SHORT_MS;
76
+ logger.info(`Backing off ${backoff}ms`, { consecutiveFailures });
77
+ await sleep(backoff, controller.signal);
78
+ }
79
+ }
80
+ stopped = true;
81
+ logger.info('Monitor stopped');
82
+ }
83
+ function stop() {
84
+ if (!controller.signal.aborted) {
85
+ logger.info('Stopping monitor...');
86
+ controller.abort();
87
+ }
88
+ }
89
+ return { run, stop };
90
+ }
91
+ function sleep(ms, signal) {
92
+ return new Promise((resolve) => {
93
+ if (signal?.aborted) {
94
+ resolve();
95
+ return;
96
+ }
97
+ const timer = setTimeout(resolve, ms);
98
+ signal?.addEventListener('abort', () => {
99
+ clearTimeout(timer);
100
+ resolve();
101
+ }, { once: true });
102
+ });
103
+ }
104
+ //# sourceMappingURL=monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../../src/bridge/wechat/monitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { MessageItemType, MessageType, MessageState, TypingStatus } from './types.js';
4
+ import { uploadFile } from './upload.js';
5
+ import { logger } from '../logger.js';
6
+ const TYPING_KEEPALIVE_MS = 5_000;
7
+ export function createSender(api, botAccountId) {
8
+ let clientCounter = 0;
9
+ const typingTicketCache = new Map();
10
+ const TICKET_TTL = 24 * 60 * 60 * 1000;
11
+ function generateClientId() {
12
+ return `wcc-${Date.now()}-${++clientCounter}`;
13
+ }
14
+ async function getTypingTicket(userId, contextToken) {
15
+ const cached = typingTicketCache.get(userId);
16
+ if (cached && Date.now() - cached.fetchedAt < TICKET_TTL) {
17
+ return cached.ticket;
18
+ }
19
+ try {
20
+ const resp = await api.getConfig(userId, contextToken);
21
+ if (resp.ret === 0 && resp.typing_ticket) {
22
+ typingTicketCache.set(userId, { ticket: resp.typing_ticket, fetchedAt: Date.now() });
23
+ return resp.typing_ticket;
24
+ }
25
+ logger.warn('getConfig returned no typing_ticket', { ret: resp.ret });
26
+ }
27
+ catch (err) {
28
+ logger.warn('getConfig failed', { err: err instanceof Error ? err.message : String(err) });
29
+ }
30
+ return '';
31
+ }
32
+ /**
33
+ * Start typing indicator with keepalive. Returns a stop function.
34
+ * Fire-and-forget: errors are logged but not thrown.
35
+ */
36
+ function startTyping(toUserId, contextToken) {
37
+ let cancelled = false;
38
+ (async () => {
39
+ const ticket = await getTypingTicket(toUserId, contextToken);
40
+ if (!ticket || cancelled)
41
+ return;
42
+ try {
43
+ await api.sendTyping({
44
+ ilink_user_id: toUserId,
45
+ typing_ticket: ticket,
46
+ status: TypingStatus.TYPING,
47
+ });
48
+ }
49
+ catch (err) {
50
+ logger.debug('sendTyping start failed', { err: err instanceof Error ? err.message : String(err) });
51
+ return;
52
+ }
53
+ // Keepalive loop
54
+ while (!cancelled) {
55
+ await new Promise(r => setTimeout(r, TYPING_KEEPALIVE_MS));
56
+ if (cancelled)
57
+ break;
58
+ try {
59
+ await api.sendTyping({
60
+ ilink_user_id: toUserId,
61
+ typing_ticket: ticket,
62
+ status: TypingStatus.TYPING,
63
+ });
64
+ }
65
+ catch {
66
+ break;
67
+ }
68
+ }
69
+ // Send CANCEL to tell WeChat we're done typing
70
+ if (!ticket)
71
+ return;
72
+ try {
73
+ await api.sendTyping({
74
+ ilink_user_id: toUserId,
75
+ typing_ticket: ticket,
76
+ status: TypingStatus.CANCEL,
77
+ });
78
+ }
79
+ catch {
80
+ // ignore
81
+ }
82
+ })();
83
+ return () => {
84
+ cancelled = true;
85
+ };
86
+ }
87
+ async function sendText(toUserId, contextToken, text) {
88
+ const clientId = generateClientId();
89
+ const items = [
90
+ {
91
+ type: MessageItemType.TEXT,
92
+ text_item: { text },
93
+ },
94
+ ];
95
+ const msg = {
96
+ from_user_id: botAccountId,
97
+ to_user_id: toUserId,
98
+ client_id: clientId,
99
+ message_type: MessageType.BOT,
100
+ message_state: MessageState.FINISH,
101
+ context_token: contextToken,
102
+ item_list: items,
103
+ };
104
+ logger.info('Sending text message', { toUserId, clientId, textLength: text.length });
105
+ await api.sendMessage({ msg });
106
+ logger.info('Text message sent', { toUserId, clientId });
107
+ }
108
+ async function sendFile(toUserId, contextToken, filePath) {
109
+ const resolved = resolve(filePath.replace(/^~/, process.env.HOME || ''));
110
+ if (!existsSync(resolved)) {
111
+ await sendText(toUserId, contextToken, `文件不存在: ${resolved}`);
112
+ return;
113
+ }
114
+ try {
115
+ const media = await uploadFile(api, toUserId, resolved);
116
+ const clientId = generateClientId();
117
+ // Convert aesKeyHex to base64: treat hex string as UTF-8, then base64 encode
118
+ // (matches OpenClaw's format: Buffer.from(hexString).toString("base64"))
119
+ const aesKeyBase64 = Buffer.from(media.aesKeyHex).toString('base64');
120
+ let item;
121
+ if (media.mediaType === 'image') {
122
+ item = {
123
+ type: MessageItemType.IMAGE,
124
+ image_item: {
125
+ media: {
126
+ encrypt_query_param: media.encryptQueryParam,
127
+ aes_key: aesKeyBase64,
128
+ encrypt_type: 1,
129
+ },
130
+ mid_size: media.fileSize,
131
+ },
132
+ };
133
+ }
134
+ else {
135
+ item = {
136
+ type: MessageItemType.FILE,
137
+ file_item: {
138
+ media: {
139
+ encrypt_query_param: media.encryptQueryParam,
140
+ aes_key: aesKeyBase64,
141
+ encrypt_type: 1,
142
+ },
143
+ file_name: media.fileName,
144
+ len: String(media.rawSize),
145
+ },
146
+ };
147
+ }
148
+ const msg = {
149
+ from_user_id: botAccountId,
150
+ to_user_id: toUserId,
151
+ client_id: clientId,
152
+ message_type: MessageType.BOT,
153
+ message_state: MessageState.FINISH,
154
+ context_token: contextToken,
155
+ item_list: [item],
156
+ };
157
+ logger.info('Sending file message', { toUserId, clientId, fileName: media.fileName, mediaType: media.mediaType });
158
+ await api.sendMessage({ msg });
159
+ logger.info('File message sent', { toUserId, clientId, fileName: media.fileName });
160
+ }
161
+ catch (err) {
162
+ const msg = err instanceof Error ? err.message : String(err);
163
+ logger.error('Failed to send file', { filePath: resolved, error: msg });
164
+ if (!msg.includes('rate-limited')) {
165
+ await sendText(toUserId, contextToken, `发送文件失败: ${msg}`);
166
+ }
167
+ throw err;
168
+ }
169
+ }
170
+ return { sendText, startTyping, sendFile };
171
+ }
172
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../../src/bridge/wechat/send.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { loadJson, saveJson } from '../store.js';
2
+ import { DATA_DIR } from '../constants.js';
3
+ import { join } from 'node:path';
4
+ const SYNC_BUF_PATH = join(DATA_DIR, 'get_updates_buf');
5
+ export function loadSyncBuf() {
6
+ return loadJson(SYNC_BUF_PATH, '');
7
+ }
8
+ export function saveSyncBuf(buf) {
9
+ saveJson(SYNC_BUF_PATH, buf);
10
+ }
11
+ //# sourceMappingURL=sync-buf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-buf.js","sourceRoot":"","sources":["../../../src/bridge/wechat/sync-buf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAExD,MAAM,UAAU,WAAW;IACzB,OAAO,QAAQ,CAAS,aAAa,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,35 @@
1
+ // WeChat Work (企业微信) protocol type definitions
2
+ // Extracted from the ClawBot WeChat plugin API
3
+ // ── Enums ──────────────────────────────────────────────────────────────────
4
+ export var MessageType;
5
+ (function (MessageType) {
6
+ MessageType[MessageType["USER"] = 1] = "USER";
7
+ MessageType[MessageType["BOT"] = 2] = "BOT";
8
+ })(MessageType || (MessageType = {}));
9
+ export var MessageItemType;
10
+ (function (MessageItemType) {
11
+ MessageItemType[MessageItemType["TEXT"] = 1] = "TEXT";
12
+ MessageItemType[MessageItemType["IMAGE"] = 2] = "IMAGE";
13
+ MessageItemType[MessageItemType["VOICE"] = 3] = "VOICE";
14
+ MessageItemType[MessageItemType["FILE"] = 4] = "FILE";
15
+ MessageItemType[MessageItemType["VIDEO"] = 5] = "VIDEO";
16
+ })(MessageItemType || (MessageItemType = {}));
17
+ export var MessageState;
18
+ (function (MessageState) {
19
+ MessageState[MessageState["NEW"] = 0] = "NEW";
20
+ MessageState[MessageState["GENERATING"] = 1] = "GENERATING";
21
+ MessageState[MessageState["FINISH"] = 2] = "FINISH";
22
+ })(MessageState || (MessageState = {}));
23
+ // ── Typing API ──────────────────────────────────────────────────────────────
24
+ export const TypingStatus = {
25
+ TYPING: 1,
26
+ CANCEL: 2,
27
+ };
28
+ // ── GetUploadUrl API ────────────────────────────────────────────────────────
29
+ export const UploadMediaType = {
30
+ IMAGE: 1,
31
+ VIDEO: 2,
32
+ FILE: 3,
33
+ VOICE: 4,
34
+ };
35
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/bridge/wechat/types.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,+CAA+C;AAE/C,8EAA8E;AAE9E,MAAM,CAAN,IAAY,WAGX;AAHD,WAAY,WAAW;IACrB,6CAAQ,CAAA;IACR,2CAAO,CAAA;AACT,CAAC,EAHW,WAAW,KAAX,WAAW,QAGtB;AAED,MAAM,CAAN,IAAY,eAMX;AAND,WAAY,eAAe;IACzB,qDAAQ,CAAA;IACR,uDAAS,CAAA;IACT,uDAAS,CAAA;IACT,qDAAQ,CAAA;IACR,uDAAS,CAAA;AACX,CAAC,EANW,eAAe,KAAf,eAAe,QAM1B;AAED,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,6CAAO,CAAA;IACP,2DAAc,CAAA;IACd,mDAAU,CAAA;AACZ,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB;AAgGD,+EAA+E;AAE/E,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACD,CAAC;AAcX,+EAA+E;AAE/E,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACA,CAAC"}