@badzz88/baileys 8.5.4 → 8.5.6

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