@badzz88/baileys 8.5.6 → 8.5.7

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 (279) hide show
  1. package/README.md +2 -2
  2. package/WAProto/WAProto.proto +75 -1256
  3. package/WAProto/fix-imports.js +79 -69
  4. package/WAProto/index.d.ts +71491 -15254
  5. package/WAProto/index.js +126432 -19810
  6. package/engine-requirements.js +4 -3
  7. package/lib/Defaults/index.js +187 -0
  8. package/lib/Signal/Group/ciphertext-message.js +15 -0
  9. package/lib/Signal/Group/group-session-builder.js +92 -0
  10. package/lib/Signal/Group/group_cipher.js +89 -0
  11. package/lib/Signal/Group/index.js +136 -0
  12. package/lib/Signal/Group/keyhelper.js +73 -0
  13. package/lib/Signal/Group/sender-chain-key.js +32 -0
  14. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  15. package/lib/Signal/Group/sender-key-message.js +69 -0
  16. package/lib/Signal/Group/sender-key-name.js +50 -0
  17. package/lib/Signal/Group/sender-key-record.js +44 -0
  18. package/lib/Signal/Group/sender-key-state.js +97 -0
  19. package/lib/Signal/Group/sender-message-key.js +30 -0
  20. package/lib/Signal/libsignal.js +459 -0
  21. package/lib/Signal/lid-mapping.js +260 -0
  22. package/lib/Socket/Client/index.js +30 -0
  23. package/lib/Socket/Client/types.js +13 -0
  24. package/lib/Socket/Client/websocket.js +62 -0
  25. package/lib/Socket/aigroups.js +240 -0
  26. package/lib/Socket/business.js +422 -0
  27. package/lib/Socket/chats.js +1816 -0
  28. package/lib/Socket/communities.js +549 -0
  29. package/lib/Socket/graphql.js +656 -0
  30. package/lib/Socket/groups.js +764 -0
  31. package/lib/Socket/index.js +39 -0
  32. package/lib/Socket/interactive-handler.js +522 -0
  33. package/lib/Socket/interop.js +549 -0
  34. package/{src → lib}/Socket/luxu.js +120 -82
  35. package/lib/Socket/managed-account.js +183 -0
  36. package/lib/Socket/messages-recv.js +2442 -0
  37. package/lib/Socket/messages-send.js +1853 -0
  38. package/lib/Socket/mex.js +60 -0
  39. package/lib/Socket/newsletter.js +818 -0
  40. package/lib/Socket/privacy.js +449 -0
  41. package/lib/Socket/registration.js +427 -0
  42. package/lib/Socket/socket.js +1092 -0
  43. package/lib/Socket/text-router.js +83 -0
  44. package/lib/Socket/username.js +243 -0
  45. package/lib/Store/index.js +36 -0
  46. package/{src → lib}/Store/keyed-db.js +11 -21
  47. package/lib/Store/make-cache-manager-store.js +90 -0
  48. package/lib/Store/make-in-memory-store.js +604 -0
  49. package/lib/Store/make-ordered-dictionary.js +81 -0
  50. package/lib/Store/object-repository.js +29 -0
  51. package/lib/Types/Auth.js +38 -0
  52. package/lib/Types/Bussines.js +2 -0
  53. package/lib/Types/Call.js +2 -0
  54. package/lib/Types/Chat.js +4 -0
  55. package/lib/Types/Contact.js +2 -0
  56. package/lib/Types/Events.js +2 -0
  57. package/lib/Types/GroupMetadata.js +2 -0
  58. package/lib/Types/Label.js +27 -0
  59. package/lib/Types/LabelAssociation.js +9 -0
  60. package/lib/Types/Message.js +95 -0
  61. package/lib/Types/Mex.js +112 -0
  62. package/lib/Types/Newsletter.js +121 -0
  63. package/lib/Types/Product.js +2 -0
  64. package/lib/Types/Signal.js +2 -0
  65. package/lib/Types/Socket.js +2 -0
  66. package/lib/Types/State.js +70 -0
  67. package/lib/Types/USync.js +2 -0
  68. package/lib/Types/index.js +55 -0
  69. package/lib/Utils/auth-utils.js +306 -0
  70. package/lib/Utils/browser-utils.js +114 -0
  71. package/lib/Utils/business.js +247 -0
  72. package/lib/Utils/chat-utils.js +1208 -0
  73. package/lib/Utils/command-loader.d.ts +31 -0
  74. package/lib/Utils/command-loader.js +100 -0
  75. package/lib/Utils/companion-reg-client-utils.js +42 -0
  76. package/lib/Utils/consumer-application.js +107 -0
  77. package/lib/Utils/crypto.js +155 -0
  78. package/lib/Utils/curve25519-js.js +275 -0
  79. package/lib/Utils/decode-wa-message.js +560 -0
  80. package/lib/Utils/event-buffer.js +607 -0
  81. package/lib/Utils/generics.js +563 -0
  82. package/lib/Utils/group-history.js +60 -0
  83. package/lib/Utils/history.js +244 -0
  84. package/lib/Utils/identity-change-handler.js +52 -0
  85. package/lib/Utils/index.js +57 -0
  86. package/lib/Utils/jid-display-normalization.js +218 -0
  87. package/lib/Utils/link-preview.js +143 -0
  88. package/lib/Utils/logger.js +9 -0
  89. package/lib/Utils/lt-hash.js +39 -0
  90. package/lib/Utils/make-mutex.js +36 -0
  91. package/lib/Utils/message-composer.js +479 -0
  92. package/lib/Utils/message-retry-manager.js +270 -0
  93. package/lib/Utils/messages-media.js +896 -0
  94. package/lib/Utils/messages.js +2904 -0
  95. package/lib/Utils/meta-ai-msmsg.js +262 -0
  96. package/lib/Utils/native-bridge.js +82 -0
  97. package/lib/Utils/noise-handler.js +196 -0
  98. package/lib/Utils/offline-node-processor.js +42 -0
  99. package/lib/Utils/pre-key-manager.js +107 -0
  100. package/lib/Utils/process-message.js +1048 -0
  101. package/lib/Utils/reporting-utils.js +262 -0
  102. package/lib/Utils/session-pool.d.ts +16 -0
  103. package/lib/Utils/session-pool.js +94 -0
  104. package/lib/Utils/signal.js +224 -0
  105. package/lib/Utils/stanza-ack.js +40 -0
  106. package/lib/Utils/sticker.d.ts +17 -0
  107. package/lib/Utils/sticker.js +145 -0
  108. package/lib/Utils/sync-action-utils.js +54 -0
  109. package/lib/Utils/tc-token-utils.js +161 -0
  110. package/lib/Utils/use-multi-file-auth-state.js +121 -0
  111. package/lib/Utils/validate-connection.js +219 -0
  112. package/lib/Utils/view-once-cache.d.ts +9 -0
  113. package/lib/Utils/view-once-cache.js +79 -0
  114. package/lib/Utils/voip-rekey.js +22 -0
  115. package/lib/WABinary/constants.js +1304 -0
  116. package/lib/WABinary/decode.js +343 -0
  117. package/{src → lib}/WABinary/encode.js +12 -9
  118. package/lib/WABinary/generic-utils.js +240 -0
  119. package/lib/WABinary/index.js +33 -0
  120. package/lib/WABinary/jid-utils.js +361 -0
  121. package/lib/WABinary/types.js +2 -0
  122. package/lib/WAM/BinaryInfo.js +13 -0
  123. package/lib/WAM/constants.js +39486 -0
  124. package/lib/WAM/encode.js +142 -0
  125. package/lib/WAM/index.js +31 -0
  126. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +55 -0
  127. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  128. package/lib/WAUSync/Protocols/USyncContactProtocol.js +54 -0
  129. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  130. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  131. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +59 -0
  132. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +31 -0
  133. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
  134. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
  135. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +39 -0
  136. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  137. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +27 -0
  138. package/lib/WAUSync/Protocols/index.js +39 -0
  139. package/lib/WAUSync/USyncQuery.js +133 -0
  140. package/lib/WAUSync/USyncUser.js +50 -0
  141. package/lib/WAUSync/index.js +31 -0
  142. package/lib/antiban.js +4637 -0
  143. package/{src → lib}/index.js +49 -51
  144. package/package.json +4 -4
  145. package/src/Defaults/index.js +0 -186
  146. package/src/Signal/Group/ciphertext-message.js +0 -15
  147. package/src/Signal/Group/group-session-builder.js +0 -86
  148. package/src/Signal/Group/group_cipher.js +0 -82
  149. package/src/Signal/Group/index.js +0 -140
  150. package/src/Signal/Group/keyhelper.js +0 -77
  151. package/src/Signal/Group/sender-chain-key.js +0 -29
  152. package/src/Signal/Group/sender-key-distribution-message.js +0 -66
  153. package/src/Signal/Group/sender-key-message.js +0 -69
  154. package/src/Signal/Group/sender-key-name.js +0 -51
  155. package/src/Signal/Group/sender-key-record.js +0 -44
  156. package/src/Signal/Group/sender-key-state.js +0 -87
  157. package/src/Signal/Group/sender-message-key.js +0 -29
  158. package/src/Signal/libsignal.js +0 -455
  159. package/src/Signal/lid-mapping.js +0 -273
  160. package/src/Socket/Client/index.js +0 -31
  161. package/src/Socket/Client/types.js +0 -13
  162. package/src/Socket/Client/websocket.js +0 -61
  163. package/src/Socket/aigroups.js +0 -221
  164. package/src/Socket/business.js +0 -411
  165. package/src/Socket/chats.js +0 -1449
  166. package/src/Socket/communities.js +0 -463
  167. package/src/Socket/graphql.js +0 -523
  168. package/src/Socket/groups.js +0 -516
  169. package/src/Socket/index.js +0 -31
  170. package/src/Socket/interactive-handler.js +0 -527
  171. package/src/Socket/interop.js +0 -339
  172. package/src/Socket/managed-account.js +0 -98
  173. package/src/Socket/messages-recv.js +0 -2685
  174. package/src/Socket/messages-send.js +0 -2047
  175. package/src/Socket/mex.js +0 -57
  176. package/src/Socket/newsletter.js +0 -455
  177. package/src/Socket/privacy.js +0 -125
  178. package/src/Socket/registration.js +0 -236
  179. package/src/Socket/socket.js +0 -983
  180. package/src/Socket/text-router.js +0 -65
  181. package/src/Socket/username.js +0 -130
  182. package/src/Store/index.js +0 -33
  183. package/src/Store/make-cache-manager-store.js +0 -84
  184. package/src/Store/make-in-memory-store.js +0 -551
  185. package/src/Store/make-ordered-dictionary.js +0 -81
  186. package/src/Store/object-repository.js +0 -26
  187. package/src/Types/Auth.js +0 -31
  188. package/src/Types/Bussines.js +0 -2
  189. package/src/Types/Call.js +0 -2
  190. package/src/Types/Chat.js +0 -4
  191. package/src/Types/Contact.js +0 -2
  192. package/src/Types/Events.js +0 -2
  193. package/src/Types/GroupMetadata.js +0 -2
  194. package/src/Types/Label.js +0 -26
  195. package/src/Types/LabelAssociation.js +0 -8
  196. package/src/Types/Message.js +0 -93
  197. package/src/Types/Mex.js +0 -112
  198. package/src/Types/Newsletter.js +0 -109
  199. package/src/Types/Product.js +0 -2
  200. package/src/Types/Signal.js +0 -2
  201. package/src/Types/Socket.js +0 -2
  202. package/src/Types/State.js +0 -62
  203. package/src/Types/USync.js +0 -2
  204. package/src/Types/index.js +0 -56
  205. package/src/Utils/auth-utils.js +0 -254
  206. package/src/Utils/browser-utils.js +0 -112
  207. package/src/Utils/business.js +0 -240
  208. package/src/Utils/chat-utils.js +0 -1230
  209. package/src/Utils/command-loader.d.ts +0 -27
  210. package/src/Utils/command-loader.js +0 -89
  211. package/src/Utils/companion-reg-client-utils.js +0 -40
  212. package/src/Utils/consumer-application.js +0 -105
  213. package/src/Utils/crypto.js +0 -118
  214. package/src/Utils/curve25519-js.js +0 -242
  215. package/src/Utils/decode-wa-message.js +0 -529
  216. package/src/Utils/event-buffer.js +0 -580
  217. package/src/Utils/generics.js +0 -483
  218. package/src/Utils/group-history.js +0 -44
  219. package/src/Utils/history.js +0 -232
  220. package/src/Utils/identity-change-handler.js +0 -52
  221. package/src/Utils/index.js +0 -58
  222. package/src/Utils/jid-display-normalization.js +0 -195
  223. package/src/Utils/link-preview.js +0 -135
  224. package/src/Utils/logger.js +0 -8
  225. package/src/Utils/lt-hash.js +0 -25
  226. package/src/Utils/make-mutex.js +0 -36
  227. package/src/Utils/message-composer.js +0 -471
  228. package/src/Utils/message-retry-manager.js +0 -217
  229. package/src/Utils/messages-media.js +0 -843
  230. package/src/Utils/messages.js +0 -2817
  231. package/src/Utils/meta-ai-msmsg.js +0 -222
  232. package/src/Utils/native-bridge.js +0 -68
  233. package/src/Utils/noise-handler.js +0 -169
  234. package/src/Utils/offline-node-processor.js +0 -34
  235. package/src/Utils/pre-key-manager.js +0 -90
  236. package/src/Utils/process-message.js +0 -950
  237. package/src/Utils/reporting-utils.js +0 -261
  238. package/src/Utils/session-pool.d.ts +0 -14
  239. package/src/Utils/session-pool.js +0 -70
  240. package/src/Utils/signal.js +0 -217
  241. package/src/Utils/stanza-ack.js +0 -30
  242. package/src/Utils/sticker.d.ts +0 -13
  243. package/src/Utils/sticker.js +0 -123
  244. package/src/Utils/sync-action-utils.js +0 -45
  245. package/src/Utils/tc-token-utils.js +0 -158
  246. package/src/Utils/use-multi-file-auth-state.js +0 -111
  247. package/src/Utils/validate-connection.js +0 -209
  248. package/src/Utils/view-once-cache.d.ts +0 -12
  249. package/src/Utils/view-once-cache.js +0 -63
  250. package/src/Utils/voip-rekey.js +0 -22
  251. package/src/WABinary/constants.js +0 -1304
  252. package/src/WABinary/decode.js +0 -342
  253. package/src/WABinary/generic-utils.js +0 -238
  254. package/src/WABinary/index.js +0 -34
  255. package/src/WABinary/jid-utils.js +0 -351
  256. package/src/WABinary/types.js +0 -2
  257. package/src/WAM/BinaryInfo.js +0 -13
  258. package/src/WAM/constants.js +0 -39484
  259. package/src/WAM/encode.js +0 -149
  260. package/src/WAM/index.js +0 -32
  261. package/src/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -53
  262. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +0 -44
  263. package/src/WAUSync/Protocols/USyncContactProtocol.js +0 -55
  264. package/src/WAUSync/Protocols/USyncDeviceProtocol.js +0 -55
  265. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +0 -31
  266. package/src/WAUSync/Protocols/USyncFeatureProtocol.js +0 -54
  267. package/src/WAUSync/Protocols/USyncLIDProtocol.js +0 -32
  268. package/src/WAUSync/Protocols/USyncNewsletterProtocol.js +0 -473
  269. package/src/WAUSync/Protocols/USyncPictureProtocol.js +0 -34
  270. package/src/WAUSync/Protocols/USyncSidelistProtocol.js +0 -29
  271. package/src/WAUSync/Protocols/USyncStatusProtocol.js +0 -42
  272. package/src/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -38
  273. package/src/WAUSync/Protocols/USyncUsernameProtocol.js +0 -28
  274. package/src/WAUSync/Protocols/index.js +0 -40
  275. package/src/WAUSync/USyncQuery.js +0 -126
  276. package/src/WAUSync/USyncUser.js +0 -50
  277. package/src/WAUSync/index.js +0 -32
  278. package/src/antiban.js +0 -4152
  279. /package/{src → lib}/Defaults/phonenumber-mcc.json +0 -0
@@ -1,2685 +0,0 @@
1
- 'use strict';
2
- var __importDefault = (this && this.__importDefault) ||
3
- function (mod) {
4
- return mod && mod.__esModule ? mod : { default: mod };
5
- };
6
- Object.defineProperty(exports, '__esModule', { value: true });
7
- exports.makeMessagesRecvSocket = void 0;
8
- const node_cache_1 = __importDefault(require('@cacheable/node-cache'));
9
- const boom_1 = require('@hapi/boom');
10
- const crypto_1 = require('crypto');
11
- const index_js_1 = require('../../WAProto/index.js');
12
- const Defaults_1 = require('../Defaults');
13
- const Types_1 = require('../Types');
14
- const Utils_1 = require('../Utils');
15
- const jid_display_normalization_1 = require('../Utils/jid-display-normalization');
16
- const make_mutex_1 = require('../Utils/make-mutex');
17
- const offline_node_processor_1 = require('../Utils/offline-node-processor');
18
- const stanza_ack_1 = require('../Utils/stanza-ack');
19
- const tc_token_utils_1 = require('../Utils/tc-token-utils');
20
- const WABinary_1 = require('../WABinary');
21
- const groups_1 = require('./groups');
22
- const aigroup_1 = require('./aigroups');
23
- const messages_send_1 = require('./messages-send');
24
- const makeMessagesRecvSocket = config => {
25
- const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
26
- const sock = (0, messages_send_1.makeMessagesSocket)(config);
27
- const { ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, messageRetryManager, issuePrivacyTokens, getUSyncDevices, createParticipantNodes, newsletterServerIdCache } = sock;
28
- const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
29
- const socketCreatedAt = Math.floor(Date.now() / 1000);
30
- let isConnected = false;
31
- const retryMutex = (0, make_mutex_1.makeMutex)();
32
- const msgRetryCache = config.msgRetryCounterCache ||
33
- new node_cache_1.default({
34
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
35
- useClones: false
36
- });
37
- const callOfferCache = config.callOfferCache ||
38
- new node_cache_1.default({
39
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.CALL_OFFER,
40
- useClones: false
41
- });
42
- const placeholderResendCache = config.placeholderResendCache ||
43
- new node_cache_1.default({
44
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
45
- useClones: false
46
- });
47
- const identityAssertDebounce = new node_cache_1.default({ stdTTL: 5, useClones: false });
48
- let sendActiveReceipts = false;
49
- const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
50
- if (!authState.creds.me?.id) {
51
- throw new boom_1.Boom('Not authenticated');
52
- }
53
- const pdoMessage = {
54
- historySyncOnDemandRequest: {
55
- chatJid: oldestMsgKey.remoteJid,
56
- oldestMsgFromMe: oldestMsgKey.fromMe,
57
- oldestMsgId: oldestMsgKey.id,
58
- oldestMsgTimestampMs: oldestMsgTimestamp,
59
- onDemandMsgCount: count
60
- },
61
- peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
62
- };
63
- return sendPeerDataOperationMessage(pdoMessage);
64
- };
65
- const requestPlaceholderResend = async (messageKey, msgData) => {
66
- if (!authState.creds.me?.id) {
67
- throw new boom_1.Boom('Not authenticated');
68
- }
69
- if (await placeholderResendCache.get(messageKey?.id)) {
70
- logger.debug({ messageKey }, 'already requested resend');
71
- return;
72
- }
73
- else {
74
- await placeholderResendCache.set(messageKey?.id, msgData || true);
75
- }
76
- await (0, Utils_1.delay)(2000);
77
- if (!(await placeholderResendCache.get(messageKey?.id))) {
78
- logger.debug({ messageKey }, 'message received while resend requested');
79
- return 'RESOLVED';
80
- }
81
- const pdoMessage = {
82
- placeholderMessageResendRequest: [
83
- {
84
- messageKey
85
- }
86
- ],
87
- peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
88
- };
89
- setTimeout(async () => {
90
- if (await placeholderResendCache.get(messageKey?.id)) {
91
- logger.debug({ messageKey }, 'PDO message without response after 8 seconds. Phone possibly offline');
92
- await placeholderResendCache.del(messageKey?.id);
93
- }
94
- }, 8000);
95
- return sendPeerDataOperationMessage(pdoMessage);
96
- };
97
- const requestWaffleNonce = async () => {
98
- if (!authState.creds.me?.id) {
99
- throw new boom_1.Boom('Not authenticated');
100
- }
101
- return sendPeerDataOperationMessage({
102
- peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.WAFFLE_LINKING_NONCE_FETCH
103
- });
104
- };
105
- const requestCompanionCanonicalNonce = async (registrationTraceId) => {
106
- if (!authState.creds.me?.id) {
107
- throw new boom_1.Boom('Not authenticated');
108
- }
109
- return sendPeerDataOperationMessage({
110
- companionCanonicalUserNonceFetchRequest: registrationTraceId ? { registrationTraceId } : {},
111
- peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.COMPANION_CANONICAL_USER_NONCE_FETCH
112
- });
113
- };
114
- const requestCompanionMetaNonce = async () => {
115
- if (!authState.creds.me?.id) {
116
- throw new boom_1.Boom('Not authenticated');
117
- }
118
- return sendPeerDataOperationMessage({
119
- peerDataOperationRequestType: index_js_1.proto.Message.PeerDataOperationRequestType.COMPANION_META_NONCE_FETCH
120
- });
121
- };
122
- const handleMexNotification = async (node) => {
123
- const updateNodes = (0, WABinary_1.getBinaryNodeChildren)(node, 'update');
124
- if (!updateNodes.length) {
125
- logger.debug({ node }, 'mex notification with no update children');
126
- return;
127
- }
128
- for (const updateNode of updateNodes) {
129
- const opName = updateNode.attrs?.op_name;
130
- if (!opName)
131
- continue;
132
- let payload;
133
- try {
134
- const raw = updateNode.content?.text ?? updateNode.content?.toString?.();
135
- payload = raw ? JSON.parse(raw) : null;
136
- }
137
- catch (e) {
138
- logger.error({ err: e, opName }, 'failed to parse mex update payload');
139
- continue;
140
- }
141
- if (!payload?.data) {
142
- logger.debug({ opName }, 'mex update with no data field');
143
- continue;
144
- }
145
- const d = payload.data;
146
- switch (opName) {
147
- case 'NotificationNewsletterUpdate': {
148
- const upd = d.xwa2_notify_newsletter_on_metadata_update;
149
- if (upd?.id) {
150
- ev.emit('newsletter-settings.update', {
151
- id: upd.id,
152
- update: upd.thread_metadata?.settings ?? {}
153
- });
154
- }
155
- break;
156
- }
157
- case 'NotificationNewsletterJoin': {
158
- const upd = d.xwa2_notify_newsletter_on_join;
159
- if (upd?.id) {
160
- ev.emit('newsletter-participants.update', {
161
- id: upd.id,
162
- author: node.attrs.from,
163
- user: (0, WABinary_1.jidNormalizedUser)(node.attrs.from),
164
- new_role: upd.viewer_metadata?.role ?? 'SUBSCRIBER',
165
- action: 'join',
166
- metadata: upd.thread_metadata
167
- });
168
- }
169
- break;
170
- }
171
- case 'NotificationNewsletterMuteChange': {
172
- const upd = d.xwa2_notify_newsletter_on_mute_change;
173
- if (upd?.id) {
174
- ev.emit('newsletter-settings.update', {
175
- id: upd.id,
176
- update: { mute: upd.mute }
177
- });
178
- }
179
- break;
180
- }
181
- case 'NotificationNewsletterUserSettingChange': {
182
- const upd = d.xwa2_notify_newsletter_on_user_setting_change;
183
- if (upd?.id && upd.setting) {
184
- ev.emit('newsletter-settings.update', {
185
- id: upd.id,
186
- update: { userSetting: upd.setting }
187
- });
188
- }
189
- break;
190
- }
191
- case 'NotificationNewsletterAdminPromote': {
192
- const upd = d.xwa2_notify_newsletter_on_admin_promote;
193
- if (upd?.id) {
194
- ev.emit('newsletter-participants.update', {
195
- id: upd.id,
196
- author: node.attrs.from,
197
- user: upd.user,
198
- new_role: 'ADMIN',
199
- action: 'promote'
200
- });
201
- }
202
- break;
203
- }
204
- case 'NotificationGroupMemberLinkPropertyUpdate': {
205
- const upd = d.xwa2_notify_group_on_prop_change;
206
- if (upd?.id && upd.properties?.member_link_mode !== undefined) {
207
- ev.emit('groups.update', [
208
- {
209
- id: upd.id,
210
- memberAddMode: upd.properties.member_link_mode
211
- }
212
- ]);
213
- }
214
- break;
215
- }
216
- case 'NotificationGroupLimitSharingPropertyUpdate': {
217
- const upd = d.xwa2_notify_group_on_prop_change;
218
- if (upd?.id && upd.properties?.limit_sharing !== undefined) {
219
- ev.emit('groups.update', [
220
- {
221
- id: upd.id,
222
- limitSharing: upd.properties.limit_sharing
223
- }
224
- ]);
225
- }
226
- break;
227
- }
228
- case 'NotificationGroupMemberShareGroupHistoryModePropertyUpdate': {
229
- const upd = d.xwa2_notify_group_on_prop_change;
230
- if (upd?.id && upd.properties?.member_share_group_history_mode !== undefined) {
231
- ev.emit('groups.update', [
232
- {
233
- id: upd.id,
234
- memberShareHistoryMode: upd.properties.member_share_group_history_mode
235
- }
236
- ]);
237
- }
238
- break;
239
- }
240
- default:
241
- logger.debug({ opName, from: node.attrs.from }, 'unhandled mex op');
242
- }
243
- }
244
- };
245
- const handleNewsletterNotification = async (node) => {
246
- const from = node.attrs.from;
247
- const child = (0, WABinary_1.getAllBinaryNodeChildren)(node)[0];
248
- const author = node.attrs.participant;
249
- logger.info({ from, child }, 'got newsletter notification');
250
- switch (child.tag) {
251
- case 'reaction':
252
- const reactionUpdate = {
253
- id: from,
254
- server_id: child.attrs.message_id,
255
- reaction: {
256
- code: (0, WABinary_1.getBinaryNodeChildString)(child, 'reaction'),
257
- count: 1
258
- }
259
- };
260
- ev.emit('newsletter.reaction', reactionUpdate);
261
- break;
262
- case 'view':
263
- const viewUpdate = {
264
- id: from,
265
- server_id: child.attrs.message_id,
266
- count: parseInt(child.content?.toString() || '0', 10)
267
- };
268
- ev.emit('newsletter.view', viewUpdate);
269
- break;
270
- case 'participant':
271
- const participantUpdate = {
272
- id: from,
273
- author,
274
- user: child.attrs.jid,
275
- action: child.attrs.action,
276
- new_role: child.attrs.role
277
- };
278
- ev.emit('newsletter-participants.update', participantUpdate);
279
- break;
280
- case 'update':
281
- const settingsNode = (0, WABinary_1.getBinaryNodeChild)(child, 'settings');
282
- if (settingsNode) {
283
- const update = {};
284
- const nameNode = (0, WABinary_1.getBinaryNodeChild)(settingsNode, 'name');
285
- if (nameNode?.content)
286
- update.name = nameNode.content.toString();
287
- const descriptionNode = (0, WABinary_1.getBinaryNodeChild)(settingsNode, 'description');
288
- if (descriptionNode?.content)
289
- update.description = descriptionNode.content.toString();
290
- ev.emit('newsletter-settings.update', {
291
- id: from,
292
- update
293
- });
294
- }
295
- break;
296
- case 'message': {
297
- const viewCount = child.attrs.view_count !== undefined ? +child.attrs.view_count : undefined;
298
- const impressionCount = child.attrs.impression_count !== undefined ? +child.attrs.impression_count : undefined;
299
- const plaintextNode = (0, WABinary_1.getBinaryNodeChild)(child, 'plaintext');
300
- if (plaintextNode?.content) {
301
- try {
302
- const contentBuf = typeof plaintextNode.content === 'string'
303
- ? Buffer.from(plaintextNode.content, 'binary')
304
- : Buffer.from(plaintextNode.content);
305
- const messageProto = index_js_1.proto.Message.decode(contentBuf).toJSON();
306
- const fullMessage = index_js_1.proto.WebMessageInfo.fromObject({
307
- key: {
308
- remoteJid: from,
309
- id: child.attrs.message_id || child.attrs.server_id,
310
- fromMe: false
311
- },
312
- message: messageProto,
313
- messageTimestamp: +child.attrs.t
314
- }).toJSON();
315
- if (viewCount !== undefined)
316
- fullMessage.views = viewCount;
317
- if (impressionCount !== undefined)
318
- fullMessage.impressions = impressionCount;
319
- await upsertMessage(fullMessage, 'append');
320
- logger.info('Processed plaintext newsletter message');
321
- }
322
- catch (error) {
323
- logger.error({ error }, 'Failed to decode plaintext newsletter message');
324
- }
325
- }
326
- break;
327
- }
328
- case 'live_updates': {
329
- const messagesNode = (0, WABinary_1.getBinaryNodeChild)(child, 'messages');
330
- const msgTs = messagesNode?.attrs?.t ? +messagesNode.attrs.t : undefined;
331
- for (const msgNode of (0, WABinary_1.getBinaryNodeChildren)(messagesNode ?? child, 'message')) {
332
- const serverId = msgNode.attrs.server_id;
333
- const fwdNode = (0, WABinary_1.getBinaryNodeChild)(msgNode, 'forwards_count');
334
- const forwardsCount = fwdNode?.attrs?.count !== undefined ? +fwdNode.attrs.count : undefined;
335
- const reactionsNode = (0, WABinary_1.getBinaryNodeChild)(msgNode, 'reactions');
336
- const reactions = (0, WABinary_1.getBinaryNodeChildren)(reactionsNode ?? msgNode, 'reaction').map(r => ({
337
- code: r.attrs.code,
338
- count: +r.attrs.count
339
- }));
340
- ev.emit('newsletter.live-update', {
341
- id: from,
342
- server_id: serverId,
343
- timestamp: msgTs,
344
- forwardsCount,
345
- reactions
346
- });
347
- }
348
- break;
349
- }
350
- case 'pin': {
351
- const pinnedServerId = child.attrs.message_id || child.attrs.server_id;
352
- const isPinned = child.attrs.action !== 'unpin';
353
- ev.emit('newsletter.pin', {
354
- id: from,
355
- server_id: pinnedServerId,
356
- pinned: isPinned
357
- });
358
- ev.emit('newsletters.update', [{ id: from, pinnedMessage: isPinned ? pinnedServerId : null }]);
359
- break;
360
- }
361
- case 'category':
362
- ev.emit('newsletter-settings.update', {
363
- id: from,
364
- update: { category: child.attrs.value || child.content?.toString() }
365
- });
366
- break;
367
- case 'invite':
368
- ev.emit('newsletter.invite', {
369
- id: from,
370
- inviteCode: child.attrs.code,
371
- inviter: child.attrs.jid || author,
372
- role: child.attrs.role || 'SUBSCRIBER'
373
- });
374
- break;
375
- default:
376
- logger.warn({ node }, 'Unknown newsletter notification');
377
- break;
378
- }
379
- };
380
- const handleNewsletterStatus = async (node) => {
381
- const { id, from, server_id, t, is_sender, offline, type, edit } = node.attrs;
382
- const serverId = server_id ? +server_id : undefined;
383
- const timestamp = t ? +t : undefined;
384
- const isSender = is_sender === 'true';
385
- const offlineIndex = offline !== undefined ? +offline : undefined;
386
- if (id && serverId != null) {
387
- newsletterServerIdCache?.set(id, serverId);
388
- }
389
- const metaNode = (0, WABinary_1.getBinaryNodeChild)(node, 'meta');
390
- const meta = metaNode
391
- ? {
392
- ...(metaNode.attrs.msg_edit_t ? { editedAt: +metaNode.attrs.msg_edit_t } : {}),
393
- ...(metaNode.attrs.original_msg_t ? { originalTimestamp: +metaNode.attrs.original_msg_t } : {}),
394
- ...(metaNode.attrs.interaction_type ? { interactionType: metaNode.attrs.interaction_type } : {}),
395
- ...(metaNode.attrs.parent_server_id ? { parentServerId: +metaNode.attrs.parent_server_id } : {}),
396
- ...(metaNode.attrs.response_server_id ? { responseServerId: +metaNode.attrs.response_server_id } : {})
397
- }
398
- : undefined;
399
- const viewsNode = (0, WABinary_1.getBinaryNodeChild)(node, 'views_count');
400
- const viewsCount = viewsNode?.attrs?.count !== undefined ? +viewsNode.attrs.count : undefined;
401
- const responsesNode = (0, WABinary_1.getBinaryNodeChild)(node, 'responses_count');
402
- const responsesCount = responsesNode?.attrs?.count !== undefined ? +responsesNode.attrs.count : undefined;
403
- const reactionsNode = (0, WABinary_1.getBinaryNodeChild)(node, 'reactions');
404
- const reactionCounts = (0, WABinary_1.getBinaryNodeChildren)(reactionsNode ?? { content: [] }, 'reaction').map(r => ({ code: r.attrs.code, count: +r.attrs.count }));
405
- let content = null;
406
- let mediaType = undefined;
407
- if (type === 'reaction') {
408
- const reactionNode = (0, WABinary_1.getBinaryNodeChild)(node, 'reaction');
409
- content = { type: 'reaction', code: reactionNode?.attrs?.code };
410
- }
411
- else if (type === 'text' || type === 'media') {
412
- const plaintextNode = (0, WABinary_1.getBinaryNodeChild)(node, 'plaintext');
413
- if (type === 'media')
414
- mediaType = plaintextNode?.attrs?.mediatype;
415
- if (edit === '7' || edit === '8') {
416
- content = { type: 'revoke', edit };
417
- }
418
- else if (plaintextNode?.content) {
419
- try {
420
- const buf = Buffer.isBuffer(plaintextNode.content)
421
- ? plaintextNode.content
422
- : Buffer.from(plaintextNode.content);
423
- const message = index_js_1.proto.Message.decode(buf).toJSON();
424
- content = { type, message, ...(mediaType ? { mediaType } : {}) };
425
- }
426
- catch (err) {
427
- logger.error({ err }, 'Failed to decode newsletter status plaintext');
428
- content = { type, raw: true, ...(mediaType ? { mediaType } : {}) };
429
- }
430
- }
431
- }
432
- const ackType = type === 'reaction' ? 'reaction' : edit ? 'revoke' : type;
433
- await sendNode({
434
- tag: 'ack',
435
- attrs: { id, to: from, class: 'status', type: ackType || 'text' },
436
- content: undefined
437
- });
438
- ev.emit('newsletter.status', {
439
- id: from,
440
- messageId: id,
441
- serverId,
442
- timestamp,
443
- isSender,
444
- ...(offlineIndex !== undefined ? { offlineIndex } : {}),
445
- ...(meta ? { meta } : {}),
446
- ...(viewsCount !== undefined ? { viewsCount } : {}),
447
- ...(responsesCount !== undefined ? { responsesCount } : {}),
448
- ...(reactionCounts.length ? { reactionCounts } : {}),
449
- content
450
- });
451
- };
452
- const sendMessageAck = async (node, errorCode) => {
453
- const stanza = (0, stanza_ack_1.buildAckStanza)(node, errorCode, authState.creds.me?.id);
454
- logger.debug({ recv: { tag: node.tag, attrs: node.attrs }, sent: stanza.attrs }, 'sent ack');
455
- await sendNode(stanza);
456
- };
457
- const offerCall = async (toJid, isVideo = false) => {
458
- const callId = crypto_1.randomBytes(16).toString('hex').toUpperCase().substring(0, 64);
459
- const offerContent = [];
460
- offerContent.push({
461
- tag: 'audio',
462
- attrs: { enc: 'opus', rate: '16000' },
463
- content: undefined
464
- });
465
- offerContent.push({
466
- tag: 'audio',
467
- attrs: { enc: 'opus', rate: '8000' },
468
- content: undefined
469
- });
470
- if (isVideo) {
471
- offerContent.push({
472
- tag: 'video',
473
- attrs: {
474
- enc: 'vp8',
475
- dec: 'vp8',
476
- orientation: '0',
477
- screen_width: '1920',
478
- screen_height: '1080',
479
- device_orientation: '0'
480
- },
481
- content: undefined
482
- });
483
- }
484
- offerContent.push({
485
- tag: 'net',
486
- attrs: { medium: '3' },
487
- content: undefined
488
- });
489
- offerContent.push({
490
- tag: 'capability',
491
- attrs: { ver: '1' },
492
- content: new Uint8Array([1, 4, 255, 131, 207, 4])
493
- });
494
- offerContent.push({
495
- tag: 'encopt',
496
- attrs: { keygen: '2' },
497
- content: undefined
498
- });
499
- const encKey = crypto_1.randomBytes(32);
500
- const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) => WABinary_1.jidEncode(user, 's.whatsapp.net', device));
501
- await assertSessions(devices, true);
502
- const { nodes: destinations, shouldIncludeDeviceIdentity } = await createParticipantNodes(devices, {
503
- call: {
504
- callKey: new Uint8Array(encKey)
505
- }
506
- }, { count: '0' });
507
- offerContent.push({ tag: 'destination', attrs: {}, content: destinations });
508
- if (shouldIncludeDeviceIdentity) {
509
- offerContent.push({
510
- tag: 'device-identity',
511
- attrs: {},
512
- content: Utils_1.encodeSignedDeviceIdentity(authState.creds.account, true)
513
- });
514
- }
515
- const stanza = {
516
- tag: 'call',
517
- attrs: {
518
- id: Utils_1.generateMessageID(),
519
- to: toJid
520
- },
521
- content: [
522
- {
523
- tag: 'offer',
524
- attrs: {
525
- 'call-id': callId,
526
- 'call-creator': authState.creds.me.id
527
- },
528
- content: offerContent
529
- }
530
- ]
531
- };
532
- await query(stanza);
533
- return {
534
- id: callId,
535
- to: toJid
536
- };
537
- };
538
- const rejectCall = async (callId, callFrom) => {
539
- const stanza = {
540
- tag: 'call',
541
- attrs: {
542
- from: authState.creds.me.id,
543
- to: callFrom
544
- },
545
- content: [
546
- {
547
- tag: 'reject',
548
- attrs: {
549
- 'call-id': callId,
550
- 'call-creator': callFrom,
551
- count: '0'
552
- },
553
- content: undefined
554
- }
555
- ]
556
- };
557
- await query(stanza);
558
- };
559
- const acceptCall = async (callId, callFrom) => {
560
- const stanza = {
561
- tag: 'call',
562
- attrs: {
563
- from: authState.creds.me.id,
564
- to: callFrom
565
- },
566
- content: [
567
- {
568
- tag: 'accept',
569
- attrs: {
570
- 'call-id': callId,
571
- 'call-creator': callFrom,
572
- count: '0'
573
- },
574
- content: undefined
575
- }
576
- ]
577
- };
578
- await query(stanza);
579
- };
580
- const terminateCall = async (callId, callFrom) => {
581
- const stanza = {
582
- tag: 'call',
583
- attrs: {
584
- from: authState.creds.me.id,
585
- to: callFrom
586
- },
587
- content: [
588
- {
589
- tag: 'terminate',
590
- attrs: {
591
- 'call-id': callId,
592
- 'call-creator': callFrom,
593
- reason: 'user-terminated',
594
- count: '0'
595
- },
596
- content: undefined
597
- }
598
- ]
599
- };
600
- await query(stanza);
601
- };
602
- const rekeyCall = async (callId, callFrom, encryptedKeyBytes, count = 0) => {
603
- const stanza = {
604
- tag: 'call',
605
- attrs: {
606
- from: authState.creds.me.id,
607
- to: callFrom
608
- },
609
- content: [
610
- {
611
- tag: 'enc_rekey',
612
- attrs: {
613
- 'call-id': callId,
614
- 'call-creator': callFrom,
615
- count: count.toString()
616
- },
617
- content: [
618
- {
619
- tag: 'enc',
620
- attrs: { v: '2', type: 'msg' },
621
- content: encryptedKeyBytes
622
- }
623
- ]
624
- }
625
- ]
626
- };
627
- await query(stanza);
628
- };
629
- const joinCallLink = async (callId, callCreator, linkToken) => {
630
- const stanza = {
631
- tag: 'call',
632
- attrs: {
633
- from: authState.creds.me.id,
634
- to: callCreator
635
- },
636
- content: [
637
- {
638
- tag: 'link_join',
639
- attrs: {
640
- 'call-id': callId,
641
- 'call-creator': callCreator,
642
- token: linkToken
643
- },
644
- content: undefined
645
- }
646
- ]
647
- };
648
- await query(stanza);
649
- };
650
- const queryCallLink = async (callLinkCode, to) => {
651
- const stanza = {
652
- tag: 'call',
653
- attrs: {
654
- from: authState.creds.me.id,
655
- to
656
- },
657
- content: [
658
- {
659
- tag: 'link_query',
660
- attrs: { code: callLinkCode },
661
- content: undefined
662
- }
663
- ]
664
- };
665
- return query(stanza);
666
- };
667
- const sendRetryRequest = async (node, forceIncludeKeys = false) => {
668
- const { fullMessage } = (0, Utils_1.decodeMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '');
669
- const { key: msgKey } = fullMessage;
670
- const msgId = msgKey.id;
671
- if (messageRetryManager) {
672
- if (messageRetryManager.hasExceededMaxRetries(msgId)) {
673
- logger.debug({ msgId }, 'reached retry limit with new retry manager, clearing');
674
- messageRetryManager.markRetryFailed(msgId);
675
- return;
676
- }
677
- const retryCount = messageRetryManager.incrementRetryCount(msgId);
678
- const key = `${msgId}:${msgKey?.participant}`;
679
- await msgRetryCache.set(key, retryCount);
680
- }
681
- else {
682
- const key = `${msgId}:${msgKey?.participant}`;
683
- let retryCount = (await msgRetryCache.get(key)) || 0;
684
- if (retryCount >= maxMsgRetryCount) {
685
- logger.debug({ retryCount, msgId }, 'reached retry limit, clearing');
686
- await msgRetryCache.del(key);
687
- return;
688
- }
689
- retryCount += 1;
690
- await msgRetryCache.set(key, retryCount);
691
- }
692
- const key = `${msgId}:${msgKey?.participant}`;
693
- const retryCount = (await msgRetryCache.get(key)) || 1;
694
- const { account, signedPreKey, signedIdentityKey: identityKey } = authState.creds;
695
- const fromJid = node.attrs.from;
696
- let shouldRecreateSession = false;
697
- let recreateReason = '';
698
- if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
699
- try {
700
- const sessionId = signalRepository.jidToSignalProtocolAddress(fromJid);
701
- const hasSession = await signalRepository.validateSession(fromJid);
702
- const result = messageRetryManager.shouldRecreateSession(fromJid, hasSession.exists);
703
- shouldRecreateSession = result.recreate;
704
- recreateReason = result.reason;
705
- if (shouldRecreateSession) {
706
- logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry');
707
- await authState.keys.set({ session: { [sessionId]: null } });
708
- forceIncludeKeys = true;
709
- }
710
- }
711
- catch (error) {
712
- logger.warn({ error, fromJid }, 'failed to check session recreation');
713
- }
714
- }
715
- if (retryCount <= 2) {
716
- if (messageRetryManager) {
717
- messageRetryManager.schedulePhoneRequest(msgId, async () => {
718
- try {
719
- const requestId = await requestPlaceholderResend(msgKey);
720
- logger.debug(`sendRetryRequest: requested placeholder resend (${requestId}) for message ${msgId} (scheduled)`);
721
- }
722
- catch (error) {
723
- logger.warn({ error, msgId }, 'failed to send scheduled phone request');
724
- }
725
- });
726
- }
727
- else {
728
- const msgId = await requestPlaceholderResend(msgKey);
729
- logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`);
730
- }
731
- }
732
- const deviceIdentity = (0, Utils_1.encodeSignedDeviceIdentity)(account, true);
733
- await authState.keys.transaction(async () => {
734
- const receipt = {
735
- tag: 'receipt',
736
- attrs: {
737
- id: msgId,
738
- type: 'retry',
739
- to: node.attrs.from
740
- },
741
- content: [
742
- {
743
- tag: 'retry',
744
- attrs: {
745
- count: retryCount.toString(),
746
- id: node.attrs.id,
747
- t: node.attrs.t,
748
- v: '1',
749
- error: '1'
750
- }
751
- },
752
- {
753
- tag: 'registration',
754
- attrs: {},
755
- content: (0, Utils_1.encodeBigEndian)(authState.creds.registrationId)
756
- }
757
- ]
758
- };
759
- if (node.attrs.recipient) {
760
- receipt.attrs.recipient = node.attrs.recipient;
761
- }
762
- if (node.attrs.participant) {
763
- receipt.attrs.participant = node.attrs.participant;
764
- }
765
- if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
766
- const { update, preKeys } = await (0, Utils_1.getNextPreKeys)(authState, 1);
767
- const [keyId] = Object.keys(preKeys);
768
- const key = preKeys[+keyId];
769
- const content = receipt.content;
770
- content.push({
771
- tag: 'keys',
772
- attrs: {},
773
- content: [
774
- { tag: 'type', attrs: {}, content: Buffer.from(Defaults_1.KEY_BUNDLE_TYPE) },
775
- { tag: 'identity', attrs: {}, content: identityKey.public },
776
- (0, Utils_1.xmppPreKey)(key, +keyId),
777
- (0, Utils_1.xmppSignedPreKey)(signedPreKey),
778
- { tag: 'device-identity', attrs: {}, content: deviceIdentity }
779
- ]
780
- });
781
- ev.emit('creds.update', update);
782
- }
783
- await sendNode(receipt);
784
- logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
785
- }, authState?.creds?.me?.id || 'sendRetryRequest');
786
- };
787
- const reissueTcTokenAfterIdentityChange = from => {
788
- void (async () => {
789
- const normalizedJid = (0, WABinary_1.jidNormalizedUser)(from);
790
- const tcJid = await (0, tc_token_utils_1.resolveTcTokenJid)(normalizedJid, getLIDForPN);
791
- const tcTokenData = await authState.keys.get('tctoken', [tcJid]);
792
- const senderTs = tcTokenData?.[tcJid]?.senderTimestamp;
793
- if (senderTs == null || (0, tc_token_utils_1.isTcTokenExpired)(senderTs)) {
794
- return;
795
- }
796
- logger.debug({ jid: normalizedJid, senderTimestamp: senderTs }, 'identity changed, re-issuing tctoken');
797
- const getPNForLID = signalRepository.lidMapping.getPNForLID.bind(signalRepository.lidMapping);
798
- const issueJid = await (0, tc_token_utils_1.resolveIssuanceJid)(normalizedJid, sock.serverProps.lidTrustedTokenIssueToLid, getLIDForPN, getPNForLID);
799
- const result = await issuePrivacyTokens([issueJid], senderTs);
800
- await (0, tc_token_utils_1.storeTcTokensFromIqResult)({
801
- result,
802
- fallbackJid: tcJid,
803
- keys: authState.keys,
804
- getLIDForPN,
805
- onNewJidStored: trackTcTokenJid
806
- });
807
- })().catch(err => {
808
- logger.debug({ jid: from, err: err?.message }, 'failed to re-issue tctoken after identity change');
809
- });
810
- };
811
- const handleEncryptNotification = async (node) => {
812
- const from = node.attrs.from;
813
- if (from === WABinary_1.S_WHATSAPP_NET) {
814
- const countChild = (0, WABinary_1.getBinaryNodeChild)(node, 'count');
815
- const count = +countChild.attrs.value;
816
- const shouldUploadMorePreKeys = count < Defaults_1.MIN_PREKEY_COUNT;
817
- logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count');
818
- if (shouldUploadMorePreKeys) {
819
- await uploadPreKeys();
820
- }
821
- }
822
- else {
823
- const result = await (0, Utils_1.handleIdentityChange)(node, {
824
- meId: authState.creds.me?.id,
825
- meLid: authState.creds.me?.lid,
826
- validateSession: signalRepository.validateSession,
827
- assertSessions,
828
- debounceCache: identityAssertDebounce,
829
- logger,
830
- onBeforeSessionRefresh: reissueTcTokenAfterIdentityChange
831
- });
832
- if (result.action === 'no_identity_node') {
833
- logger.info({ node }, 'unknown encrypt notification');
834
- }
835
- }
836
- };
837
- const storeParticipantLidPairs = participants => {
838
- const pairs = [];
839
- for (const p of participants || []) {
840
- if (p.phoneNumber)
841
- pairs.push({ lid: p.id, pn: p.phoneNumber });
842
- else if (p.lid)
843
- pairs.push({ lid: p.lid, pn: p.id });
844
- }
845
- if (!pairs.length)
846
- return;
847
- signalRepository.lidMapping
848
- .storeLIDPNMappings(pairs)
849
- .catch(error => logger.debug({ error }, 'failed to store LID/PN mappings from group notification'));
850
- };
851
- const handleGroupNotification = (fullNode, child, msg) => {
852
- const actingParticipantLid = fullNode.attrs.participant;
853
- const actingParticipantPn = fullNode.attrs.participant_pn;
854
- const actingParticipantUsername = fullNode.attrs.participant_username;
855
- const affectedParticipantLid = (0, WABinary_1.getBinaryNodeChild)(child, 'participant')?.attrs?.jid || actingParticipantLid;
856
- const affectedParticipantPn = (0, WABinary_1.getBinaryNodeChild)(child, 'participant')?.attrs?.phone_number || actingParticipantPn;
857
- switch (child?.tag) {
858
- case 'create':
859
- const metadata = (0, groups_1.extractGroupMetadata)(child);
860
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE;
861
- msg.messageStubParameters = [metadata.subject];
862
- msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn };
863
- storeParticipantLidPairs(metadata.participants);
864
- ev.emit('chats.upsert', [
865
- {
866
- id: metadata.id,
867
- name: metadata.subject,
868
- conversationTimestamp: metadata.creation
869
- }
870
- ]);
871
- ev.emit('groups.upsert', [
872
- {
873
- ...metadata,
874
- author: actingParticipantLid,
875
- authorPn: actingParticipantPn,
876
- authorUsername: actingParticipantUsername
877
- }
878
- ]);
879
- break;
880
- case 'ephemeral':
881
- case 'not_ephemeral':
882
- msg.message = {
883
- protocolMessage: {
884
- type: index_js_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
885
- ephemeralExpiration: +(child.attrs.expiration || 0)
886
- }
887
- };
888
- break;
889
- case 'modify':
890
- const oldNumber = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(p => p.attrs.jid);
891
- msg.messageStubParameters = oldNumber || [];
892
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER;
893
- break;
894
- case 'promote':
895
- case 'demote':
896
- case 'remove':
897
- case 'add':
898
- case 'leave':
899
- const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`;
900
- msg.messageStubType = Types_1.WAMessageStubType[stubType];
901
- const participants = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(({ attrs }) => {
902
- return {
903
- id: attrs.jid,
904
- phoneNumber: (0, WABinary_1.isLidUser)(attrs.jid) && (0, WABinary_1.isPnUser)(attrs.phone_number)
905
- ? attrs.phone_number
906
- : undefined,
907
- lid: (0, WABinary_1.isPnUser)(attrs.jid) && (0, WABinary_1.isLidUser)(attrs.lid) ? attrs.lid : undefined,
908
- username: attrs.participant_username || attrs.username || undefined,
909
- admin: attrs.type || null,
910
- uuid: attrs.uuid || attrs.participant_uuid || undefined
911
- };
912
- });
913
- if (participants.length === 1 &&
914
- ((0, WABinary_1.areJidsSameUser)(participants[0].id, actingParticipantLid) ||
915
- (0, WABinary_1.areJidsSameUser)(participants[0].id, actingParticipantPn)) &&
916
- child.tag === 'remove') {
917
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE;
918
- }
919
- storeParticipantLidPairs(participants);
920
- msg.messageStubParameters = participants.map(a => JSON.stringify(a));
921
- break;
922
- case 'subject':
923
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_SUBJECT;
924
- msg.messageStubParameters = [child.attrs.subject];
925
- break;
926
- case 'description':
927
- const description = (0, WABinary_1.getBinaryNodeChild)(child, 'body')?.content?.toString();
928
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_DESCRIPTION;
929
- msg.messageStubParameters = description ? [description] : undefined;
930
- break;
931
- case 'announcement':
932
- case 'not_announcement':
933
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ANNOUNCE;
934
- msg.messageStubParameters = [child.tag === 'announcement' ? 'on' : 'off'];
935
- break;
936
- case 'locked':
937
- case 'unlocked':
938
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_RESTRICT;
939
- msg.messageStubParameters = [child.tag === 'locked' ? 'on' : 'off'];
940
- break;
941
- case 'invite':
942
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_INVITE_LINK;
943
- msg.messageStubParameters = [child.attrs.code];
944
- break;
945
- case 'member_add_mode':
946
- const addMode = child.content;
947
- if (addMode) {
948
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBER_ADD_MODE;
949
- msg.messageStubParameters = [addMode.toString()];
950
- }
951
- break;
952
- case 'membership_approval_mode':
953
- const approvalMode = (0, WABinary_1.getBinaryNodeChild)(child, 'group_join');
954
- if (approvalMode) {
955
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE;
956
- msg.messageStubParameters = [approvalMode.attrs.state];
957
- }
958
- break;
959
- case 'created_membership_requests':
960
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
961
- msg.messageStubParameters = [
962
- JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
963
- 'created',
964
- child.attrs.request_method
965
- ];
966
- break;
967
- case 'revoked_membership_requests':
968
- const isDenied = (0, WABinary_1.areJidsSameUser)(affectedParticipantLid, actingParticipantLid);
969
- if ((0, WABinary_1.isLidUser)(affectedParticipantLid) && (0, WABinary_1.isPnUser)(affectedParticipantPn)) {
970
- storeParticipantLidPairs([{ id: affectedParticipantLid, phoneNumber: affectedParticipantPn }]);
971
- }
972
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD;
973
- msg.messageStubParameters = [
974
- JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
975
- isDenied ? 'revoked' : 'rejected'
976
- ];
977
- break;
978
- case 'sibling_link': {
979
- const linkedGroupJid = (0, WABinary_1.getBinaryNodeChild)(child, 'group')?.attrs?.jid || child.attrs?.jid;
980
- ev.emit('groups.update', [
981
- {
982
- id: fullNode.attrs.from,
983
- siblingGroupLinked: linkedGroupJid || true,
984
- author: actingParticipantLid,
985
- authorPn: actingParticipantPn
986
- }
987
- ]);
988
- break;
989
- }
990
- case 'sibling_unlink': {
991
- const unlinkedGroupJid = (0, WABinary_1.getBinaryNodeChild)(child, 'group')?.attrs?.jid || child.attrs?.jid;
992
- ev.emit('groups.update', [
993
- {
994
- id: fullNode.attrs.from,
995
- siblingGroupUnlinked: unlinkedGroupJid || true,
996
- author: actingParticipantLid,
997
- authorPn: actingParticipantPn
998
- }
999
- ]);
1000
- break;
1001
- }
1002
- case 'clear_history': {
1003
- const historyClearTimestamp = child.attrs?.t ? +child.attrs.t : (0, Date)();
1004
- ev.emit('groups.update', [
1005
- {
1006
- id: fullNode.attrs.from,
1007
- historyClearTimestamp,
1008
- author: actingParticipantLid,
1009
- authorPn: actingParticipantPn
1010
- }
1011
- ]);
1012
- break;
1013
- }
1014
- }
1015
- };
1016
- const normalizeNotificationParticipant = async (jid, groupData) => {
1017
- if (!jid || typeof jid !== 'string') {
1018
- return jid;
1019
- }
1020
- if (!(0, WABinary_1.isLidUser)(jid) && !(0, WABinary_1.isHostedLidUser)(jid)) {
1021
- return jid;
1022
- }
1023
- const normalized = await (0, jid_display_normalization_1.normalizeMentionedJidsForSend)([jid], groupData, signalRepository, logger);
1024
- return normalized?.[0] || jid;
1025
- };
1026
- const normalizeNotificationParticipantsArray = async (participants, groupData) => {
1027
- if (!Array.isArray(participants)) {
1028
- return participants;
1029
- }
1030
- return Promise.all(participants.map(jid => normalizeNotificationParticipant(jid, groupData)));
1031
- };
1032
- const getNotificationGroupData = async (node) => {
1033
- const groupJid = (0, WABinary_1.jidNormalizedUser)(node?.attrs?.from);
1034
- if (!(0, WABinary_1.isJidGroup)(groupJid)) {
1035
- return undefined;
1036
- }
1037
- try {
1038
- return ((config.useCachedGroupMetadata && config.cachedGroupMetadata
1039
- ? await config.cachedGroupMetadata(groupJid)
1040
- : undefined) || (await sock.groupMetadata(groupJid)));
1041
- }
1042
- catch (error) {
1043
- logger.debug({ error, groupJid }, 'failed to fetch group metadata for notification normalization');
1044
- return undefined;
1045
- }
1046
- };
1047
- const normalizeNotificationStubParameters = async (stubParameters, groupData) => {
1048
- if (!Array.isArray(stubParameters)) {
1049
- return stubParameters;
1050
- }
1051
- const normalized = [];
1052
- for (const entry of stubParameters) {
1053
- if (typeof entry !== 'string') {
1054
- normalized.push(entry);
1055
- continue;
1056
- }
1057
- if ((0, WABinary_1.isLidUser)(entry) || (0, WABinary_1.isHostedLidUser)(entry)) {
1058
- normalized.push(await normalizeNotificationParticipant(entry, groupData));
1059
- continue;
1060
- }
1061
- if (entry.startsWith('{') && entry.includes('"id"')) {
1062
- try {
1063
- const parsed = JSON.parse(entry);
1064
- const explicitPn = typeof parsed?.phoneNumber === 'string'
1065
- ? parsed.phoneNumber
1066
- : typeof parsed?.pn === 'string'
1067
- ? parsed.pn
1068
- : undefined;
1069
- if ((0, WABinary_1.isPnUser)(explicitPn) || (0, WABinary_1.isHostedPnUser)(explicitPn)) {
1070
- parsed.id = explicitPn;
1071
- parsed.pn = explicitPn;
1072
- normalized.push(JSON.stringify(parsed));
1073
- continue;
1074
- }
1075
- if (parsed?.id) {
1076
- parsed.id = await normalizeNotificationParticipant(parsed.id, groupData);
1077
- }
1078
- if (parsed?.lid && !parsed?.pn) {
1079
- parsed.pn = await normalizeNotificationParticipant(parsed.lid, groupData);
1080
- }
1081
- normalized.push(JSON.stringify(parsed));
1082
- continue;
1083
- }
1084
- catch (err) {
1085
- logger.debug({ err, entry }, 'failed to normalize stub parameter JSON');
1086
- }
1087
- }
1088
- normalized.push(entry);
1089
- }
1090
- return normalized;
1091
- };
1092
- const normalizeCallEventJids = async (call, infoChild) => {
1093
- if (!call) {
1094
- return call;
1095
- }
1096
- const callContextGroupJid = call.groupJid || ((0, WABinary_1.isJidGroup)(call.chatId) ? call.chatId : undefined);
1097
- let groupData;
1098
- if (callContextGroupJid) {
1099
- try {
1100
- groupData =
1101
- (config.useCachedGroupMetadata && config.cachedGroupMetadata
1102
- ? await config.cachedGroupMetadata(callContextGroupJid)
1103
- : undefined) || (await sock.groupMetadata(callContextGroupJid));
1104
- }
1105
- catch (error) {
1106
- logger.debug({ error, groupJid: callContextGroupJid }, 'failed to fetch group metadata for call normalization');
1107
- }
1108
- }
1109
- if (call.chatId && !call.isGroup) {
1110
- call.chatId = await normalizeNotificationParticipant(call.chatId, groupData);
1111
- }
1112
- if (call.from) {
1113
- call.from = await normalizeNotificationParticipant(call.from, groupData);
1114
- }
1115
- if (call.groupJid) {
1116
- call.groupJid = await normalizeNotificationParticipant(call.groupJid, groupData);
1117
- }
1118
- if (!call.callerPn && infoChild?.attrs?.caller_lid) {
1119
- call.callerPn = await normalizeNotificationParticipant(infoChild.attrs.caller_lid, groupData);
1120
- }
1121
- if (!call.callerPn && call.from) {
1122
- call.callerPn = call.from;
1123
- }
1124
- return call;
1125
- };
1126
- const normalizeNotificationResult = async (node, result, groupData) => {
1127
- const groupJid = (0, WABinary_1.jidNormalizedUser)(node?.attrs?.from);
1128
- if (!(0, WABinary_1.isJidGroup)(groupJid)) {
1129
- return;
1130
- }
1131
- if (result?.key?.participant) {
1132
- result.key.participant = await normalizeNotificationParticipant(result.key.participant, groupData);
1133
- }
1134
- if (result?.participant) {
1135
- result.participant = await normalizeNotificationParticipant(result.participant, groupData);
1136
- }
1137
- if (Array.isArray(result?.messageStubParameters)) {
1138
- result.messageStubParameters = await normalizeNotificationStubParameters(result.messageStubParameters, groupData);
1139
- }
1140
- };
1141
- const handleInteropNotification = (node, child) => {
1142
- const childTag = child?.tag;
1143
- const attrs = child?.attrs || {};
1144
- if (childTag === 'feature') {
1145
- const feature = attrs.name;
1146
- const enabled = attrs.value === 'true' || attrs.value === '1';
1147
- logger.info({ feature, enabled }, '[interop] feature flag update');
1148
- ev.emit('interop.feature-update', { feature, enabled });
1149
- return;
1150
- }
1151
- if (childTag === 'ig_profile') {
1152
- const contactUpdate = {
1153
- id: (0, WABinary_1.jidNormalizedUser)(node.attrs.from),
1154
- ...(attrs.ig_handle ? { igHandle: attrs.ig_handle } : {}),
1155
- ...(attrs.ig_professional !== undefined ? { igProfessional: attrs.ig_professional === 'true' } : {}),
1156
- ...(attrs.followers !== undefined ? { igFollowers: parseInt(attrs.followers, 10) } : {})
1157
- };
1158
- logger.debug({ contactUpdate }, '[interop] ig_profile update');
1159
- ev.emit('contacts.update', [contactUpdate]);
1160
- return;
1161
- }
1162
- if (childTag === 'peer_device_presence') {
1163
- const jid = attrs.jid || (0, WABinary_1.jidNormalizedUser)(node.attrs.from);
1164
- const presence = attrs.type === 'unavailable' ? 'unavailable' : 'available';
1165
- const isIosInterop = !!authState.creds.interopIosEnabled;
1166
- logger.debug({ jid, presence, isIosInterop }, '[interop] peer_device_presence');
1167
- ev.emit('presence.update', { id: jid, presences: { [jid]: { lastKnownPresence: presence } }, isIosInterop });
1168
- return;
1169
- }
1170
- if (childTag === 'fbid_thread' || childTag === 'fbid_devices') {
1171
- const isIosInterop = !!authState.creds.interopIosEnabled;
1172
- logger.debug({ childTag, attrs, from: node.attrs.from, isIosInterop }, '[interop] fbid association update');
1173
- ev.emit('interop.fbid-update', { type: childTag, jid: node.attrs.from, attrs, isIosInterop });
1174
- return;
1175
- }
1176
- if (childTag === 'participants') {
1177
- const groupJid = node.attrs.from;
1178
- const action = attrs.type;
1179
- const participants = (0, WABinary_1.getBinaryNodeChildren)(child, 'participant').map(p => p.attrs.jid);
1180
- logger.info({ groupJid, action, participants }, '[interop] group participants update');
1181
- ev.emit('group-participants.update', { id: groupJid, participants, action });
1182
- return;
1183
- }
1184
- logger.debug({ childTag, from: node.attrs.from }, '[interop] unhandled interop notification subtype');
1185
- };
1186
- const processNotification = async (node) => {
1187
- const result = {};
1188
- const [child] = (0, WABinary_1.getAllBinaryNodeChildren)(node);
1189
- const nodeType = node.attrs.type;
1190
- const from = (0, WABinary_1.jidNormalizedUser)(node.attrs.from);
1191
- switch (nodeType) {
1192
- case 'newsletter':
1193
- await handleNewsletterNotification(node);
1194
- break;
1195
- case 'mex':
1196
- await handleMexNotification(node);
1197
- break;
1198
- case 'w:gp2':
1199
- if (child?.tag === 'groups_dirty') {
1200
- const dirtyGroupJids = (0, WABinary_1.getBinaryNodeChildren)(child, 'group')
1201
- .map(g => g.attrs.jid)
1202
- .filter(Boolean);
1203
- if (dirtyGroupJids.length) {
1204
- ev.emit('groups.update', dirtyGroupJids.map(jid => ({ id: jid })));
1205
- }
1206
- break;
1207
- }
1208
- const groupData = await getNotificationGroupData(node);
1209
- handleGroupNotification(node, child, result);
1210
- await normalizeNotificationResult(node, result, groupData);
1211
- break;
1212
- case 'mediaretry':
1213
- const event = (0, Utils_1.decodeMediaRetryNode)(node);
1214
- ev.emit('messages.media-update', [event]);
1215
- break;
1216
- case 'encrypt':
1217
- await handleEncryptNotification(node);
1218
- break;
1219
- case 'devices': {
1220
- const addNode = (0, WABinary_1.getBinaryNodeChild)(node, 'add');
1221
- const removeNode = (0, WABinary_1.getBinaryNodeChild)(node, 'remove');
1222
- const changedNode = addNode || removeNode;
1223
- if (!changedNode) {
1224
- logger.debug({ node }, 'devices hash refresh, no add/remove list to report');
1225
- break;
1226
- }
1227
- const isAdded = !!addNode;
1228
- const devices = (0, WABinary_1.getBinaryNodeChildren)(changedNode, 'device');
1229
- const deviceOwnerJid = from;
1230
- const deviceData = devices.map(d => ({
1231
- id: d.attrs.jid,
1232
- lid: d.attrs.lid,
1233
- keyIndex: d.attrs['key-index'] ? +d.attrs['key-index'] : undefined,
1234
- platform: d.attrs.platform || undefined,
1235
- isCompanion: d.attrs.companion === 'true' || undefined
1236
- }));
1237
- const isSelf = (0, WABinary_1.areJidsSameUser)(from, authState.creds.me?.id) ||
1238
- (0, WABinary_1.areJidsSameUser)(from, authState.creds.me?.lid);
1239
- if (isSelf) {
1240
- logger.info({ deviceData, isAdded }, 'my own devices changed');
1241
- }
1242
- if (deviceOwnerJid) {
1243
- ev.emit('devices.update', { id: deviceOwnerJid, devices: deviceData, isSelf, added: isAdded });
1244
- }
1245
- break;
1246
- }
1247
- case 'server_sync':
1248
- const update = (0, WABinary_1.getBinaryNodeChild)(node, 'collection');
1249
- if (update) {
1250
- const name = update.attrs.name;
1251
- await resyncAppState([name], false);
1252
- }
1253
- break;
1254
- case 'picture': {
1255
- const setPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'set');
1256
- const delPicture = (0, WABinary_1.getBinaryNodeChild)(node, 'delete');
1257
- const pictureOwnerJid = (0, WABinary_1.jidNormalizedUser)(node?.attrs?.from);
1258
- if (!pictureOwnerJid) {
1259
- logger.debug({ node }, 'picture notification missing "from", skipping contacts.update');
1260
- break;
1261
- }
1262
- ev.emit('contacts.update', [
1263
- {
1264
- id: pictureOwnerJid,
1265
- imgUrl: setPicture ? 'changed' : 'removed'
1266
- }
1267
- ]);
1268
- if ((0, WABinary_1.isJidGroup)(from)) {
1269
- const node = setPicture || delPicture;
1270
- result.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ICON;
1271
- if (setPicture) {
1272
- result.messageStubParameters = [setPicture.attrs.id];
1273
- }
1274
- result.participant = node?.attrs.author;
1275
- result.key = {
1276
- ...(result.key || {}),
1277
- participant: setPicture?.attrs.author
1278
- };
1279
- }
1280
- break;
1281
- }
1282
- case 'account_sync':
1283
- if (child.tag === 'disappearing_mode') {
1284
- const newDuration = +child.attrs.duration;
1285
- const timestamp = +child.attrs.t;
1286
- logger.info({ newDuration }, 'updated account disappearing mode');
1287
- ev.emit('creds.update', {
1288
- accountSettings: {
1289
- ...authState.creds.accountSettings,
1290
- defaultDisappearingMode: {
1291
- ephemeralExpiration: newDuration,
1292
- ephemeralSettingTimestamp: timestamp
1293
- }
1294
- }
1295
- });
1296
- }
1297
- else if (child.tag === 'blocklist') {
1298
- sock.fetchBlocklist().catch(error => logger.warn({ error }, 'failed to refresh blocklist after account_sync'));
1299
- }
1300
- else if (child.tag === 'devices') {
1301
- const dhash = child.attrs.dhash;
1302
- const deviceNodes = (0, WABinary_1.getBinaryNodeChildren)(child, 'device');
1303
- const keyIndexListNode = (0, WABinary_1.getBinaryNodeChild)(child, 'key-index-list');
1304
- const devices = deviceNodes.map(d => ({
1305
- jid: d.attrs.jid ? String(d.attrs.jid) : undefined,
1306
- keyIndex: d.attrs['key-index'] ? +d.attrs['key-index'] : undefined
1307
- }));
1308
- logger.info({ dhash, deviceCount: devices.length }, 'account devices list synced');
1309
- ev.emit('account.devices-synced', {
1310
- dhash,
1311
- devices,
1312
- keyIndexListTimestamp: keyIndexListNode?.attrs?.ts ? +keyIndexListNode.attrs.ts : undefined,
1313
- keyIndexList: keyIndexListNode?.content ? Buffer.from(keyIndexListNode.content) : undefined
1314
- });
1315
- }
1316
- break;
1317
- case 'disappearing_mode': {
1318
- const newDuration = +child.attrs.duration;
1319
- const timestamp = +child.attrs.t;
1320
- logger.info({ newDuration }, 'updated account disappearing mode');
1321
- ev.emit('creds.update', {
1322
- accountSettings: {
1323
- ...authState.creds.accountSettings,
1324
- defaultDisappearingMode: {
1325
- ephemeralExpiration: newDuration,
1326
- ephemeralSettingTimestamp: timestamp
1327
- }
1328
- }
1329
- });
1330
- break;
1331
- }
1332
- case 'contacts': {
1333
- const updateChild = (0, WABinary_1.getBinaryNodeChild)(node, 'update');
1334
- if (updateChild?.attrs?.jid) {
1335
- ev.emit('contacts.update', [{ id: (0, WABinary_1.jidNormalizedUser)(updateChild.attrs.jid) }]);
1336
- }
1337
- else {
1338
- logger.debug({ node }, 'contacts list changed (hash/sync signal), no per-contact jid to update');
1339
- }
1340
- break;
1341
- }
1342
- case 'business':
1343
- if (child?.tag === 'privacy') {
1344
- ev.emit('business.privacy-settings-sync', {
1345
- jid: from,
1346
- categories: (0, WABinary_1.getBinaryNodeChildren)(child, 'category').map(c => ({
1347
- name: c.attrs.name,
1348
- value: c.attrs.value
1349
- })),
1350
- attrs: child.attrs
1351
- });
1352
- }
1353
- else if (child?.tag === 'profile') {
1354
- ev.emit('contacts.update', [
1355
- {
1356
- id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid || from),
1357
- businessProfileTag: child.attrs.tag
1358
- }
1359
- ]);
1360
- }
1361
- else if (child?.tag === 'verified_name') {
1362
- ev.emit('contacts.update', [
1363
- {
1364
- id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid || from),
1365
- verifiedName: {
1366
- verifiedLevel: child.attrs.verified_level,
1367
- serial: child.attrs.serial,
1368
- version: child.attrs.v
1369
- }
1370
- }
1371
- ]);
1372
- }
1373
- else if (child?.tag === 'remove') {
1374
- if (child.attrs.jid) {
1375
- ev.emit('contacts.update', [{ id: (0, WABinary_1.jidNormalizedUser)(child.attrs.jid), isBusiness: false }]);
1376
- }
1377
- }
1378
- break;
1379
- case 'hosted':
1380
- if (child?.tag === 'onboarding_status') {
1381
- ev.emit('coexistence.update', {
1382
- jid: from,
1383
- kind: 'onboarding',
1384
- status: child.attrs.status,
1385
- productSurface: child.attrs['product_surface']
1386
- });
1387
- }
1388
- else if (child?.tag === 'offboarding') {
1389
- ev.emit('coexistence.update', {
1390
- jid: from,
1391
- kind: 'offboarding',
1392
- productSurface: child.attrs['product_surface']
1393
- });
1394
- }
1395
- break;
1396
- case 'link_code_companion_reg':
1397
- const linkCodeCompanionReg = (0, WABinary_1.getBinaryNodeChild)(node, 'link_code_companion_reg');
1398
- const refBuffer = (0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'link_code_pairing_ref');
1399
- const primaryIdentityPublicKeyBuffer = (0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'primary_identity_pub');
1400
- const primaryEphemeralPublicKeyWrappedBuffer = (0, WABinary_1.getBinaryNodeChildBuffer)(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub');
1401
- if (refBuffer === undefined ||
1402
- primaryIdentityPublicKeyBuffer === undefined ||
1403
- primaryEphemeralPublicKeyWrappedBuffer === undefined) {
1404
- logger.debug({ id: node.attrs.id, type: node.attrs.type }, 'skipping empty link code companion registration');
1405
- break;
1406
- }
1407
- const ref = toRequiredBuffer(refBuffer);
1408
- const primaryIdentityPublicKey = toRequiredBuffer(primaryIdentityPublicKeyBuffer);
1409
- const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(primaryEphemeralPublicKeyWrappedBuffer);
1410
- const codePairingPublicKey = await decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped);
1411
- const companionSharedKey = Utils_1.Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey);
1412
- const random = (0, crypto_1.randomBytes)(32);
1413
- const linkCodeSalt = (0, crypto_1.randomBytes)(32);
1414
- const linkCodePairingExpanded = (0, Utils_1.hkdf)(companionSharedKey, 32, {
1415
- salt: linkCodeSalt,
1416
- info: 'link_code_pairing_key_bundle_encryption_key'
1417
- });
1418
- const encryptPayload = Buffer.concat([
1419
- Buffer.from(authState.creds.signedIdentityKey.public),
1420
- primaryIdentityPublicKey,
1421
- random
1422
- ]);
1423
- const encryptIv = (0, crypto_1.randomBytes)(12);
1424
- const encrypted = (0, Utils_1.aesEncryptGCM)(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0));
1425
- const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted]);
1426
- const identitySharedKey = Utils_1.Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey);
1427
- const identityPayload = Buffer.concat([companionSharedKey, identitySharedKey, random]);
1428
- authState.creds.advSecretKey = Buffer.from((0, Utils_1.hkdf)(identityPayload, 32, { info: 'adv_secret' })).toString('base64');
1429
- await query({
1430
- tag: 'iq',
1431
- attrs: {
1432
- to: WABinary_1.S_WHATSAPP_NET,
1433
- type: 'set',
1434
- id: sock.generateMessageTag(),
1435
- xmlns: 'md'
1436
- },
1437
- content: [
1438
- {
1439
- tag: 'link_code_companion_reg',
1440
- attrs: {
1441
- jid: authState.creds.me.id,
1442
- stage: 'companion_finish'
1443
- },
1444
- content: [
1445
- {
1446
- tag: 'link_code_pairing_wrapped_key_bundle',
1447
- attrs: {},
1448
- content: encryptedPayload
1449
- },
1450
- {
1451
- tag: 'companion_identity_public',
1452
- attrs: {},
1453
- content: authState.creds.signedIdentityKey.public
1454
- },
1455
- {
1456
- tag: 'link_code_pairing_ref',
1457
- attrs: {},
1458
- content: ref
1459
- }
1460
- ]
1461
- }
1462
- ]
1463
- });
1464
- authState.creds.registered = true;
1465
- ev.emit('creds.update', authState.creds);
1466
- break;
1467
- case 'privacy_token':
1468
- await handlePrivacyTokenNotification(node);
1469
- break;
1470
- case 'security':
1471
- const securityType = child?.tag || node.attrs.type;
1472
- const securityData = {
1473
- type: securityType,
1474
- jid: from,
1475
- timestamp: node.attrs.t ? +node.attrs.t : Math.floor(Date.now() / 1000),
1476
- details: child?.attrs || {}
1477
- };
1478
- logger.warn({ securityData }, 'received security notification');
1479
- ev.emit('security.alert', securityData);
1480
- break;
1481
- case 'identity':
1482
- const identityJid = node.attrs.from;
1483
- const identityNewKey = child?.content ? Buffer.from(child.content) : undefined;
1484
- ev.emit('identity.update', {
1485
- jid: (0, WABinary_1.jidNormalizedUser)(identityJid),
1486
- newIdentityKey: identityNewKey,
1487
- timestamp: node.attrs.t ? +node.attrs.t : Math.floor(Date.now() / 1000)
1488
- });
1489
- break;
1490
- case 'server':
1491
- const serverTag = child?.tag;
1492
- if (serverTag === 'config') {
1493
- const configData = {};
1494
- for (const attr of Object.keys(child?.attrs || {})) {
1495
- configData[attr] = child.attrs[attr];
1496
- }
1497
- ev.emit('server.config', configData);
1498
- }
1499
- else if (serverTag === 'app_state_key') {
1500
- logger.info('server pushed app state key update');
1501
- await resyncAppState(['critical_block', 'regular_high', 'regular_low'], false);
1502
- }
1503
- else {
1504
- logger.debug({ node }, 'unhandled server notification');
1505
- }
1506
- break;
1507
- case 'status':
1508
- const statusOwner = node.attrs.from;
1509
- const statusText = child?.content ? child.content.toString() : undefined;
1510
- if (statusOwner && statusText !== undefined) {
1511
- ev.emit('contacts.update', [
1512
- {
1513
- id: (0, WABinary_1.jidNormalizedUser)(statusOwner),
1514
- status: statusText
1515
- }
1516
- ]);
1517
- }
1518
- break;
1519
- case 'usync':
1520
- const usyncResults = (0, WABinary_1.getBinaryNodeChildren)(child || node, 'user');
1521
- if (usyncResults.length) {
1522
- const updates = usyncResults
1523
- .map(u => ({
1524
- id: (0, WABinary_1.jidNormalizedUser)(u.attrs.jid),
1525
- ...(u.attrs.lid ? { lid: u.attrs.lid } : {}),
1526
- ...(u.attrs.username ? { username: u.attrs.username } : {}),
1527
- ...(u.attrs.status ? { status: u.attrs.status } : {})
1528
- }))
1529
- .filter(u => u.id);
1530
- if (updates.length) {
1531
- ev.emit('contacts.update', updates);
1532
- }
1533
- }
1534
- break;
1535
- case 'interop':
1536
- handleInteropNotification(node, child);
1537
- break;
1538
- }
1539
- if (Object.keys(result).length) {
1540
- return result;
1541
- }
1542
- };
1543
- const tcTokenKnownJids = new Set();
1544
- const tcTokenIndexLoaded = (async () => {
1545
- try {
1546
- const jids = await (0, tc_token_utils_1.readTcTokenIndex)(authState.keys);
1547
- for (const jid of jids)
1548
- tcTokenKnownJids.add(jid);
1549
- logger.debug({ count: tcTokenKnownJids.size }, 'loaded tctoken index');
1550
- }
1551
- catch (err) {
1552
- logger.warn({ err: err?.message }, 'failed to load tctoken index');
1553
- }
1554
- })();
1555
- let tcTokenIndexTimer;
1556
- async function flushTcTokenIndex() {
1557
- if (tcTokenIndexTimer) {
1558
- clearTimeout(tcTokenIndexTimer);
1559
- tcTokenIndexTimer = undefined;
1560
- }
1561
- const write = await (0, tc_token_utils_1.buildMergedTcTokenIndexWrite)(authState.keys, tcTokenKnownJids);
1562
- return authState.keys.set({ tctoken: write });
1563
- }
1564
- function scheduleTcTokenIndexSave() {
1565
- if (tcTokenIndexTimer) {
1566
- clearTimeout(tcTokenIndexTimer);
1567
- }
1568
- tcTokenIndexTimer = setTimeout(() => {
1569
- tcTokenIndexTimer = undefined;
1570
- flushTcTokenIndex().catch(err => {
1571
- logger.warn({ err: err?.message }, 'failed to save tctoken index');
1572
- });
1573
- }, 5000);
1574
- }
1575
- function trackTcTokenJid(jid) {
1576
- if (jid && jid !== tc_token_utils_1.TC_TOKEN_INDEX_KEY && !tcTokenKnownJids.has(jid)) {
1577
- tcTokenKnownJids.add(jid);
1578
- scheduleTcTokenIndexSave();
1579
- }
1580
- }
1581
- const handlePrivacyTokenNotification = async (node) => {
1582
- const tokensNode = (0, WABinary_1.getBinaryNodeChild)(node, 'tokens');
1583
- if (!tokensNode)
1584
- return;
1585
- const from = (0, WABinary_1.jidNormalizedUser)(node.attrs.from);
1586
- const senderLid = node.attrs.sender_lid && (0, WABinary_1.isLidUser)((0, WABinary_1.jidNormalizedUser)(node.attrs.sender_lid))
1587
- ? (0, WABinary_1.jidNormalizedUser)(node.attrs.sender_lid)
1588
- : undefined;
1589
- const fallbackJid = senderLid ?? (await (0, tc_token_utils_1.resolveTcTokenJid)(from, getLIDForPN));
1590
- logger.debug({ from, storageJid: fallbackJid }, 'processing privacy token notification');
1591
- await (0, tc_token_utils_1.storeTcTokensFromIqResult)({
1592
- result: node,
1593
- fallbackJid,
1594
- keys: authState.keys,
1595
- getLIDForPN,
1596
- onNewJidStored: trackTcTokenJid
1597
- });
1598
- };
1599
- async function decipherLinkPublicKey(data) {
1600
- const buffer = toRequiredBuffer(data);
1601
- const salt = buffer.slice(0, 32);
1602
- const secretKey = await (0, Utils_1.derivePairingCodeKey)(authState.creds.pairingCode, salt);
1603
- const iv = buffer.slice(32, 48);
1604
- const payload = buffer.slice(48, 80);
1605
- return (0, Utils_1.aesDecryptCTR)(payload, secretKey, iv);
1606
- }
1607
- function toRequiredBuffer(data) {
1608
- if (data === undefined) {
1609
- throw new boom_1.Boom('Invalid buffer', { statusCode: 400 });
1610
- }
1611
- return data instanceof Buffer ? data : Buffer.from(data);
1612
- }
1613
- const willSendMessageAgain = async (id, participant) => {
1614
- const key = `${id}:${participant}`;
1615
- const retryCount = (await msgRetryCache.get(key)) || 0;
1616
- return retryCount < maxMsgRetryCount;
1617
- };
1618
- const updateSendMessageAgainCount = async (id, participant) => {
1619
- const key = `${id}:${participant}`;
1620
- const newValue = ((await msgRetryCache.get(key)) || 0) + 1;
1621
- await msgRetryCache.set(key, newValue);
1622
- };
1623
- const sendMessagesAgain = async (key, ids, retryNode) => {
1624
- const remoteJid = key.remoteJid;
1625
- const participant = key.participant || remoteJid;
1626
- const retryCount = +retryNode.attrs.count || 1;
1627
- const msgs = [];
1628
- for (const id of ids) {
1629
- let msg;
1630
- if (messageRetryManager) {
1631
- const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id);
1632
- if (cachedMsg) {
1633
- msg = cachedMsg.message;
1634
- logger.debug({ jid: remoteJid, id }, 'found message in retry cache');
1635
- messageRetryManager.markRetrySuccess(id);
1636
- }
1637
- }
1638
- if (!msg) {
1639
- msg = await getMessage({ ...key, id });
1640
- if (msg) {
1641
- logger.debug({ jid: remoteJid, id }, 'found message via getMessage');
1642
- if (messageRetryManager) {
1643
- messageRetryManager.markRetrySuccess(id);
1644
- }
1645
- }
1646
- }
1647
- msgs.push(msg);
1648
- }
1649
- const sendToAll = !(0, WABinary_1.jidDecode)(participant)?.device;
1650
- let shouldRecreateSession = false;
1651
- let recreateReason = '';
1652
- if (enableAutoSessionRecreation && messageRetryManager && retryCount > 1) {
1653
- try {
1654
- const sessionId = signalRepository.jidToSignalProtocolAddress(participant);
1655
- const hasSession = await signalRepository.validateSession(participant);
1656
- const result = messageRetryManager.shouldRecreateSession(participant, hasSession.exists);
1657
- shouldRecreateSession = result.recreate;
1658
- recreateReason = result.reason;
1659
- if (shouldRecreateSession) {
1660
- logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry');
1661
- await authState.keys.set({ session: { [sessionId]: null } });
1662
- }
1663
- }
1664
- catch (error) {
1665
- logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry');
1666
- }
1667
- }
1668
- await assertSessions([participant], true);
1669
- if ((0, WABinary_1.isJidGroup)(remoteJid)) {
1670
- await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
1671
- }
1672
- logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp');
1673
- for (const [i, msg] of msgs.entries()) {
1674
- if (!ids[i])
1675
- continue;
1676
- if (msg && (await willSendMessageAgain(ids[i], participant))) {
1677
- await updateSendMessageAgainCount(ids[i], participant);
1678
- const msgRelayOpts = { messageId: ids[i] };
1679
- if (sendToAll) {
1680
- msgRelayOpts.useUserDevicesCache = false;
1681
- }
1682
- else {
1683
- msgRelayOpts.participant = {
1684
- jid: participant,
1685
- count: +retryNode.attrs.count
1686
- };
1687
- }
1688
- await relayMessage(key.remoteJid, msg, msgRelayOpts);
1689
- }
1690
- else {
1691
- logger.debug({ jid: key.remoteJid, id: ids[i] }, 'recv retry request, but message not available');
1692
- }
1693
- }
1694
- };
1695
- const handleReceipt = async (node) => {
1696
- const { attrs, content } = node;
1697
- const isLid = attrs.from.includes('lid');
1698
- const isNodeFromMe = (0, WABinary_1.areJidsSameUser)(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id);
1699
- const remoteJid = !isNodeFromMe || (0, WABinary_1.isJidGroup)(attrs.from) ? attrs.from : attrs.recipient;
1700
- const fromMe = !attrs.recipient || ((attrs.type === 'retry' || attrs.type === 'sender') && isNodeFromMe);
1701
- const key = {
1702
- remoteJid,
1703
- id: '',
1704
- fromMe,
1705
- participant: attrs.participant
1706
- };
1707
- if (shouldIgnoreJid(remoteJid) && remoteJid !== WABinary_1.S_WHATSAPP_NET) {
1708
- logger.debug({ remoteJid }, 'ignoring receipt from jid');
1709
- await sendMessageAck(node);
1710
- return;
1711
- }
1712
- const ids = [attrs.id];
1713
- if (Array.isArray(content)) {
1714
- const items = (0, WABinary_1.getBinaryNodeChildren)(content[0], 'item');
1715
- ids.push(...items.map(i => i.attrs.id));
1716
- }
1717
- if (attrs.type === 'media-retry') {
1718
- const mediaRetryNode = (0, WABinary_1.getBinaryNodeChild)(node, 'media-retry') ||
1719
- (0, WABinary_1.getBinaryNodeChild)(node, 'media_retry');
1720
- ev.emit('messages.media-retry', {
1721
- ids,
1722
- from: attrs.from,
1723
- participant: attrs.participant,
1724
- t: attrs.t,
1725
- retryAttrs: mediaRetryNode?.attrs
1726
- });
1727
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack media-retry receipt'));
1728
- return;
1729
- }
1730
- if (attrs.type === 'server-error') {
1731
- const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error');
1732
- ev.emit('messages.server-error', {
1733
- ids,
1734
- from: attrs.from,
1735
- participant: attrs.participant,
1736
- t: attrs.t,
1737
- errorCode: errorNode?.attrs?.code || attrs.error_code,
1738
- errorText: errorNode?.attrs?.text || errorNode?.content?.toString?.()
1739
- });
1740
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack server-error receipt'));
1741
- return;
1742
- }
1743
- if (attrs.receipt_agg) {
1744
- ev.emit('receipt.batched', {
1745
- ids,
1746
- from: attrs.from,
1747
- participant: attrs.participant,
1748
- type: attrs.type,
1749
- t: attrs.t,
1750
- receiptAgg: attrs.receipt_agg
1751
- });
1752
- }
1753
- try {
1754
- await Promise.all([
1755
- receiptMutex.mutex(async () => {
1756
- const status = (0, Utils_1.getStatusFromReceiptType)(attrs.type);
1757
- if (typeof status !== 'undefined' &&
1758
- (status >= index_js_1.proto.WebMessageInfo.Status.SERVER_ACK || !isNodeFromMe)) {
1759
- if ((0, WABinary_1.isJidGroup)(remoteJid) || (0, WABinary_1.isJidStatusBroadcast)(remoteJid)) {
1760
- if (attrs.participant) {
1761
- const updateKey = status === index_js_1.proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
1762
- ev.emit('message-receipt.update', ids.map(id => ({
1763
- key: { ...key, id },
1764
- receipt: {
1765
- userJid: (0, WABinary_1.jidNormalizedUser)(attrs.participant),
1766
- [updateKey]: +attrs.t
1767
- }
1768
- })));
1769
- }
1770
- }
1771
- else {
1772
- ev.emit('messages.update', ids.map(id => ({
1773
- key: { ...key, id },
1774
- update: { status, messageTimestamp: (0, Utils_1.toNumber)(+(attrs.t ?? 0)) }
1775
- })));
1776
- }
1777
- }
1778
- if (attrs.type === 'retry') {
1779
- key.participant = key.participant || attrs.from;
1780
- const retryNode = (0, WABinary_1.getBinaryNodeChild)(node, 'retry');
1781
- if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
1782
- if (key.fromMe) {
1783
- try {
1784
- await updateSendMessageAgainCount(ids[0], key.participant);
1785
- logger.debug({ attrs, key }, 'recv retry request');
1786
- await sendMessagesAgain(key, ids, retryNode);
1787
- }
1788
- catch (error) {
1789
- logger.error({ key, ids, trace: error instanceof Error ? error.stack : 'Unknown error' }, 'error in sending message again');
1790
- }
1791
- }
1792
- else {
1793
- logger.info({ attrs, key }, 'recv retry for not fromMe message');
1794
- }
1795
- }
1796
- else {
1797
- logger.info({ attrs, key }, 'will not send message again, as sent too many times');
1798
- }
1799
- }
1800
- })
1801
- ]);
1802
- }
1803
- finally {
1804
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack receipt'));
1805
- }
1806
- };
1807
- const handleNotification = async (node) => {
1808
- const remoteJid = node.attrs.from;
1809
- if (shouldIgnoreJid(remoteJid) && remoteJid !== WABinary_1.S_WHATSAPP_NET) {
1810
- logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification');
1811
- await sendMessageAck(node);
1812
- return;
1813
- }
1814
- try {
1815
- await Promise.all([
1816
- notificationMutex.mutex(async () => {
1817
- const msg = await processNotification(node);
1818
- if (msg) {
1819
- const fromMe = (0, WABinary_1.areJidsSameUser)(node.attrs.participant || remoteJid, authState.creds.me.id);
1820
- const { senderAlt: participantAlt, addressingMode } = (0, Utils_1.extractAddressingContext)(node);
1821
- msg.key = {
1822
- remoteJid,
1823
- fromMe,
1824
- participant: node.attrs.participant,
1825
- participantAlt,
1826
- participantUsername: node.attrs.participant_username,
1827
- addressingMode,
1828
- id: node.attrs.id,
1829
- ...(msg.key || {})
1830
- };
1831
- msg.participant ?? (msg.participant = node.attrs.participant);
1832
- msg.messageTimestamp = +node.attrs.t;
1833
- let groupDataForNormalization;
1834
- if ((0, WABinary_1.isJidGroup)(msg?.key?.remoteJid)) {
1835
- try {
1836
- groupDataForNormalization =
1837
- (config.useCachedGroupMetadata && config.cachedGroupMetadata
1838
- ? await config.cachedGroupMetadata(msg.key.remoteJid)
1839
- : undefined) || (await sock.groupMetadata(msg.key.remoteJid));
1840
- }
1841
- catch (error) {
1842
- logger.debug({ error, jid: msg.key.remoteJid }, 'failed to fetch group metadata for recv jid normalization');
1843
- }
1844
- }
1845
- await (0, jid_display_normalization_1.normalizeMessageForDisplayJids)(msg, signalRepository, logger, groupDataForNormalization);
1846
- const fullMsg = index_js_1.proto.WebMessageInfo.fromObject(msg);
1847
- await upsertMessage(fullMsg, 'append');
1848
- }
1849
- })
1850
- ]);
1851
- }
1852
- finally {
1853
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack notification'));
1854
- }
1855
- };
1856
- const handleMessage = async (node) => {
1857
- const isInteropNode = (0, WABinary_1.isInteropUser)(node.attrs.from);
1858
- if (isInteropNode) {
1859
- logger.info({
1860
- from: node.attrs.from,
1861
- id: node.attrs.id,
1862
- type: node.attrs.type,
1863
- sts: node.attrs.sts,
1864
- display_name: node.attrs.display_name,
1865
- encType: (0, WABinary_1.getBinaryNodeChild)(node, 'enc')?.attrs?.type
1866
- }, '[interop] node arrived');
1867
- }
1868
- if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== WABinary_1.S_WHATSAPP_NET) {
1869
- if (isInteropNode)
1870
- logger.warn({ from: node.attrs.from }, '[interop] node dropped by shouldIgnoreJid');
1871
- logger.debug({ key: node.attrs.key }, 'ignored message');
1872
- await sendMessageAck(node, Utils_1.NACK_REASONS.UnhandledError);
1873
- return;
1874
- }
1875
- const groupJid = node.attrs.from;
1876
- const communityJid = linkedParentMap[groupJid];
1877
- const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc');
1878
- if (encNode?.attrs.type === 'msmsg') {
1879
- if (getMessage) {
1880
- const metaNode = (0, WABinary_1.getBinaryNodeChild)(node, 'meta');
1881
- const targetId = metaNode?.attrs?.target_id;
1882
- if (targetId) {
1883
- try {
1884
- const targetMsg = await getMessage({ remoteJid: node.attrs.from, id: targetId, fromMe: true });
1885
- const secret = targetMsg?.messageContextInfo?.messageSecret;
1886
- if (secret) {
1887
- ;
1888
- (0, Utils_1.setBotMessageSecret)(targetId, secret);
1889
- }
1890
- }
1891
- catch (err) {
1892
- logger.debug({ err, targetId }, 'failed to retrieve message secret for msmsg');
1893
- }
1894
- }
1895
- }
1896
- }
1897
- let acked = false;
1898
- try {
1899
- const { fullMessage: msg, category, author, decrypt } = (0, Utils_1.decryptMessageNode)(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
1900
- if (isInteropNode) {
1901
- logger.info({ remoteJid: msg.key.remoteJid, id: msg.key.id, fromMe: msg.key.fromMe, pushName: msg.pushName }, '[interop] decodeMessageNode OK');
1902
- }
1903
- const alt = msg.key.participantAlt || msg.key.remoteJidAlt;
1904
- if (!!alt) {
1905
- const altServer = (0, WABinary_1.jidDecode)(alt)?.server;
1906
- const primaryJid = msg.key.participant || msg.key.remoteJid;
1907
- if (altServer === 'lid') {
1908
- if (!(await signalRepository.lidMapping.getPNForLID(alt))) {
1909
- await signalRepository.lidMapping.storeLIDPNMappings([{ lid: alt, pn: primaryJid }]);
1910
- await signalRepository.migrateSession(primaryJid, alt);
1911
- }
1912
- }
1913
- else {
1914
- await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }]);
1915
- await signalRepository.migrateSession(alt, primaryJid);
1916
- }
1917
- }
1918
- else if (msg.key.addressingMode === 'lid') {
1919
- const primaryJid = msg.key.participant || msg.key.remoteJid;
1920
- if ((0, WABinary_1.isLidUser)(primaryJid)) {
1921
- try {
1922
- const pn = await signalRepository.lidMapping.getPNForLID(primaryJid);
1923
- const pnJid = pn ? (0, WABinary_1.jidNormalizedUser)(pn) : '';
1924
- if (pnJid) {
1925
- if ((0, WABinary_1.isJidGroup)(msg.key.remoteJid)) {
1926
- msg.key.participantAlt = pnJid;
1927
- }
1928
- else {
1929
- msg.key.remoteJidAlt = pnJid;
1930
- }
1931
- logger.debug({ lid: primaryJid, pn: pnJid }, 'recovered alt JID from LID mapping store');
1932
- }
1933
- }
1934
- catch (err) {
1935
- logger.warn({ err, lid: primaryJid }, 'failed to recover alt JID from LID mapping store');
1936
- }
1937
- }
1938
- }
1939
- await messageMutex.mutex(async () => {
1940
- await decrypt();
1941
- if (isInteropNode) {
1942
- const stubType = msg.messageStubType;
1943
- const stubText = msg.messageStubParameters?.[0];
1944
- logger.info({
1945
- id: msg.key.id,
1946
- hasMessage: !!msg.message,
1947
- messageKeys: msg.message ? Object.keys(msg.message) : [],
1948
- stubType,
1949
- stubText
1950
- }, '[interop] decrypt() done');
1951
- }
1952
- if (msg.key?.remoteJid && msg.key?.id && msg.message && messageRetryManager) {
1953
- messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
1954
- }
1955
- if (msg.messageStubType === index_js_1.proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
1956
- if (msg?.messageStubParameters?.[0] === Utils_1.MISSING_KEYS_ERROR_TEXT) {
1957
- if (isInteropNode)
1958
- logger.warn({ id: msg.key.id }, '[interop] decrypt failed: MISSING_KEYS');
1959
- acked = true;
1960
- return sendMessageAck(node, Utils_1.NACK_REASONS.ParsingError);
1961
- }
1962
- if (msg.messageStubParameters?.[0] === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT) {
1963
- const unavailableNode = (0, WABinary_1.getBinaryNodeChild)(node, 'unavailable');
1964
- const unavailableType = unavailableNode?.attrs?.type;
1965
- if (unavailableType === 'bot_unavailable_fanout' ||
1966
- unavailableType === 'hosted_unavailable_fanout' ||
1967
- unavailableType === 'view_once_unavailable_fanout') {
1968
- logger.debug({ msgId: msg.key.id, unavailableType }, 'skipping placeholder resend for excluded unavailable type');
1969
- acked = true;
1970
- return sendMessageAck(node);
1971
- }
1972
- const messageAge = (0, Utils_1.unixTimestampSeconds)() - (0, Utils_1.toNumber)(msg.messageTimestamp);
1973
- if (messageAge > Defaults_1.PLACEHOLDER_MAX_AGE_SECONDS) {
1974
- logger.debug({ msgId: msg.key.id, messageAge }, 'skipping placeholder resend for old message');
1975
- acked = true;
1976
- return sendMessageAck(node);
1977
- }
1978
- const cleanKey = {
1979
- remoteJid: msg.key.remoteJid,
1980
- fromMe: msg.key.fromMe,
1981
- id: msg.key.id,
1982
- participant: msg.key.participant
1983
- };
1984
- const msgData = {
1985
- key: msg.key,
1986
- messageTimestamp: msg.messageTimestamp,
1987
- pushName: msg.pushName,
1988
- participant: msg.participant,
1989
- verifiedBizName: msg.verifiedBizName
1990
- };
1991
- requestPlaceholderResend(cleanKey, msgData)
1992
- .then(requestId => {
1993
- if (requestId && requestId !== 'RESOLVED') {
1994
- logger.debug({ msgId: msg.key.id, requestId }, 'requested placeholder resend for unavailable message');
1995
- ev.emit('messages.update', [
1996
- {
1997
- key: msg.key,
1998
- update: { messageStubParameters: [Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT, requestId] }
1999
- }
2000
- ]);
2001
- }
2002
- })
2003
- .catch(err => {
2004
- logger.warn({ err, msgId: msg.key.id }, 'failed to request placeholder resend for unavailable message');
2005
- });
2006
- acked = true;
2007
- await sendMessageAck(node);
2008
- }
2009
- else {
2010
- if ((0, WABinary_1.isJidStatusBroadcast)(msg.key.remoteJid)) {
2011
- const messageAge = (0, Utils_1.unixTimestampSeconds)() - (0, Utils_1.toNumber)(msg.messageTimestamp);
2012
- if (messageAge > Defaults_1.STATUS_EXPIRY_SECONDS) {
2013
- logger.debug({ msgId: msg.key.id, messageAge, remoteJid: msg.key.remoteJid }, 'skipping retry for expired status message');
2014
- acked = true;
2015
- return sendMessageAck(node);
2016
- }
2017
- }
2018
- const errorMessage = msg?.messageStubParameters?.[0] || '';
2019
- const isPreKeyError = errorMessage.includes('PreKey');
2020
- logger.debug(`[handleMessage] Attempting retry request for failed decryption`);
2021
- await retryMutex.mutex(async () => {
2022
- try {
2023
- if (!ws.isOpen) {
2024
- logger.debug({ node }, 'Connection closed, skipping retry');
2025
- return;
2026
- }
2027
- if (isPreKeyError) {
2028
- logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
2029
- try {
2030
- logger.debug('Uploading pre-keys for error recovery');
2031
- await uploadPreKeys(5);
2032
- logger.debug('Waiting for server to process new pre-keys');
2033
- await (0, Utils_1.delay)(1000);
2034
- }
2035
- catch (uploadErr) {
2036
- logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
2037
- }
2038
- }
2039
- const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc');
2040
- await sendRetryRequest(node, !encNode);
2041
- if (retryRequestDelayMs) {
2042
- await (0, Utils_1.delay)(retryRequestDelayMs);
2043
- }
2044
- }
2045
- catch (err) {
2046
- logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
2047
- try {
2048
- const encNode = (0, WABinary_1.getBinaryNodeChild)(node, 'enc');
2049
- await sendRetryRequest(node, !encNode);
2050
- }
2051
- catch (retryErr) {
2052
- logger.error({ retryErr }, 'Failed to send retry after error handling');
2053
- }
2054
- }
2055
- acked = true;
2056
- await sendMessageAck(node, Utils_1.NACK_REASONS.UnhandledError);
2057
- });
2058
- }
2059
- }
2060
- else {
2061
- if (messageRetryManager && msg.key.id) {
2062
- messageRetryManager.cancelPendingPhoneRequest(msg.key.id);
2063
- }
2064
- const isNewsletter = (0, WABinary_1.isJidNewsletter)(msg.key.remoteJid);
2065
- if (!isNewsletter) {
2066
- let type = undefined;
2067
- let participant = msg.key.participant;
2068
- if (communityJid) {
2069
- msg.communityJid = communityJid;
2070
- }
2071
- if (category === 'peer') {
2072
- type = 'peer_msg';
2073
- }
2074
- else if (msg.key.fromMe) {
2075
- type = 'sender';
2076
- if ((0, WABinary_1.isLidUser)(msg.key.remoteJid) || (0, WABinary_1.isLidUser)(msg.key.remoteJidAlt)) {
2077
- participant = author;
2078
- }
2079
- }
2080
- else if (!sendActiveReceipts) {
2081
- type = 'inactive';
2082
- }
2083
- acked = true;
2084
- const interopSts = (0, WABinary_1.isInteropUser)(msg.key.remoteJid) ? node.attrs.sts : undefined;
2085
- await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type, interopSts);
2086
- const isAnyHistoryMsg = (0, Utils_1.getHistoryMsg)(msg.message);
2087
- if (isAnyHistoryMsg) {
2088
- const jid = (0, WABinary_1.jidNormalizedUser)(msg.key.remoteJid);
2089
- await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
2090
- }
2091
- }
2092
- else {
2093
- acked = true;
2094
- await sendMessageAck(node);
2095
- logger.debug({ key: msg.key }, 'processed newsletter message without receipts');
2096
- }
2097
- }
2098
- ;
2099
- (0, Utils_1.cleanMessage)(msg, authState.creds.me.id, authState.creds.me.lid);
2100
- const msgTs = (0, Utils_1.toNumber)(msg.messageTimestamp);
2101
- const isPending = !isConnected || node.attrs.offline || msgTs < socketCreatedAt;
2102
- if (isInteropNode) {
2103
- logger.info({
2104
- id: msg.key.id,
2105
- isPending,
2106
- isConnected,
2107
- offline: node.attrs.offline,
2108
- msgTs,
2109
- socketCreatedAt
2110
- }, isPending ? '[interop] upsert as append (pending/offline)' : '[interop] upsert as notify');
2111
- }
2112
- if (isPending) {
2113
- await upsertMessage(msg, 'append');
2114
- return;
2115
- }
2116
- let groupDataForNormalization;
2117
- if ((0, WABinary_1.isJidGroup)(msg?.key?.remoteJid)) {
2118
- try {
2119
- groupDataForNormalization =
2120
- (config.useCachedGroupMetadata && config.cachedGroupMetadata
2121
- ? await config.cachedGroupMetadata(msg.key.remoteJid)
2122
- : undefined) || (await sock.groupMetadata(msg.key.remoteJid));
2123
- }
2124
- catch (error) {
2125
- logger.debug({ error, jid: msg.key.remoteJid }, 'failed to fetch group metadata for recv jid normalization');
2126
- }
2127
- }
2128
- await (0, jid_display_normalization_1.normalizeMessageForDisplayJids)(msg, signalRepository, logger, groupDataForNormalization);
2129
- await upsertMessage(msg, 'notify');
2130
- });
2131
- }
2132
- catch (error) {
2133
- if (isInteropNode) {
2134
- logger.error({ error: error?.message, stack: error?.stack, from: node.attrs.from, id: node.attrs.id }, '[interop] unhandled error in handleMessage');
2135
- }
2136
- logger.error({ error, node: (0, WABinary_1.binaryNodeToString)(node) }, 'error in handling message');
2137
- if (!acked) {
2138
- await sendMessageAck(node, Utils_1.NACK_REASONS.UnhandledError).catch(ackErr => logger.error({ ackErr }, 'failed to ack message after error'));
2139
- }
2140
- }
2141
- };
2142
- const parseGroupCallRoster = groupInfoNode => (0, WABinary_1.getBinaryNodeChildren)(groupInfoNode, 'user').map(userNode => ({
2143
- jid: userNode.attrs.jid,
2144
- state: userNode.attrs.state,
2145
- userPn: userNode.attrs.user_pn,
2146
- devices: (0, WABinary_1.getBinaryNodeChildren)(userNode, 'device').map(d => ({
2147
- jid: d.attrs.jid,
2148
- platform: d.attrs.platform
2149
- }))
2150
- }));
2151
- const handleCall = async (node) => {
2152
- try {
2153
- const { attrs } = node;
2154
- const [infoChild] = (0, WABinary_1.getAllBinaryNodeChildren)(node);
2155
- if (!infoChild) {
2156
- throw new boom_1.Boom('Missing call info in call node');
2157
- }
2158
- const status = (0, Utils_1.getCallStatusFromNode)(infoChild);
2159
- const callId = infoChild.attrs['call-id'];
2160
- const from = infoChild.attrs.from || infoChild.attrs['call-creator'];
2161
- const call = {
2162
- chatId: attrs.from,
2163
- from,
2164
- callerPn: infoChild.attrs['caller_pn'],
2165
- id: callId,
2166
- date: new Date(+attrs.t * 1000),
2167
- offline: !!attrs.offline,
2168
- status
2169
- };
2170
- if (status === 'relaylatency') {
2171
- const latencyValue = infoChild.attrs.latency || infoChild.attrs['latency_ms'] || infoChild.attrs['latency-ms'];
2172
- const latencyMs = latencyValue ? Number(latencyValue) : undefined;
2173
- if (Number.isFinite(latencyMs)) {
2174
- call.latencyMs = latencyMs;
2175
- }
2176
- }
2177
- if (status === 'offer') {
2178
- const videoNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'video');
2179
- const audioNodes = (0, WABinary_1.getBinaryNodeChildren)(infoChild, 'audio');
2180
- const silenceNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'silence');
2181
- call.isVideo = !!videoNode;
2182
- call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
2183
- call.groupJid = infoChild.attrs['group-jid'];
2184
- call.isLightweight = infoChild.attrs.lightweight === '1';
2185
- if (silenceNode?.attrs?.reason) {
2186
- call.silenceReason = silenceNode.attrs.reason;
2187
- }
2188
- if (audioNodes.length) {
2189
- call.audioCodecs = audioNodes.map(n => ({ enc: n.attrs.enc, rate: n.attrs.rate ? +n.attrs.rate : undefined }));
2190
- call.audioCodec = audioNodes[0].attrs.enc;
2191
- }
2192
- if (videoNode?.attrs?.dec) {
2193
- call.videoCodec = videoNode.attrs.dec;
2194
- }
2195
- const encNode = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc');
2196
- if (encNode?.content) {
2197
- try {
2198
- const encType = encNode.attrs.type || 'msg';
2199
- const ciphertext = Buffer.isBuffer(encNode.content) ? encNode.content : Buffer.from(encNode.content);
2200
- const decrypted = await signalRepository.decryptMessage({ jid: attrs.from, type: encType, ciphertext });
2201
- const unpadded = (0, Utils_1.unpadRandomMax16)(decrypted);
2202
- const callMsg = index_js_1.proto.Message.decode(unpadded);
2203
- if (callMsg?.call?.callKey?.length) {
2204
- call.callKey = Buffer.from(callMsg.call.callKey);
2205
- }
2206
- }
2207
- catch (e) {
2208
- logger.debug({ e }, 'failed to decrypt call enc');
2209
- }
2210
- }
2211
- await callOfferCache.set(call.id, call);
2212
- }
2213
- else if (status === 'waiting_room_request') {
2214
- call.peerJid = infoChild.attrs.from || infoChild.attrs['peer-jid'] || infoChild.attrs['user-jid'];
2215
- }
2216
- const existingCall = await callOfferCache.get(call.id);
2217
- if (existingCall) {
2218
- call.isVideo = existingCall.isVideo;
2219
- call.isGroup = existingCall.isGroup;
2220
- call.callerPn = call.callerPn || existingCall.callerPn;
2221
- }
2222
- if (status === 'peer_state') {
2223
- const stateChild = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'peer_state');
2224
- call.state = stateChild?.attrs?.state || infoChild.attrs?.state;
2225
- }
2226
- else if (status === 'group_info') {
2227
- call.payload = infoChild.attrs;
2228
- call.participants = parseGroupCallRoster(infoChild);
2229
- }
2230
- else if (status === 'video_state') {
2231
- const vsChild = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'video_state');
2232
- const enabledRaw = vsChild?.attrs?.enabled ?? infoChild.attrs?.enabled;
2233
- call.enabled = enabledRaw === 'true' || enabledRaw === '1';
2234
- }
2235
- else if (status === 'enc_rekey') {
2236
- const rekeyChild = (0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc-rekey') ||
2237
- (0, WABinary_1.getBinaryNodeChild)(infoChild, 'enc_rekey');
2238
- if (rekeyChild?.content && Buffer.isBuffer(rekeyChild.content)) {
2239
- try {
2240
- call.rekeyPayload = (0, Utils_1.decodeE2eRekeyPayload)(rekeyChild.content);
2241
- }
2242
- catch (e) {
2243
- logger.debug({ e }, 'failed to decode enc-rekey payload in handleCall');
2244
- }
2245
- }
2246
- }
2247
- if (status === 'reject' ||
2248
- status === 'accept' ||
2249
- status === 'timeout' ||
2250
- status === 'terminate' ||
2251
- status === 'reject_do_not_disturb' ||
2252
- status === 'mic_permission_denied' ||
2253
- status === 'camera_permission_denied' ||
2254
- status === 'remote_busy' ||
2255
- status === 'remote_offline') {
2256
- await callOfferCache.del(call.id);
2257
- }
2258
- await normalizeCallEventJids(call, infoChild);
2259
- ev.emit('call', [call]);
2260
- }
2261
- catch (error) {
2262
- logger.error({ error, node: (0, WABinary_1.binaryNodeToString)(node) }, 'error in handling call');
2263
- }
2264
- finally {
2265
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack call'));
2266
- }
2267
- };
2268
- const CALL_STATE_TAGS = new Set([
2269
- 'offer',
2270
- 'offer_notice',
2271
- 'terminate',
2272
- 'accept',
2273
- 'reject',
2274
- 'preaccept',
2275
- 'accept_ack',
2276
- 'enc-rekey',
2277
- 'enc_rekey',
2278
- 'peer_state',
2279
- 'group_info',
2280
- 'video_state',
2281
- 'video_state_ack',
2282
- 'flow_control',
2283
- 'mute_v2',
2284
- 'waiting_room_request'
2285
- ]);
2286
- const handleStandaloneCallStanza = async (node) => {
2287
- try {
2288
- if (!CALL_STATE_TAGS.has(node.tag)) {
2289
- return;
2290
- }
2291
- const { attrs } = node;
2292
- const status = (0, Utils_1.getCallStatusFromNode)(node);
2293
- const callId = attrs['call-id'];
2294
- const from = attrs.from || attrs['call-creator'];
2295
- const call = {
2296
- chatId: attrs.from || from,
2297
- from,
2298
- callerPn: attrs['caller_pn'],
2299
- id: callId,
2300
- date: attrs.t ? new Date(+attrs.t * 1000) : new Date(),
2301
- offline: !!attrs.offline,
2302
- status
2303
- };
2304
- if (status === 'offer') {
2305
- const videoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'video');
2306
- const audioNodes = (0, WABinary_1.getBinaryNodeChildren)(node, 'audio');
2307
- const silenceNode = (0, WABinary_1.getBinaryNodeChild)(node, 'silence');
2308
- call.isVideo = !!videoNode;
2309
- call.isGroup = attrs.type === 'group' || !!attrs['group-jid'];
2310
- call.groupJid = attrs['group-jid'];
2311
- call.isLightweight = attrs.lightweight === '1';
2312
- if (silenceNode?.attrs?.reason) {
2313
- call.silenceReason = silenceNode.attrs.reason;
2314
- }
2315
- if (audioNodes.length) {
2316
- call.audioCodecs = audioNodes.map(n => ({ enc: n.attrs.enc, rate: n.attrs.rate ? +n.attrs.rate : undefined }));
2317
- call.audioCodec = audioNodes[0].attrs.enc;
2318
- }
2319
- if (videoNode?.attrs?.dec) {
2320
- call.videoCodec = videoNode.attrs.dec;
2321
- }
2322
- if (callId) {
2323
- await callOfferCache.set(callId, call);
2324
- }
2325
- }
2326
- const existingCall = callId ? await callOfferCache.get(callId) : undefined;
2327
- if (existingCall) {
2328
- call.isVideo = existingCall.isVideo;
2329
- call.isGroup = existingCall.isGroup;
2330
- call.callerPn = call.callerPn || existingCall.callerPn;
2331
- }
2332
- if (status === 'peer_state') {
2333
- const stateChild = (0, WABinary_1.getBinaryNodeChild)(node, 'peer_state');
2334
- call.state = stateChild?.attrs?.state || attrs?.state;
2335
- }
2336
- else if (status === 'group_info') {
2337
- call.payload = attrs;
2338
- call.participants = parseGroupCallRoster(node);
2339
- }
2340
- else if (status === 'video_state') {
2341
- const vsChild = (0, WABinary_1.getBinaryNodeChild)(node, 'video_state');
2342
- const enabledRaw = vsChild?.attrs?.enabled ?? attrs?.enabled;
2343
- call.enabled = enabledRaw === 'true' || enabledRaw === '1';
2344
- }
2345
- else if (status === 'enc_rekey') {
2346
- const rekeyChild = (0, WABinary_1.getBinaryNodeChild)(node, 'enc-rekey') || (0, WABinary_1.getBinaryNodeChild)(node, 'enc_rekey');
2347
- if (rekeyChild?.content && Buffer.isBuffer(rekeyChild.content)) {
2348
- try {
2349
- call.rekeyPayload = (0, Utils_1.decodeE2eRekeyPayload)(rekeyChild.content);
2350
- }
2351
- catch (e) {
2352
- logger.debug({ e }, 'failed to decode enc-rekey payload in standalone call stanza');
2353
- }
2354
- }
2355
- }
2356
- else if (status === 'mute') {
2357
- call.muted = node.attrs['mute-state'] === '1';
2358
- }
2359
- if (callId &&
2360
- (status === 'reject' ||
2361
- status === 'accept' ||
2362
- status === 'timeout' ||
2363
- status === 'terminate' ||
2364
- status === 'reject_do_not_disturb' ||
2365
- status === 'mic_permission_denied' ||
2366
- status === 'camera_permission_denied' ||
2367
- status === 'remote_busy' ||
2368
- status === 'remote_offline')) {
2369
- await callOfferCache.del(callId);
2370
- }
2371
- await normalizeCallEventJids(call, node);
2372
- ev.emit('call', [call]);
2373
- }
2374
- catch (error) {
2375
- logger.error({ error, node: (0, WABinary_1.binaryNodeToString)(node) }, 'error handling standalone call stanza');
2376
- }
2377
- finally {
2378
- await sendMessageAck(node).catch(ackErr => logger.error({ ackErr }, 'failed to ack standalone call'));
2379
- }
2380
- };
2381
- const handleBadAck = async ({ attrs }) => {
2382
- const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id };
2383
- if (attrs.error) {
2384
- if (attrs.error === Utils_1.SERVER_ERROR_CODES.MissingTcToken) {
2385
- logger.warn({ msgId: attrs.id, from: attrs.from }, 'error 463: account restricted or missing tctoken for contact');
2386
- }
2387
- else if (attrs.error === Utils_1.SERVER_ERROR_CODES.SmaxInvalid) {
2388
- logger.warn({ msgId: attrs.id, from: attrs.from }, 'smax-invalid (479): stanza rejected by server — likely stale device session or malformed addressing');
2389
- }
2390
- else {
2391
- logger.warn({ attrs }, 'received error in ack');
2392
- }
2393
- ev.emit('messages.update', [
2394
- {
2395
- key,
2396
- update: {
2397
- status: Types_1.WAMessageStatus.ERROR,
2398
- messageStubParameters: [attrs.error]
2399
- }
2400
- }
2401
- ]);
2402
- }
2403
- };
2404
- const handleChatstate = async (node) => {
2405
- const { from, state } = node.attrs;
2406
- if (!from)
2407
- return;
2408
- const isTyping = state === 'typing';
2409
- ev.emit('presence.update', [
2410
- {
2411
- id: from,
2412
- presences: {
2413
- [from]: isTyping ? 'composing' : 'paused'
2414
- }
2415
- }
2416
- ]);
2417
- };
2418
- const handlePresence = async (node) => {
2419
- const { from, type } = node.attrs;
2420
- if (!from || !type)
2421
- return;
2422
- const presenceMap = {
2423
- available: 'available',
2424
- unavailable: 'unavailable'
2425
- };
2426
- const presence = presenceMap[type] || type;
2427
- ev.emit('presence.update', [
2428
- {
2429
- id: from,
2430
- presences: {
2431
- [from]: presence
2432
- },
2433
- lastSeen: node.attrs.t ? Number(node.attrs.t) * 1000 : undefined
2434
- }
2435
- ]);
2436
- };
2437
- const processNodeWithBuffer = async (node, identifier, exec) => {
2438
- ev.buffer();
2439
- await execTask();
2440
- ev.flush();
2441
- function execTask() {
2442
- return exec(node, false).catch(err => onUnexpectedError(err, identifier));
2443
- }
2444
- };
2445
- const offlineNodeProcessor = (0, offline_node_processor_1.makeOfflineNodeProcessor)(new Map([
2446
- ['message', handleMessage],
2447
- ['call', handleCall],
2448
- ['receipt', handleReceipt],
2449
- ['notification', handleNotification]
2450
- ]), {
2451
- isWsOpen: () => ws.isOpen,
2452
- onUnexpectedError,
2453
- yieldToEventLoop: () => new Promise(resolve => setImmediate(resolve))
2454
- });
2455
- const processNode = async (type, node, identifier, exec) => {
2456
- const isOffline = !!node.attrs.offline;
2457
- if (isOffline) {
2458
- offlineNodeProcessor.enqueue(type, node);
2459
- }
2460
- else {
2461
- await processNodeWithBuffer(node, identifier, exec);
2462
- }
2463
- };
2464
- let latestNodeInMemory = null;
2465
- const nodelogger = node => {
2466
- if (!node)
2467
- return null;
2468
- latestNodeInMemory = node;
2469
- return latestNodeInMemory;
2470
- };
2471
- const setNodeLoggerListener = () => {
2472
- return latestNodeInMemory;
2473
- };
2474
- ws.on('CB:message', async (node) => {
2475
- nodelogger(node);
2476
- await processNode('message', node, 'processing message', handleMessage);
2477
- });
2478
- ws.on('CB:call', async (node) => {
2479
- nodelogger(node);
2480
- await processNode('call', node, 'handling call', handleCall);
2481
- });
2482
- for (const callTag of [
2483
- 'offer',
2484
- 'offer_notice',
2485
- 'terminate',
2486
- 'accept',
2487
- 'reject',
2488
- 'preaccept',
2489
- 'accept_ack',
2490
- 'enc-rekey',
2491
- 'enc_rekey',
2492
- 'peer_state',
2493
- 'group_info',
2494
- 'video_state',
2495
- 'video_state_ack',
2496
- 'flow_control',
2497
- 'transport',
2498
- 'video',
2499
- 'duration',
2500
- 'mute_v2',
2501
- 'lobby',
2502
- 'heartbeat',
2503
- 'relaylatency',
2504
- 'link_query',
2505
- 'waiting_room_request',
2506
- 'group_update'
2507
- ]) {
2508
- ws.on('CB:' + callTag, node => {
2509
- nodelogger(node);
2510
- handleStandaloneCallStanza(node).catch(error => onUnexpectedError(error, 'handling standalone call stanza'));
2511
- });
2512
- }
2513
- ws.on('CB:receipt', async (node) => {
2514
- nodelogger(node);
2515
- await processNode('receipt', node, 'handling receipt', handleReceipt);
2516
- });
2517
- ws.on('CB:notification', async (node) => {
2518
- nodelogger(node);
2519
- await processNode('notification', node, 'handling notification', handleNotification);
2520
- });
2521
- ws.on('CB:status', async (node) => {
2522
- nodelogger(node);
2523
- await handleNewsletterStatus(node).catch(error => onUnexpectedError(error, 'handling newsletter status'));
2524
- });
2525
- ws.on('CB:chatstate', async (node) => {
2526
- nodelogger(node);
2527
- await handleChatstate(node).catch(error => onUnexpectedError(error, 'handling chatstate'));
2528
- });
2529
- ws.on('CB:presence', async (node) => {
2530
- nodelogger(node);
2531
- await handlePresence(node).catch(error => onUnexpectedError(error, 'handling presence'));
2532
- });
2533
- ws.on('CB:ack,class:message', node => {
2534
- nodelogger(node);
2535
- handleBadAck(node).catch(error => onUnexpectedError(error, 'handling bad ack'));
2536
- });
2537
- const linkedParentMap = {};
2538
- ws.on('CB:iq', node => {
2539
- if (node && node.tag === 'iq' && node.attrs.type === 'result') {
2540
- const groups = node.content;
2541
- if (Array.isArray(groups)) {
2542
- for (const group of groups) {
2543
- const groupId = group.attrs.id + '@g.us';
2544
- if (group && Array.isArray(group.content)) {
2545
- for (const item of group.content) {
2546
- if (item.tag === 'linked_parent' && item.attrs && item.attrs.jid) {
2547
- linkedParentMap[groupId] = item.attrs.jid;
2548
- }
2549
- }
2550
- }
2551
- }
2552
- }
2553
- }
2554
- });
2555
- ev.on('call', async ([call]) => {
2556
- if (!call) {
2557
- return;
2558
- }
2559
- nodelogger(call);
2560
- if (call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
2561
- const msg = {
2562
- key: {
2563
- remoteJid: call.chatId,
2564
- id: call.id,
2565
- fromMe: false
2566
- },
2567
- messageTimestamp: (0, Utils_1.unixTimestampSeconds)(call.date)
2568
- };
2569
- if (call.status === 'timeout') {
2570
- if (call.isGroup) {
2571
- msg.messageStubType = call.isVideo
2572
- ? Types_1.WAMessageStubType.CALL_MISSED_GROUP_VIDEO
2573
- : Types_1.WAMessageStubType.CALL_MISSED_GROUP_VOICE;
2574
- }
2575
- else {
2576
- msg.messageStubType = call.isVideo
2577
- ? Types_1.WAMessageStubType.CALL_MISSED_VIDEO
2578
- : Types_1.WAMessageStubType.CALL_MISSED_VOICE;
2579
- }
2580
- }
2581
- else {
2582
- msg.message = { call: { callKey: Buffer.from(call.id) } };
2583
- }
2584
- const protoMsg = index_js_1.proto.WebMessageInfo.fromObject(msg);
2585
- await upsertMessage(protoMsg, call.offline ? 'append' : 'notify');
2586
- }
2587
- });
2588
- let lastTcTokenPruneTs = 0;
2589
- ev.on('connection.update', ({ isOnline, connection }) => {
2590
- if (connection === 'open') {
2591
- isConnected = true;
2592
- }
2593
- if (typeof isOnline !== 'undefined') {
2594
- sendActiveReceipts = isOnline;
2595
- logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
2596
- }
2597
- if (isOnline) {
2598
- const now = Date.now();
2599
- const DAY_MS = 24 * 60 * 60 * 1000;
2600
- if (now - lastTcTokenPruneTs >= DAY_MS) {
2601
- lastTcTokenPruneTs = now;
2602
- void pruneExpiredTcTokens();
2603
- }
2604
- }
2605
- });
2606
- async function pruneExpiredTcTokens() {
2607
- try {
2608
- await tcTokenIndexLoaded;
2609
- const persisted = await (0, tc_token_utils_1.readTcTokenIndex)(authState.keys);
2610
- const allJids = new Set(tcTokenKnownJids);
2611
- for (const jid of persisted)
2612
- allJids.add(jid);
2613
- if (!allJids.size)
2614
- return;
2615
- const jids = [...allJids];
2616
- const allTokens = await authState.keys.get('tctoken', jids);
2617
- const writes = {};
2618
- const survivors = new Set();
2619
- let mutated = 0;
2620
- for (const jid of jids) {
2621
- const entry = allTokens[jid];
2622
- if (!entry) {
2623
- mutated++;
2624
- continue;
2625
- }
2626
- const hasPeerToken = !!entry.token?.length;
2627
- const peerTokenExpired = hasPeerToken && (0, tc_token_utils_1.isTcTokenExpired)(entry.timestamp);
2628
- const hasSenderTs = entry.senderTimestamp !== undefined;
2629
- const senderTsExpired = hasSenderTs && (0, tc_token_utils_1.isTcTokenExpired)(entry.senderTimestamp);
2630
- const keepPeerToken = hasPeerToken && !peerTokenExpired;
2631
- const keepSenderTs = hasSenderTs && !senderTsExpired;
2632
- if (!keepPeerToken && !keepSenderTs) {
2633
- writes[jid] = null;
2634
- mutated++;
2635
- }
2636
- else if (peerTokenExpired && keepSenderTs) {
2637
- writes[jid] = { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp };
2638
- survivors.add(jid);
2639
- mutated++;
2640
- }
2641
- else {
2642
- survivors.add(jid);
2643
- }
2644
- }
2645
- if (mutated === 0)
2646
- return;
2647
- await authState.keys.set({
2648
- tctoken: {
2649
- ...writes,
2650
- [tc_token_utils_1.TC_TOKEN_INDEX_KEY]: {
2651
- token: Buffer.from(JSON.stringify([...survivors]))
2652
- }
2653
- }
2654
- });
2655
- tcTokenKnownJids.clear();
2656
- for (const jid of survivors)
2657
- tcTokenKnownJids.add(jid);
2658
- logger.debug({ mutated, remaining: survivors.size }, 'pruned expired tctokens');
2659
- }
2660
- catch (err) {
2661
- logger.warn({ err: err?.message }, 'failed to prune expired tctokens');
2662
- }
2663
- }
2664
- return {
2665
- ...sock,
2666
- sendMessageAck,
2667
- sendRetryRequest,
2668
- offerCall,
2669
- rejectCall,
2670
- acceptCall,
2671
- terminateCall,
2672
- rekeyCall,
2673
- joinCallLink,
2674
- queryCallLink,
2675
- nodelogger,
2676
- setNodeLoggerListener,
2677
- fetchMessageHistory,
2678
- requestPlaceholderResend,
2679
- requestWaffleNonce,
2680
- requestCompanionCanonicalNonce,
2681
- requestCompanionMetaNonce,
2682
- messageRetryManager
2683
- };
2684
- };
2685
- exports.makeMessagesRecvSocket = makeMessagesRecvSocket;