zapo-js 1.7.0 → 1.8.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/README.md +26 -12
- package/dist/appstate/crypto/WaAppStateCrypto.js +4 -6
- package/dist/auth/WaAuthClient.d.ts +14 -1
- package/dist/auth/WaAuthClient.js +22 -1
- package/dist/auth/credentials-flow.d.ts +3 -1
- package/dist/auth/credentials-flow.js +3 -0
- package/dist/auth/pairing/WaPairingFlow.d.ts +5 -0
- package/dist/auth/pairing/WaPairingFlow.js +66 -14
- package/dist/auth/types.d.ts +13 -0
- package/dist/client/WaClient.js +1 -0
- package/dist/client/WaClientFactory.d.ts +2 -0
- package/dist/client/WaClientFactory.js +29 -3
- package/dist/client/coordinators/WaIncomingNodeCoordinator.d.ts +1 -0
- package/dist/client/coordinators/WaIncomingNodeCoordinator.js +2 -1
- package/dist/client/coordinators/WaMessageCoordinator.d.ts +50 -0
- package/dist/client/coordinators/WaMessageCoordinator.js +39 -9
- package/dist/client/coordinators/WaMessageDispatchCoordinator.d.ts +8 -0
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +74 -29
- package/dist/client/coordinators/WaMobileCoordinator.js +1 -2
- package/dist/client/coordinators/WaPrivacyCoordinator.d.ts +18 -2
- package/dist/client/coordinators/WaPrivacyCoordinator.js +36 -3
- package/dist/client/coordinators/WaProfileCoordinator.d.ts +36 -4
- package/dist/client/coordinators/WaProfileCoordinator.js +80 -2
- package/dist/client/events/business.js +1 -1
- package/dist/client/events/incoming.d.ts +2 -0
- package/dist/client/events/incoming.js +7 -0
- package/dist/client/events/privacy.d.ts +9 -0
- package/dist/client/events/privacy.js +30 -26
- package/dist/client/events/username.d.ts +8 -0
- package/dist/client/events/username.js +26 -0
- package/dist/client/messaging/messages.js +16 -12
- package/dist/client/newsletter/discovery.js +4 -2
- package/dist/client/persistence/WriteBehindPersistence.js +1 -0
- package/dist/client/persistence/group-history.js +57 -45
- package/dist/client/persistence/history-blob.d.ts +21 -0
- package/dist/client/persistence/history-blob.js +41 -0
- package/dist/client/persistence/history-sync.d.ts +26 -13
- package/dist/client/persistence/history-sync.js +307 -185
- package/dist/client/persistence/mailbox.js +1 -1
- package/dist/client/plugins/types.d.ts +10 -2
- package/dist/client/types.d.ts +45 -0
- package/dist/crypto/core/index.d.ts +1 -1
- package/dist/crypto/core/index.js +2 -1
- package/dist/crypto/core/primitives.d.ts +19 -0
- package/dist/crypto/core/primitives.js +48 -0
- package/dist/crypto/core/xeddsa.js +19 -4
- package/dist/crypto/curves/X25519.js +16 -2
- package/dist/esm/appstate/crypto/WaAppStateCrypto.js +4 -6
- package/dist/esm/auth/WaAuthClient.js +22 -1
- package/dist/esm/auth/credentials-flow.js +3 -0
- package/dist/esm/auth/pairing/WaPairingFlow.js +67 -15
- package/dist/esm/client/WaClient.js +1 -0
- package/dist/esm/client/WaClientFactory.js +29 -3
- package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +2 -1
- package/dist/esm/client/coordinators/WaMessageCoordinator.js +40 -10
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +76 -31
- package/dist/esm/client/coordinators/WaMobileCoordinator.js +2 -3
- package/dist/esm/client/coordinators/WaPrivacyCoordinator.js +36 -3
- package/dist/esm/client/coordinators/WaProfileCoordinator.js +81 -3
- package/dist/esm/client/events/business.js +2 -2
- package/dist/esm/client/events/incoming.js +7 -0
- package/dist/esm/client/events/privacy.js +30 -26
- package/dist/esm/client/events/username.js +23 -0
- package/dist/esm/client/messaging/messages.js +16 -12
- package/dist/esm/client/newsletter/discovery.js +4 -2
- package/dist/esm/client/persistence/WriteBehindPersistence.js +1 -0
- package/dist/esm/client/persistence/group-history.js +59 -47
- package/dist/esm/client/persistence/history-blob.js +40 -0
- package/dist/esm/client/persistence/history-sync.js +308 -187
- package/dist/esm/client/persistence/mailbox.js +1 -1
- package/dist/esm/crypto/core/index.js +1 -1
- package/dist/esm/crypto/core/primitives.js +47 -0
- package/dist/esm/crypto/core/xeddsa.js +19 -4
- package/dist/esm/crypto/curves/X25519.js +16 -2
- package/dist/esm/media/crypto/WaMediaCrypto.js +38 -19
- package/dist/esm/media/crypto/media-retry.js +40 -0
- package/dist/esm/message/crypto/addon-crypto.js +114 -0
- package/dist/esm/message/crypto/reporting-token.js +38 -216
- package/dist/esm/message/encode/content.js +56 -9
- package/dist/esm/message/kinds/group-history.js +32 -0
- package/dist/esm/message/primitives/incoming.js +62 -13
- package/dist/esm/message/primitives/media-retry.js +198 -0
- package/dist/esm/protocol/constants.js +1 -0
- package/dist/esm/protocol/defaults.js +9 -0
- package/dist/esm/protocol/device-identity.js +10 -5
- package/dist/esm/protocol/jid.js +83 -36
- package/dist/esm/protocol/notification.js +1 -0
- package/dist/esm/protocol/username.js +127 -0
- package/dist/esm/signal/group/SenderKeyChain.js +1 -2
- package/dist/esm/signal/group/SenderKeyManager.js +1 -7
- package/dist/esm/signal/session/SignalProtocol.js +22 -21
- package/dist/esm/signal/session/SignalRatchet.js +20 -17
- package/dist/esm/signal/session/encoding.js +25 -3
- package/dist/esm/signal/session/resolver.js +11 -5
- package/dist/esm/store/memory/contact.store.js +1 -0
- package/dist/esm/transport/binary/constants.js +19 -0
- package/dist/esm/transport/binary/decoder.js +17 -19
- package/dist/esm/transport/binary/encoder.js +61 -18
- package/dist/esm/transport/index.js +1 -1
- package/dist/esm/transport/node/builders/global.js +20 -0
- package/dist/esm/transport/node/builders/message.js +32 -15
- package/dist/esm/transport/node/builders/privacy.js +32 -9
- package/dist/esm/transport/node/builders/profile.js +34 -1
- package/dist/esm/transport/node/builders/usync.js +1 -1
- package/dist/esm/transport/node/helpers.js +9 -0
- package/dist/esm/transport/noise/WaClientPayload.js +43 -22
- package/dist/esm/transport/noise/WaFrameCodec.js +14 -4
- package/dist/esm/transport/noise/WaMobileClientPayload.js +3 -1
- package/dist/esm/util/proto-stream.js +325 -0
- package/dist/esm/util/protoscan.js +181 -0
- package/dist/esm/util/runtime.js +34 -0
- package/dist/index.d.ts +4 -3
- package/dist/media/crypto/WaMediaCrypto.js +36 -17
- package/dist/media/crypto/media-retry.d.ts +29 -0
- package/dist/media/crypto/media-retry.js +45 -0
- package/dist/message/crypto/addon-crypto.d.ts +49 -0
- package/dist/message/crypto/addon-crypto.js +118 -0
- package/dist/message/crypto/reporting-token.d.ts +3 -1
- package/dist/message/crypto/reporting-token.js +38 -216
- package/dist/message/encode/content.d.ts +17 -1
- package/dist/message/encode/content.js +57 -9
- package/dist/message/kinds/group-history.d.ts +24 -0
- package/dist/message/kinds/group-history.js +34 -0
- package/dist/message/primitives/incoming.d.ts +2 -1
- package/dist/message/primitives/incoming.js +62 -13
- package/dist/message/primitives/media-retry.d.ts +43 -0
- package/dist/message/primitives/media-retry.js +202 -0
- package/dist/message/types.d.ts +10 -0
- package/dist/protocol/constants.d.ts +2 -0
- package/dist/protocol/constants.js +12 -2
- package/dist/protocol/defaults.d.ts +9 -0
- package/dist/protocol/defaults.js +9 -0
- package/dist/protocol/device-identity.d.ts +14 -4
- package/dist/protocol/device-identity.js +9 -4
- package/dist/protocol/jid.d.ts +4 -1
- package/dist/protocol/jid.js +83 -36
- package/dist/protocol/notification.d.ts +1 -0
- package/dist/protocol/notification.js +1 -0
- package/dist/protocol/username.d.ts +45 -0
- package/dist/protocol/username.js +137 -0
- package/dist/signal/group/SenderKeyChain.d.ts +1 -1
- package/dist/signal/group/SenderKeyChain.js +1 -2
- package/dist/signal/group/SenderKeyManager.js +1 -7
- package/dist/signal/session/SignalProtocol.d.ts +13 -0
- package/dist/signal/session/SignalProtocol.js +22 -21
- package/dist/signal/session/SignalRatchet.d.ts +4 -4
- package/dist/signal/session/SignalRatchet.js +19 -16
- package/dist/signal/session/SignalSession.d.ts +8 -1
- package/dist/signal/session/encoding.d.ts +5 -0
- package/dist/signal/session/encoding.js +25 -3
- package/dist/signal/session/resolver.js +11 -5
- package/dist/store/contracts/contact.store.d.ts +2 -0
- package/dist/store/memory/contact.store.js +1 -0
- package/dist/transport/binary/constants.d.ts +8 -0
- package/dist/transport/binary/constants.js +20 -1
- package/dist/transport/binary/decoder.js +17 -19
- package/dist/transport/binary/encoder.js +60 -17
- package/dist/transport/index.d.ts +1 -1
- package/dist/transport/index.js +2 -1
- package/dist/transport/node/builders/global.d.ts +8 -0
- package/dist/transport/node/builders/global.js +21 -0
- package/dist/transport/node/builders/message.js +32 -15
- package/dist/transport/node/builders/privacy.d.ts +12 -7
- package/dist/transport/node/builders/privacy.js +32 -9
- package/dist/transport/node/builders/profile.d.ts +16 -0
- package/dist/transport/node/builders/profile.js +35 -0
- package/dist/transport/node/builders/usync.d.ts +2 -1
- package/dist/transport/node/builders/usync.js +1 -1
- package/dist/transport/node/helpers.d.ts +7 -0
- package/dist/transport/node/helpers.js +10 -0
- package/dist/transport/noise/WaClientPayload.js +43 -22
- package/dist/transport/noise/WaFrameCodec.d.ts +1 -0
- package/dist/transport/noise/WaFrameCodec.js +14 -4
- package/dist/transport/noise/WaMobileClientPayload.d.ts +1 -0
- package/dist/transport/noise/WaMobileClientPayload.js +3 -1
- package/dist/transport/noise/types.d.ts +17 -0
- package/dist/util/proto-stream.d.ts +48 -0
- package/dist/util/proto-stream.js +329 -0
- package/dist/util/protoscan.d.ts +33 -0
- package/dist/util/protoscan.js +186 -0
- package/dist/util/runtime.d.ts +12 -0
- package/dist/util/runtime.js +35 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ import type { WaDownloadMediaOptions, WaIncomingAddonEvent, WaIncomingMessageEve
|
|
|
8
8
|
import type { Logger } from '../../infra/log/types';
|
|
9
9
|
import type { WaMediaTransferClient } from '../../media/transfer/WaMediaTransferClient';
|
|
10
10
|
import type { MediaKind } from '../../media/types';
|
|
11
|
+
import type { WaMediaRetryRequest, WaMediaRetryRequester, WaMediaRetryResult } from '../../message/primitives/media-retry';
|
|
11
12
|
import type { PeerDataOperationRequester } from '../../message/primitives/peer-data-operation';
|
|
12
13
|
import type { WaMessagePublishResult, WaSendMessageContent, WaSendReceiptEventOptions, WaSendReceiptOptions } from '../../message/types';
|
|
13
14
|
import { type Proto } from '../../proto';
|
|
@@ -17,6 +18,8 @@ import { type WaMexQuerySocket } from '../../transport/node/mex/client';
|
|
|
17
18
|
export interface WaMessageCoordinatorDeps {
|
|
18
19
|
readonly messageDispatch: WaMessageDispatchCoordinator;
|
|
19
20
|
readonly mediaTransfer: WaMediaTransferClient;
|
|
21
|
+
/** Backs {@link WaMessageCoordinator.requestMediaReupload}. */
|
|
22
|
+
readonly mediaRetry: WaMediaRetryRequester;
|
|
20
23
|
/** Media upload wiring shared with the send path; backs {@link WaMessageCoordinator.upload}. */
|
|
21
24
|
readonly mediaUploadOptions: WaMediaMessageOptions;
|
|
22
25
|
readonly logger: Logger;
|
|
@@ -163,6 +166,7 @@ export interface WaMediaUploadResult extends UploadResult {
|
|
|
163
166
|
export declare class WaMessageCoordinator {
|
|
164
167
|
private readonly messageDispatch;
|
|
165
168
|
private readonly mediaTransfer;
|
|
169
|
+
private readonly mediaRetry;
|
|
166
170
|
private readonly mediaUploadOptions;
|
|
167
171
|
private readonly logger;
|
|
168
172
|
private readonly messageStore;
|
|
@@ -373,6 +377,52 @@ export declare class WaMessageCoordinator {
|
|
|
373
377
|
* `options.maxBytes`.
|
|
374
378
|
*/
|
|
375
379
|
downloadBytes(source: WaIncomingMessageEvent | Proto.IMessage, options?: WaDownloadMediaOptions): Promise<Uint8Array>;
|
|
380
|
+
/**
|
|
381
|
+
* Asks the sender's primary device to re-upload a message's media, for when
|
|
382
|
+
* the CDN answers `404`/`410` because the blob expired - typically old
|
|
383
|
+
* media surfaced by a history sync. Sends a `server-error` receipt carrying
|
|
384
|
+
* the sealed request and resolves once the server answers with a
|
|
385
|
+
* `mediaretry` notification, usually within a couple of seconds.
|
|
386
|
+
*
|
|
387
|
+
* On `result: 'success'` only the `directPath` changes - the media key,
|
|
388
|
+
* hashes, and length of the original message stay valid, so patch the path
|
|
389
|
+
* into the message and download again. A second call for a message already
|
|
390
|
+
* in flight joins the first request instead of sending another receipt.
|
|
391
|
+
*
|
|
392
|
+
* The other three `result` values are answers too, not thrown errors:
|
|
393
|
+
* `not_found` means the sender no longer holds the file and nothing can
|
|
394
|
+
* recover it, `decryption_error` that it could not open its own copy, and
|
|
395
|
+
* `general_error` anything else including a result code this version does
|
|
396
|
+
* not recognize.
|
|
397
|
+
*
|
|
398
|
+
* Requires the message's media key, so it only works for media you received
|
|
399
|
+
* or sent, never for a bare message id. Newsletter messages are rejected -
|
|
400
|
+
* channel media has no per-message key to seal the request with. Pass an
|
|
401
|
+
* explicit `WaMediaRetryRequest` instead of an event when you hold the
|
|
402
|
+
* message id, chat jid, and media key but not the decoded message.
|
|
403
|
+
*
|
|
404
|
+
* @throws when the message carries no downloadable media, is a newsletter
|
|
405
|
+
* message, the session is not paired, the stanza cannot be sent, or no
|
|
406
|
+
* notification arrives before `options.timeoutMs`. It does **not** throw on
|
|
407
|
+
* a `not_found` / `general_error` answer - check `result`.
|
|
408
|
+
* @example
|
|
409
|
+
* ```ts
|
|
410
|
+
* const image = event.message?.imageMessage
|
|
411
|
+
* try {
|
|
412
|
+
* return await client.message.downloadBytes(event)
|
|
413
|
+
* } catch {
|
|
414
|
+
* const retry = await client.message.requestMediaReupload(event)
|
|
415
|
+
* if (retry.result !== 'success') throw new Error(`reupload ${retry.result}`)
|
|
416
|
+
* return await client.message.downloadBytes({
|
|
417
|
+
* imageMessage: { ...image, directPath: retry.directPath }
|
|
418
|
+
* })
|
|
419
|
+
* }
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
requestMediaReupload(event: WaIncomingMessageEvent, options?: {
|
|
423
|
+
readonly timeoutMs?: number;
|
|
424
|
+
}): Promise<WaMediaRetryResult>;
|
|
425
|
+
requestMediaReupload(request: WaMediaRetryRequest): Promise<WaMediaRetryResult>;
|
|
376
426
|
/**
|
|
377
427
|
* Attempts to decrypt an addon payload (poll vote, reaction, edit, ...)
|
|
378
428
|
* attached to `event` and, on success, emits a typed
|
|
@@ -9,6 +9,7 @@ const messages_1 = require("../messaging/messages");
|
|
|
9
9
|
const constants_1 = require("../../media/constants");
|
|
10
10
|
const addon_crypto_1 = require("../../message/crypto/addon-crypto");
|
|
11
11
|
const content_1 = require("../../message/encode/content");
|
|
12
|
+
const media_payload_1 = require("../../message/encode/media-payload");
|
|
12
13
|
const group_history_1 = require("../../message/kinds/group-history");
|
|
13
14
|
const _proto_1 = require("../../proto");
|
|
14
15
|
const jid_1 = require("../../protocol/jid");
|
|
@@ -79,6 +80,7 @@ class WaMessageCoordinator {
|
|
|
79
80
|
constructor(deps) {
|
|
80
81
|
this.messageDispatch = deps.messageDispatch;
|
|
81
82
|
this.mediaTransfer = deps.mediaTransfer;
|
|
83
|
+
this.mediaRetry = deps.mediaRetry;
|
|
82
84
|
this.mediaUploadOptions = deps.mediaUploadOptions;
|
|
83
85
|
this.logger = deps.logger;
|
|
84
86
|
this.messageStore = deps.messageStore;
|
|
@@ -486,6 +488,30 @@ class WaMessageCoordinator {
|
|
|
486
488
|
const stream = await this.download(source, options);
|
|
487
489
|
return (0, bytes_1.readAllBytes)(stream, { maxBytes: options.maxBytes });
|
|
488
490
|
}
|
|
491
|
+
requestMediaReupload(source, options) {
|
|
492
|
+
if (!('key' in source)) {
|
|
493
|
+
return this.mediaRetry.request(source);
|
|
494
|
+
}
|
|
495
|
+
const key = source.key;
|
|
496
|
+
if (!key.id || !key.remoteJid) {
|
|
497
|
+
throw new Error('requestMediaReupload event is missing key.remoteJid or key.id');
|
|
498
|
+
}
|
|
499
|
+
if (key.isNewsletter) {
|
|
500
|
+
throw new Error('requestMediaReupload is not supported on newsletter messages');
|
|
501
|
+
}
|
|
502
|
+
const payload = (0, media_payload_1.resolveMediaPayload)(source.message);
|
|
503
|
+
if (!payload) {
|
|
504
|
+
throw new Error('message has no downloadable media');
|
|
505
|
+
}
|
|
506
|
+
return this.mediaRetry.request({
|
|
507
|
+
messageId: key.id,
|
|
508
|
+
chatJid: key.remoteJid,
|
|
509
|
+
mediaKey: payload.mediaKey,
|
|
510
|
+
fromMe: key.fromMe,
|
|
511
|
+
participant: key.participant,
|
|
512
|
+
timeoutMs: options?.timeoutMs
|
|
513
|
+
});
|
|
514
|
+
}
|
|
489
515
|
/**
|
|
490
516
|
* Attempts to decrypt an addon payload (poll vote, reaction, edit, ...)
|
|
491
517
|
* attached to `event` and, on success, emits a typed
|
|
@@ -529,19 +555,23 @@ class WaMessageCoordinator {
|
|
|
529
555
|
}
|
|
530
556
|
return;
|
|
531
557
|
}
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
558
|
+
const modificationSenderRaw = event.key.participant ?? event.key.remoteJid;
|
|
559
|
+
if (!modificationSenderRaw)
|
|
560
|
+
return;
|
|
561
|
+
const modificationSenderCandidates = (0, addon_crypto_1.collectUniqueUserJids)(event.key.fromMe ? event.rawNode.attrs.from : undefined, modificationSenderRaw, event.key.participantAlt, event.key.remoteJidAlt, event.rawNode.attrs.participant_pn, event.rawNode.attrs.sender_pn, event.rawNode.attrs.participant, event.key.isGroup ? undefined : event.rawNode.attrs.from);
|
|
562
|
+
const keyParentSender = (0, addon_crypto_1.resolveAddonParentSenderFromKey)(addon.targetMessageKey, event.key.isGroup);
|
|
563
|
+
const parentMsgOriginalSenderCandidates = (0, addon_crypto_1.collectUniqueUserJids)(keyParentSender, parentEntry.senderJid);
|
|
564
|
+
const senderPairs = (0, addon_crypto_1.buildAddonSenderPairs)({
|
|
565
|
+
parentCandidates: parentMsgOriginalSenderCandidates,
|
|
566
|
+
modificationCandidates: modificationSenderCandidates
|
|
567
|
+
});
|
|
568
|
+
const plaintext = await (0, addon_crypto_1.decryptAddonPayloadWithSenderFallback)({
|
|
535
569
|
messageSecret: parentEntry.secret,
|
|
536
570
|
stanzaId: targetMessageId,
|
|
537
|
-
|
|
538
|
-
modificationSender,
|
|
571
|
+
senderPairs,
|
|
539
572
|
modificationType: addon.modificationType,
|
|
540
573
|
ciphertext: addon.encPayload,
|
|
541
|
-
iv: addon.encIv
|
|
542
|
-
additionalData: (0, addon_crypto_1.shouldUseAddonAdditionalData)(addon.modificationType)
|
|
543
|
-
? (0, addon_crypto_1.buildAddonAdditionalData)(targetMessageId, modificationSender)
|
|
544
|
-
: undefined
|
|
574
|
+
iv: addon.encIv
|
|
545
575
|
});
|
|
546
576
|
let decrypted = (0, addon_crypto_1.decodeAddonPlaintext)(addon.kind, plaintext);
|
|
547
577
|
if (decrypted.kind === 'poll_vote' && decrypted.pollVote.selectedOptions) {
|
|
@@ -188,6 +188,14 @@ export declare class WaMessageDispatchCoordinator {
|
|
|
188
188
|
private buildBotSidecarParticipant;
|
|
189
189
|
private publishGroupSenderKeyMessage;
|
|
190
190
|
private resolveGroupAddressingMode;
|
|
191
|
+
/**
|
|
192
|
+
* Parent-author JID stored with the outgoing message secret. Peers address
|
|
193
|
+
* us by LID in `pollCreationMessageKey` during the PN→LID migration, so a
|
|
194
|
+
* valid `meLid` wins. A malformed LID must not abort the send: fall back to
|
|
195
|
+
* a normalized `meJid`, then to the empty sender (same recoverable path as
|
|
196
|
+
* {@link resolveSenderForAddressingMode}).
|
|
197
|
+
*/
|
|
198
|
+
private resolveOutgoingSecretSenderJid;
|
|
191
199
|
private resolveSenderForAddressingMode;
|
|
192
200
|
private encryptGroupDistributionParticipants;
|
|
193
201
|
private publishDirectSignalMessageWithFanout;
|
|
@@ -374,9 +374,11 @@ class WaMessageDispatchCoordinator {
|
|
|
374
374
|
rawSecret.length > 0 &&
|
|
375
375
|
sendOptions.id &&
|
|
376
376
|
(this.deps.persistAllMessageSecrets || (0, content_1.needsSecretPersistence)(messageWithSecret))) {
|
|
377
|
-
const meJid = this.deps.getCurrentCredentials()?.meJid ?? '';
|
|
378
377
|
void this.deps.messageSecretStore
|
|
379
|
-
.set(sendOptions.id, {
|
|
378
|
+
.set(sendOptions.id, {
|
|
379
|
+
secret: rawSecret,
|
|
380
|
+
senderJid: this.resolveOutgoingSecretSenderJid()
|
|
381
|
+
})
|
|
380
382
|
.catch((error) => {
|
|
381
383
|
this.deps.logger.warn('failed to persist outgoing message secret', {
|
|
382
384
|
id: sendOptions.id,
|
|
@@ -398,17 +400,16 @@ class WaMessageDispatchCoordinator {
|
|
|
398
400
|
]);
|
|
399
401
|
const messageWithIcdc = (0, icdc_1.injectDeviceListMetadata)(messageWithSecret, senderIcdc, recipientIcdc);
|
|
400
402
|
const plaintext = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode(messageWithIcdc).finish());
|
|
401
|
-
const
|
|
403
|
+
const outboundAttrs = (0, content_1.resolveOutboundMessageAttrs)(messageWithIcdc);
|
|
404
|
+
const buttonAddonKind = outboundAttrs.buttonAddonKind;
|
|
402
405
|
const buttonAddonNode = buttonAddonKind ? (0, message_1.buildButtonAddonNode)(buttonAddonKind) : undefined;
|
|
403
406
|
// when a <biz> companion is attached the stanza must advertise type=text and
|
|
404
407
|
// omit enc.mediatype; sending type=media + mediatype=list/button alongside the
|
|
405
408
|
// companion is rejected by the server as SMAX_INVALID (479).
|
|
406
|
-
const type = buttonAddonKind ? 'text' :
|
|
407
|
-
const edit =
|
|
408
|
-
const mediatype = buttonAddonKind
|
|
409
|
-
|
|
410
|
-
: ((0, content_1.resolveEncMediaType)(messageWithIcdc) ?? undefined);
|
|
411
|
-
const metaAttrs = (0, content_1.resolveMetaAttrs)(messageWithIcdc);
|
|
409
|
+
const type = buttonAddonKind ? 'text' : outboundAttrs.typeAttr;
|
|
410
|
+
const edit = outboundAttrs.edit ?? undefined;
|
|
411
|
+
const mediatype = buttonAddonKind ? undefined : (outboundAttrs.mediatype ?? undefined);
|
|
412
|
+
const metaAttrs = outboundAttrs.metaAttrs;
|
|
412
413
|
const metaNode = metaAttrs ? (0, message_1.buildMetaNode)(metaAttrs) : undefined;
|
|
413
414
|
const customNodes = [];
|
|
414
415
|
if (metaNode)
|
|
@@ -420,7 +421,7 @@ class WaMessageDispatchCoordinator {
|
|
|
420
421
|
customNodes.push(node);
|
|
421
422
|
}
|
|
422
423
|
}
|
|
423
|
-
const decryptFail =
|
|
424
|
+
const decryptFail = outboundAttrs.decryptFail;
|
|
424
425
|
const envelope = {
|
|
425
426
|
message: messageWithIcdc,
|
|
426
427
|
plaintext,
|
|
@@ -540,7 +541,7 @@ class WaMessageDispatchCoordinator {
|
|
|
540
541
|
replayStatusSetting: statusSetting,
|
|
541
542
|
// Bare `<to jid=user>` ack hints route the skmsg through primary
|
|
542
543
|
// devices that already hold the sender key.
|
|
543
|
-
customize:
|
|
544
|
+
customize: ({ fanoutDeviceJids, distributionParticipants, messageWithSecret, sendOptions }) => {
|
|
544
545
|
const distributedAddressKeys = new Set();
|
|
545
546
|
for (let i = 0; i < distributionParticipants.length; i += 1) {
|
|
546
547
|
distributedAddressKeys.add((0, jid_1.signalAddressKey)(distributionParticipants[i].address));
|
|
@@ -560,7 +561,7 @@ class WaMessageDispatchCoordinator {
|
|
|
560
561
|
seenAck.add(userJid);
|
|
561
562
|
ackHints.push({ jid: userJid });
|
|
562
563
|
}
|
|
563
|
-
const reportingArtifacts =
|
|
564
|
+
const reportingArtifacts = this.tryBuildReportingTokenArtifacts({
|
|
564
565
|
message: messageWithSecret,
|
|
565
566
|
stanzaId: sendOptions.id,
|
|
566
567
|
senderUserJid: (0, jid_1.toUserJid)(senderJid),
|
|
@@ -638,14 +639,15 @@ class WaMessageDispatchCoordinator {
|
|
|
638
639
|
participants.push(entry);
|
|
639
640
|
}
|
|
640
641
|
}
|
|
642
|
+
const outboundAttrs = (0, content_1.resolveOutboundMessageAttrs)(messageWithSecret);
|
|
641
643
|
const messageNode = (0, message_1.buildGroupSenderKeyMessageNode)({
|
|
642
644
|
to: input.groupJid,
|
|
643
|
-
type:
|
|
645
|
+
type: outboundAttrs.typeAttr,
|
|
644
646
|
id: sendOptions.id,
|
|
645
647
|
phash: extras.phash,
|
|
646
|
-
edit:
|
|
647
|
-
mediatype:
|
|
648
|
-
decryptFail:
|
|
648
|
+
edit: outboundAttrs.edit ?? undefined,
|
|
649
|
+
mediatype: outboundAttrs.mediatype ?? undefined,
|
|
650
|
+
decryptFail: outboundAttrs.decryptFail,
|
|
649
651
|
groupCiphertext: groupCiphertext.ciphertext,
|
|
650
652
|
participants,
|
|
651
653
|
deviceIdentity: shouldAttachDeviceIdentity
|
|
@@ -819,8 +821,9 @@ class WaMessageDispatchCoordinator {
|
|
|
819
821
|
break;
|
|
820
822
|
}
|
|
821
823
|
}
|
|
822
|
-
const reportingArtifacts =
|
|
824
|
+
const reportingArtifacts = this.tryBuildReportingTokenArtifacts({
|
|
823
825
|
message,
|
|
826
|
+
messageBytes: (0, padding_1.unpadPkcs7)(plaintext),
|
|
824
827
|
stanzaId: sendOptions.id,
|
|
825
828
|
senderUserJid: (0, jid_1.toUserJid)(senderJid),
|
|
826
829
|
remoteJid: groupJid,
|
|
@@ -975,8 +978,9 @@ class WaMessageDispatchCoordinator {
|
|
|
975
978
|
phashTargets[phashTargetCount] = senderJid;
|
|
976
979
|
phashTargets.length = phashTargetCount + 1;
|
|
977
980
|
const localPhash = (0, phash_1.computePhashV2)(phashTargets);
|
|
978
|
-
const reportingArtifacts =
|
|
981
|
+
const reportingArtifacts = this.tryBuildReportingTokenArtifacts({
|
|
979
982
|
message,
|
|
983
|
+
messageBytes: (0, padding_1.unpadPkcs7)(plaintext),
|
|
980
984
|
stanzaId: sendOptions.id,
|
|
981
985
|
senderUserJid: (0, jid_1.toUserJid)(senderJid),
|
|
982
986
|
remoteJid: groupJid,
|
|
@@ -1070,6 +1074,41 @@ class WaMessageDispatchCoordinator {
|
|
|
1070
1074
|
});
|
|
1071
1075
|
return constants_1.WA_ADDRESSING_MODES.PN;
|
|
1072
1076
|
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Parent-author JID stored with the outgoing message secret. Peers address
|
|
1079
|
+
* us by LID in `pollCreationMessageKey` during the PN→LID migration, so a
|
|
1080
|
+
* valid `meLid` wins. A malformed LID must not abort the send: fall back to
|
|
1081
|
+
* a normalized `meJid`, then to the empty sender (same recoverable path as
|
|
1082
|
+
* {@link resolveSenderForAddressingMode}).
|
|
1083
|
+
*/
|
|
1084
|
+
resolveOutgoingSecretSenderJid() {
|
|
1085
|
+
const credentials = this.deps.getCurrentCredentials();
|
|
1086
|
+
const meLid = credentials?.meLid;
|
|
1087
|
+
if (meLid && meLid.includes('@')) {
|
|
1088
|
+
try {
|
|
1089
|
+
return (0, jid_1.toUserJid)(meLid);
|
|
1090
|
+
}
|
|
1091
|
+
catch (error) {
|
|
1092
|
+
this.deps.logger.trace('ignoring malformed me lid jid', {
|
|
1093
|
+
meLid,
|
|
1094
|
+
message: (0, primitives_2.toError)(error).message
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const meJid = credentials?.meJid;
|
|
1099
|
+
if (meJid && meJid.includes('@')) {
|
|
1100
|
+
try {
|
|
1101
|
+
return (0, jid_1.toUserJid)(meJid);
|
|
1102
|
+
}
|
|
1103
|
+
catch (error) {
|
|
1104
|
+
this.deps.logger.trace('ignoring malformed me jid', {
|
|
1105
|
+
meJid,
|
|
1106
|
+
message: (0, primitives_2.toError)(error).message
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return '';
|
|
1111
|
+
}
|
|
1073
1112
|
resolveSenderForAddressingMode(addressingMode, meJid) {
|
|
1074
1113
|
if (addressingMode === constants_1.WA_ADDRESSING_MODES.LID) {
|
|
1075
1114
|
const meLid = this.deps.getCurrentCredentials()?.meLid;
|
|
@@ -1088,9 +1127,6 @@ class WaMessageDispatchCoordinator {
|
|
|
1088
1127
|
return (0, jid_1.normalizeDeviceJid)(meJid);
|
|
1089
1128
|
}
|
|
1090
1129
|
async encryptGroupDistributionParticipants(groupJid, senderKeyId, senderKeyDistributionMessage, participantUserJids) {
|
|
1091
|
-
const distributionPayload = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode({
|
|
1092
|
-
senderKeyDistributionMessage
|
|
1093
|
-
}).finish());
|
|
1094
1130
|
const fanoutDeviceJids = await this.deps.fanoutResolver.resolveGroupParticipantDeviceJids(participantUserJids);
|
|
1095
1131
|
if (fanoutDeviceJids.length === 0) {
|
|
1096
1132
|
return {
|
|
@@ -1098,13 +1134,9 @@ class WaMessageDispatchCoordinator {
|
|
|
1098
1134
|
distributionParticipants: []
|
|
1099
1135
|
};
|
|
1100
1136
|
}
|
|
1101
|
-
const fanoutTargetsByAddressKey = new Map();
|
|
1102
1137
|
const fanoutAddresses = new Array(fanoutDeviceJids.length);
|
|
1103
1138
|
for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
|
|
1104
|
-
|
|
1105
|
-
const address = (0, jid_1.parseSignalAddressFromJid)(jid);
|
|
1106
|
-
fanoutAddresses[index] = address;
|
|
1107
|
-
fanoutTargetsByAddressKey.set((0, jid_1.signalAddressKey)(address), { jid, address });
|
|
1139
|
+
fanoutAddresses[index] = (0, jid_1.parseSignalAddressFromJid)(fanoutDeviceJids[index]);
|
|
1108
1140
|
}
|
|
1109
1141
|
const pendingAddresses = await this.deps.senderKeyManager.filterParticipantsNeedingDistribution(groupJid, senderKeyId, fanoutAddresses);
|
|
1110
1142
|
if (pendingAddresses.length === 0) {
|
|
@@ -1113,6 +1145,14 @@ class WaMessageDispatchCoordinator {
|
|
|
1113
1145
|
distributionParticipants: []
|
|
1114
1146
|
};
|
|
1115
1147
|
}
|
|
1148
|
+
const fanoutTargetsByAddressKey = new Map();
|
|
1149
|
+
for (let index = 0; index < fanoutAddresses.length; index += 1) {
|
|
1150
|
+
const address = fanoutAddresses[index];
|
|
1151
|
+
fanoutTargetsByAddressKey.set((0, jid_1.signalAddressKey)(address), {
|
|
1152
|
+
jid: fanoutDeviceJids[index],
|
|
1153
|
+
address
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1116
1156
|
const pendingAddressKeys = new Set();
|
|
1117
1157
|
const pendingTargets = [];
|
|
1118
1158
|
for (let index = 0; index < pendingAddresses.length; index += 1) {
|
|
@@ -1172,6 +1212,9 @@ class WaMessageDispatchCoordinator {
|
|
|
1172
1212
|
distributionParticipants: []
|
|
1173
1213
|
};
|
|
1174
1214
|
}
|
|
1215
|
+
const distributionPayload = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode({
|
|
1216
|
+
senderKeyDistributionMessage
|
|
1217
|
+
}).finish());
|
|
1175
1218
|
const distributionEncryptRequests = new Array(availableTargets.length);
|
|
1176
1219
|
for (let index = 0; index < availableTargets.length; index += 1) {
|
|
1177
1220
|
const target = availableTargets[index];
|
|
@@ -1324,8 +1367,9 @@ class WaMessageDispatchCoordinator {
|
|
|
1324
1367
|
const deviceIdentity = shouldAttachDeviceIdentity
|
|
1325
1368
|
? this.getEncodedSignedDeviceIdentity()
|
|
1326
1369
|
: undefined;
|
|
1327
|
-
const reportingArtifacts =
|
|
1370
|
+
const reportingArtifacts = this.tryBuildReportingTokenArtifacts({
|
|
1328
1371
|
message,
|
|
1372
|
+
messageBytes: (0, padding_1.unpadPkcs7)(plaintext),
|
|
1329
1373
|
stanzaId: sendOptions.id,
|
|
1330
1374
|
senderUserJid: meUserJid,
|
|
1331
1375
|
remoteJid: recipientUserJid,
|
|
@@ -1428,13 +1472,14 @@ class WaMessageDispatchCoordinator {
|
|
|
1428
1472
|
return `3EB0${(0, bytes_1.bytesToHex)(await (0, _crypto_1.randomBytesAsync)(8)).toUpperCase()}`;
|
|
1429
1473
|
}
|
|
1430
1474
|
}
|
|
1431
|
-
|
|
1475
|
+
tryBuildReportingTokenArtifacts(input) {
|
|
1432
1476
|
if (!input.stanzaId) {
|
|
1433
1477
|
return null;
|
|
1434
1478
|
}
|
|
1435
1479
|
try {
|
|
1436
|
-
return
|
|
1480
|
+
return (0, reporting_token_1.buildReportingTokenArtifacts)({
|
|
1437
1481
|
message: input.message,
|
|
1482
|
+
messageBytes: input.messageBytes,
|
|
1438
1483
|
stanzaId: input.stanzaId,
|
|
1439
1484
|
senderUserJid: input.senderUserJid,
|
|
1440
1485
|
remoteJid: input.remoteJid
|
|
@@ -607,8 +607,7 @@ class WaMobileCoordinator {
|
|
|
607
607
|
}
|
|
608
608
|
}
|
|
609
609
|
nodeBytes(parent, tag) {
|
|
610
|
-
|
|
611
|
-
return content instanceof Uint8Array ? content : null;
|
|
610
|
+
return (0, _transport_1.getNodeBytesContent)((0, _transport_1.findNodeChild)(parent, tag)) ?? null;
|
|
612
611
|
}
|
|
613
612
|
requirePrimaryIdentityKeyPair() {
|
|
614
613
|
if (!this.isMobilePrimary()) {
|
|
@@ -2,6 +2,7 @@ import { type WaBlocklistResult, type WaPrivacyDisallowedListResult, type WaPriv
|
|
|
2
2
|
import type { Logger } from '../../infra/log/types';
|
|
3
3
|
import { type WaPrivacyDisallowedListSettingName, type WaPrivacySettingName, type WaPrivacySettingValueMap } from '../../protocol/privacy';
|
|
4
4
|
import type { SignalUserJidPair } from '../../signal/api/SignalDeviceSyncApi';
|
|
5
|
+
import type { WaContactStore } from '../../store/contracts/contact.store';
|
|
5
6
|
import type { BinaryNode } from '../../transport/types';
|
|
6
7
|
/** JIDs to add to / remove from a category's disallowed list. */
|
|
7
8
|
export interface WaPrivacyDisallowedListInput {
|
|
@@ -47,7 +48,9 @@ export interface WaPrivacyCoordinator {
|
|
|
47
48
|
/**
|
|
48
49
|
* Fetches the per-category disallowed list (the JIDs explicitly excluded
|
|
49
50
|
* while the category sits on `'contact_blacklist'`). Returns an empty
|
|
50
|
-
* list when the category is on any other value.
|
|
51
|
+
* list when the category is on any other value. `entries` carries the same
|
|
52
|
+
* membership as `jids` plus each entry's handle, when the server
|
|
53
|
+
* identified it that way.
|
|
51
54
|
*/
|
|
52
55
|
readonly getDisallowedList: (category: WaPrivacyDisallowedListSettingName) => Promise<WaPrivacyDisallowedListResult>;
|
|
53
56
|
/**
|
|
@@ -83,7 +86,9 @@ export interface WaPrivacyCoordinator {
|
|
|
83
86
|
/**
|
|
84
87
|
* Returns the current account-wide blocklist. Blocks/unblocks performed
|
|
85
88
|
* on another device are refetched through the same dirty-bit path as
|
|
86
|
-
* {@link getPrivacySettings} and re-emitted as `blocklist`.
|
|
89
|
+
* {@link getPrivacySettings} and re-emitted as `blocklist`. `entries`
|
|
90
|
+
* carries the same membership as `jids` plus each entry's handle, when the
|
|
91
|
+
* server identified it that way.
|
|
87
92
|
*/
|
|
88
93
|
readonly getBlocklist: () => Promise<WaBlocklistResult>;
|
|
89
94
|
/**
|
|
@@ -98,6 +103,10 @@ export interface WaPrivacyCoordinator {
|
|
|
98
103
|
* phone-number input is resolved to its LID first (device-list cache,
|
|
99
104
|
* then a usync query). Non-migrated accounts fall back to the plain
|
|
100
105
|
* phone-jid form.
|
|
106
|
+
*
|
|
107
|
+
* A migrated entry also carries one identifier attribute, read from the
|
|
108
|
+
* stored contact: the phone jid, the username handle, or the display name.
|
|
109
|
+
* Entries with none of them are sent as `unknown_identifier`.
|
|
101
110
|
*/
|
|
102
111
|
readonly blockUser: (jid: string) => Promise<void>;
|
|
103
112
|
/**
|
|
@@ -138,6 +147,13 @@ interface WaPrivacyCoordinatorOptions {
|
|
|
138
147
|
*/
|
|
139
148
|
readonly getSelfLid: () => string | null;
|
|
140
149
|
readonly emitPrivacy: (event: WaPrivacyAccountSyncResult) => void;
|
|
150
|
+
/** Source of the username / display-name identifiers a migrated write carries. */
|
|
151
|
+
readonly contactStore: WaContactStore;
|
|
152
|
+
/**
|
|
153
|
+
* Server-synced `username_contact_privacy_setting_allow_uncontact_set_enable`
|
|
154
|
+
* AB-prop, off by default - entries then fall back to `pn_jid`.
|
|
155
|
+
*/
|
|
156
|
+
readonly isUsernamePrivacyListIdentifierEnabled: () => boolean;
|
|
141
157
|
}
|
|
142
158
|
/** Builds a {@link WaPrivacyCoordinator} backed by the given IQ query function. */
|
|
143
159
|
export declare function createPrivacyCoordinator(options: WaPrivacyCoordinatorOptions): WaPrivacyCoordinatorRuntime;
|
|
@@ -10,6 +10,30 @@ const privacy_2 = require("../../protocol/privacy");
|
|
|
10
10
|
const privacy_3 = require("../../transport/node/builders/privacy");
|
|
11
11
|
const query_1 = require("../../transport/node/query");
|
|
12
12
|
const primitives_1 = require("../../util/primitives");
|
|
13
|
+
/**
|
|
14
|
+
* Adds the identifier fields a migrated write may carry. Only the writes that
|
|
15
|
+
* consume them call it - an unblock addresses by jid alone, and a
|
|
16
|
+
* disallowed-list write with the AB gate closed falls back to `pn_jid`.
|
|
17
|
+
*/
|
|
18
|
+
async function withContactIdentifiers(options, target) {
|
|
19
|
+
if (target.lidJid === null)
|
|
20
|
+
return target;
|
|
21
|
+
try {
|
|
22
|
+
const contact = await options.contactStore.getByJid(target.lidJid);
|
|
23
|
+
return {
|
|
24
|
+
...target,
|
|
25
|
+
username: contact?.username ?? null,
|
|
26
|
+
displayName: contact?.displayName ?? null
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
options.logger.debug('contact identifier lookup failed', {
|
|
31
|
+
jid: target.lidJid,
|
|
32
|
+
message: (0, primitives_1.toError)(error).message
|
|
33
|
+
});
|
|
34
|
+
return target;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
13
37
|
/**
|
|
14
38
|
* Resolves a blocklist input into both addressing forms via
|
|
15
39
|
* `resolveUserJidPair` (device-list cache first, usync fallback for phone
|
|
@@ -50,10 +74,19 @@ async function resolveDisallowedListEntries(options, input) {
|
|
|
50
74
|
{ action: privacy_2.WA_PRIVACY_LIST_ACTIONS.ADD, jids: input.add ?? [] },
|
|
51
75
|
{ action: privacy_2.WA_PRIVACY_LIST_ACTIONS.REMOVE, jids: input.remove ?? [] }
|
|
52
76
|
];
|
|
77
|
+
const usernameIdentifierAllowed = options.isUsernamePrivacyListIdentifierEnabled();
|
|
53
78
|
for (const { action, jids } of actions) {
|
|
54
79
|
for (const jid of jids) {
|
|
55
|
-
const
|
|
56
|
-
|
|
80
|
+
const resolved = await resolveBlocklistTarget(options, jid);
|
|
81
|
+
const target = usernameIdentifierAllowed
|
|
82
|
+
? await withContactIdentifiers(options, resolved)
|
|
83
|
+
: resolved;
|
|
84
|
+
entries.push({
|
|
85
|
+
action,
|
|
86
|
+
lidJid: target.lidJid,
|
|
87
|
+
pnJid: target.pnJid,
|
|
88
|
+
username: target.username ?? null
|
|
89
|
+
});
|
|
57
90
|
}
|
|
58
91
|
}
|
|
59
92
|
if (entries.length === 0) {
|
|
@@ -192,7 +225,7 @@ function createPrivacyCoordinator(options) {
|
|
|
192
225
|
return (0, privacy_1.parseBlocklist)(result);
|
|
193
226
|
},
|
|
194
227
|
blockUser: async (jid) => {
|
|
195
|
-
const target = await resolveBlocklistTarget(options, jid);
|
|
228
|
+
const target = await withContactIdentifiers(options, await resolveBlocklistTarget(options, jid));
|
|
196
229
|
const node = (0, privacy_3.buildBlocklistBlockIq)(target);
|
|
197
230
|
const result = await queryWithContext('privacy.blockUser', node, undefined, {
|
|
198
231
|
jid: target.lidJid ?? target.pnJid
|
|
@@ -54,6 +54,24 @@ export interface WaUsernameAvailabilityResult {
|
|
|
54
54
|
readonly available: boolean;
|
|
55
55
|
readonly suggestions: readonly string[];
|
|
56
56
|
}
|
|
57
|
+
export type WaUsernameLookupResult = {
|
|
58
|
+
readonly status: 'found';
|
|
59
|
+
readonly jid: string;
|
|
60
|
+
readonly username: string | null;
|
|
61
|
+
readonly isBusiness: boolean;
|
|
62
|
+
readonly pnJid: string | null;
|
|
63
|
+
} | {
|
|
64
|
+
readonly status: 'key-required';
|
|
65
|
+
readonly username: string | null;
|
|
66
|
+
} | {
|
|
67
|
+
readonly status: 'not-found';
|
|
68
|
+
};
|
|
69
|
+
export interface WaResolveUsernameInput {
|
|
70
|
+
/** A leading `@` and a `:1234` key suffix are accepted. */
|
|
71
|
+
readonly username: string;
|
|
72
|
+
/** Overrides a key parsed out of {@link username}. */
|
|
73
|
+
readonly usernameKey?: string;
|
|
74
|
+
}
|
|
57
75
|
/**
|
|
58
76
|
* Coordinates own/peer profile queries and mutations: picture, status, text
|
|
59
77
|
* status, username, disappearing mode, and LID lookup. Accessed via
|
|
@@ -109,13 +127,23 @@ export interface WaProfileCoordinator {
|
|
|
109
127
|
readonly setTextStatus: (input: WaSetTextStatusInput) => Promise<void>;
|
|
110
128
|
/** Batched fetch of username per JID. */
|
|
111
129
|
readonly getUsernames: (jids: readonly string[]) => Promise<readonly WaUsernameResult[]>;
|
|
130
|
+
/**
|
|
131
|
+
* Resolves a username handle to the account's LID, so a message can be
|
|
132
|
+
* addressed to `@handle`. `'key-required'` means the server withheld the
|
|
133
|
+
* JID - repeat the call with `usernameKey`.
|
|
134
|
+
*
|
|
135
|
+
* @throws when the handle fails local validation, before any round-trip.
|
|
136
|
+
*/
|
|
137
|
+
readonly resolveUsername: (input: WaResolveUsernameInput) => Promise<WaUsernameLookupResult>;
|
|
112
138
|
/** Fetches the current account's username record (value, state, recovery pin). */
|
|
113
139
|
readonly getOwnUsername: () => Promise<WaOwnUsernameResult>;
|
|
114
140
|
/**
|
|
115
141
|
* Reserves/sets a username on the current account. Returns `true` only
|
|
116
|
-
* when the server reports `'SUCCESS'`; on any other outcome (taken,
|
|
117
|
-
*
|
|
118
|
-
*
|
|
142
|
+
* when the server reports `'SUCCESS'`; on any other server outcome (taken,
|
|
143
|
+
* rate-limited) it returns `false` without throwing - check the value and
|
|
144
|
+
* consult {@link checkUsernameAvailability} for suggestions.
|
|
145
|
+
*
|
|
146
|
+
* @throws when the handle fails local validation, before any round-trip.
|
|
119
147
|
*/
|
|
120
148
|
readonly setUsername: (input: WaSetUsernameInput) => Promise<boolean>;
|
|
121
149
|
/** Deletes the current account's username. Returns `true` on success. */
|
|
@@ -124,7 +152,11 @@ export interface WaProfileCoordinator {
|
|
|
124
152
|
readonly getAboutStatus: (jid: string) => Promise<string | null>;
|
|
125
153
|
/** Checks whether a username is available and returns server suggestions. */
|
|
126
154
|
readonly checkUsernameAvailability: (username: string) => Promise<WaUsernameAvailabilityResult>;
|
|
127
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* Sets the username recovery PIN. Returns `true` on success.
|
|
157
|
+
*
|
|
158
|
+
* @throws when `pin` is not exactly 4 digits, before any round-trip.
|
|
159
|
+
*/
|
|
128
160
|
readonly setUsernameKey: (pin: string) => Promise<boolean>;
|
|
129
161
|
/** Resolves LIDs for a list of phone numbers (handles normalization). */
|
|
130
162
|
readonly getLidsByPhoneNumbers: (phoneNumbers: readonly string[]) => Promise<readonly SignalLidSyncResult[]>;
|