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
|
@@ -2,9 +2,8 @@ import { parseGroupNotificationEvents } from './events/group.js';
|
|
|
2
2
|
import { WA_NODE_TAGS, WA_NOTIFICATION_TYPES } from '../protocol/constants.js';
|
|
3
3
|
import { buildInboundReceiptAckNode, buildInboundRetryReceiptAckNode } from '../transport/node/builders/message.js';
|
|
4
4
|
import { buildNotificationAckNode } from '../transport/node/builders/pairing.js';
|
|
5
|
-
import { getFirstNodeChild,
|
|
6
|
-
import { parseOptionalInt } from '../
|
|
7
|
-
import { toError } from '../util/primitives.js';
|
|
5
|
+
import { getFirstNodeChild, getNodeChildrenNonEmptyAttrValuesByTag } from '../transport/node/helpers.js';
|
|
6
|
+
import { parseOptionalInt, toError } from '../util/primitives.js';
|
|
8
7
|
const LOGOUT_FAILURE_REASONS = new Set([401, 403, 406]);
|
|
9
8
|
const DISCONNECT_FAILURE_REASONS = new Set([405, 409, 503]);
|
|
10
9
|
const CORE_NOTIFICATION_TYPES = new Set([
|
|
@@ -141,47 +140,50 @@ export function createIncomingNotificationHandler(options) {
|
|
|
141
140
|
const notificationType = node.attrs.type ?? '';
|
|
142
141
|
const classification = classifyNotificationType(notificationType);
|
|
143
142
|
const firstChildTag = getFirstNodeChild(node)?.tag;
|
|
143
|
+
const baseEvent = createIncomingBaseEvent(node);
|
|
144
144
|
const serverSyncCollections = notificationType === 'server_sync'
|
|
145
|
-
?
|
|
146
|
-
.map((collectionNode) => collectionNode.attrs.name)
|
|
147
|
-
.filter((name) => typeof name === 'string' && name.length > 0)
|
|
145
|
+
? getNodeChildrenNonEmptyAttrValuesByTag(node, WA_NODE_TAGS.COLLECTION, 'name')
|
|
148
146
|
: [];
|
|
147
|
+
let details;
|
|
148
|
+
if (firstChildTag || serverSyncCollections.length > 0) {
|
|
149
|
+
details = {};
|
|
150
|
+
if (firstChildTag) {
|
|
151
|
+
details.firstChildTag = firstChildTag;
|
|
152
|
+
}
|
|
153
|
+
if (serverSyncCollections.length > 0) {
|
|
154
|
+
details.collections = serverSyncCollections;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
149
157
|
options.emitIncomingNotification({
|
|
150
|
-
...
|
|
158
|
+
...baseEvent,
|
|
151
159
|
notificationType,
|
|
152
160
|
classification,
|
|
153
|
-
details
|
|
154
|
-
? {
|
|
155
|
-
...(firstChildTag ? { firstChildTag } : {}),
|
|
156
|
-
...(serverSyncCollections.length > 0
|
|
157
|
-
? { collections: serverSyncCollections }
|
|
158
|
-
: {})
|
|
159
|
-
}
|
|
160
|
-
: undefined
|
|
161
|
+
details
|
|
161
162
|
});
|
|
162
163
|
if (classification === 'out_of_scope') {
|
|
163
164
|
options.emitUnhandledStanza({
|
|
164
|
-
...
|
|
165
|
+
...baseEvent,
|
|
165
166
|
reason: `notification.${notificationType}.out_of_scope`
|
|
166
167
|
});
|
|
167
168
|
}
|
|
168
169
|
else if (classification === 'unknown') {
|
|
169
170
|
options.emitUnhandledStanza({
|
|
170
|
-
...
|
|
171
|
+
...baseEvent,
|
|
171
172
|
reason: `notification.${notificationType || 'unknown'}.not_supported`
|
|
172
173
|
});
|
|
173
174
|
}
|
|
174
175
|
await sendSafeAck(options.logger, options.sendNode, buildNotificationAckNode(node));
|
|
175
176
|
if (notificationType === 'server_sync' && serverSyncCollections.length > 0) {
|
|
177
|
+
const collectionsCsv = serverSyncCollections.join(',');
|
|
176
178
|
if (!options.syncAppState) {
|
|
177
179
|
options.logger.warn('received server_sync notification without app-state sync runtime', {
|
|
178
|
-
collections:
|
|
180
|
+
collections: collectionsCsv
|
|
179
181
|
});
|
|
180
182
|
return true;
|
|
181
183
|
}
|
|
182
184
|
void options.syncAppState().catch((error) => {
|
|
183
185
|
options.logger.warn('failed to sync app-state after server_sync notification', {
|
|
184
|
-
collections:
|
|
186
|
+
collections: collectionsCsv,
|
|
185
187
|
message: toError(error).message
|
|
186
188
|
});
|
|
187
189
|
});
|
|
@@ -194,6 +196,7 @@ export function createIncomingGroupNotificationHandler(options) {
|
|
|
194
196
|
if (node.attrs.type !== WA_NOTIFICATION_TYPES.GROUP) {
|
|
195
197
|
return false;
|
|
196
198
|
}
|
|
199
|
+
const baseEvent = createIncomingBaseEvent(node);
|
|
197
200
|
const parsed = parseGroupNotificationEvents(node);
|
|
198
201
|
for (const event of parsed.events) {
|
|
199
202
|
options.emitGroupEvent(event);
|
|
@@ -203,7 +206,7 @@ export function createIncomingGroupNotificationHandler(options) {
|
|
|
203
206
|
}
|
|
204
207
|
if (parsed.events.length === 0 && parsed.unhandled.length === 0) {
|
|
205
208
|
options.emitUnhandledStanza({
|
|
206
|
-
...
|
|
209
|
+
...baseEvent,
|
|
207
210
|
reason: `notification.${WA_NOTIFICATION_TYPES.GROUP}.empty`
|
|
208
211
|
});
|
|
209
212
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { proto } from '../proto.js';
|
|
2
2
|
import { toError } from '../util/primitives.js';
|
|
3
3
|
async function persistContacts(contactStore, event, nowMs) {
|
|
4
|
-
const candidateJids =
|
|
5
|
-
if (
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
if (event.rawNode.attrs.participant) {
|
|
9
|
-
candidateJids.add(event.rawNode.attrs.participant);
|
|
4
|
+
const candidateJids = [event.senderJid, event.rawNode.attrs.participant].filter((jid) => !!jid);
|
|
5
|
+
if (candidateJids.length === 0) {
|
|
6
|
+
return;
|
|
10
7
|
}
|
|
11
|
-
|
|
8
|
+
const contacts = [...new Set(candidateJids)].map((jid) => ({ jid, lastUpdatedMs: nowMs }));
|
|
9
|
+
await contactStore.upsertBatch(contacts);
|
|
12
10
|
}
|
|
13
11
|
export async function persistIncomingMailboxEntities(options) {
|
|
14
12
|
const { logger, contactStore, messageStore, event } = options;
|
|
@@ -28,7 +26,9 @@ export async function persistIncomingMailboxEntities(options) {
|
|
|
28
26
|
senderJid: event.senderJid,
|
|
29
27
|
participantJid: event.rawNode.attrs.participant,
|
|
30
28
|
fromMe: false,
|
|
31
|
-
timestampMs: event.timestampSeconds === undefined
|
|
29
|
+
timestampMs: event.timestampSeconds === undefined
|
|
30
|
+
? undefined
|
|
31
|
+
: event.timestampSeconds * 1000,
|
|
32
32
|
encType: event.encryptionType,
|
|
33
33
|
plaintext: event.plaintext,
|
|
34
34
|
messageBytes
|
|
@@ -4,7 +4,7 @@ import { WaMediaCrypto } from '../media/WaMediaCrypto.js';
|
|
|
4
4
|
import { isSendMediaMessage } from '../message/content.js';
|
|
5
5
|
import { WA_DEFAULTS } from '../protocol/constants.js';
|
|
6
6
|
import { buildMediaConnIq } from '../transport/node/builders/media.js';
|
|
7
|
-
import { bytesToBase64UrlSafe } from '../util/
|
|
7
|
+
import { bytesToBase64UrlSafe } from '../util/bytes.js';
|
|
8
8
|
import { TEXT_DECODER, toBytesView } from '../util/bytes.js';
|
|
9
9
|
import { toError } from '../util/primitives.js';
|
|
10
10
|
export async function buildMediaMessageContent(options, content) {
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { normalizeDeviceJid, splitJid, toUserJid } from '../../protocol/jid.js';
|
|
2
|
+
import { toError } from '../../util/primitives.js';
|
|
3
|
+
export function createDeviceFanoutResolver(options) {
|
|
4
|
+
const { signalDeviceSync, getCurrentMeJid, getCurrentMeLid, logger } = options;
|
|
5
|
+
const requireCurrentMeJid = (context) => {
|
|
6
|
+
const meJid = getCurrentMeJid();
|
|
7
|
+
if (meJid) {
|
|
8
|
+
return meJid;
|
|
9
|
+
}
|
|
10
|
+
throw new Error(`${context} requires registered meJid`);
|
|
11
|
+
};
|
|
12
|
+
const resolveDirectFanoutDeviceJids = async (recipientJid, selfDeviceJidForRecipient) => {
|
|
13
|
+
const recipientUserJid = toUserJid(recipientJid);
|
|
14
|
+
const meUserJid = toUserJid(selfDeviceJidForRecipient);
|
|
15
|
+
const targets = recipientUserJid === meUserJid ? [recipientUserJid] : [recipientUserJid, meUserJid];
|
|
16
|
+
try {
|
|
17
|
+
const synced = await signalDeviceSync.syncDeviceList(targets);
|
|
18
|
+
const byUser = new Map(synced.map((entry) => [toUserJid(entry.jid), entry.deviceJids]));
|
|
19
|
+
const fanout = new Set();
|
|
20
|
+
const recipientDevices = byUser.get(recipientUserJid) ?? [];
|
|
21
|
+
if (recipientDevices.length === 0) {
|
|
22
|
+
fanout.add(recipientUserJid);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
for (let index = 0; index < recipientDevices.length; index += 1) {
|
|
26
|
+
fanout.add(recipientDevices[index]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const meDevices = byUser.get(meUserJid) ?? [];
|
|
30
|
+
const normalizedMeJid = normalizeDeviceJid(selfDeviceJidForRecipient);
|
|
31
|
+
for (let index = 0; index < meDevices.length; index += 1) {
|
|
32
|
+
const deviceJid = meDevices[index];
|
|
33
|
+
if (normalizeDeviceJid(deviceJid) === normalizedMeJid) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
fanout.add(deviceJid);
|
|
37
|
+
}
|
|
38
|
+
return [...fanout];
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
logger.warn('signal device fanout sync failed, falling back to direct recipient', {
|
|
42
|
+
to: recipientJid,
|
|
43
|
+
message: toError(error).message
|
|
44
|
+
});
|
|
45
|
+
return [recipientUserJid];
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const resolveGroupParticipantDeviceJids = async (participantUserJids) => {
|
|
49
|
+
const meDeviceJids = new Set();
|
|
50
|
+
const meJid = getCurrentMeJid();
|
|
51
|
+
if (meJid) {
|
|
52
|
+
try {
|
|
53
|
+
meDeviceJids.add(normalizeDeviceJid(meJid));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
logger.trace('ignoring malformed me jid', {
|
|
57
|
+
meJid,
|
|
58
|
+
message: toError(error).message
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const meLid = getCurrentMeLid();
|
|
63
|
+
if (meLid && meLid.includes('@')) {
|
|
64
|
+
try {
|
|
65
|
+
meDeviceJids.add(normalizeDeviceJid(meLid));
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
logger.trace('ignoring malformed me lid jid', {
|
|
69
|
+
meLid,
|
|
70
|
+
message: toError(error).message
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const candidateUsers = [...new Set(participantUserJids)];
|
|
75
|
+
if (candidateUsers.length === 0) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const synced = await signalDeviceSync.syncDeviceList(candidateUsers);
|
|
80
|
+
const fanout = new Set();
|
|
81
|
+
for (const entry of synced) {
|
|
82
|
+
if (entry.deviceJids.length === 0) {
|
|
83
|
+
const normalizedEntryJid = normalizeDeviceJid(entry.jid);
|
|
84
|
+
if (meDeviceJids.has(normalizedEntryJid)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
fanout.add(normalizedEntryJid);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
for (const deviceJid of entry.deviceJids) {
|
|
91
|
+
const normalizedDeviceJid = normalizeDeviceJid(deviceJid);
|
|
92
|
+
if (meDeviceJids.has(normalizedDeviceJid)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
fanout.add(normalizedDeviceJid);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return [...fanout];
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
logger.warn('group participant device sync failed, falling back to participant user jids', {
|
|
102
|
+
participants: candidateUsers.length,
|
|
103
|
+
message: toError(error).message
|
|
104
|
+
});
|
|
105
|
+
const fallbackJids = new Set();
|
|
106
|
+
for (const candidateJid of candidateUsers) {
|
|
107
|
+
try {
|
|
108
|
+
const normalizedCandidateJid = normalizeDeviceJid(candidateJid);
|
|
109
|
+
if (meDeviceJids.has(normalizedCandidateJid)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
fallbackJids.add(normalizedCandidateJid);
|
|
113
|
+
}
|
|
114
|
+
catch (fallbackError) {
|
|
115
|
+
logger.trace('ignoring malformed participant jid in fallback fanout resolution', {
|
|
116
|
+
participantJid: candidateJid,
|
|
117
|
+
message: toError(fallbackError).message
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return [...fallbackJids];
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const resolveOwnPeerDeviceJids = async () => {
|
|
125
|
+
const meJid = requireCurrentMeJid('resolveOwnPeerDeviceJids');
|
|
126
|
+
const meUserJid = toUserJid(meJid);
|
|
127
|
+
const meDevices = new Set();
|
|
128
|
+
meDevices.add(normalizeDeviceJid(meJid));
|
|
129
|
+
const meLid = getCurrentMeLid();
|
|
130
|
+
if (meLid && meLid.includes('@')) {
|
|
131
|
+
try {
|
|
132
|
+
meDevices.add(normalizeDeviceJid(meLid));
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
logger.trace('ignoring malformed me lid jid while resolving peer devices', {
|
|
136
|
+
meLid,
|
|
137
|
+
message: toError(error).message
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const synced = await signalDeviceSync.syncDeviceList([meUserJid]);
|
|
143
|
+
const peerDevices = new Set();
|
|
144
|
+
for (const entry of synced) {
|
|
145
|
+
const sourceDevices = entry.deviceJids.length > 0 ? entry.deviceJids : [entry.jid];
|
|
146
|
+
for (const deviceJid of sourceDevices) {
|
|
147
|
+
try {
|
|
148
|
+
const normalized = normalizeDeviceJid(deviceJid);
|
|
149
|
+
if (meDevices.has(normalized)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
peerDevices.add(normalized);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger.trace('ignoring malformed peer device jid while resolving app-state peers', {
|
|
156
|
+
deviceJid,
|
|
157
|
+
message: toError(error).message
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return [...peerDevices];
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
logger.warn('failed to resolve peer devices for app-state key request', {
|
|
166
|
+
message: toError(error).message
|
|
167
|
+
});
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const resolveSelfDeviceJidForRecipient = (recipientJid, meJid, meLid) => {
|
|
172
|
+
if (splitJid(recipientJid).server !== 'lid') {
|
|
173
|
+
return meJid;
|
|
174
|
+
}
|
|
175
|
+
if (!meLid || !meLid.includes('@')) {
|
|
176
|
+
return meJid;
|
|
177
|
+
}
|
|
178
|
+
return meLid;
|
|
179
|
+
};
|
|
180
|
+
return {
|
|
181
|
+
resolveDirectFanoutDeviceJids,
|
|
182
|
+
resolveGroupParticipantDeviceJids,
|
|
183
|
+
resolveOwnPeerDeviceJids,
|
|
184
|
+
resolveSelfDeviceJidForRecipient
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { writeRandomPadMax16 } from '../../message/padding.js';
|
|
2
|
+
import { proto } from '../../proto.js';
|
|
3
|
+
import { normalizeDeviceJid } from '../../protocol/jid.js';
|
|
4
|
+
import { bytesToHex } from '../../util/bytes.js';
|
|
5
|
+
export function createAppStateSyncKeyProtocol(options) {
|
|
6
|
+
const { publishSignalMessage, fanoutResolver, getCurrentMeJid, getCurrentMeLid, logger } = options;
|
|
7
|
+
const requireCurrentIdentity = (context) => {
|
|
8
|
+
const meJid = getCurrentMeJid();
|
|
9
|
+
const meLid = getCurrentMeLid();
|
|
10
|
+
if (meJid || meLid) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
throw new Error(`${context} requires registered identity`);
|
|
14
|
+
};
|
|
15
|
+
const publishProtocolMessageToDevice = async (deviceJid, protocolMessage) => {
|
|
16
|
+
const plaintext = await writeRandomPadMax16(proto.Message.encode({
|
|
17
|
+
protocolMessage
|
|
18
|
+
}).finish());
|
|
19
|
+
await publishSignalMessage({
|
|
20
|
+
to: deviceJid,
|
|
21
|
+
plaintext,
|
|
22
|
+
type: 'protocol',
|
|
23
|
+
category: 'peer',
|
|
24
|
+
pushPriority: 'high'
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const requestKeys = async (keyIds) => {
|
|
28
|
+
requireCurrentIdentity('requestKeys');
|
|
29
|
+
const normalizedKeyIds = [];
|
|
30
|
+
const seenKeyIds = new Set();
|
|
31
|
+
for (const keyId of keyIds) {
|
|
32
|
+
if (keyId.byteLength === 0) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const keyHex = bytesToHex(keyId);
|
|
36
|
+
if (seenKeyIds.has(keyHex)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
seenKeyIds.add(keyHex);
|
|
40
|
+
normalizedKeyIds.push(keyId);
|
|
41
|
+
}
|
|
42
|
+
if (normalizedKeyIds.length === 0) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const peerDeviceJids = await fanoutResolver.resolveOwnPeerDeviceJids();
|
|
46
|
+
if (peerDeviceJids.length === 0) {
|
|
47
|
+
logger.warn('app-state sync key request skipped: no peer devices available', {
|
|
48
|
+
keys: normalizedKeyIds.length
|
|
49
|
+
});
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const protocolMessage = {
|
|
53
|
+
type: proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_REQUEST,
|
|
54
|
+
appStateSyncKeyRequest: {
|
|
55
|
+
keyIds: normalizedKeyIds.map((keyId) => ({
|
|
56
|
+
keyId
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const publishResults = await Promise.allSettled(peerDeviceJids.map((deviceJid) => publishProtocolMessageToDevice(deviceJid, protocolMessage)));
|
|
61
|
+
const failedPublishes = publishResults.filter((result) => result.status === 'rejected').length;
|
|
62
|
+
if (failedPublishes > 0) {
|
|
63
|
+
logger.warn('some app-state sync key requests failed', {
|
|
64
|
+
total: peerDeviceJids.length,
|
|
65
|
+
failed: failedPublishes
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
logger.info('app-state sync key request sent to peer devices', {
|
|
69
|
+
devices: peerDeviceJids.length,
|
|
70
|
+
keys: normalizedKeyIds.length,
|
|
71
|
+
keyIds: normalizedKeyIds.map((keyId) => bytesToHex(keyId)).join(',')
|
|
72
|
+
});
|
|
73
|
+
return peerDeviceJids;
|
|
74
|
+
};
|
|
75
|
+
const sendKeyShare = async (toDeviceJid, keys, missingKeyIds = []) => {
|
|
76
|
+
requireCurrentIdentity('sendKeyShare');
|
|
77
|
+
const normalizedTo = normalizeDeviceJid(toDeviceJid);
|
|
78
|
+
const seenKeyIds = new Set();
|
|
79
|
+
const keyShareEntries = [];
|
|
80
|
+
let sharedKeyCount = 0;
|
|
81
|
+
for (const key of keys) {
|
|
82
|
+
const keyHex = bytesToHex(key.keyId);
|
|
83
|
+
if (seenKeyIds.has(keyHex)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
seenKeyIds.add(keyHex);
|
|
87
|
+
sharedKeyCount += 1;
|
|
88
|
+
keyShareEntries.push({
|
|
89
|
+
keyId: { keyId: key.keyId },
|
|
90
|
+
keyData: {
|
|
91
|
+
keyData: key.keyData,
|
|
92
|
+
timestamp: key.timestamp,
|
|
93
|
+
...(key.fingerprint ? { fingerprint: key.fingerprint } : {})
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
for (const keyId of missingKeyIds) {
|
|
98
|
+
if (keyId.byteLength === 0) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const keyHex = bytesToHex(keyId);
|
|
102
|
+
if (seenKeyIds.has(keyHex)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
seenKeyIds.add(keyHex);
|
|
106
|
+
keyShareEntries.push({
|
|
107
|
+
keyId: { keyId }
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const protocolMessage = {
|
|
111
|
+
type: proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE,
|
|
112
|
+
appStateSyncKeyShare: {
|
|
113
|
+
keys: keyShareEntries
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
await publishProtocolMessageToDevice(normalizedTo, protocolMessage);
|
|
117
|
+
logger.info('app-state sync key share sent', {
|
|
118
|
+
to: normalizedTo,
|
|
119
|
+
keys: sharedKeyCount,
|
|
120
|
+
orphanKeys: keyShareEntries.length - sharedKeyCount
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
requestKeys,
|
|
125
|
+
sendKeyShare
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { toUserJid } from '../../protocol/jid.js';
|
|
2
|
+
import { toError } from '../../util/primitives.js';
|
|
3
|
+
export function createGroupParticipantsCache(options) {
|
|
4
|
+
const { participantsStore, queryGroupParticipantJids, logger } = options;
|
|
5
|
+
const sanitizeParticipantUsers = (participants) => {
|
|
6
|
+
const deduped = new Set();
|
|
7
|
+
for (const participant of participants) {
|
|
8
|
+
if (!participant || !participant.includes('@')) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
deduped.add(toUserJid(participant));
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
logger.trace('ignoring malformed participant jid', {
|
|
16
|
+
participant,
|
|
17
|
+
message: toError(error).message
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return [...deduped];
|
|
22
|
+
};
|
|
23
|
+
const areParticipantListsEqual = (left, right) => {
|
|
24
|
+
if (left.length !== right.length) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
28
|
+
if (left[index] !== right[index]) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
};
|
|
34
|
+
const mergeParticipantUsersIntoCache = async (groupJid, cachedParticipants, participantsToAdd) => {
|
|
35
|
+
if (participantsToAdd.length === 0) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const nextParticipants = [...cachedParticipants];
|
|
39
|
+
const existing = new Set(cachedParticipants);
|
|
40
|
+
for (const participant of participantsToAdd) {
|
|
41
|
+
if (existing.has(participant)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
existing.add(participant);
|
|
45
|
+
nextParticipants.push(participant);
|
|
46
|
+
}
|
|
47
|
+
if (nextParticipants.length === cachedParticipants.length) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
await participantsStore.upsertGroupParticipants({
|
|
51
|
+
groupJid,
|
|
52
|
+
participants: nextParticipants,
|
|
53
|
+
updatedAtMs: Date.now()
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const removeParticipantUsersFromCache = async (groupJid, cachedParticipants, participantsToRemove) => {
|
|
57
|
+
if (participantsToRemove.length === 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const removed = new Set(participantsToRemove);
|
|
61
|
+
const nextParticipants = cachedParticipants.filter((participant) => !removed.has(participant));
|
|
62
|
+
if (nextParticipants.length === cachedParticipants.length) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (nextParticipants.length === 0) {
|
|
66
|
+
await participantsStore.deleteGroupParticipants(groupJid);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await participantsStore.upsertGroupParticipants({
|
|
70
|
+
groupJid,
|
|
71
|
+
participants: nextParticipants,
|
|
72
|
+
updatedAtMs: Date.now()
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
const replaceParticipantUsersInCache = async (groupJid, cachedParticipants, participantsToReplace, replacementParticipants) => {
|
|
76
|
+
const toReplace = new Set(participantsToReplace);
|
|
77
|
+
const nextParticipants = cachedParticipants.filter((participant) => !toReplace.has(participant));
|
|
78
|
+
const existing = new Set(nextParticipants);
|
|
79
|
+
for (const participant of replacementParticipants) {
|
|
80
|
+
if (existing.has(participant)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
existing.add(participant);
|
|
84
|
+
nextParticipants.push(participant);
|
|
85
|
+
}
|
|
86
|
+
if (areParticipantListsEqual(cachedParticipants, nextParticipants)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (nextParticipants.length === 0) {
|
|
90
|
+
await participantsStore.deleteGroupParticipants(groupJid);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await participantsStore.upsertGroupParticipants({
|
|
94
|
+
groupJid,
|
|
95
|
+
participants: nextParticipants,
|
|
96
|
+
updatedAtMs: Date.now()
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const resolveGroupJidForParticipantCacheEvent = (event) => {
|
|
100
|
+
if (event.action === 'linked_group_promote' || event.action === 'linked_group_demote') {
|
|
101
|
+
return event.contextGroupJid ?? event.groupJid ?? null;
|
|
102
|
+
}
|
|
103
|
+
return event.groupJid ?? null;
|
|
104
|
+
};
|
|
105
|
+
const extractParticipantUsersFromGroupEvent = (event) => {
|
|
106
|
+
const candidates = [];
|
|
107
|
+
for (const participant of event.participants ?? []) {
|
|
108
|
+
if (participant.jid) {
|
|
109
|
+
candidates.push(participant.jid);
|
|
110
|
+
}
|
|
111
|
+
if (participant.lidJid) {
|
|
112
|
+
candidates.push(participant.lidJid);
|
|
113
|
+
}
|
|
114
|
+
if (participant.phoneJid) {
|
|
115
|
+
candidates.push(participant.phoneJid);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return sanitizeParticipantUsers(candidates);
|
|
119
|
+
};
|
|
120
|
+
const refreshParticipantUsers = async (groupJid) => {
|
|
121
|
+
const queried = await queryGroupParticipantJids(groupJid);
|
|
122
|
+
const participants = sanitizeParticipantUsers(queried);
|
|
123
|
+
await participantsStore.upsertGroupParticipants({
|
|
124
|
+
groupJid,
|
|
125
|
+
participants,
|
|
126
|
+
updatedAtMs: Date.now()
|
|
127
|
+
});
|
|
128
|
+
return participants;
|
|
129
|
+
};
|
|
130
|
+
const resolveParticipantUsers = async (groupJid) => {
|
|
131
|
+
const cached = await participantsStore.getGroupParticipants(groupJid);
|
|
132
|
+
if (cached && cached.participants.length > 0) {
|
|
133
|
+
return sanitizeParticipantUsers(cached.participants);
|
|
134
|
+
}
|
|
135
|
+
return refreshParticipantUsers(groupJid);
|
|
136
|
+
};
|
|
137
|
+
const mutateFromGroupEvent = async (event) => {
|
|
138
|
+
const groupJid = resolveGroupJidForParticipantCacheEvent(event);
|
|
139
|
+
if (!groupJid) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (event.action === 'delete') {
|
|
143
|
+
await participantsStore.deleteGroupParticipants(groupJid);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const participantUsers = extractParticipantUsersFromGroupEvent(event);
|
|
147
|
+
if (event.action === 'create') {
|
|
148
|
+
if (participantUsers.length === 0) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
await participantsStore.upsertGroupParticipants({
|
|
152
|
+
groupJid,
|
|
153
|
+
participants: participantUsers,
|
|
154
|
+
updatedAtMs: Date.now()
|
|
155
|
+
});
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const cached = await participantsStore.getGroupParticipants(groupJid);
|
|
159
|
+
if (!cached || cached.participants.length === 0) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const cachedParticipants = sanitizeParticipantUsers(cached.participants);
|
|
163
|
+
if (cachedParticipants.length === 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (event.action === 'add' ||
|
|
167
|
+
event.action === 'promote' ||
|
|
168
|
+
event.action === 'demote' ||
|
|
169
|
+
event.action === 'linked_group_promote' ||
|
|
170
|
+
event.action === 'linked_group_demote') {
|
|
171
|
+
await mergeParticipantUsersIntoCache(groupJid, cachedParticipants, participantUsers);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (event.action === 'remove') {
|
|
175
|
+
await removeParticipantUsersFromCache(groupJid, cachedParticipants, participantUsers);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (event.action === 'modify') {
|
|
179
|
+
const authorUsers = event.authorJid ? sanitizeParticipantUsers([event.authorJid]) : [];
|
|
180
|
+
await replaceParticipantUsersInCache(groupJid, cachedParticipants, authorUsers, participantUsers);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return {
|
|
184
|
+
resolveParticipantUsers,
|
|
185
|
+
refreshParticipantUsers,
|
|
186
|
+
mutateFromGroupEvent
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import { webcrypto } from 'node:crypto';
|
|
2
2
|
import { EMPTY_BYTES, TEXT_ENCODER, toBytesView } from '../../util/bytes.js';
|
|
3
|
-
/**
|
|
4
|
-
* HKDF key derivation using SHA-256
|
|
5
|
-
*/
|
|
6
3
|
export async function hkdf(ikm, salt, info, outLength) {
|
|
7
4
|
const key = await webcrypto.subtle.importKey('raw', ikm, 'HKDF', false, ['deriveBits']);
|
|
5
|
+
const infoBytes = typeof info === 'string' ? (info === '' ? EMPTY_BYTES : TEXT_ENCODER.encode(info)) : info;
|
|
8
6
|
const bits = await webcrypto.subtle.deriveBits({
|
|
9
7
|
name: 'HKDF',
|
|
10
8
|
hash: 'SHA-256',
|
|
11
9
|
salt: salt ?? EMPTY_BYTES,
|
|
12
|
-
info:
|
|
10
|
+
info: infoBytes
|
|
13
11
|
}, key, outLength * 8);
|
|
14
12
|
return toBytesView(bits);
|
|
15
13
|
}
|
|
16
|
-
/**
|
|
17
|
-
* HKDF key derivation that outputs two 32-byte keys
|
|
18
|
-
*/
|
|
19
14
|
export async function hkdfSplit(ikm, salt, info) {
|
|
20
15
|
const out = await hkdf(ikm, salt, info, 64);
|
|
21
|
-
return [out.subarray(0, 32), out.subarray(32
|
|
16
|
+
return [out.subarray(0, 32), out.subarray(32)];
|
|
22
17
|
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { Ed25519 } from '../curves/Ed25519.js';
|
|
5
5
|
export { X25519 } from '../curves/X25519.js';
|
|
6
|
-
export { decodeBase64Url, assert32 } from '../core/encoding.js';
|
|
7
6
|
export { hkdf, hkdfSplit } from '../core/hkdf.js';
|
|
8
7
|
export { toSerializedPubKey, toRawPubKey, prependVersion, readVersionedContent } from '../core/keys.js';
|
|
9
8
|
export { buildNonce } from '../core/nonce.js';
|