@fer2809fl/baileys 7.0.1 → 7.0.4
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 +322 -578
- package/WAProto/WAProto.proto +916 -360
- package/WAProto/fix-imports.js +18 -22
- package/WAProto/index.d.ts +15471 -8638
- package/WAProto/index.js +80576 -45941
- package/lib/Defaults/index.d.ts +14 -18
- package/lib/Defaults/index.js +111 -115
- package/lib/Signal/libsignal.js +22 -31
- package/lib/Signal/lid-mapping.d.ts +0 -4
- package/lib/Signal/lid-mapping.js +0 -6
- package/lib/Socket/business.d.ts +8 -75
- package/lib/Socket/business.js +1 -0
- package/lib/Socket/chats.d.ts +0 -6
- package/lib/Socket/chats.js +3 -67
- package/lib/Socket/communities.d.ts +641 -336
- package/lib/Socket/communities.js +3 -6
- package/lib/Socket/dugong.d.ts +130 -0
- package/lib/Socket/dugong.js +772 -0
- package/lib/Socket/groups.d.ts +18 -23
- package/lib/Socket/groups.js +88 -168
- package/lib/Socket/index.d.ts +631 -322
- package/lib/Socket/index.js +12 -9
- package/lib/Socket/messages-recv.d.ts +52 -84
- package/lib/Socket/messages-recv.js +193 -677
- package/lib/Socket/messages-send.d.ts +643 -262
- package/lib/Socket/messages-send.js +1559 -1359
- package/lib/Socket/mex.d.ts +0 -1
- package/lib/Socket/mex.js +8 -22
- package/lib/Socket/native-buttons.js +231 -0
- package/lib/Socket/newsletter.d.ts +27 -42
- package/lib/Socket/newsletter.js +439 -402
- package/lib/Socket/socket.d.ts +2 -6
- package/lib/Socket/socket.js +47 -39
- package/lib/Types/Auth.d.ts +0 -1
- package/lib/Types/Bussines.d.ts +34 -22
- package/lib/Types/Call.d.ts +1 -1
- package/lib/Types/Chat.d.ts +0 -1
- package/lib/Types/Contact.d.ts +0 -2
- package/lib/Types/Events.d.ts +1 -20
- package/lib/Types/GroupMetadata.d.ts +1 -4
- package/lib/Types/Message.d.ts +348 -354
- package/lib/Types/Message.js +0 -4
- package/lib/Types/{Mex.d.ts → Newsletter.d.ts} +7 -17
- package/lib/Types/{Mex.js → Newsletter.js} +5 -3
- package/lib/Types/Signal.d.ts +1 -12
- package/lib/Types/Socket.d.ts +148 -135
- package/lib/Types/State.d.ts +0 -65
- package/lib/Types/State.js +0 -43
- package/lib/Types/index.d.ts +52 -52
- package/lib/Types/index.js +30 -23
- package/lib/Utils/auth-utils.d.ts +0 -5
- package/lib/Utils/auth-utils.js +0 -12
- package/lib/Utils/browser-utils.js +0 -1
- package/lib/Utils/crypto.d.ts +12 -12
- package/lib/Utils/decode-wa-message.d.ts +0 -18
- package/lib/Utils/decode-wa-message.js +0 -34
- package/lib/Utils/event-buffer.d.ts +0 -2
- package/lib/Utils/generics.d.ts +2 -3
- package/lib/Utils/generics.js +1 -10
- package/lib/Utils/history.d.ts +0 -2
- package/lib/Utils/history.js +9 -12
- package/lib/Utils/identity-change-handler.d.ts +0 -7
- package/lib/Utils/identity-change-handler.js +0 -1
- package/lib/Utils/index.d.ts +23 -30
- package/lib/Utils/index.js +3 -9
- package/lib/Utils/link-preview.js +2 -2
- package/lib/Utils/message-retry-manager.d.ts +0 -5
- package/lib/Utils/message-retry-manager.js +11 -11
- package/lib/Utils/messages-media.d.ts +13 -45
- package/lib/Utils/messages-media.js +98 -293
- package/lib/Utils/messages.d.ts +3 -16
- package/lib/Utils/messages.js +1123 -1219
- package/lib/Utils/noise-handler.d.ts +1 -1
- package/lib/Utils/process-message.js +1 -103
- package/lib/Utils/rich-messages.d.ts +277 -0
- package/lib/Utils/rich-messages.js +1144 -0
- package/lib/Utils/signal.d.ts +0 -13
- package/lib/Utils/signal.js +0 -42
- package/lib/Utils/sticker-pack.d.ts +10 -0
- package/lib/Utils/sticker-pack.js +111 -0
- package/lib/Utils/sync-action-utils.js +0 -1
- package/lib/Utils/tc-token-utils.d.ts +1 -26
- package/lib/Utils/tc-token-utils.js +5 -151
- package/lib/Utils/validate-connection.d.ts +0 -1
- package/lib/Utils/validate-connection.js +3 -13
- package/lib/WABinary/decode.d.ts +1 -1
- package/lib/WABinary/generic-utils.d.ts +3 -1
- package/lib/WABinary/generic-utils.js +78 -0
- package/lib/WABinary/jid-utils.d.ts +0 -11
- package/lib/WABinary/jid-utils.js +0 -33
- package/lib/WAM/encode.d.ts +1 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +3 -26
- package/lib/WAUSync/Protocols/index.d.ts +0 -1
- package/lib/WAUSync/USyncQuery.d.ts +0 -1
- package/lib/WAUSync/USyncQuery.js +1 -1
- package/lib/WAUSync/USyncUser.d.ts +0 -4
- package/lib/WAUSync/USyncUser.js +0 -8
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -4
- package/package.json +27 -31
- package/LICENSE +0 -21
- package/engine-requirements.js +0 -10
- package/lib/Defaults/index.d.ts.map +0 -1
- package/lib/Defaults/index.js.map +0 -1
- package/lib/Signal/Group/ciphertext-message.d.ts.map +0 -1
- package/lib/Signal/Group/ciphertext-message.js.map +0 -1
- package/lib/Signal/Group/group-session-builder.d.ts.map +0 -1
- package/lib/Signal/Group/group-session-builder.js.map +0 -1
- package/lib/Signal/Group/group_cipher.d.ts.map +0 -1
- package/lib/Signal/Group/group_cipher.js.map +0 -1
- package/lib/Signal/Group/index.d.ts.map +0 -1
- package/lib/Signal/Group/index.js.map +0 -1
- package/lib/Signal/Group/keyhelper.d.ts.map +0 -1
- package/lib/Signal/Group/keyhelper.js.map +0 -1
- package/lib/Signal/Group/sender-chain-key.d.ts.map +0 -1
- package/lib/Signal/Group/sender-chain-key.js.map +0 -1
- package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-distribution-message.js.map +0 -1
- package/lib/Signal/Group/sender-key-message.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-message.js.map +0 -1
- package/lib/Signal/Group/sender-key-name.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-name.js.map +0 -1
- package/lib/Signal/Group/sender-key-record.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-record.js.map +0 -1
- package/lib/Signal/Group/sender-key-state.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-state.js.map +0 -1
- package/lib/Signal/Group/sender-message-key.d.ts.map +0 -1
- package/lib/Signal/Group/sender-message-key.js.map +0 -1
- package/lib/Signal/libsignal.d.ts.map +0 -1
- package/lib/Signal/libsignal.js.map +0 -1
- package/lib/Signal/lid-mapping.d.ts.map +0 -1
- package/lib/Signal/lid-mapping.js.map +0 -1
- package/lib/Socket/Client/index.d.ts.map +0 -1
- package/lib/Socket/Client/index.js.map +0 -1
- package/lib/Socket/Client/types.d.ts.map +0 -1
- package/lib/Socket/Client/types.js.map +0 -1
- package/lib/Socket/Client/websocket.d.ts.map +0 -1
- package/lib/Socket/Client/websocket.js.map +0 -1
- package/lib/Socket/business.d.ts.map +0 -1
- package/lib/Socket/business.js.map +0 -1
- package/lib/Socket/chats.d.ts.map +0 -1
- package/lib/Socket/chats.js.map +0 -1
- package/lib/Socket/communities.d.ts.map +0 -1
- package/lib/Socket/communities.js.map +0 -1
- package/lib/Socket/groups.d.ts.map +0 -1
- package/lib/Socket/groups.js.map +0 -1
- package/lib/Socket/index.d.ts.map +0 -1
- package/lib/Socket/index.js.map +0 -1
- package/lib/Socket/messages-recv.d.ts.map +0 -1
- package/lib/Socket/messages-recv.js.map +0 -1
- package/lib/Socket/messages-send.d.ts.map +0 -1
- package/lib/Socket/messages-send.js.map +0 -1
- package/lib/Socket/mex.d.ts.map +0 -1
- package/lib/Socket/mex.js.map +0 -1
- package/lib/Socket/newsletter.d.ts.map +0 -1
- package/lib/Socket/newsletter.js.map +0 -1
- package/lib/Socket/smgss.d.ts +0 -119
- package/lib/Socket/smgss.d.ts.map +0 -1
- package/lib/Socket/smgss.js +0 -453
- package/lib/Socket/smgss.js.map +0 -1
- package/lib/Socket/socket.d.ts.map +0 -1
- package/lib/Socket/socket.js.map +0 -1
- package/lib/Store/index.d.ts +0 -4
- package/lib/Store/index.d.ts.map +0 -1
- package/lib/Store/index.js +0 -4
- package/lib/Store/index.js.map +0 -1
- package/lib/Store/make-in-memory-store.d.ts +0 -132
- package/lib/Store/make-in-memory-store.d.ts.map +0 -1
- package/lib/Store/make-in-memory-store.js +0 -450
- package/lib/Store/make-in-memory-store.js.map +0 -1
- package/lib/Store/make-ordered-dictionary.d.ts +0 -14
- package/lib/Store/make-ordered-dictionary.d.ts.map +0 -1
- package/lib/Store/make-ordered-dictionary.js +0 -80
- package/lib/Store/make-ordered-dictionary.js.map +0 -1
- package/lib/Store/object-repository.d.ts +0 -13
- package/lib/Store/object-repository.d.ts.map +0 -1
- package/lib/Store/object-repository.js +0 -24
- package/lib/Store/object-repository.js.map +0 -1
- package/lib/Types/Auth.d.ts.map +0 -1
- package/lib/Types/Auth.js.map +0 -1
- package/lib/Types/Bussines.d.ts.map +0 -1
- package/lib/Types/Bussines.js.map +0 -1
- package/lib/Types/Call.d.ts.map +0 -1
- package/lib/Types/Call.js.map +0 -1
- package/lib/Types/Chat.d.ts.map +0 -1
- package/lib/Types/Chat.js.map +0 -1
- package/lib/Types/Contact.d.ts.map +0 -1
- package/lib/Types/Contact.js.map +0 -1
- package/lib/Types/Events.d.ts.map +0 -1
- package/lib/Types/Events.js.map +0 -1
- package/lib/Types/GroupMetadata.d.ts.map +0 -1
- package/lib/Types/GroupMetadata.js.map +0 -1
- package/lib/Types/Label.d.ts.map +0 -1
- package/lib/Types/Label.js.map +0 -1
- package/lib/Types/LabelAssociation.d.ts.map +0 -1
- package/lib/Types/LabelAssociation.js.map +0 -1
- package/lib/Types/Message.d.ts.map +0 -1
- package/lib/Types/Message.js.map +0 -1
- package/lib/Types/Mex.d.ts.map +0 -1
- package/lib/Types/Mex.js.map +0 -1
- package/lib/Types/Product.d.ts.map +0 -1
- package/lib/Types/Product.js.map +0 -1
- package/lib/Types/Signal.d.ts.map +0 -1
- package/lib/Types/Signal.js.map +0 -1
- package/lib/Types/Socket.d.ts.map +0 -1
- package/lib/Types/Socket.js.map +0 -1
- package/lib/Types/State.d.ts.map +0 -1
- package/lib/Types/State.js.map +0 -1
- package/lib/Types/USync.d.ts.map +0 -1
- package/lib/Types/USync.js.map +0 -1
- package/lib/Types/index.d.ts.map +0 -1
- package/lib/Types/index.js.map +0 -1
- package/lib/Utils/auth-utils.d.ts.map +0 -1
- package/lib/Utils/auth-utils.js.map +0 -1
- package/lib/Utils/browser-utils.d.ts.map +0 -1
- package/lib/Utils/browser-utils.js.map +0 -1
- package/lib/Utils/business.d.ts.map +0 -1
- package/lib/Utils/business.js.map +0 -1
- package/lib/Utils/button-handler.d.ts +0 -28
- package/lib/Utils/button-handler.d.ts.map +0 -1
- package/lib/Utils/button-handler.js +0 -140
- package/lib/Utils/button-handler.js.map +0 -1
- package/lib/Utils/chat-utils.d.ts.map +0 -1
- package/lib/Utils/chat-utils.js.map +0 -1
- package/lib/Utils/companion-reg-client-utils.d.ts.map +0 -1
- package/lib/Utils/companion-reg-client-utils.js.map +0 -1
- package/lib/Utils/crypto.d.ts.map +0 -1
- package/lib/Utils/crypto.js.map +0 -1
- package/lib/Utils/decode-wa-message.d.ts.map +0 -1
- package/lib/Utils/decode-wa-message.js.map +0 -1
- package/lib/Utils/event-buffer.d.ts.map +0 -1
- package/lib/Utils/event-buffer.js.map +0 -1
- package/lib/Utils/generics.d.ts.map +0 -1
- package/lib/Utils/generics.js.map +0 -1
- package/lib/Utils/history.d.ts.map +0 -1
- package/lib/Utils/history.js.map +0 -1
- package/lib/Utils/identity-change-handler.d.ts.map +0 -1
- package/lib/Utils/identity-change-handler.js.map +0 -1
- package/lib/Utils/index.d.ts.map +0 -1
- package/lib/Utils/index.js.map +0 -1
- package/lib/Utils/interactive.d.ts +0 -60
- package/lib/Utils/interactive.d.ts.map +0 -1
- package/lib/Utils/interactive.js +0 -224
- package/lib/Utils/interactive.js.map +0 -1
- package/lib/Utils/link-preview.d.ts.map +0 -1
- package/lib/Utils/link-preview.js.map +0 -1
- package/lib/Utils/logger.d.ts.map +0 -1
- package/lib/Utils/logger.js.map +0 -1
- package/lib/Utils/lt-hash.d.ts.map +0 -1
- package/lib/Utils/lt-hash.js.map +0 -1
- package/lib/Utils/make-mutex.d.ts.map +0 -1
- package/lib/Utils/make-mutex.js.map +0 -1
- package/lib/Utils/message-composer.d.ts +0 -93
- package/lib/Utils/message-composer.d.ts.map +0 -1
- package/lib/Utils/message-composer.js +0 -311
- package/lib/Utils/message-composer.js.map +0 -1
- package/lib/Utils/message-retry-manager.d.ts.map +0 -1
- package/lib/Utils/message-retry-manager.js.map +0 -1
- package/lib/Utils/messages-media.d.ts.map +0 -1
- package/lib/Utils/messages-media.js.map +0 -1
- package/lib/Utils/messages-newsletter.d.ts +0 -74
- package/lib/Utils/messages-newsletter.d.ts.map +0 -1
- package/lib/Utils/messages-newsletter.js +0 -128
- package/lib/Utils/messages-newsletter.js.map +0 -1
- package/lib/Utils/messages.d.ts.map +0 -1
- package/lib/Utils/messages.js.map +0 -1
- package/lib/Utils/noise-handler.d.ts.map +0 -1
- package/lib/Utils/noise-handler.js.map +0 -1
- package/lib/Utils/offline-node-processor.d.ts.map +0 -1
- package/lib/Utils/offline-node-processor.js.map +0 -1
- package/lib/Utils/pre-key-manager.d.ts.map +0 -1
- package/lib/Utils/pre-key-manager.js.map +0 -1
- package/lib/Utils/process-message.d.ts.map +0 -1
- package/lib/Utils/process-message.js.map +0 -1
- package/lib/Utils/reporting-utils.d.ts.map +0 -1
- package/lib/Utils/reporting-utils.js.map +0 -1
- package/lib/Utils/shortcake-crypto.d.ts +0 -36
- package/lib/Utils/shortcake-crypto.d.ts.map +0 -1
- package/lib/Utils/shortcake-crypto.js +0 -71
- package/lib/Utils/shortcake-crypto.js.map +0 -1
- package/lib/Utils/shortcake.d.ts +0 -36
- package/lib/Utils/shortcake.d.ts.map +0 -1
- package/lib/Utils/shortcake.js +0 -162
- package/lib/Utils/shortcake.js.map +0 -1
- package/lib/Utils/signal.d.ts.map +0 -1
- package/lib/Utils/signal.js.map +0 -1
- package/lib/Utils/stanza-ack.d.ts.map +0 -1
- package/lib/Utils/stanza-ack.js.map +0 -1
- package/lib/Utils/sync-action-utils.d.ts.map +0 -1
- package/lib/Utils/sync-action-utils.js.map +0 -1
- package/lib/Utils/tc-token-utils.d.ts.map +0 -1
- package/lib/Utils/tc-token-utils.js.map +0 -1
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +0 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +0 -1
- package/lib/Utils/use-single-file-auth-state.d.ts +0 -6
- package/lib/Utils/use-single-file-auth-state.d.ts.map +0 -1
- package/lib/Utils/use-single-file-auth-state.js +0 -111
- package/lib/Utils/use-single-file-auth-state.js.map +0 -1
- package/lib/Utils/use-sqlite-auth-state.d.ts +0 -14
- package/lib/Utils/use-sqlite-auth-state.d.ts.map +0 -1
- package/lib/Utils/use-sqlite-auth-state.js +0 -107
- package/lib/Utils/use-sqlite-auth-state.js.map +0 -1
- package/lib/Utils/validate-connection.d.ts.map +0 -1
- package/lib/Utils/validate-connection.js.map +0 -1
- package/lib/WABinary/constants.d.ts.map +0 -1
- package/lib/WABinary/constants.js.map +0 -1
- package/lib/WABinary/decode.d.ts.map +0 -1
- package/lib/WABinary/decode.js.map +0 -1
- package/lib/WABinary/encode.d.ts.map +0 -1
- package/lib/WABinary/encode.js.map +0 -1
- package/lib/WABinary/generic-utils.d.ts.map +0 -1
- package/lib/WABinary/generic-utils.js.map +0 -1
- package/lib/WABinary/index.d.ts.map +0 -1
- package/lib/WABinary/index.js.map +0 -1
- package/lib/WABinary/jid-utils.d.ts.map +0 -1
- package/lib/WABinary/jid-utils.js.map +0 -1
- package/lib/WABinary/types.d.ts.map +0 -1
- package/lib/WABinary/types.js.map +0 -1
- package/lib/WAM/BinaryInfo.d.ts.map +0 -1
- package/lib/WAM/BinaryInfo.js.map +0 -1
- package/lib/WAM/constants.d.ts.map +0 -1
- package/lib/WAM/constants.js.map +0 -1
- package/lib/WAM/encode.d.ts.map +0 -1
- package/lib/WAM/encode.js.map +0 -1
- package/lib/WAM/index.d.ts.map +0 -1
- package/lib/WAM/index.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/index.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/index.js.map +0 -1
- package/lib/WAUSync/USyncQuery.d.ts.map +0 -1
- package/lib/WAUSync/USyncQuery.js.map +0 -1
- package/lib/WAUSync/USyncUser.d.ts.map +0 -1
- package/lib/WAUSync/USyncUser.js.map +0 -1
- package/lib/WAUSync/index.d.ts.map +0 -1
- package/lib/WAUSync/index.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
|
@@ -4,56 +4,18 @@ import { randomBytes } from 'crypto';
|
|
|
4
4
|
import Long from 'long';
|
|
5
5
|
import { proto } from '../../WAProto/index.js';
|
|
6
6
|
import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT, PLACEHOLDER_MAX_AGE_SECONDS, STATUS_EXPIRY_SECONDS } from '../Defaults/index.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
|
|
8
|
+
import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, extractAddressingContext, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, handleIdentityChange, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, toNumber, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
|
|
9
9
|
import { makeMutex } from '../Utils/make-mutex.js';
|
|
10
10
|
import { makeOfflineNodeProcessor } from '../Utils/offline-node-processor.js';
|
|
11
11
|
import { buildAckStanza } from '../Utils/stanza-ack.js';
|
|
12
|
-
import {
|
|
13
|
-
import { areJidsSameUser, binaryNodeToString, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, getBinaryNodeChildUInt, isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
12
|
+
import { areJidsSameUser, binaryNodeToString, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
14
13
|
import { extractGroupMetadata } from './groups.js';
|
|
15
14
|
import { makeMessagesSocket } from './messages-send.js';
|
|
16
|
-
const ENFORCEMENT_TYPE_VALUES = new Set(Object.values(ReachoutTimelockEnforcementType));
|
|
17
|
-
function isValidEnforcementType(value) {
|
|
18
|
-
return typeof value === 'string' && ENFORCEMENT_TYPE_VALUES.has(value);
|
|
19
|
-
}
|
|
20
15
|
export const makeMessagesRecvSocket = (config) => {
|
|
21
16
|
const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
|
|
22
17
|
const sock = makeMessagesSocket(config);
|
|
23
|
-
const {
|
|
24
|
-
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
25
|
-
/** Shortcake passkey-linking flow, only when a `signPasskeyAssertion` is configured. */
|
|
26
|
-
const shortcakeFlow = config.signPasskeyAssertion
|
|
27
|
-
? makeShortcakeFlow({
|
|
28
|
-
logger,
|
|
29
|
-
query,
|
|
30
|
-
signAssertion: config.signPasskeyAssertion,
|
|
31
|
-
getCreds: () => authState.creds,
|
|
32
|
-
updateCreds: patch => ev.emit('creds.update', patch),
|
|
33
|
-
deviceType: getPlatformType(config.browser[1]),
|
|
34
|
-
emitVerificationCode: code => logger.debug({ code }, 'shortcake verification code')
|
|
35
|
-
})
|
|
36
|
-
: null;
|
|
37
|
-
registerSocketEndHandler(() => shortcakeFlow?.clearSession());
|
|
38
|
-
/** Serializes prologue -> continuation off the notification mutex, so the ack isn't blocked by the handshake. */
|
|
39
|
-
const shortcakeMutex = makeMutex();
|
|
40
|
-
/** Runs the forced-passkey handshake (detached); with no signer, surfaces `passkeyRequired` and warns. */
|
|
41
|
-
const handleShortcakeNotification = (node) => {
|
|
42
|
-
if (node.attrs.type === 'passkey_prologue_request') {
|
|
43
|
-
ev.emit('connection.update', { passkeyRequired: { hasSigner: !!shortcakeFlow } });
|
|
44
|
-
}
|
|
45
|
-
if (!shortcakeFlow) {
|
|
46
|
-
if (node.attrs.type === 'passkey_prologue_request') {
|
|
47
|
-
logger.warn({ id: node.attrs.id }, 'server requested passkey prologue but no signPasskeyAssertion configured');
|
|
48
|
-
}
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
// run detached + serialized: the notification is acked immediately, and the
|
|
52
|
-
// handshake (external signer + follow-up IQs) never blocks other notifications
|
|
53
|
-
shortcakeMutex
|
|
54
|
-
.mutex(() => shortcakeFlow.handleIncomingNotification(node))
|
|
55
|
-
.catch(err => logger.error({ err, id: node.attrs.id }, 'shortcake handshake failed'));
|
|
56
|
-
};
|
|
18
|
+
const { ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager } = sock;
|
|
57
19
|
/** this mutex ensures that each retryRequest will wait for the previous one to finish */
|
|
58
20
|
const retryMutex = makeMutex();
|
|
59
21
|
const msgRetryCache = config.msgRetryCounterCache ||
|
|
@@ -66,6 +28,11 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
66
28
|
stdTTL: DEFAULT_CACHE_TTLS.CALL_OFFER, // 5 mins
|
|
67
29
|
useClones: false
|
|
68
30
|
});
|
|
31
|
+
const placeholderResendCache = config.placeholderResendCache ||
|
|
32
|
+
new NodeCache({
|
|
33
|
+
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
34
|
+
useClones: false
|
|
35
|
+
});
|
|
69
36
|
// Debounce identity-change session refreshes per JID to avoid bursts
|
|
70
37
|
const identityAssertDebounce = new NodeCache({ stdTTL: 5, useClones: false });
|
|
71
38
|
let sendActiveReceipts = false;
|
|
@@ -119,140 +86,25 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
119
86
|
}, 8000);
|
|
120
87
|
return sendPeerDataOperationMessage(pdoMessage);
|
|
121
88
|
};
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (updateNode) {
|
|
125
|
-
const opName = updateNode.attrs?.op_name;
|
|
126
|
-
if (!opName) {
|
|
127
|
-
logger.warn({ node: binaryNodeToString(node) }, 'mex notification missing op_name, fallback to legacy');
|
|
128
|
-
await handleLegacyMexNewsletterNotification(node);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
let mexResponse;
|
|
132
|
-
try {
|
|
133
|
-
mexResponse = JSON.parse(updateNode.content.toString());
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
logger.error({ err: error, opName }, 'failed to parse mex notification JSON');
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (mexResponse.errors?.length) {
|
|
140
|
-
logger.warn({ errors: mexResponse.errors, opName }, 'mex notification has GQL errors');
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
const data = mexResponse.data;
|
|
144
|
-
if (!data) {
|
|
145
|
-
logger.warn({ opName }, 'mex notification has null data');
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
logger.debug({ opName }, 'processing mex notification');
|
|
149
|
-
switch (opName) {
|
|
150
|
-
case 'NotificationUserReachoutTimelockUpdate':
|
|
151
|
-
handleReachoutTimelockNotification(data);
|
|
152
|
-
break;
|
|
153
|
-
case 'MessageCappingInfoNotification':
|
|
154
|
-
handleMessageCappingNotification(data);
|
|
155
|
-
break;
|
|
156
|
-
// newsletter ops still use the legacy <mex> child structure
|
|
157
|
-
case 'NotificationNewsletterUpdate':
|
|
158
|
-
case 'NotificationLinkedProfilesUpdates':
|
|
159
|
-
case 'NotificationNewsletterAdminPromote':
|
|
160
|
-
case 'NotificationNewsletterAdminDemote':
|
|
161
|
-
case 'NotificationNewsletterUserSettingChange':
|
|
162
|
-
case 'NotificationNewsletterJoin':
|
|
163
|
-
case 'NotificationNewsletterLeave':
|
|
164
|
-
case 'NotificationNewsletterStateChange':
|
|
165
|
-
case 'NotificationNewsletterAdminMetadataUpdate':
|
|
166
|
-
case 'NotificationNewsletterOwnerUpdate':
|
|
167
|
-
case 'NotificationNewsletterAdminInviteRevoke':
|
|
168
|
-
case 'NotificationNewsletterWamoSubStatusChange':
|
|
169
|
-
case 'NotificationNewsletterBlockUser':
|
|
170
|
-
case 'NotificationNewsletterPaidPartnership':
|
|
171
|
-
case 'NotificationNewsletterMilestone':
|
|
172
|
-
case 'NewsletterResponseStateUpdate':
|
|
173
|
-
await handleLegacyMexNewsletterNotification(node);
|
|
174
|
-
break;
|
|
175
|
-
default:
|
|
176
|
-
logger.debug({ opName }, 'unhandled mex notification');
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
await handleLegacyMexNewsletterNotification(node);
|
|
182
|
-
};
|
|
183
|
-
const handleReachoutTimelockNotification = (data) => {
|
|
184
|
-
const payload = data.xwa2_notify_account_reachout_timelock;
|
|
185
|
-
if (!payload) {
|
|
186
|
-
logger.warn('reachout timelock notification missing payload');
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (!payload.is_active) {
|
|
190
|
-
logger.info('reachout timelock restriction lifted');
|
|
191
|
-
ev.emit('connection.update', {
|
|
192
|
-
reachoutTimeLock: {
|
|
193
|
-
isActive: false,
|
|
194
|
-
enforcementType: ReachoutTimelockEnforcementType.DEFAULT
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
// WA Web defaults to now+60s when the server omits the expiry
|
|
200
|
-
const timeEnforcementEnds = payload.time_enforcement_ends
|
|
201
|
-
? new Date(parseInt(payload.time_enforcement_ends, 10) * 1000)
|
|
202
|
-
: new Date(Date.now() + 60000);
|
|
203
|
-
const enforcementType = isValidEnforcementType(payload.enforcement_type)
|
|
204
|
-
? payload.enforcement_type
|
|
205
|
-
: ReachoutTimelockEnforcementType.DEFAULT;
|
|
206
|
-
logger.info({ enforcementType, timeEnforcementEnds }, 'reachout timelock restriction set');
|
|
207
|
-
ev.emit('connection.update', {
|
|
208
|
-
reachoutTimeLock: {
|
|
209
|
-
isActive: true,
|
|
210
|
-
timeEnforcementEnds,
|
|
211
|
-
enforcementType
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
};
|
|
215
|
-
const handleMessageCappingNotification = (data) => {
|
|
216
|
-
const payload = data.xwa2_notify_new_chat_messages_capping_info_update;
|
|
217
|
-
if (!payload) {
|
|
218
|
-
logger.warn('message capping notification missing payload');
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
logger.info({ payload }, 'received message capping update');
|
|
222
|
-
ev.emit('message-capping.update', payload);
|
|
223
|
-
};
|
|
224
|
-
const handleLegacyMexNewsletterNotification = async (node) => {
|
|
89
|
+
// Handles mex newsletter notifications
|
|
90
|
+
const handleMexNewsletterNotification = async (node) => {
|
|
225
91
|
const mexNode = getBinaryNodeChild(node, 'mex');
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (!payloadNode?.content) {
|
|
229
|
-
logger.warn({ node: binaryNodeToString(node) }, 'invalid mex newsletter notification');
|
|
92
|
+
if (!mexNode?.content) {
|
|
93
|
+
logger.warn({ node }, 'Invalid mex newsletter notification');
|
|
230
94
|
return;
|
|
231
95
|
}
|
|
232
96
|
let data;
|
|
233
97
|
try {
|
|
234
|
-
|
|
235
|
-
if (Array.isArray(payloadContent)) {
|
|
236
|
-
logger.warn({ payloadNode }, 'invalid mex newsletter notification payload format');
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
const contentBuf = typeof payloadContent === 'string' ? Buffer.from(payloadContent, 'binary') : Buffer.from(payloadContent);
|
|
240
|
-
data = JSON.parse(contentBuf.toString());
|
|
98
|
+
data = JSON.parse(mexNode.content.toString());
|
|
241
99
|
}
|
|
242
100
|
catch (error) {
|
|
243
|
-
logger.error({ err: error, node
|
|
101
|
+
logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
|
|
244
102
|
return;
|
|
245
103
|
}
|
|
246
|
-
const operation = data?.operation
|
|
247
|
-
|
|
248
|
-
if (!updates) {
|
|
249
|
-
const linkedProfiles = data?.data?.xwa2_notify_linked_profiles;
|
|
250
|
-
if (linkedProfiles) {
|
|
251
|
-
updates = [linkedProfiles];
|
|
252
|
-
}
|
|
253
|
-
}
|
|
104
|
+
const operation = data?.operation;
|
|
105
|
+
const updates = data?.updates;
|
|
254
106
|
if (!updates || !operation) {
|
|
255
|
-
logger.warn({ data }, '
|
|
107
|
+
logger.warn({ data }, 'Invalid mex newsletter notification content');
|
|
256
108
|
return;
|
|
257
109
|
}
|
|
258
110
|
logger.info({ operation, updates }, 'got mex newsletter notification');
|
|
@@ -280,114 +132,91 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
280
132
|
}
|
|
281
133
|
}
|
|
282
134
|
break;
|
|
283
|
-
case 'NotificationLinkedProfilesUpdates':
|
|
284
|
-
for (const update of updates) {
|
|
285
|
-
const lid = update?.jid;
|
|
286
|
-
const addedProfiles = Array.isArray(update?.added_profiles) ? update.added_profiles : [];
|
|
287
|
-
const mappings = [];
|
|
288
|
-
for (const profile of addedProfiles) {
|
|
289
|
-
const pn = typeof profile === 'string' ? profile : (profile?.pn ?? profile?.jid ?? null);
|
|
290
|
-
if (lid && pn) {
|
|
291
|
-
const mapping = { lid, pn };
|
|
292
|
-
ev.emit('lid-mapping.update', mapping);
|
|
293
|
-
mappings.push(mapping);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
await signalRepository.lidMapping.storeLIDPNMappings(mappings);
|
|
297
|
-
}
|
|
298
|
-
break;
|
|
299
135
|
default:
|
|
300
|
-
logger.info({ operation, data }, '
|
|
136
|
+
logger.info({ operation, data }, 'Unhandled mex newsletter notification');
|
|
301
137
|
break;
|
|
302
138
|
}
|
|
303
139
|
};
|
|
304
140
|
// Handles newsletter notifications
|
|
305
141
|
const handleNewsletterNotification = async (node) => {
|
|
306
142
|
const from = node.attrs.from;
|
|
307
|
-
const
|
|
143
|
+
const child = getAllBinaryNodeChildren(node)[0];
|
|
308
144
|
const author = node.attrs.participant;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
145
|
+
logger.info({ from, child }, 'got newsletter notification');
|
|
146
|
+
switch (child.tag) {
|
|
147
|
+
case 'reaction':
|
|
148
|
+
const reactionUpdate = {
|
|
149
|
+
id: from,
|
|
150
|
+
server_id: child.attrs.message_id,
|
|
151
|
+
reaction: {
|
|
152
|
+
code: getBinaryNodeChildString(child, 'reaction'),
|
|
153
|
+
count: 1
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
ev.emit('newsletter.reaction', reactionUpdate);
|
|
157
|
+
break;
|
|
158
|
+
case 'view':
|
|
159
|
+
const viewUpdate = {
|
|
160
|
+
id: from,
|
|
161
|
+
server_id: child.attrs.message_id,
|
|
162
|
+
count: parseInt(child.content?.toString() || '0', 10)
|
|
163
|
+
};
|
|
164
|
+
ev.emit('newsletter.view', viewUpdate);
|
|
165
|
+
break;
|
|
166
|
+
case 'participant':
|
|
167
|
+
const participantUpdate = {
|
|
168
|
+
id: from,
|
|
169
|
+
author,
|
|
170
|
+
user: child.attrs.jid,
|
|
171
|
+
action: child.attrs.action,
|
|
172
|
+
new_role: child.attrs.role
|
|
173
|
+
};
|
|
174
|
+
ev.emit('newsletter-participants.update', participantUpdate);
|
|
175
|
+
break;
|
|
176
|
+
case 'update':
|
|
177
|
+
const settingsNode = getBinaryNodeChild(child, 'settings');
|
|
178
|
+
if (settingsNode) {
|
|
179
|
+
const update = {};
|
|
180
|
+
const nameNode = getBinaryNodeChild(settingsNode, 'name');
|
|
181
|
+
if (nameNode?.content)
|
|
182
|
+
update.name = nameNode.content.toString();
|
|
183
|
+
const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
|
|
184
|
+
if (descriptionNode?.content)
|
|
185
|
+
update.description = descriptionNode.content.toString();
|
|
186
|
+
ev.emit('newsletter-settings.update', {
|
|
335
187
|
id: from,
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
action: child.attrs.action,
|
|
339
|
-
new_role: child.attrs.role
|
|
340
|
-
};
|
|
341
|
-
ev.emit('newsletter-participants.update', participantUpdate);
|
|
342
|
-
break;
|
|
188
|
+
update
|
|
189
|
+
});
|
|
343
190
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
191
|
+
break;
|
|
192
|
+
case 'message':
|
|
193
|
+
const plaintextNode = getBinaryNodeChild(child, 'plaintext');
|
|
194
|
+
if (plaintextNode?.content) {
|
|
195
|
+
try {
|
|
196
|
+
const contentBuf = typeof plaintextNode.content === 'string'
|
|
197
|
+
? Buffer.from(plaintextNode.content, 'binary')
|
|
198
|
+
: Buffer.from(plaintextNode.content);
|
|
199
|
+
const messageProto = proto.Message.decode(contentBuf).toJSON();
|
|
200
|
+
const fullMessage = proto.WebMessageInfo.fromObject({
|
|
201
|
+
key: {
|
|
202
|
+
remoteJid: from,
|
|
203
|
+
id: child.attrs.message_id || child.attrs.server_id,
|
|
204
|
+
fromMe: false // TODO: is this really true though
|
|
205
|
+
},
|
|
206
|
+
message: messageProto,
|
|
207
|
+
messageTimestamp: +child.attrs.t
|
|
208
|
+
}).toJSON();
|
|
209
|
+
await upsertMessage(fullMessage, 'append');
|
|
210
|
+
logger.info('Processed plaintext newsletter message');
|
|
358
211
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
case 'message': {
|
|
362
|
-
const plaintextNode = getBinaryNodeChild(child, 'plaintext');
|
|
363
|
-
if (plaintextNode?.content) {
|
|
364
|
-
try {
|
|
365
|
-
const contentBuf = typeof plaintextNode.content === 'string'
|
|
366
|
-
? Buffer.from(plaintextNode.content, 'binary')
|
|
367
|
-
: Buffer.from(plaintextNode.content);
|
|
368
|
-
const messageProto = proto.Message.decode(contentBuf).toJSON();
|
|
369
|
-
const fullMessage = proto.WebMessageInfo.fromObject({
|
|
370
|
-
key: {
|
|
371
|
-
remoteJid: from,
|
|
372
|
-
id: child.attrs.message_id || child.attrs.server_id,
|
|
373
|
-
fromMe: false // TODO: is this really true though
|
|
374
|
-
},
|
|
375
|
-
message: messageProto,
|
|
376
|
-
messageTimestamp: +child.attrs.t
|
|
377
|
-
}).toJSON();
|
|
378
|
-
await upsertMessage(fullMessage, 'append');
|
|
379
|
-
logger.debug('Processed plaintext newsletter message');
|
|
380
|
-
}
|
|
381
|
-
catch (error) {
|
|
382
|
-
logger.error({ error }, 'Failed to decode plaintext newsletter message');
|
|
383
|
-
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
logger.error({ error }, 'Failed to decode plaintext newsletter message');
|
|
384
214
|
}
|
|
385
|
-
break;
|
|
386
215
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
216
|
+
break;
|
|
217
|
+
default:
|
|
218
|
+
logger.warn({ node }, 'Unknown newsletter notification');
|
|
219
|
+
break;
|
|
391
220
|
}
|
|
392
221
|
};
|
|
393
222
|
const sendMessageAck = async (node, errorCode) => {
|
|
@@ -547,58 +376,15 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
547
376
|
logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
|
|
548
377
|
}, authState?.creds?.me?.id || 'sendRetryRequest');
|
|
549
378
|
};
|
|
550
|
-
// Mirrors WAWeb/Handle/PreKeyLow.js: skip a re-issued notification with the same stanza id.
|
|
551
|
-
const inFlightPreKeyLow = new Set();
|
|
552
|
-
/**
|
|
553
|
-
* Fire-and-forget tctoken re-issuance after a peer's device identity changed.
|
|
554
|
-
* Mirrors WAWebSendTcTokenWhenDeviceIdentityChange — runs in parallel with
|
|
555
|
-
* the session refresh (not after it).
|
|
556
|
-
*/
|
|
557
|
-
const reissueTcTokenAfterIdentityChange = (from) => {
|
|
558
|
-
void (async () => {
|
|
559
|
-
const normalizedJid = jidNormalizedUser(from);
|
|
560
|
-
const tcJid = await resolveTcTokenJid(normalizedJid, getLIDForPN);
|
|
561
|
-
const tcTokenData = await authState.keys.get('tctoken', [tcJid]);
|
|
562
|
-
const senderTs = tcTokenData?.[tcJid]?.senderTimestamp;
|
|
563
|
-
if (senderTs === null || senderTs === undefined || isTcTokenExpired(senderTs)) {
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken');
|
|
567
|
-
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
|
|
568
|
-
const issueJid = await resolveIssuanceJid(normalizedJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
|
|
569
|
-
const result = await issuePrivacyTokens([issueJid], senderTs);
|
|
570
|
-
await storeTcTokensFromIqResult({
|
|
571
|
-
result,
|
|
572
|
-
fallbackJid: tcJid,
|
|
573
|
-
keys: authState.keys,
|
|
574
|
-
getLIDForPN,
|
|
575
|
-
onNewJidStored: trackTcTokenJid
|
|
576
|
-
});
|
|
577
|
-
})().catch(err => {
|
|
578
|
-
logger.debug({ jid: from, err: err?.message }, 'failed to re-issue tctoken after identity change');
|
|
579
|
-
});
|
|
580
|
-
};
|
|
581
379
|
const handleEncryptNotification = async (node) => {
|
|
582
380
|
const from = node.attrs.from;
|
|
583
381
|
if (from === S_WHATSAPP_NET) {
|
|
584
|
-
const stanzaId = node.attrs.id;
|
|
585
|
-
if (stanzaId && inFlightPreKeyLow.has(stanzaId)) {
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
382
|
const countChild = getBinaryNodeChild(node, 'count');
|
|
589
383
|
const count = +countChild.attrs.value;
|
|
590
384
|
const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT;
|
|
591
385
|
logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count');
|
|
592
386
|
if (shouldUploadMorePreKeys) {
|
|
593
|
-
|
|
594
|
-
inFlightPreKeyLow.add(stanzaId);
|
|
595
|
-
try {
|
|
596
|
-
await uploadPreKeys();
|
|
597
|
-
}
|
|
598
|
-
finally {
|
|
599
|
-
if (stanzaId)
|
|
600
|
-
inFlightPreKeyLow.delete(stanzaId);
|
|
601
|
-
}
|
|
387
|
+
await uploadPreKeys();
|
|
602
388
|
}
|
|
603
389
|
}
|
|
604
390
|
else {
|
|
@@ -608,8 +394,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
608
394
|
validateSession: signalRepository.validateSession,
|
|
609
395
|
assertSessions,
|
|
610
396
|
debounceCache: identityAssertDebounce,
|
|
611
|
-
logger
|
|
612
|
-
onBeforeSessionRefresh: reissueTcTokenAfterIdentityChange
|
|
397
|
+
logger
|
|
613
398
|
});
|
|
614
399
|
if (result.action === 'no_identity_node') {
|
|
615
400
|
logger.info({ node }, 'unknown encrypt notification');
|
|
@@ -620,7 +405,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
620
405
|
// TODO: Support PN/LID (Here is only LID now)
|
|
621
406
|
const actingParticipantLid = fullNode.attrs.participant;
|
|
622
407
|
const actingParticipantPn = fullNode.attrs.participant_pn;
|
|
623
|
-
const actingParticipantUsername = fullNode.attrs.participant_username;
|
|
624
408
|
const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid;
|
|
625
409
|
const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
|
|
626
410
|
switch (child?.tag) {
|
|
@@ -640,8 +424,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
640
424
|
{
|
|
641
425
|
...metadata,
|
|
642
426
|
author: actingParticipantLid,
|
|
643
|
-
authorPn: actingParticipantPn
|
|
644
|
-
authorUsername: actingParticipantUsername
|
|
427
|
+
authorPn: actingParticipantPn
|
|
645
428
|
}
|
|
646
429
|
]);
|
|
647
430
|
break;
|
|
@@ -667,13 +450,11 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
667
450
|
const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`;
|
|
668
451
|
msg.messageStubType = WAMessageStubType[stubType];
|
|
669
452
|
const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
|
|
670
|
-
|
|
671
|
-
const hasPn = isPnUser(attrs.phone_number);
|
|
453
|
+
// TODO: Store LID MAPPINGS
|
|
672
454
|
return {
|
|
673
|
-
id:
|
|
674
|
-
phoneNumber:
|
|
675
|
-
lid:
|
|
676
|
-
username: attrs.participant_username || attrs.username || undefined,
|
|
455
|
+
id: attrs.jid,
|
|
456
|
+
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
|
|
457
|
+
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
|
|
677
458
|
admin: (attrs.type || null)
|
|
678
459
|
};
|
|
679
460
|
});
|
|
@@ -743,89 +524,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
743
524
|
break;
|
|
744
525
|
}
|
|
745
526
|
};
|
|
746
|
-
const handleDevicesNotification = async (node) => {
|
|
747
|
-
const [child] = getAllBinaryNodeChildren(node);
|
|
748
|
-
const from = jidNormalizedUser(node.attrs.from);
|
|
749
|
-
if (!child) {
|
|
750
|
-
logger.debug({ from }, 'devices notification missing child, skipping');
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
const tag = child.tag;
|
|
754
|
-
const deviceHash = child.attrs.device_hash;
|
|
755
|
-
const devices = getBinaryNodeChildren(child, 'device');
|
|
756
|
-
if (areJidsSameUser(from, authState.creds.me.id) || areJidsSameUser(from, authState.creds.me.lid)) {
|
|
757
|
-
const deviceJids = devices.map(d => d.attrs.jid);
|
|
758
|
-
logger.info({ deviceJids }, 'got my own devices');
|
|
759
|
-
}
|
|
760
|
-
if (!devices.length) {
|
|
761
|
-
logger.debug({ from, tag }, 'no devices in notification, skipping');
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
const decoded = [];
|
|
765
|
-
for (const d of devices) {
|
|
766
|
-
const jid = d.attrs.jid;
|
|
767
|
-
if (!jid)
|
|
768
|
-
continue;
|
|
769
|
-
const parts = jidDecode(jid);
|
|
770
|
-
if (!parts) {
|
|
771
|
-
logger.debug({ jid }, 'failed to decode device jid, skipping');
|
|
772
|
-
continue;
|
|
773
|
-
}
|
|
774
|
-
decoded.push({ jid, user: parts.user, server: parts.server, device: parts.device });
|
|
775
|
-
}
|
|
776
|
-
if (!decoded.length)
|
|
777
|
-
return;
|
|
778
|
-
await devicesMutex.mutex(async () => {
|
|
779
|
-
const byUser = new Map();
|
|
780
|
-
for (const d of decoded) {
|
|
781
|
-
const list = byUser.get(d.user) || [];
|
|
782
|
-
list.push(d);
|
|
783
|
-
byUser.set(d.user, list);
|
|
784
|
-
}
|
|
785
|
-
for (const [user, entries] of byUser) {
|
|
786
|
-
if (tag === 'update') {
|
|
787
|
-
logger.debug({ user }, `${user}'s device list updated, dropping cached devices`);
|
|
788
|
-
await userDevicesCache?.del(user);
|
|
789
|
-
continue;
|
|
790
|
-
}
|
|
791
|
-
if (tag === 'remove') {
|
|
792
|
-
await signalRepository.deleteSession(entries.map(e => e.jid));
|
|
793
|
-
}
|
|
794
|
-
const existingCache = (await userDevicesCache?.get(user)) || [];
|
|
795
|
-
if (!existingCache.length) {
|
|
796
|
-
// No baseline yet; skip applying the delta so getUSyncDevices can
|
|
797
|
-
// later fetch the full device list. Caching just the notification
|
|
798
|
-
// entries would make a partial list look authoritative.
|
|
799
|
-
logger.debug({ user, tag }, 'device list not cached, deferring to USync refresh');
|
|
800
|
-
continue;
|
|
801
|
-
}
|
|
802
|
-
const affected = new Set(entries.map(e => e.device));
|
|
803
|
-
let updatedDevices;
|
|
804
|
-
switch (tag) {
|
|
805
|
-
case 'add':
|
|
806
|
-
logger.info({ deviceHash, count: entries.length }, 'devices added');
|
|
807
|
-
updatedDevices = [
|
|
808
|
-
...existingCache.filter(d => !affected.has(d.device)),
|
|
809
|
-
...entries.map(e => ({ user: e.user, server: e.server, device: e.device }))
|
|
810
|
-
];
|
|
811
|
-
break;
|
|
812
|
-
case 'remove':
|
|
813
|
-
logger.info({ deviceHash, count: entries.length }, 'devices removed');
|
|
814
|
-
updatedDevices = existingCache.filter(d => !affected.has(d.device));
|
|
815
|
-
break;
|
|
816
|
-
default:
|
|
817
|
-
logger.debug({ tag }, 'Unknown device list change tag');
|
|
818
|
-
continue;
|
|
819
|
-
}
|
|
820
|
-
if (updatedDevices.length === 0) {
|
|
821
|
-
await userDevicesCache?.del(user);
|
|
822
|
-
}
|
|
823
|
-
else {
|
|
824
|
-
await userDevicesCache?.set(user, updatedDevices);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
});
|
|
828
|
-
};
|
|
829
527
|
const processNotification = async (node) => {
|
|
830
528
|
const result = {};
|
|
831
529
|
const [child] = getAllBinaryNodeChildren(node);
|
|
@@ -836,7 +534,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
836
534
|
await handleNewsletterNotification(node);
|
|
837
535
|
break;
|
|
838
536
|
case 'mex':
|
|
839
|
-
await
|
|
537
|
+
await handleMexNewsletterNotification(node);
|
|
840
538
|
break;
|
|
841
539
|
case 'w:gp2':
|
|
842
540
|
// TODO: HANDLE PARTICIPANT_PN
|
|
@@ -850,12 +548,13 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
850
548
|
await handleEncryptNotification(node);
|
|
851
549
|
break;
|
|
852
550
|
case 'devices':
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
logger.
|
|
551
|
+
const devices = getBinaryNodeChildren(child, 'device');
|
|
552
|
+
if (areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
|
|
553
|
+
areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
|
|
554
|
+
const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }));
|
|
555
|
+
logger.info({ deviceData }, 'my own devices changed');
|
|
858
556
|
}
|
|
557
|
+
//TODO: drop a new event, add hashes
|
|
859
558
|
break;
|
|
860
559
|
case 'server_sync':
|
|
861
560
|
const update = getBinaryNodeChild(node, 'collection');
|
|
@@ -973,10 +672,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
973
672
|
authState.creds.registered = true;
|
|
974
673
|
ev.emit('creds.update', authState.creds);
|
|
975
674
|
break;
|
|
976
|
-
case 'passkey_prologue_request':
|
|
977
|
-
case 'crsc_continuation':
|
|
978
|
-
handleShortcakeNotification(node);
|
|
979
|
-
break;
|
|
980
675
|
case 'privacy_token':
|
|
981
676
|
await handlePrivacyTokenNotification(node);
|
|
982
677
|
break;
|
|
@@ -985,70 +680,27 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
985
680
|
return result;
|
|
986
681
|
}
|
|
987
682
|
};
|
|
988
|
-
/**
|
|
989
|
-
* In-memory cache of storage JIDs with stored tctokens, seeded from the persisted index.
|
|
990
|
-
* Used to coalesce writes during a session; pruning always re-reads the persisted index
|
|
991
|
-
* to cover writes made by other layers (e.g. history sync).
|
|
992
|
-
*/
|
|
993
|
-
const tcTokenKnownJids = new Set();
|
|
994
|
-
const tcTokenIndexLoaded = (async () => {
|
|
995
|
-
try {
|
|
996
|
-
const jids = await readTcTokenIndex(authState.keys);
|
|
997
|
-
for (const jid of jids)
|
|
998
|
-
tcTokenKnownJids.add(jid);
|
|
999
|
-
logger.debug({ count: tcTokenKnownJids.size }, 'loaded tctoken index');
|
|
1000
|
-
}
|
|
1001
|
-
catch (err) {
|
|
1002
|
-
logger.warn({ err: err?.message }, 'failed to load tctoken index');
|
|
1003
|
-
}
|
|
1004
|
-
})();
|
|
1005
|
-
let tcTokenIndexTimer;
|
|
1006
|
-
async function flushTcTokenIndex() {
|
|
1007
|
-
if (tcTokenIndexTimer) {
|
|
1008
|
-
clearTimeout(tcTokenIndexTimer);
|
|
1009
|
-
tcTokenIndexTimer = undefined;
|
|
1010
|
-
}
|
|
1011
|
-
// Merge with whatever is already persisted so we don't clobber writes from other
|
|
1012
|
-
// paths (history sync, concurrent sessions on the same store).
|
|
1013
|
-
const write = await buildMergedTcTokenIndexWrite(authState.keys, tcTokenKnownJids);
|
|
1014
|
-
return authState.keys.set({ tctoken: write });
|
|
1015
|
-
}
|
|
1016
|
-
function scheduleTcTokenIndexSave() {
|
|
1017
|
-
if (tcTokenIndexTimer) {
|
|
1018
|
-
clearTimeout(tcTokenIndexTimer);
|
|
1019
|
-
}
|
|
1020
|
-
tcTokenIndexTimer = setTimeout(() => {
|
|
1021
|
-
tcTokenIndexTimer = undefined;
|
|
1022
|
-
flushTcTokenIndex().catch(err => {
|
|
1023
|
-
logger.warn({ err: err?.message }, 'failed to save tctoken index');
|
|
1024
|
-
});
|
|
1025
|
-
}, 5000);
|
|
1026
|
-
}
|
|
1027
|
-
function trackTcTokenJid(jid) {
|
|
1028
|
-
if (jid && jid !== TC_TOKEN_INDEX_KEY && !tcTokenKnownJids.has(jid)) {
|
|
1029
|
-
tcTokenKnownJids.add(jid);
|
|
1030
|
-
scheduleTcTokenIndexSave();
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
683
|
const handlePrivacyTokenNotification = async (node) => {
|
|
1034
684
|
const tokensNode = getBinaryNodeChild(node, 'tokens');
|
|
685
|
+
const from = jidNormalizedUser(node.attrs.from);
|
|
1035
686
|
if (!tokensNode)
|
|
1036
687
|
return;
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
688
|
+
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token');
|
|
689
|
+
for (const tokenNode of tokenNodes) {
|
|
690
|
+
const { attrs, content } = tokenNode;
|
|
691
|
+
const type = attrs.type;
|
|
692
|
+
const timestamp = attrs.t;
|
|
693
|
+
if (type === 'trusted_contact' && content instanceof Buffer) {
|
|
694
|
+
logger.debug({
|
|
695
|
+
from,
|
|
696
|
+
timestamp,
|
|
697
|
+
tcToken: content
|
|
698
|
+
}, 'received trusted contact token');
|
|
699
|
+
await authState.keys.set({
|
|
700
|
+
tctoken: { [from]: { token: content, timestamp } }
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
}
|
|
1052
704
|
};
|
|
1053
705
|
async function decipherLinkPublicKey(data) {
|
|
1054
706
|
const buffer = toRequiredBuffer(data);
|
|
@@ -1074,11 +726,10 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1074
726
|
const newValue = ((await msgRetryCache.get(key)) || 0) + 1;
|
|
1075
727
|
await msgRetryCache.set(key, newValue);
|
|
1076
728
|
};
|
|
1077
|
-
const sendMessagesAgain = async (key, ids, retryNode
|
|
729
|
+
const sendMessagesAgain = async (key, ids, retryNode) => {
|
|
1078
730
|
const remoteJid = key.remoteJid;
|
|
1079
731
|
const participant = key.participant || remoteJid;
|
|
1080
732
|
const retryCount = +retryNode.attrs.count || 1;
|
|
1081
|
-
const msgId = ids[0];
|
|
1082
733
|
// Try to get messages from cache first, then fallback to getMessage
|
|
1083
734
|
const msgs = [];
|
|
1084
735
|
for (const id of ids) {
|
|
@@ -1110,48 +761,28 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1110
761
|
// just re-send the message to everyone
|
|
1111
762
|
// prevents the first message decryption failure
|
|
1112
763
|
const sendToAll = !jidDecode(participant)?.device;
|
|
764
|
+
// Check if we should recreate session for this retry
|
|
765
|
+
let shouldRecreateSession = false;
|
|
766
|
+
let recreateReason = '';
|
|
1113
767
|
const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
|
|
1114
|
-
let injectedFromBundle = false;
|
|
1115
|
-
const bundle = extractE2ESessionFromRetryReceipt(receiptNode);
|
|
1116
|
-
if (bundle) {
|
|
1117
|
-
try {
|
|
1118
|
-
await signalRepository.injectE2ESession({ jid: participant, session: bundle });
|
|
1119
|
-
injectedFromBundle = true;
|
|
1120
|
-
logger.debug({ participant, retryCount }, 'injected session from retry receipt key bundle');
|
|
1121
|
-
}
|
|
1122
|
-
catch (error) {
|
|
1123
|
-
logger.warn({ error, participant }, 'failed to inject session from retry receipt');
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
if (!injectedFromBundle) {
|
|
1127
|
-
const receivedRegId = getBinaryNodeChildUInt(receiptNode, 'registration', 4);
|
|
1128
|
-
if (typeof receivedRegId === 'number' && Number.isInteger(receivedRegId)) {
|
|
1129
|
-
const info = await signalRepository.getSessionInfo(participant);
|
|
1130
|
-
if (info && info.registrationId !== 0 && info.registrationId !== receivedRegId) {
|
|
1131
|
-
logger.info({ participant, stored: info.registrationId, received: receivedRegId }, 'reg id mismatch on retry without bundle, deleting session');
|
|
1132
|
-
await authState.keys.set({ session: { [sessionId]: null } });
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
768
|
const BASE_KEY_CHECK_RETRY = 2;
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
769
|
+
const primaryMsgId = ids[0];
|
|
770
|
+
if (primaryMsgId && messageRetryManager) {
|
|
771
|
+
const baseKeyInfo = await signalRepository.getSessionInfo(participant);
|
|
772
|
+
if (baseKeyInfo) {
|
|
1140
773
|
if (retryCount === BASE_KEY_CHECK_RETRY) {
|
|
1141
|
-
messageRetryManager.saveBaseKey(sessionId,
|
|
774
|
+
messageRetryManager.saveBaseKey(sessionId, primaryMsgId, baseKeyInfo.baseKey);
|
|
1142
775
|
}
|
|
1143
776
|
else if (retryCount > BASE_KEY_CHECK_RETRY) {
|
|
1144
|
-
if (messageRetryManager.hasSameBaseKey(sessionId,
|
|
777
|
+
if (messageRetryManager.hasSameBaseKey(sessionId, primaryMsgId, baseKeyInfo.baseKey)) {
|
|
1145
778
|
logger.warn({ participant, retryCount }, 'base key collision on retry, forcing fresh session');
|
|
1146
779
|
await authState.keys.set({ session: { [sessionId]: null } });
|
|
1147
780
|
}
|
|
1148
|
-
messageRetryManager.deleteBaseKey(sessionId,
|
|
781
|
+
messageRetryManager.deleteBaseKey(sessionId, primaryMsgId);
|
|
1149
782
|
}
|
|
1150
783
|
}
|
|
1151
784
|
}
|
|
1152
|
-
|
|
1153
|
-
let recreateReason = '';
|
|
1154
|
-
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1 && !injectedFromBundle) {
|
|
785
|
+
if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
|
|
1155
786
|
try {
|
|
1156
787
|
const hasSession = await signalRepository.validateSession(participant);
|
|
1157
788
|
const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists);
|
|
@@ -1166,13 +797,11 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1166
797
|
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
|
|
1167
798
|
}
|
|
1168
799
|
}
|
|
1169
|
-
|
|
1170
|
-
await assertSessions([participant], true);
|
|
1171
|
-
}
|
|
800
|
+
await assertSessions([participant], true);
|
|
1172
801
|
if (isJidGroup(remoteJid)) {
|
|
1173
802
|
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
|
|
1174
803
|
}
|
|
1175
|
-
logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason
|
|
804
|
+
logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp');
|
|
1176
805
|
for (const [i, msg] of msgs.entries()) {
|
|
1177
806
|
if (!ids[i])
|
|
1178
807
|
continue;
|
|
@@ -1207,6 +836,11 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1207
836
|
fromMe,
|
|
1208
837
|
participant: attrs.participant
|
|
1209
838
|
};
|
|
839
|
+
if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
|
|
840
|
+
logger.debug({ remoteJid }, 'ignoring receipt from jid');
|
|
841
|
+
await sendMessageAck(node);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
1210
844
|
const ids = [attrs.id];
|
|
1211
845
|
if (Array.isArray(content)) {
|
|
1212
846
|
const items = getBinaryNodeChildren(content[0], 'item');
|
|
@@ -1248,7 +882,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1248
882
|
try {
|
|
1249
883
|
await updateSendMessageAgainCount(ids[0], key.participant);
|
|
1250
884
|
logger.debug({ attrs, key }, 'recv retry request');
|
|
1251
|
-
await sendMessagesAgain(key, ids, retryNode
|
|
885
|
+
await sendMessagesAgain(key, ids, retryNode);
|
|
1252
886
|
}
|
|
1253
887
|
catch (error) {
|
|
1254
888
|
logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
|
|
@@ -1271,6 +905,11 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1271
905
|
};
|
|
1272
906
|
const handleNotification = async (node) => {
|
|
1273
907
|
const remoteJid = node.attrs.from;
|
|
908
|
+
if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
|
|
909
|
+
logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification');
|
|
910
|
+
await sendMessageAck(node);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
1274
913
|
try {
|
|
1275
914
|
await Promise.all([
|
|
1276
915
|
notificationMutex.mutex(async () => {
|
|
@@ -1283,7 +922,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1283
922
|
fromMe,
|
|
1284
923
|
participant: node.attrs.participant,
|
|
1285
924
|
participantAlt,
|
|
1286
|
-
participantUsername: node.attrs.participant_username,
|
|
1287
925
|
addressingMode,
|
|
1288
926
|
id: node.attrs.id,
|
|
1289
927
|
...(msg.key || {})
|
|
@@ -1301,6 +939,11 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1301
939
|
}
|
|
1302
940
|
};
|
|
1303
941
|
const handleMessage = async (node) => {
|
|
942
|
+
if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== S_WHATSAPP_NET) {
|
|
943
|
+
logger.debug({ key: node.attrs.key }, 'ignored message');
|
|
944
|
+
await sendMessageAck(node, NACK_REASONS.UnhandledError);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
1304
947
|
const encNode = getBinaryNodeChild(node, 'enc');
|
|
1305
948
|
// TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
|
|
1306
949
|
if (encNode?.attrs.type === 'msmsg') {
|
|
@@ -1404,14 +1047,29 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1404
1047
|
return sendMessageAck(node);
|
|
1405
1048
|
}
|
|
1406
1049
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1050
|
+
const errorMessage = msg?.messageStubParameters?.[0] || '';
|
|
1051
|
+
const isPreKeyError = errorMessage.includes('PreKey');
|
|
1052
|
+
logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
|
|
1053
|
+
// Handle both pre-key and normal retries in single mutex
|
|
1409
1054
|
await retryMutex.mutex(async () => {
|
|
1410
1055
|
try {
|
|
1411
1056
|
if (!ws.isOpen) {
|
|
1412
1057
|
logger.debug({ node }, 'Connection closed, skipping retry');
|
|
1413
1058
|
return;
|
|
1414
1059
|
}
|
|
1060
|
+
// Handle pre-key errors with upload and delay
|
|
1061
|
+
if (isPreKeyError) {
|
|
1062
|
+
logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
|
|
1063
|
+
try {
|
|
1064
|
+
logger.debug('Uploading pre-keys for error recovery');
|
|
1065
|
+
await uploadPreKeys(5);
|
|
1066
|
+
logger.debug('Waiting for server to process new pre-keys');
|
|
1067
|
+
await delay(1000);
|
|
1068
|
+
}
|
|
1069
|
+
catch (uploadErr) {
|
|
1070
|
+
logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1415
1073
|
const encNode = getBinaryNodeChild(node, 'enc');
|
|
1416
1074
|
await sendRetryRequest(node, !encNode);
|
|
1417
1075
|
if (retryRequestDelayMs) {
|
|
@@ -1419,7 +1077,15 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1419
1077
|
}
|
|
1420
1078
|
}
|
|
1421
1079
|
catch (err) {
|
|
1422
|
-
logger.error({ err }, 'Failed to
|
|
1080
|
+
logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
|
|
1081
|
+
// Still attempt retry even if pre-key upload failed
|
|
1082
|
+
try {
|
|
1083
|
+
const encNode = getBinaryNodeChild(node, 'enc');
|
|
1084
|
+
await sendRetryRequest(node, !encNode);
|
|
1085
|
+
}
|
|
1086
|
+
catch (retryErr) {
|
|
1087
|
+
logger.error({ retryErr }, 'Failed to send retry after error handling');
|
|
1088
|
+
}
|
|
1423
1089
|
}
|
|
1424
1090
|
acked = true;
|
|
1425
1091
|
await sendMessageAck(node, NACK_REASONS.UnhandledError);
|
|
@@ -1495,13 +1161,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1495
1161
|
offline: !!attrs.offline,
|
|
1496
1162
|
status
|
|
1497
1163
|
};
|
|
1498
|
-
if (status === 'relaylatency') {
|
|
1499
|
-
const latencyValue = infoChild.attrs.latency || infoChild.attrs['latency_ms'] || infoChild.attrs['latency-ms'];
|
|
1500
|
-
const latencyMs = latencyValue ? Number(latencyValue) : undefined;
|
|
1501
|
-
if (Number.isFinite(latencyMs)) {
|
|
1502
|
-
call.latencyMs = latencyMs;
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
1164
|
if (status === 'offer') {
|
|
1506
1165
|
call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
|
|
1507
1166
|
call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
|
|
@@ -1546,61 +1205,29 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1546
1205
|
// error in acknowledgement,
|
|
1547
1206
|
// device could not display the message
|
|
1548
1207
|
if (attrs.error) {
|
|
1549
|
-
|
|
1550
|
-
if (attrs.error === SERVER_ERROR_CODES.MessageAccountRestriction) {
|
|
1551
|
-
// 463 = 1:1 message missing privacy token (tctoken). Usually means the
|
|
1552
|
-
// account is restricted: WhatsApp blocks starting new chats but preserves
|
|
1553
|
-
// existing ones, since established chats already carry a tctoken.
|
|
1554
|
-
// WA Web prevents this client-side (disables the compose bar).
|
|
1555
|
-
// No retry — retrying counts as another "reach out" and worsens the restriction.
|
|
1556
|
-
logger.warn({ msgId: attrs.id, from: attrs.from }, 'error 463: account restricted or missing tctoken for contact');
|
|
1557
|
-
const ackFrom = attrs.from;
|
|
1558
|
-
if (ackFrom && !inFlight463Recoveries.has(ackFrom)) {
|
|
1559
|
-
inFlight463Recoveries.add(ackFrom);
|
|
1560
|
-
void (async () => {
|
|
1561
|
-
try {
|
|
1562
|
-
const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
|
|
1563
|
-
const tcStorageJid = await resolveTcTokenJid(ackFrom, getLIDForPN);
|
|
1564
|
-
const issueJid = await resolveIssuanceJid(ackFrom, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
|
|
1565
|
-
const result = await issuePrivacyTokens([issueJid], unixTimestampSeconds());
|
|
1566
|
-
await storeTcTokensFromIqResult({
|
|
1567
|
-
result,
|
|
1568
|
-
fallbackJid: tcStorageJid,
|
|
1569
|
-
keys: authState.keys,
|
|
1570
|
-
getLIDForPN,
|
|
1571
|
-
onNewJidStored: trackTcTokenJid
|
|
1572
|
-
});
|
|
1573
|
-
logger.debug({ from: ackFrom }, 'completed 463 token recovery issuance');
|
|
1574
|
-
}
|
|
1575
|
-
catch (err) {
|
|
1576
|
-
logger.debug({ from: ackFrom, err: err?.message }, 'failed 463 token recovery issuance');
|
|
1577
|
-
}
|
|
1578
|
-
finally {
|
|
1579
|
-
inFlight463Recoveries.delete(ackFrom);
|
|
1580
|
-
}
|
|
1581
|
-
})();
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
else if (attrs.error === SERVER_ERROR_CODES.SmaxInvalid) {
|
|
1585
|
-
logger.warn({ msgId: attrs.id, from: attrs.from }, 'smax-invalid (479): stanza rejected by server — likely stale device session or malformed addressing');
|
|
1586
|
-
}
|
|
1587
|
-
else if (isReachoutTimelocked) {
|
|
1588
|
-
// user is temporarily restricted, fetch current restriction details
|
|
1589
|
-
await fetchAccountReachoutTimelock().catch(err => logger.warn({ err }, 'failed to fetch reachout timelock'));
|
|
1590
|
-
logger.warn({ attrs }, 'received error in ack');
|
|
1591
|
-
}
|
|
1592
|
-
else {
|
|
1593
|
-
logger.warn({ attrs }, 'received error in ack');
|
|
1594
|
-
}
|
|
1208
|
+
logger.warn({ attrs }, 'received error in ack');
|
|
1595
1209
|
ev.emit('messages.update', [
|
|
1596
1210
|
{
|
|
1597
1211
|
key,
|
|
1598
1212
|
update: {
|
|
1599
1213
|
status: WAMessageStatus.ERROR,
|
|
1600
|
-
messageStubParameters:
|
|
1214
|
+
messageStubParameters: [attrs.error]
|
|
1601
1215
|
}
|
|
1602
1216
|
}
|
|
1603
1217
|
]);
|
|
1218
|
+
// resend the message with device_fanout=false, use at your own risk
|
|
1219
|
+
// if (attrs.error === '475') {
|
|
1220
|
+
// const msg = await getMessage(key)
|
|
1221
|
+
// if (msg) {
|
|
1222
|
+
// await relayMessage(key.remoteJid!, msg, {
|
|
1223
|
+
// messageId: key.id!,
|
|
1224
|
+
// useUserDevicesCache: false,
|
|
1225
|
+
// additionalAttributes: {
|
|
1226
|
+
// device_fanout: 'false'
|
|
1227
|
+
// }
|
|
1228
|
+
// })
|
|
1229
|
+
// }
|
|
1230
|
+
// }
|
|
1604
1231
|
}
|
|
1605
1232
|
};
|
|
1606
1233
|
/// processes a node with the given function
|
|
@@ -1624,19 +1251,6 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1624
1251
|
yieldToEventLoop: () => new Promise(resolve => setImmediate(resolve))
|
|
1625
1252
|
});
|
|
1626
1253
|
const processNode = async (type, node, identifier, exec) => {
|
|
1627
|
-
// Fast path: ack and drop ignored JIDs before entering the buffer/queue
|
|
1628
|
-
const from = node.attrs.from;
|
|
1629
|
-
let ignoreJid = from;
|
|
1630
|
-
if (type === 'receipt' && from) {
|
|
1631
|
-
const attrs = node.attrs;
|
|
1632
|
-
const isLid = attrs.from.includes('lid');
|
|
1633
|
-
const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id);
|
|
1634
|
-
ignoreJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient;
|
|
1635
|
-
}
|
|
1636
|
-
if (ignoreJid && ignoreJid !== S_WHATSAPP_NET && shouldIgnoreJid(ignoreJid)) {
|
|
1637
|
-
await sendMessageAck(node, type === 'message' ? NACK_REASONS.UnhandledError : undefined);
|
|
1638
|
-
return;
|
|
1639
|
-
}
|
|
1640
1254
|
const isOffline = !!node.attrs.offline;
|
|
1641
1255
|
if (isOffline) {
|
|
1642
1256
|
offlineNodeProcessor.enqueue(type, node);
|
|
@@ -1692,110 +1306,12 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1692
1306
|
await upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
|
|
1693
1307
|
}
|
|
1694
1308
|
});
|
|
1695
|
-
|
|
1696
|
-
let lastTcTokenPruneTs = 0;
|
|
1697
|
-
/** dedupe in-flight 463 recovery token issuance by target JID */
|
|
1698
|
-
const inFlight463Recoveries = new Set();
|
|
1699
|
-
ev.on('connection.update', ({ isOnline, connection }) => {
|
|
1309
|
+
ev.on('connection.update', ({ isOnline }) => {
|
|
1700
1310
|
if (typeof isOnline !== 'undefined') {
|
|
1701
1311
|
sendActiveReceipts = isOnline;
|
|
1702
1312
|
logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
|
|
1703
1313
|
}
|
|
1704
|
-
// Flush pending tctoken index save on disconnect to avoid writing after close
|
|
1705
|
-
if (connection === 'close' && tcTokenIndexTimer) {
|
|
1706
|
-
clearTimeout(tcTokenIndexTimer);
|
|
1707
|
-
tcTokenIndexTimer = undefined;
|
|
1708
|
-
// Best-effort flush — may fail if store is already closed
|
|
1709
|
-
try {
|
|
1710
|
-
void Promise.resolve(flushTcTokenIndex()).catch(() => { });
|
|
1711
|
-
}
|
|
1712
|
-
catch {
|
|
1713
|
-
/* ignore sync errors */
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
// Prune expired tctokens when coming online, at most once per 24 hours
|
|
1717
|
-
// Matches WA Web's CLEAN_TC_TOKENS task
|
|
1718
|
-
// Note: don't gate on tcTokenKnownJids.size — the index may still be loading
|
|
1719
|
-
if (isOnline) {
|
|
1720
|
-
const now = Date.now();
|
|
1721
|
-
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
1722
|
-
if (now - lastTcTokenPruneTs >= DAY_MS) {
|
|
1723
|
-
lastTcTokenPruneTs = now;
|
|
1724
|
-
void pruneExpiredTcTokens();
|
|
1725
|
-
}
|
|
1726
|
-
}
|
|
1727
|
-
});
|
|
1728
|
-
registerSocketEndHandler(() => {
|
|
1729
|
-
if (!config.msgRetryCounterCache && msgRetryCache.close) {
|
|
1730
|
-
msgRetryCache.close();
|
|
1731
|
-
}
|
|
1732
|
-
if (!config.callOfferCache && callOfferCache.close) {
|
|
1733
|
-
callOfferCache.close();
|
|
1734
|
-
}
|
|
1735
|
-
identityAssertDebounce.close();
|
|
1736
|
-
sendActiveReceipts = false;
|
|
1737
1314
|
});
|
|
1738
|
-
async function pruneExpiredTcTokens() {
|
|
1739
|
-
try {
|
|
1740
|
-
await tcTokenIndexLoaded;
|
|
1741
|
-
// Union with the persisted index picks up JIDs added by other layers
|
|
1742
|
-
// (history sync) without needing inter-module wiring.
|
|
1743
|
-
const persisted = await readTcTokenIndex(authState.keys);
|
|
1744
|
-
const allJids = new Set(tcTokenKnownJids);
|
|
1745
|
-
for (const jid of persisted)
|
|
1746
|
-
allJids.add(jid);
|
|
1747
|
-
if (!allJids.size)
|
|
1748
|
-
return;
|
|
1749
|
-
const jids = [...allJids];
|
|
1750
|
-
const allTokens = await authState.keys.get('tctoken', jids);
|
|
1751
|
-
const writes = {};
|
|
1752
|
-
const survivors = new Set();
|
|
1753
|
-
let mutated = 0;
|
|
1754
|
-
for (const jid of jids) {
|
|
1755
|
-
const entry = allTokens[jid];
|
|
1756
|
-
if (!entry) {
|
|
1757
|
-
// Tracked but nothing in store — drop from index.
|
|
1758
|
-
mutated++;
|
|
1759
|
-
continue;
|
|
1760
|
-
}
|
|
1761
|
-
const hasPeerToken = !!entry.token?.length;
|
|
1762
|
-
const peerTokenExpired = hasPeerToken && isTcTokenExpired(entry.timestamp);
|
|
1763
|
-
const hasSenderTs = entry.senderTimestamp !== undefined;
|
|
1764
|
-
const senderTsExpired = hasSenderTs && isTcTokenExpired(entry.senderTimestamp);
|
|
1765
|
-
const keepPeerToken = hasPeerToken && !peerTokenExpired;
|
|
1766
|
-
const keepSenderTs = hasSenderTs && !senderTsExpired;
|
|
1767
|
-
if (!keepPeerToken && !keepSenderTs) {
|
|
1768
|
-
writes[jid] = null;
|
|
1769
|
-
mutated++;
|
|
1770
|
-
}
|
|
1771
|
-
else if (peerTokenExpired && keepSenderTs) {
|
|
1772
|
-
writes[jid] = { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp };
|
|
1773
|
-
survivors.add(jid);
|
|
1774
|
-
mutated++;
|
|
1775
|
-
}
|
|
1776
|
-
else {
|
|
1777
|
-
survivors.add(jid);
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
if (mutated === 0)
|
|
1781
|
-
return;
|
|
1782
|
-
await authState.keys.set({
|
|
1783
|
-
tctoken: {
|
|
1784
|
-
...writes,
|
|
1785
|
-
[TC_TOKEN_INDEX_KEY]: {
|
|
1786
|
-
token: Buffer.from(JSON.stringify([...survivors]))
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
});
|
|
1790
|
-
tcTokenKnownJids.clear();
|
|
1791
|
-
for (const jid of survivors)
|
|
1792
|
-
tcTokenKnownJids.add(jid);
|
|
1793
|
-
logger.debug({ mutated, remaining: survivors.size }, 'pruned expired tctokens');
|
|
1794
|
-
}
|
|
1795
|
-
catch (err) {
|
|
1796
|
-
logger.warn({ err: err?.message }, 'failed to prune expired tctokens');
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
1315
|
return {
|
|
1800
1316
|
...sock,
|
|
1801
1317
|
sendMessageAck,
|