zapo-js 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/WaClient.d.ts +1 -1
- package/dist/client/WaClient.js +1 -1
- package/dist/client/WaClientFactory.js +1 -0
- package/dist/client/coordinators/WaAppStateMutationCoordinator.js +2 -2
- package/dist/client/coordinators/WaMessageCoordinator.d.ts +69 -1
- package/dist/client/coordinators/WaMessageCoordinator.js +76 -1
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +1 -1
- package/dist/client/media.d.ts +1 -0
- package/dist/client/media.js +1 -0
- package/dist/client/messaging/messages.d.ts +45 -1
- package/dist/client/messaging/messages.js +57 -45
- package/dist/esm/client/WaClient.js +1 -1
- package/dist/esm/client/WaClientFactory.js +1 -0
- package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +2 -2
- package/dist/esm/client/coordinators/WaMessageCoordinator.js +78 -3
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +1 -1
- package/dist/esm/client/media.js +1 -1
- package/dist/esm/client/messaging/messages.js +56 -45
- package/dist/esm/index.js +2 -0
- package/dist/esm/signal/api/SignalDeviceSyncApi.js +133 -63
- package/dist/index.d.ts +4 -1
- package/dist/index.js +6 -2
- package/dist/signal/api/SignalDeviceSyncApi.d.ts +29 -2
- package/dist/signal/api/SignalDeviceSyncApi.js +131 -61
- package/package.json +1 -1
- package/spec/appstate/index.d.ts +5 -4
- package/spec/appstate/index.js +21 -8
- package/spec/mex/index.d.ts +335 -73
- package/spec/mex/index.js +29 -21
- package/spec/proto/index.d.ts +833 -69
- package/spec/proto/index.js +1 -1
- package/spec/version/index.d.ts +1 -1
- package/spec/version/index.js +1 -1
- package/spec/version/version.json +1 -1
|
@@ -315,7 +315,7 @@ export class WaMessageDispatchCoordinator {
|
|
|
315
315
|
}
|
|
316
316
|
try {
|
|
317
317
|
const results = await this.deps.signalDeviceSync.queryLidsByPhoneJids([pnUserJid]);
|
|
318
|
-
const match = results.find((entry) => entry.
|
|
318
|
+
const match = results.find((entry) => entry.queriedJid === pnUserJid);
|
|
319
319
|
if (match?.lidJid)
|
|
320
320
|
return match.lidJid;
|
|
321
321
|
}
|
package/dist/esm/client/media.js
CHANGED
|
@@ -198,7 +198,7 @@ export async function performPlaintextMediaUpload(deps, input) {
|
|
|
198
198
|
await prepared.cleanup?.();
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
-
async function assertReadableFile(filePath) {
|
|
201
|
+
export async function assertReadableFile(filePath) {
|
|
202
202
|
try {
|
|
203
203
|
const stats = await stat(filePath);
|
|
204
204
|
if (!stats.isFile()) {
|
|
@@ -341,9 +341,15 @@ async function buildMediaMessage(options, content) {
|
|
|
341
341
|
const firstFrameLength = content.type === 'sticker'
|
|
342
342
|
? (content.firstFrameLength ?? detectedFirstFrameLength)
|
|
343
343
|
: undefined;
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
:
|
|
344
|
+
const uploadType = resolveUploadType(content);
|
|
345
|
+
const uploadPromise = uploadMedia(options, {
|
|
346
|
+
source: resolved.uploadMedia,
|
|
347
|
+
cryptoType: uploadType,
|
|
348
|
+
uploadPath: resolveUploadPath(uploadType),
|
|
349
|
+
contentType: mimetype,
|
|
350
|
+
sidecar: needsSidecar(content),
|
|
351
|
+
firstFrameLength
|
|
352
|
+
});
|
|
347
353
|
const processPromise = runMediaProcessor(options.media, resolved.processorInput, content, options.logger);
|
|
348
354
|
const [uploadResult, processResult] = await Promise.allSettled([
|
|
349
355
|
uploadPromise,
|
|
@@ -504,29 +510,42 @@ function parseUploadResponse(body, status) {
|
|
|
504
510
|
...(parsed.metadata_url ? { metadataUrl: parsed.metadata_url } : {})
|
|
505
511
|
};
|
|
506
512
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
513
|
+
/**
|
|
514
|
+
* Shared media upload primitive: derives (or reuses) a media key, encrypts,
|
|
515
|
+
* fetches a media connection, and POSTs the ciphertext. Bytes take a
|
|
516
|
+
* zero-temp-file fast path; streams stage to a temp file first. Backs both the
|
|
517
|
+
* send path and `WaMessageCoordinator.upload`.
|
|
518
|
+
*/
|
|
519
|
+
export async function uploadMedia(options, input) {
|
|
520
|
+
const mediaKey = input.mediaKey ?? (await WaMediaCrypto.generateMediaKey());
|
|
521
|
+
if (input.source instanceof Uint8Array) {
|
|
522
|
+
return uploadMediaBytes(options, input, mediaKey, input.source);
|
|
523
|
+
}
|
|
524
|
+
return uploadMediaStream(options, input, mediaKey, input.source);
|
|
525
|
+
}
|
|
526
|
+
async function uploadMediaBytes(options, input, mediaKey, mediaBytes) {
|
|
510
527
|
const [encrypted, mediaConn] = await Promise.all([
|
|
511
|
-
WaMediaCrypto.encryptBytes(
|
|
512
|
-
sidecar:
|
|
513
|
-
firstFrameLength
|
|
528
|
+
WaMediaCrypto.encryptBytes(input.cryptoType, mediaKey, mediaBytes, {
|
|
529
|
+
sidecar: input.sidecar,
|
|
530
|
+
firstFrameLength: input.firstFrameLength
|
|
514
531
|
}),
|
|
515
532
|
getMediaConn(options)
|
|
516
533
|
]);
|
|
517
534
|
const selectedHost = selectMediaUploadHost(mediaConn);
|
|
518
|
-
const uploadUrl = buildMediaUploadUrl(selectedHost,
|
|
519
|
-
options.logger.debug('sending media upload request', {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
535
|
+
const uploadUrl = buildMediaUploadUrl(selectedHost, input.uploadPath, mediaConn.auth, encrypted.fileEncSha256);
|
|
536
|
+
options.logger.debug(input.logLabel ?? 'sending media upload request', {
|
|
537
|
+
cryptoType: input.cryptoType,
|
|
538
|
+
host: selectedHost,
|
|
539
|
+
size: mediaBytes.byteLength
|
|
523
540
|
});
|
|
524
541
|
const uploadResponse = await options.mediaTransfer.uploadStream({
|
|
525
542
|
url: uploadUrl,
|
|
526
543
|
method: 'POST',
|
|
527
544
|
body: encrypted.ciphertextHmac,
|
|
528
545
|
contentLength: encrypted.ciphertextHmac.byteLength,
|
|
529
|
-
contentType:
|
|
546
|
+
contentType: input.contentType,
|
|
547
|
+
timeoutMs: input.timeoutMs,
|
|
548
|
+
signal: input.signal
|
|
530
549
|
});
|
|
531
550
|
const responseBody = await options.mediaTransfer.readResponseBytes(uploadResponse);
|
|
532
551
|
const parsed = parseUploadResponse(responseBody, uploadResponse.status);
|
|
@@ -538,19 +557,22 @@ async function uploadMediaBytes(options, content, mediaBytes, firstFrameLength,
|
|
|
538
557
|
fileLength: mediaBytes.byteLength,
|
|
539
558
|
streamingSidecar: encrypted.streamingSidecar,
|
|
540
559
|
firstFrameSidecar: encrypted.firstFrameSidecar,
|
|
541
|
-
firstFrameLength
|
|
560
|
+
firstFrameLength: input.firstFrameLength
|
|
542
561
|
};
|
|
543
562
|
}
|
|
544
|
-
async function
|
|
545
|
-
const encResult = await WaMediaCrypto.encryptToFile(input.cryptoType,
|
|
563
|
+
async function uploadMediaStream(options, input, mediaKey, plaintext) {
|
|
564
|
+
const encResult = await WaMediaCrypto.encryptToFile(input.cryptoType, mediaKey, plaintext, {
|
|
565
|
+
sidecar: input.sidecar,
|
|
566
|
+
firstFrameLength: input.firstFrameLength
|
|
567
|
+
});
|
|
546
568
|
let readStream;
|
|
547
569
|
try {
|
|
548
570
|
const mediaConn = await getMediaConn(options);
|
|
549
571
|
const selectedHost = selectMediaUploadHost(mediaConn);
|
|
550
572
|
const uploadUrl = buildMediaUploadUrl(selectedHost, input.uploadPath, mediaConn.auth, encResult.fileEncSha256);
|
|
551
|
-
options.logger.debug(input.logLabel, {
|
|
573
|
+
options.logger.debug(input.logLabel ?? 'sending media stream upload request', {
|
|
574
|
+
cryptoType: input.cryptoType,
|
|
552
575
|
host: selectedHost,
|
|
553
|
-
uploadPath: input.uploadPath,
|
|
554
576
|
plaintextLength: encResult.plaintextLength,
|
|
555
577
|
encryptedSize: encResult.fileSize
|
|
556
578
|
});
|
|
@@ -560,13 +582,15 @@ async function uploadEncryptedStream(options, input) {
|
|
|
560
582
|
method: 'POST',
|
|
561
583
|
body: readStream,
|
|
562
584
|
contentLength: encResult.fileSize,
|
|
563
|
-
contentType: input.contentType
|
|
585
|
+
contentType: input.contentType,
|
|
586
|
+
timeoutMs: input.timeoutMs,
|
|
587
|
+
signal: input.signal
|
|
564
588
|
});
|
|
565
589
|
const responseBody = await options.mediaTransfer.readResponseBytes(uploadResponse);
|
|
566
590
|
const parsed = parseUploadResponse(responseBody, uploadResponse.status);
|
|
567
591
|
return {
|
|
568
592
|
...parsed,
|
|
569
|
-
mediaKey
|
|
593
|
+
mediaKey,
|
|
570
594
|
fileSha256: encResult.fileSha256,
|
|
571
595
|
fileEncSha256: encResult.fileEncSha256,
|
|
572
596
|
fileLength: encResult.plaintextLength,
|
|
@@ -585,19 +609,6 @@ async function uploadEncryptedStream(options, input) {
|
|
|
585
609
|
await WaMediaCrypto.cleanupEncryptedFile(encResult.filePath);
|
|
586
610
|
}
|
|
587
611
|
}
|
|
588
|
-
async function uploadMediaStream(options, content, stream, firstFrameLength, mimetype) {
|
|
589
|
-
const cryptoType = resolveUploadType(content);
|
|
590
|
-
return uploadEncryptedStream(options, {
|
|
591
|
-
plaintext: stream,
|
|
592
|
-
mediaKey: await WaMediaCrypto.generateMediaKey(),
|
|
593
|
-
cryptoType,
|
|
594
|
-
uploadPath: resolveUploadPath(cryptoType),
|
|
595
|
-
contentType: mimetype,
|
|
596
|
-
logLabel: 'sending media stream upload request',
|
|
597
|
-
sidecar: needsSidecar(content),
|
|
598
|
-
firstFrameLength
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
612
|
function openStickerPackInputStream(media) {
|
|
602
613
|
return typeof media === 'string' ? createReadStream(media) : Readable.from([media]);
|
|
603
614
|
}
|
|
@@ -609,23 +620,23 @@ async function buildStickerPackMediaMessage(options, content) {
|
|
|
609
620
|
const coverThumbnail = content.coverThumbnail;
|
|
610
621
|
const mediaKey = await WaMediaCrypto.generateMediaKey();
|
|
611
622
|
const [bundle, cover] = await Promise.all([
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
mediaKey,
|
|
623
|
+
uploadMedia(options, {
|
|
624
|
+
source: createStickerPackZipStream(toStickerPackZipEntries(content)),
|
|
615
625
|
cryptoType: 'sticker-pack',
|
|
616
626
|
uploadPath: MEDIA_UPLOAD_PATHS['sticker-pack'],
|
|
617
627
|
contentType: 'application/zip',
|
|
618
|
-
logLabel: 'sending sticker pack bundle upload',
|
|
619
|
-
sidecar: false
|
|
620
|
-
}),
|
|
621
|
-
uploadEncryptedStream(options, {
|
|
622
|
-
plaintext: openStickerPackInputStream(coverThumbnail),
|
|
623
628
|
mediaKey,
|
|
629
|
+
sidecar: false,
|
|
630
|
+
logLabel: 'sending sticker pack bundle upload'
|
|
631
|
+
}),
|
|
632
|
+
uploadMedia(options, {
|
|
633
|
+
source: openStickerPackInputStream(coverThumbnail),
|
|
624
634
|
cryptoType: 'thumbnail-sticker-pack',
|
|
625
635
|
uploadPath: MEDIA_UPLOAD_PATHS['thumbnail-sticker-pack'],
|
|
626
636
|
contentType: 'image/jpeg',
|
|
627
|
-
|
|
628
|
-
sidecar: false
|
|
637
|
+
mediaKey,
|
|
638
|
+
sidecar: false,
|
|
639
|
+
logLabel: 'sending sticker pack thumbnail upload'
|
|
629
640
|
})
|
|
630
641
|
]);
|
|
631
642
|
if (cover.fileLength === 0) {
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { WaClient } from './client/index.js';
|
|
2
2
|
export { defineWaClientPlugin } from './client/plugins/index.js';
|
|
3
3
|
export { downloadMediaMessage } from './client/media.js';
|
|
4
|
+
export { WaMediaCrypto } from './media/crypto/WaMediaCrypto.js';
|
|
5
|
+
export { WaMediaTransferClient } from './media/transfer/WaMediaTransferClient.js';
|
|
4
6
|
export { createFileCompanionHostPersistence } from './client/persistence/companion-host.js';
|
|
5
7
|
export { parseUsyncResultEnvelope } from './transport/node/builders/usync.js';
|
|
6
8
|
export { getContentType, resolveEncMediaType, resolveMessageTarget, unwrapMessage } from './message/encode/content.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { PromiseDedup } from '../../infra/perf/PromiseDedup.js';
|
|
2
2
|
import { WA_DEFAULTS, WA_NODE_TAGS, WA_USYNC_CONTEXTS } from '../../protocol/constants.js';
|
|
3
|
-
import { buildDeviceJid, isHostedDeviceId, splitJid, toUserJid } from '../../protocol/jid.js';
|
|
3
|
+
import { buildDeviceJid, isHostedDeviceId, parsePhoneJid, splitJid, toUserJid } from '../../protocol/jid.js';
|
|
4
4
|
import { buildUsyncIq, iterateUsyncUsers, parseUsyncResultEnvelope } from '../../transport/node/builders/usync.js';
|
|
5
|
-
import { findNodeChild, getNodeChildrenByTag } from '../../transport/node/helpers.js';
|
|
5
|
+
import { findNodeChild, getNodeChildrenByTag, getNodeTextContent } from '../../transport/node/helpers.js';
|
|
6
6
|
import { assertIqResult } from '../../transport/node/query.js';
|
|
7
7
|
import { createUsyncSidGenerator, logUsyncProtocolErrors } from '../../transport/node/usync.js';
|
|
8
8
|
/**
|
|
@@ -112,22 +112,21 @@ export class SignalDeviceSyncApi {
|
|
|
112
112
|
const parsedByPhoneJid = new Map();
|
|
113
113
|
for (let index = 0; index < parsed.length; index += 1) {
|
|
114
114
|
const entry = parsed[index];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
phoneJid,
|
|
115
|
+
parsedByPhoneJid.set(entry.jid, {
|
|
116
|
+
phoneJid: entry.phoneJid ?? entry.jid,
|
|
118
117
|
lidJid: entry.lidJid,
|
|
119
|
-
exists: entry.exists
|
|
118
|
+
exists: entry.exists,
|
|
119
|
+
invalid: entry.invalid
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
const result = new Array(normalizedPhoneJids.length);
|
|
123
123
|
let found = 0;
|
|
124
124
|
for (let index = 0; index < normalizedPhoneJids.length; index += 1) {
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
exists: false
|
|
130
|
-
};
|
|
125
|
+
const queriedJid = normalizedPhoneJids[index];
|
|
126
|
+
const hit = parsedByPhoneJid.get(queriedJid);
|
|
127
|
+
const resolved = hit
|
|
128
|
+
? { queriedJid, ...hit }
|
|
129
|
+
: { queriedJid, phoneJid: queriedJid, lidJid: null, exists: false, invalid: false };
|
|
131
130
|
if (resolved.exists) {
|
|
132
131
|
found += 1;
|
|
133
132
|
}
|
|
@@ -151,24 +150,23 @@ export class SignalDeviceSyncApi {
|
|
|
151
150
|
if (!this.deviceListStore || results.length === 0)
|
|
152
151
|
return;
|
|
153
152
|
const nowMs = Date.now();
|
|
154
|
-
const
|
|
155
|
-
const
|
|
153
|
+
const lidByQueriedJid = new Map();
|
|
154
|
+
const queriedJids = [];
|
|
156
155
|
for (const entry of results) {
|
|
157
|
-
if (entry.lidJid
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
if (entry.lidJid) {
|
|
157
|
+
lidByQueriedJid.set(entry.queriedJid, entry.lidJid);
|
|
158
|
+
queriedJids.push(entry.queriedJid);
|
|
160
159
|
}
|
|
161
160
|
}
|
|
162
|
-
if (
|
|
161
|
+
if (queriedJids.length === 0)
|
|
163
162
|
return;
|
|
164
|
-
const existing = await this.deviceListStore.getUserDevicesBatch(
|
|
163
|
+
const existing = await this.deviceListStore.getUserDevicesBatch(queriedJids, nowMs);
|
|
165
164
|
const updates = [];
|
|
166
|
-
for (let index = 0; index <
|
|
165
|
+
for (let index = 0; index < queriedJids.length; index += 1) {
|
|
167
166
|
const snapshot = existing[index];
|
|
168
167
|
if (!snapshot)
|
|
169
168
|
continue;
|
|
170
|
-
const
|
|
171
|
-
const lidJid = lidByPhoneJid.get(phoneJid);
|
|
169
|
+
const lidJid = lidByQueriedJid.get(queriedJids[index]);
|
|
172
170
|
if (!lidJid || snapshot.altUserJid === lidJid)
|
|
173
171
|
continue;
|
|
174
172
|
updates.push({
|
|
@@ -271,7 +269,13 @@ export class SignalDeviceSyncApi {
|
|
|
271
269
|
if (!userJid) {
|
|
272
270
|
continue;
|
|
273
271
|
}
|
|
274
|
-
const normalizedUserJid = this.
|
|
272
|
+
const normalizedUserJid = this.tryNormalizeUserJid(userJid);
|
|
273
|
+
if (normalizedUserJid === null) {
|
|
274
|
+
this.logger.debug('signal device sync skipping user node with invalid jid', {
|
|
275
|
+
jid: userJid
|
|
276
|
+
});
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
275
279
|
if (!requestedSet.has(normalizedUserJid)) {
|
|
276
280
|
continue;
|
|
277
281
|
}
|
|
@@ -292,55 +296,107 @@ export class SignalDeviceSyncApi {
|
|
|
292
296
|
throw new Error('signal lid sync response missing usync envelope');
|
|
293
297
|
}
|
|
294
298
|
const requestedSet = new Set(requestedUsers);
|
|
295
|
-
const parsed =
|
|
296
|
-
|
|
299
|
+
const parsed = [];
|
|
300
|
+
const lidUserErrors = [];
|
|
297
301
|
for (let index = 0; index < userNodes.length; index += 1) {
|
|
298
302
|
const userNode = userNodes[index];
|
|
299
|
-
const
|
|
300
|
-
if (!
|
|
303
|
+
const rawUserJid = userNode.attrs.jid;
|
|
304
|
+
if (!rawUserJid) {
|
|
301
305
|
continue;
|
|
302
306
|
}
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
? this.
|
|
307
|
+
const resolvedJid = this.tryNormalizeUserJid(rawUserJid);
|
|
308
|
+
const pnJid = userNode.attrs.pn_jid
|
|
309
|
+
? this.tryNormalizeUserJid(userNode.attrs.pn_jid)
|
|
310
|
+
: null;
|
|
311
|
+
const phoneJid = pnJid ?? resolvedJid;
|
|
312
|
+
const lidNode = findNodeChild(userNode, WA_NODE_TAGS.LID);
|
|
313
|
+
const lidErrorNode = lidNode ? findNodeChild(lidNode, WA_NODE_TAGS.ERROR) : null;
|
|
314
|
+
if (lidErrorNode) {
|
|
315
|
+
lidUserErrors.push({
|
|
316
|
+
jid: resolvedJid ?? rawUserJid,
|
|
317
|
+
code: lidErrorNode.attrs.code,
|
|
318
|
+
text: lidErrorNode.attrs.text
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
const lidJid = !lidErrorNode && lidNode?.attrs.val
|
|
322
|
+
? this.tryNormalizeUserJid(lidNode.attrs.val)
|
|
306
323
|
: null;
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
324
|
+
const contactNodes = getNodeChildrenByTag(userNode, WA_NODE_TAGS.CONTACT);
|
|
325
|
+
let matched = false;
|
|
326
|
+
for (let c = 0; c < contactNodes.length; c += 1) {
|
|
327
|
+
const contactNode = contactNodes[c];
|
|
328
|
+
const inputJid = this.recoverContactJid(contactNode);
|
|
329
|
+
if (inputJid === null || !requestedSet.has(inputJid)) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
matched = true;
|
|
333
|
+
const invalid = resolvedJid === null || contactNode.attrs.type === 'invalid';
|
|
334
|
+
parsed.push({
|
|
335
|
+
jid: inputJid,
|
|
336
|
+
phoneJid: phoneJid ?? inputJid,
|
|
337
|
+
lidJid,
|
|
338
|
+
exists: !invalid &&
|
|
339
|
+
this.parseLidSyncContactExists(contactNode, inputJid, lidJid !== null),
|
|
340
|
+
invalid
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
if (matched) {
|
|
310
344
|
continue;
|
|
311
345
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
parsedCount += 1;
|
|
346
|
+
if (resolvedJid === null) {
|
|
347
|
+
this.logger.debug('signal lid sync skipping user node with invalid jid', {
|
|
348
|
+
jid: rawUserJid
|
|
349
|
+
});
|
|
317
350
|
continue;
|
|
318
351
|
}
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
352
|
+
const requestedKey = requestedSet.has(resolvedJid)
|
|
353
|
+
? resolvedJid
|
|
354
|
+
: pnJid !== null && requestedSet.has(pnJid)
|
|
355
|
+
? pnJid
|
|
356
|
+
: null;
|
|
357
|
+
if (requestedKey === null) {
|
|
358
|
+
this.logger.debug('signal lid sync unmatched user (no contact echo)', {
|
|
359
|
+
jid: resolvedJid,
|
|
360
|
+
pnJid
|
|
325
361
|
});
|
|
326
|
-
parsed[parsedCount] = this.buildLidSyncResult(normalizedUserJid, normalizedPhoneJid, contactNode, null);
|
|
327
|
-
parsedCount += 1;
|
|
328
362
|
continue;
|
|
329
363
|
}
|
|
330
|
-
const
|
|
331
|
-
parsed
|
|
332
|
-
|
|
364
|
+
const contactNode = findNodeChild(userNode, WA_NODE_TAGS.CONTACT);
|
|
365
|
+
parsed.push({
|
|
366
|
+
jid: requestedKey,
|
|
367
|
+
phoneJid: phoneJid ?? requestedKey,
|
|
368
|
+
lidJid,
|
|
369
|
+
exists: this.parseLidSyncContactExists(contactNode, requestedKey, lidJid !== null),
|
|
370
|
+
invalid: false
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
if (lidUserErrors.length > 0) {
|
|
374
|
+
this.logger.warn('signal lid sync user errors', {
|
|
375
|
+
droppedCount: lidUserErrors.length,
|
|
376
|
+
totalExpected: requestedUsers.length,
|
|
377
|
+
sample: lidUserErrors.slice(0, 3)
|
|
378
|
+
});
|
|
333
379
|
}
|
|
334
|
-
parsed.length = parsedCount;
|
|
335
380
|
return parsed;
|
|
336
381
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Recovers the queried phone JID from a `<contact>` echo. Each `<contact>` in a
|
|
384
|
+
* usync response echoes the `+<number>` we sent (base64 text content), so it maps
|
|
385
|
+
* a response node back to the exact input - even when the server corrected the
|
|
386
|
+
* `<user jid>` or rejected it as `jid='undefined'`. Returns `null` when there is
|
|
387
|
+
* no decodable phone echo.
|
|
388
|
+
*/
|
|
389
|
+
recoverContactJid(contactNode) {
|
|
390
|
+
const echoed = getNodeTextContent(contactNode);
|
|
391
|
+
if (!echoed) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
return parsePhoneJid(echoed);
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
344
400
|
}
|
|
345
401
|
parseLidSyncContactExists(contactNode, userJid, defaultExists) {
|
|
346
402
|
if (!contactNode) {
|
|
@@ -401,7 +457,10 @@ export class SignalDeviceSyncApi {
|
|
|
401
457
|
let normalizedCount = 0;
|
|
402
458
|
const dedup = new Set();
|
|
403
459
|
for (let index = 0; index < userJids.length; index += 1) {
|
|
404
|
-
const normalizedJid =
|
|
460
|
+
const normalizedJid = toUserJid(userJids[index], {
|
|
461
|
+
canonicalizeSignalServer: true,
|
|
462
|
+
hostDomain: this.hostDomain
|
|
463
|
+
});
|
|
405
464
|
if (dedup.has(normalizedJid)) {
|
|
406
465
|
continue;
|
|
407
466
|
}
|
|
@@ -412,10 +471,21 @@ export class SignalDeviceSyncApi {
|
|
|
412
471
|
normalized.length = normalizedCount;
|
|
413
472
|
return normalized;
|
|
414
473
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
474
|
+
/**
|
|
475
|
+
* Canonicalizes a user jid, returning `null` instead of throwing when the input
|
|
476
|
+
* is not a valid jid (for example the literal `'undefined'` the server returns
|
|
477
|
+
* for an invalid contact). Lets a single malformed response node be skipped
|
|
478
|
+
* without discarding the rest of the batch.
|
|
479
|
+
*/
|
|
480
|
+
tryNormalizeUserJid(jid) {
|
|
481
|
+
try {
|
|
482
|
+
return toUserJid(jid, {
|
|
483
|
+
canonicalizeSignalServer: true,
|
|
484
|
+
hostDomain: this.hostDomain
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
420
490
|
}
|
|
421
491
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { defineWaClientPlugin } from './client/plugins';
|
|
|
3
3
|
export type { WaClientDependencies } from './client/WaClientFactory';
|
|
4
4
|
export type { WaClientEventMap, WaClientOptions, WaClientProxyOptions, WaDownloadMediaOptions, WaHistorySyncChunkEvent, WaHistorySyncOptions, WaWriteBehindOptions } from './client/types';
|
|
5
5
|
export type { WaClientPluginContext, WaClientPluginDefinition } from './client/plugins';
|
|
6
|
-
export type { WaMessageCoordinator } from './client/coordinators/WaMessageCoordinator';
|
|
6
|
+
export type { WaMediaUploadResult, WaMessageCoordinator, WaUploadMediaOptions, WaUploadMediaType } from './client/coordinators/WaMessageCoordinator';
|
|
7
7
|
export type { WaAccountTakeoverNoticeEvent, WaAppStateMutationEvent, WaAppStateMutationSource, WaBusinessEvent, WaBusinessEventAction, WaBusinessProfileResult, WaConnectionEvent, WaGroupEvent, WaGroupEventAction, WaGroupEventLinkedGroup, WaGroupEventMembershipRequest, WaGroupEventParticipant, WaGroupEventSubgroupSuggestion, WaIgnoreKey, WaIgnoreKeyContext, WaIgnoreKeyPredicate, WaIgnoreStanzaKind, WaIncomingAddonEvent, WaIncomingBaseEvent, WaIncomingBotChunkEvent, WaIncomingCallEvent, WaIncomingChatstateEvent, WaIncomingErrorStanzaEvent, WaIncomingFailureEvent, WaIncomingMessageEvent, WaIncomingMessageKey, WaIncomingNewsletterEvent, WaIncomingNewsletterMessageUpdateEvent, WaIncomingNodeHandler, WaIncomingNodeHandlerRegistration, WaIncomingNotificationEvent, WaIncomingPresenceEvent, WaIncomingProtocolMessageEvent, WaIncomingReceiptEvent, WaIncomingStanzaFilter, WaIncomingUnavailableMessageEvent, WaIncomingUnhandledStanzaEvent, WaMexLidChangeEvent, WaMexMessageCappingEvent, WaMexMessageCappingStatus, WaMexNotificationEvent, WaMexNotificationGraphQlError, WaMexNotificationOperationName, WaMexNotificationUnknownEvent, WaMexOwnUsernameSyncEvent, WaMexTextStatusUpdateEvent, WaMexTextStatusUpdateHintEvent, WaMexUsernameDeleteEvent, WaMexUsernameSetEvent, WaMexUsernameUpdateHintEvent, WaOfflineResumeEvent, WaOutgoingMessageEvent, WaPictureEvent, WaPictureEventAction, WaPrivacyTokenUpdateEvent, WaReceiptStatus, WaRegistrationCodeEvent, WaSendMessageOptions, WaUnavailableMessageKind, WaVerifiedNameResult, WaAddonKind, WaNewsletterEventAction, WaNewsletterMessageUpdate, WaNewsletterPollVoteEntry, WaNewsletterReactionEntry } from './client/types';
|
|
8
8
|
export type { WaAppStateMutationCoordinator, WaBroadcastListParticipant, WaSetBroadcastListInput, WaSetStatusPrivacyInput } from './client/coordinators/WaAppStateMutationCoordinator';
|
|
9
9
|
export type { WaBotCoordinator, WaBotInfo, WaBotPosingAsProfessional, WaBotProfileCommand, WaBotProfilePrompt, WaBotProfileResult, WaBotPromptOptions, WaGetBotProfileOptions } from './client/coordinators/WaBotCoordinator';
|
|
@@ -11,6 +11,9 @@ export type { WaBroadcastListCoordinator, WaSendBroadcastListMessageInput } from
|
|
|
11
11
|
export type { WaBusinessCoordinator, WaVerifiedNameBatchEntry } from './client/coordinators/WaBusinessCoordinator';
|
|
12
12
|
export { downloadMediaMessage } from './client/media';
|
|
13
13
|
export type { WaDownloadMediaMessageOptions, WaUploadMediaSource } from './client/media';
|
|
14
|
+
export { WaMediaCrypto } from './media/crypto/WaMediaCrypto';
|
|
15
|
+
export { WaMediaTransferClient } from './media/transfer/WaMediaTransferClient';
|
|
16
|
+
export type { MediaCryptoType, MediaKind, WaMediaConn, WaMediaDecryptReadableOptions, WaMediaDecryptionResult, WaMediaDerivedKeys, WaMediaEncryptionResult, WaMediaFileEncryptionResult, WaMediaReadableDecryptionResult, WaMediaReadableEncryptionResult, WaMediaTransferClientOptions } from './media/types';
|
|
14
17
|
export type { WaEditBusinessProfileInput } from './transport/node/builders/business';
|
|
15
18
|
export type { WaEmailCoordinator, WaEmailStatus, WaEmailVerifyCodeResult } from './client/coordinators/WaEmailCoordinator';
|
|
16
19
|
export type { WaCommunityCreateOptions, WaCommunitySubGroup, WaCommunitySubGroupResult, WaCommunitySubGroupsResult, WaGroupCoordinator, WaGroupCreateOptions, WaGroupMetadata, WaGroupParticipant, WaLinkSubGroupsResult, WaMembershipRequest, WaUnlinkSubGroupsResult } from './client/coordinators/WaGroupCoordinator';
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.proto = exports.WA_XMLNS = exports.WA_PRIVACY_VALUES = exports.WA_PRIVACY_TAGS = exports.WA_PRIVACY_SETTING_TO_CATEGORY = exports.WA_PRIVACY_DISALLOWED_LIST_CATEGORIES = exports.WA_PRIVACY_CATEGORY_TO_SETTING = exports.WA_PRIVACY_CATEGORIES = exports.WA_SUPPORTED_DIRTY_TYPES = exports.WA_STREAM_SIGNALING = exports.WA_SIGNALING = exports.WA_RETRYABLE_ACK_CODES = exports.WA_READY_STATES = exports.WA_PREVIEW_MEDIA_HKDF_INFO = exports.WA_PAIRING_KDF_INFO = exports.WA_NODE_TAGS = exports.WA_MEDIA_HKDF_INFO = exports.WA_MESSAGE_TYPES = exports.WA_MESSAGE_TAGS = exports.WA_LOGOUT_REASONS = exports.WA_IQ_TYPES = exports.WA_DISCONNECT_REASONS = exports.WA_DIRTY_TYPES = exports.WA_DIRTY_PROTOCOLS = exports.WA_DEFAULTS = exports.WA_COMPANION_PLATFORM_IDS = exports.WA_BUSINESS_HOURS_MODES = exports.WA_BUSINESS_HOURS_DAYS = exports.WA_BROWSERS = exports.WA_APP_STATE_SYNC_DATA_TYPE = exports.WA_APP_STATE_KEY_TYPES = exports.WA_APP_STATE_KDF_INFO = exports.WA_APP_STATE_ERROR_CODES = exports.WA_APP_STATE_COLLECTION_STATES = void 0;
|
|
3
|
+
exports.toUserJid = exports.splitJid = exports.signalAddressKey = exports.parseSignalAddressFromJid = exports.parsePhoneJid = exports.parseJidFull = exports.normalizeRecipientJid = exports.normalizeDeviceJid = exports.isUserJid = exports.isStatusBroadcastJid = exports.isNewsletterJid = exports.isLidJid = exports.isHostedServer = exports.isHostedDeviceJid = exports.isHostedDeviceId = exports.isGroupOrBroadcastJid = exports.isGroupJid = exports.isBroadcastJid = exports.isBotJid = exports.getWaMediaHkdfInfo = exports.getWaCompanionPlatformId = exports.getLoginIdentity = exports.canonicalizeSignalServer = exports.canonicalizeSignalJid = exports.buildDeviceJid = exports.delay = exports.WaAuthMemoryStore = exports.createStore = exports.WA_VERSION = exports.createNoopLogger = exports.createPinoLogger = exports.PinoLogger = exports.ConsoleLogger = exports.fetchLatestWaMobileVersion = exports.fetchLatestWaWebVersion = exports.writeRandomPadMax16 = exports.unpadPkcs7 = exports.resolveMediaPayload = exports.getContextInfo = exports.unwrapMessage = exports.resolveMessageTarget = exports.resolveEncMediaType = exports.getContentType = exports.parseUsyncResultEnvelope = exports.createFileCompanionHostPersistence = exports.WaMediaTransferClient = exports.WaMediaCrypto = exports.downloadMediaMessage = exports.defineWaClientPlugin = exports.WaClient = void 0;
|
|
4
|
+
exports.proto = exports.WA_XMLNS = exports.WA_PRIVACY_VALUES = exports.WA_PRIVACY_TAGS = exports.WA_PRIVACY_SETTING_TO_CATEGORY = exports.WA_PRIVACY_DISALLOWED_LIST_CATEGORIES = exports.WA_PRIVACY_CATEGORY_TO_SETTING = exports.WA_PRIVACY_CATEGORIES = exports.WA_SUPPORTED_DIRTY_TYPES = exports.WA_STREAM_SIGNALING = exports.WA_SIGNALING = exports.WA_RETRYABLE_ACK_CODES = exports.WA_READY_STATES = exports.WA_PREVIEW_MEDIA_HKDF_INFO = exports.WA_PAIRING_KDF_INFO = exports.WA_NODE_TAGS = exports.WA_MEDIA_HKDF_INFO = exports.WA_MESSAGE_TYPES = exports.WA_MESSAGE_TAGS = exports.WA_LOGOUT_REASONS = exports.WA_IQ_TYPES = exports.WA_DISCONNECT_REASONS = exports.WA_DIRTY_TYPES = exports.WA_DIRTY_PROTOCOLS = exports.WA_DEFAULTS = exports.WA_COMPANION_PLATFORM_IDS = exports.WA_BUSINESS_HOURS_MODES = exports.WA_BUSINESS_HOURS_DAYS = exports.WA_BROWSERS = exports.WA_APP_STATE_SYNC_DATA_TYPE = exports.WA_APP_STATE_KEY_TYPES = exports.WA_APP_STATE_KDF_INFO = exports.WA_APP_STATE_ERROR_CODES = exports.WA_APP_STATE_COLLECTION_STATES = exports.WA_APP_STATE_COLLECTIONS = exports.WA_ACCOUNT_SYNC_PROTOCOLS = void 0;
|
|
5
5
|
var _client_1 = require("./client");
|
|
6
6
|
Object.defineProperty(exports, "WaClient", { enumerable: true, get: function () { return _client_1.WaClient; } });
|
|
7
7
|
var plugins_1 = require("./client/plugins");
|
|
8
8
|
Object.defineProperty(exports, "defineWaClientPlugin", { enumerable: true, get: function () { return plugins_1.defineWaClientPlugin; } });
|
|
9
9
|
var media_1 = require("./client/media");
|
|
10
10
|
Object.defineProperty(exports, "downloadMediaMessage", { enumerable: true, get: function () { return media_1.downloadMediaMessage; } });
|
|
11
|
+
var WaMediaCrypto_1 = require("./media/crypto/WaMediaCrypto");
|
|
12
|
+
Object.defineProperty(exports, "WaMediaCrypto", { enumerable: true, get: function () { return WaMediaCrypto_1.WaMediaCrypto; } });
|
|
13
|
+
var WaMediaTransferClient_1 = require("./media/transfer/WaMediaTransferClient");
|
|
14
|
+
Object.defineProperty(exports, "WaMediaTransferClient", { enumerable: true, get: function () { return WaMediaTransferClient_1.WaMediaTransferClient; } });
|
|
11
15
|
var companion_host_1 = require("./client/persistence/companion-host");
|
|
12
16
|
Object.defineProperty(exports, "createFileCompanionHostPersistence", { enumerable: true, get: function () { return companion_host_1.createFileCompanionHostPersistence; } });
|
|
13
17
|
var usync_1 = require("./transport/node/builders/usync");
|
|
@@ -11,9 +11,23 @@ interface SignalDeviceSyncApiOptions {
|
|
|
11
11
|
readonly generateSid?: WaUsyncSidGenerator;
|
|
12
12
|
}
|
|
13
13
|
export interface SignalLidSyncResult {
|
|
14
|
+
/**
|
|
15
|
+
* The phone jid the caller queried (normalized), so results can be correlated
|
|
16
|
+
* back to the input by value rather than by array position. Equals `phoneJid`
|
|
17
|
+
* unless the server corrected the number.
|
|
18
|
+
*/
|
|
19
|
+
readonly queriedJid: string;
|
|
20
|
+
/** The server's canonical/corrected phone jid (e.g. BR 9th digit added). */
|
|
14
21
|
readonly phoneJid: string;
|
|
15
22
|
readonly lidJid: string | null;
|
|
16
23
|
readonly exists: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* `true` when the server rejected the number as malformed (`<user
|
|
26
|
+
* jid='undefined'>` with `<contact type='invalid'>`) - distinct from a
|
|
27
|
+
* well-formed number that simply has no WhatsApp account (`exists: false`,
|
|
28
|
+
* `invalid: false`).
|
|
29
|
+
*/
|
|
30
|
+
readonly invalid: boolean;
|
|
17
31
|
}
|
|
18
32
|
/**
|
|
19
33
|
* Resolves the device list and LID mapping for a set of users via the `usync`
|
|
@@ -55,10 +69,23 @@ export declare class SignalDeviceSyncApi {
|
|
|
55
69
|
private makeLidSyncRequest;
|
|
56
70
|
private parseDeviceSyncResponse;
|
|
57
71
|
private parseLidSyncResponse;
|
|
58
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Recovers the queried phone JID from a `<contact>` echo. Each `<contact>` in a
|
|
74
|
+
* usync response echoes the `+<number>` we sent (base64 text content), so it maps
|
|
75
|
+
* a response node back to the exact input - even when the server corrected the
|
|
76
|
+
* `<user jid>` or rejected it as `jid='undefined'`. Returns `null` when there is
|
|
77
|
+
* no decodable phone echo.
|
|
78
|
+
*/
|
|
79
|
+
private recoverContactJid;
|
|
59
80
|
private parseLidSyncContactExists;
|
|
60
81
|
private parseUserDeviceJids;
|
|
61
82
|
private normalizeUsers;
|
|
62
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Canonicalizes a user jid, returning `null` instead of throwing when the input
|
|
85
|
+
* is not a valid jid (for example the literal `'undefined'` the server returns
|
|
86
|
+
* for an invalid contact). Lets a single malformed response node be skipped
|
|
87
|
+
* without discarding the rest of the batch.
|
|
88
|
+
*/
|
|
89
|
+
private tryNormalizeUserJid;
|
|
63
90
|
}
|
|
64
91
|
export {};
|