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