@alannxd/baileys 6.0.5 → 6.0.6

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.
Files changed (234) hide show
  1. package/WAProto/fix-import.js +29 -0
  2. package/WAProto/index.js +160 -201
  3. package/engine-requirements.js +1 -1
  4. package/lib/Defaults/baileys-version.json +3 -0
  5. package/lib/Defaults/index.d.ts +15 -37
  6. package/lib/Defaults/index.js +136 -119
  7. package/lib/Defaults/phonenumber-mcc.json +223 -0
  8. package/lib/Signal/Group/ciphertext-message.d.ts +0 -1
  9. package/lib/Signal/Group/ciphertext-message.js +5 -2
  10. package/lib/Signal/Group/group-session-builder.d.ts +3 -4
  11. package/lib/Signal/Group/group-session-builder.js +41 -7
  12. package/lib/Signal/Group/group_cipher.d.ts +4 -4
  13. package/lib/Signal/Group/group_cipher.js +51 -37
  14. package/lib/Signal/Group/index.d.ts +11 -12
  15. package/lib/Signal/Group/index.js +57 -12
  16. package/lib/Signal/Group/keyhelper.d.ts +1 -2
  17. package/lib/Signal/Group/keyhelper.js +44 -7
  18. package/lib/Signal/Group/queue-job.d.ts +1 -0
  19. package/lib/Signal/Group/queue-job.js +57 -0
  20. package/lib/Signal/Group/sender-chain-key.d.ts +2 -3
  21. package/lib/Signal/Group/sender-chain-key.js +15 -7
  22. package/lib/Signal/Group/sender-key-distribution-message.d.ts +1 -2
  23. package/lib/Signal/Group/sender-key-distribution-message.js +11 -8
  24. package/lib/Signal/Group/sender-key-message.d.ts +1 -2
  25. package/lib/Signal/Group/sender-key-message.js +12 -9
  26. package/lib/Signal/Group/sender-key-name.d.ts +0 -1
  27. package/lib/Signal/Group/sender-key-name.js +5 -2
  28. package/lib/Signal/Group/sender-key-record.d.ts +2 -3
  29. package/lib/Signal/Group/sender-key-record.js +21 -9
  30. package/lib/Signal/Group/sender-key-state.d.ts +6 -7
  31. package/lib/Signal/Group/sender-key-state.js +42 -27
  32. package/lib/Signal/Group/sender-message-key.d.ts +0 -1
  33. package/lib/Signal/Group/sender-message-key.js +7 -4
  34. package/lib/Signal/libsignal.d.ts +3 -5
  35. package/lib/Signal/libsignal.js +90 -347
  36. package/lib/Socket/Client/{types.d.ts → abstract-socket-client.d.ts} +5 -4
  37. package/lib/Socket/Client/abstract-socket-client.js +13 -0
  38. package/lib/Socket/Client/index.d.ts +3 -3
  39. package/lib/Socket/Client/index.js +19 -3
  40. package/lib/Socket/Client/mobile-socket-client.d.ts +13 -0
  41. package/lib/Socket/Client/mobile-socket-client.js +65 -0
  42. package/lib/Socket/Client/{websocket.d.ts → web-socket-client.d.ts} +2 -3
  43. package/lib/Socket/Client/web-socket-client.js +62 -0
  44. package/lib/Socket/business.d.ts +108 -154
  45. package/lib/Socket/business.js +43 -162
  46. package/lib/Socket/chats.d.ts +239 -96
  47. package/lib/Socket/chats.js +427 -627
  48. package/lib/Socket/communities.d.ts +146 -239
  49. package/lib/Socket/communities.js +80 -90
  50. package/lib/Socket/groups.d.ts +57 -104
  51. package/lib/Socket/groups.js +161 -154
  52. package/lib/Socket/index.d.ts +115 -202
  53. package/lib/Socket/index.js +10 -11
  54. package/lib/Socket/luxu.d.ts +266 -22
  55. package/lib/Socket/luxu.js +465 -422
  56. package/lib/Socket/messages-recv.d.ts +84 -136
  57. package/lib/Socket/messages-recv.js +615 -1421
  58. package/lib/Socket/messages-send.d.ts +126 -142
  59. package/lib/Socket/messages-send.js +671 -878
  60. package/lib/Socket/newsletter.d.ts +85 -121
  61. package/lib/Socket/newsletter.js +272 -147
  62. package/lib/Socket/registration.d.ts +267 -0
  63. package/lib/Socket/registration.js +166 -0
  64. package/lib/Socket/socket.d.ts +19 -34
  65. package/lib/Socket/socket.js +313 -544
  66. package/lib/Socket/usync.d.ts +36 -0
  67. package/lib/Socket/usync.js +70 -0
  68. package/lib/Store/index.d.ts +3 -10
  69. package/lib/Store/index.js +10 -10
  70. package/lib/Store/make-cache-manager-store.d.ts +11 -17
  71. package/lib/Store/make-cache-manager-store.js +41 -43
  72. package/lib/Store/make-in-memory-store.d.ts +118 -39
  73. package/lib/Store/make-in-memory-store.js +341 -112
  74. package/lib/Store/make-ordered-dictionary.d.ts +10 -11
  75. package/lib/Store/make-ordered-dictionary.js +20 -14
  76. package/lib/Store/object-repository.d.ts +9 -10
  77. package/lib/Store/object-repository.js +6 -11
  78. package/lib/Types/Auth.d.ts +12 -19
  79. package/lib/Types/Auth.js +2 -2
  80. package/lib/Types/Call.d.ts +1 -3
  81. package/lib/Types/Call.js +2 -2
  82. package/lib/Types/Chat.d.ts +13 -35
  83. package/lib/Types/Chat.js +4 -8
  84. package/lib/Types/Contact.d.ts +1 -8
  85. package/lib/Types/Contact.js +2 -2
  86. package/lib/Types/Events.d.ts +17 -116
  87. package/lib/Types/Events.js +2 -2
  88. package/lib/Types/GroupMetadata.d.ts +5 -21
  89. package/lib/Types/GroupMetadata.js +2 -2
  90. package/lib/Types/Label.d.ts +0 -12
  91. package/lib/Types/Label.js +5 -3
  92. package/lib/Types/LabelAssociation.d.ts +0 -1
  93. package/lib/Types/LabelAssociation.js +5 -3
  94. package/lib/Types/Message.d.ts +58 -105
  95. package/lib/Types/Message.js +9 -11
  96. package/lib/Types/Newsletter.d.ts +103 -0
  97. package/lib/Types/Newsletter.js +38 -0
  98. package/lib/Types/Product.d.ts +1 -2
  99. package/lib/Types/Product.js +2 -2
  100. package/lib/Types/Signal.d.ts +2 -32
  101. package/lib/Types/Signal.js +2 -2
  102. package/lib/Types/Socket.d.ts +25 -50
  103. package/lib/Types/Socket.js +2 -3
  104. package/lib/Types/State.d.ts +2 -72
  105. package/lib/Types/State.js +2 -56
  106. package/lib/Types/USync.d.ts +2 -3
  107. package/lib/Types/USync.js +2 -2
  108. package/lib/Types/index.d.ts +14 -22
  109. package/lib/Types/index.js +31 -15
  110. package/lib/Utils/auth-utils.d.ts +6 -12
  111. package/lib/Utils/auth-utils.js +143 -239
  112. package/lib/Utils/baileys-event-stream.d.ts +16 -0
  113. package/lib/Utils/baileys-event-stream.js +63 -0
  114. package/lib/Utils/business.d.ts +2 -3
  115. package/lib/Utils/business.js +69 -66
  116. package/lib/Utils/chat-utils.d.ts +23 -52
  117. package/lib/Utils/chat-utils.js +253 -396
  118. package/lib/Utils/crypto.d.ts +22 -18
  119. package/lib/Utils/crypto.js +90 -57
  120. package/lib/Utils/decode-wa-message.d.ts +8 -55
  121. package/lib/Utils/decode-wa-message.js +84 -203
  122. package/lib/Utils/event-buffer.d.ts +8 -9
  123. package/lib/Utils/event-buffer.js +77 -185
  124. package/lib/Utils/generics.d.ts +29 -28
  125. package/lib/Utils/generics.js +210 -180
  126. package/lib/Utils/history.d.ts +9 -18
  127. package/lib/Utils/history.js +55 -93
  128. package/lib/Utils/index.d.ts +17 -22
  129. package/lib/Utils/index.js +33 -22
  130. package/lib/Utils/link-preview.d.ts +5 -5
  131. package/lib/Utils/link-preview.js +24 -16
  132. package/lib/Utils/logger.d.ts +3 -11
  133. package/lib/Utils/logger.js +7 -3
  134. package/lib/Utils/lt-hash.d.ts +12 -8
  135. package/lib/Utils/lt-hash.js +46 -3
  136. package/lib/Utils/make-mutex.d.ts +2 -4
  137. package/lib/Utils/make-mutex.js +34 -24
  138. package/lib/Utils/messages-media.d.ts +44 -61
  139. package/lib/Utils/messages-media.js +482 -451
  140. package/lib/Utils/messages.d.ts +18 -32
  141. package/lib/Utils/messages.js +369 -458
  142. package/lib/Utils/noise-handler.d.ts +14 -13
  143. package/lib/Utils/noise-handler.js +99 -145
  144. package/lib/Utils/process-message.d.ts +12 -31
  145. package/lib/Utils/process-message.js +150 -459
  146. package/lib/Utils/signal.d.ts +5 -20
  147. package/lib/Utils/signal.js +72 -120
  148. package/lib/Utils/use-multi-file-auth-state.d.ts +2 -2
  149. package/lib/Utils/use-multi-file-auth-state.js +27 -29
  150. package/lib/Utils/validate-connection.d.ts +7 -7
  151. package/lib/Utils/validate-connection.js +99 -73
  152. package/lib/WABinary/constants.d.ts +27 -25
  153. package/lib/WABinary/constants.js +20 -1281
  154. package/lib/WABinary/decode.d.ts +5 -5
  155. package/lib/WABinary/decode.js +42 -52
  156. package/lib/WABinary/encode.d.ts +3 -3
  157. package/lib/WABinary/encode.js +155 -110
  158. package/lib/WABinary/generic-utils.d.ts +7 -8
  159. package/lib/WABinary/generic-utils.js +49 -48
  160. package/lib/WABinary/index.d.ts +5 -6
  161. package/lib/WABinary/index.js +21 -6
  162. package/lib/WABinary/jid-utils.d.ts +8 -25
  163. package/lib/WABinary/jid-utils.js +40 -74
  164. package/lib/WABinary/types.d.ts +1 -2
  165. package/lib/WABinary/types.js +2 -2
  166. package/lib/WAM/BinaryInfo.d.ts +11 -3
  167. package/lib/WAM/BinaryInfo.js +5 -2
  168. package/lib/WAM/constants.d.ts +3 -5
  169. package/lib/WAM/constants.js +11958 -19461
  170. package/lib/WAM/encode.d.ts +3 -3
  171. package/lib/WAM/encode.js +22 -17
  172. package/lib/WAM/index.d.ts +3 -4
  173. package/lib/WAM/index.js +19 -4
  174. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +3 -4
  175. package/lib/WAUSync/Protocols/USyncContactProtocol.js +13 -33
  176. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +2 -3
  177. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +14 -11
  178. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +2 -3
  179. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +12 -9
  180. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +2 -3
  181. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +13 -9
  182. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +3 -4
  183. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +22 -20
  184. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +3 -5
  185. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +8 -13
  186. package/lib/WAUSync/Protocols/index.d.ts +4 -6
  187. package/lib/WAUSync/Protocols/index.js +20 -6
  188. package/lib/WAUSync/USyncQuery.d.ts +4 -6
  189. package/lib/WAUSync/USyncQuery.js +35 -44
  190. package/lib/WAUSync/USyncUser.d.ts +5 -10
  191. package/lib/WAUSync/USyncUser.js +5 -10
  192. package/lib/WAUSync/index.js +19 -4
  193. package/lib/index.d.ts +9 -10
  194. package/lib/index.js +34 -12
  195. package/package.json +50 -83
  196. package/WAProto/GenerateStatics.sh +0 -3
  197. package/WAProto/WAProto.proto +0 -5479
  198. package/WAProto/fix-imports.js +0 -85
  199. package/WAProto/index.d.ts +0 -14017
  200. package/lib/Signal/lid-mapping.d.ts +0 -23
  201. package/lib/Signal/lid-mapping.js +0 -277
  202. package/lib/Socket/Client/types.js +0 -11
  203. package/lib/Socket/Client/websocket.js +0 -54
  204. package/lib/Socket/mex.d.ts +0 -3
  205. package/lib/Socket/mex.js +0 -42
  206. package/lib/Store/keyed-db.d.ts +0 -22
  207. package/lib/Store/keyed-db.js +0 -108
  208. package/lib/Types/Bussines.d.ts +0 -25
  209. package/lib/Types/Bussines.js +0 -2
  210. package/lib/Types/Mex.d.ts +0 -141
  211. package/lib/Types/Mex.js +0 -37
  212. package/lib/Utils/browser-utils.d.ts +0 -4
  213. package/lib/Utils/browser-utils.js +0 -28
  214. package/lib/Utils/companion-reg-client-utils.d.ts +0 -17
  215. package/lib/Utils/companion-reg-client-utils.js +0 -35
  216. package/lib/Utils/identity-change-handler.d.ts +0 -44
  217. package/lib/Utils/identity-change-handler.js +0 -50
  218. package/lib/Utils/message-retry-manager.d.ts +0 -115
  219. package/lib/Utils/message-retry-manager.js +0 -265
  220. package/lib/Utils/offline-node-processor.d.ts +0 -17
  221. package/lib/Utils/offline-node-processor.js +0 -40
  222. package/lib/Utils/pre-key-manager.d.ts +0 -28
  223. package/lib/Utils/pre-key-manager.js +0 -106
  224. package/lib/Utils/reporting-utils.d.ts +0 -11
  225. package/lib/Utils/reporting-utils.js +0 -258
  226. package/lib/Utils/stanza-ack.d.ts +0 -11
  227. package/lib/Utils/stanza-ack.js +0 -38
  228. package/lib/Utils/sync-action-utils.d.ts +0 -19
  229. package/lib/Utils/sync-action-utils.js +0 -49
  230. package/lib/Utils/tc-token-utils.d.ts +0 -37
  231. package/lib/Utils/tc-token-utils.js +0 -163
  232. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +0 -10
  233. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +0 -25
  234. package/lib/WAUSync/index.d.ts +0 -4
@@ -1,599 +1,219 @@
1
- import NodeCache from '@cacheable/node-cache';
2
- import { Boom } from '@hapi/boom';
3
- import { randomBytes } from 'crypto';
4
- import Long from 'long';
5
- import { proto } from '../../WAProto/index.js';
6
- import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT, PLACEHOLDER_MAX_AGE_SECONDS, STATUS_EXPIRY_SECONDS } from '../Defaults/index.js';
7
- import { ReachoutTimelockEnforcementType, WAMessageStatus, WAMessageStubType } from '../Types/index.js';
8
- import { ACCOUNT_RESTRICTED_TEXT, aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, extractAddressingContext, extractE2ESessionFromRetryReceipt, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, handleIdentityChange, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, SERVER_ERROR_CODES, toNumber, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey, generateWAMessageFromContent } from '../Utils/index.js';
9
- import { makeMutex } from '../Utils/make-mutex.js';
10
- import { makeOfflineNodeProcessor } from '../Utils/offline-node-processor.js';
11
- import { buildAckStanza } from '../Utils/stanza-ack.js';
12
- import { buildMergedTcTokenIndexWrite, isTcTokenExpired, readTcTokenIndex, resolveIssuanceJid, resolveTcTokenJid, storeTcTokensFromIqResult, TC_TOKEN_INDEX_KEY } from '../Utils/tc-token-utils.js';
13
- import { areJidsSameUser, binaryNodeToString, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, getBinaryNodeChildUInt, isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
14
- import { extractGroupMetadata } from './groups.js';
15
- 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
- export const makeMessagesRecvSocket = (config) => {
21
- const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
22
- const sock = makeMessagesSocket(config);
23
- const { userDevicesCache, devicesMutex, ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, sendMessage, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager, registerSocketEndHandler, issuePrivacyTokens, fetchAccountReachoutTimelock, placeholderResendCache } = sock;
24
- const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.makeMessagesRecvSocket = void 0;
7
+ const boom_1 = require("@hapi/boom");
8
+ const crypto_1 = require("crypto");
9
+ const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
10
+ const WAProto_1 = require("../../WAProto");
11
+ const Defaults_1 = require("../Defaults");
12
+ const Types_1 = require("../Types");
13
+ const Utils_1 = require("../Utils");
14
+ const make_mutex_1 = require("../Utils/make-mutex");
15
+ const WABinary_1 = require("../WABinary");
16
+ const groups_1 = require("./groups");
17
+ const messages_send_1 = require("./messages-send");
18
+ const makeMessagesRecvSocket = (config) => {
19
+ const {
20
+ logger,
21
+ retryRequestDelayMs,
22
+ maxMsgRetryCount,
23
+ getMessage,
24
+ shouldIgnoreJid
25
+ } = config;
26
+ const sock = (0, messages_send_1.makeMessagesSocket)(config);
27
+ const {
28
+ ev,
29
+ authState,
30
+ ws,
31
+ processingMutex,
32
+ signalRepository,
33
+ query,
34
+ upsertMessage,
35
+ resyncAppState,
36
+ groupMetadata,
37
+ onUnexpectedError,
38
+ assertSessions,
39
+ sendNode,
40
+ relayMessage,
41
+ sendReceipt,
42
+ uploadPreKeys,
43
+ createParticipantNodes,
44
+ getUSyncDevices,
45
+ sendPeerDataOperationMessage
46
+ } = sock;
25
47
  /** this mutex ensures that each retryRequest will wait for the previous one to finish */
26
- const retryMutex = makeMutex();
27
- const msgRetryCache = config.msgRetryCounterCache ||
28
- new NodeCache({
29
- stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
30
- useClones: false
31
- });
32
- const callOfferCache = config.callOfferCache ||
33
- new NodeCache({
34
- stdTTL: DEFAULT_CACHE_TTLS.CALL_OFFER, // 5 mins
35
- useClones: false
36
- });
37
- // Debounce identity-change session refreshes per JID to avoid bursts
38
- const identityAssertDebounce = new NodeCache({ stdTTL: 5, useClones: false });
48
+ const retryMutex = (0, make_mutex_1.makeMutex)();
49
+ const msgRetryCache = config.msgRetryCounterCache || new node_cache_1.default({
50
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
51
+ useClones: false
52
+ });
53
+ const callOfferCache = config.callOfferCache || new node_cache_1.default({
54
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.CALL_OFFER, // 5 mins
55
+ useClones: false
56
+ });
57
+ const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
58
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
59
+ useClones: false
60
+ });
39
61
  let sendActiveReceipts = false;
40
- const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
41
- if (!authState.creds.me?.id) {
42
- throw new Boom('Not authenticated');
62
+ const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
63
+ const stanza = {
64
+ tag: 'ack',
65
+ attrs: {
66
+ id: attrs.id,
67
+ to: attrs.from,
68
+ class: tag
69
+ }
43
70
  }
44
- const pdoMessage = {
45
- historySyncOnDemandRequest: {
46
- chatJid: oldestMsgKey.remoteJid,
47
- oldestMsgFromMe: oldestMsgKey.fromMe,
48
- oldestMsgId: oldestMsgKey.id,
49
- oldestMsgTimestampMs: oldestMsgTimestamp,
50
- onDemandMsgCount: count
51
- },
52
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
53
- };
54
- return sendPeerDataOperationMessage(pdoMessage);
55
- };
56
- const requestPlaceholderResend = async (messageKey, msgData) => {
57
- if (!authState.creds.me?.id) {
58
- throw new Boom('Not authenticated');
71
+ if (!!errorCode) {
72
+ stanza.attrs.error = errorCode.toString();
59
73
  }
60
- if (await placeholderResendCache.get(messageKey?.id)) {
61
- logger.debug({ messageKey }, 'already requested resend');
62
- return;
74
+ if (!!attrs.participant) {
75
+ stanza.attrs.participant = attrs.participant;
63
76
  }
64
- else {
65
- // Store original message data so PDO response handler can preserve
66
- // metadata (LID details, timestamps, etc.) that the phone may omit
67
- await placeholderResendCache.set(messageKey?.id, msgData || true);
77
+ if (!!attrs.recipient) {
78
+ stanza.attrs.recipient = attrs.recipient;
68
79
  }
69
- await delay(2000);
70
- if (!(await placeholderResendCache.get(messageKey?.id))) {
71
- logger.debug({ messageKey }, 'message received while resend requested');
72
- return 'RESOLVED';
80
+ if (!!attrs.type && (tag !== 'message' || (0, WABinary_1.getBinaryNodeChild)({ tag, attrs, content }, 'unavailable') || errorCode !== 0)) {
81
+ stanza.attrs.type = attrs.type;
73
82
  }
74
- const pdoMessage = {
75
- placeholderMessageResendRequest: [
76
- {
77
- messageKey
78
- }
79
- ],
80
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
81
- };
82
- setTimeout(async () => {
83
- if (await placeholderResendCache.get(messageKey?.id)) {
84
- logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline');
85
- await placeholderResendCache.del(messageKey?.id);
86
- }
87
- }, 8000);
88
- return sendPeerDataOperationMessage(pdoMessage);
89
- };
90
- const handleMexNotification = async (node) => {
91
- const updateNode = getBinaryNodeChild(node, 'update');
92
- if (updateNode) {
93
- const opName = updateNode.attrs?.op_name;
94
- if (!opName) {
95
- logger.warn({ node: binaryNodeToString(node) }, 'mex notification missing op_name, fallback to legacy');
96
- await handleLegacyMexNewsletterNotification(node);
97
- return;
98
- }
99
- let mexResponse;
100
- try {
101
- mexResponse = JSON.parse(updateNode.content.toString());
102
- }
103
- catch (error) {
104
- logger.error({ err: error, opName }, 'failed to parse mex notification JSON');
105
- return;
106
- }
107
- if (mexResponse.errors?.length) {
108
- logger.warn({ errors: mexResponse.errors, opName }, 'mex notification has GQL errors');
109
- return;
110
- }
111
- const data = mexResponse.data;
112
- if (!data) {
113
- logger.warn({ opName }, 'mex notification has null data');
114
- return;
115
- }
116
- logger.debug({ opName }, 'processing mex notification');
117
- switch (opName) {
118
- case 'NotificationUserReachoutTimelockUpdate':
119
- handleReachoutTimelockNotification(data);
120
- break;
121
- case 'MessageCappingInfoNotification':
122
- handleMessageCappingNotification(data);
123
- break;
124
- // newsletter ops still use the legacy <mex> child structure
125
- case 'NotificationNewsletterUpdate':
126
- case 'NotificationLinkedProfilesUpdates':
127
- case 'NotificationNewsletterAdminPromote':
128
- case 'NotificationNewsletterAdminDemote':
129
- case 'NotificationNewsletterUserSettingChange':
130
- case 'NotificationNewsletterJoin':
131
- case 'NotificationNewsletterLeave':
132
- case 'NotificationNewsletterStateChange':
133
- case 'NotificationNewsletterAdminMetadataUpdate':
134
- case 'NotificationNewsletterOwnerUpdate':
135
- case 'NotificationNewsletterAdminInviteRevoke':
136
- case 'NotificationNewsletterWamoSubStatusChange':
137
- case 'NotificationNewsletterBlockUser':
138
- case 'NotificationNewsletterPaidPartnership':
139
- case 'NotificationNewsletterMilestone':
140
- case 'NewsletterResponseStateUpdate':
141
- await handleLegacyMexNewsletterNotification(node);
142
- break;
143
- default:
144
- logger.debug({ opName }, 'unhandled mex notification');
145
- break;
146
- }
147
- return;
83
+ if (tag === 'message' && (0, WABinary_1.getBinaryNodeChild)({ tag, attrs, content }, 'unavailable')) {
84
+ stanza.attrs.from = authState.creds.me.id;
148
85
  }
149
- await handleLegacyMexNewsletterNotification(node);
86
+ logger.debug({
87
+ recv: {
88
+ tag,
89
+ attrs
90
+ },
91
+ sent: stanza.attrs }, 'sent ack');
92
+ await sendNode(stanza);
150
93
  };
151
- const handleReachoutTimelockNotification = (data) => {
152
- const payload = data.xwa2_notify_account_reachout_timelock;
153
- if (!payload) {
154
- logger.warn('reachout timelock notification missing payload');
155
- return;
156
- }
157
- if (!payload.is_active) {
158
- logger.info('reachout timelock restriction lifted');
159
- ev.emit('connection.update', {
160
- reachoutTimeLock: {
161
- isActive: false,
162
- enforcementType: ReachoutTimelockEnforcementType.DEFAULT
94
+ const offerCall = async (toJid, isVideo = false) => {
95
+ const callId = (0, crypto_1.randomBytes)(16).toString('hex').toUpperCase().substring(0, 64);
96
+ const offerContent = [];
97
+ offerContent.push({
98
+ tag: 'audio',
99
+ attrs: {
100
+ enc: 'opus',
101
+ rate: '16000'
102
+ }, content: undefined
103
+ });
104
+ offerContent.push({
105
+ tag: 'audio',
106
+ attrs: {
107
+ enc: 'opus',
108
+ rate: '8000'
109
+ }, content: undefined
110
+ });
111
+ if (isVideo) {
112
+ offerContent.push({
113
+ tag: 'video',
114
+ attrs: {
115
+ orientation: '0',
116
+ 'screen_width': '1920',
117
+ 'screen_height': '1080',
118
+ 'device_orientation': '0',
119
+ enc: 'vp8',
120
+ dec: 'vp8',
163
121
  }
164
122
  });
165
- return;
166
123
  }
167
- // WA Web defaults to now+60s when the server omits the expiry
168
- const timeEnforcementEnds = payload.time_enforcement_ends
169
- ? new Date(parseInt(payload.time_enforcement_ends, 10) * 1000)
170
- : new Date(Date.now() + 60000);
171
- const enforcementType = isValidEnforcementType(payload.enforcement_type)
172
- ? payload.enforcement_type
173
- : ReachoutTimelockEnforcementType.DEFAULT;
174
- logger.info({ enforcementType, timeEnforcementEnds }, 'reachout timelock restriction set');
175
- ev.emit('connection.update', {
176
- reachoutTimeLock: {
177
- isActive: true,
178
- timeEnforcementEnds,
179
- enforcementType
180
- }
124
+ offerContent.push({
125
+ tag: 'net',
126
+ attrs: {
127
+ medium: '3'
128
+ }, content: undefined
181
129
  });
182
- };
183
- const handleMessageCappingNotification = (data) => {
184
- const payload = data.xwa2_notify_new_chat_messages_capping_info_update;
185
- if (!payload) {
186
- logger.warn('message capping notification missing payload');
187
- return;
188
- }
189
- logger.info({ payload }, 'received message capping update');
190
- ev.emit('message-capping.update', payload);
191
- };
192
- const handleLegacyMexNewsletterNotification = async (node) => {
193
- const mexNode = getBinaryNodeChild(node, 'mex');
194
- const updateNode = mexNode?.content ? null : getBinaryNodeChild(node, 'update') || getAllBinaryNodeChildren(node)[0];
195
- const payloadNode = mexNode?.content ? mexNode : updateNode;
196
- if (!payloadNode?.content) {
197
- logger.warn({ node: binaryNodeToString(node) }, 'invalid mex newsletter notification');
198
- return;
199
- }
200
- let data;
201
- try {
202
- const payloadContent = payloadNode.content;
203
- if (Array.isArray(payloadContent)) {
204
- logger.warn({ payloadNode }, 'invalid mex newsletter notification payload format');
205
- return;
206
- }
207
- const contentBuf = typeof payloadContent === 'string' ? Buffer.from(payloadContent, 'binary') : Buffer.from(payloadContent);
208
- data = JSON.parse(contentBuf.toString());
209
- }
210
- catch (error) {
211
- logger.error({ err: error, node: binaryNodeToString(node) }, 'failed to parse mex newsletter notification');
212
- return;
213
- }
214
- const operation = data?.operation ?? payloadNode?.attrs?.op_name;
215
- let updates = data?.updates;
216
- if (!updates) {
217
- const linkedProfiles = data?.data?.xwa2_notify_linked_profiles;
218
- if (linkedProfiles) {
219
- updates = [linkedProfiles];
220
- }
221
- }
222
- if (!updates || !operation) {
223
- logger.warn({ data }, 'invalid mex newsletter notification content');
224
- return;
225
- }
226
- logger.info({ operation, updates }, 'got mex newsletter notification');
227
- switch (operation) {
228
- case 'NotificationNewsletterUpdate':
229
- for (const update of updates) {
230
- if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
231
- ev.emit('newsletter-settings.update', {
232
- id: update.jid,
233
- update: update.settings
234
- });
235
- }
236
- }
237
- break;
238
- case 'NotificationNewsletterAdminPromote':
239
- for (const update of updates) {
240
- if (update.jid && update.user) {
241
- ev.emit('newsletter-participants.update', {
242
- id: update.jid,
243
- author: node.attrs.from,
244
- user: update.user,
245
- new_role: 'ADMIN',
246
- action: 'promote'
247
- });
248
- }
249
- }
250
- break;
251
- case 'NotificationLinkedProfilesUpdates':
252
- for (const update of updates) {
253
- const lid = update?.jid;
254
- const addedProfiles = Array.isArray(update?.added_profiles) ? update.added_profiles : [];
255
- const mappings = [];
256
- for (const profile of addedProfiles) {
257
- const pn = typeof profile === 'string' ? profile : (profile?.pn ?? profile?.jid ?? null);
258
- if (lid && pn) {
259
- const mapping = { lid, pn };
260
- ev.emit('lid-mapping.update', mapping);
261
- mappings.push(mapping);
262
- }
263
- }
264
- await signalRepository.lidMapping.storeLIDPNMappings(mappings);
265
- }
266
- break;
267
- default:
268
- logger.info({ operation, data }, 'unhandled mex newsletter notification');
269
- break;
270
- }
271
- };
272
- // Handles newsletter notifications
273
- const handleNewsletterNotification = async (node) => {
274
- const from = node.attrs.from;
275
- const children = getAllBinaryNodeChildren(node);
276
- const author = node.attrs.participant;
277
- for (const child of children) {
278
- logger.debug({ from, child }, 'got newsletter notification');
279
- switch (child.tag) {
280
- case 'reaction': {
281
- const reactionUpdate = {
282
- id: from,
283
- server_id: child.attrs.message_id,
284
- reaction: {
285
- code: getBinaryNodeChildString(child, 'reaction'),
286
- count: 1
287
- }
288
- };
289
- ev.emit('newsletter.reaction', reactionUpdate);
290
- break;
291
- }
292
- case 'view': {
293
- const viewUpdate = {
294
- id: from,
295
- server_id: child.attrs.message_id,
296
- count: parseInt(child.content?.toString() || '0', 10)
297
- };
298
- ev.emit('newsletter.view', viewUpdate);
299
- break;
300
- }
301
- case 'participant': {
302
- const participantUpdate = {
303
- id: from,
304
- author,
305
- user: child.attrs.jid,
306
- action: child.attrs.action,
307
- new_role: child.attrs.role
308
- };
309
- ev.emit('newsletter-participants.update', participantUpdate);
310
- break;
311
- }
312
- case 'update': {
313
- const settingsNode = getBinaryNodeChild(child, 'settings');
314
- if (settingsNode) {
315
- const update = {};
316
- const nameNode = getBinaryNodeChild(settingsNode, 'name');
317
- if (nameNode?.content)
318
- update.name = nameNode.content.toString();
319
- const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
320
- if (descriptionNode?.content)
321
- update.description = descriptionNode.content.toString();
322
- ev.emit('newsletter-settings.update', {
323
- id: from,
324
- update
325
- });
326
- }
327
- break;
328
- }
329
- case 'message': {
330
- const plaintextNode = getBinaryNodeChild(child, 'plaintext');
331
- if (plaintextNode?.content) {
332
- try {
333
- const contentBuf = typeof plaintextNode.content === 'string'
334
- ? Buffer.from(plaintextNode.content, 'binary')
335
- : Buffer.from(plaintextNode.content);
336
- const messageProto = proto.Message.decode(contentBuf).toJSON();
337
- const fullMessage = proto.WebMessageInfo.fromObject({
338
- key: {
339
- remoteJid: from,
340
- id: child.attrs.message_id || child.attrs.server_id,
341
- fromMe: false // TODO: is this really true though
342
- },
343
- message: messageProto,
344
- messageTimestamp: +child.attrs.t
345
- }).toJSON();
346
- await upsertMessage(fullMessage, 'append');
347
- logger.debug('Processed plaintext newsletter message');
348
- }
349
- catch (error) {
350
- logger.error({ error }, 'Failed to decode plaintext newsletter message');
351
- }
352
- }
353
- break;
354
- }
355
- default:
356
- logger.warn({ node, child }, 'Unknown newsletter notification child');
357
- break;
130
+ offerContent.push({
131
+ tag: 'capability',
132
+ attrs: {
133
+ ver: '1'
134
+ }, content: new Uint8Array([1, 4, 255, 131, 207, 4]) });
135
+ offerContent.push({
136
+ tag: 'encopt',
137
+ attrs: {
138
+ keygen: '2'
139
+ }, content: undefined
140
+ })
141
+ const encKey = (0, crypto_1.randomBytes)(32);
142
+ const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) => (0, WABinary_1.jidEncode)(user, 's.whatsapp.net', device));
143
+ await assertSessions(devices, true);
144
+ const { nodes: destinations, shouldIncludeDeviceIdentity } = await createParticipantNodes(devices, {
145
+ call: {
146
+ callKey: encKey
358
147
  }
148
+ });
149
+ offerContent.push({ tag: 'destination', attrs: {}, content: destinations });
150
+ if (shouldIncludeDeviceIdentity) {
151
+ offerContent.push({
152
+ tag: 'device-identity',
153
+ attrs: {},
154
+ content: (0, Utils_1.encodeSignedDeviceIdentity)(authState.creds.account, true)
155
+ });
359
156
  }
360
- };
361
- const sendMessageAck = async (node, errorCode) => {
362
- const stanza = buildAckStanza(node, errorCode, authState.creds.me.id);
363
- logger.debug({ recv: { tag: node.tag, attrs: node.attrs }, sent: stanza.attrs }, 'sent ack');
364
- await sendNode(stanza);
157
+ const stanza = ({
158
+ tag: 'call',
159
+ attrs: {
160
+ to: toJid,
161
+ },
162
+ content: [{
163
+ tag: 'offer',
164
+ attrs: {
165
+ 'call-id': callId,
166
+ 'call-creator': authState.creds.me.id,
167
+ },
168
+ content: offerContent,
169
+ }],
170
+ });
171
+ await query(stanza);
172
+ return {
173
+ callId,
174
+ toJid,
175
+ isVideo,
176
+ };
365
177
  };
366
178
  const rejectCall = async (callId, callFrom) => {
367
- const stanza = {
179
+ const stanza = ({
368
180
  tag: 'call',
369
181
  attrs: {
370
182
  from: authState.creds.me.id,
371
- to: callFrom
183
+ to: callFrom,
372
184
  },
373
- content: [
374
- {
185
+ content: [{
375
186
  tag: 'reject',
376
187
  attrs: {
377
188
  'call-id': callId,
378
189
  'call-creator': callFrom,
379
- count: '0'
190
+ count: '0',
380
191
  },
381
- content: undefined
382
- }
383
- ]
384
- };
192
+ content: undefined,
193
+ }],
194
+ });
385
195
  await query(stanza);
386
196
  };
387
- const sendText = async (jid, text, options, quoted = null) => {
388
- return sendMessage(jid, {
389
- text,
390
- ...options
391
- }, { quoted })
392
- }
393
- const sendImage = async (jid, image, caption, options, quoted = null) => {
394
- return sendMessage(jid, {
395
- image,
396
- caption,
397
- ...options
398
- }, { quoted })
399
- }
400
- const sendVideo = async (jid, video, caption, options, quoted = null) => {
401
- return sendMessage(jid, {
402
- video,
403
- caption,
404
- ...options
405
- }, { quoted })
406
- }
407
- const sendDocument = async (jid, document, fileName, caption, options, quoted = null) => {
408
- return sendMessage(jid, {
409
- document,
410
- fileName,
411
- caption,
412
- ...options
413
- }, { quoted })
414
- }
415
- const sendAudio = async (jid, audio, options, quoted = null) => {
416
- return sendMessage(jid, {
417
- audio,
418
- ...options
419
- }, { quoted })
420
- }
421
- const sendLocation = async (jid, name, degreesLongitude, degreesLatitude, url, address, options, quoted = null) => {
422
- return sendMessage(jid, {
423
- location: {
424
- degreesLongitude,
425
- degreesLatitude,
426
- name,
427
- url,
428
- address
429
- },
430
- ...options
431
- }, { quoted })
432
- }
433
- const sendPoll = async (jid, name, pollVote = [], multiSelect = false, options, quoted = null) => {
434
- const selectableCount = multiSelect ? pollVote.length : 1;
435
-
436
- return sendMessage(jid, {
437
- poll: {
438
- name,
439
- values: pollVote,
440
- selectableCount
441
- },
442
- ...options
443
- }, { quoted });
444
- }
445
- const sendQuiz = async (
446
- jid,
447
- name,
448
- pollVote = [],
449
- answer,
450
- options,
451
- quoted
452
- ) => {
453
- const poll = {
454
- name,
455
- values: pollVote,
456
- selectableCount: 1,
457
- type: "QUIZ",
458
- answer: { optionName: answer }
459
- }
460
- return sendMessage(jid, {
461
- poll,
462
- ...options
463
- }, { quoted })
464
- }
465
- const sendPtv = (jid, ptv, options, quoted = null) => {
466
- return sendMessage(jid, {
467
- ptv,
468
- ...options
469
- }, { quoted })
470
- }
471
- const statusMention = async (jid, content) => {
472
- const msg = await generateWAMessageFromContent(jid, content, {
473
- userJid: authState.creds.me.id
474
- })
475
- await relayMessage("status@broadcast", msg.message, {
476
- statusJidList: [jid, authState.creds.me.id],
477
- additionalNodes: [
478
- {
479
- tag: "meta",
480
- attrs: {},
481
- content: [
482
- {
483
- tag: "mentioned_users",
484
- attrs: {},
485
- content: [
486
- {
487
- tag: "to",
488
- attrs: { jid },
489
- content: undefined
490
- }
491
- ]
492
- }
493
- ]
494
- }
495
- ]
496
- })
497
-
498
- const mentionMsg = {
499
- statusMentionMessage: {
500
- message: {
501
- protocolMessage: {
502
- key: msg.key,
503
- type: 25,
504
- timestamp: Math.floor(Date.now() / 1000)
505
- }
506
- }
507
- }
508
- }
509
-
510
- const x = generateWAMessageFromContent(jid, mentionMsg, {})
511
- return relayMessage(jid, x.message, {
512
- messageId: x.key.id,
513
- additionalNodes: [
514
- {
515
- tag: "meta",
516
- attrs: { is_status_mention: "true" }
517
- }
518
- ]
519
- })
520
- };
521
197
  const sendRetryRequest = async (node, forceIncludeKeys = false) => {
522
- const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '');
198
+ const { fullMessage } = (0, Utils_1.decodeMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '');
523
199
  const { key: msgKey } = fullMessage;
524
200
  const msgId = msgKey.id;
525
- if (messageRetryManager) {
526
- // Check if we've exceeded max retries using the new system
527
- if (messageRetryManager.hasExceededMaxRetries(msgId)) {
528
- logger.debug({ msgId }, 'reached retry limit with new retry manager, clearing');
529
- messageRetryManager.markRetryFailed(msgId);
530
- return;
531
- }
532
- // Increment retry count using new system
533
- const retryCount = messageRetryManager.incrementRetryCount(msgId);
534
- // Use the new retry count for the rest of the logic
535
- const key = `${msgId}:${msgKey?.participant}`;
536
- await msgRetryCache.set(key, retryCount);
537
- }
538
- else {
539
- // Fallback to old system
540
- const key = `${msgId}:${msgKey?.participant}`;
541
- let retryCount = (await msgRetryCache.get(key)) || 0;
542
- if (retryCount >= maxMsgRetryCount) {
543
- logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
544
- await msgRetryCache.del(key);
545
- return;
546
- }
547
- retryCount += 1;
548
- await msgRetryCache.set(key, retryCount);
201
+ const key = `${msgId}:${msgKey === null || msgKey === void 0 ? void 0 : msgKey.participant}`;
202
+ let retryCount = msgRetryCache.get(key) || 0;
203
+ if (retryCount >= maxMsgRetryCount) {
204
+ logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
205
+ msgRetryCache.del(key);
206
+ return;
549
207
  }
550
- const key = `${msgId}:${msgKey?.participant}`;
551
- const retryCount = (await msgRetryCache.get(key)) || 1;
208
+ retryCount += 1;
209
+ msgRetryCache.set(key, retryCount);
552
210
  const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
553
- const fromJid = node.attrs.from;
554
- // Check if we should recreate the session
555
- let shouldRecreateSession = false;
556
- let recreateReason = '';
557
- if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
558
- try {
559
- // Check if we have a session with this JID
560
- const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid);
561
- const hasSession = await signalRepository.validateSession(fromJid);
562
- const result = messageRetryManager.shouldRecreateSession(fromJid, hasSession.exists);
563
- shouldRecreateSession = result.recreate;
564
- recreateReason = result.reason;
565
- if (shouldRecreateSession) {
566
- logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry');
567
- // Delete existing session to force recreation
568
- await authState.keys.set({ session: { [sessionId]: null } });
569
- forceIncludeKeys = true;
570
- }
571
- }
572
- catch (error) {
573
- logger.warn({ error, fromJid }, 'failed to check session recreation');
574
- }
575
- }
576
- if (retryCount <= 2) {
577
- // Use new retry manager for phone requests if available
578
- if (messageRetryManager) {
579
- // Schedule phone request with delay (like whatsmeow)
580
- messageRetryManager.schedulePhoneRequest(msgId, async () => {
581
- try {
582
- const requestId = await requestPlaceholderResend(msgKey);
583
- logger.debug(`sendRetryRequest: requested placeholder resend (${requestId}) for message ${msgId} (scheduled)`);
584
- }
585
- catch (error) {
586
- logger.warn({ error, msgId }, 'failed to send scheduled phone request');
587
- }
588
- });
589
- }
590
- else {
591
- // Fallback to immediate request
592
- const msgId = await requestPlaceholderResend(msgKey);
593
- logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
594
- }
211
+ if (retryCount === 1) {
212
+ //request a resend via phone
213
+ const msgId = await requestPlaceholderResend(msgKey);
214
+ logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
595
215
  }
596
- const deviceIdentity = encodeSignedDeviceIdentity(account, true);
216
+ const deviceIdentity = (0, Utils_1.encodeSignedDeviceIdentity)(account, true);
597
217
  await authState.keys.transaction(async () => {
598
218
  const receipt = {
599
219
  tag: 'receipt',
@@ -609,15 +229,13 @@ export const makeMessagesRecvSocket = (config) => {
609
229
  count: retryCount.toString(),
610
230
  id: node.attrs.id,
611
231
  t: node.attrs.t,
612
- v: '1',
613
- // ADD ERROR FIELD
614
- error: '0'
232
+ v: '1'
615
233
  }
616
234
  },
617
235
  {
618
236
  tag: 'registration',
619
237
  attrs: {},
620
- content: encodeBigEndian(authState.creds.registrationId)
238
+ content: (0, Utils_1.encodeBigEndian)(authState.creds.registrationId)
621
239
  }
622
240
  ]
623
241
  };
@@ -627,8 +245,8 @@ export const makeMessagesRecvSocket = (config) => {
627
245
  if (node.attrs.participant) {
628
246
  receipt.attrs.participant = node.attrs.participant;
629
247
  }
630
- if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
631
- const { update, preKeys } = await getNextPreKeys(authState, 1);
248
+ if (retryCount > 1 || forceIncludeKeys) {
249
+ const { update, preKeys } = await (0, Utils_1.getNextPreKeys)(authState, 1);
632
250
  const [keyId] = Object.keys(preKeys);
633
251
  const key = preKeys[+keyId];
634
252
  const content = receipt.content;
@@ -636,10 +254,10 @@ export const makeMessagesRecvSocket = (config) => {
636
254
  tag: 'keys',
637
255
  attrs: {},
638
256
  content: [
639
- { tag: 'type', attrs: {}, content: Buffer.from(KEY_BUNDLE_TYPE) },
257
+ { tag: 'type', attrs: {}, content: Buffer.from(Defaults_1.KEY_BUNDLE_TYPE) },
640
258
  { tag: 'identity', attrs: {}, content: identityKey.public },
641
- xmppPreKey(key, +keyId),
642
- xmppSignedPreKey(signedPreKey),
259
+ (0, Utils_1.xmppPreKey)(key, +keyId),
260
+ (0, Utils_1.xmppSignedPreKey)(signedPreKey),
643
261
  { tag: 'device-identity', attrs: {}, content: deviceIdentity }
644
262
  ]
645
263
  });
@@ -647,119 +265,63 @@ export const makeMessagesRecvSocket = (config) => {
647
265
  }
648
266
  await sendNode(receipt);
649
267
  logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
650
- }, authState?.creds?.me?.id || 'sendRetryRequest');
651
- };
652
- // Mirrors WAWeb/Handle/PreKeyLow.js: skip a re-issued notification with the same stanza id.
653
- const inFlightPreKeyLow = new Set();
654
- /**
655
- * Fire-and-forget tctoken re-issuance after a peer's device identity changed.
656
- * Mirrors WAWebSendTcTokenWhenDeviceIdentityChange — runs in parallel with
657
- * the session refresh (not after it).
658
- */
659
- const reissueTcTokenAfterIdentityChange = (from) => {
660
- void (async () => {
661
- const normalizedJid = jidNormalizedUser(from);
662
- const tcJid = await resolveTcTokenJid(normalizedJid, getLIDForPN);
663
- const tcTokenData = await authState.keys.get('tctoken', [tcJid]);
664
- const senderTs = tcTokenData?.[tcJid]?.senderTimestamp;
665
- if (senderTs === null || senderTs === undefined || isTcTokenExpired(senderTs)) {
666
- return;
667
- }
668
- logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken');
669
- const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
670
- const issueJid = await resolveIssuanceJid(normalizedJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
671
- const result = await issuePrivacyTokens([issueJid], senderTs);
672
- await storeTcTokensFromIqResult({
673
- result,
674
- fallbackJid: tcJid,
675
- keys: authState.keys,
676
- getLIDForPN,
677
- onNewJidStored: trackTcTokenJid
678
- });
679
- })().catch(err => {
680
- logger.debug({ jid: from, err: err?.message }, 'failed to re-issue tctoken after identity change');
681
268
  });
682
269
  };
683
270
  const handleEncryptNotification = async (node) => {
684
271
  const from = node.attrs.from;
685
- if (from === S_WHATSAPP_NET) {
686
- const stanzaId = node.attrs.id;
687
- if (stanzaId && inFlightPreKeyLow.has(stanzaId)) {
688
- return;
689
- }
690
- const countChild = getBinaryNodeChild(node, 'count');
272
+ if (from === WABinary_1.S_WHATSAPP_NET) {
273
+ const countChild = (0, WABinary_1.getBinaryNodeChild)(node, 'count');
691
274
  const count = +countChild.attrs.value;
692
- const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT;
275
+ const shouldUploadMorePreKeys = count < Defaults_1.MIN_PREKEY_COUNT;
693
276
  logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count');
694
277
  if (shouldUploadMorePreKeys) {
695
- if (stanzaId)
696
- inFlightPreKeyLow.add(stanzaId);
697
- try {
698
- await uploadPreKeys();
699
- }
700
- finally {
701
- if (stanzaId)
702
- inFlightPreKeyLow.delete(stanzaId);
703
- }
278
+ await uploadPreKeys();
704
279
  }
705
280
  }
706
281
  else {
707
- const result = await handleIdentityChange(node, {
708
- meId: authState.creds.me?.id,
709
- meLid: authState.creds.me?.lid,
710
- validateSession: signalRepository.validateSession,
711
- assertSessions,
712
- debounceCache: identityAssertDebounce,
713
- logger,
714
- onBeforeSessionRefresh: reissueTcTokenAfterIdentityChange
715
- });
716
- if (result.action === 'no_identity_node') {
282
+ const identityNode = (0, WABinary_1.getBinaryNodeChild)(node, 'identity');
283
+ if (identityNode) {
284
+ logger.info({ jid: from }, 'identity changed');
285
+ // not handling right now
286
+ // signal will override new identity anyway
287
+ }
288
+ else {
717
289
  logger.info({ node }, 'unknown encrypt notification');
718
290
  }
719
291
  }
720
292
  };
721
- const handleGroupNotification = (fullNode, child, msg) => {
722
- // TODO: Support PN/LID (Here is only LID now)
723
- const actingParticipantLid = fullNode.attrs.participant;
724
- const actingParticipantPn = fullNode.attrs.participant_pn;
725
- const actingParticipantUsername = fullNode.attrs.participant_username;
726
- const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid;
727
- const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
728
- switch (child?.tag) {
293
+ const handleGroupNotification = (participant, child, msg) => {
294
+ var _a, _b, _c, _d;
295
+ const participantJid = ((_b = (_a = (0, WABinary_1.getBinaryNodeChild)(child, 'participant')) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.jid) || participant;
296
+ switch (child === null || child === void 0 ? void 0 : child.tag) {
729
297
  case 'create':
730
- const metadata = extractGroupMetadata(child);
731
- msg.messageStubType = WAMessageStubType.GROUP_CREATE;
298
+ const metadata = (0, groups_1.extractGroupMetadata)(child);
299
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE;
732
300
  msg.messageStubParameters = [metadata.subject];
733
- msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn };
734
- ev.emit('chats.upsert', [
735
- {
301
+ msg.key = { participant: metadata.owner };
302
+ ev.emit('chats.upsert', [{
736
303
  id: metadata.id,
737
304
  name: metadata.subject,
738
- conversationTimestamp: metadata.creation
739
- }
740
- ]);
741
- ev.emit('groups.upsert', [
742
- {
305
+ conversationTimestamp: metadata.creation,
306
+ }]);
307
+ ev.emit('groups.upsert', [{
743
308
  ...metadata,
744
- author: actingParticipantLid,
745
- authorPn: actingParticipantPn,
746
- authorUsername: actingParticipantUsername
747
- }
748
- ]);
309
+ author: participant
310
+ }]);
749
311
  break;
750
312
  case 'ephemeral':
751
313
  case 'not_ephemeral':
752
314
  msg.message = {
753
315
  protocolMessage: {
754
- type: proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
316
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
755
317
  ephemeralExpiration: +(child.attrs.expiration || 0)
756
318
  }
757
319
  };
758
320
  break;
759
321
  case 'modify':
760
- const oldNumber = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid);
322
+ const oldNumber = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(p => p.attrs.jid);
761
323
  msg.messageStubParameters = oldNumber || [];
762
- msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER;
324
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER;
763
325
  break;
764
326
  case 'promote':
765
327
  case 'demote':
@@ -767,224 +329,179 @@ export const makeMessagesRecvSocket = (config) => {
767
329
  case 'add':
768
330
  case 'leave':
769
331
  const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`;
770
- msg.messageStubType = WAMessageStubType[stubType];
771
- const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
772
- // TODO: Store LID MAPPINGS
773
- return {
774
- id: attrs.jid,
775
- phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
776
- lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
777
- username: attrs.participant_username || attrs.username || undefined,
778
- admin: (attrs.type || null)
779
- };
780
- });
332
+ msg.messageStubType = Types_1.WAMessageStubType[stubType];
333
+ const participants = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(p => p.attrs.jid);
781
334
  if (participants.length === 1 &&
782
335
  // if recv. "remove" message and sender removed themselves
783
336
  // mark as left
784
- (areJidsSameUser(participants[0].id, actingParticipantLid) ||
785
- areJidsSameUser(participants[0].id, actingParticipantPn)) &&
337
+ (0, WABinary_1.areJidsSameUser)(participants[0], participant) &&
786
338
  child.tag === 'remove') {
787
- msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
339
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
788
340
  }
789
- msg.messageStubParameters = participants.map(a => JSON.stringify(a));
341
+ msg.messageStubParameters = participants;
790
342
  break;
791
343
  case 'subject':
792
- msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT;
344
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_SUBJECT;
793
345
  msg.messageStubParameters = [child.attrs.subject];
794
346
  break;
795
347
  case 'description':
796
- const description = getBinaryNodeChild(child, 'body')?.content?.toString();
797
- msg.messageStubType = WAMessageStubType.GROUP_CHANGE_DESCRIPTION;
348
+ const description = (_d = (_c = (0, WABinary_1.getBinaryNodeChild)(child, 'body')) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d.toString();
349
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_DESCRIPTION;
798
350
  msg.messageStubParameters = description ? [description] : undefined;
799
351
  break;
800
352
  case 'announcement':
801
353
  case 'not_announcement':
802
- msg.messageStubType = WAMessageStubType.GROUP_CHANGE_ANNOUNCE;
803
- msg.messageStubParameters = [child.tag === 'announcement' ? 'on' : 'off'];
354
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ANNOUNCE;
355
+ msg.messageStubParameters = [(child.tag === 'announcement') ? 'on' : 'off'];
804
356
  break;
805
357
  case 'locked':
806
358
  case 'unlocked':
807
- msg.messageStubType = WAMessageStubType.GROUP_CHANGE_RESTRICT;
808
- msg.messageStubParameters = [child.tag === 'locked' ? 'on' : 'off'];
359
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_RESTRICT;
360
+ msg.messageStubParameters = [(child.tag === 'locked') ? 'on' : 'off'];
809
361
  break;
810
362
  case 'invite':
811
- msg.messageStubType = WAMessageStubType.GROUP_CHANGE_INVITE_LINK;
363
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_INVITE_LINK;
812
364
  msg.messageStubParameters = [child.attrs.code];
813
365
  break;
814
366
  case 'member_add_mode':
815
367
  const addMode = child.content;
816
368
  if (addMode) {
817
- msg.messageStubType = WAMessageStubType.GROUP_MEMBER_ADD_MODE;
369
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBER_ADD_MODE;
818
370
  msg.messageStubParameters = [addMode.toString()];
819
371
  }
820
372
  break;
821
373
  case 'membership_approval_mode':
822
- const approvalMode = getBinaryNodeChild(child, 'group_join');
374
+ const approvalMode = (0, WABinary_1.getBinaryNodeChild)(child, 'group_join');
823
375
  if (approvalMode) {
824
- msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE;
376
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE;
825
377
  msg.messageStubParameters = [approvalMode.attrs.state];
826
378
  }
827
379
  break;
828
380
  case 'created_membership_requests':
829
- msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
830
- msg.messageStubParameters = [
831
- JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
832
- 'created',
833
- child.attrs.request_method
834
- ];
381
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
382
+ msg.messageStubParameters = [participantJid, 'created', child.attrs.request_method];
835
383
  break;
836
384
  case 'revoked_membership_requests':
837
- const isDenied = areJidsSameUser(affectedParticipantLid, actingParticipantLid);
838
- // TODO: LIDMAPPING SUPPORT
839
- msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
840
- msg.messageStubParameters = [
841
- JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
842
- isDenied ? 'revoked' : 'rejected'
843
- ];
385
+ const isDenied = (0, WABinary_1.areJidsSameUser)(participantJid, participant);
386
+ msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
387
+ msg.messageStubParameters = [participantJid, isDenied ? 'revoked' : 'rejected'];
388
+ break;
844
389
  break;
390
+ default:
391
+ // console.log("BAILEYS-DEBUG:", JSON.stringify({ ...child, content: Buffer.isBuffer(child.content) ? child.content.toString() : child.content, participant }, null, 2))
845
392
  }
846
393
  };
847
- const handleDevicesNotification = async (node) => {
848
- const [child] = getAllBinaryNodeChildren(node);
849
- const from = jidNormalizedUser(node.attrs.from);
850
- if (!child) {
851
- logger.debug({ from }, 'devices notification missing child, skipping');
852
- return;
853
- }
854
- const tag = child.tag;
855
- const deviceHash = child.attrs.device_hash;
856
- const devices = getBinaryNodeChildren(child, 'device');
857
- if (areJidsSameUser(from, authState.creds.me.id) || areJidsSameUser(from, authState.creds.me.lid)) {
858
- const deviceJids = devices.map(d => d.attrs.jid);
859
- logger.info({ deviceJids }, 'got my own devices');
860
- }
861
- if (!devices.length) {
862
- logger.debug({ from, tag }, 'no devices in notification, skipping');
863
- return;
864
- }
865
- const decoded = [];
866
- for (const d of devices) {
867
- const jid = d.attrs.jid;
868
- if (!jid)
869
- continue;
870
- const parts = jidDecode(jid);
871
- if (!parts) {
872
- logger.debug({ jid }, 'failed to decode device jid, skipping');
873
- continue;
394
+ const handleNewsletterNotification = (id, node) => {
395
+ const messages = (0, WABinary_1.getBinaryNodeChild)(node, 'messages');
396
+ const message = (0, WABinary_1.getBinaryNodeChild)(messages, 'message');
397
+ const serverId = message.attrs.server_id;
398
+ const reactionsList = (0, WABinary_1.getBinaryNodeChild)(message, 'reactions');
399
+ const viewsList = (0, WABinary_1.getBinaryNodeChildren)(message, 'views_count');
400
+ if (reactionsList) {
401
+ const reactions = (0, WABinary_1.getBinaryNodeChildren)(reactionsList, 'reaction');
402
+ if (reactions.length === 0) {
403
+ ev.emit('newsletter.reaction', { id, 'server_id': serverId, reaction: { removed: true } });
874
404
  }
875
- decoded.push({ jid, user: parts.user, server: parts.server, device: parts.device });
405
+ reactions.forEach(item => {
406
+ var _a, _b;
407
+ ev.emit('newsletter.reaction', { id, 'server_id': serverId, reaction: { code: (_a = item.attrs) === null || _a === void 0 ? void 0 : _a.code, count: +((_b = item.attrs) === null || _b === void 0 ? void 0 : _b.count) } });
408
+ });
876
409
  }
877
- if (!decoded.length)
878
- return;
879
- await devicesMutex.mutex(async () => {
880
- const byUser = new Map();
881
- for (const d of decoded) {
882
- const list = byUser.get(d.user) || [];
883
- list.push(d);
884
- byUser.set(d.user, list);
410
+ if (viewsList.length) {
411
+ viewsList.forEach(item => {
412
+ ev.emit('newsletter.view', { id, 'server_id': serverId, count: +item.attrs.count });
413
+ });
414
+ }
415
+ };
416
+ const handleMexNewsletterNotification = (id, node) => {
417
+ var _a;
418
+ const operation = node === null || node === void 0 ? void 0 : node.attrs.op_name;
419
+ const content = JSON.parse((_a = node === null || node === void 0 ? void 0 : node.content) === null || _a === void 0 ? void 0 : _a.toString());
420
+ let contentPath;
421
+ if (operation === Types_1.MexOperations.PROMOTE || operation === Types_1.MexOperations.DEMOTE) {
422
+ let action;
423
+ if (operation === Types_1.MexOperations.PROMOTE) {
424
+ action = 'promote';
425
+ contentPath = content.data[Types_1.XWAPaths.PROMOTE];
885
426
  }
886
- for (const [user, entries] of byUser) {
887
- if (tag === 'update') {
888
- logger.debug({ user }, `${user}'s device list updated, dropping cached devices`);
889
- await userDevicesCache?.del(user);
890
- continue;
891
- }
892
- if (tag === 'remove') {
893
- await signalRepository.deleteSession(entries.map(e => e.jid));
894
- }
895
- const existingCache = (await userDevicesCache?.get(user)) || [];
896
- if (!existingCache.length) {
897
- // No baseline yet; skip applying the delta so getUSyncDevices can
898
- // later fetch the full device list. Caching just the notification
899
- // entries would make a partial list look authoritative.
900
- logger.debug({ user, tag }, 'device list not cached, deferring to USync refresh');
901
- continue;
902
- }
903
- const affected = new Set(entries.map(e => e.device));
904
- let updatedDevices;
905
- switch (tag) {
906
- case 'add':
907
- logger.info({ deviceHash, count: entries.length }, 'devices added');
908
- updatedDevices = [
909
- ...existingCache.filter(d => !affected.has(d.device)),
910
- ...entries.map(e => ({ user: e.user, server: e.server, device: e.device }))
911
- ];
912
- break;
913
- case 'remove':
914
- logger.info({ deviceHash, count: entries.length }, 'devices removed');
915
- updatedDevices = existingCache.filter(d => !affected.has(d.device));
916
- break;
917
- default:
918
- logger.debug({ tag }, 'Unknown device list change tag');
919
- continue;
920
- }
921
- if (updatedDevices.length === 0) {
922
- await userDevicesCache?.del(user);
923
- }
924
- else {
925
- await userDevicesCache?.set(user, updatedDevices);
926
- }
427
+ if (operation === Types_1.MexOperations.DEMOTE) {
428
+ action = 'demote';
429
+ contentPath = content.data[Types_1.XWAPaths.DEMOTE];
927
430
  }
928
- });
431
+ ev.emit('newsletter-participants.update', { id, author: contentPath.actor.pn, user: contentPath.user.pn, new_role: contentPath.user_new_role, action });
432
+ }
433
+ if (operation === Types_1.MexOperations.UPDATE) {
434
+ contentPath = content.data[Types_1.XWAPaths.METADATA_UPDATE];
435
+ ev.emit('newsletter-settings.update', { id, update: contentPath.thread_metadata.settings });
436
+ }
929
437
  };
930
438
  const processNotification = async (node) => {
439
+ var _a, _b;
931
440
  const result = {};
932
- const [child] = getAllBinaryNodeChildren(node);
441
+ const [child] = (0, WABinary_1.getAllBinaryNodeChildren)(node);
933
442
  const nodeType = node.attrs.type;
934
- const from = jidNormalizedUser(node.attrs.from);
443
+ const from = (0, WABinary_1.jidNormalizedUser)(node.attrs.from);
935
444
  switch (nodeType) {
445
+ case 'privacy_token':
446
+ const tokenList = (0, WABinary_1.getBinaryNodeChildren)(child, 'token');
447
+ for (const { attrs, content } of tokenList) {
448
+ const jid = attrs.jid;
449
+ ev.emit('chats.update', [
450
+ {
451
+ id: jid,
452
+ tcToken: content
453
+ }
454
+ ]);
455
+ logger.debug({ jid }, 'got privacy token update');
456
+ }
457
+ break;
936
458
  case 'newsletter':
937
- await handleNewsletterNotification(node);
459
+ handleNewsletterNotification(node.attrs.from, child);
938
460
  break;
939
461
  case 'mex':
940
- await handleMexNotification(node);
462
+ handleMexNewsletterNotification(node.attrs.from, child);
941
463
  break;
942
464
  case 'w:gp2':
943
- // TODO: HANDLE PARTICIPANT_PN
944
- handleGroupNotification(node, child, result);
465
+ handleGroupNotification(node.attrs.participant, child, result);
945
466
  break;
946
467
  case 'mediaretry':
947
- const event = decodeMediaRetryNode(node);
468
+ const event = (0, Utils_1.decodeMediaRetryNode)(node);
948
469
  ev.emit('messages.media-update', [event]);
949
470
  break;
950
471
  case 'encrypt':
951
472
  await handleEncryptNotification(node);
952
473
  break;
953
474
  case 'devices':
954
- try {
955
- await handleDevicesNotification(node);
956
- }
957
- catch (error) {
958
- logger.error({ error, node }, 'failed to handle devices notification');
475
+ const devices = (0, WABinary_1.getBinaryNodeChildren)(child, 'device');
476
+ if ((0, WABinary_1.areJidsSameUser)(child.attrs.jid, authState.creds.me.id)) {
477
+ const deviceJids = devices.map(d => d.attrs.jid);
478
+ logger.info({ deviceJids }, 'got my own devices');
959
479
  }
960
480
  break;
961
481
  case 'server_sync':
962
- const update = getBinaryNodeChild(node, 'collection');
482
+ const update = (0, WABinary_1.getBinaryNodeChild)(node, 'collection');
963
483
  if (update) {
964
484
  const name = update.attrs.name;
965
485
  await resyncAppState([name], false);
966
486
  }
967
487
  break;
968
488
  case 'picture':
969
- const setPicture = getBinaryNodeChild(node, 'set');
970
- const delPicture = getBinaryNodeChild(node, 'delete');
971
- // TODO: WAJIDHASH stuff proper support inhouse
972
- ev.emit('contacts.update', [
973
- {
974
- id: jidNormalizedUser(node?.attrs?.from) || (setPicture || delPicture)?.attrs?.hash || '',
489
+ const setPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'set');
490
+ const delPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'delete');
491
+ ev.emit('contacts.update', [{
492
+ id: from || ((_b = (_a = (setPicture || delPicture)) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.hash) || '',
975
493
  imgUrl: setPicture ? 'changed' : 'removed'
976
- }
977
- ]);
978
- if (isJidGroup(from)) {
494
+ }]);
495
+ if ((0, WABinary_1.isJidGroup)(from)) {
979
496
  const node = setPicture || delPicture;
980
- result.messageStubType = WAMessageStubType.GROUP_CHANGE_ICON;
497
+ result.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ICON;
981
498
  if (setPicture) {
982
499
  result.messageStubParameters = [setPicture.attrs.id];
983
500
  }
984
- result.participant = node?.attrs.author;
501
+ result.participant = node === null || node === void 0 ? void 0 : node.attrs.author;
985
502
  result.key = {
986
- ...(result.key || {}),
987
- participant: setPicture?.attrs.author
503
+ ...result.key || {},
504
+ participant: setPicture === null || setPicture === void 0 ? void 0 : setPicture.attrs.author
988
505
  };
989
506
  }
990
507
  break;
@@ -998,48 +515,44 @@ export const makeMessagesRecvSocket = (config) => {
998
515
  ...authState.creds.accountSettings,
999
516
  defaultDisappearingMode: {
1000
517
  ephemeralExpiration: newDuration,
1001
- ephemeralSettingTimestamp: timestamp
1002
- }
518
+ ephemeralSettingTimestamp: timestamp,
519
+ },
1003
520
  }
1004
521
  });
1005
522
  }
1006
523
  else if (child.tag === 'blocklist') {
1007
- const blocklists = getBinaryNodeChildren(child, 'item');
524
+ const blocklists = (0, WABinary_1.getBinaryNodeChildren)(child, 'item');
1008
525
  for (const { attrs } of blocklists) {
1009
526
  const blocklist = [attrs.jid];
1010
- const type = attrs.action === 'block' ? 'add' : 'remove';
527
+ const type = (attrs.action === 'block') ? 'add' : 'remove';
1011
528
  ev.emit('blocklist.update', { blocklist, type });
1012
529
  }
1013
530
  }
1014
531
  break;
1015
532
  case 'link_code_companion_reg':
1016
- const linkCodeCompanionReg = getBinaryNodeChild(node, 'link_code_companion_reg');
1017
- const ref = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_ref'));
1018
- const primaryIdentityPublicKey = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'primary_identity_pub'));
1019
- const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'));
533
+ const linkCodeCompanionReg = (0, WABinary_1.getBinaryNodeChild)(node, 'link_code_companion_reg');
534
+ const ref = toRequiredBuffer((0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'link_code_pairing_ref'));
535
+ const primaryIdentityPublicKey = toRequiredBuffer((0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'primary_identity_pub'));
536
+ const primaryEphemeralPublicKeyWrapped = toRequiredBuffer((0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'));
1020
537
  const codePairingPublicKey = await decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped);
1021
- const companionSharedKey = Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey);
1022
- const random = randomBytes(32);
1023
- const linkCodeSalt = randomBytes(32);
1024
- const linkCodePairingExpanded = hkdf(companionSharedKey, 32, {
538
+ const companionSharedKey = Utils_1.Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey);
539
+ const random = (0, crypto_1.randomBytes)(32);
540
+ const linkCodeSalt = (0, crypto_1.randomBytes)(32);
541
+ const linkCodePairingExpanded = await (0, Utils_1.hkdf)(companionSharedKey, 32, {
1025
542
  salt: linkCodeSalt,
1026
543
  info: 'link_code_pairing_key_bundle_encryption_key'
1027
544
  });
1028
- const encryptPayload = Buffer.concat([
1029
- Buffer.from(authState.creds.signedIdentityKey.public),
1030
- primaryIdentityPublicKey,
1031
- random
1032
- ]);
1033
- const encryptIv = randomBytes(12);
1034
- const encrypted = aesEncryptGCM(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0));
545
+ const encryptPayload = Buffer.concat([Buffer.from(authState.creds.signedIdentityKey.public), primaryIdentityPublicKey, random]);
546
+ const encryptIv = (0, crypto_1.randomBytes)(12);
547
+ const encrypted = (0, Utils_1.aesEncryptGCM)(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0));
1035
548
  const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted]);
1036
- const identitySharedKey = Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey);
549
+ const identitySharedKey = Utils_1.Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey);
1037
550
  const identityPayload = Buffer.concat([companionSharedKey, identitySharedKey, random]);
1038
- authState.creds.advSecretKey = Buffer.from(hkdf(identityPayload, 32, { info: 'adv_secret' })).toString('base64');
551
+ authState.creds.advSecretKey = (await (0, Utils_1.hkdf)(identityPayload, 32, { info: 'adv_secret' })).toString('base64');
1039
552
  await query({
1040
553
  tag: 'iq',
1041
554
  attrs: {
1042
- to: S_WHATSAPP_NET,
555
+ to: WABinary_1.S_WHATSAPP_NET,
1043
556
  type: 'set',
1044
557
  id: sock.generateMessageTag(),
1045
558
  xmlns: 'md'
@@ -1049,7 +562,7 @@ export const makeMessagesRecvSocket = (config) => {
1049
562
  tag: 'link_code_companion_reg',
1050
563
  attrs: {
1051
564
  jid: authState.creds.me.id,
1052
- stage: 'companion_finish'
565
+ stage: 'companion_finish',
1053
566
  },
1054
567
  content: [
1055
568
  {
@@ -1073,208 +586,53 @@ export const makeMessagesRecvSocket = (config) => {
1073
586
  });
1074
587
  authState.creds.registered = true;
1075
588
  ev.emit('creds.update', authState.creds);
1076
- break;
1077
- case 'privacy_token':
1078
- await handlePrivacyTokenNotification(node);
1079
- break;
1080
589
  }
1081
590
  if (Object.keys(result).length) {
1082
591
  return result;
1083
592
  }
1084
593
  };
1085
- /**
1086
- * In-memory cache of storage JIDs with stored tctokens, seeded from the persisted index.
1087
- * Used to coalesce writes during a session; pruning always re-reads the persisted index
1088
- * to cover writes made by other layers (e.g. history sync).
1089
- */
1090
- const tcTokenKnownJids = new Set();
1091
- const tcTokenIndexLoaded = (async () => {
1092
- try {
1093
- const jids = await readTcTokenIndex(authState.keys);
1094
- for (const jid of jids)
1095
- tcTokenKnownJids.add(jid);
1096
- logger.debug({ count: tcTokenKnownJids.size }, 'loaded tctoken index');
1097
- }
1098
- catch (err) {
1099
- logger.warn({ err: err?.message }, 'failed to load tctoken index');
1100
- }
1101
- })();
1102
- let tcTokenIndexTimer;
1103
- async function flushTcTokenIndex() {
1104
- if (tcTokenIndexTimer) {
1105
- clearTimeout(tcTokenIndexTimer);
1106
- tcTokenIndexTimer = undefined;
1107
- }
1108
- // Merge with whatever is already persisted so we don't clobber writes from other
1109
- // paths (history sync, concurrent sessions on the same store).
1110
- const write = await buildMergedTcTokenIndexWrite(authState.keys, tcTokenKnownJids);
1111
- return authState.keys.set({ tctoken: write });
1112
- }
1113
- function scheduleTcTokenIndexSave() {
1114
- if (tcTokenIndexTimer) {
1115
- clearTimeout(tcTokenIndexTimer);
1116
- }
1117
- tcTokenIndexTimer = setTimeout(() => {
1118
- tcTokenIndexTimer = undefined;
1119
- flushTcTokenIndex().catch(err => {
1120
- logger.warn({ err: err?.message }, 'failed to save tctoken index');
1121
- });
1122
- }, 5000);
1123
- }
1124
- function trackTcTokenJid(jid) {
1125
- if (jid && jid !== TC_TOKEN_INDEX_KEY && !tcTokenKnownJids.has(jid)) {
1126
- tcTokenKnownJids.add(jid);
1127
- scheduleTcTokenIndexSave();
1128
- }
1129
- }
1130
- const handlePrivacyTokenNotification = async (node) => {
1131
- const tokensNode = getBinaryNodeChild(node, 'tokens');
1132
- if (!tokensNode)
1133
- return;
1134
- const from = jidNormalizedUser(node.attrs.from);
1135
- // WA Web uses: senderLid ?? toLid(from) for the storage key
1136
- // The sender_lid attribute provides the LID directly when available
1137
- const senderLid = node.attrs.sender_lid && isLidUser(jidNormalizedUser(node.attrs.sender_lid))
1138
- ? jidNormalizedUser(node.attrs.sender_lid)
1139
- : undefined;
1140
- const fallbackJid = senderLid ?? (await resolveTcTokenJid(from, getLIDForPN));
1141
- logger.debug({ from, storageJid: fallbackJid }, 'processing privacy token notification');
1142
- await storeTcTokensFromIqResult({
1143
- result: node,
1144
- fallbackJid,
1145
- keys: authState.keys,
1146
- getLIDForPN,
1147
- onNewJidStored: trackTcTokenJid
1148
- });
1149
- };
1150
594
  async function decipherLinkPublicKey(data) {
1151
595
  const buffer = toRequiredBuffer(data);
1152
596
  const salt = buffer.slice(0, 32);
1153
- const secretKey = await derivePairingCodeKey(authState.creds.pairingCode, salt);
597
+ const secretKey = await (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt);
1154
598
  const iv = buffer.slice(32, 48);
1155
599
  const payload = buffer.slice(48, 80);
1156
- return aesDecryptCTR(payload, secretKey, iv);
600
+ return (0, Utils_1.aesDecryptCTR)(payload, secretKey, iv);
1157
601
  }
1158
602
  function toRequiredBuffer(data) {
1159
603
  if (data === undefined) {
1160
- throw new Boom('Invalid buffer', { statusCode: 400 });
604
+ throw new boom_1.Boom('Invalid buffer', { statusCode: 400 });
1161
605
  }
1162
606
  return data instanceof Buffer ? data : Buffer.from(data);
1163
607
  }
1164
- const willSendMessageAgain = async (id, participant) => {
608
+ const willSendMessageAgain = (id, participant) => {
1165
609
  const key = `${id}:${participant}`;
1166
- const retryCount = (await msgRetryCache.get(key)) || 0;
610
+ const retryCount = msgRetryCache.get(key) || 0;
1167
611
  return retryCount < maxMsgRetryCount;
1168
612
  };
1169
- const updateSendMessageAgainCount = async (id, participant) => {
613
+ const updateSendMessageAgainCount = (id, participant) => {
1170
614
  const key = `${id}:${participant}`;
1171
- const newValue = ((await msgRetryCache.get(key)) || 0) + 1;
1172
- await msgRetryCache.set(key, newValue);
615
+ const newValue = (msgRetryCache.get(key) || 0) + 1;
616
+ msgRetryCache.set(key, newValue);
1173
617
  };
1174
- const sendMessagesAgain = async (key, ids, retryNode, receiptNode) => {
618
+ const sendMessagesAgain = async (key, ids, retryNode) => {
619
+ var _a;
620
+ // todo: implement a cache to store the last 256 sent messages (copy whatsmeow)
621
+ const msgs = await Promise.all(ids.map(id => getMessage({ ...key, id })));
1175
622
  const remoteJid = key.remoteJid;
1176
623
  const participant = key.participant || remoteJid;
1177
- const retryCount = +retryNode.attrs.count || 1;
1178
- const msgId = ids[0];
1179
- // Try to get messages from cache first, then fallback to getMessage
1180
- const msgs = [];
1181
- for (const id of ids) {
1182
- let msg;
1183
- // Try to get from retry cache first if enabled
1184
- if (messageRetryManager) {
1185
- const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id);
1186
- if (cachedMsg) {
1187
- msg = cachedMsg.message;
1188
- logger.debug({ jid: remoteJid, id }, 'found message in retry cache');
1189
- // Mark retry as successful since we found the message
1190
- messageRetryManager.markRetrySuccess(id);
1191
- }
1192
- }
1193
- // Fallback to getMessage if not found in cache
1194
- if (!msg) {
1195
- msg = await getMessage({ ...key, id });
1196
- if (msg) {
1197
- logger.debug({ jid: remoteJid, id }, 'found message via getMessage');
1198
- // Also mark as successful if found via getMessage
1199
- if (messageRetryManager) {
1200
- messageRetryManager.markRetrySuccess(id);
1201
- }
1202
- }
1203
- }
1204
- msgs.push(msg);
1205
- }
1206
624
  // if it's the primary jid sending the request
1207
625
  // just re-send the message to everyone
1208
626
  // prevents the first message decryption failure
1209
- const sendToAll = !jidDecode(participant)?.device;
1210
- const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
1211
- let injectedFromBundle = false;
1212
- const bundle = extractE2ESessionFromRetryReceipt(receiptNode);
1213
- if (bundle) {
1214
- try {
1215
- await signalRepository.injectE2ESession({ jid: participant, session: bundle });
1216
- injectedFromBundle = true;
1217
- logger.debug({ participant, retryCount }, 'injected session from retry receipt key bundle');
1218
- }
1219
- catch (error) {
1220
- logger.warn({ error, participant }, 'failed to inject session from retry receipt');
1221
- }
1222
- }
1223
- if (!injectedFromBundle) {
1224
- const receivedRegId = getBinaryNodeChildUInt(receiptNode, 'registration', 4);
1225
- if (typeof receivedRegId === 'number' && Number.isInteger(receivedRegId)) {
1226
- const info = await signalRepository.getSessionInfo(participant);
1227
- if (info && info.registrationId !== 0 && info.registrationId !== receivedRegId) {
1228
- logger.info({ participant, stored: info.registrationId, received: receivedRegId }, 'reg id mismatch on retry without bundle, deleting session');
1229
- await authState.keys.set({ session: { [sessionId]: null } });
1230
- }
1231
- }
1232
- }
1233
- const BASE_KEY_CHECK_RETRY = 2;
1234
- if (msgId && messageRetryManager) {
1235
- const info = await signalRepository.getSessionInfo(participant);
1236
- if (info) {
1237
- if (retryCount === BASE_KEY_CHECK_RETRY) {
1238
- messageRetryManager.saveBaseKey(sessionId, msgId, info.baseKey);
1239
- }
1240
- else if (retryCount > BASE_KEY_CHECK_RETRY) {
1241
- if (messageRetryManager.hasSameBaseKey(sessionId, msgId, info.baseKey)) {
1242
- logger.warn({ participant, retryCount }, 'base key collision on retry, forcing fresh session');
1243
- await authState.keys.set({ session: { [sessionId]: null } });
1244
- }
1245
- messageRetryManager.deleteBaseKey(sessionId, msgId);
1246
- }
1247
- }
1248
- }
1249
- let shouldRecreateSession = false;
1250
- let recreateReason = '';
1251
- if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1 && !injectedFromBundle) {
1252
- try {
1253
- const hasSession = await signalRepository.validateSession(participant);
1254
- const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists);
1255
- shouldRecreateSession = result.recreate;
1256
- recreateReason = result.reason;
1257
- if (shouldRecreateSession) {
1258
- logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry');
1259
- await authState.keys.set({ session: { [sessionId]: null } });
1260
- }
1261
- }
1262
- catch (error) {
1263
- logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
1264
- }
1265
- }
1266
- if (!injectedFromBundle) {
1267
- await assertSessions([participant], true);
1268
- }
1269
- if (isJidGroup(remoteJid)) {
627
+ const sendToAll = !((_a = (0, WABinary_1.jidDecode)(participant)) === null || _a === void 0 ? void 0 : _a.device);
628
+ await assertSessions([participant], true);
629
+ if ((0, WABinary_1.isJidGroup)(remoteJid)) {
1270
630
  await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
1271
631
  }
1272
- logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason, injectedFromBundle }, 'prepared session for retry resend');
632
+ logger.debug({ participant, sendToAll }, 'forced new session for retry recp');
1273
633
  for (const [i, msg] of msgs.entries()) {
1274
- if (!ids[i])
1275
- continue;
1276
- if (msg && (await willSendMessageAgain(ids[i], participant))) {
1277
- await updateSendMessageAgainCount(ids[i], participant);
634
+ if (msg) {
635
+ updateSendMessageAgainCount(ids[i], participant);
1278
636
  const msgRelayOpts = { messageId: ids[i] };
1279
637
  if (sendToAll) {
1280
638
  msgRelayOpts.useUserDevicesCache = false;
@@ -1293,10 +651,11 @@ export const makeMessagesRecvSocket = (config) => {
1293
651
  }
1294
652
  };
1295
653
  const handleReceipt = async (node) => {
654
+ var _a, _b;
1296
655
  const { attrs, content } = node;
1297
656
  const isLid = attrs.from.includes('lid');
1298
- const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id);
1299
- const remoteJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient;
657
+ const isNodeFromMe = (0, WABinary_1.areJidsSameUser)(attrs.participant || attrs.from, isLid ? (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.lid : (_b = authState.creds.me) === null || _b === void 0 ? void 0 : _b.id);
658
+ const remoteJid = !isNodeFromMe || (0, WABinary_1.isJidGroup)(attrs.from) ? attrs.from : attrs.recipient;
1300
659
  const fromMe = !attrs.recipient || ((attrs.type === 'retry' || attrs.type === 'sender') && isNodeFromMe);
1301
660
  const key = {
1302
661
  remoteJid,
@@ -1304,26 +663,33 @@ export const makeMessagesRecvSocket = (config) => {
1304
663
  fromMe,
1305
664
  participant: attrs.participant
1306
665
  };
666
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
667
+ logger.debug({ remoteJid }, 'ignoring receipt from jid');
668
+ await sendMessageAck(node);
669
+ return;
670
+ }
1307
671
  const ids = [attrs.id];
1308
672
  if (Array.isArray(content)) {
1309
- const items = getBinaryNodeChildren(content[0], 'item');
673
+ const items = (0, WABinary_1.getBinaryNodeChildren)(content[0], 'item');
1310
674
  ids.push(...items.map(i => i.attrs.id));
1311
675
  }
1312
676
  try {
1313
677
  await Promise.all([
1314
- receiptMutex.mutex(async () => {
1315
- const status = getStatusFromReceiptType(attrs.type);
678
+ processingMutex.mutex(async () => {
679
+ const status = (0, Utils_1.getStatusFromReceiptType)(attrs.type);
1316
680
  if (typeof status !== 'undefined' &&
681
+ (
1317
682
  // basically, we only want to know when a message from us has been delivered to/read by the other person
1318
683
  // or another device of ours has read some messages
1319
- (status >= proto.WebMessageInfo.Status.SERVER_ACK || !isNodeFromMe)) {
1320
- if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
684
+ status >= WAProto_1.proto.WebMessageInfo.Status.SERVER_ACK ||
685
+ !isNodeFromMe)) {
686
+ if ((0, WABinary_1.isJidGroup)(remoteJid) || (0, WABinary_1.isJidStatusBroadcast)(remoteJid)) {
1321
687
  if (attrs.participant) {
1322
- const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
688
+ const updateKey = status === WAProto_1.proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
1323
689
  ev.emit('message-receipt.update', ids.map(id => ({
1324
690
  key: { ...key, id },
1325
691
  receipt: {
1326
- userJid: jidNormalizedUser(attrs.participant),
692
+ userJid: (0, WABinary_1.jidNormalizedUser)(attrs.participant),
1327
693
  [updateKey]: +attrs.t
1328
694
  }
1329
695
  })));
@@ -1332,23 +698,22 @@ export const makeMessagesRecvSocket = (config) => {
1332
698
  else {
1333
699
  ev.emit('messages.update', ids.map(id => ({
1334
700
  key: { ...key, id },
1335
- update: { status, messageTimestamp: toNumber(+(attrs.t ?? 0)) }
701
+ update: { status }
1336
702
  })));
1337
703
  }
1338
704
  }
1339
705
  if (attrs.type === 'retry') {
1340
706
  // correctly set who is asking for the retry
1341
707
  key.participant = key.participant || attrs.from;
1342
- const retryNode = getBinaryNodeChild(node, 'retry');
1343
- if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
708
+ const retryNode = (0, WABinary_1.getBinaryNodeChild)(node, 'retry');
709
+ if (willSendMessageAgain(ids[0], key.participant)) {
1344
710
  if (key.fromMe) {
1345
711
  try {
1346
- await updateSendMessageAgainCount(ids[0], key.participant);
1347
712
  logger.debug({ attrs, key }, 'recv retry request');
1348
- await sendMessagesAgain(key, ids, retryNode, node);
713
+ await sendMessagesAgain(key, ids, retryNode);
1349
714
  }
1350
715
  catch (error) {
1351
- logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
716
+ logger.error({ key, ids, trace: error.stack }, 'error in sending message again');
1352
717
  }
1353
718
  }
1354
719
  else {
@@ -1363,338 +728,269 @@ export const makeMessagesRecvSocket = (config) => {
1363
728
  ]);
1364
729
  }
1365
730
  finally {
1366
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack receipt'));
731
+ await sendMessageAck(node);
1367
732
  }
1368
733
  };
1369
734
  const handleNotification = async (node) => {
1370
735
  const remoteJid = node.attrs.from;
736
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
737
+ logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification');
738
+ await sendMessageAck(node);
739
+ return;
740
+ }
1371
741
  try {
1372
742
  await Promise.all([
1373
- notificationMutex.mutex(async () => {
743
+ processingMutex.mutex(async () => {
744
+ var _a;
1374
745
  const msg = await processNotification(node);
1375
746
  if (msg) {
1376
- const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id);
1377
- const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node);
747
+ const fromMe = (0, WABinary_1.areJidsSameUser)(node.attrs.participant || remoteJid, authState.creds.me.id);
1378
748
  msg.key = {
1379
749
  remoteJid,
1380
750
  fromMe,
1381
751
  participant: node.attrs.participant,
1382
- participantAlt,
1383
- participantUsername: node.attrs.participant_username,
1384
- addressingMode,
1385
752
  id: node.attrs.id,
1386
753
  ...(msg.key || {})
1387
754
  };
1388
- msg.participant ?? (msg.participant = node.attrs.participant);
755
+ (_a = msg.participant) !== null && _a !== void 0 ? _a : (msg.participant = node.attrs.participant);
1389
756
  msg.messageTimestamp = +node.attrs.t;
1390
- const fullMsg = proto.WebMessageInfo.fromObject(msg);
757
+ const fullMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg);
1391
758
  await upsertMessage(fullMsg, 'append');
1392
759
  }
1393
760
  })
1394
761
  ]);
1395
762
  }
1396
763
  finally {
1397
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack notification'));
764
+ await sendMessageAck(node);
1398
765
  }
1399
766
  };
1400
767
  const handleMessage = async (node) => {
1401
- const encNode = getBinaryNodeChild(node, 'enc');
768
+ var _a, _b, _c;
769
+ if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
770
+ logger.debug({ key: node.attrs.key }, 'ignored message');
771
+ await sendMessageAck(node);
772
+ return;
773
+ }
774
+ const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc');
1402
775
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
1403
- if (encNode?.attrs.type === 'msmsg') {
776
+ if (encNode && encNode.attrs.type === 'msmsg') {
1404
777
  logger.debug({ key: node.attrs.key }, 'ignored msmsg');
1405
- await sendMessageAck(node, NACK_REASONS.MissingMessageSecret);
778
+ await sendMessageAck(node);
1406
779
  return;
1407
780
  }
1408
- let acked = false;
1409
- try {
1410
- const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
1411
- const alt = msg.key.participantAlt || msg.key.remoteJidAlt;
1412
- // store new mappings we didn't have before
1413
- if (!!alt) {
1414
- const altServer = jidDecode(alt)?.server;
1415
- const primaryJid = msg.key.participant || msg.key.remoteJid;
1416
- if (altServer === 'lid') {
1417
- if (!(await signalRepository.lidMapping.getPNForLID(alt))) {
1418
- await signalRepository.lidMapping.storeLIDPNMappings([{ lid: alt, pn: primaryJid }]);
1419
- await signalRepository.migrateSession(primaryJid, alt);
1420
- }
1421
- }
1422
- else {
1423
- await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }]);
1424
- await signalRepository.migrateSession(alt, primaryJid);
1425
- }
781
+ let response;
782
+ if ((0, WABinary_1.getBinaryNodeChild)(node, 'unavailable') && !encNode) {
783
+ await sendMessageAck(node);
784
+ const { key } = (0, Utils_1.decodeMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage;
785
+ response = await requestPlaceholderResend(key);
786
+ if (response === 'RESOLVED') {
787
+ return;
1426
788
  }
1427
- await messageMutex.mutex(async () => {
1428
- await decrypt();
1429
- if (msg.key?.remoteJid && msg.key?.id && msg.message && messageRetryManager) {
1430
- messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
1431
- }
1432
- // message failed to decrypt
1433
- if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
1434
- if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
1435
- acked = true;
1436
- return sendMessageAck(node, NACK_REASONS.ParsingError);
1437
- }
1438
- if (msg.messageStubParameters?.[0] === NO_MESSAGE_FOUND_ERROR_TEXT) {
1439
- // Message arrived without encryption (e.g. CTWA ads messages).
1440
- // Check if this is eligible for placeholder resend (matching WA Web filters).
1441
- const unavailableNode = getBinaryNodeChild(node, 'unavailable');
1442
- const unavailableType = unavailableNode?.attrs?.type;
1443
- if (unavailableType === 'bot_unavailable_fanout' ||
1444
- unavailableType === 'hosted_unavailable_fanout' ||
1445
- unavailableType === 'view_once_unavailable_fanout') {
1446
- logger.debug({ msgId: msg.key.id, unavailableType }, 'skipping placeholder resend for excluded unavailable type');
1447
- acked = true;
1448
- return sendMessageAck(node);
1449
- }
1450
- const messageAge = unixTimestampSeconds() - toNumber(msg.messageTimestamp);
1451
- if (messageAge > PLACEHOLDER_MAX_AGE_SECONDS) {
1452
- logger.debug({ msgId: msg.key.id, messageAge }, 'skipping placeholder resend for old message');
1453
- acked = true;
1454
- return sendMessageAck(node);
1455
- }
1456
- // Request the real content from the phone via placeholder resend PDO.
1457
- // Upsert the CIPHERTEXT stub as a placeholder (like WA Web's processPlaceholderMsg),
1458
- // and store the requestId in stubParameters[1] so users can correlate
1459
- // with the incoming PDO response event.
1460
- const cleanKey = {
1461
- remoteJid: msg.key.remoteJid,
1462
- fromMe: msg.key.fromMe,
1463
- id: msg.key.id,
1464
- participant: msg.key.participant
1465
- };
1466
- // Cache the original message metadata so the PDO response handler
1467
- // can preserve key fields (LID details etc.) that the phone may omit
1468
- const msgData = {
1469
- key: msg.key,
1470
- messageTimestamp: msg.messageTimestamp,
1471
- pushName: msg.pushName,
1472
- participant: msg.participant,
1473
- verifiedBizName: msg.verifiedBizName
1474
- };
1475
- requestPlaceholderResend(cleanKey, msgData)
1476
- .then(requestId => {
1477
- if (requestId && requestId !== 'RESOLVED') {
1478
- logger.debug({ msgId: msg.key.id, requestId }, 'requested placeholder resend for unavailable message');
1479
- ev.emit('messages.update', [
1480
- {
1481
- key: msg.key,
1482
- update: { messageStubParameters: [NO_MESSAGE_FOUND_ERROR_TEXT, requestId] }
1483
- }
1484
- ]);
1485
- }
1486
- })
1487
- .catch(err => {
1488
- logger.warn({ err, msgId: msg.key.id }, 'failed to request placeholder resend for unavailable message');
1489
- });
1490
- acked = true;
1491
- await sendMessageAck(node);
1492
- // Don't return — fall through to upsertMessage so the stub is emitted
1493
- }
1494
- else {
1495
- // Skip retry for expired status messages (>24h old)
1496
- if (isJidStatusBroadcast(msg.key.remoteJid)) {
1497
- const messageAge = unixTimestampSeconds() - toNumber(msg.messageTimestamp);
1498
- if (messageAge > STATUS_EXPIRY_SECONDS) {
1499
- logger.debug({ msgId: msg.key.id, messageAge, remoteJid: msg.key.remoteJid }, 'skipping retry for expired status message');
1500
- acked = true;
1501
- return sendMessageAck(node);
1502
- }
789
+ logger.debug('received unavailable message, acked and requested resend from phone');
790
+ }
791
+ else {
792
+ if (placeholderResendCache.get(node.attrs.id)) {
793
+ placeholderResendCache.del(node.attrs.id);
794
+ }
795
+ }
796
+ const { fullMessage: msg, category, author, decrypt } = (0, Utils_1.decryptMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
797
+ if (response && ((_a = msg === null || msg === void 0 ? void 0 : msg.messageStubParameters) === null || _a === void 0 ? void 0 : _a[0]) === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT) {
798
+ msg.messageStubParameters = [Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT, response];
799
+ }
800
+ if (((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.protocolMessage) === null || _c === void 0 ? void 0 : _c.type) === WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER && node.attrs.sender_pn) {
801
+ ev.emit('chats.phoneNumberShare', { lid: node.attrs.from, jid: node.attrs.sender_pn });
802
+ }
803
+ try {
804
+ await Promise.all([
805
+ processingMutex.mutex(async () => {
806
+ var _a, _b, _c, _d, _e, _f;
807
+ await decrypt();
808
+ // message failed to decrypt
809
+ if (msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT) {
810
+ if (((_a = msg === null || msg === void 0 ? void 0 : msg.messageStubParameters) === null || _a === void 0 ? void 0 : _a[0]) === Utils_1.MISSING_KEYS_ERROR_TEXT) {
811
+ return sendMessageAck(node, Utils_1.NACK_REASONS.ParsingError);
1503
812
  }
1504
- logger.debug('[handleMessage] Attempting retry request for failed decryption');
1505
- // WAWeb only retry-receipts here; server emits PreKeyLow if prekeys run low.
1506
- await retryMutex.mutex(async () => {
1507
- try {
1508
- if (!ws.isOpen) {
1509
- logger.debug({ node }, 'Connection closed, skipping retry');
813
+ retryMutex.mutex(async () => {
814
+ if (ws.isOpen) {
815
+ if ((0, WABinary_1.getBinaryNodeChild)(node, 'unavailable')) {
1510
816
  return;
1511
817
  }
1512
- const encNode = getBinaryNodeChild(node, 'enc');
818
+ const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc');
1513
819
  await sendRetryRequest(node, !encNode);
1514
820
  if (retryRequestDelayMs) {
1515
- await delay(retryRequestDelayMs);
821
+ await (0, Utils_1.delay)(retryRequestDelayMs);
1516
822
  }
1517
823
  }
1518
- catch (err) {
1519
- logger.error({ err }, 'Failed to send retry');
824
+ else {
825
+ logger.debug({ node }, 'connection closed, ignoring retry req');
1520
826
  }
1521
- acked = true;
1522
- await sendMessageAck(node, NACK_REASONS.UnhandledError);
1523
827
  });
1524
828
  }
1525
- }
1526
- else {
1527
- if (messageRetryManager && msg.key.id) {
1528
- messageRetryManager.cancelPendingPhoneRequest(msg.key.id);
1529
- }
1530
- const isNewsletter = isJidNewsletter(msg.key.remoteJid);
1531
- if (!isNewsletter) {
829
+ else {
1532
830
  // no type in the receipt => message delivered
1533
831
  let type = undefined;
832
+ if ((_b = msg.key.participant) === null || _b === void 0 ? void 0 : _b.endsWith('@lid')) {
833
+ msg.key.participant = node.attrs.participant_pn || authState.creds.me.id;
834
+ }
835
+ if ((0, WABinary_1.isJidGroup)(msg.key.remoteJid) && ((_f = (_e = (_d = (_c = msg.message) === null || _c === void 0 ? void 0 : _c.extendedTextMessage) === null || _d === void 0 ? void 0 : _d.contextInfo) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.endsWith('@lid'))) {
836
+ if (msg.message.extendedTextMessage.contextInfo) {
837
+ const metadata = await groupMetadata(msg.key.remoteJid);
838
+ const sender = msg.message.extendedTextMessage.contextInfo.participant;
839
+ const found = metadata.participants.find(p => p.id === sender);
840
+ msg.message.extendedTextMessage.contextInfo.participant = (found === null || found === void 0 ? void 0 : found.jid) || sender;
841
+ }
842
+ }
843
+ if (!(0, WABinary_1.isJidGroup)(msg.key.remoteJid) && (0, WABinary_1.isLidUser)(msg.key.remoteJid)) {
844
+ msg.key.remoteJid = node.attrs.sender_pn || node.attrs.peer_recipient_pn;
845
+ }
1534
846
  let participant = msg.key.participant;
1535
- if (category === 'peer') {
1536
- // special peer message
847
+ if (category === 'peer') { // special peer message
1537
848
  type = 'peer_msg';
1538
849
  }
1539
- else if (msg.key.fromMe) {
1540
- // message was sent by us from a different device
850
+ else if (msg.key.fromMe) { // message was sent by us from a different device
1541
851
  type = 'sender';
1542
852
  // need to specially handle this case
1543
- if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1544
- participant = author; // TODO: investigate sending receipts to LIDs and not PNs
853
+ if ((0, WABinary_1.isJidUser)(msg.key.remoteJid)) {
854
+ participant = author;
1545
855
  }
1546
856
  }
1547
857
  else if (!sendActiveReceipts) {
1548
858
  type = 'inactive';
1549
859
  }
1550
- acked = true;
1551
860
  await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
1552
861
  // send ack for history message
1553
- const isAnyHistoryMsg = getHistoryMsg(msg.message);
862
+ const isAnyHistoryMsg = (0, Utils_1.getHistoryMsg)(msg.message);
1554
863
  if (isAnyHistoryMsg) {
1555
- const jid = jidNormalizedUser(msg.key.remoteJid);
1556
- await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync'); // TODO: investigate
864
+ const jid = (0, WABinary_1.jidNormalizedUser)(msg.key.remoteJid);
865
+ await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
1557
866
  }
1558
867
  }
1559
- else {
1560
- acked = true;
1561
- await sendMessageAck(node);
1562
- logger.debug({ key: msg.key }, 'processed newsletter message without receipts');
1563
- }
1564
- }
1565
- cleanMessage(msg, authState.creds.me.id, authState.creds.me.lid);
1566
- await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
1567
- });
868
+ (0, Utils_1.cleanMessage)(msg, authState.creds.me.id);
869
+ await sendMessageAck(node);
870
+ await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
871
+ })
872
+ ]);
1568
873
  }
1569
874
  catch (error) {
1570
- logger.error({ error, node: binaryNodeToString(node) }, 'error in handling message');
1571
- if (!acked) {
1572
- await sendMessageAck(node, NACK_REASONS.UnhandledError).catch(ackErr => logger.error({ ackErr }, 'failed to ack message after error'));
1573
- }
875
+ logger.error({ error, node }, 'error in handling message');
1574
876
  }
1575
877
  };
1576
- const handleCall = async (node) => {
1577
- try {
1578
- const { attrs } = node;
1579
- const [infoChild] = getAllBinaryNodeChildren(node);
1580
- if (!infoChild) {
1581
- throw new Boom('Missing call info in call node');
1582
- }
1583
- const status = getCallStatusFromNode(infoChild);
1584
- const callId = infoChild.attrs['call-id'];
1585
- const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
1586
- const call = {
1587
- chatId: attrs.from,
1588
- from,
1589
- callerPn: infoChild.attrs['caller_pn'],
1590
- id: callId,
1591
- date: new Date(+attrs.t * 1000),
1592
- offline: !!attrs.offline,
1593
- status
1594
- };
1595
- if (status === 'relaylatency') {
1596
- const latencyValue = infoChild.attrs.latency || infoChild.attrs['latency_ms'] || infoChild.attrs['latency-ms'];
1597
- const latencyMs = latencyValue ? Number(latencyValue) : undefined;
1598
- if (Number.isFinite(latencyMs)) {
1599
- call.latencyMs = latencyMs;
1600
- }
1601
- }
1602
- if (status === 'offer') {
1603
- call.isVideo = !!getBinaryNodeChild(infoChild, 'video');
1604
- call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
1605
- call.groupJid = infoChild.attrs['group-jid'];
1606
- await callOfferCache.set(call.id, call);
1607
- }
1608
- const existingCall = await callOfferCache.get(call.id);
1609
- // use existing call info to populate this event
1610
- if (existingCall) {
1611
- call.isVideo = existingCall.isVideo;
1612
- call.isGroup = existingCall.isGroup;
1613
- call.callerPn = call.callerPn || existingCall.callerPn;
1614
- }
1615
- // delete data once call has ended
1616
- if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
1617
- await callOfferCache.del(call.id);
1618
- }
1619
- ev.emit('call', [call]);
878
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
879
+ var _a;
880
+ if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
881
+ throw new boom_1.Boom('Not authenticated');
1620
882
  }
1621
- catch (error) {
1622
- logger.error({ error, node: binaryNodeToString(node) }, 'error in handling call');
883
+ const pdoMessage = {
884
+ historySyncOnDemandRequest: {
885
+ chatJid: oldestMsgKey.remoteJid,
886
+ oldestMsgFromMe: oldestMsgKey.fromMe,
887
+ oldestMsgId: oldestMsgKey.id,
888
+ oldestMsgTimestampMs: oldestMsgTimestamp,
889
+ onDemandMsgCount: count
890
+ },
891
+ peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
892
+ };
893
+ return sendPeerDataOperationMessage(pdoMessage);
894
+ };
895
+ const requestPlaceholderResend = async (messageKey) => {
896
+ var _a;
897
+ if (!((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id)) {
898
+ throw new boom_1.Boom('Not authenticated');
1623
899
  }
1624
- finally {
1625
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack call'));
900
+ if (placeholderResendCache.get(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id)) {
901
+ logger.debug({ messageKey }, 'already requested resend');
902
+ return;
903
+ }
904
+ else {
905
+ placeholderResendCache.set(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id, true);
1626
906
  }
907
+ await (0, Utils_1.delay)(5000);
908
+ if (!placeholderResendCache.get(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id)) {
909
+ logger.debug({ messageKey }, 'message received while resend requested');
910
+ return 'RESOLVED';
911
+ }
912
+ const pdoMessage = {
913
+ placeholderMessageResendRequest: [{
914
+ messageKey
915
+ }],
916
+ peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
917
+ };
918
+ setTimeout(() => {
919
+ if (placeholderResendCache.get(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id)) {
920
+ logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
921
+ placeholderResendCache.del(messageKey === null || messageKey === void 0 ? void 0 : messageKey.id);
922
+ }
923
+ }, 15000);
924
+ return sendPeerDataOperationMessage(pdoMessage);
925
+ };
926
+ const handleCall = async (node) => {
927
+ const { attrs } = node;
928
+ const [infoChild] = (0, WABinary_1.getAllBinaryNodeChildren)(node);
929
+ const callId = infoChild.attrs['call-id'];
930
+ const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
931
+ const status = (0, Utils_1.getCallStatusFromNode)(infoChild);
932
+ const call = {
933
+ chatId: attrs.from,
934
+ from,
935
+ id: callId,
936
+ date: new Date(+attrs.t * 1000),
937
+ offline: !!attrs.offline,
938
+ status,
939
+ };
940
+ if (status === 'offer') {
941
+ call.isVideo = !!(0, WABinary_1.getBinaryNodeChild)(infoChild, 'video');
942
+ call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
943
+ call.groupJid = infoChild.attrs['group-jid'];
944
+ callOfferCache.set(call.id, call);
945
+ }
946
+ const existingCall = callOfferCache.get(call.id);
947
+ // use existing call info to populate this event
948
+ if (existingCall) {
949
+ call.isVideo = existingCall.isVideo;
950
+ call.isGroup = existingCall.isGroup;
951
+ }
952
+ // delete data once call has ended
953
+ if (status === 'reject' || status === 'accept' || status === 'timeout' || status === 'terminate') {
954
+ callOfferCache.del(call.id);
955
+ }
956
+ ev.emit('call', [call]);
957
+ await sendMessageAck(node);
1627
958
  };
1628
959
  const handleBadAck = async ({ attrs }) => {
1629
- const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id };
1630
- // WARNING: REFRAIN FROM ENABLING THIS FOR NOW. IT WILL CAUSE A LOOP
1631
- // // current hypothesis is that if pash is sent in the ack
1632
- // // it means -- the message hasn't reached all devices yet
1633
- // // we'll retry sending the message here
1634
- // if(attrs.phash) {
1635
- // logger.info({ attrs }, 'received phash in ack, resending message...')
1636
- // const msg = await getMessage(key)
1637
- // if(msg) {
1638
- // await relayMessage(key.remoteJid!, msg, { messageId: key.id!, useUserDevicesCache: false })
1639
- // } else {
1640
- // logger.warn({ attrs }, 'could not send message again, as it was not found')
1641
- // }
1642
- // }
1643
- // error in acknowledgement,
1644
- // device could not display the message
1645
- if (attrs.error) {
1646
- const isReachoutTimelocked = attrs.error === String(NACK_REASONS.SenderReachoutTimelocked);
1647
- if (attrs.error === SERVER_ERROR_CODES.MessageAccountRestriction) {
1648
- // 463 = 1:1 message missing privacy token (tctoken). Usually means the
1649
- // account is restricted: WhatsApp blocks starting new chats but preserves
1650
- // existing ones, since established chats already carry a tctoken.
1651
- // WA Web prevents this client-side (disables the compose bar).
1652
- // No retry — retrying counts as another "reach out" and worsens the restriction.
1653
- logger.warn({ msgId: attrs.id, from: attrs.from }, 'error 463: account restricted or missing tctoken for contact');
1654
- const ackFrom = attrs.from;
1655
- if (ackFrom && !inFlight463Recoveries.has(ackFrom)) {
1656
- inFlight463Recoveries.add(ackFrom);
1657
- void (async () => {
1658
- try {
1659
- const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
1660
- const tcStorageJid = await resolveTcTokenJid(ackFrom, getLIDForPN);
1661
- const issueJid = await resolveIssuanceJid(ackFrom, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
1662
- const result = await issuePrivacyTokens([issueJid], unixTimestampSeconds());
1663
- await storeTcTokensFromIqResult({
1664
- result,
1665
- fallbackJid: tcStorageJid,
1666
- keys: authState.keys,
1667
- getLIDForPN,
1668
- onNewJidStored: trackTcTokenJid
1669
- });
1670
- logger.debug({ from: ackFrom }, 'completed 463 token recovery issuance');
1671
- }
1672
- catch (err) {
1673
- logger.debug({ from: ackFrom, err: err?.message }, 'failed 463 token recovery issuance');
1674
- }
1675
- finally {
1676
- inFlight463Recoveries.delete(ackFrom);
1677
- }
1678
- })();
1679
- }
1680
- }
1681
- else if (attrs.error === SERVER_ERROR_CODES.SmaxInvalid) {
1682
- logger.warn({ msgId: attrs.id, from: attrs.from }, 'smax-invalid (479): stanza rejected by server — likely stale device session or malformed addressing');
960
+ const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id, 'server_id': attrs === null || attrs === void 0 ? void 0 : attrs.server_id };
961
+ // current hypothesis is that if pash is sent in the ack
962
+ // it means -- the message hasn't reached all devices yet
963
+ // we'll retry sending the message here
964
+ if (attrs.phash) {
965
+ logger.info({ attrs }, 'received phash in ack, resending message...');
966
+ const cacheKey = `${key.remoteJid}:${key.id}`;
967
+ if ((msgRetryCache.get(cacheKey) || 0) >= maxMsgRetryCount) {
968
+ logger.warn({ attrs }, 'reached max retry count, not sending message again');
969
+ msgRetryCache.del(cacheKey);
970
+ return;
1683
971
  }
1684
- else if (isReachoutTimelocked) {
1685
- // user is temporarily restricted, fetch current restriction details
1686
- await fetchAccountReachoutTimelock().catch(err => logger.warn({ err }, 'failed to fetch reachout timelock'));
1687
- logger.warn({ attrs }, 'received error in ack');
972
+ const retryCount = msgRetryCache.get(cacheKey) || 0;
973
+ const msg = await getMessage(key);
974
+ if (msg) {
975
+ await relayMessage(key.remoteJid, msg, { messageId: key.id, useUserDevicesCache: false });
976
+ msgRetryCache.set(cacheKey, retryCount + 1);
1688
977
  }
1689
978
  else {
1690
- logger.warn({ attrs }, 'received error in ack');
979
+ logger.warn({ attrs }, 'could not send message again, as it was not found');
1691
980
  }
981
+ }
982
+ // error in acknowledgement,
983
+ // device could not display the message
984
+ if (attrs.error) {
985
+ logger.warn({ attrs }, 'received error in ack');
1692
986
  ev.emit('messages.update', [
1693
987
  {
1694
988
  key,
1695
989
  update: {
1696
- status: WAMessageStatus.ERROR,
1697
- messageStubParameters: isReachoutTimelocked ? [attrs.error, ACCOUNT_RESTRICTED_TEXT] : [attrs.error]
990
+ status: Types_1.WAMessageStatus.ERROR,
991
+ messageStubParameters: [
992
+ attrs.error
993
+ ]
1698
994
  }
1699
995
  }
1700
996
  ]);
@@ -1707,61 +1003,69 @@ export const makeMessagesRecvSocket = (config) => {
1707
1003
  await execTask();
1708
1004
  ev.flush();
1709
1005
  function execTask() {
1710
- return exec(node, false).catch(err => onUnexpectedError(err, identifier));
1006
+ return exec(node, false)
1007
+ .catch(err => onUnexpectedError(err, identifier));
1711
1008
  }
1712
1009
  };
1713
- const offlineNodeProcessor = makeOfflineNodeProcessor(new Map([
1714
- ['message', handleMessage],
1715
- ['call', handleCall],
1716
- ['receipt', handleReceipt],
1717
- ['notification', handleNotification]
1718
- ]), {
1719
- isWsOpen: () => ws.isOpen,
1720
- onUnexpectedError,
1721
- yieldToEventLoop: () => new Promise(resolve => setImmediate(resolve))
1722
- });
1723
- const processNode = async (type, node, identifier, exec) => {
1724
- // Fast path: ack and drop ignored JIDs before entering the buffer/queue
1725
- const from = node.attrs.from;
1726
- let ignoreJid = from;
1727
- if (type === 'receipt' && from) {
1728
- const attrs = node.attrs;
1729
- const isLid = attrs.from.includes('lid');
1730
- const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id);
1731
- ignoreJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient;
1732
- }
1733
- if (ignoreJid && ignoreJid !== S_WHATSAPP_NET && shouldIgnoreJid(ignoreJid)) {
1734
- await sendMessageAck(node, type === 'message' ? NACK_REASONS.UnhandledError : undefined);
1735
- return;
1736
- }
1010
+ const makeOfflineNodeProcessor = () => {
1011
+ const nodeProcessorMap = new Map([
1012
+ ['message', handleMessage],
1013
+ ['call', handleCall],
1014
+ ['receipt', handleReceipt],
1015
+ ['notification', handleNotification]
1016
+ ]);
1017
+ const nodes = [];
1018
+ let isProcessing = false;
1019
+ const enqueue = (type, node) => {
1020
+ nodes.push({ type, node });
1021
+ if (isProcessing) {
1022
+ return;
1023
+ }
1024
+ isProcessing = true;
1025
+ const promise = async () => {
1026
+ while (nodes.length && ws.isOpen) {
1027
+ const { type, node } = nodes.shift();
1028
+ const nodeProcessor = nodeProcessorMap.get(type);
1029
+ if (!nodeProcessor) {
1030
+ onUnexpectedError(new Error(`unknown offline node type: ${type}`), 'processing offline node');
1031
+ continue;
1032
+ }
1033
+ await nodeProcessor(node);
1034
+ }
1035
+ isProcessing = false;
1036
+ };
1037
+ promise().catch(error => onUnexpectedError(error, 'processing offline nodes'));
1038
+ };
1039
+ return { enqueue };
1040
+ };
1041
+ const offlineNodeProcessor = makeOfflineNodeProcessor();
1042
+ const processNode = (type, node, identifier, exec) => {
1737
1043
  const isOffline = !!node.attrs.offline;
1738
1044
  if (isOffline) {
1739
1045
  offlineNodeProcessor.enqueue(type, node);
1740
1046
  }
1741
1047
  else {
1742
- await processNodeWithBuffer(node, identifier, exec);
1048
+ processNodeWithBuffer(node, identifier, exec);
1743
1049
  }
1744
1050
  };
1745
1051
  // recv a message
1746
- ws.on('CB:message', async (node) => {
1747
- await processNode('message', node, 'processing message', handleMessage);
1052
+ ws.on('CB:message', (node) => {
1053
+ processNode('message', node, 'processing message', handleMessage);
1748
1054
  });
1749
1055
  ws.on('CB:call', async (node) => {
1750
- await processNode('call', node, 'handling call', handleCall);
1056
+ processNode('call', node, 'handling call', handleCall);
1751
1057
  });
1752
- ws.on('CB:receipt', async (node) => {
1753
- await processNode('receipt', node, 'handling receipt', handleReceipt);
1058
+ ws.on('CB:receipt', node => {
1059
+ processNode('receipt', node, 'handling receipt', handleReceipt);
1754
1060
  });
1755
1061
  ws.on('CB:notification', async (node) => {
1756
- await processNode('notification', node, 'handling notification', handleNotification);
1062
+ processNode('notification', node, 'handling notification', handleNotification);
1757
1063
  });
1758
1064
  ws.on('CB:ack,class:message', (node) => {
1759
- handleBadAck(node).catch(error => onUnexpectedError(error, 'handling bad ack'));
1065
+ handleBadAck(node)
1066
+ .catch(error => onUnexpectedError(error, 'handling bad ack'));
1760
1067
  });
1761
- ev.on('call', async ([call]) => {
1762
- if (!call) {
1763
- return;
1764
- }
1068
+ ev.on('call', ([call]) => {
1765
1069
  // missed call + group call notification message generation
1766
1070
  if (call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
1767
1071
  const msg = {
@@ -1770,147 +1074,37 @@ export const makeMessagesRecvSocket = (config) => {
1770
1074
  id: call.id,
1771
1075
  fromMe: false
1772
1076
  },
1773
- messageTimestamp: unixTimestampSeconds(call.date)
1077
+ messageTimestamp: (0, Utils_1.unixTimestampSeconds)(call.date),
1774
1078
  };
1775
1079
  if (call.status === 'timeout') {
1776
1080
  if (call.isGroup) {
1777
- msg.messageStubType = call.isVideo
1778
- ? WAMessageStubType.CALL_MISSED_GROUP_VIDEO
1779
- : WAMessageStubType.CALL_MISSED_GROUP_VOICE;
1081
+ msg.messageStubType = call.isVideo ? Types_1.WAMessageStubType.CALL_MISSED_GROUP_VIDEO : Types_1.WAMessageStubType.CALL_MISSED_GROUP_VOICE;
1780
1082
  }
1781
1083
  else {
1782
- msg.messageStubType = call.isVideo ? WAMessageStubType.CALL_MISSED_VIDEO : WAMessageStubType.CALL_MISSED_VOICE;
1084
+ msg.messageStubType = call.isVideo ? Types_1.WAMessageStubType.CALL_MISSED_VIDEO : Types_1.WAMessageStubType.CALL_MISSED_VOICE;
1783
1085
  }
1784
1086
  }
1785
1087
  else {
1786
1088
  msg.message = { call: { callKey: Buffer.from(call.id) } };
1787
1089
  }
1788
- const protoMsg = proto.WebMessageInfo.fromObject(msg);
1789
- await upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
1090
+ const protoMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg);
1091
+ upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
1790
1092
  }
1791
1093
  });
1792
- /** timestamp of last tctoken prune run — throttles to once per 24h */
1793
- let lastTcTokenPruneTs = 0;
1794
- /** dedupe in-flight 463 recovery token issuance by target JID */
1795
- const inFlight463Recoveries = new Set();
1796
- ev.on('connection.update', ({ isOnline, connection }) => {
1094
+ ev.on('connection.update', ({ isOnline }) => {
1797
1095
  if (typeof isOnline !== 'undefined') {
1798
1096
  sendActiveReceipts = isOnline;
1799
1097
  logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
1800
1098
  }
1801
- // Flush pending tctoken index save on disconnect to avoid writing after close
1802
- if (connection === 'close' && tcTokenIndexTimer) {
1803
- clearTimeout(tcTokenIndexTimer);
1804
- tcTokenIndexTimer = undefined;
1805
- // Best-effort flush — may fail if store is already closed
1806
- try {
1807
- void Promise.resolve(flushTcTokenIndex()).catch(() => { });
1808
- }
1809
- catch {
1810
- /* ignore sync errors */
1811
- }
1812
- }
1813
- // Prune expired tctokens when coming online, at most once per 24 hours
1814
- // Matches WA Web's CLEAN_TC_TOKENS task
1815
- // Note: don't gate on tcTokenKnownJids.size — the index may still be loading
1816
- if (isOnline) {
1817
- const now = Date.now();
1818
- const DAY_MS = 24 * 60 * 60 * 1000;
1819
- if (now - lastTcTokenPruneTs >= DAY_MS) {
1820
- lastTcTokenPruneTs = now;
1821
- void pruneExpiredTcTokens();
1822
- }
1823
- }
1824
1099
  });
1825
- registerSocketEndHandler(() => {
1826
- if (!config.msgRetryCounterCache && msgRetryCache.close) {
1827
- msgRetryCache.close();
1828
- }
1829
- if (!config.callOfferCache && callOfferCache.close) {
1830
- callOfferCache.close();
1831
- }
1832
- identityAssertDebounce.close();
1833
- sendActiveReceipts = false;
1834
- });
1835
- async function pruneExpiredTcTokens() {
1836
- try {
1837
- await tcTokenIndexLoaded;
1838
- // Union with the persisted index picks up JIDs added by other layers
1839
- // (history sync) without needing inter-module wiring.
1840
- const persisted = await readTcTokenIndex(authState.keys);
1841
- const allJids = new Set(tcTokenKnownJids);
1842
- for (const jid of persisted)
1843
- allJids.add(jid);
1844
- if (!allJids.size)
1845
- return;
1846
- const jids = [...allJids];
1847
- const allTokens = await authState.keys.get('tctoken', jids);
1848
- const writes = {};
1849
- const survivors = new Set();
1850
- let mutated = 0;
1851
- for (const jid of jids) {
1852
- const entry = allTokens[jid];
1853
- if (!entry) {
1854
- // Tracked but nothing in store — drop from index.
1855
- mutated++;
1856
- continue;
1857
- }
1858
- const hasPeerToken = !!entry.token?.length;
1859
- const peerTokenExpired = hasPeerToken && isTcTokenExpired(entry.timestamp);
1860
- const hasSenderTs = entry.senderTimestamp !== undefined;
1861
- const senderTsExpired = hasSenderTs && isTcTokenExpired(entry.senderTimestamp);
1862
- const keepPeerToken = hasPeerToken && !peerTokenExpired;
1863
- const keepSenderTs = hasSenderTs && !senderTsExpired;
1864
- if (!keepPeerToken && !keepSenderTs) {
1865
- writes[jid] = null;
1866
- mutated++;
1867
- }
1868
- else if (peerTokenExpired && keepSenderTs) {
1869
- writes[jid] = { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp };
1870
- survivors.add(jid);
1871
- mutated++;
1872
- }
1873
- else {
1874
- survivors.add(jid);
1875
- }
1876
- }
1877
- if (mutated === 0)
1878
- return;
1879
- await authState.keys.set({
1880
- tctoken: {
1881
- ...writes,
1882
- [TC_TOKEN_INDEX_KEY]: {
1883
- token: Buffer.from(JSON.stringify([...survivors]))
1884
- }
1885
- }
1886
- });
1887
- tcTokenKnownJids.clear();
1888
- for (const jid of survivors)
1889
- tcTokenKnownJids.add(jid);
1890
- logger.debug({ mutated, remaining: survivors.size }, 'pruned expired tctokens');
1891
- }
1892
- catch (err) {
1893
- logger.warn({ err: err?.message }, 'failed to prune expired tctokens');
1894
- }
1895
- }
1896
1100
  return {
1897
1101
  ...sock,
1898
1102
  sendMessageAck,
1899
1103
  sendRetryRequest,
1900
1104
  rejectCall,
1105
+ offerCall,
1901
1106
  fetchMessageHistory,
1902
1107
  requestPlaceholderResend,
1903
- messageRetryManager,
1904
- sendText,
1905
- sendImage,
1906
- sendVideo,
1907
- sendAudio,
1908
- sendDocument,
1909
- sendLocation,
1910
- sendPoll,
1911
- sendQuiz,
1912
- sendPtv,
1913
- statusMention
1914
1108
  };
1915
1109
  };
1916
- //# sourceMappingURL=messages-recv.js.map
1110
+ exports.makeMessagesRecvSocket = makeMessagesRecvSocket;