zapo-js 0.1.1 → 0.2.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 +20 -4
- package/dist/appstate/WaAppStateCrypto.js +19 -26
- package/dist/appstate/WaAppStateSyncClient.js +293 -181
- package/dist/appstate/WaAppStateSyncResponseParser.js +16 -5
- package/dist/appstate/constants.js +4 -3
- package/dist/appstate/{store/sqlite.js → encoding.js} +13 -8
- package/dist/appstate/index.js +8 -6
- package/dist/appstate/utils.js +9 -34
- package/dist/auth/WaAuthClient.js +43 -61
- package/dist/auth/flow/WaAuthCredentialsFlow.js +22 -15
- package/dist/auth/index.js +1 -8
- package/dist/auth/pairing/WaPairingCodeCrypto.js +6 -4
- package/dist/auth/pairing/WaPairingFlow.js +34 -26
- package/dist/auth/pairing/WaQrFlow.js +37 -24
- package/dist/client/WaClient.js +275 -324
- package/dist/client/WaClientFactory.js +500 -133
- package/dist/client/connection/WaConnectionManager.js +301 -0
- package/dist/client/connection/WaKeyShareCoordinator.js +63 -0
- package/dist/client/connection/WaReceiptQueue.js +51 -0
- package/dist/client/coordinators/WaAppStateMutationCoordinator.js +471 -0
- package/dist/client/coordinators/WaBusinessCoordinator.js +241 -0
- package/dist/client/coordinators/WaGroupCoordinator.js +30 -16
- package/dist/client/coordinators/WaIncomingNodeCoordinator.js +21 -27
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +439 -701
- package/dist/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
- package/dist/client/coordinators/WaPrivacyCoordinator.js +134 -0
- package/dist/client/coordinators/WaProfileCoordinator.js +212 -0
- package/dist/client/coordinators/WaRetryCoordinator.js +242 -57
- package/dist/client/coordinators/WaStreamControlCoordinator.js +18 -11
- package/dist/client/coordinators/WaTrustedContactTokenCoordinator.js +166 -0
- package/dist/client/dirty.js +74 -48
- package/dist/client/events/chat.js +4 -3
- package/dist/client/events/devices.js +72 -0
- package/dist/client/events/group.js +62 -47
- package/dist/client/events/identity.js +22 -0
- package/dist/client/events/privacy-token.js +39 -0
- package/dist/client/history-sync.js +94 -63
- package/dist/client/incoming.js +60 -27
- package/dist/client/mailbox.js +24 -23
- package/dist/client/messages.js +107 -31
- package/dist/client/messaging/fanout.js +199 -0
- package/dist/client/messaging/key-protocol.js +130 -0
- package/dist/client/messaging/participants.js +193 -0
- package/dist/client/persistence/WriteBehindPersistence.js +129 -0
- package/dist/client/tokens/cs-token.js +50 -0
- package/dist/client/tokens/tc-token.js +25 -0
- package/dist/crypto/core/hkdf.js +3 -8
- package/dist/crypto/core/index.js +2 -5
- package/dist/crypto/core/keys.js +6 -7
- package/dist/crypto/core/nonce.js +2 -0
- package/dist/crypto/core/primitives.js +12 -23
- package/dist/crypto/core/random.js +26 -23
- package/dist/crypto/curves/Ed25519.js +7 -8
- package/dist/crypto/curves/X25519.js +38 -22
- package/dist/crypto/index.js +1 -3
- package/dist/crypto/math/constants.js +13 -36
- package/dist/crypto/math/edwards.js +171 -44
- package/dist/crypto/math/fe.js +706 -0
- package/dist/crypto/math/mod.js +10 -3
- package/dist/esm/appstate/WaAppStateCrypto.js +7 -14
- package/dist/esm/appstate/WaAppStateSyncClient.js +284 -172
- package/dist/esm/appstate/WaAppStateSyncResponseParser.js +17 -6
- package/dist/esm/appstate/constants.js +3 -2
- package/dist/esm/appstate/{store/sqlite.js → encoding.js} +13 -8
- package/dist/esm/appstate/index.js +2 -2
- package/dist/esm/appstate/utils.js +8 -30
- package/dist/esm/auth/WaAuthClient.js +43 -61
- package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +22 -15
- package/dist/esm/auth/index.js +0 -3
- package/dist/esm/auth/pairing/WaPairingCodeCrypto.js +6 -4
- package/dist/esm/auth/pairing/WaPairingFlow.js +28 -20
- package/dist/esm/auth/pairing/WaQrFlow.js +37 -24
- package/dist/esm/client/WaClient.js +275 -324
- package/dist/esm/client/WaClientFactory.js +501 -134
- package/dist/esm/client/connection/WaConnectionManager.js +297 -0
- package/dist/esm/client/connection/WaKeyShareCoordinator.js +59 -0
- package/dist/esm/client/connection/WaReceiptQueue.js +47 -0
- package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +467 -0
- package/dist/esm/client/coordinators/WaBusinessCoordinator.js +238 -0
- package/dist/esm/client/coordinators/WaGroupCoordinator.js +23 -9
- package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +21 -27
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +443 -705
- package/dist/esm/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
- package/dist/esm/client/coordinators/WaPrivacyCoordinator.js +131 -0
- package/dist/esm/client/coordinators/WaProfileCoordinator.js +209 -0
- package/dist/esm/client/coordinators/WaRetryCoordinator.js +244 -59
- package/dist/esm/client/coordinators/WaStreamControlCoordinator.js +19 -12
- package/dist/esm/client/coordinators/WaTrustedContactTokenCoordinator.js +162 -0
- package/dist/esm/client/dirty.js +69 -43
- package/dist/esm/client/events/chat.js +4 -3
- package/dist/esm/client/events/devices.js +68 -0
- package/dist/esm/client/events/group.js +53 -39
- package/dist/esm/client/events/identity.js +19 -0
- package/dist/esm/client/events/privacy-token.js +36 -0
- package/dist/esm/client/history-sync.js +91 -60
- package/dist/esm/client/incoming.js +61 -28
- package/dist/esm/client/mailbox.js +24 -23
- package/dist/esm/client/messages.js +108 -32
- package/dist/esm/client/messaging/fanout.js +196 -0
- package/dist/esm/client/messaging/key-protocol.js +127 -0
- package/dist/esm/client/messaging/participants.js +190 -0
- package/dist/esm/client/persistence/WriteBehindPersistence.js +125 -0
- package/dist/esm/client/tokens/cs-token.js +46 -0
- package/dist/esm/client/tokens/tc-token.js +18 -0
- package/dist/esm/crypto/core/hkdf.js +3 -8
- package/dist/esm/crypto/core/index.js +2 -3
- package/dist/esm/crypto/core/keys.js +3 -4
- package/dist/esm/crypto/core/nonce.js +2 -0
- package/dist/esm/crypto/core/primitives.js +12 -22
- package/dist/esm/crypto/core/random.js +25 -23
- package/dist/esm/crypto/curves/Ed25519.js +4 -5
- package/dist/esm/crypto/curves/X25519.js +35 -19
- package/dist/esm/crypto/index.js +0 -1
- package/dist/esm/crypto/math/constants.js +12 -35
- package/dist/esm/crypto/math/edwards.js +174 -47
- package/dist/esm/crypto/math/fe.js +691 -0
- package/dist/esm/crypto/math/mod.js +10 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/infra/log/ConsoleLogger.js +18 -17
- package/dist/esm/infra/log/PinoLogger.js +15 -9
- package/dist/esm/infra/log/types.js +11 -1
- package/dist/esm/infra/perf/BackgroundQueue.js +478 -0
- package/dist/esm/infra/perf/BoundedTaskQueue.js +16 -18
- package/dist/esm/infra/perf/PromiseDedup.js +20 -0
- package/dist/esm/infra/perf/SharedExclusiveGate.js +109 -0
- package/dist/esm/infra/perf/StoreLock.js +77 -0
- package/dist/esm/media/WaMediaCrypto.js +96 -16
- package/dist/esm/media/WaMediaTransferClient.js +251 -91
- package/dist/esm/media/conn.js +10 -6
- package/dist/esm/media/constants.js +6 -2
- package/dist/esm/message/WaMessageClient.js +30 -32
- package/dist/esm/message/ack.js +6 -6
- package/dist/esm/message/addon-crypto.js +59 -0
- package/dist/esm/message/content.js +195 -9
- package/dist/esm/message/icdc.js +76 -0
- package/dist/esm/message/incoming.js +129 -122
- package/dist/esm/message/index.js +2 -0
- package/dist/esm/message/phash.js +3 -1
- package/dist/esm/message/reporting-token.js +425 -0
- package/dist/esm/message/use-case-secret.js +49 -0
- package/dist/esm/protocol/appstate.js +27 -0
- package/dist/esm/protocol/browser.js +10 -18
- package/dist/esm/protocol/constants.js +6 -3
- package/dist/esm/protocol/defaults.js +6 -0
- package/dist/esm/protocol/index.js +2 -11
- package/dist/esm/protocol/jid.js +133 -52
- package/dist/esm/protocol/media.js +3 -3
- package/dist/esm/protocol/message.js +61 -1
- package/dist/esm/protocol/nodes.js +4 -0
- package/dist/esm/protocol/notification.js +3 -1
- package/dist/esm/protocol/privacy-token.js +17 -0
- package/dist/esm/protocol/privacy.js +55 -0
- package/dist/esm/protocol/stream.js +26 -1
- package/dist/esm/protocol/usync.js +11 -0
- package/dist/esm/retry/codec.js +216 -0
- package/dist/esm/retry/constants.js +1 -1
- package/dist/esm/retry/index.js +3 -2
- package/dist/esm/retry/parse.js +88 -86
- package/dist/esm/retry/replay.js +54 -51
- package/dist/esm/retry/tracker.js +94 -0
- package/dist/esm/signal/api/SignalDeviceSyncApi.js +276 -92
- package/dist/esm/signal/api/SignalDigestSyncApi.js +17 -8
- package/dist/esm/signal/api/SignalIdentitySyncApi.js +67 -37
- package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +86 -67
- package/dist/esm/signal/api/SignalRotateKeyApi.js +4 -2
- package/dist/esm/signal/api/SignalSessionSyncApi.js +36 -34
- package/dist/esm/signal/api/result-map.js +10 -0
- package/dist/esm/signal/constants.js +0 -4
- package/dist/esm/signal/crypto/WaAdvSignature.js +13 -9
- package/dist/esm/signal/{store/sqlite.js → encoding.js} +93 -60
- package/dist/esm/signal/group/SenderKeyChain.js +28 -23
- package/dist/esm/signal/group/SenderKeyCodec.js +5 -6
- package/dist/esm/signal/group/SenderKeyManager.js +144 -115
- package/dist/esm/signal/index.js +2 -0
- package/dist/esm/signal/registration/keygen.js +6 -2
- package/dist/esm/signal/registration/utils.js +1 -0
- package/dist/esm/signal/session/SignalProtocol.js +164 -53
- package/dist/esm/signal/session/SignalRatchet.js +24 -15
- package/dist/esm/signal/session/SignalSession.js +14 -9
- package/dist/esm/signal/session/resolver.js +221 -0
- package/dist/esm/store/contracts/privacy-token.store.js +1 -0
- package/dist/esm/store/createStore.js +100 -188
- package/dist/esm/store/index.js +1 -10
- package/dist/esm/store/locks/appstate.lock.js +26 -0
- package/dist/esm/store/locks/auth.lock.js +15 -0
- package/dist/esm/store/locks/contact.lock.js +20 -0
- package/dist/esm/store/locks/device-list.lock.js +20 -0
- package/dist/esm/store/locks/message.lock.js +21 -0
- package/dist/esm/store/locks/participants.lock.js +20 -0
- package/dist/esm/store/locks/privacy-token.lock.js +18 -0
- package/dist/esm/store/locks/retry.lock.js +29 -0
- package/dist/esm/store/locks/sender-key.lock.js +52 -0
- package/dist/esm/store/locks/signal.lock.js +63 -0
- package/dist/esm/store/locks/thread.lock.js +21 -0
- package/dist/esm/store/noop.store.js +4 -7
- package/dist/esm/store/providers/memory/appstate.store.js +38 -16
- package/dist/esm/store/providers/memory/contact.store.js +5 -0
- package/dist/esm/store/providers/memory/device-list.store.js +12 -34
- package/dist/esm/store/providers/memory/message.store.js +11 -5
- package/dist/esm/store/providers/memory/participants.store.js +1 -8
- package/dist/esm/store/providers/memory/privacy-token.store.js +43 -0
- package/dist/esm/store/providers/memory/retry.store.js +77 -2
- package/dist/esm/store/providers/memory/sender-key.store.js +11 -8
- package/dist/esm/store/providers/memory/signal.store.js +47 -18
- package/dist/esm/store/providers/memory/thread.store.js +5 -0
- package/dist/esm/transport/WaComms.js +28 -24
- package/dist/esm/transport/WaWebSocket.js +115 -18
- package/dist/esm/transport/binary/constants.js +0 -30
- package/dist/esm/transport/binary/decoder.js +8 -8
- package/dist/esm/transport/binary/encoder.js +10 -9
- package/dist/esm/transport/binary/index.js +0 -1
- package/dist/esm/transport/index.js +1 -0
- package/dist/esm/transport/keepalive/WaKeepAlive.js +2 -8
- package/dist/esm/transport/node/WaNodeOrchestrator.js +25 -21
- package/dist/esm/transport/node/WaNodeTransport.js +0 -3
- package/dist/esm/transport/node/builders/{accountSync.js → account-sync.js} +16 -36
- package/dist/esm/transport/node/builders/business.js +129 -0
- package/dist/esm/transport/node/builders/global.js +370 -0
- package/dist/esm/transport/node/builders/index.js +7 -3
- package/dist/esm/transport/node/builders/message.js +63 -230
- package/dist/esm/transport/node/builders/pairing.js +2 -27
- package/dist/esm/transport/node/builders/privacy-token.js +41 -0
- package/dist/esm/transport/node/builders/privacy.js +48 -0
- package/dist/esm/transport/node/builders/profile.js +70 -0
- package/dist/esm/transport/node/builders/retry.js +10 -22
- package/dist/esm/transport/node/builders/usync.js +45 -0
- package/dist/esm/transport/node/helpers.js +125 -5
- package/dist/esm/transport/node/usync.js +5 -0
- package/dist/esm/transport/node/xml.js +35 -14
- package/dist/esm/transport/noise/WaClientPayload.js +10 -10
- package/dist/esm/transport/noise/WaFrameCodec.js +48 -33
- package/dist/esm/transport/noise/WaNoiseCert.js +4 -7
- package/dist/esm/transport/noise/WaNoiseSession.js +77 -29
- package/dist/esm/transport/noise/WaNoiseSocket.js +8 -4
- package/dist/esm/transport/proxy.js +27 -0
- package/dist/esm/transport/stream/parse.js +17 -48
- package/dist/esm/util/bytes.js +67 -45
- package/dist/esm/util/coercion.js +6 -14
- package/dist/esm/util/index.js +5 -0
- package/dist/esm/util/primitives.js +40 -14
- package/dist/index.js +7 -1
- package/dist/infra/log/ConsoleLogger.js +18 -17
- package/dist/infra/log/PinoLogger.js +15 -9
- package/dist/infra/log/types.js +12 -0
- package/dist/infra/perf/BackgroundQueue.js +482 -0
- package/dist/infra/perf/BoundedTaskQueue.js +16 -18
- package/dist/infra/perf/PromiseDedup.js +24 -0
- package/dist/infra/perf/SharedExclusiveGate.js +113 -0
- package/dist/infra/perf/StoreLock.js +81 -0
- package/dist/media/WaMediaCrypto.js +95 -15
- package/dist/media/WaMediaTransferClient.js +284 -91
- package/dist/media/conn.js +10 -6
- package/dist/media/constants.js +6 -2
- package/dist/message/WaMessageClient.js +31 -33
- package/dist/message/ack.js +6 -6
- package/dist/message/addon-crypto.js +65 -0
- package/dist/message/content.js +198 -9
- package/dist/message/icdc.js +81 -0
- package/dist/message/incoming.js +127 -120
- package/dist/message/index.js +2 -0
- package/dist/message/phash.js +3 -1
- package/dist/message/reporting-token.js +429 -0
- package/dist/message/use-case-secret.js +55 -0
- package/dist/protocol/appstate.js +28 -1
- package/dist/protocol/browser.js +10 -18
- package/dist/protocol/constants.js +26 -1
- package/dist/protocol/defaults.js +6 -0
- package/dist/protocol/index.js +23 -42
- package/dist/protocol/jid.js +140 -52
- package/dist/protocol/media.js +3 -3
- package/dist/protocol/message.js +62 -2
- package/dist/protocol/nodes.js +4 -0
- package/dist/protocol/notification.js +3 -1
- package/dist/protocol/privacy-token.js +20 -0
- package/dist/protocol/privacy.js +58 -0
- package/dist/protocol/stream.js +27 -2
- package/dist/protocol/usync.js +14 -0
- package/dist/retry/codec.js +220 -0
- package/dist/retry/constants.js +1 -1
- package/dist/retry/index.js +7 -5
- package/dist/retry/parse.js +88 -85
- package/dist/retry/replay.js +52 -49
- package/dist/retry/tracker.js +97 -0
- package/dist/signal/api/SignalDeviceSyncApi.js +273 -89
- package/dist/signal/api/SignalDigestSyncApi.js +17 -8
- package/dist/signal/api/SignalIdentitySyncApi.js +66 -36
- package/dist/signal/api/SignalMissingPreKeysSyncApi.js +82 -63
- package/dist/signal/api/SignalRotateKeyApi.js +4 -2
- package/dist/signal/api/SignalSessionSyncApi.js +36 -34
- package/dist/signal/api/result-map.js +13 -0
- package/dist/signal/constants.js +1 -5
- package/dist/signal/crypto/WaAdvSignature.js +11 -7
- package/dist/signal/{store/sqlite.js → encoding.js} +94 -61
- package/dist/signal/group/SenderKeyChain.js +27 -22
- package/dist/signal/group/SenderKeyCodec.js +5 -6
- package/dist/signal/group/SenderKeyManager.js +144 -115
- package/dist/signal/index.js +15 -1
- package/dist/signal/registration/keygen.js +6 -2
- package/dist/signal/registration/utils.js +1 -0
- package/dist/signal/session/SignalProtocol.js +164 -53
- package/dist/signal/session/SignalRatchet.js +24 -15
- package/dist/signal/session/SignalSession.js +14 -9
- package/dist/signal/session/resolver.js +224 -0
- package/dist/store/contracts/privacy-token.store.js +2 -0
- package/dist/store/createStore.js +100 -188
- package/dist/store/index.js +15 -33
- package/dist/store/locks/appstate.lock.js +29 -0
- package/dist/store/locks/auth.lock.js +18 -0
- package/dist/store/locks/contact.lock.js +23 -0
- package/dist/store/locks/device-list.lock.js +23 -0
- package/dist/store/locks/message.lock.js +24 -0
- package/dist/store/locks/participants.lock.js +23 -0
- package/dist/store/locks/privacy-token.lock.js +21 -0
- package/dist/store/locks/retry.lock.js +32 -0
- package/dist/store/locks/sender-key.lock.js +55 -0
- package/dist/store/locks/signal.lock.js +66 -0
- package/dist/store/locks/thread.lock.js +24 -0
- package/dist/store/noop.store.js +4 -7
- package/dist/store/providers/memory/appstate.store.js +36 -14
- package/dist/store/providers/memory/contact.store.js +5 -0
- package/dist/store/providers/memory/device-list.store.js +12 -34
- package/dist/store/providers/memory/message.store.js +11 -5
- package/dist/store/providers/memory/participants.store.js +1 -8
- package/dist/store/providers/memory/privacy-token.store.js +47 -0
- package/dist/store/providers/memory/retry.store.js +77 -2
- package/dist/store/providers/memory/sender-key.store.js +14 -11
- package/dist/store/providers/memory/signal.store.js +54 -25
- package/dist/store/providers/memory/thread.store.js +5 -0
- package/dist/transport/WaComms.js +30 -26
- package/dist/transport/WaWebSocket.js +148 -18
- package/dist/transport/binary/constants.js +1 -31
- package/dist/transport/binary/decoder.js +8 -8
- package/dist/transport/binary/encoder.js +10 -9
- package/dist/transport/binary/index.js +0 -4
- package/dist/transport/index.js +7 -1
- package/dist/transport/keepalive/WaKeepAlive.js +1 -7
- package/dist/transport/node/WaNodeOrchestrator.js +25 -21
- package/dist/transport/node/WaNodeTransport.js +0 -3
- package/dist/transport/node/builders/{accountSync.js → account-sync.js} +15 -35
- package/dist/transport/node/builders/business.js +137 -0
- package/dist/transport/node/builders/global.js +375 -0
- package/dist/transport/node/builders/index.js +29 -17
- package/dist/transport/node/builders/message.js +64 -236
- package/dist/transport/node/builders/pairing.js +2 -29
- package/dist/transport/node/builders/privacy-token.js +46 -0
- package/dist/transport/node/builders/privacy.js +55 -0
- package/dist/transport/node/builders/profile.js +78 -0
- package/dist/transport/node/builders/retry.js +9 -21
- package/dist/transport/node/builders/usync.js +49 -0
- package/dist/transport/node/helpers.js +131 -4
- package/dist/transport/node/usync.js +8 -0
- package/dist/transport/node/xml.js +35 -14
- package/dist/transport/noise/WaClientPayload.js +13 -13
- package/dist/transport/noise/WaFrameCodec.js +47 -32
- package/dist/transport/noise/WaNoiseCert.js +5 -8
- package/dist/transport/noise/WaNoiseSession.js +77 -29
- package/dist/transport/noise/WaNoiseSocket.js +8 -4
- package/dist/transport/proxy.js +34 -0
- package/dist/transport/stream/parse.js +20 -52
- package/dist/types/appstate/WaAppStateCrypto.d.ts +0 -1
- package/dist/types/appstate/WaAppStateSyncClient.d.ts +5 -2
- package/dist/types/appstate/constants.d.ts +1 -0
- package/dist/types/appstate/encoding.d.ts +7 -0
- package/dist/types/appstate/index.d.ts +3 -3
- package/dist/types/appstate/utils.d.ts +0 -3
- package/dist/types/auth/WaAuthClient.d.ts +10 -12
- package/dist/types/auth/flow/WaAuthCredentialsFlow.d.ts +1 -1
- package/dist/types/auth/index.d.ts +0 -4
- package/dist/types/auth/pairing/WaQrFlow.d.ts +1 -1
- package/dist/types/auth/types.d.ts +7 -9
- package/dist/types/client/WaClient.d.ts +42 -25
- package/dist/types/client/WaClientFactory.d.ts +33 -26
- package/dist/types/client/connection/WaConnectionManager.d.ts +66 -0
- package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +14 -0
- package/dist/types/client/connection/WaReceiptQueue.d.ts +13 -0
- package/dist/types/client/coordinators/WaAppStateMutationCoordinator.d.ts +46 -0
- package/dist/types/client/coordinators/WaBusinessCoordinator.d.ts +57 -0
- package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +3 -2
- package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +29 -38
- package/dist/types/client/coordinators/WaPassiveTasksCoordinator.d.ts +4 -0
- package/dist/types/client/coordinators/WaPrivacyCoordinator.d.ts +26 -0
- package/dist/types/client/coordinators/WaProfileCoordinator.d.ts +36 -0
- package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +8 -0
- package/dist/types/client/coordinators/WaStreamControlCoordinator.d.ts +3 -2
- package/dist/types/client/coordinators/WaTrustedContactTokenCoordinator.d.ts +45 -0
- package/dist/types/client/dirty.d.ts +1 -0
- package/dist/types/client/events/devices.d.ts +20 -0
- package/dist/types/client/events/group.d.ts +2 -1
- package/dist/types/client/events/identity.d.ts +9 -0
- package/dist/types/client/events/privacy-token.d.ts +7 -0
- package/dist/types/client/history-sync.d.ts +9 -6
- package/dist/types/client/incoming.d.ts +3 -1
- package/dist/types/client/index.d.ts +1 -1
- package/dist/types/client/mailbox.d.ts +3 -5
- package/dist/types/client/messages.d.ts +1 -2
- package/dist/types/client/messaging/fanout.d.ts +14 -0
- package/dist/types/client/messaging/key-protocol.d.ts +18 -0
- package/dist/types/client/messaging/participants.d.ts +13 -0
- package/dist/types/client/persistence/WriteBehindPersistence.d.ts +34 -0
- package/dist/types/client/tokens/cs-token.d.ts +10 -0
- package/dist/types/client/tokens/tc-token.d.ts +5 -0
- package/dist/types/client/types.d.ts +75 -4
- package/dist/types/crypto/core/hkdf.d.ts +0 -6
- package/dist/types/crypto/core/index.d.ts +2 -3
- package/dist/types/crypto/core/nonce.d.ts +2 -0
- package/dist/types/crypto/core/primitives.d.ts +0 -1
- package/dist/types/crypto/core/random.d.ts +2 -7
- package/dist/types/crypto/index.d.ts +0 -1
- package/dist/types/crypto/math/constants.d.ts +4 -2
- package/dist/types/crypto/math/fe.d.ts +30 -0
- package/dist/types/crypto/math/mod.d.ts +0 -2
- package/dist/types/crypto/math/types.d.ts +11 -4
- package/dist/types/index.d.ts +5 -3
- package/dist/types/infra/log/ConsoleLogger.d.ts +2 -1
- package/dist/types/infra/log/PinoLogger.d.ts +1 -1
- package/dist/types/infra/log/types.d.ts +1 -0
- package/dist/types/infra/perf/BackgroundQueue.d.ts +58 -0
- package/dist/types/infra/perf/BoundedTaskQueue.d.ts +1 -1
- package/dist/types/infra/perf/PromiseDedup.d.ts +4 -0
- package/dist/types/infra/perf/SharedExclusiveGate.d.ts +17 -0
- package/dist/types/infra/perf/StoreLock.d.ts +10 -0
- package/dist/types/media/WaMediaCrypto.d.ts +3 -2
- package/dist/types/media/WaMediaTransferClient.d.ts +16 -15
- package/dist/types/media/constants.d.ts +1 -1
- package/dist/types/media/index.d.ts +1 -1
- package/dist/types/media/types.d.ts +15 -2
- package/dist/types/message/addon-crypto.d.ts +25 -0
- package/dist/types/message/content.d.ts +8 -0
- package/dist/types/message/icdc.d.ts +13 -0
- package/dist/types/message/index.d.ts +2 -0
- package/dist/types/message/reporting-token.d.ts +18 -0
- package/dist/types/message/types.d.ts +45 -6
- package/dist/types/message/use-case-secret.d.ts +20 -0
- package/dist/types/protocol/appstate.d.ts +47 -0
- package/dist/types/protocol/constants.d.ts +8 -3
- package/dist/types/protocol/defaults.d.ts +6 -0
- package/dist/types/protocol/index.d.ts +2 -11
- package/dist/types/protocol/jid.d.ts +22 -5
- package/dist/types/protocol/message.d.ts +60 -0
- package/dist/types/protocol/nodes.d.ts +4 -0
- package/dist/types/protocol/notification.d.ts +2 -0
- package/dist/types/protocol/privacy-token.d.ts +17 -0
- package/dist/types/protocol/privacy.d.ts +75 -0
- package/dist/types/protocol/stream.d.ts +30 -0
- package/dist/types/protocol/usync.d.ts +11 -0
- package/dist/types/retry/codec.d.ts +3 -0
- package/dist/types/retry/index.d.ts +4 -3
- package/dist/types/retry/parse.d.ts +5 -2
- package/dist/types/retry/replay.d.ts +0 -4
- package/dist/types/retry/tracker.d.ts +20 -0
- package/dist/types/retry/types.d.ts +10 -4
- package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +15 -2
- package/dist/types/signal/api/SignalDigestSyncApi.d.ts +6 -0
- package/dist/types/signal/api/SignalIdentitySyncApi.d.ts +2 -0
- package/dist/types/signal/api/SignalRotateKeyApi.d.ts +4 -5
- package/dist/types/signal/api/SignalSessionSyncApi.d.ts +8 -6
- package/dist/types/signal/api/result-map.d.ts +1 -0
- package/dist/types/signal/constants.d.ts +0 -3
- package/dist/types/signal/{store/sqlite.d.ts → encoding.d.ts} +3 -3
- package/dist/types/signal/group/SenderKeyCodec.d.ts +4 -6
- package/dist/types/signal/group/SenderKeyManager.d.ts +10 -5
- package/dist/types/signal/index.d.ts +3 -0
- package/dist/types/signal/session/SignalProtocol.d.ts +19 -4
- package/dist/types/signal/session/resolver.d.ts +22 -0
- package/dist/types/store/contracts/appstate.store.d.ts +4 -1
- package/dist/types/store/contracts/contact.store.d.ts +1 -0
- package/dist/types/store/contracts/device-list.store.d.ts +0 -3
- package/dist/types/store/contracts/message.store.d.ts +1 -0
- package/dist/types/store/contracts/participants.store.d.ts +0 -1
- package/dist/types/store/contracts/privacy-token.store.d.ts +16 -0
- package/dist/types/store/contracts/retry.store.d.ts +7 -0
- package/dist/types/store/contracts/sender-key.store.d.ts +0 -1
- package/dist/types/store/contracts/signal.store.d.ts +13 -0
- package/dist/types/store/contracts/thread.store.d.ts +1 -0
- package/dist/types/store/createStore.d.ts +1 -1
- package/dist/types/store/index.d.ts +5 -13
- package/dist/types/store/locks/appstate.lock.d.ts +3 -0
- package/dist/types/store/locks/auth.lock.d.ts +3 -0
- package/dist/types/store/locks/contact.lock.d.ts +3 -0
- package/dist/types/store/locks/device-list.lock.d.ts +2 -0
- package/dist/types/store/locks/message.lock.d.ts +3 -0
- package/dist/types/store/locks/participants.lock.d.ts +2 -0
- package/dist/types/store/locks/privacy-token.lock.d.ts +2 -0
- package/dist/types/store/locks/retry.lock.d.ts +2 -0
- package/dist/types/store/locks/sender-key.lock.d.ts +3 -0
- package/dist/types/store/locks/signal.lock.d.ts +3 -0
- package/dist/types/store/locks/thread.lock.d.ts +3 -0
- package/dist/types/store/providers/memory/appstate.store.d.ts +3 -1
- package/dist/types/store/providers/memory/contact.store.d.ts +1 -0
- package/dist/types/store/providers/memory/device-list.store.d.ts +0 -3
- package/dist/types/store/providers/memory/message.store.d.ts +1 -0
- package/dist/types/store/providers/memory/participants.store.d.ts +0 -1
- package/dist/types/store/providers/memory/privacy-token.store.d.ts +13 -0
- package/dist/types/store/providers/memory/retry.store.d.ts +8 -0
- package/dist/types/store/providers/memory/sender-key.store.d.ts +0 -1
- package/dist/types/store/providers/memory/signal.store.d.ts +8 -1
- package/dist/types/store/providers/memory/thread.store.d.ts +1 -0
- package/dist/types/store/types.d.ts +49 -58
- package/dist/types/transport/WaWebSocket.d.ts +3 -1
- package/dist/types/transport/binary/constants.d.ts +0 -30
- package/dist/types/transport/binary/index.d.ts +0 -1
- package/dist/types/transport/index.d.ts +2 -1
- package/dist/types/transport/keepalive/WaKeepAlive.d.ts +0 -1
- package/dist/types/transport/node/WaNodeOrchestrator.d.ts +3 -4
- package/dist/types/transport/node/WaNodeTransport.d.ts +0 -9
- package/dist/types/transport/node/builders/business.d.ts +29 -0
- package/dist/types/transport/node/builders/global.d.ts +102 -0
- package/dist/types/transport/node/builders/group.d.ts +4 -6
- package/dist/types/transport/node/builders/index.d.ts +7 -3
- package/dist/types/transport/node/builders/message.d.ts +20 -30
- package/dist/types/transport/node/builders/pairing.d.ts +0 -2
- package/dist/types/transport/node/builders/privacy-token.d.ts +9 -0
- package/dist/types/transport/node/builders/privacy.d.ts +7 -0
- package/dist/types/transport/node/builders/profile.d.ts +8 -0
- package/dist/types/transport/node/builders/retry.d.ts +2 -5
- package/dist/types/transport/node/builders/usync.d.ts +21 -0
- package/dist/types/transport/node/helpers.d.ts +13 -0
- package/dist/types/transport/node/usync.d.ts +2 -0
- package/dist/types/transport/noise/WaFrameCodec.d.ts +3 -0
- package/dist/types/transport/noise/WaNoiseSession.d.ts +4 -2
- package/dist/types/transport/noise/WaNoiseSocket.d.ts +4 -2
- package/dist/types/transport/proxy.d.ts +6 -0
- package/dist/types/transport/stream/parse.d.ts +0 -1
- package/dist/types/transport/types.d.ts +18 -1
- package/dist/types/util/bytes.d.ts +5 -0
- package/dist/types/util/index.d.ts +5 -0
- package/dist/types/util/primitives.d.ts +2 -0
- package/dist/util/bytes.js +72 -46
- package/dist/util/coercion.js +6 -14
- package/dist/util/index.js +23 -0
- package/dist/util/primitives.js +42 -14
- package/package.json +52 -9
- package/proto/index.js +1 -1
- package/dist/crypto/core/constants.js +0 -4
- package/dist/crypto/core/encoding.js +0 -29
- package/dist/esm/crypto/core/constants.js +0 -1
- package/dist/esm/crypto/core/encoding.js +0 -25
- package/dist/esm/retry/outbound.js +0 -83
- package/dist/esm/store/providers/sqlite/BaseSqliteStore.js +0 -37
- package/dist/esm/store/providers/sqlite/appstate.store.js +0 -169
- package/dist/esm/store/providers/sqlite/auth.store.js +0 -176
- package/dist/esm/store/providers/sqlite/connection.js +0 -240
- package/dist/esm/store/providers/sqlite/contact.store.js +0 -61
- package/dist/esm/store/providers/sqlite/device-list.store.js +0 -155
- package/dist/esm/store/providers/sqlite/message.store.js +0 -119
- package/dist/esm/store/providers/sqlite/migrations.js +0 -347
- package/dist/esm/store/providers/sqlite/participants.store.js +0 -85
- package/dist/esm/store/providers/sqlite/retry.store.js +0 -144
- package/dist/esm/store/providers/sqlite/sender-key.store.js +0 -203
- package/dist/esm/store/providers/sqlite/signal.store.js +0 -353
- package/dist/esm/store/providers/sqlite/thread.store.js +0 -72
- package/dist/esm/util/base64.js +0 -18
- package/dist/esm/util/signal-address.js +0 -5
- package/dist/retry/outbound.js +0 -88
- package/dist/store/providers/sqlite/BaseSqliteStore.js +0 -41
- package/dist/store/providers/sqlite/appstate.store.js +0 -173
- package/dist/store/providers/sqlite/auth.store.js +0 -180
- package/dist/store/providers/sqlite/connection.js +0 -276
- package/dist/store/providers/sqlite/contact.store.js +0 -65
- package/dist/store/providers/sqlite/device-list.store.js +0 -159
- package/dist/store/providers/sqlite/message.store.js +0 -123
- package/dist/store/providers/sqlite/migrations.js +0 -350
- package/dist/store/providers/sqlite/participants.store.js +0 -89
- package/dist/store/providers/sqlite/retry.store.js +0 -148
- package/dist/store/providers/sqlite/sender-key.store.js +0 -207
- package/dist/store/providers/sqlite/signal.store.js +0 -357
- package/dist/store/providers/sqlite/thread.store.js +0 -76
- package/dist/types/appstate/store/sqlite.d.ts +0 -21
- package/dist/types/crypto/core/constants.d.ts +0 -1
- package/dist/types/crypto/core/encoding.d.ts +0 -11
- package/dist/types/retry/outbound.d.ts +0 -4
- package/dist/types/store/providers/sqlite/BaseSqliteStore.d.ts +0 -12
- package/dist/types/store/providers/sqlite/appstate.store.d.ts +0 -15
- package/dist/types/store/providers/sqlite/auth.store.d.ts +0 -10
- package/dist/types/store/providers/sqlite/connection.d.ts +0 -10
- package/dist/types/store/providers/sqlite/contact.store.d.ts +0 -10
- package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -18
- package/dist/types/store/providers/sqlite/message.store.d.ts +0 -11
- package/dist/types/store/providers/sqlite/migrations.d.ts +0 -3
- package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -13
- package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -16
- package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -25
- package/dist/types/store/providers/sqlite/signal.store.d.ts +0 -46
- package/dist/types/store/providers/sqlite/thread.store.d.ts +0 -11
- package/dist/types/util/base64.d.ts +0 -4
- package/dist/types/util/signal-address.d.ts +0 -2
- package/dist/util/base64.js +0 -24
- package/dist/util/signal-address.js +0 -8
- /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
|
@@ -1,36 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { randomBytesAsync, sha256 } from '../../crypto/index.js';
|
|
2
|
+
import { PromiseDedup } from '../../infra/perf/PromiseDedup.js';
|
|
3
|
+
import { ensureMessageSecret } from '../../message/index.js';
|
|
4
|
+
import { resolveEditAttr, resolveEncMediaType, resolveMessageTypeAttr, resolveMetaAttrs } from '../../message/content.js';
|
|
3
5
|
import { wrapDeviceSentMessage } from '../../message/device-sent.js';
|
|
6
|
+
import { injectDeviceListMetadata, resolveIcdcMeta } from '../../message/icdc.js';
|
|
4
7
|
import { writeRandomPadMax16 } from '../../message/padding.js';
|
|
5
8
|
import { computePhashV2 } from '../../message/phash.js';
|
|
9
|
+
import { buildReportingTokenArtifacts } from '../../message/reporting-token.js';
|
|
6
10
|
import { proto } from '../../proto.js';
|
|
7
11
|
import { WA_DEFAULTS } from '../../protocol/constants.js';
|
|
8
|
-
import { isGroupJid, normalizeDeviceJid, normalizeRecipientJid, parseSignalAddressFromJid, splitJid, toUserJid } from '../../protocol/jid.js';
|
|
9
|
-
import {
|
|
10
|
-
import { encodeRetryReplayPayload } from '../../retry/outbound.js';
|
|
12
|
+
import { isGroupJid, normalizeDeviceJid, normalizeRecipientJid, parseJidFull, parseSignalAddressFromJid, splitJid, toUserJid } from '../../protocol/jid.js';
|
|
13
|
+
import { signalAddressKey } from '../../protocol/jid.js';
|
|
11
14
|
import { encodeBinaryNode } from '../../transport/binary/index.js';
|
|
12
|
-
import { buildDirectMessageFanoutNode,
|
|
13
|
-
import { bytesToHex,
|
|
15
|
+
import { buildDirectMessageFanoutNode, buildGroupSenderKeyMessageNode, buildMetaNode } from '../../transport/node/builders/message.js';
|
|
16
|
+
import { bytesToHex, concatBytes, TEXT_ENCODER } from '../../util/bytes.js';
|
|
14
17
|
import { toError } from '../../util/primitives.js';
|
|
15
|
-
import { signalAddressKey } from '../../util/signal-address.js';
|
|
16
18
|
export class WaMessageDispatchCoordinator {
|
|
17
19
|
constructor(options) {
|
|
20
|
+
this.icdcDedup = new PromiseDedup();
|
|
21
|
+
this.privacyTokenDedup = new PromiseDedup();
|
|
22
|
+
this.distributionDedup = new PromiseDedup();
|
|
18
23
|
this.logger = options.logger;
|
|
19
24
|
this.messageClient = options.messageClient;
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
25
|
+
this.retryTracker = options.retryTracker;
|
|
26
|
+
this.sessionResolver = options.sessionResolver;
|
|
27
|
+
this.fanoutResolver = options.fanoutResolver;
|
|
28
|
+
this.participantsCache = options.participantsCache;
|
|
29
|
+
this.appStateSyncKeyProtocol = options.appStateSyncKeyProtocol;
|
|
23
30
|
this.buildMessageContent = options.buildMessageContent;
|
|
24
|
-
this.queryGroupParticipantJids = options.queryGroupParticipantJids;
|
|
25
31
|
this.senderKeyManager = options.senderKeyManager;
|
|
26
32
|
this.signalProtocol = options.signalProtocol;
|
|
27
33
|
this.signalStore = options.signalStore;
|
|
28
|
-
this.
|
|
29
|
-
this.signalIdentitySync = options.signalIdentitySync;
|
|
30
|
-
this.signalSessionSync = options.signalSessionSync;
|
|
34
|
+
this.deviceListStore = options.deviceListStore;
|
|
31
35
|
this.getCurrentMeJid = options.getCurrentMeJid;
|
|
32
36
|
this.getCurrentMeLid = options.getCurrentMeLid;
|
|
33
37
|
this.getCurrentSignedIdentity = options.getCurrentSignedIdentity;
|
|
38
|
+
this.resolvePrivacyTokenNode = options.resolvePrivacyTokenNode;
|
|
39
|
+
this.onDirectMessageSent = options.onDirectMessageSent;
|
|
34
40
|
}
|
|
35
41
|
async publishMessageNode(node, options = {}) {
|
|
36
42
|
this.logger.debug('wa client publish message node', {
|
|
@@ -43,13 +49,13 @@ export class WaMessageDispatchCoordinator {
|
|
|
43
49
|
mode: 'opaque_node',
|
|
44
50
|
node: encodeBinaryNode(node)
|
|
45
51
|
};
|
|
46
|
-
return this.
|
|
52
|
+
return this.retryTracker.track({
|
|
47
53
|
messageIdHint: node.attrs.id,
|
|
48
54
|
toJid: node.attrs.to,
|
|
55
|
+
type: messageType,
|
|
56
|
+
replayPayload,
|
|
49
57
|
participantJid: node.attrs.participant,
|
|
50
|
-
recipientJid: node.attrs.recipient
|
|
51
|
-
messageType,
|
|
52
|
-
replayPayload
|
|
58
|
+
recipientJid: node.attrs.recipient
|
|
53
59
|
}, async () => this.messageClient.publishNode(node, options));
|
|
54
60
|
}
|
|
55
61
|
async publishEncryptedMessage(input, options = {}) {
|
|
@@ -66,12 +72,13 @@ export class WaMessageDispatchCoordinator {
|
|
|
66
72
|
ciphertext: input.ciphertext,
|
|
67
73
|
participant: input.participant
|
|
68
74
|
};
|
|
69
|
-
return this.
|
|
75
|
+
return this.retryTracker.track({
|
|
70
76
|
messageIdHint: input.id,
|
|
71
77
|
toJid: input.to,
|
|
78
|
+
type: input.type ?? 'text',
|
|
79
|
+
replayPayload,
|
|
72
80
|
participantJid: input.participant,
|
|
73
|
-
|
|
74
|
-
replayPayload
|
|
81
|
+
eligibleRequesterDeviceJids: [input.to]
|
|
75
82
|
}, async () => this.messageClient.publishEncrypted(input, options));
|
|
76
83
|
}
|
|
77
84
|
async publishSignalMessage(input, options = {}) {
|
|
@@ -86,7 +93,7 @@ export class WaMessageDispatchCoordinator {
|
|
|
86
93
|
});
|
|
87
94
|
const [paddedPlaintext] = await Promise.all([
|
|
88
95
|
writeRandomPadMax16(input.plaintext),
|
|
89
|
-
this.
|
|
96
|
+
this.sessionResolver.ensureSession(address, input.to, input.expectedIdentity)
|
|
90
97
|
]);
|
|
91
98
|
const encrypted = await this.signalProtocol.encryptMessage(address, paddedPlaintext, input.expectedIdentity);
|
|
92
99
|
const messageType = input.type ?? 'text';
|
|
@@ -96,12 +103,13 @@ export class WaMessageDispatchCoordinator {
|
|
|
96
103
|
type: messageType,
|
|
97
104
|
plaintext: paddedPlaintext
|
|
98
105
|
};
|
|
99
|
-
return this.
|
|
106
|
+
return this.retryTracker.track({
|
|
100
107
|
messageIdHint: input.id,
|
|
101
108
|
toJid: input.to,
|
|
109
|
+
type: messageType,
|
|
110
|
+
replayPayload,
|
|
102
111
|
participantJid: input.participant,
|
|
103
|
-
|
|
104
|
-
replayPayload
|
|
112
|
+
eligibleRequesterDeviceJids: [input.to]
|
|
105
113
|
}, async () => this.messageClient.publishEncrypted({
|
|
106
114
|
to: input.to,
|
|
107
115
|
encType: encrypted.type,
|
|
@@ -116,286 +124,56 @@ export class WaMessageDispatchCoordinator {
|
|
|
116
124
|
}
|
|
117
125
|
async sendMessage(to, content, options = {}) {
|
|
118
126
|
const recipientJid = normalizeRecipientJid(to);
|
|
119
|
-
const message = await
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
const [message, sendOptions] = await Promise.all([
|
|
128
|
+
this.buildMessageContent(content),
|
|
129
|
+
this.withResolvedMessageId(options)
|
|
130
|
+
]);
|
|
131
|
+
const messageWithSecret = await ensureMessageSecret(message);
|
|
132
|
+
const meJid = this.getCurrentMeJid();
|
|
133
|
+
const regInfo = meJid ? await this.signalStore.getRegistrationInfo() : null;
|
|
134
|
+
const localPubKey = regInfo?.identityKeyPair.pubKey;
|
|
135
|
+
const meParsed = meJid ? parseJidFull(meJid) : undefined;
|
|
136
|
+
const meUserJid = meParsed?.userJid;
|
|
137
|
+
const localIdentity = meParsed && localPubKey ? { address: meParsed.address, pubKey: localPubKey } : undefined;
|
|
138
|
+
const isGroup = isGroupJid(recipientJid);
|
|
139
|
+
const [senderIcdc, recipientIcdc] = await Promise.all([
|
|
140
|
+
meUserJid ? this.resolveUserIcdc(meUserJid, localIdentity) : null,
|
|
141
|
+
!isGroup ? this.resolveUserIcdc(toUserJid(recipientJid)) : null
|
|
142
|
+
]);
|
|
143
|
+
const messageWithIcdc = injectDeviceListMetadata(messageWithSecret, senderIcdc, recipientIcdc);
|
|
144
|
+
const plaintext = await writeRandomPadMax16(proto.Message.encode(messageWithIcdc).finish());
|
|
145
|
+
const type = resolveMessageTypeAttr(messageWithIcdc);
|
|
146
|
+
const edit = resolveEditAttr(messageWithIcdc, sendOptions.subtype) ?? undefined;
|
|
147
|
+
const mediatype = resolveEncMediaType(messageWithIcdc) ?? undefined;
|
|
148
|
+
const metaAttrs = resolveMetaAttrs(messageWithIcdc);
|
|
149
|
+
const metaNode = metaAttrs ? buildMetaNode(metaAttrs) : undefined;
|
|
150
|
+
if (isGroup) {
|
|
151
|
+
if (this.shouldUseGroupDirectPath(messageWithIcdc)) {
|
|
152
|
+
return this.publishGroupDirectMessage(recipientJid, messageWithIcdc, plaintext, type, sendOptions, {}, edit, mediatype, metaNode);
|
|
153
|
+
}
|
|
154
|
+
return this.publishGroupSenderKeyMessage(recipientJid, messageWithIcdc, plaintext, type, sendOptions, {}, edit, mediatype, metaNode);
|
|
127
155
|
}
|
|
128
156
|
const directRecipientJid = toUserJid(recipientJid);
|
|
129
|
-
return this.publishDirectSignalMessageWithFanout(directRecipientJid,
|
|
157
|
+
return this.publishDirectSignalMessageWithFanout(directRecipientJid, messageWithIcdc, plaintext, type, sendOptions, edit, mediatype, metaNode);
|
|
130
158
|
}
|
|
131
159
|
async syncSignalSession(jid, reasonIdentity = false) {
|
|
132
160
|
const address = parseSignalAddressFromJid(jid);
|
|
133
161
|
if (address.server === WA_DEFAULTS.GROUP_SERVER) {
|
|
134
162
|
throw new Error('syncSignalSession supports only direct chats');
|
|
135
163
|
}
|
|
136
|
-
await this.
|
|
164
|
+
await this.sessionResolver.ensureSession(address, jid, undefined, reasonIdentity);
|
|
137
165
|
}
|
|
138
166
|
async sendReceipt(input) {
|
|
139
167
|
await this.messageClient.sendReceipt(input);
|
|
140
168
|
}
|
|
141
169
|
async requestAppStateSyncKeys(keyIds) {
|
|
142
|
-
|
|
143
|
-
if (normalizedKeyIds.length === 0) {
|
|
144
|
-
return [];
|
|
145
|
-
}
|
|
146
|
-
const peerDeviceJids = await this.resolveOwnPeerDeviceJids();
|
|
147
|
-
if (peerDeviceJids.length === 0) {
|
|
148
|
-
this.logger.warn('app-state sync key request skipped: no peer devices available', {
|
|
149
|
-
keys: normalizedKeyIds.length
|
|
150
|
-
});
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
const protocolMessage = {
|
|
154
|
-
type: proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_REQUEST,
|
|
155
|
-
appStateSyncKeyRequest: {
|
|
156
|
-
keyIds: normalizedKeyIds.map((keyId) => ({
|
|
157
|
-
keyId
|
|
158
|
-
}))
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
await Promise.all(peerDeviceJids.map((deviceJid) => this.publishProtocolMessageToDevice(deviceJid, protocolMessage)));
|
|
162
|
-
this.logger.info('app-state sync key request sent to peer devices', {
|
|
163
|
-
devices: peerDeviceJids.length,
|
|
164
|
-
keys: normalizedKeyIds.length,
|
|
165
|
-
keyIds: normalizedKeyIds.map((keyId) => bytesToHex(keyId)).join(',')
|
|
166
|
-
});
|
|
167
|
-
return peerDeviceJids;
|
|
170
|
+
return this.appStateSyncKeyProtocol.requestKeys(keyIds);
|
|
168
171
|
}
|
|
169
172
|
async sendAppStateSyncKeyShare(toDeviceJid, keys, missingKeyIds = []) {
|
|
170
|
-
|
|
171
|
-
const dedupedKeysById = new Map();
|
|
172
|
-
for (const key of keys) {
|
|
173
|
-
dedupedKeysById.set(bytesToHex(key.keyId), key);
|
|
174
|
-
}
|
|
175
|
-
const dedupedKeys = [...dedupedKeysById.values()];
|
|
176
|
-
const dedupedMissingKeyIds = this.normalizeKeyIds(missingKeyIds).filter((keyId) => !dedupedKeysById.has(bytesToHex(keyId)));
|
|
177
|
-
const keyShareEntries = [
|
|
178
|
-
...dedupedKeys.map((key) => ({
|
|
179
|
-
keyId: { keyId: key.keyId },
|
|
180
|
-
keyData: {
|
|
181
|
-
keyData: key.keyData,
|
|
182
|
-
timestamp: key.timestamp,
|
|
183
|
-
...(key.fingerprint ? { fingerprint: key.fingerprint } : {})
|
|
184
|
-
}
|
|
185
|
-
})),
|
|
186
|
-
...dedupedMissingKeyIds.map((keyId) => ({
|
|
187
|
-
keyId: { keyId }
|
|
188
|
-
}))
|
|
189
|
-
];
|
|
190
|
-
const protocolMessage = {
|
|
191
|
-
type: proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE,
|
|
192
|
-
appStateSyncKeyShare: {
|
|
193
|
-
keys: keyShareEntries
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
await this.publishProtocolMessageToDevice(normalizedTo, protocolMessage);
|
|
197
|
-
this.logger.info('app-state sync key share sent', {
|
|
198
|
-
to: normalizedTo,
|
|
199
|
-
keys: dedupedKeys.length,
|
|
200
|
-
orphanKeys: dedupedMissingKeyIds.length
|
|
201
|
-
});
|
|
173
|
+
await this.appStateSyncKeyProtocol.sendKeyShare(toDeviceJid, keys, missingKeyIds);
|
|
202
174
|
}
|
|
203
175
|
async mutateParticipantsCacheFromGroupEvent(event) {
|
|
204
|
-
|
|
205
|
-
if (!groupJid) {
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
if (event.action === 'delete') {
|
|
209
|
-
await this.participantsStore.deleteGroupParticipants(groupJid);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
const participantUsers = this.extractParticipantUsersFromGroupEvent(event);
|
|
213
|
-
if (event.action === 'create') {
|
|
214
|
-
if (participantUsers.length === 0) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
await this.participantsStore.upsertGroupParticipants({
|
|
218
|
-
groupJid,
|
|
219
|
-
participants: participantUsers,
|
|
220
|
-
updatedAtMs: Date.now()
|
|
221
|
-
});
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const cached = await this.participantsStore.getGroupParticipants(groupJid);
|
|
225
|
-
if (!cached || cached.participants.length === 0) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const cachedParticipants = this.sanitizeParticipantUsers(cached.participants);
|
|
229
|
-
if (cachedParticipants.length === 0) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
if (event.action === 'add' ||
|
|
233
|
-
event.action === 'promote' ||
|
|
234
|
-
event.action === 'demote' ||
|
|
235
|
-
event.action === 'linked_group_promote' ||
|
|
236
|
-
event.action === 'linked_group_demote') {
|
|
237
|
-
await this.mergeParticipantUsersIntoCache(groupJid, cachedParticipants, participantUsers);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
if (event.action === 'remove') {
|
|
241
|
-
await this.removeParticipantUsersFromCache(groupJid, cachedParticipants, participantUsers);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (event.action === 'modify') {
|
|
245
|
-
const authorUsers = event.authorJid
|
|
246
|
-
? this.sanitizeParticipantUsers([event.authorJid])
|
|
247
|
-
: [];
|
|
248
|
-
await this.replaceParticipantUsersInCache(groupJid, cachedParticipants, authorUsers, participantUsers);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
async publishProtocolMessageToDevice(deviceJid, protocolMessage) {
|
|
252
|
-
const plaintext = await writeRandomPadMax16(proto.Message.encode({
|
|
253
|
-
protocolMessage
|
|
254
|
-
}).finish());
|
|
255
|
-
await this.publishSignalMessage({
|
|
256
|
-
to: deviceJid,
|
|
257
|
-
plaintext,
|
|
258
|
-
type: 'protocol',
|
|
259
|
-
category: 'peer',
|
|
260
|
-
pushPriority: 'high'
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
async resolveOwnPeerDeviceJids() {
|
|
264
|
-
const meJid = this.requireCurrentMeJid('resolveOwnPeerDeviceJids');
|
|
265
|
-
const meUserJid = toUserJid(meJid);
|
|
266
|
-
const meDevices = new Set();
|
|
267
|
-
meDevices.add(normalizeDeviceJid(meJid));
|
|
268
|
-
const meLid = this.getCurrentMeLid();
|
|
269
|
-
if (meLid && meLid.includes('@')) {
|
|
270
|
-
try {
|
|
271
|
-
meDevices.add(normalizeDeviceJid(meLid));
|
|
272
|
-
}
|
|
273
|
-
catch (error) {
|
|
274
|
-
this.logger.trace('ignoring malformed me lid jid while resolving peer devices', {
|
|
275
|
-
meLid,
|
|
276
|
-
message: toError(error).message
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
try {
|
|
281
|
-
const synced = await this.signalDeviceSync.syncDeviceList([meUserJid]);
|
|
282
|
-
const peerDevices = new Set();
|
|
283
|
-
for (const entry of synced) {
|
|
284
|
-
const sourceDevices = entry.deviceJids.length > 0 ? entry.deviceJids : [entry.jid];
|
|
285
|
-
for (const deviceJid of sourceDevices) {
|
|
286
|
-
try {
|
|
287
|
-
const normalized = normalizeDeviceJid(deviceJid);
|
|
288
|
-
if (meDevices.has(normalized)) {
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
peerDevices.add(normalized);
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
this.logger.trace('ignoring malformed peer device jid while resolving app-state peers', {
|
|
295
|
-
deviceJid,
|
|
296
|
-
message: toError(error).message
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return [...peerDevices];
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
this.logger.warn('failed to resolve peer devices for app-state key request', {
|
|
305
|
-
message: toError(error).message
|
|
306
|
-
});
|
|
307
|
-
return [];
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
normalizeKeyIds(keyIds) {
|
|
311
|
-
const deduped = new Map();
|
|
312
|
-
for (const keyId of keyIds) {
|
|
313
|
-
if (keyId.byteLength === 0) {
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
const keyHex = bytesToHex(keyId);
|
|
317
|
-
if (deduped.has(keyHex)) {
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
deduped.set(keyHex, keyId);
|
|
321
|
-
}
|
|
322
|
-
return [...deduped.values()];
|
|
323
|
-
}
|
|
324
|
-
async publishWithRetryTracking(args, publish) {
|
|
325
|
-
const nowMs = Date.now();
|
|
326
|
-
const expiresAtMs = nowMs + this.retryTtlMs;
|
|
327
|
-
const hintedMessageId = args.messageIdHint?.trim();
|
|
328
|
-
const resolvedToJid = args.toJid ?? (args.replayPayload.mode === 'opaque_node' ? '' : args.replayPayload.to);
|
|
329
|
-
let hintedPersisted = false;
|
|
330
|
-
if (hintedMessageId) {
|
|
331
|
-
hintedPersisted = await this.safeUpsertRetryOutboundRecord(this.createRetryOutboundRecord({
|
|
332
|
-
messageId: hintedMessageId,
|
|
333
|
-
toJid: resolvedToJid,
|
|
334
|
-
participantJid: args.participantJid,
|
|
335
|
-
recipientJid: args.recipientJid,
|
|
336
|
-
messageType: args.messageType,
|
|
337
|
-
replayPayload: args.replayPayload,
|
|
338
|
-
createdAtMs: nowMs,
|
|
339
|
-
updatedAtMs: nowMs,
|
|
340
|
-
expiresAtMs
|
|
341
|
-
}));
|
|
342
|
-
}
|
|
343
|
-
const result = await publish();
|
|
344
|
-
if (hintedPersisted && hintedMessageId && result.id === hintedMessageId) {
|
|
345
|
-
// Hint and final message id matched; avoid a second equivalent upsert on the hot path.
|
|
346
|
-
return result;
|
|
347
|
-
}
|
|
348
|
-
const persistedNowMs = Date.now();
|
|
349
|
-
await this.safeUpsertRetryOutboundRecord(this.createRetryOutboundRecord({
|
|
350
|
-
messageId: result.id,
|
|
351
|
-
toJid: resolvedToJid,
|
|
352
|
-
participantJid: args.participantJid,
|
|
353
|
-
recipientJid: args.recipientJid,
|
|
354
|
-
messageType: args.messageType,
|
|
355
|
-
replayPayload: args.replayPayload,
|
|
356
|
-
createdAtMs: hintedMessageId ? nowMs : persistedNowMs,
|
|
357
|
-
updatedAtMs: persistedNowMs,
|
|
358
|
-
expiresAtMs: persistedNowMs + this.retryTtlMs
|
|
359
|
-
}));
|
|
360
|
-
return result;
|
|
361
|
-
}
|
|
362
|
-
createRetryOutboundRecord(input) {
|
|
363
|
-
return {
|
|
364
|
-
messageId: input.messageId,
|
|
365
|
-
toJid: input.toJid,
|
|
366
|
-
participantJid: input.participantJid,
|
|
367
|
-
recipientJid: input.recipientJid,
|
|
368
|
-
messageType: input.messageType,
|
|
369
|
-
replayMode: input.replayPayload.mode,
|
|
370
|
-
replayPayload: encodeRetryReplayPayload(input.replayPayload),
|
|
371
|
-
state: 'pending',
|
|
372
|
-
createdAtMs: input.createdAtMs,
|
|
373
|
-
updatedAtMs: input.updatedAtMs,
|
|
374
|
-
expiresAtMs: input.expiresAtMs
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
async safeUpsertRetryOutboundRecord(record) {
|
|
378
|
-
try {
|
|
379
|
-
await this.retryStore.upsertOutboundMessage(record);
|
|
380
|
-
}
|
|
381
|
-
catch (error) {
|
|
382
|
-
this.logger.warn('failed to persist retry outbound message record', {
|
|
383
|
-
messageId: record.messageId,
|
|
384
|
-
to: record.toJid,
|
|
385
|
-
mode: record.replayMode,
|
|
386
|
-
message: toError(error).message
|
|
387
|
-
});
|
|
388
|
-
return false;
|
|
389
|
-
}
|
|
390
|
-
try {
|
|
391
|
-
await this.retryStore.cleanupExpired(Date.now());
|
|
392
|
-
}
|
|
393
|
-
catch (error) {
|
|
394
|
-
this.logger.warn('failed to cleanup retry records after outbound persist', {
|
|
395
|
-
message: toError(error).message
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
return true;
|
|
176
|
+
await this.participantsCache.mutateFromGroupEvent(event);
|
|
399
177
|
}
|
|
400
178
|
shouldUseGroupDirectPath(message) {
|
|
401
179
|
const protocolType = message.protocolMessage?.type;
|
|
@@ -405,41 +183,81 @@ export class WaMessageDispatchCoordinator {
|
|
|
405
183
|
}
|
|
406
184
|
return message.keepInChatMessage?.keepType === proto.KeepType.UNDO_KEEP_FOR_ALL;
|
|
407
185
|
}
|
|
408
|
-
async publishGroupDirectMessage(groupJid, plaintext, type, options, retryContext = {}) {
|
|
186
|
+
async publishGroupDirectMessage(groupJid, message, plaintext, type, options, retryContext = {}, edit, mediatype, metaNode) {
|
|
187
|
+
const sendOptions = await this.withResolvedMessageId(options);
|
|
409
188
|
const meJid = this.requireCurrentMeJid('sendMessage');
|
|
410
189
|
const participantUserJids = retryContext.forceRefreshParticipants
|
|
411
|
-
? await this.
|
|
412
|
-
: await this.
|
|
190
|
+
? await this.participantsCache.refreshParticipantUsers(groupJid)
|
|
191
|
+
: await this.participantsCache.resolveParticipantUsers(groupJid);
|
|
413
192
|
const addressingMode = retryContext.forceAddressingMode ??
|
|
414
193
|
this.resolveGroupAddressingMode(participantUserJids, groupJid);
|
|
415
194
|
const senderForPhash = this.resolveSenderForAddressingMode(addressingMode, meJid);
|
|
416
|
-
const fanoutDeviceJids = await this.resolveGroupParticipantDeviceJids(participantUserJids);
|
|
195
|
+
const fanoutDeviceJids = await this.fanoutResolver.resolveGroupParticipantDeviceJids(participantUserJids);
|
|
417
196
|
if (fanoutDeviceJids.length === 0) {
|
|
418
197
|
throw new Error('group direct send resolved no target devices');
|
|
419
198
|
}
|
|
420
|
-
await this.
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
199
|
+
const resolvedFanoutTargets = await this.sessionResolver.ensureSessionsBatch(fanoutDeviceJids);
|
|
200
|
+
const uniqueNormalizedFanoutJids = new Set();
|
|
201
|
+
for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
|
|
202
|
+
uniqueNormalizedFanoutJids.add(normalizeDeviceJid(fanoutDeviceJids[index]));
|
|
203
|
+
}
|
|
204
|
+
if (resolvedFanoutTargets.length !== uniqueNormalizedFanoutJids.size) {
|
|
205
|
+
throw new Error('group direct send resolved incomplete signal sessions');
|
|
206
|
+
}
|
|
207
|
+
const participantEncryptRequests = new Array(resolvedFanoutTargets.length);
|
|
208
|
+
for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
|
|
209
|
+
const target = resolvedFanoutTargets[index];
|
|
210
|
+
participantEncryptRequests[index] = {
|
|
211
|
+
address: target.address,
|
|
212
|
+
plaintext
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const encryptedParticipants = await this.signalProtocol.encryptMessagesBatch(participantEncryptRequests, resolvedFanoutTargets);
|
|
216
|
+
const participants = new Array(resolvedFanoutTargets.length);
|
|
217
|
+
for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
|
|
218
|
+
const target = resolvedFanoutTargets[index];
|
|
219
|
+
participants[index] = {
|
|
220
|
+
jid: target.jid,
|
|
221
|
+
encType: encryptedParticipants[index].type,
|
|
222
|
+
ciphertext: encryptedParticipants[index].ciphertext
|
|
429
223
|
};
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
224
|
+
}
|
|
225
|
+
let shouldAttachDeviceIdentity = false;
|
|
226
|
+
for (let index = 0; index < participants.length; index += 1) {
|
|
227
|
+
if (participants[index].encType === 'pkmsg') {
|
|
228
|
+
shouldAttachDeviceIdentity = true;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const phashTargets = new Array(resolvedFanoutTargets.length + 1);
|
|
233
|
+
for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
|
|
234
|
+
phashTargets[index] = resolvedFanoutTargets[index].jid;
|
|
235
|
+
}
|
|
236
|
+
phashTargets[resolvedFanoutTargets.length] = senderForPhash;
|
|
237
|
+
const [localPhash, reportingArtifacts] = await Promise.all([
|
|
238
|
+
computePhashV2(phashTargets),
|
|
239
|
+
this.tryBuildReportingTokenArtifacts({
|
|
240
|
+
message,
|
|
241
|
+
stanzaId: sendOptions.id,
|
|
242
|
+
senderUserJid: toUserJid(senderForPhash),
|
|
243
|
+
remoteJid: groupJid,
|
|
244
|
+
context: 'group_direct'
|
|
245
|
+
})
|
|
246
|
+
]);
|
|
247
|
+
const messageNode = buildDirectMessageFanoutNode({
|
|
434
248
|
to: groupJid,
|
|
435
249
|
type,
|
|
436
|
-
id:
|
|
250
|
+
id: sendOptions.id,
|
|
251
|
+
edit,
|
|
437
252
|
phash: localPhash,
|
|
438
253
|
addressingMode,
|
|
439
254
|
participants,
|
|
440
255
|
deviceIdentity: shouldAttachDeviceIdentity
|
|
441
256
|
? this.getEncodedSignedDeviceIdentity()
|
|
442
|
-
: undefined
|
|
257
|
+
: undefined,
|
|
258
|
+
reportingNode: reportingArtifacts?.node ?? undefined,
|
|
259
|
+
metaNode,
|
|
260
|
+
mediatype
|
|
443
261
|
});
|
|
444
262
|
const replayPayload = {
|
|
445
263
|
mode: 'plaintext',
|
|
@@ -447,12 +265,13 @@ export class WaMessageDispatchCoordinator {
|
|
|
447
265
|
type,
|
|
448
266
|
plaintext
|
|
449
267
|
};
|
|
450
|
-
const result = await this.
|
|
451
|
-
messageIdHint:
|
|
268
|
+
const result = await this.retryTracker.track({
|
|
269
|
+
messageIdHint: sendOptions.id ?? messageNode.attrs.id,
|
|
452
270
|
toJid: groupJid,
|
|
453
|
-
|
|
454
|
-
replayPayload
|
|
455
|
-
|
|
271
|
+
type,
|
|
272
|
+
replayPayload,
|
|
273
|
+
eligibleRequesterDeviceJids: undefined
|
|
274
|
+
}, async () => this.messageClient.publishNode(messageNode, sendOptions));
|
|
456
275
|
const ackError = result.ack.error;
|
|
457
276
|
const serverPhash = result.ack.phash;
|
|
458
277
|
const serverAddressingMode = result.ack.addressingMode;
|
|
@@ -470,43 +289,67 @@ export class WaMessageDispatchCoordinator {
|
|
|
470
289
|
serverAddressingMode,
|
|
471
290
|
ackError
|
|
472
291
|
});
|
|
473
|
-
return this.publishGroupDirectMessage(groupJid, plaintext, type, {
|
|
474
|
-
...
|
|
292
|
+
return this.publishGroupDirectMessage(groupJid, message, plaintext, type, {
|
|
293
|
+
...sendOptions,
|
|
475
294
|
id: result.id
|
|
476
295
|
}, {
|
|
477
296
|
retried: true,
|
|
478
297
|
forceRefreshParticipants: true,
|
|
479
298
|
forceAddressingMode: serverAddressingMode
|
|
480
|
-
});
|
|
299
|
+
}, edit, mediatype, metaNode);
|
|
481
300
|
}
|
|
482
301
|
return result;
|
|
483
302
|
}
|
|
484
|
-
async publishGroupSenderKeyMessage(groupJid, plaintext, type, options, retryContext = {}) {
|
|
303
|
+
async publishGroupSenderKeyMessage(groupJid, message, plaintext, type, options, retryContext = {}, edit, mediatype, metaNode) {
|
|
304
|
+
const sendOptions = await this.withResolvedMessageId(options);
|
|
485
305
|
const meJid = this.requireCurrentMeJid('sendMessage');
|
|
486
306
|
const participantUserJids = retryContext.forceRefreshParticipants
|
|
487
|
-
? await this.
|
|
488
|
-
: await this.
|
|
307
|
+
? await this.participantsCache.refreshParticipantUsers(groupJid)
|
|
308
|
+
: await this.participantsCache.resolveParticipantUsers(groupJid);
|
|
489
309
|
const addressingMode = retryContext.forceAddressingMode ??
|
|
490
310
|
this.resolveGroupAddressingMode(participantUserJids, groupJid);
|
|
491
311
|
const senderJid = this.resolveSenderForAddressingMode(addressingMode, meJid);
|
|
492
312
|
const sender = parseSignalAddressFromJid(senderJid);
|
|
493
|
-
const senderKeyDistributionMessage = await this.senderKeyManager.
|
|
494
|
-
const
|
|
495
|
-
const distributionData = await this.encryptGroupDistributionParticipants(groupJid, sender, senderKeyDistributionMessage, participantUserJids);
|
|
313
|
+
const { distributionMessage: senderKeyDistributionMessage, ciphertext: groupCiphertext, keyId: senderKeyId } = await this.senderKeyManager.prepareGroupEncryption(groupJid, sender, plaintext);
|
|
314
|
+
const distributionData = await this.distributionDedup.run(`dist:${groupJid}:${senderKeyId}`, () => this.encryptGroupDistributionParticipants(groupJid, senderKeyId, senderKeyDistributionMessage, participantUserJids));
|
|
496
315
|
const { fanoutDeviceJids, distributionParticipants } = distributionData;
|
|
497
|
-
|
|
498
|
-
|
|
316
|
+
let shouldAttachDeviceIdentity = false;
|
|
317
|
+
for (let index = 0; index < distributionParticipants.length; index += 1) {
|
|
318
|
+
if (distributionParticipants[index].encType === 'pkmsg') {
|
|
319
|
+
shouldAttachDeviceIdentity = true;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const phashTargets = new Array(fanoutDeviceJids.length + 1);
|
|
324
|
+
for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
|
|
325
|
+
phashTargets[index] = fanoutDeviceJids[index];
|
|
326
|
+
}
|
|
327
|
+
phashTargets[fanoutDeviceJids.length] = senderJid;
|
|
328
|
+
const [localPhash, reportingArtifacts] = await Promise.all([
|
|
329
|
+
computePhashV2(phashTargets),
|
|
330
|
+
this.tryBuildReportingTokenArtifacts({
|
|
331
|
+
message,
|
|
332
|
+
stanzaId: sendOptions.id,
|
|
333
|
+
senderUserJid: toUserJid(senderJid),
|
|
334
|
+
remoteJid: groupJid,
|
|
335
|
+
context: 'group_sender_key'
|
|
336
|
+
})
|
|
337
|
+
]);
|
|
499
338
|
const messageNode = buildGroupSenderKeyMessageNode({
|
|
500
339
|
to: groupJid,
|
|
501
340
|
type,
|
|
502
|
-
id:
|
|
341
|
+
id: sendOptions.id,
|
|
342
|
+
edit,
|
|
503
343
|
phash: localPhash,
|
|
504
344
|
addressingMode,
|
|
505
345
|
groupCiphertext: groupCiphertext.ciphertext,
|
|
506
346
|
participants: distributionParticipants,
|
|
507
347
|
deviceIdentity: shouldAttachDeviceIdentity
|
|
508
348
|
? this.getEncodedSignedDeviceIdentity()
|
|
509
|
-
: undefined
|
|
349
|
+
: undefined,
|
|
350
|
+
reportingNode: reportingArtifacts?.node ?? undefined,
|
|
351
|
+
metaNode,
|
|
352
|
+
mediatype
|
|
510
353
|
});
|
|
511
354
|
const replayPayload = {
|
|
512
355
|
mode: 'plaintext',
|
|
@@ -514,15 +357,19 @@ export class WaMessageDispatchCoordinator {
|
|
|
514
357
|
type,
|
|
515
358
|
plaintext
|
|
516
359
|
};
|
|
517
|
-
const result = await this.
|
|
518
|
-
messageIdHint:
|
|
360
|
+
const result = await this.retryTracker.track({
|
|
361
|
+
messageIdHint: sendOptions.id ?? messageNode.attrs.id,
|
|
519
362
|
toJid: groupJid,
|
|
520
|
-
|
|
521
|
-
replayPayload
|
|
522
|
-
|
|
523
|
-
|
|
363
|
+
type,
|
|
364
|
+
replayPayload,
|
|
365
|
+
eligibleRequesterDeviceJids: undefined
|
|
366
|
+
}, async () => this.messageClient.publishNode(messageNode, sendOptions));
|
|
367
|
+
const distributedAddresses = new Array(distributionParticipants.length);
|
|
368
|
+
for (let index = 0; index < distributionParticipants.length; index += 1) {
|
|
369
|
+
distributedAddresses[index] = distributionParticipants[index].address;
|
|
370
|
+
}
|
|
524
371
|
try {
|
|
525
|
-
await this.senderKeyManager.markSenderKeyDistributed(groupJid,
|
|
372
|
+
await this.senderKeyManager.markSenderKeyDistributed(groupJid, senderKeyId, distributedAddresses);
|
|
526
373
|
}
|
|
527
374
|
catch (error) {
|
|
528
375
|
this.logger.warn('failed to mark sender key distribution targets', {
|
|
@@ -548,150 +395,20 @@ export class WaMessageDispatchCoordinator {
|
|
|
548
395
|
serverAddressingMode,
|
|
549
396
|
ackError
|
|
550
397
|
});
|
|
551
|
-
return this.publishGroupSenderKeyMessage(groupJid, plaintext, type, {
|
|
552
|
-
...
|
|
398
|
+
return this.publishGroupSenderKeyMessage(groupJid, message, plaintext, type, {
|
|
399
|
+
...sendOptions,
|
|
553
400
|
id: result.id
|
|
554
401
|
}, {
|
|
555
402
|
retried: true,
|
|
556
403
|
forceRefreshParticipants: true,
|
|
557
404
|
forceAddressingMode: serverAddressingMode
|
|
558
|
-
});
|
|
405
|
+
}, edit, mediatype, metaNode);
|
|
559
406
|
}
|
|
560
407
|
return result;
|
|
561
408
|
}
|
|
562
|
-
async resolveGroupParticipantUsers(groupJid) {
|
|
563
|
-
const cached = await this.participantsStore.getGroupParticipants(groupJid);
|
|
564
|
-
if (cached && cached.participants.length > 0) {
|
|
565
|
-
return this.sanitizeParticipantUsers(cached.participants);
|
|
566
|
-
}
|
|
567
|
-
return this.refreshGroupParticipantUsers(groupJid);
|
|
568
|
-
}
|
|
569
|
-
resolveGroupJidForParticipantCacheEvent(event) {
|
|
570
|
-
if (event.action === 'linked_group_promote' || event.action === 'linked_group_demote') {
|
|
571
|
-
return event.contextGroupJid ?? event.groupJid ?? null;
|
|
572
|
-
}
|
|
573
|
-
return event.groupJid ?? null;
|
|
574
|
-
}
|
|
575
|
-
extractParticipantUsersFromGroupEvent(event) {
|
|
576
|
-
const candidates = [];
|
|
577
|
-
for (const participant of event.participants ?? []) {
|
|
578
|
-
if (participant.jid) {
|
|
579
|
-
candidates.push(participant.jid);
|
|
580
|
-
}
|
|
581
|
-
if (participant.lidJid) {
|
|
582
|
-
candidates.push(participant.lidJid);
|
|
583
|
-
}
|
|
584
|
-
if (participant.phoneJid) {
|
|
585
|
-
candidates.push(participant.phoneJid);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return this.sanitizeParticipantUsers(candidates);
|
|
589
|
-
}
|
|
590
|
-
async mergeParticipantUsersIntoCache(groupJid, cachedParticipants, participantsToAdd) {
|
|
591
|
-
if (participantsToAdd.length === 0) {
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
const nextParticipants = [...cachedParticipants];
|
|
595
|
-
const existing = new Set(cachedParticipants);
|
|
596
|
-
for (const participant of participantsToAdd) {
|
|
597
|
-
if (existing.has(participant)) {
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
existing.add(participant);
|
|
601
|
-
nextParticipants.push(participant);
|
|
602
|
-
}
|
|
603
|
-
if (nextParticipants.length === cachedParticipants.length) {
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
await this.participantsStore.upsertGroupParticipants({
|
|
607
|
-
groupJid,
|
|
608
|
-
participants: nextParticipants,
|
|
609
|
-
updatedAtMs: Date.now()
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
async removeParticipantUsersFromCache(groupJid, cachedParticipants, participantsToRemove) {
|
|
613
|
-
if (participantsToRemove.length === 0) {
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
const removed = new Set(participantsToRemove);
|
|
617
|
-
const nextParticipants = cachedParticipants.filter((participant) => !removed.has(participant));
|
|
618
|
-
if (nextParticipants.length === cachedParticipants.length) {
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
if (nextParticipants.length === 0) {
|
|
622
|
-
await this.participantsStore.deleteGroupParticipants(groupJid);
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
await this.participantsStore.upsertGroupParticipants({
|
|
626
|
-
groupJid,
|
|
627
|
-
participants: nextParticipants,
|
|
628
|
-
updatedAtMs: Date.now()
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
async replaceParticipantUsersInCache(groupJid, cachedParticipants, participantsToReplace, replacementParticipants) {
|
|
632
|
-
const toReplace = new Set(participantsToReplace);
|
|
633
|
-
const nextParticipants = cachedParticipants.filter((participant) => !toReplace.has(participant));
|
|
634
|
-
const existing = new Set(nextParticipants);
|
|
635
|
-
for (const participant of replacementParticipants) {
|
|
636
|
-
if (existing.has(participant)) {
|
|
637
|
-
continue;
|
|
638
|
-
}
|
|
639
|
-
existing.add(participant);
|
|
640
|
-
nextParticipants.push(participant);
|
|
641
|
-
}
|
|
642
|
-
if (this.areParticipantListsEqual(cachedParticipants, nextParticipants)) {
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
if (nextParticipants.length === 0) {
|
|
646
|
-
await this.participantsStore.deleteGroupParticipants(groupJid);
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
await this.participantsStore.upsertGroupParticipants({
|
|
650
|
-
groupJid,
|
|
651
|
-
participants: nextParticipants,
|
|
652
|
-
updatedAtMs: Date.now()
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
areParticipantListsEqual(left, right) {
|
|
656
|
-
if (left.length !== right.length) {
|
|
657
|
-
return false;
|
|
658
|
-
}
|
|
659
|
-
for (let index = 0; index < left.length; index += 1) {
|
|
660
|
-
if (left[index] !== right[index]) {
|
|
661
|
-
return false;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
return true;
|
|
665
|
-
}
|
|
666
|
-
async refreshGroupParticipantUsers(groupJid) {
|
|
667
|
-
const queried = await this.queryGroupParticipantJids(groupJid);
|
|
668
|
-
const participants = this.sanitizeParticipantUsers(queried);
|
|
669
|
-
await this.participantsStore.upsertGroupParticipants({
|
|
670
|
-
groupJid,
|
|
671
|
-
participants,
|
|
672
|
-
updatedAtMs: Date.now()
|
|
673
|
-
});
|
|
674
|
-
return participants;
|
|
675
|
-
}
|
|
676
|
-
sanitizeParticipantUsers(participants) {
|
|
677
|
-
const deduped = new Set();
|
|
678
|
-
for (const participant of participants) {
|
|
679
|
-
if (!participant || !participant.includes('@'))
|
|
680
|
-
continue;
|
|
681
|
-
try {
|
|
682
|
-
deduped.add(toUserJid(participant));
|
|
683
|
-
}
|
|
684
|
-
catch (error) {
|
|
685
|
-
this.logger.trace('ignoring malformed participant jid', {
|
|
686
|
-
participant,
|
|
687
|
-
message: toError(error).message
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
return [...deduped];
|
|
692
|
-
}
|
|
693
409
|
resolveGroupAddressingMode(participantUserJids, groupJid) {
|
|
694
|
-
for (
|
|
410
|
+
for (let index = 0; index < participantUserJids.length; index += 1) {
|
|
411
|
+
const participantJid = participantUserJids[index];
|
|
695
412
|
try {
|
|
696
413
|
if (splitJid(participantJid).server === 'lid') {
|
|
697
414
|
return 'lid';
|
|
@@ -724,115 +441,131 @@ export class WaMessageDispatchCoordinator {
|
|
|
724
441
|
}
|
|
725
442
|
return normalizeDeviceJid(meJid);
|
|
726
443
|
}
|
|
727
|
-
async encryptGroupDistributionParticipants(groupJid,
|
|
444
|
+
async encryptGroupDistributionParticipants(groupJid, senderKeyId, senderKeyDistributionMessage, participantUserJids) {
|
|
728
445
|
const distributionPayload = await writeRandomPadMax16(proto.Message.encode({
|
|
729
446
|
senderKeyDistributionMessage
|
|
730
447
|
}).finish());
|
|
731
|
-
const fanoutDeviceJids = await this.resolveGroupParticipantDeviceJids(participantUserJids);
|
|
448
|
+
const fanoutDeviceJids = await this.fanoutResolver.resolveGroupParticipantDeviceJids(participantUserJids);
|
|
732
449
|
if (fanoutDeviceJids.length === 0) {
|
|
733
450
|
return {
|
|
734
451
|
fanoutDeviceJids,
|
|
735
452
|
distributionParticipants: []
|
|
736
453
|
};
|
|
737
454
|
}
|
|
738
|
-
const
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
455
|
+
const fanoutTargetsByAddressKey = new Map();
|
|
456
|
+
const fanoutAddresses = new Array(fanoutDeviceJids.length);
|
|
457
|
+
for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
|
|
458
|
+
const jid = fanoutDeviceJids[index];
|
|
459
|
+
const address = parseSignalAddressFromJid(jid);
|
|
460
|
+
fanoutAddresses[index] = address;
|
|
461
|
+
fanoutTargetsByAddressKey.set(signalAddressKey(address), { jid, address });
|
|
462
|
+
}
|
|
463
|
+
const pendingAddresses = await this.senderKeyManager.filterParticipantsNeedingDistribution(groupJid, senderKeyId, fanoutAddresses);
|
|
743
464
|
if (pendingAddresses.length === 0) {
|
|
744
465
|
return {
|
|
745
466
|
fanoutDeviceJids,
|
|
746
467
|
distributionParticipants: []
|
|
747
468
|
};
|
|
748
469
|
}
|
|
749
|
-
const pendingAddressKeys = new Set(
|
|
750
|
-
const pendingTargets =
|
|
470
|
+
const pendingAddressKeys = new Set();
|
|
471
|
+
const pendingTargets = [];
|
|
472
|
+
for (let index = 0; index < pendingAddresses.length; index += 1) {
|
|
473
|
+
const key = signalAddressKey(pendingAddresses[index]);
|
|
474
|
+
if (pendingAddressKeys.has(key)) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
pendingAddressKeys.add(key);
|
|
478
|
+
const target = fanoutTargetsByAddressKey.get(key);
|
|
479
|
+
if (target) {
|
|
480
|
+
pendingTargets.push(target);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
751
483
|
if (pendingTargets.length === 0) {
|
|
752
484
|
return {
|
|
753
485
|
fanoutDeviceJids,
|
|
754
486
|
distributionParticipants: []
|
|
755
487
|
};
|
|
756
488
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
489
|
+
const pendingTargetJids = new Array(pendingTargets.length);
|
|
490
|
+
for (let index = 0; index < pendingTargets.length; index += 1) {
|
|
491
|
+
pendingTargetJids[index] = pendingTargets[index].jid;
|
|
492
|
+
}
|
|
493
|
+
let availableTargets = [];
|
|
494
|
+
let prefetchedAvailableTargets;
|
|
495
|
+
try {
|
|
496
|
+
const resolvedTargets = await this.sessionResolver.ensureSessionsBatch(pendingTargetJids);
|
|
497
|
+
availableTargets = resolvedTargets;
|
|
498
|
+
prefetchedAvailableTargets = resolvedTargets;
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
const normalized = toError(error);
|
|
502
|
+
if (normalized.message === 'identity mismatch') {
|
|
503
|
+
throw normalized;
|
|
504
|
+
}
|
|
505
|
+
this.logger.warn('group sender-key distribution session sync failed, continuing with available sessions', {
|
|
506
|
+
groupJid,
|
|
507
|
+
requested: pendingTargetJids.length,
|
|
508
|
+
message: normalized.message
|
|
509
|
+
});
|
|
510
|
+
const pendingTargetAddresses = new Array(pendingTargets.length);
|
|
511
|
+
for (let index = 0; index < pendingTargets.length; index += 1) {
|
|
512
|
+
pendingTargetAddresses[index] = pendingTargets[index].address;
|
|
513
|
+
}
|
|
514
|
+
const hasPendingSessions = await this.signalStore.hasSessions(pendingTargetAddresses);
|
|
515
|
+
const nextAvailableTargets = [];
|
|
516
|
+
for (let index = 0; index < pendingTargets.length; index += 1) {
|
|
517
|
+
if (hasPendingSessions[index]) {
|
|
518
|
+
nextAvailableTargets.push(pendingTargets[index]);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
availableTargets = nextAvailableTargets;
|
|
522
|
+
}
|
|
523
|
+
if (availableTargets.length === 0) {
|
|
761
524
|
return {
|
|
525
|
+
fanoutDeviceJids,
|
|
526
|
+
distributionParticipants: []
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
const distributionEncryptRequests = new Array(availableTargets.length);
|
|
530
|
+
for (let index = 0; index < availableTargets.length; index += 1) {
|
|
531
|
+
const target = availableTargets[index];
|
|
532
|
+
distributionEncryptRequests[index] = {
|
|
533
|
+
address: target.address,
|
|
534
|
+
plaintext: distributionPayload
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
const encryptedDistributionParticipants = await this.signalProtocol.encryptMessagesBatch(distributionEncryptRequests, prefetchedAvailableTargets);
|
|
538
|
+
const distributionParticipants = new Array(availableTargets.length);
|
|
539
|
+
for (let index = 0; index < availableTargets.length; index += 1) {
|
|
540
|
+
const target = availableTargets[index];
|
|
541
|
+
distributionParticipants[index] = {
|
|
762
542
|
jid: target.jid,
|
|
763
543
|
address: target.address,
|
|
764
|
-
encType:
|
|
765
|
-
ciphertext:
|
|
544
|
+
encType: encryptedDistributionParticipants[index].type,
|
|
545
|
+
ciphertext: encryptedDistributionParticipants[index].ciphertext
|
|
766
546
|
};
|
|
767
|
-
}
|
|
547
|
+
}
|
|
768
548
|
return {
|
|
769
549
|
fanoutDeviceJids,
|
|
770
550
|
distributionParticipants
|
|
771
551
|
};
|
|
772
552
|
}
|
|
773
|
-
async
|
|
774
|
-
const
|
|
775
|
-
const meJid = this.getCurrentMeJid();
|
|
776
|
-
if (meJid) {
|
|
777
|
-
try {
|
|
778
|
-
meDeviceJids.add(normalizeDeviceJid(meJid));
|
|
779
|
-
}
|
|
780
|
-
catch (error) {
|
|
781
|
-
this.logger.trace('ignoring malformed me jid', {
|
|
782
|
-
meJid,
|
|
783
|
-
message: toError(error).message
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
const meLid = this.getCurrentMeLid();
|
|
788
|
-
if (meLid && meLid.includes('@')) {
|
|
789
|
-
try {
|
|
790
|
-
meDeviceJids.add(normalizeDeviceJid(meLid));
|
|
791
|
-
}
|
|
792
|
-
catch (error) {
|
|
793
|
-
this.logger.trace('ignoring malformed me lid jid', {
|
|
794
|
-
meLid,
|
|
795
|
-
message: toError(error).message
|
|
796
|
-
});
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
const candidateUsers = [...new Set(participantUserJids)];
|
|
800
|
-
if (candidateUsers.length === 0) {
|
|
801
|
-
return [];
|
|
802
|
-
}
|
|
803
|
-
try {
|
|
804
|
-
const synced = await this.signalDeviceSync.syncDeviceList(candidateUsers);
|
|
805
|
-
const fanout = new Set();
|
|
806
|
-
for (const entry of synced) {
|
|
807
|
-
if (entry.deviceJids.length === 0) {
|
|
808
|
-
const normalizedEntryJid = normalizeDeviceJid(entry.jid);
|
|
809
|
-
if (meDeviceJids.has(normalizedEntryJid))
|
|
810
|
-
continue;
|
|
811
|
-
fanout.add(normalizedEntryJid);
|
|
812
|
-
continue;
|
|
813
|
-
}
|
|
814
|
-
for (const deviceJid of entry.deviceJids) {
|
|
815
|
-
const normalizedDeviceJid = normalizeDeviceJid(deviceJid);
|
|
816
|
-
if (meDeviceJids.has(normalizedDeviceJid))
|
|
817
|
-
continue;
|
|
818
|
-
fanout.add(normalizedDeviceJid);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
return [...fanout];
|
|
822
|
-
}
|
|
823
|
-
catch (error) {
|
|
824
|
-
this.logger.warn('group participant device sync failed, falling back to participant user jids', {
|
|
825
|
-
participants: candidateUsers.length,
|
|
826
|
-
message: toError(error).message
|
|
827
|
-
});
|
|
828
|
-
return [...new Set(candidateUsers.map((jid) => normalizeDeviceJid(jid)))].filter((jid) => !meDeviceJids.has(jid));
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
async publishDirectSignalMessageWithFanout(recipientJid, message, plaintext, type, options) {
|
|
553
|
+
async publishDirectSignalMessageWithFanout(recipientJid, message, plaintext, type, options, edit, mediatype, metaNode) {
|
|
554
|
+
const sendOptions = await this.withResolvedMessageId(options);
|
|
832
555
|
const meJid = this.requireCurrentMeJid('sendMessage');
|
|
833
556
|
const meLid = this.getCurrentMeLid();
|
|
834
|
-
const selfDeviceJidForRecipient = this.resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid);
|
|
835
|
-
const deviceJids = await this.resolveDirectFanoutDeviceJids(recipientJid, selfDeviceJidForRecipient);
|
|
557
|
+
const selfDeviceJidForRecipient = this.fanoutResolver.resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid);
|
|
558
|
+
const deviceJids = await this.fanoutResolver.resolveDirectFanoutDeviceJids(recipientJid, selfDeviceJidForRecipient);
|
|
559
|
+
const targets = new Array(deviceJids.length);
|
|
560
|
+
for (let index = 0; index < deviceJids.length; index += 1) {
|
|
561
|
+
const jid = deviceJids[index];
|
|
562
|
+
const parsed = parseJidFull(jid);
|
|
563
|
+
targets[index] = {
|
|
564
|
+
jid,
|
|
565
|
+
normalizedJid: parsed.normalizedJid,
|
|
566
|
+
userJid: parsed.userJid
|
|
567
|
+
};
|
|
568
|
+
}
|
|
836
569
|
const recipientUserJid = toUserJid(recipientJid);
|
|
837
570
|
const meUserJid = toUserJid(selfDeviceJidForRecipient);
|
|
838
571
|
this.logger.debug('wa client publish signal fanout', {
|
|
@@ -841,44 +574,115 @@ export class WaMessageDispatchCoordinator {
|
|
|
841
574
|
type
|
|
842
575
|
});
|
|
843
576
|
const expectedIdentityByJid = new Map();
|
|
844
|
-
if (
|
|
845
|
-
for (let index = 0; index <
|
|
846
|
-
const
|
|
847
|
-
if (
|
|
848
|
-
expectedIdentityByJid.set(
|
|
577
|
+
if (sendOptions.expectedIdentity) {
|
|
578
|
+
for (let index = 0; index < targets.length; index += 1) {
|
|
579
|
+
const target = targets[index];
|
|
580
|
+
if (target.userJid === recipientUserJid) {
|
|
581
|
+
expectedIdentityByJid.set(target.normalizedJid, sendOptions.expectedIdentity);
|
|
849
582
|
}
|
|
850
583
|
}
|
|
851
584
|
}
|
|
852
|
-
await this.
|
|
853
|
-
const
|
|
585
|
+
const resolvedFanoutTargets = await this.sessionResolver.ensureSessionsBatch(deviceJids, expectedIdentityByJid);
|
|
586
|
+
const resolvedFanoutTargetsByJid = new Map();
|
|
587
|
+
for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
|
|
588
|
+
const target = resolvedFanoutTargets[index];
|
|
589
|
+
resolvedFanoutTargetsByJid.set(normalizeDeviceJid(target.jid), target);
|
|
590
|
+
}
|
|
591
|
+
for (let index = 0; index < targets.length; index += 1) {
|
|
592
|
+
if (!resolvedFanoutTargetsByJid.has(targets[index].normalizedJid)) {
|
|
593
|
+
throw new Error('direct fanout missing signal sessions for one or more targets');
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
let hasSelfDeviceFanout = false;
|
|
597
|
+
for (let index = 0; index < targets.length; index += 1) {
|
|
598
|
+
if (targets[index].userJid === meUserJid) {
|
|
599
|
+
hasSelfDeviceFanout = true;
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
854
603
|
const selfDevicePlaintext = hasSelfDeviceFanout
|
|
855
604
|
? await writeRandomPadMax16(proto.Message.encode(wrapDeviceSentMessage(message, recipientUserJid)).finish())
|
|
856
605
|
: null;
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
const
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
606
|
+
const participantRequests = new Array(targets.length);
|
|
607
|
+
for (let index = 0; index < targets.length; index += 1) {
|
|
608
|
+
const target = targets[index];
|
|
609
|
+
const resolvedTarget = resolvedFanoutTargetsByJid.get(target.normalizedJid);
|
|
610
|
+
if (!resolvedTarget) {
|
|
611
|
+
throw new Error('direct fanout missing signal session for target');
|
|
612
|
+
}
|
|
613
|
+
participantRequests[index] = {
|
|
614
|
+
target,
|
|
615
|
+
address: resolvedTarget.address,
|
|
616
|
+
session: resolvedTarget.session,
|
|
617
|
+
expectedIdentity: target.userJid === recipientUserJid ? sendOptions.expectedIdentity : undefined,
|
|
618
|
+
plaintext: selfDevicePlaintext && target.userJid === meUserJid
|
|
619
|
+
? selfDevicePlaintext
|
|
620
|
+
: plaintext
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
const encryptRequests = new Array(participantRequests.length);
|
|
624
|
+
const prefetchedSessions = new Array(participantRequests.length);
|
|
625
|
+
for (let index = 0; index < participantRequests.length; index += 1) {
|
|
626
|
+
const request = participantRequests[index];
|
|
627
|
+
encryptRequests[index] = {
|
|
628
|
+
address: request.address,
|
|
629
|
+
plaintext: request.plaintext,
|
|
630
|
+
expectedIdentity: request.expectedIdentity
|
|
631
|
+
};
|
|
632
|
+
prefetchedSessions[index] = {
|
|
633
|
+
address: request.address,
|
|
634
|
+
session: request.session
|
|
870
635
|
};
|
|
871
|
-
}
|
|
872
|
-
const
|
|
636
|
+
}
|
|
637
|
+
const encryptedParticipants = await this.signalProtocol.encryptMessagesBatch(encryptRequests, prefetchedSessions);
|
|
638
|
+
const participants = new Array(participantRequests.length);
|
|
639
|
+
for (let index = 0; index < participantRequests.length; index += 1) {
|
|
640
|
+
const request = participantRequests[index];
|
|
641
|
+
participants[index] = {
|
|
642
|
+
jid: request.target.jid,
|
|
643
|
+
encType: encryptedParticipants[index].type,
|
|
644
|
+
ciphertext: encryptedParticipants[index].ciphertext
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
let shouldAttachDeviceIdentity = false;
|
|
648
|
+
for (let index = 0; index < participants.length; index += 1) {
|
|
649
|
+
if (participants[index].encType === 'pkmsg') {
|
|
650
|
+
shouldAttachDeviceIdentity = true;
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
873
654
|
const deviceIdentity = shouldAttachDeviceIdentity
|
|
874
655
|
? this.getEncodedSignedDeviceIdentity()
|
|
875
656
|
: undefined;
|
|
657
|
+
const reportingArtifacts = await this.tryBuildReportingTokenArtifacts({
|
|
658
|
+
message,
|
|
659
|
+
stanzaId: sendOptions.id,
|
|
660
|
+
senderUserJid: meUserJid,
|
|
661
|
+
remoteJid: recipientUserJid,
|
|
662
|
+
context: 'direct_fanout'
|
|
663
|
+
});
|
|
664
|
+
let privacyTokenNode;
|
|
665
|
+
try {
|
|
666
|
+
privacyTokenNode =
|
|
667
|
+
(await this.privacyTokenDedup.run(`pt:${recipientUserJid}`, () => this.resolvePrivacyTokenNode(recipientUserJid))) ?? undefined;
|
|
668
|
+
}
|
|
669
|
+
catch (error) {
|
|
670
|
+
this.logger.warn('privacy token resolution failed', {
|
|
671
|
+
to: recipientUserJid,
|
|
672
|
+
message: toError(error).message
|
|
673
|
+
});
|
|
674
|
+
}
|
|
876
675
|
const messageNode = buildDirectMessageFanoutNode({
|
|
877
676
|
to: recipientJid,
|
|
878
677
|
type,
|
|
879
|
-
id:
|
|
678
|
+
id: sendOptions.id,
|
|
679
|
+
edit,
|
|
880
680
|
participants,
|
|
881
|
-
deviceIdentity
|
|
681
|
+
deviceIdentity,
|
|
682
|
+
reportingNode: reportingArtifacts?.node ?? undefined,
|
|
683
|
+
privacyTokenNode,
|
|
684
|
+
metaNode,
|
|
685
|
+
mediatype
|
|
882
686
|
});
|
|
883
687
|
const replayPayload = {
|
|
884
688
|
mode: 'plaintext',
|
|
@@ -886,130 +690,75 @@ export class WaMessageDispatchCoordinator {
|
|
|
886
690
|
type,
|
|
887
691
|
plaintext
|
|
888
692
|
};
|
|
889
|
-
|
|
890
|
-
messageIdHint:
|
|
693
|
+
const result = await this.retryTracker.track({
|
|
694
|
+
messageIdHint: sendOptions.id ?? messageNode.attrs.id,
|
|
891
695
|
toJid: recipientJid,
|
|
892
|
-
|
|
893
|
-
replayPayload
|
|
894
|
-
|
|
696
|
+
type,
|
|
697
|
+
replayPayload,
|
|
698
|
+
eligibleRequesterDeviceJids: deviceJids
|
|
699
|
+
}, async () => this.messageClient.publishNode(messageNode, sendOptions));
|
|
700
|
+
this.onDirectMessageSent(recipientUserJid);
|
|
701
|
+
return result;
|
|
895
702
|
}
|
|
896
|
-
async
|
|
897
|
-
const
|
|
898
|
-
if (
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
const missingTargets = normalizedTargets.filter((_, index) => !hasSessions[index]);
|
|
907
|
-
if (missingTargets.length === 0) {
|
|
908
|
-
return;
|
|
703
|
+
async withResolvedMessageId(options) {
|
|
704
|
+
const normalizedId = options.id?.trim();
|
|
705
|
+
if (normalizedId) {
|
|
706
|
+
if (normalizedId === options.id) {
|
|
707
|
+
return options;
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
...options,
|
|
711
|
+
id: normalizedId
|
|
712
|
+
};
|
|
909
713
|
}
|
|
714
|
+
return {
|
|
715
|
+
...options,
|
|
716
|
+
id: await this.generateOutgoingMessageId()
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
async generateOutgoingMessageId() {
|
|
910
720
|
try {
|
|
911
|
-
const
|
|
912
|
-
const
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const expectedIdentity = expectedIdentityByJid.get(target.jid);
|
|
923
|
-
const remoteIdentity = toSerializedPubKey(result.bundle.identity);
|
|
924
|
-
if (expectedIdentity &&
|
|
925
|
-
!uint8Equal(remoteIdentity, toSerializedPubKey(expectedIdentity))) {
|
|
926
|
-
throw new Error('identity mismatch');
|
|
927
|
-
}
|
|
928
|
-
establishPromises.push(this.signalProtocol
|
|
929
|
-
.establishOutgoingSession(target.address, result.bundle)
|
|
930
|
-
.then(() => {
|
|
931
|
-
this.logger.debug('signal session synchronized from batch key fetch', {
|
|
932
|
-
jid: target.jid,
|
|
933
|
-
regId: result.bundle.regId,
|
|
934
|
-
hasOneTimeKey: result.bundle.oneTimeKey !== undefined
|
|
935
|
-
});
|
|
936
|
-
}));
|
|
937
|
-
}
|
|
938
|
-
await Promise.all(establishPromises);
|
|
939
|
-
if (fallbackJids.length === 0) {
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
this.logger.warn('signal batch key fetch returned partial errors, falling back to single requests', {
|
|
943
|
-
requested: missingTargets.length,
|
|
944
|
-
fallbackTargets: fallbackJids.length
|
|
945
|
-
});
|
|
946
|
-
for (let index = 0; index < fallbackJids.length; index += 1) {
|
|
947
|
-
const jid = fallbackJids[index];
|
|
948
|
-
const address = parseSignalAddressFromJid(jid);
|
|
949
|
-
await this.ensureSignalSession(address, jid, expectedIdentityByJid.get(jid));
|
|
950
|
-
}
|
|
721
|
+
const meUserJid = toUserJid(this.requireCurrentMeJid('sendMessage'));
|
|
722
|
+
const timestampSeconds = Math.floor(Date.now() / 1000);
|
|
723
|
+
const timestampBytes = new Uint8Array(8);
|
|
724
|
+
new DataView(timestampBytes.buffer, timestampBytes.byteOffset, timestampBytes.byteLength).setBigUint64(0, BigInt(timestampSeconds), false);
|
|
725
|
+
const entropy = concatBytes([
|
|
726
|
+
timestampBytes,
|
|
727
|
+
TEXT_ENCODER.encode(meUserJid),
|
|
728
|
+
await randomBytesAsync(8)
|
|
729
|
+
]);
|
|
730
|
+
const digest = await sha256(entropy);
|
|
731
|
+
return `3EB0${bytesToHex(digest.subarray(0, 9)).toUpperCase()}`;
|
|
951
732
|
}
|
|
952
733
|
catch (error) {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
throw normalized;
|
|
956
|
-
}
|
|
957
|
-
this.logger.warn('signal batch key fetch failed, falling back to single requests', {
|
|
958
|
-
requested: missingTargets.length,
|
|
959
|
-
message: normalized.message
|
|
734
|
+
this.logger.warn('failed to generate sha256 message id, falling back to random id', {
|
|
735
|
+
message: toError(error).message
|
|
960
736
|
});
|
|
961
|
-
|
|
962
|
-
const target = missingTargets[index];
|
|
963
|
-
await this.ensureSignalSession(target.address, target.jid, expectedIdentityByJid.get(target.jid));
|
|
964
|
-
}
|
|
737
|
+
return `3EB0${bytesToHex(await randomBytesAsync(8)).toUpperCase()}`;
|
|
965
738
|
}
|
|
966
739
|
}
|
|
967
|
-
async
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
740
|
+
async tryBuildReportingTokenArtifacts(input) {
|
|
741
|
+
if (!input.stanzaId) {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
971
744
|
try {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
else {
|
|
980
|
-
for (let index = 0; index < recipientDevices.length; index += 1) {
|
|
981
|
-
fanout.add(recipientDevices[index]);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
const meDevices = byUser.get(meUserJid) ?? [];
|
|
985
|
-
const normalizedMeJid = normalizeDeviceJid(selfDeviceJidForRecipient);
|
|
986
|
-
for (let index = 0; index < meDevices.length; index += 1) {
|
|
987
|
-
const deviceJid = meDevices[index];
|
|
988
|
-
if (normalizeDeviceJid(deviceJid) === normalizedMeJid) {
|
|
989
|
-
continue;
|
|
990
|
-
}
|
|
991
|
-
fanout.add(deviceJid);
|
|
992
|
-
}
|
|
993
|
-
return [...fanout];
|
|
745
|
+
return await buildReportingTokenArtifacts({
|
|
746
|
+
message: input.message,
|
|
747
|
+
stanzaId: input.stanzaId,
|
|
748
|
+
senderUserJid: input.senderUserJid,
|
|
749
|
+
remoteJid: input.remoteJid
|
|
750
|
+
});
|
|
994
751
|
}
|
|
995
752
|
catch (error) {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
753
|
+
this.logger.warn('failed to generate reporting token', {
|
|
754
|
+
context: input.context,
|
|
755
|
+
id: input.stanzaId,
|
|
756
|
+
remoteJid: input.remoteJid,
|
|
757
|
+
message: toError(error).message
|
|
1000
758
|
});
|
|
1001
|
-
return
|
|
759
|
+
return null;
|
|
1002
760
|
}
|
|
1003
761
|
}
|
|
1004
|
-
resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid) {
|
|
1005
|
-
if (splitJid(recipientJid).server !== 'lid') {
|
|
1006
|
-
return meJid;
|
|
1007
|
-
}
|
|
1008
|
-
if (!meLid || !meLid.includes('@')) {
|
|
1009
|
-
return meJid;
|
|
1010
|
-
}
|
|
1011
|
-
return meLid;
|
|
1012
|
-
}
|
|
1013
762
|
getEncodedSignedDeviceIdentity() {
|
|
1014
763
|
const signedIdentity = this.getCurrentSignedIdentity();
|
|
1015
764
|
if (!signedIdentity) {
|
|
@@ -1017,34 +766,23 @@ export class WaMessageDispatchCoordinator {
|
|
|
1017
766
|
}
|
|
1018
767
|
return proto.ADVSignedDeviceIdentity.encode(signedIdentity).finish();
|
|
1019
768
|
}
|
|
1020
|
-
|
|
1021
|
-
this.
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
if (storedIdentity && !uint8Equal(remoteIdentity, storedIdentity)) {
|
|
1037
|
-
throw new Error('identity mismatch');
|
|
769
|
+
resolveUserIcdc(userJid, localIdentity) {
|
|
770
|
+
return this.icdcDedup.run(`icdc:${userJid}:${localIdentity ? '1' : '0'}`, async () => {
|
|
771
|
+
try {
|
|
772
|
+
const snapshots = await this.deviceListStore.getUserDevicesBatch([userJid]);
|
|
773
|
+
const snapshot = snapshots[0];
|
|
774
|
+
if (!snapshot || snapshot.deviceJids.length === 0) {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
return resolveIcdcMeta(snapshot.deviceJids, this.signalStore, snapshot.updatedAtMs, localIdentity);
|
|
778
|
+
}
|
|
779
|
+
catch (error) {
|
|
780
|
+
this.logger.trace('icdc resolution failed', {
|
|
781
|
+
userJid,
|
|
782
|
+
message: toError(error).message
|
|
783
|
+
});
|
|
784
|
+
return null;
|
|
1038
785
|
}
|
|
1039
|
-
}
|
|
1040
|
-
if (expectedIdentity && !uint8Equal(remoteIdentity, toSerializedPubKey(expectedIdentity))) {
|
|
1041
|
-
throw new Error('identity mismatch');
|
|
1042
|
-
}
|
|
1043
|
-
await this.signalProtocol.establishOutgoingSession(address, fetched.bundle);
|
|
1044
|
-
this.logger.info('signal session synchronized', {
|
|
1045
|
-
jid,
|
|
1046
|
-
regId: fetched.bundle.regId,
|
|
1047
|
-
hasOneTimeKey: fetched.bundle.oneTimeKey !== undefined
|
|
1048
786
|
});
|
|
1049
787
|
}
|
|
1050
788
|
requireCurrentMeJid(context) {
|