wechaty-puppet-matrix 0.0.53 → 0.0.55

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 (57) hide show
  1. package/dist/cjs/src/matrix/messages/message-image.d.ts +8 -0
  2. package/dist/cjs/src/matrix/messages/message-image.d.ts.map +1 -1
  3. package/dist/cjs/src/matrix/messages/message-image.js +38 -2
  4. package/dist/cjs/src/matrix/messages/message-image.spec.d.ts +3 -0
  5. package/dist/cjs/src/matrix/messages/message-image.spec.d.ts.map +1 -0
  6. package/dist/cjs/src/matrix/messages/message-image.spec.js +66 -0
  7. package/dist/cjs/src/matrix/messages/message-video.d.ts +6 -0
  8. package/dist/cjs/src/matrix/messages/message-video.d.ts.map +1 -1
  9. package/dist/cjs/src/matrix/messages/message-video.js +20 -0
  10. package/dist/cjs/src/matrix/messages/message-video.spec.d.ts +3 -0
  11. package/dist/cjs/src/matrix/messages/message-video.spec.d.ts.map +1 -0
  12. package/dist/cjs/src/matrix/messages/message-video.spec.js +27 -0
  13. package/dist/cjs/src/matrix/service/request.d.ts +9 -0
  14. package/dist/cjs/src/matrix/service/request.d.ts.map +1 -1
  15. package/dist/cjs/src/matrix/service/request.js +76 -42
  16. package/dist/cjs/src/matrix/utils/file-type.d.ts +4 -0
  17. package/dist/cjs/src/matrix/utils/file-type.d.ts.map +1 -0
  18. package/dist/cjs/src/matrix/utils/file-type.js +55 -0
  19. package/dist/cjs/src/matrix/utils/file-type.spec.d.ts +3 -0
  20. package/dist/cjs/src/matrix/utils/file-type.spec.d.ts.map +1 -0
  21. package/dist/cjs/src/matrix/utils/file-type.spec.js +25 -0
  22. package/dist/cjs/src/puppet-matrix.d.ts +1 -0
  23. package/dist/cjs/src/puppet-matrix.d.ts.map +1 -1
  24. package/dist/cjs/src/puppet-matrix.js +17 -2
  25. package/dist/esm/src/matrix/messages/message-image.d.ts +8 -0
  26. package/dist/esm/src/matrix/messages/message-image.d.ts.map +1 -1
  27. package/dist/esm/src/matrix/messages/message-image.js +37 -2
  28. package/dist/esm/src/matrix/messages/message-image.spec.d.ts +3 -0
  29. package/dist/esm/src/matrix/messages/message-image.spec.d.ts.map +1 -0
  30. package/dist/esm/src/matrix/messages/message-image.spec.js +64 -0
  31. package/dist/esm/src/matrix/messages/message-video.d.ts +6 -0
  32. package/dist/esm/src/matrix/messages/message-video.d.ts.map +1 -1
  33. package/dist/esm/src/matrix/messages/message-video.js +19 -0
  34. package/dist/esm/src/matrix/messages/message-video.spec.d.ts +3 -0
  35. package/dist/esm/src/matrix/messages/message-video.spec.d.ts.map +1 -0
  36. package/dist/esm/src/matrix/messages/message-video.spec.js +25 -0
  37. package/dist/esm/src/matrix/service/request.d.ts +9 -0
  38. package/dist/esm/src/matrix/service/request.d.ts.map +1 -1
  39. package/dist/esm/src/matrix/service/request.js +75 -42
  40. package/dist/esm/src/matrix/utils/file-type.d.ts +4 -0
  41. package/dist/esm/src/matrix/utils/file-type.d.ts.map +1 -0
  42. package/dist/esm/src/matrix/utils/file-type.js +47 -0
  43. package/dist/esm/src/matrix/utils/file-type.spec.d.ts +3 -0
  44. package/dist/esm/src/matrix/utils/file-type.spec.d.ts.map +1 -0
  45. package/dist/esm/src/matrix/utils/file-type.spec.js +23 -0
  46. package/dist/esm/src/puppet-matrix.d.ts +1 -0
  47. package/dist/esm/src/puppet-matrix.d.ts.map +1 -1
  48. package/dist/esm/src/puppet-matrix.js +17 -2
  49. package/package.json +1 -1
  50. package/src/matrix/messages/message-image.spec.ts +107 -0
  51. package/src/matrix/messages/message-image.ts +63 -2
  52. package/src/matrix/messages/message-video.spec.ts +37 -0
  53. package/src/matrix/messages/message-video.ts +28 -1
  54. package/src/matrix/service/request.ts +108 -42
  55. package/src/matrix/utils/file-type.spec.ts +29 -0
  56. package/src/matrix/utils/file-type.ts +59 -0
  57. package/src/puppet-matrix.ts +22 -3
@@ -97,6 +97,10 @@ export var WechatMessageType;
97
97
  WechatMessageType[WechatMessageType["Sys"] = 10000] = "Sys";
98
98
  WechatMessageType[WechatMessageType["SysTemplate"] = 10002] = "SysTemplate";
99
99
  })(WechatMessageType || (WechatMessageType = {}));
100
+ export function parseSizeMismatch(errmsg) {
101
+ const match = typeof errmsg === 'string' ? errmsg.match(/size differs: expected \d+, got (\d+)/) : null;
102
+ return match && Number(match[1]) > 0 ? match[1] : undefined;
103
+ }
100
104
  export var FriendshipType;
101
105
  (function (FriendshipType) {
102
106
  FriendshipType[FriendshipType["Unknown"] = 0] = "Unknown";
@@ -1349,8 +1353,7 @@ class Client extends EventEmitter {
1349
1353
  },
1350
1354
  });
1351
1355
  }
1352
- async retryDownload(fn) {
1353
- const maxRetries = 5;
1356
+ async retryDownload(fn, maxRetries = 5) {
1354
1357
  const delayMs = 5000;
1355
1358
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
1356
1359
  try {
@@ -1359,6 +1362,9 @@ class Client extends EventEmitter {
1359
1362
  return res;
1360
1363
  }
1361
1364
  log.warn(PRE, 'download attempt %d/%d failed (no url in response): %s', attempt, maxRetries, JSON.stringify(res));
1365
+ if (parseSizeMismatch(res?.errmsg)) {
1366
+ return res;
1367
+ }
1362
1368
  }
1363
1369
  catch (e) {
1364
1370
  log.warn(PRE, 'download attempt %d/%d error: %s', attempt, maxRetries, e);
@@ -1369,21 +1375,51 @@ class Client extends EventEmitter {
1369
1375
  }
1370
1376
  return null;
1371
1377
  }
1378
+ async cdnDownloadWithFallback(label, baseParams, candidates) {
1379
+ const queue = [...candidates];
1380
+ const tried = new Set();
1381
+ const retriesPerCandidate = candidates.length > 1 ? 2 : 5;
1382
+ while (queue.length) {
1383
+ const candidate = queue.shift();
1384
+ const key = `${candidate.file_id}|${candidate.file_size}|${candidate.aes_key || ''}`;
1385
+ if (!candidate.file_id || tried.has(key)) {
1386
+ continue;
1387
+ }
1388
+ tried.add(key);
1389
+ const params = {
1390
+ ...baseParams,
1391
+ file_id: candidate.file_id,
1392
+ file_size: candidate.file_size,
1393
+ ...(candidate.aes_key ? { aes_key: candidate.aes_key } : {}),
1394
+ };
1395
+ const res = await this.retryDownload(() => this.postData({ path: '/cloud/cdn_download', data: params }), retriesPerCandidate);
1396
+ if (res?.url) {
1397
+ if (tried.size > 1) {
1398
+ log.info(PRE, '%s succeeded with fallback, file_size: %s', label, candidate.file_size);
1399
+ }
1400
+ return encodeURI(res.url) || '';
1401
+ }
1402
+ const actualSize = parseSizeMismatch(res?.errmsg);
1403
+ if (actualSize) {
1404
+ log.warn(PRE, '%s size mismatch (xml %s, actual %s), retry with actual size', label, candidate.file_size, actualSize);
1405
+ queue.unshift({ ...candidate, file_size: actualSize });
1406
+ continue;
1407
+ }
1408
+ log.warn(PRE, '%s candidate failed, params: %s', label, JSON.stringify(params));
1409
+ }
1410
+ log.error(PRE, '%s failed for all candidates: %s', label, JSON.stringify(candidates));
1411
+ return '';
1412
+ }
1372
1413
  async downloadImage(imageInfo) {
1373
- const params = {
1414
+ const candidates = imageInfo.candidates?.length
1415
+ ? imageInfo.candidates
1416
+ : [{ file_id: imageInfo.file_id, file_size: imageInfo.file_size || '' }];
1417
+ return this.cdnDownloadWithFallback('downloadImage', {
1374
1418
  log_response: true,
1375
1419
  file_type: 2,
1376
- file_id: imageInfo.file_id,
1377
1420
  aes_key: imageInfo.aes_key,
1378
- file_size: imageInfo.file_size,
1379
1421
  file_name: `/微信图片_${format(new Date(), 'yyyyMMddHHmmss')}.png`,
1380
- };
1381
- const res = await this.retryDownload(() => this.postData({ path: '/cloud/cdn_download', data: params }));
1382
- if (res?.url) {
1383
- return encodeURI(res.url) || '';
1384
- }
1385
- log.error(PRE, 'downloadImage failed after %d retries, params: %s', 5, JSON.stringify(params));
1386
- return '';
1422
+ }, candidates);
1387
1423
  }
1388
1424
  async downloadWWMedia(imageInfo, type) {
1389
1425
  const typeNameMap = {
@@ -1407,20 +1443,12 @@ class Client extends EventEmitter {
1407
1443
  return '';
1408
1444
  }
1409
1445
  async downloadFile(imageInfo) {
1410
- const params = {
1446
+ return this.cdnDownloadWithFallback('downloadFile', {
1411
1447
  log_response: true,
1412
1448
  file_type: 5,
1413
- file_id: imageInfo.file_id.replace('@cdn_', ''),
1414
1449
  aes_key: imageInfo.aes_key,
1415
- file_size: imageInfo.file_size,
1416
1450
  file_name: '/' + sanitizeFilename(imageInfo.file_name),
1417
- };
1418
- const res = await this.retryDownload(() => this.postData({ path: '/cloud/cdn_download', data: params }));
1419
- if (res?.url) {
1420
- return encodeURI(res.url) || '';
1421
- }
1422
- log.error(PRE, 'downloadFile failed after %d retries, params: %s', 5, JSON.stringify(params));
1423
- return '';
1451
+ }, [{ file_id: imageInfo.file_id.replace('@cdn_', ''), file_size: imageInfo.file_size || '' }]);
1424
1452
  }
1425
1453
  async downloadAudio(msgId, voiceInfo, roomId) {
1426
1454
  const params = {
@@ -1450,6 +1478,7 @@ class Client extends EventEmitter {
1450
1478
  return await this.postData({ path: '/msg/get_voice_trans', data: { voice_id: voiceId } });
1451
1479
  }
1452
1480
  async voiceToText(msgId, length, timeoutMs = 20000) {
1481
+ const brief = (res) => JSON.stringify(res, (k, v) => (k === 'buffer' ? undefined : v));
1453
1482
  try {
1454
1483
  const idRes = await this.newVoiceTransId();
1455
1484
  const voiceId = idRes?.voice_id;
@@ -1457,15 +1486,24 @@ class Client extends EventEmitter {
1457
1486
  log.warn(PRE, 'voiceToText: no voice_id: %s', JSON.stringify(idRes));
1458
1487
  return '';
1459
1488
  }
1460
- const checkRes = await this.checkVoiceTrans(voiceId, msgId, length);
1461
- if (checkRes?.baseResponse?.ret !== 0) {
1462
- log.warn(PRE, 'voiceToText: check failed: %s', JSON.stringify(checkRes));
1463
- return '';
1464
- }
1465
- const uploadRes = await this.uploadVoiceTrans(voiceId, msgId, length);
1466
- if (uploadRes?.baseResponse?.ret !== 0) {
1467
- log.warn(PRE, 'voiceToText: upload failed: %s', JSON.stringify(uploadRes));
1468
- return '';
1489
+ let checkRes;
1490
+ for (let round = 0; round < 20; round++) {
1491
+ checkRes = await this.checkVoiceTrans(voiceId, msgId, length);
1492
+ log.verbose(PRE, 'voiceToText check#%d(length=%d): %s', round, length, brief(checkRes));
1493
+ if (checkRes?.baseResponse?.ret !== 0) {
1494
+ log.warn(PRE, 'voiceToText: check failed: %s', brief(checkRes));
1495
+ return '';
1496
+ }
1497
+ const ctx = checkRes?.uploadCtx;
1498
+ if (!ctx?.dataLen) {
1499
+ break;
1500
+ }
1501
+ const uploadRes = await this.uploadVoiceTrans(voiceId, msgId, length);
1502
+ log.verbose(PRE, 'voiceToText upload#%d: %s', round, brief(uploadRes));
1503
+ if (uploadRes?.baseResponse?.ret !== 0) {
1504
+ log.warn(PRE, 'voiceToText: upload failed: %s', brief(uploadRes));
1505
+ return '';
1506
+ }
1469
1507
  }
1470
1508
  const interval = (checkRes?.queryCtx?.interval || 2) * 1000;
1471
1509
  const deadline = Date.now() + timeoutMs;
@@ -1473,7 +1511,7 @@ class Client extends EventEmitter {
1473
1511
  await new Promise(resolve => setTimeout(resolve, interval));
1474
1512
  const res = await this.getVoiceTrans(voiceId);
1475
1513
  if (res?.baseResponse?.ret !== 0) {
1476
- log.warn(PRE, 'voiceToText: get result failed: %s', JSON.stringify(res));
1514
+ log.warn(PRE, 'voiceToText: get result failed: %s', brief(res));
1477
1515
  return '';
1478
1516
  }
1479
1517
  if (res?.transRes?.endFlag === 1) {
@@ -1488,19 +1526,14 @@ class Client extends EventEmitter {
1488
1526
  return '';
1489
1527
  }
1490
1528
  async downloadVideo(videoInfo) {
1491
- const params = {
1529
+ const candidates = videoInfo.candidates?.length
1530
+ ? videoInfo.candidates
1531
+ : [{ file_id: videoInfo.file_id, file_size: videoInfo.file_size }];
1532
+ return this.cdnDownloadWithFallback('downloadVideo', {
1492
1533
  file_type: 4,
1493
- file_id: videoInfo.file_id,
1494
1534
  aes_key: videoInfo.aes_key,
1495
- file_size: videoInfo.file_size,
1496
1535
  file_name: `/微信视频_${format(new Date(), 'yyyyMMddHHmmss')}.mp4`,
1497
- };
1498
- const res = await this.retryDownload(() => this.postData({ path: '/cloud/cdn_download', data: params }));
1499
- if (res?.url) {
1500
- return encodeURI(res.url) || '';
1501
- }
1502
- log.error(PRE, 'downloadVideo failed after %d retries, params: %s', 5, JSON.stringify(params));
1503
- return '';
1536
+ }, candidates);
1504
1537
  }
1505
1538
  async sendUrlImg(contactId, url, selfId) {
1506
1539
  try {
@@ -0,0 +1,4 @@
1
+ export declare function detectImageExt(head: Buffer): string | undefined;
2
+ export declare function fetchRemoteHead(url: string, bytes?: number, timeoutMs?: number): Promise<Buffer | undefined>;
3
+ export declare function replaceExt(fileName: string, ext: string): string;
4
+ //# sourceMappingURL=file-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-type.d.ts","sourceRoot":"","sources":["../../../../../src/matrix/utils/file-type.ts"],"names":[],"mappings":"AAKA,wBAAgB,cAAc,CAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA0BhE;AAKD,wBAAsB,eAAe,CAAE,GAAG,EAAE,MAAM,EAAE,KAAK,SAAK,EAAE,SAAS,SAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAa7G;AAKD,wBAAgB,UAAU,CAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjE"}
@@ -0,0 +1,47 @@
1
+ import axios from 'axios';
2
+ export function detectImageExt(head) {
3
+ if (head.length >= 3 && head[0] === 0xff && head[1] === 0xd8 && head[2] === 0xff) {
4
+ return 'jpg';
5
+ }
6
+ if (head.length >= 8 && head.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
7
+ return 'png';
8
+ }
9
+ if (head.length >= 6 && ['GIF87a', 'GIF89a'].includes(head.subarray(0, 6).toString('ascii'))) {
10
+ return 'gif';
11
+ }
12
+ if (head.length >= 12 && head.subarray(0, 4).toString('ascii') === 'RIFF' && head.subarray(8, 12).toString('ascii') === 'WEBP') {
13
+ return 'webp';
14
+ }
15
+ if (head.length >= 2 && head[0] === 0x42 && head[1] === 0x4d) {
16
+ return 'bmp';
17
+ }
18
+ if (head.length >= 12 && head.subarray(4, 8).toString('ascii') === 'ftyp') {
19
+ const brand = head.subarray(8, 12).toString('ascii');
20
+ if (['heic', 'heix', 'hevc', 'hevx', 'mif1', 'msf1'].includes(brand)) {
21
+ return 'heic';
22
+ }
23
+ if (['avif', 'avis'].includes(brand)) {
24
+ return 'avif';
25
+ }
26
+ }
27
+ return undefined;
28
+ }
29
+ export async function fetchRemoteHead(url, bytes = 32, timeoutMs = 5000) {
30
+ try {
31
+ const res = await axios.get(url, {
32
+ headers: { Range: `bytes=0-${bytes - 1}` },
33
+ responseType: 'arraybuffer',
34
+ timeout: timeoutMs,
35
+ maxContentLength: 50 * 1024 * 1024,
36
+ });
37
+ return Buffer.from(res.data).subarray(0, bytes);
38
+ }
39
+ catch {
40
+ return undefined;
41
+ }
42
+ }
43
+ export function replaceExt(fileName, ext) {
44
+ const dot = fileName.lastIndexOf('.');
45
+ const base = dot > 0 ? fileName.slice(0, dot) : fileName;
46
+ return `${base}.${ext}`;
47
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env -S node -r ts-node/register
2
+ export {};
3
+ //# sourceMappingURL=file-type.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-type.spec.d.ts","sourceRoot":"","sources":["../../../../../src/matrix/utils/file-type.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env -S node -r ts-node/register
2
+ import { test } from 'tstest';
3
+ import { detectImageExt, replaceExt } from './file-type.js';
4
+ const hex = (s) => Buffer.from(s, 'hex');
5
+ test('detectImageExt recognizes common image headers', async (t) => {
6
+ t.equal(detectImageExt(hex('ffd8fffe00104c61766335392e33372e')), 'jpg', 'jpeg');
7
+ t.equal(detectImageExt(hex('89504e470d0a1a0a0000000d49484452')), 'png', 'png');
8
+ t.equal(detectImageExt(Buffer.from('GIF89a......')), 'gif', 'gif');
9
+ t.equal(detectImageExt(Buffer.concat([Buffer.from('RIFF'), hex('00000000'), Buffer.from('WEBPVP8 ')])), 'webp', 'webp');
10
+ t.equal(detectImageExt(hex('424d000000000000')), 'bmp', 'bmp');
11
+ t.equal(detectImageExt(Buffer.concat([hex('00000018'), Buffer.from('ftypheic'), hex('00000000')])), 'heic', 'heic');
12
+ t.equal(detectImageExt(Buffer.concat([hex('0000001c'), Buffer.from('ftypavif'), hex('00000000')])), 'avif', 'avif');
13
+ });
14
+ test('detectImageExt returns undefined for unknown or short data', async (t) => {
15
+ t.equal(detectImageExt(hex('0000001c667479706d703432')), undefined, 'mp4 is not an image');
16
+ t.equal(detectImageExt(Buffer.from('<html>')), undefined, 'html');
17
+ t.equal(detectImageExt(Buffer.alloc(0)), undefined, 'empty');
18
+ });
19
+ test('replaceExt replaces or appends extension', async (t) => {
20
+ t.equal(replaceExt('微信图片_20261105234022.png', 'jpg'), '微信图片_20261105234022.jpg', 'replace');
21
+ t.equal(replaceExt('image', 'gif'), 'image.gif', 'append');
22
+ t.equal(replaceExt('.hidden', 'jpg'), '.hidden.jpg', 'dotfile');
23
+ });
@@ -126,6 +126,7 @@ declare class PuppetMatrix extends PUPPET.Puppet {
126
126
  private _stopPuppetHeart;
127
127
  private _fileBoxFromUrl;
128
128
  private _getMessageImageFileBox;
129
+ private _imageNameByHead;
129
130
  private _getMessageAudioFileBox;
130
131
  private _getMessageVideoFileBox;
131
132
  private _getMessageFileFileBox;
@@ -1 +1 @@
1
- {"version":3,"file":"puppet-matrix.d.ts","sourceRoot":"","sources":["../../../src/puppet-matrix.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"puppet-matrix.d.ts","sourceRoot":"","sources":["../../../src/puppet-matrix.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { WechatMessageType } from './matrix/types.js';
5
5
  import Client from './matrix/service/request.js';
6
6
  import { getUnixTime } from 'date-fns';
7
7
  import { mergeWithFallback } from './matrix/utils/index.js';
8
+ import { detectImageExt, fetchRemoteHead, replaceExt } from './matrix/utils/file-type.js';
8
9
  import path from 'path';
9
10
  import { CacheManager } from './matrix/cache-manager.js';
10
11
  import { isIMContactId, isRoomId } from './matrix/utils/is-type.js';
@@ -1288,7 +1289,7 @@ class PuppetMatrix extends PUPPET.Puppet {
1288
1289
  }
1289
1290
  try {
1290
1291
  const audio = await parseAudioMessagePayload(message);
1291
- const length = parseInt(audio.voicelength, 10);
1292
+ const length = parseInt(audio.length, 10);
1292
1293
  if (!length) {
1293
1294
  return;
1294
1295
  }
@@ -1456,7 +1457,21 @@ class PuppetMatrix extends PUPPET.Puppet {
1456
1457
  log.error(`Can not get file for message: ${messageId}`);
1457
1458
  }
1458
1459
  const url = imageInfo.is_ww ? await this._client?.downloadWWMedia(imageInfo, 1) : await this._client?.downloadImage(imageInfo);
1459
- return this._fileBoxFromUrl(url, messageId);
1460
+ return this._fileBoxFromUrl(url, messageId, url ? await this._imageNameByHead(url) : undefined);
1461
+ }
1462
+ async _imageNameByHead(url) {
1463
+ const head = await fetchRemoteHead(url);
1464
+ const ext = head && detectImageExt(head);
1465
+ if (!ext) {
1466
+ return undefined;
1467
+ }
1468
+ let rawName = 'image';
1469
+ try {
1470
+ rawName = decodeURIComponent(new URL(url).pathname.split('/').pop() || rawName);
1471
+ }
1472
+ catch {
1473
+ }
1474
+ return replaceExt(rawName, ext);
1460
1475
  }
1461
1476
  async _getMessageAudioFileBox(messageId, messagePayload) {
1462
1477
  const audioInfo = await parseAudioMessagePayload(messagePayload);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wechaty-puppet-matrix",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "description": "Puppet matrix for Wechaty",
5
5
  "type": "module",
6
6
  "typings": "./dist/esm/src/mod.d.ts",
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env -S node -r ts-node/register
2
+ import { test } from 'tstest'
3
+
4
+ import type { MessagePayload } from '../../engine-schema.js'
5
+ import { parseImageMessagePayload } from './message-image.js'
6
+
7
+ const buildPayload = (imgAttrs: string): MessagePayload => ({
8
+ msg: `<?xml version="1.0"?>\n<msg>\n\t<img ${imgAttrs}>\n\t</img>\n</msg>\n`,
9
+ } as unknown as MessagePayload)
10
+
11
+ test('parseImageMessagePayload uses hevc_mid_size when downloading mid image', async t => {
12
+ const payload = await parseImageMessagePayload(buildPayload(
13
+ 'aeskey="key" cdnmidimgurl="mid-url" length="157626" md5="md5" hevc_mid_size="53274"',
14
+ ))
15
+
16
+ t.equal(payload.file_id, 'mid-url', 'should use mid image url')
17
+ t.equal(payload.file_size, '53274', 'size should match the mid image, not the origin length')
18
+ t.equal(payload.is_ww, false, 'should not be ww image')
19
+ })
20
+
21
+ test('parseImageMessagePayload prefers cdnmidimgurl_size', async t => {
22
+ const payload = await parseImageMessagePayload(buildPayload(
23
+ 'aeskey="key" cdnmidimgurl="mid-url" length="157626" hevc_mid_size="53274" cdnmidimgurl_size="60000"',
24
+ ))
25
+
26
+ t.equal(payload.file_size, '60000', 'cdnmidimgurl_size should win')
27
+ })
28
+
29
+ test('parseImageMessagePayload falls back to length for legacy xml', async t => {
30
+ const payload = await parseImageMessagePayload(buildPayload(
31
+ 'aeskey="key" cdnmidimgurl="mid-url" length="12345" hevc_mid_size="0"',
32
+ ))
33
+
34
+ t.equal(payload.file_size, '12345', 'length should be used when no mid size is available')
35
+ })
36
+
37
+ test('parseImageMessagePayload falls back to length when mid size attrs are absent', async t => {
38
+ const payload = await parseImageMessagePayload(buildPayload(
39
+ 'aeskey="key" cdnmidimgurl="mid-url" length="12345" md5="md5"',
40
+ ))
41
+
42
+ t.equal(payload.file_size, '12345', 'length should be used when hevc_mid_size is missing')
43
+ })
44
+
45
+ test('parseImageMessagePayload uses big image url when mid size is missing', async t => {
46
+ const payload = await parseImageMessagePayload(buildPayload(
47
+ 'aeskey="key" cdnmidimgurl="mid-url" cdnbigimgurl="big-url" length="157626"',
48
+ ))
49
+
50
+ t.equal(payload.file_id, 'big-url', 'should switch to origin image url')
51
+ t.equal(payload.file_size, '157626', 'size should be origin length')
52
+ })
53
+
54
+ test('parseImageMessagePayload keeps mid url for legacy xml with hdlength', async t => {
55
+ const payload = await parseImageMessagePayload(buildPayload(
56
+ 'aeskey="key" cdnmidimgurl="mid-url" cdnbigimgurl="big-url" length="12345" hdlength="99999"',
57
+ ))
58
+
59
+ t.equal(payload.file_id, 'mid-url', 'legacy xml should still use mid url')
60
+ t.equal(payload.file_size, '12345', 'legacy length is mid image size')
61
+ })
62
+
63
+ test('parseImageMessagePayload keeps length for ww image', async t => {
64
+ const payload = await parseImageMessagePayload(buildPayload(
65
+ 'aeskey="key" tpurl="tp-url" tpauthkey="auth" length="888" hevc_mid_size="111"',
66
+ ))
67
+
68
+ t.equal(payload.is_ww, true, 'should be ww image')
69
+ t.equal(payload.file_id, 'tp-url', 'should use tpurl')
70
+ t.equal(payload.file_size, '888', 'ww image should keep length')
71
+ })
72
+
73
+ test('parseImageMessagePayload builds fallback candidates for new xml', async t => {
74
+ const payload = await parseImageMessagePayload(buildPayload(
75
+ 'aeskey="key" cdnmidimgurl="mid-url" cdnbigimgurl="big-url" length="157626" hevc_mid_size="53274"',
76
+ ))
77
+
78
+ t.same(payload.candidates, [
79
+ { file_id: 'mid-url', file_size: '53274' },
80
+ { file_id: 'big-url', file_size: '157626' },
81
+ { file_id: 'mid-url', file_size: '157626' },
82
+ ], 'should try mid size first, then origin, then legacy combination')
83
+ t.equal(payload.file_id, payload.candidates?.[0]?.file_id, 'file_id should be first candidate')
84
+ t.equal(payload.file_size, payload.candidates?.[0]?.file_size, 'file_size should be first candidate')
85
+ })
86
+
87
+ test('parseImageMessagePayload builds fallback candidates for legacy xml', async t => {
88
+ const payload = await parseImageMessagePayload(buildPayload(
89
+ 'aeskey="key" cdnmidimgurl="mid-url" cdnbigimgurl="big-url" length="12345" hdlength="99999"',
90
+ ))
91
+
92
+ t.same(payload.candidates, [
93
+ { file_id: 'mid-url', file_size: '12345' },
94
+ { file_id: 'big-url', file_size: '99999' },
95
+ ], 'legacy xml should try mid + length, then big + hdlength')
96
+ })
97
+
98
+ test('parseImageMessagePayload dedupes candidates', async t => {
99
+ const payload = await parseImageMessagePayload(buildPayload(
100
+ 'aeskey="key" cdnmidimgurl="mid-url" length="157626" hevc_mid_size="53274" cdnmidimgurl_size="53274"',
101
+ ))
102
+
103
+ t.same(payload.candidates, [
104
+ { file_id: 'mid-url', file_size: '53274' },
105
+ { file_id: 'mid-url', file_size: '157626' },
106
+ ], 'same url + size should only appear once')
107
+ })
@@ -20,6 +20,9 @@ export interface ImageXmlSchema {
20
20
  length: string
21
21
  cdnbigimgurl: string
22
22
  hdlength: string
23
+ // 新版 XML 中 length 是原图大小,中图大小单独给出
24
+ hevc_mid_size?: string
25
+ cdnmidimgurl_size?: string
23
26
  md5: string
24
27
  tpurl?: string
25
28
  tpauthkey?: string
@@ -28,6 +31,11 @@ export interface ImageXmlSchema {
28
31
  }
29
32
  }
30
33
 
34
+ export interface ImageFileCandidate {
35
+ file_id: string,
36
+ file_size: string,
37
+ }
38
+
31
39
  export interface ImageMessagePayload {
32
40
  aes_key: string,
33
41
  auth_key: string,
@@ -35,18 +43,71 @@ export interface ImageMessagePayload {
35
43
  file_size: string,
36
44
  file_name: string,
37
45
  is_ww: boolean,
46
+ /**
47
+ * 按优先级排列的 (file_id, file_size) 组合,第一项即 file_id/file_size。
48
+ * 下载失败时按顺序回落到下一项,避免在错误的 size 上反复重试。
49
+ */
50
+ candidates?: ImageFileCandidate[],
38
51
  }
39
52
 
40
53
  export async function parseImageMessagePayload (rawPayload: MessagePayload): Promise<ImageMessagePayload> {
41
54
  const imageXml: ImageXmlSchema = await xmlToJson(rawPayload.msg)
42
55
  const schema = imageXml.msg.img.$
43
56
 
57
+ const candidates = buildImageCandidates(schema)
58
+ const primary = candidates[0] || { file_id: schema.cdnmidimgurl || schema.tpurl || '', file_size: schema.length }
59
+
44
60
  return {
45
61
  is_ww: !!schema.tpurl,
46
62
  aes_key: schema.aeskey,
47
63
  auth_key: schema.tpauthkey || '',
48
- file_id: schema.cdnmidimgurl || schema.tpurl || '',
49
- file_size: schema.length,
64
+ file_id: primary.file_id,
65
+ file_size: primary.file_size,
50
66
  file_name: '',
67
+ candidates: candidates.length ? candidates : [ primary ],
68
+ }
69
+ }
70
+
71
+ const isPositiveSize = (size?: string): size is string => !!size && Number(size) > 0
72
+
73
+ /**
74
+ * file_id 和 file_size 必须指向同一张图,否则下载端按错误的 size 做 Range/断点会失败。
75
+ * 候选优先级:
76
+ * 1. 企业微信 (tpurl):保持原逻辑,只有一个候选,size 用 length
77
+ * 2. cdnmidimgurl + 中图大小 (cdnmidimgurl_size,其次 hevc_mid_size)
78
+ * 3. 旧版 XML (有 hdlength,length 是中图大小):cdnmidimgurl + length,其次 cdnbigimgurl + hdlength
79
+ * 新版 XML (无 hdlength,length 是原图大小):cdnbigimgurl + length,其次 cdnmidimgurl + length
80
+ */
81
+ export function buildImageCandidates (schema: ImageXmlSchema['msg']['img']['$']): ImageFileCandidate[] {
82
+ if (schema.tpurl) {
83
+ return [ { file_id: schema.cdnmidimgurl || schema.tpurl, file_size: schema.length } ]
84
+ }
85
+
86
+ const midUrl = schema.cdnmidimgurl
87
+ const bigUrl = schema.cdnbigimgurl
88
+ const list: Array<[string | undefined, string | undefined]> = [
89
+ [ midUrl, schema.cdnmidimgurl_size ],
90
+ [ midUrl, schema.hevc_mid_size ],
91
+ ]
92
+
93
+ if (isPositiveSize(schema.hdlength)) {
94
+ list.push([ midUrl, schema.length ], [ bigUrl, schema.hdlength ])
95
+ } else {
96
+ list.push([ bigUrl, schema.length ], [ midUrl, schema.length ])
97
+ }
98
+
99
+ const seen = new Set<string>()
100
+ const candidates: ImageFileCandidate[] = []
101
+ for (const [ fileId, fileSize ] of list) {
102
+ if (!fileId || !isPositiveSize(fileSize)) {
103
+ continue
104
+ }
105
+ const key = `${fileId}|${fileSize}`
106
+ if (seen.has(key)) {
107
+ continue
108
+ }
109
+ seen.add(key)
110
+ candidates.push({ file_id: fileId, file_size: fileSize })
51
111
  }
112
+ return candidates
52
113
  }
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env -S node -r ts-node/register
2
+ import { test } from 'tstest'
3
+
4
+ import type { MessagePayload } from '../../engine-schema.js'
5
+ import { parseVideoMessagePayload } from './message-video.js'
6
+ import { parseSizeMismatch } from '../service/request.js'
7
+
8
+ const buildPayload = (attrs: string): MessagePayload => ({
9
+ msg: `<?xml version="1.0"?>\n<msg>\n\t<videomsg ${attrs} />\n</msg>\n`,
10
+ } as unknown as MessagePayload)
11
+
12
+ test('parseVideoMessagePayload adds raw video as fallback candidate', async t => {
13
+ const payload = await parseVideoMessagePayload(buildPayload(
14
+ 'aeskey="key" cdnvideourl="video-url" length="766838" playlength="5" rawlength="769445" cdnrawvideourl="raw-url" cdnrawvideoaeskey="raw-key"',
15
+ ))
16
+
17
+ t.equal(payload.file_id, 'video-url', 'primary file_id unchanged')
18
+ t.equal(payload.file_size, '766838', 'primary file_size unchanged')
19
+ t.same(payload.candidates, [
20
+ { file_id: 'video-url', file_size: '766838' },
21
+ { file_id: 'raw-url', file_size: '769445', aes_key: 'raw-key' },
22
+ ], 'raw video should be second candidate with its own aes key')
23
+ })
24
+
25
+ test('parseVideoMessagePayload skips empty raw video', async t => {
26
+ const payload = await parseVideoMessagePayload(buildPayload(
27
+ 'aeskey="key" cdnvideourl="video-url" length="766838" playlength="5" rawlength="0" cdnrawvideourl="" cdnrawvideoaeskey=""',
28
+ ))
29
+
30
+ t.same(payload.candidates, [ { file_id: 'video-url', file_size: '766838' } ], 'only cdn video candidate')
31
+ })
32
+
33
+ test('parseSizeMismatch extracts actual size from backend error', async t => {
34
+ t.equal(parseSizeMismatch('download plaintext size differs: expected 766838, got 1228708'), '1228708', 'should return actual size')
35
+ t.equal(parseSizeMismatch('timeout'), undefined, 'other errors return undefined')
36
+ t.equal(parseSizeMismatch(undefined), undefined, 'undefined returns undefined')
37
+ })