@hansaka02/baileys 7.3.2 → 7.3.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 (210) hide show
  1. package/lib/Defaults/baileys-version.json +2 -2
  2. package/lib/Defaults/connection.js +51 -0
  3. package/lib/Defaults/constants.js +74 -0
  4. package/lib/Defaults/history.js +19 -0
  5. package/lib/Defaults/index.js +36 -142
  6. package/lib/Defaults/media.js +48 -0
  7. package/lib/Defaults/prefix.js +18 -0
  8. package/lib/Signal/Group/group-session-builder.js +10 -42
  9. package/lib/Signal/Group/group_cipher.js +9 -6
  10. package/lib/Signal/Group/index.js +39 -53
  11. package/lib/Signal/Group/keyhelper.js +8 -41
  12. package/lib/Signal/Group/sender-chain-key.js +5 -18
  13. package/lib/Signal/Group/sender-key-distribution-message.js +7 -7
  14. package/lib/Signal/Group/sender-key-message.js +12 -8
  15. package/lib/Signal/Group/sender-key-record.js +7 -16
  16. package/lib/Signal/Group/sender-key-state.js +15 -61
  17. package/lib/Signal/Group/sender-message-key.js +2 -2
  18. package/lib/Signal/libsignal.js +237 -177
  19. package/lib/Signal/lid-mapping.js +128 -71
  20. package/lib/Socket/Client/types.js +2 -2
  21. package/lib/Socket/Client/websocket.js +25 -16
  22. package/lib/Socket/business.js +46 -33
  23. package/lib/Socket/chats.js +286 -170
  24. package/lib/Socket/community.js +215 -77
  25. package/lib/Socket/groups.js +77 -61
  26. package/lib/Socket/index.js +4 -4
  27. package/lib/Socket/messages-recv.js +629 -457
  28. package/lib/Socket/messages-send.js +645 -656
  29. package/lib/Socket/mex.js +61 -0
  30. package/lib/Socket/newsletter.js +166 -245
  31. package/lib/Socket/socket.js +396 -170
  32. package/lib/Store/index.js +27 -11
  33. package/lib/Store/make-cache-manager-store.js +14 -15
  34. package/lib/Store/make-in-memory-store.js +28 -24
  35. package/lib/Types/LabelAssociation.js +2 -2
  36. package/lib/Types/Message.js +6 -6
  37. package/lib/Types/MexUpdates.js +5 -5
  38. package/lib/Types/Newsletter.js +32 -25
  39. package/lib/Types/State.js +4 -4
  40. package/lib/Types/index.js +28 -12
  41. package/lib/Utils/auth-utils.js +212 -375
  42. package/lib/Utils/baileys-event-stream.js +68 -69
  43. package/lib/Utils/browser-utils.js +43 -0
  44. package/lib/Utils/business.js +63 -53
  45. package/lib/Utils/chat-utils.js +241 -106
  46. package/lib/Utils/crypto.js +25 -45
  47. package/lib/Utils/decode-wa-message.js +361 -311
  48. package/lib/Utils/event-buffer.js +97 -42
  49. package/lib/Utils/generics.js +90 -207
  50. package/lib/Utils/history.js +29 -27
  51. package/lib/Utils/index.js +28 -14
  52. package/lib/Utils/link-preview.js +24 -62
  53. package/lib/Utils/logger.js +5 -5
  54. package/lib/Utils/lt-hash.js +29 -23
  55. package/lib/Utils/make-mutex.js +26 -28
  56. package/lib/Utils/message-retry-manager.js +55 -7
  57. package/lib/Utils/messages-media.js +434 -247
  58. package/lib/Utils/messages.js +963 -917
  59. package/lib/Utils/noise-handler.js +60 -20
  60. package/lib/Utils/pre-key-manager.js +126 -0
  61. package/lib/Utils/process-message.js +216 -141
  62. package/lib/Utils/signal.js +75 -37
  63. package/lib/Utils/use-multi-file-auth-state.js +18 -22
  64. package/lib/Utils/validate-connection.js +96 -66
  65. package/lib/WABinary/constants.js +1268 -1268
  66. package/lib/WABinary/decode.js +62 -34
  67. package/lib/WABinary/encode.js +57 -36
  68. package/lib/WABinary/generic-utils.js +4 -4
  69. package/lib/WABinary/index.js +27 -11
  70. package/lib/WABinary/jid-utils.js +58 -11
  71. package/lib/WAM/constants.js +19064 -11563
  72. package/lib/WAM/encode.js +71 -14
  73. package/lib/WAM/index.js +27 -11
  74. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
  75. package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
  76. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
  77. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
  78. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
  79. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
  80. package/lib/WAUSync/Protocols/index.js +27 -11
  81. package/lib/WAUSync/USyncQuery.js +51 -28
  82. package/lib/WAUSync/index.js +27 -11
  83. package/lib/index.js +60 -31
  84. package/package.json +12 -17
  85. package/WAProto/AICommon/AICommon.d.ts +0 -11702
  86. package/WAProto/Adv/Adv.d.ts +0 -643
  87. package/WAProto/BotMetadata/BotMetadata.d.ts +0 -5654
  88. package/WAProto/Cert/Cert.d.ts +0 -613
  89. package/WAProto/ChatLockSettings/ChatLockSettings.d.ts +0 -476
  90. package/WAProto/CompanionReg/CompanionReg.d.ts +0 -1361
  91. package/WAProto/DeviceCapabilities/DeviceCapabilities.d.ts +0 -577
  92. package/WAProto/E2E/E2E.d.ts +0 -41724
  93. package/WAProto/Ephemeral/Ephemeral.d.ts +0 -114
  94. package/WAProto/HistorySync/HistorySync.d.ts +0 -51700
  95. package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.d.ts +0 -229
  96. package/WAProto/MdStorageChatRowOpaqueData/MdStorageChatRowOpaqueData.d.ts +0 -583
  97. package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +0 -42897
  98. package/WAProto/MmsRetry/MmsRetry.d.ts +0 -243
  99. package/WAProto/Protocol/Protocol.d.ts +0 -270
  100. package/WAProto/Reporting/Reporting.d.ts +0 -371
  101. package/WAProto/ServerSync/ServerSync.d.ts +0 -1285
  102. package/WAProto/SignalLocalStorageProtocol/SignalLocalStorageProtocol.d.ts +0 -1868
  103. package/WAProto/SignalWhisperTextProtocol/SignalWhisperTextProtocol.d.ts +0 -767
  104. package/WAProto/StatusAttributions/StatusAttributions.d.ts +0 -1027
  105. package/WAProto/SyncAction/SyncAction.d.ts +0 -11193
  106. package/WAProto/UserPassword/UserPassword.d.ts +0 -363
  107. package/WAProto/VnameCert/VnameCert.d.ts +0 -821
  108. package/WAProto/Wa6/Wa6.d.ts +0 -2128
  109. package/WAProto/Web/Web.d.ts +0 -46383
  110. package/WAProto/index.d.ts +0 -55
  111. package/lib/Defaults/index.d.ts +0 -77
  112. package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
  113. package/lib/Signal/Group/group-session-builder.d.ts +0 -17
  114. package/lib/Signal/Group/group_cipher.d.ts +0 -19
  115. package/lib/Signal/Group/index.d.ts +0 -11
  116. package/lib/Signal/Group/keyhelper.d.ts +0 -16
  117. package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
  118. package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
  119. package/lib/Signal/Group/sender-key-message.d.ts +0 -19
  120. package/lib/Signal/Group/sender-key-name.d.ts +0 -19
  121. package/lib/Signal/Group/sender-key-record.d.ts +0 -32
  122. package/lib/Signal/Group/sender-key-state.d.ts +0 -44
  123. package/lib/Signal/Group/sender-message-key.d.ts +0 -11
  124. package/lib/Signal/libsignal.d.ts +0 -8
  125. package/lib/Signal/lid-mapping.d.ts +0 -28
  126. package/lib/Socket/Client/index.d.ts +0 -2
  127. package/lib/Socket/Client/types.d.ts +0 -16
  128. package/lib/Socket/Client/websocket.d.ts +0 -13
  129. package/lib/Socket/business.d.ts +0 -187
  130. package/lib/Socket/chats.d.ts +0 -97
  131. package/lib/Socket/community.d.ts +0 -129
  132. package/lib/Socket/groups.d.ts +0 -129
  133. package/lib/Socket/index.d.ts +0 -191
  134. package/lib/Socket/messages-recv.d.ts +0 -174
  135. package/lib/Socket/messages-send.d.ts +0 -165
  136. package/lib/Socket/newsletter.d.ts +0 -145
  137. package/lib/Socket/socket.d.ts +0 -45
  138. package/lib/Socket/usync.d.ts +0 -37
  139. package/lib/Socket/usync.js +0 -83
  140. package/lib/Store/index.d.ts +0 -4
  141. package/lib/Store/make-cache-manager-store.d.ts +0 -14
  142. package/lib/Store/make-in-memory-store.d.ts +0 -123
  143. package/lib/Store/make-ordered-dictionary.d.ts +0 -12
  144. package/lib/Store/object-repository.d.ts +0 -10
  145. package/lib/Types/Auth.d.ts +0 -121
  146. package/lib/Types/Bussiness.d.ts +0 -28
  147. package/lib/Types/Call.d.ts +0 -14
  148. package/lib/Types/Chat.d.ts +0 -143
  149. package/lib/Types/Contact.d.ts +0 -23
  150. package/lib/Types/Events.d.ts +0 -226
  151. package/lib/Types/GroupMetadata.d.ts +0 -66
  152. package/lib/Types/Label.d.ts +0 -48
  153. package/lib/Types/LabelAssociation.d.ts +0 -35
  154. package/lib/Types/Message.d.ts +0 -484
  155. package/lib/Types/MexUpdates.d.ts +0 -9
  156. package/lib/Types/Newsletter.d.ts +0 -109
  157. package/lib/Types/Product.d.ts +0 -92
  158. package/lib/Types/Signal.d.ts +0 -98
  159. package/lib/Types/Socket.d.ts +0 -141
  160. package/lib/Types/State.d.ts +0 -41
  161. package/lib/Types/USync.d.ts +0 -26
  162. package/lib/Types/index.d.ts +0 -80
  163. package/lib/Utils/auth-utils.d.ts +0 -21
  164. package/lib/Utils/baileys-event-stream.d.ts +0 -18
  165. package/lib/Utils/business.d.ts +0 -29
  166. package/lib/Utils/chat-utils.d.ts +0 -82
  167. package/lib/Utils/crypto.d.ts +0 -56
  168. package/lib/Utils/decode-wa-message.d.ts +0 -53
  169. package/lib/Utils/event-buffer.d.ts +0 -39
  170. package/lib/Utils/generics.d.ts +0 -117
  171. package/lib/Utils/history.d.ts +0 -23
  172. package/lib/Utils/index.d.ts +0 -20
  173. package/lib/Utils/link-preview.d.ts +0 -23
  174. package/lib/Utils/logger.d.ts +0 -13
  175. package/lib/Utils/lt-hash.d.ts +0 -14
  176. package/lib/Utils/make-mutex.d.ts +0 -9
  177. package/lib/Utils/message-retry-manager.d.ts +0 -88
  178. package/lib/Utils/messages-media.d.ts +0 -135
  179. package/lib/Utils/messages.d.ts +0 -105
  180. package/lib/Utils/noise-handler.d.ts +0 -20
  181. package/lib/Utils/process-message.d.ts +0 -49
  182. package/lib/Utils/signal.d.ts +0 -42
  183. package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
  184. package/lib/Utils/use-mongo-file-auth-state.js +0 -84
  185. package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
  186. package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
  187. package/lib/Utils/use-single-file-auth-state.js +0 -80
  188. package/lib/Utils/validate-connection.d.ts +0 -13
  189. package/lib/WABinary/constants.d.ts +0 -30
  190. package/lib/WABinary/decode.d.ts +0 -9
  191. package/lib/WABinary/encode.d.ts +0 -3
  192. package/lib/WABinary/generic-utils.d.ts +0 -28
  193. package/lib/WABinary/index.d.ts +0 -5
  194. package/lib/WABinary/jid-utils.d.ts +0 -58
  195. package/lib/WABinary/types.d.ts +0 -22
  196. package/lib/WAM/BinaryInfo.d.ts +0 -16
  197. package/lib/WAM/constants.d.ts +0 -47
  198. package/lib/WAM/encode.d.ts +0 -3
  199. package/lib/WAM/index.d.ts +0 -3
  200. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +0 -28
  201. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
  202. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -26
  203. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -14
  204. package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +0 -10
  205. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -14
  206. package/lib/WAUSync/Protocols/index.d.ts +0 -6
  207. package/lib/WAUSync/USyncQuery.d.ts +0 -31
  208. package/lib/WAUSync/USyncUser.d.ts +0 -12
  209. package/lib/WAUSync/index.d.ts +0 -3
  210. package/lib/index.d.ts +0 -13
@@ -1,48 +1,192 @@
1
1
  "use strict"
2
2
 
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod }
5
- }
6
-
7
3
  Object.defineProperty(exports, "__esModule", { value: true })
8
4
 
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 WAProto_1 = require("../../WAProto")
13
- const Defaults_1 = require("../Defaults")
14
- const Types_1 = require("../Types")
15
- const Utils_1 = require("../Utils")
16
- const WABinary_1 = require("../WABinary")
17
- const groups_1 = require("./groups")
18
- const make_mutex_1 = require("../Utils/make-mutex")
19
- const messages_send_1 = require("./messages-send")
5
+ const { default: NodeCache } = require("@cacheable/node-cache")
6
+ const { Boom } = require("@hapi/boom")
7
+ const { randomBytes } = require("crypto")
8
+ const { proto } = require("../../WAProto")
9
+ const {
10
+ KEY_BUNDLE_TYPE,
11
+ MIN_PREKEY_COUNT,
12
+ DEFAULT_CACHE_TTLS
13
+ } = require("../Defaults/constants")
14
+ const {
15
+ XWAPaths,
16
+ XWAPathsMexUpdates,
17
+ MexOperations,
18
+ MexUpdatesOperations,
19
+ WAMessageStubType,
20
+ WAMessageStatus
21
+ } = require("../Types")
22
+ const {
23
+ aesDecryptCTR,
24
+ aesEncryptGCM,
25
+ cleanMessage,
26
+ Curve,
27
+ decodeMediaRetryNode,
28
+ decodeMessageNode,
29
+ decryptMessageNode,
30
+ delay,
31
+ derivePairingCodeKey,
32
+ encodeBigEndian,
33
+ encodeSignedDeviceIdentity,
34
+ extractAddressingContext,
35
+ getCallStatusFromNode,
36
+ getHistoryMsg,
37
+ getNextPreKeys,
38
+ getStatusFromReceiptType,
39
+ hkdf,
40
+ NO_MESSAGE_FOUND_ERROR_TEXT,
41
+ MISSING_KEYS_ERROR_TEXT,
42
+ NACK_REASONS,
43
+ unixTimestampSeconds,
44
+ xmppPreKey,
45
+ xmppSignedPreKey,
46
+ generateMessageID
47
+ } = require("../Utils")
48
+ const {
49
+ areJidsSameUser,
50
+ binaryNodeToString,
51
+ getAllBinaryNodeChildren,
52
+ getBinaryNodeChild,
53
+ getBinaryNodeChildBuffer,
54
+ getBinaryNodeChildren,
55
+ getBinaryNodeChildString,
56
+ isJidGroup,
57
+ isJidNewsletter,
58
+ isJidStatusBroadcast,
59
+ isLidUser,
60
+ isPnUser,
61
+ jidDecode,
62
+ jidNormalizedUser,
63
+ S_WHATSAPP_NET
64
+ } = require("../WABinary")
65
+ const { extractGroupMetadata } = require("./groups")
66
+ const { makeMutex } = require("../Utils/make-mutex")
67
+ const { makeMessagesSocket } = require("./messages-send")
20
68
 
21
69
  const makeMessagesRecvSocket = (config) => {
22
- const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config
23
- const suki = messages_send_1.makeMessagesSocket(config)
24
- const { ev, authState, ws, processingMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, groupMetadata, getUSyncDevices, createParticipantNodes, messageRetryManager, sendPeerDataOperationMessage } = suki
70
+ const {
71
+ logger,
72
+ retryRequestDelayMs,
73
+ maxMsgRetryCount,
74
+ getMessage,
75
+ shouldIgnoreJid,
76
+ enableAutoSessionRecreation
77
+ } = config
78
+
79
+ const suki = makeMessagesSocket(config)
80
+
81
+ const {
82
+ ev,
83
+ authState,
84
+ ws,
85
+ messageMutex,
86
+ notificationMutex,
87
+ receiptMutex,
88
+ signalRepository,
89
+ query,
90
+ upsertMessage,
91
+ resyncAppState,
92
+ onUnexpectedError,
93
+ assertSessions,
94
+ sendNode,
95
+ relayMessage,
96
+ sendReceipt,
97
+ uploadPreKeys,
98
+ groupMetadata,
99
+ getUSyncDevices,
100
+ createParticipantNodes,
101
+ messageRetryManager,
102
+ sendPeerDataOperationMessage
103
+ } = suki
25
104
 
26
105
  /** this mutex ensures that each retryRequest will wait for the previous one to finish */
27
- const retryMutex = make_mutex_1.makeMutex()
106
+ const retryMutex = makeMutex()
107
+
108
+ const msgRetryCache = config.msgRetryCounterCache || new NodeCache({
109
+ stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY,
110
+ useClones: false
111
+ })
28
112
 
29
- const msgRetryCache = config.msgRetryCounterCache || new node_cache_1.default({
30
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
113
+ const callOfferCache = config.callOfferCache || new NodeCache({
114
+ stdTTL: DEFAULT_CACHE_TTLS.CALL_OFFER,
31
115
  useClones: false
32
116
  })
33
117
 
34
- const callOfferCache = config.callOfferCache || new node_cache_1.default({
35
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.CALL_OFFER,
118
+ const placeholderResendCache = config.placeholderResendCache || new NodeCache({
119
+ stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY,
36
120
  useClones: false
37
121
  })
38
122
 
39
- const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
40
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
123
+ // Debounce identity-change session refreshes per JID to avoid bursts
124
+ const identityAssertDebounce = new NodeCache({
125
+ stdTTL: 5,
41
126
  useClones: false
42
127
  })
43
128
 
44
129
  let sendActiveReceipts = false
45
130
 
131
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
132
+ if (!authState.creds.me?.id) {
133
+ throw new Boom('Not authenticated')
134
+ }
135
+
136
+ const pdoMessage = {
137
+ historySyncOnDemandRequest: {
138
+ chatJid: oldestMsgKey.remoteJid,
139
+ oldestMsgFromMe: oldestMsgKey.fromMe,
140
+ oldestMsgId: oldestMsgKey.id,
141
+ oldestMsgTimestampMs: oldestMsgTimestamp,
142
+ onDemandMsgCount: count
143
+ },
144
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
145
+ }
146
+
147
+ return sendPeerDataOperationMessage(pdoMessage)
148
+ }
149
+
150
+ const requestPlaceholderResend = async (messageKey) => {
151
+ if (!authState.creds.me?.id) {
152
+ throw new Boom('Not authenticated')
153
+ }
154
+
155
+ if (await placeholderResendCache.get(messageKey?.id)) {
156
+ logger.debug({ messageKey }, 'already requested resend')
157
+ return
158
+ }
159
+
160
+ else {
161
+ await placeholderResendCache.set(messageKey?.id, true)
162
+ }
163
+
164
+ await delay(5000)
165
+
166
+ if (!(await placeholderResendCache.get(messageKey?.id))) {
167
+ logger.debug({ messageKey }, 'message received while resend requested')
168
+ return 'RESOLVED'
169
+ }
170
+
171
+ const pdoMessage = {
172
+ placeholderMessageResendRequest: [
173
+ {
174
+ messageKey
175
+ }
176
+ ],
177
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
178
+ }
179
+
180
+ setTimeout(async () => {
181
+ if (await placeholderResendCache.get(messageKey?.id)) {
182
+ logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline')
183
+ await placeholderResendCache.del(messageKey?.id)
184
+ }
185
+ }, 15000)
186
+
187
+ return sendPeerDataOperationMessage(pdoMessage)
188
+ }
189
+
46
190
  const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
47
191
  const stanza = {
48
192
  tag: 'ack',
@@ -65,11 +209,11 @@ const makeMessagesRecvSocket = (config) => {
65
209
  stanza.attrs.recipient = attrs.recipient
66
210
  }
67
211
 
68
- if (!!attrs.type && (tag !== 'message' || WABinary_1.getBinaryNodeChild({ tag, attrs, content }, 'unavailable') || errorCode !== 0)) {
212
+ if (!!attrs.type && (tag !== 'message' || getBinaryNodeChild({ tag, attrs, content }, 'unavailable') || errorCode !== 0)) {
69
213
  stanza.attrs.type = attrs.type
70
214
  }
71
215
 
72
- if (tag === 'message' && WABinary_1.getBinaryNodeChild({ tag, attrs, content }, 'unavailable')) {
216
+ if (tag === 'message' && getBinaryNodeChild({ tag, attrs, content }, 'unavailable')) {
73
217
  stanza.attrs.from = authState.creds.me.id
74
218
  }
75
219
 
@@ -78,7 +222,7 @@ const makeMessagesRecvSocket = (config) => {
78
222
  }
79
223
 
80
224
  const offerCall = async (toJid, isVideo = false) => {
81
- const callId = crypto_1.randomBytes(16).toString('hex').toUpperCase().substring(0, 64)
225
+ const callId = randomBytes(16).toString('hex').toUpperCase().substring(0, 64)
82
226
  const offerContent = []
83
227
  offerContent.push({ tag: 'audio', attrs: { enc: 'opus', rate: '16000' }, content: undefined })
84
228
  offerContent.push({ tag: 'audio', attrs: { enc: 'opus', rate: '8000' }, content: undefined })
@@ -94,8 +238,8 @@ const makeMessagesRecvSocket = (config) => {
94
238
  offerContent.push({ tag: 'capability', attrs: { ver: '1' }, content: new Uint8Array([1, 4, 255, 131, 207, 4]) })
95
239
  offerContent.push({ tag: 'encopt', attrs: { keygen: '2' }, content: undefined })
96
240
 
97
- const encKey = crypto_1.randomBytes(32)
98
- const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) => WABinary_1.jidEncode(user, 's.whatsapp.net', device))
241
+ const encKey = randomBytes(32)
242
+ const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) => jidEncode(user, 's.whatsapp.net', device))
99
243
  await assertSessions(devices, true)
100
244
 
101
245
  const { nodes: destinations, shouldIncludeDeviceIdentity } = await createParticipantNodes(devices, {
@@ -109,14 +253,14 @@ const makeMessagesRecvSocket = (config) => {
109
253
  offerContent.push({
110
254
  tag: 'device-identity',
111
255
  attrs: {},
112
- content: Utils_1.encodeSignedDeviceIdentity(authState.creds.account, true)
256
+ content: encodeSignedDeviceIdentity(authState.creds.account, true)
113
257
  })
114
258
  }
115
259
 
116
260
  const stanza = ({
117
261
  tag: 'call',
118
262
  attrs: {
119
- id: Utils_1.generateMessageID(),
263
+ id: generateMessageID(),
120
264
  to: toJid,
121
265
  },
122
266
  content: [{
@@ -159,7 +303,7 @@ const makeMessagesRecvSocket = (config) => {
159
303
  }
160
304
 
161
305
  const sendRetryRequest = async (node, forceIncludeKeys = false) => {
162
- const { fullMessage } = Utils_1.decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '')
306
+ const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '')
163
307
  const { key: msgKey } = fullMessage
164
308
  const msgId = msgKey.id
165
309
 
@@ -176,20 +320,25 @@ const makeMessagesRecvSocket = (config) => {
176
320
 
177
321
  // Use the new retry count for the rest of the logic
178
322
  const key = `${msgId}:${msgKey?.participant}`
179
- msgRetryCache.set(key, retryCount)
323
+ await msgRetryCache.set(key, retryCount)
180
324
  }
325
+
181
326
  else {
182
327
  // Fallback to old system
183
328
  const key = `${msgId}:${msgKey?.participant}`
329
+
184
330
  let retryCount = (await msgRetryCache.get(key)) || 0
185
331
 
186
332
  if (retryCount >= maxMsgRetryCount) {
187
333
  logger.debug({ retryCount, msgId }, 'reached retry limit, clearing')
188
- msgRetryCache.del(key)
334
+
335
+ await msgRetryCache.del(key)
336
+
189
337
  return
190
338
  }
191
339
 
192
340
  retryCount += 1
341
+
193
342
  await msgRetryCache.set(key, retryCount)
194
343
  }
195
344
 
@@ -213,12 +362,15 @@ const makeMessagesRecvSocket = (config) => {
213
362
  recreateReason = result.reason
214
363
 
215
364
  if (shouldRecreateSession) {
216
- logger.info({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry')
365
+ logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry')
366
+
217
367
  // Delete existing session to force recreation
218
368
  await authState.keys.set({ session: { [sessionId]: null } })
369
+
219
370
  forceIncludeKeys = true
220
371
  }
221
372
  }
373
+
222
374
  catch (error) {
223
375
  logger.warn({ error, fromJid }, 'failed to check session recreation')
224
376
  }
@@ -230,22 +382,27 @@ const makeMessagesRecvSocket = (config) => {
230
382
  // Schedule phone request with delay (like whatsmeow)
231
383
  messageRetryManager.schedulePhoneRequest(msgId, async () => {
232
384
  try {
233
- const msgId = await requestPlaceholderResend(msgKey)
234
- logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId} (scheduled)`)
385
+ const requestId = await requestPlaceholderResend(msgKey)
386
+
387
+ logger.debug(`sendRetryRequest: requested placeholder resend (${requestId}) for message ${msgId} (scheduled)`)
235
388
  }
389
+
236
390
  catch (error) {
237
- logger.warn({ error, msgId }, 'failed to send scheduled phone request');
391
+ logger.warn({ error, msgId }, 'failed to send scheduled phone request')
238
392
  }
239
393
  })
240
394
  }
395
+
241
396
  else {
242
397
  // Fallback to immediate request
243
398
  const msgId = await requestPlaceholderResend(msgKey)
399
+
244
400
  logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`)
245
401
  }
246
402
  }
247
403
 
248
- const deviceIdentity = Utils_1.encodeSignedDeviceIdentity(account, true)
404
+ const deviceIdentity = encodeSignedDeviceIdentity(account, true)
405
+
249
406
  await authState.keys.transaction(async () => {
250
407
  const receipt = {
251
408
  tag: 'receipt',
@@ -261,13 +418,15 @@ const makeMessagesRecvSocket = (config) => {
261
418
  count: retryCount.toString(),
262
419
  id: node.attrs.id,
263
420
  t: node.attrs.t,
264
- v: '1'
421
+ v: '1',
422
+ // ADD ERROR FIELD
423
+ error: '0'
265
424
  }
266
425
  },
267
426
  {
268
427
  tag: 'registration',
269
428
  attrs: {},
270
- content: Utils_1.encodeBigEndian(authState.creds.registrationId)
429
+ content: encodeBigEndian(authState.creds.registrationId)
271
430
  }
272
431
  ]
273
432
  }
@@ -281,7 +440,7 @@ const makeMessagesRecvSocket = (config) => {
281
440
  }
282
441
 
283
442
  if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
284
- const { update, preKeys } = await Utils_1.getNextPreKeys(authState, 1)
443
+ const { update, preKeys } = await getNextPreKeys(authState, 1)
285
444
  const [keyId] = Object.keys(preKeys)
286
445
  const key = preKeys[+keyId]
287
446
  const content = receipt.content
@@ -290,26 +449,31 @@ const makeMessagesRecvSocket = (config) => {
290
449
  tag: 'keys',
291
450
  attrs: {},
292
451
  content: [
293
- { tag: 'type', attrs: {}, content: Buffer.from(Defaults_1.KEY_BUNDLE_TYPE) },
452
+ { tag: 'type', attrs: {}, content: Buffer.from(KEY_BUNDLE_TYPE) },
294
453
  { tag: 'identity', attrs: {}, content: identityKey.public },
295
- Utils_1.xmppPreKey(key, +keyId),
296
- Utils_1.xmppSignedPreKey(signedPreKey),
454
+ xmppPreKey(key, +keyId),
455
+ xmppSignedPreKey(signedPreKey),
297
456
  { tag: 'device-identity', attrs: {}, content: deviceIdentity }
298
457
  ]
299
- });
458
+ })
459
+
300
460
  ev.emit('creds.update', update)
301
461
  }
462
+
302
463
  await sendNode(receipt)
464
+
303
465
  logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt')
304
466
  }, authState?.creds?.me?.id || 'sendRetryRequest')
305
467
  }
306
468
 
307
469
  const handleEncryptNotification = async (node) => {
308
470
  const from = node.attrs.from
309
- if (from === WABinary_1.S_WHATSAPP_NET) {
310
- const countChild = WABinary_1.getBinaryNodeChild(node, 'count')
471
+
472
+ if (from === S_WHATSAPP_NET) {
473
+ const countChild = getBinaryNodeChild(node, 'count')
311
474
  const count = +countChild.attrs.value
312
- const shouldUploadMorePreKeys = count < Defaults_1.MIN_PREKEY_COUNT
475
+ const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT
476
+
313
477
  logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count')
314
478
 
315
479
  if (shouldUploadMorePreKeys) {
@@ -318,11 +482,25 @@ const makeMessagesRecvSocket = (config) => {
318
482
  }
319
483
 
320
484
  else {
321
- const identityNode = WABinary_1.getBinaryNodeChild(node, 'identity')
485
+ const identityNode = getBinaryNodeChild(node, 'identity')
486
+
322
487
  if (identityNode) {
323
488
  logger.info({ jid: from }, 'identity changed')
324
- // not handling right now
325
- // signal will override new identity anyway
489
+
490
+ if (identityAssertDebounce.get(from)) {
491
+ logger.debug({ jid: from }, 'skipping identity assert (debounced)')
492
+ return
493
+ }
494
+
495
+ identityAssertDebounce.set(from, true)
496
+
497
+ try {
498
+ await assertSessions([from], true)
499
+ }
500
+
501
+ catch (error) {
502
+ logger.warn({ error, jid: from }, 'failed to assert sessions after identity change')
503
+ }
326
504
  }
327
505
 
328
506
  else {
@@ -331,162 +509,145 @@ const makeMessagesRecvSocket = (config) => {
331
509
  }
332
510
  }
333
511
 
334
- const handleGroupNotification = (participant, child, msg, mode) => {
335
- let participantJid = mode === 'lid' ? WABinary_1.getBinaryNodeChild(child, 'participant')?.attrs?.phone_number : WABinary_1.getBinaryNodeChild(child, 'participant')?.attrs?.jid || participant
336
-
337
- // TODO: Add participant LID
338
- switch (child.tag) {
512
+ const handleGroupNotification = (fullNode, child, msg) => {
513
+ // TODO: Support PN/LID (Here is only LID now)
514
+ const actingParticipantLid = fullNode.attrs.participant
515
+ const actingParticipantPn = fullNode.attrs.participant_pn
516
+ const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid
517
+ const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn
518
+
519
+ switch (child?.tag) {
339
520
  case 'create':
340
- const metadata = groups_1.extractGroupMetadata(child)
341
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE
521
+ const metadata = extractGroupMetadata(child)
522
+ msg.messageStubType = WAMessageStubType.GROUP_CREATE
342
523
  msg.messageStubParameters = [metadata.subject]
343
- msg.key = { participant: metadata.owner }
344
- ev.emit('chats.upsert', [{
524
+ msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn }
525
+
526
+ ev.emit('chats.upsert', [
527
+ {
345
528
  id: metadata.id,
346
529
  name: metadata.subject,
347
- conversationTimestamp: metadata.creation,
348
- }])
349
- ev.emit('groups.upsert', [{
530
+ conversationTimestamp: metadata.creation
531
+ }
532
+ ])
533
+
534
+ ev.emit('groups.upsert', [
535
+ {
350
536
  ...metadata,
351
- author: participant
352
- }])
353
- break
354
- case 'delete':
355
- msg.messageStubType = Types_1.WAMessageStubType.COMMUNITY_PARENT_GROUP_DELETED
356
- msg.messageStubParameters = [participantJid, 'delete']
537
+ author: actingParticipantLid,
538
+ authorPn: actingParticipantPn
539
+ }
540
+ ])
357
541
  break
358
542
  case 'ephemeral':
359
543
  case 'not_ephemeral':
360
544
  msg.message = {
361
545
  protocolMessage: {
362
- type: WAProto_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
546
+ type: proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
363
547
  ephemeralExpiration: +(child.attrs.expiration || 0)
364
548
  }
365
549
  }
366
550
  break
367
551
  case 'modify':
368
- const oldNumber = mode === 'lid' ? WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.phone_number) : WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
552
+ const oldNumber = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
369
553
  msg.messageStubParameters = oldNumber || []
370
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER
554
+ msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER
371
555
  break
372
556
  case 'promote':
373
557
  case 'demote':
374
558
  case 'remove':
375
559
  case 'add':
376
560
  case 'leave':
377
- let stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`
378
- if (child.attrs?.reason === 'linked_group_join') {
379
- stubType = GROUP_PARTICIPANT_LINKED_GROUP_JOIN
380
- }
381
- msg.messageStubType = Types_1.WAMessageStubType[stubType]
382
- const participants = mode === 'lid' ? WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.phone_number) : WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
561
+ const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`
562
+ msg.messageStubType = WAMessageStubType[stubType]
563
+ const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
564
+ // TODO: Store LID MAPPINGS
565
+ return {
566
+ id: attrs.jid,
567
+ phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
568
+ lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
569
+ admin: (attrs.type || null)
570
+ }
571
+ })
572
+
383
573
  if (participants.length === 1 &&
384
574
  // if recv. "remove" message and sender removed themselves
385
575
  // mark as left
386
- WABinary_1.areJidsSameUser(participants[0], participant) &&
576
+ (areJidsSameUser(participants[0].id, actingParticipantLid) ||
577
+ areJidsSameUser(participants[0].id, actingParticipantPn)) &&
387
578
  child.tag === 'remove') {
388
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE
579
+ msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE
389
580
  }
390
- msg.messageStubParameters = participants
581
+
582
+ msg.messageStubParameters = participants.map(a => JSON.stringify(a))
391
583
  break
392
584
  case 'subject':
393
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_SUBJECT
394
- msg.messageStubParameters = [participantJid, child.attrs.subject]
585
+ msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT
586
+ msg.messageStubParameters = [child.attrs.subject]
395
587
  break
396
588
  case 'description':
397
- const description = WABinary_1.getBinaryNodeChild(child, 'body')?.content?.toString()
398
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_DESCRIPTION
589
+ const description = getBinaryNodeChild(child, 'body')?.content?.toString()
590
+ msg.messageStubType = WAMessageStubType.GROUP_CHANGE_DESCRIPTION
399
591
  msg.messageStubParameters = description ? [description] : undefined
400
592
  break
401
593
  case 'announcement':
402
594
  case 'not_announcement':
403
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ANNOUNCE
404
- msg.messageStubParameters = [(child.tag === 'announcement') ? 'on' : 'off']
595
+ msg.messageStubType = WAMessageStubType.GROUP_CHANGE_ANNOUNCE
596
+ msg.messageStubParameters = [child.tag === 'announcement' ? 'on' : 'off']
405
597
  break
406
598
  case 'locked':
407
599
  case 'unlocked':
408
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_RESTRICT
409
- msg.messageStubParameters = [(child.tag === 'locked') ? 'on' : 'off']
600
+ msg.messageStubType = WAMessageStubType.GROUP_CHANGE_RESTRICT
601
+ msg.messageStubParameters = [child.tag === 'locked' ? 'on' : 'off']
410
602
  break
411
603
  case 'invite':
412
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_INVITE_LINK
604
+ msg.messageStubType = WAMessageStubType.GROUP_CHANGE_INVITE_LINK
413
605
  msg.messageStubParameters = [child.attrs.code]
414
606
  break
415
607
  case 'member_add_mode':
416
- const addMode = child.content
608
+ const addMode = child.content;
417
609
  if (addMode) {
418
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBER_ADD_MODE
610
+ msg.messageStubType = WAMessageStubType.GROUP_MEMBER_ADD_MODE
419
611
  msg.messageStubParameters = [addMode.toString()]
420
612
  }
421
613
  break
422
614
  case 'membership_approval_mode':
423
- const approvalMode = WABinary_1.getBinaryNodeChild(child, 'group_join')
615
+ const approvalMode = getBinaryNodeChild(child, 'group_join')
424
616
  if (approvalMode) {
425
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE
617
+ msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE
426
618
  msg.messageStubParameters = [approvalMode.attrs.state]
427
619
  }
428
620
  break
429
621
  case 'created_membership_requests':
430
- participantJid = mode === 'lid' ? WABinary_1.getBinaryNodeChild(child, 'requested_user')?.attrs?.phone_number : WABinary_1.getBinaryNodeChild(child, 'requested_user')?.attrs?.jid || participant
431
-
432
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
433
- msg.messageStubParameters = [participantJid, 'created', child.attrs.request_method]
622
+ msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
623
+ msg.messageStubParameters = [
624
+ JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
625
+ 'created',
626
+ child.attrs.request_method
627
+ ]
434
628
  break
435
629
  case 'revoked_membership_requests':
436
- participantJid = mode === 'lid' ? WABinary_1.getBinaryNodeChild(child, 'requested_user')?.attrs?.phone_number : WABinary_1.getBinaryNodeChild(child, 'requested_user')?.attrs?.jid || participant
437
-
438
- const isDenied = WABinary_1.areJidsSameUser(participantJid, participant)
439
- msg.messageStubType = Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
440
- msg.messageStubParameters = [participantJid, isDenied ? 'revoked' : 'rejected']
441
- break
442
- case 'link':
443
- case 'unlink':
444
- const type = child.attrs?.unlink_type || child.attrs?.link_type
445
- const stubMap = {
446
- parent_group: Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_PARENT_GROUP`],
447
- sibling_group: Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_SIBLING_GROUP`],
448
- sub_group: Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_SUB_GROUP`]
449
- }
450
- const groups = WABinary_1.getBinaryNodeChildren(child, 'group')
451
- .map(g => g.attrs?.jid || g.attrs?.subject || '')
452
- .filter(x => x)
453
- msg.messageStubType = stubMap?.[type] || Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_PARENT_GROUP`]
454
- msg.messageStubParameters = [participantJid, child.tag, groups]
455
- break
456
- case 'linked_group_promote':
457
- case 'linked_group_demote':
458
- const stubtype = `COMMUNITY_PARTICIPANT_${child.tag.split('_')[2].toUpperCase()}`
459
- const participantS = mode === 'lid' ? WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.phone_number) : WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
460
- msg.messageStubType = Types_1.WAMessageStubType[stubtype]
461
- msg.messageStubParameters = participantS
462
- break
463
- case 'created_sub_group_suggestion':
464
- msg.messageStubType = Types_1.WAMessageStubType.SUGGESTED_SUBGROUP_ANNOUNCE
465
- msg.messageStubParameters = [participantJid, 'add']
466
- break
467
- case 'revoked_sub_group_suggestions':
468
- const res = WABinary_1.getBinaryNodeChildren(child, 'sub_group_suggestions')
469
- const reason = res.attrs?.reason
470
- if (reason === 'approved') msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE
471
- else msg.messageStubType = Types_1.WAMessageStubType.GENERIC_NOTIFICATION
472
- msg.messageStubParameters = [participantJid, reason]
473
- break
474
- default:
475
- logger.warn(child.tag, 'Unhandled group node')
630
+ const isDenied = areJidsSameUser(affectedParticipantLid, actingParticipantLid)
631
+ // TODO: LIDMAPPING SUPPORT
632
+ msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
633
+ msg.messageStubParameters = [
634
+ JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
635
+ isDenied ? 'revoked' : 'rejected'
636
+ ]
476
637
  break
477
638
  }
478
639
  }
479
640
 
480
641
  const handleNewsletterNotification = (id, node) => {
481
- const messages = WABinary_1.getBinaryNodeChild(node, 'messages')
482
- const message = WABinary_1.getBinaryNodeChild(node, 'message')
642
+ const messages = getBinaryNodeChild(node, 'messages')
643
+ const message = getBinaryNodeChild(node, 'message')
483
644
  const serverId = node.attrs.server_id
484
645
 
485
- const reactionsList = WABinary_1.getBinaryNodeChild(node, 'reactions')
486
- const viewsList = WABinary_1.getBinaryNodeChild(node, 'views_count')
646
+ const reactionsList = getBinaryNodeChild(node, 'reactions')
647
+ const viewsList = getBinaryNodeChild(node, 'views_count')
487
648
 
488
649
  if (reactionsList) {
489
- const reactions = WABinary_1.getBinaryNodeChild(reactionsList, 'reaction')
650
+ const reactions = getBinaryNodeChild(reactionsList, 'reaction')
490
651
 
491
652
  if (reactions.length === 0) {
492
653
  ev.emit('newsletter.reaction', {
@@ -528,23 +689,23 @@ const makeMessagesRecvSocket = (config) => {
528
689
  let contentPath
529
690
  let action
530
691
 
531
- if (operation === Types_1.MexOperations.UPDATE) {
532
- contentPath = content.data[Types_1.XWAPaths.METADATA_UPDATE]
692
+ if (operation === MexOperations.UPDATE) {
693
+ contentPath = content.data[XWAPaths.METADATA_UPDATE]
533
694
 
534
695
  ev.emit('newsletter-settings.update', {
535
696
  id,
536
697
  update: contentPath.thread_metadata.settings
537
698
  })
538
- } else if (operation === Types_1.MexUpdatesOperations.GROUP_MEMBER_LINK) {
539
- contentPath = content.data[Types_1.XWAPathsMexUpdates.GROUP_SHARING_CHANGE]
699
+ } else if (operation === MexUpdatesOperations.GROUP_MEMBER_LINK) {
700
+ contentPath = content.data[XWAPathsMexUpdates.GROUP_SHARING_CHANGE]
540
701
 
541
702
  ev.emit('groups.update', [{
542
703
  id,
543
704
  author: contentPath.updated_by.id,
544
705
  member_link_mode: contentPath.properties.member_link_mode
545
706
  }])
546
- } else if (operation === Types_1.MexUpdatesOperations.GROUP_LIMIT_SHARING) {
547
- contentPath = content.data[Types_1.XWAPathsMexUpdates.GROUP_SHARING_CHANGE]
707
+ } else if (operation === MexUpdatesOperations.GROUP_LIMIT_SHARING) {
708
+ contentPath = content.data[XWAPathsMexUpdates.GROUP_SHARING_CHANGE]
548
709
 
549
710
  ev.emit('limit-sharing.update', {
550
711
  id,
@@ -553,8 +714,8 @@ const makeMessagesRecvSocket = (config) => {
553
714
  trigger: contentPath.properties.limit_sharing.limit_sharing_trigger,
554
715
  update_time: contentPath.update_time
555
716
  })
556
- } else if (operation === Types_1.MexUpdatesOperations.OWNER_COMMUNITY) {
557
- contentPath = content.data[Types_1.XWAPathsMexUpdates.COMMUNITY_OWNER_CHANGE]
717
+ } else if (operation === MexUpdatesOperations.OWNER_COMMUNITY) {
718
+ contentPath = content.data[XWAPathsMexUpdates.COMMUNITY_OWNER_CHANGE]
558
719
 
559
720
  ev.emit('community-owner.update', {
560
721
  id,
@@ -565,12 +726,12 @@ const makeMessagesRecvSocket = (config) => {
565
726
  })
566
727
  } else {
567
728
 
568
- if (operation === Types_1.MexOperations.PROMOTE) {
729
+ if (operation === MexOperations.PROMOTE) {
569
730
  action = 'promote'
570
- contentPath = content.data[Types_1.XWAPaths.PROMOTE]
731
+ contentPath = content.data[XWAPaths.PROMOTE]
571
732
  } else {
572
733
  action = 'demote'
573
- contentPath = content.data[Types_1.XWAPaths.DEMOTE]
734
+ contentPath = content.data[XWAPaths.DEMOTE]
574
735
  }
575
736
 
576
737
  ev.emit('newsletter-participants.update', {
@@ -585,27 +746,14 @@ const makeMessagesRecvSocket = (config) => {
585
746
 
586
747
  const processNotification = async (node) => {
587
748
  const result = {}
588
- const [child] = WABinary_1.getAllBinaryNodeChildren(node)
749
+ const [child] = getAllBinaryNodeChildren(node)
589
750
  const nodeType = node.attrs.type
590
- const from = WABinary_1.jidNormalizedUser(node.attrs.from)
751
+ const from = jidNormalizedUser(node.attrs.from)
591
752
 
592
753
  switch (nodeType) {
593
- case 'privacy_token':
594
- const tokenList = WABinary_1.getBinaryNodeChildren(child, 'token')
595
- for (const { attrs, content } of tokenList) {
596
- const jid = attrs.jid
597
- ev.emit('chats.update', [
598
- {
599
- id: jid,
600
- tcToken: content
601
- }
602
- ])
603
- logger.debug({ jid }, 'got privacy token update')
604
- }
605
- break
606
754
  case 'w:gp2':
607
- const mode = node.attrs.addressing_mode
608
- handleGroupNotification(mode === 'lid' ? node.attrs.participant_pn : node.attrs.participant, child, result, mode)
755
+ // TODO: HANDLE PARTICIPANT_PN
756
+ handleGroupNotification(node, child, result)
609
757
  break
610
758
  case 'newsletter':
611
759
  handleNewsletterNotification(node.attrs.from, child)
@@ -614,45 +762,52 @@ const makeMessagesRecvSocket = (config) => {
614
762
  handleMexNotification(node.attrs.from, child, result)
615
763
  break
616
764
  case 'mediaretry':
617
- const event = Utils_1.decodeMediaRetryNode(node)
765
+ const event = decodeMediaRetryNode(node)
618
766
  ev.emit('messages.media-update', [event])
619
767
  break
620
768
  case 'encrypt':
621
769
  await handleEncryptNotification(node)
622
770
  break
623
771
  case 'devices':
624
- const devices = WABinary_1.getBinaryNodeChildren(child, 'device')
625
- if (WABinary_1.areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
626
- WABinary_1.areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
772
+ const devices = getBinaryNodeChildren(child, 'device')
773
+ if (areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
774
+ areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
627
775
  const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }))
628
776
  logger.info({ deviceData }, 'my own devices changed')
629
777
  }
630
778
  //TODO: drop a new event, add hashes
631
779
  break
632
780
  case 'server_sync':
633
- const update = WABinary_1.getBinaryNodeChild(node, 'collection')
781
+ const update = getBinaryNodeChild(node, 'collection')
634
782
  if (update) {
635
783
  const name = update.attrs.name
636
784
  await resyncAppState([name], false)
637
785
  }
638
786
  break
639
787
  case 'picture':
640
- const setPicture = WABinary_1.getBinaryNodeChild(node, 'set')
641
- const delPicture = WABinary_1.getBinaryNodeChild(node, 'delete')
642
- ev.emit('contacts.update', [{
643
- id: WABinary_1.jidNormalizedUser(node?.attrs?.from) || (setPicture || delPicture)?.attrs?.hash || '',
788
+ const setPicture = getBinaryNodeChild(node, 'set')
789
+ const delPicture = getBinaryNodeChild(node, 'delete')
790
+
791
+ ev.emit('contacts.update', [
792
+ {
793
+ id: jidNormalizedUser(node?.attrs?.from) || (setPicture || delPicture)?.attrs?.hash || '',
644
794
  imgUrl: setPicture ? 'changed' : 'removed'
645
- }])
646
- if (WABinary_1.isJidGroup(from)) {
795
+ }
796
+ ])
797
+
798
+ if (isJidGroup(from)) {
647
799
  const node = setPicture || delPicture
648
- result.messageStubType = Types_1.WAMessageStubType.GROUP_CHANGE_ICON
800
+
801
+ result.messageStubType = WAMessageStubType.GROUP_CHANGE_ICON
802
+
649
803
  if (setPicture) {
650
804
  result.messageStubParameters = [setPicture.attrs.id]
651
805
  }
652
- result.participant = node?.attrs?.author
806
+
807
+ result.participant = node?.attrs.author
653
808
  result.key = {
654
- ...result.key || {},
655
- participant: setPicture?.attrs?.author
809
+ ...(result.key || {}),
810
+ participant: setPicture?.attrs.author
656
811
  }
657
812
  }
658
813
  break
@@ -666,48 +821,51 @@ const makeMessagesRecvSocket = (config) => {
666
821
  ...authState.creds.accountSettings,
667
822
  defaultDisappearingMode: {
668
823
  ephemeralExpiration: newDuration,
669
- ephemeralSettingTimestamp: timestamp,
670
- },
824
+ ephemeralSettingTimestamp: timestamp
825
+ }
671
826
  }
672
827
  })
673
828
  }
829
+
674
830
  else if (child.tag === 'blocklist') {
675
- const blocklists = WABinary_1.getBinaryNodeChildren(child, 'item')
831
+ const blocklists = getBinaryNodeChildren(child, 'item')
676
832
  for (const { attrs } of blocklists) {
677
833
  const blocklist = [attrs.jid]
678
- const type = (attrs.action === 'block') ? 'add' : 'remove'
834
+ const type = attrs.action === 'block' ? 'add' : 'remove'
679
835
  ev.emit('blocklist.update', { blocklist, type })
680
836
  }
681
837
  }
682
838
  break
683
839
  case 'link_code_companion_reg':
684
- const linkCodeCompanionReg = WABinary_1.getBinaryNodeChild(node, 'link_code_companion_reg')
685
- const ref = toRequiredBuffer(WABinary_1.getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_ref'))
686
- const primaryIdentityPublicKey = toRequiredBuffer(WABinary_1.getBinaryNodeChildBuffer(linkCodeCompanionReg, 'primary_identity_pub'))
687
- const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(WABinary_1.getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'))
840
+ const linkCodeCompanionReg = getBinaryNodeChild(node, 'link_code_companion_reg')
841
+ const ref = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_ref'))
842
+ const primaryIdentityPublicKey = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'primary_identity_pub'))
843
+ const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'))
688
844
  const codePairingPublicKey = await decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped)
689
- const companionSharedKey = Utils_1.Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey)
690
- const random = crypto_1.randomBytes(32)
691
- const linkCodeSalt = crypto_1.randomBytes(32)
692
-
693
- const linkCodePairingExpanded = await Utils_1.hkdf(companionSharedKey, 32, {
845
+ const companionSharedKey = Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey)
846
+ const random = randomBytes(32)
847
+ const linkCodeSalt = randomBytes(32)
848
+ const linkCodePairingExpanded = await hkdf(companionSharedKey, 32, {
694
849
  salt: linkCodeSalt,
695
850
  info: 'link_code_pairing_key_bundle_encryption_key'
696
851
  })
697
-
698
- const encryptPayload = Buffer.concat([Buffer.from(authState.creds.signedIdentityKey.public), primaryIdentityPublicKey, random])
699
- const encryptIv = crypto_1.randomBytes(12)
700
- const encrypted = Utils_1.aesEncryptGCM(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0))
852
+ const encryptPayload = Buffer.concat([
853
+ Buffer.from(authState.creds.signedIdentityKey.public),
854
+ primaryIdentityPublicKey,
855
+ random
856
+ ])
857
+ const encryptIv = randomBytes(12)
858
+ const encrypted = aesEncryptGCM(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0))
701
859
  const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted])
702
- const identitySharedKey = Utils_1.Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey)
860
+ const identitySharedKey = Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey)
703
861
  const identityPayload = Buffer.concat([companionSharedKey, identitySharedKey, random])
704
862
 
705
- authState.creds.advSecretKey = (await Utils_1.hkdf(identityPayload, 32, { info: 'adv_secret' })).toString('base64')
863
+ authState.creds.advSecretKey = (await hkdf(identityPayload, 32, { info: 'adv_secret' })).toString('base64')
706
864
 
707
865
  await query({
708
866
  tag: 'iq',
709
867
  attrs: {
710
- to: WABinary_1.S_WHATSAPP_NET,
868
+ to: S_WHATSAPP_NET,
711
869
  type: 'set',
712
870
  id: suki.generateMessageTag(),
713
871
  xmlns: 'md'
@@ -717,7 +875,7 @@ const makeMessagesRecvSocket = (config) => {
717
875
  tag: 'link_code_companion_reg',
718
876
  attrs: {
719
877
  jid: authState.creds.me.id,
720
- stage: 'companion_finish',
878
+ stage: 'companion_finish'
721
879
  },
722
880
  content: [
723
881
  {
@@ -742,6 +900,10 @@ const makeMessagesRecvSocket = (config) => {
742
900
 
743
901
  authState.creds.registered = true
744
902
  ev.emit('creds.update', authState.creds)
903
+ break
904
+ case 'privacy_token':
905
+ await handlePrivacyTokenNotification(node)
906
+ break
745
907
  }
746
908
 
747
909
  if (Object.keys(result).length) {
@@ -749,18 +911,45 @@ const makeMessagesRecvSocket = (config) => {
749
911
  }
750
912
  }
751
913
 
914
+ const handlePrivacyTokenNotification = async (node) => {
915
+ const tokensNode = getBinaryNodeChild(node, 'tokens')
916
+ const from = jidNormalizedUser(node.attrs.from)
917
+
918
+ if (!tokensNode) return
919
+
920
+ const tokenNodes = getBinaryNodeChildren(tokensNode, 'token')
921
+
922
+ for (const tokenNode of tokenNodes) {
923
+ const { attrs, content } = tokenNode
924
+ const type = attrs.type
925
+ const timestamp = attrs.t
926
+
927
+ if (type === 'trusted_contact' && content instanceof Buffer) {
928
+ logger.debug({
929
+ from,
930
+ timestamp,
931
+ tcToken: content
932
+ }, 'received trusted contact token')
933
+
934
+ await authState.keys.set({
935
+ tctoken: { [from]: { token: content, timestamp } }
936
+ })
937
+ }
938
+ }
939
+ }
940
+
752
941
  async function decipherLinkPublicKey(data) {
753
942
  const buffer = toRequiredBuffer(data)
754
943
  const salt = buffer.slice(0, 32)
755
- const secretKey = await Utils_1.derivePairingCodeKey(authState.creds.pairingCode, salt)
944
+ const secretKey = await derivePairingCodeKey(authState.creds.pairingCode, salt)
756
945
  const iv = buffer.slice(32, 48)
757
946
  const payload = buffer.slice(48, 80)
758
- return Utils_1.aesDecryptCTR(payload, secretKey, iv)
947
+ return aesDecryptCTR(payload, secretKey, iv)
759
948
  }
760
949
 
761
950
  function toRequiredBuffer(data) {
762
951
  if (data === undefined) {
763
- throw new boom_1.Boom('Invalid buffer', { statusCode: 400 })
952
+ throw new Boom('Invalid buffer', { statusCode: 400 })
764
953
  }
765
954
  return data instanceof Buffer ? data : Buffer.from(data)
766
955
  }
@@ -791,6 +980,7 @@ const makeMessagesRecvSocket = (config) => {
791
980
  // Try to get from retry cache first if enabled
792
981
  if (messageRetryManager) {
793
982
  const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id)
983
+
794
984
  if (cachedMsg) {
795
985
  msg = cachedMsg.message
796
986
  logger.debug({ jid: remoteJid, id }, 'found message in retry cache')
@@ -803,22 +993,24 @@ const makeMessagesRecvSocket = (config) => {
803
993
  // Fallback to getMessage if not found in cache
804
994
  if (!msg) {
805
995
  msg = await getMessage({ ...key, id })
996
+
806
997
  if (msg) {
807
998
  logger.debug({ jid: remoteJid, id }, 'found message via getMessage')
808
999
 
809
1000
  // Also mark as successful if found via getMessage
810
1001
  if (messageRetryManager) {
811
- messageRetryManager.markRetrySuccess(id);
1002
+ messageRetryManager.markRetrySuccess(id)
812
1003
  }
813
1004
  }
814
1005
  }
1006
+
815
1007
  msgs.push(msg)
816
1008
  }
817
1009
 
818
1010
  // if it's the primary jid sending the request
819
1011
  // just re-send the message to everyone
820
1012
  // prevents the first message decryption failure
821
- const sendToAll = !WABinary_1.jidDecode(participant)?.device
1013
+ const sendToAll = !jidDecode(participant)?.device
822
1014
 
823
1015
  // Check if we should recreate session for this retry
824
1016
  let shouldRecreateSession = false
@@ -834,41 +1026,45 @@ const makeMessagesRecvSocket = (config) => {
834
1026
  recreateReason = result.reason
835
1027
 
836
1028
  if (shouldRecreateSession) {
837
- logger.info({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry')
838
- await authState.keys.set({ session: { [sessionId]: null } });
1029
+ logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry')
1030
+ await authState.keys.set({ session: { [sessionId]: null } })
839
1031
  }
840
1032
  }
1033
+
841
1034
  catch (error) {
842
1035
  logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry')
843
1036
  }
844
1037
  }
845
1038
 
846
- await assertSessions([participant], shouldRecreateSession)
1039
+ await assertSessions([participant], true)
847
1040
 
848
- if (WABinary_1.isJidGroup(remoteJid)) {
849
- await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } });
1041
+ if (isJidGroup(remoteJid)) {
1042
+ await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } })
850
1043
  }
1044
+
851
1045
  logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp')
852
1046
 
853
1047
  for (const [i, msg] of msgs.entries()) {
854
- if (!ids[i])
855
- continue
856
-
1048
+ if (!ids[i]) continue
1049
+
857
1050
  if (msg && (await willSendMessageAgain(ids[i], participant))) {
858
- updateSendMessageAgainCount(ids[i], participant)
1051
+ await updateSendMessageAgainCount(ids[i], participant)
859
1052
  const msgRelayOpts = { messageId: ids[i] }
860
1053
 
861
1054
  if (sendToAll) {
862
1055
  msgRelayOpts.useUserDevicesCache = false
863
1056
  }
1057
+
864
1058
  else {
865
1059
  msgRelayOpts.participant = {
866
1060
  jid: participant,
867
1061
  count: +retryNode.attrs.count
868
1062
  }
869
1063
  }
1064
+
870
1065
  await relayMessage(key.remoteJid, msg, msgRelayOpts)
871
1066
  }
1067
+
872
1068
  else {
873
1069
  logger.debug({ jid: key.remoteJid, id: ids[i] }, 'recv retry request, but message not available')
874
1070
  }
@@ -878,10 +1074,9 @@ const makeMessagesRecvSocket = (config) => {
878
1074
  const handleReceipt = async (node) => {
879
1075
  const { attrs, content } = node
880
1076
  const isLid = attrs.from.includes('lid')
881
- const isNodeFromMe = WABinary_1.areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id)
882
- const remoteJid = !isNodeFromMe || WABinary_1.isJidGroup(attrs.from) ? attrs.from : attrs.recipient
1077
+ const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id)
1078
+ const remoteJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient
883
1079
  const fromMe = !attrs.recipient || ((attrs.type === 'retry' || attrs.type === 'sender') && isNodeFromMe)
884
-
885
1080
  const key = {
886
1081
  remoteJid,
887
1082
  id: '',
@@ -889,35 +1084,36 @@ const makeMessagesRecvSocket = (config) => {
889
1084
  participant: attrs.participant
890
1085
  }
891
1086
 
892
- if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
1087
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
893
1088
  logger.debug({ remoteJid }, 'ignoring receipt from jid')
894
1089
  await sendMessageAck(node)
895
1090
  return
896
1091
  }
897
1092
 
898
1093
  const ids = [attrs.id]
1094
+
899
1095
  if (Array.isArray(content)) {
900
- const items = WABinary_1.getBinaryNodeChildren(content[0], 'item')
1096
+ const items = getBinaryNodeChildren(content[0], 'item')
901
1097
  ids.push(...items.map(i => i.attrs.id))
902
1098
  }
903
1099
 
904
1100
  try {
905
1101
  await Promise.all([
906
- processingMutex.mutex(async () => {
907
- const status = Utils_1.getStatusFromReceiptType(attrs.type)
1102
+ receiptMutex.mutex(async () => {
1103
+ const status = getStatusFromReceiptType(attrs.type)
908
1104
 
909
- if (typeof status !== 'undefined' && (
1105
+ if (typeof status !== 'undefined' &&
910
1106
  // basically, we only want to know when a message from us has been delivered to/read by the other person
911
1107
  // or another device of ours has read some messages
912
- status >= WAProto_1.proto.WebMessageInfo.Status.SERVER_ACK ||
913
- !isNodeFromMe)) {
914
- if (WABinary_1.isJidGroup(remoteJid) || WABinary_1.isJidStatusBroadcast(remoteJid)) {
1108
+ (status >= proto.WebMessageInfo.Status.SERVER_ACK || !isNodeFromMe)) {
1109
+ if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
915
1110
  if (attrs.participant) {
916
- const updateKey = status === WAProto_1.proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp'
1111
+ const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp'
1112
+
917
1113
  ev.emit('message-receipt.update', ids.map(id => ({
918
1114
  key: { ...key, id },
919
1115
  receipt: {
920
- userJid: WABinary_1.jidNormalizedUser(attrs.participant),
1116
+ userJid: jidNormalizedUser(attrs.participant),
921
1117
  [updateKey]: +attrs.t
922
1118
  }
923
1119
  })))
@@ -935,13 +1131,16 @@ const makeMessagesRecvSocket = (config) => {
935
1131
  if (attrs.type === 'retry') {
936
1132
  // correctly set who is asking for the retry
937
1133
  key.participant = key.participant || attrs.from
938
- const retryNode = WABinary_1.getBinaryNodeChild(node, 'retry')
1134
+
1135
+ const retryNode = getBinaryNodeChild(node, 'retry')
939
1136
 
940
1137
  if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
941
1138
  if (key.fromMe) {
942
1139
  try {
943
- updateSendMessageAgainCount(ids[0], key.participant)
1140
+ await updateSendMessageAgainCount(ids[0], key.participant)
1141
+
944
1142
  logger.debug({ attrs, key }, 'recv retry request')
1143
+
945
1144
  await sendMessagesAgain(key, ids, retryNode)
946
1145
  }
947
1146
 
@@ -962,6 +1161,7 @@ const makeMessagesRecvSocket = (config) => {
962
1161
  })
963
1162
  ])
964
1163
  }
1164
+
965
1165
  finally {
966
1166
  await sendMessageAck(node)
967
1167
  }
@@ -969,8 +1169,7 @@ const makeMessagesRecvSocket = (config) => {
969
1169
 
970
1170
  const handleNotification = async (node) => {
971
1171
  const remoteJid = node.attrs.from
972
-
973
- if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
1172
+ if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
974
1173
  logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification')
975
1174
  await sendMessageAck(node)
976
1175
  return
@@ -978,96 +1177,72 @@ const makeMessagesRecvSocket = (config) => {
978
1177
 
979
1178
  try {
980
1179
  await Promise.all([
981
- processingMutex.mutex(async () => {
1180
+ notificationMutex.mutex(async () => {
982
1181
  const msg = await processNotification(node)
983
1182
 
984
1183
  if (msg) {
985
- const fromMe = WABinary_1.areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id)
1184
+ const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id)
1185
+ const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node)
1186
+
986
1187
  msg.key = {
987
1188
  remoteJid,
988
1189
  fromMe,
989
1190
  participant: node.attrs.participant,
1191
+ participantAlt,
1192
+ addressingMode,
990
1193
  id: node.attrs.id,
991
1194
  ...(msg.key || {})
992
1195
  }
993
- msg.participant = msg.participant ? msg.participant : node.attrs.participant
1196
+
1197
+ msg.participant ?? (msg.participant = node.attrs.participant)
994
1198
  msg.messageTimestamp = +node.attrs.t
995
- const fullMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg)
1199
+
1200
+ const fullMsg = proto.WebMessageInfo.fromObject(msg)
1201
+
996
1202
  await upsertMessage(fullMsg, 'append')
997
1203
  }
998
1204
  })
999
1205
  ])
1000
1206
  }
1207
+
1001
1208
  finally {
1002
1209
  await sendMessageAck(node)
1003
1210
  }
1004
1211
  }
1005
1212
 
1006
1213
  const handleMessage = async (node) => {
1007
- if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== '@s.whatsapp.net') {
1214
+ if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== S_WHATSAPP_NET) {
1008
1215
  logger.debug({ key: node.attrs.key }, 'ignored message')
1009
- await sendMessageAck(node)
1216
+ await sendMessageAck(node, NACK_REASONS.UnhandledError)
1010
1217
  return
1011
1218
  }
1012
1219
 
1013
- let response
1014
-
1015
- const encNode = WABinary_1.getBinaryNodeChild(node, 'enc')
1220
+ const encNode = getBinaryNodeChild(node, 'enc')
1016
1221
 
1017
1222
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
1018
1223
  if (encNode && encNode.attrs.type === 'msmsg') {
1019
1224
  logger.debug({ key: node.attrs.key }, 'ignored msmsg')
1020
- await sendMessageAck(node, Utils_1.NACK_REASONS.MissingMessageSecret)
1225
+ await sendMessageAck(node, NACK_REASONS.MissingMessageSecret)
1021
1226
  return
1022
1227
  }
1023
1228
 
1024
- if (WABinary_1.getBinaryNodeChild(node, 'unavailable') && !encNode) {
1025
- await sendMessageAck(node)
1026
- const { key } = Utils_1.decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage
1027
- response = await requestPlaceholderResend(key);
1028
- if (response === 'RESOLVED') {
1029
- return
1030
- }
1031
- logger.debug('received unavailable message, acked and requested resend from phone');
1032
- }
1033
- else {
1034
- if (placeholderResendCache.get(node.attrs.id)) {
1035
- await placeholderResendCache.del(node.attrs.id)
1036
- }
1037
- }
1038
-
1039
- const { fullMessage: msg, category, author, decrypt } = Utils_1.decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger)
1040
-
1041
- if (response && msg?.messageStubParameters?.[0] === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT) {
1042
- msg.messageStubParameters = [Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT, response]
1043
- }
1044
-
1045
- if (msg.message?.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
1046
- node.attrs.sender_pn) {
1047
- const lid = WABinary_1.jidNormalizedUser(node.attrs.from), pn = WABinary_1.jidNormalizedUser(node.attrs.sender_pn)
1048
- ev.emit('lid-mapping.update', { lid, pn })
1049
- await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }])
1050
- }
1051
-
1052
- const alt = msg.key.participantAlt || msg.key.remoteJidAlt
1053
-
1229
+ const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
1230
+ const alt = msg.key.participantAlt || msg.key.remoteJidAlt;
1054
1231
  // store new mappings we didn't have before
1055
1232
  if (!!alt) {
1056
- const altServer = WABinary_1.jidDecode(alt)?.server
1233
+ const altServer = jidDecode(alt)?.server
1234
+ const primaryJid = msg.key.participant || msg.key.remoteJid
1057
1235
 
1058
1236
  if (altServer === 'lid') {
1059
- if (typeof (await signalRepository.lidMapping.getPNForLID(alt)) === 'string') {
1060
- await signalRepository.lidMapping.storeLIDPNMappings([
1061
- { lid: alt, pn: msg.key.participant || msg.key.remoteJid }
1062
- ])
1237
+ if (!(await signalRepository.lidMapping.getPNForLID(alt))) {
1238
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid: alt, pn: primaryJid }])
1239
+ await signalRepository.migrateSession(primaryJid, alt)
1063
1240
  }
1064
1241
  }
1242
+
1065
1243
  else {
1066
- if (typeof (await signalRepository.lidMapping.getLIDForPN(alt)) === 'string') {
1067
- await signalRepository.lidMapping.storeLIDPNMappings([
1068
- { lid: msg.key.participant || msg.key.remoteJid, pn: alt }
1069
- ])
1070
- }
1244
+ await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }])
1245
+ await signalRepository.migrateSession(alt, primaryJid)
1071
1246
  }
1072
1247
  }
1073
1248
 
@@ -1080,70 +1255,83 @@ const makeMessagesRecvSocket = (config) => {
1080
1255
  }
1081
1256
 
1082
1257
  try {
1083
- await Promise.all([
1084
- processingMutex.mutex(async () => {
1085
- await decrypt()
1086
- // message failed to decrypt
1087
- if (msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT) {
1088
- if (msg?.messageStubParameters?.[0] === Utils_1.MISSING_KEYS_ERROR_TEXT) {
1089
- return sendMessageAck(node, Utils_1.NACK_REASONS.ParsingError)
1090
- }
1091
-
1092
- const errorMessage = msg?.messageStubParameters?.[0] || ''
1093
- const isPreKeyError = errorMessage.includes('PreKey')
1094
- logger.debug(`[handleMessage] Attempting retry request for failed decryption`)
1095
-
1096
- // Handle both pre-key and normal retries in single mutex
1097
- retryMutex.mutex(async () => {
1098
- try {
1099
- if (!ws.isOpen) {
1100
- logger.debug({ node }, 'Connection closed, skipping retry')
1101
- return
1102
- }
1258
+ await messageMutex.mutex(async () => {
1259
+ await decrypt()
1260
+
1261
+ // message failed to decrypt
1262
+ if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
1263
+ if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT ||
1264
+ msg.messageStubParameters?.[0] === NO_MESSAGE_FOUND_ERROR_TEXT) {
1265
+ return sendMessageAck(node)
1266
+ }
1267
+
1268
+ const errorMessage = msg?.messageStubParameters?.[0] || ''
1269
+ const isPreKeyError = errorMessage.includes('PreKey')
1270
+
1271
+ logger.debug(`[handleMessage] Attempting retry request for failed decryption`)
1272
+
1273
+ // Handle both pre-key and normal retries in single mutex
1274
+ await retryMutex.mutex(async () => {
1275
+ try {
1276
+ if (!ws.isOpen) {
1277
+ logger.debug({ node }, 'Connection closed, skipping retry')
1278
+ return
1279
+ }
1280
+
1281
+ // Handle pre-key errors with upload and delay
1282
+ if (isPreKeyError) {
1283
+ logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying')
1103
1284
 
1104
- if (WABinary_1.getBinaryNodeChild(node, 'unavailable')) {
1105
- logger.debug('Message unavailable, skipping retry')
1106
- return
1285
+ try {
1286
+ logger.debug('Uploading pre-keys for error recovery')
1287
+ await uploadPreKeys(5)
1288
+ logger.debug('Waiting for server to process new pre-keys')
1289
+ await delay(1000)
1107
1290
  }
1108
1291
 
1109
- // Handle pre-key errors with upload and delay
1110
- if (isPreKeyError) {
1111
- logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying')
1112
- try {
1113
- logger.debug('Uploading pre-keys for error recovery')
1114
- await uploadPreKeys(5)
1115
- logger.debug('Waiting for server to process new pre-keys')
1116
- await Utils_1.delay(1000)
1117
- }
1118
- catch (uploadErr) {
1119
- logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway')
1120
- }
1292
+ catch (uploadErr) {
1293
+ logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway')
1121
1294
  }
1295
+ }
1296
+
1297
+ const encNode = getBinaryNodeChild(node, 'enc')
1298
+
1299
+ await sendRetryRequest(node, !encNode)
1300
+
1301
+ if (retryRequestDelayMs) {
1302
+ await delay(retryRequestDelayMs)
1303
+ }
1304
+ }
1305
+
1306
+ catch (err) {
1307
+ logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry')
1308
+
1309
+ // Still attempt retry even if pre-key upload failed
1310
+ try {
1311
+ const encNode = getBinaryNodeChild(node, 'enc')
1122
1312
 
1123
- const encNode = WABinary_1.getBinaryNodeChild(node, 'enc')
1124
1313
  await sendRetryRequest(node, !encNode)
1125
- if (retryRequestDelayMs) {
1126
- await Utils_1.delay(retryRequestDelayMs)
1127
- }
1128
1314
  }
1129
- catch (err) {
1130
- logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry')
1131
- // Still attempt retry even if pre-key upload failed
1132
- try {
1133
- const encNode = WABinary_1.getBinaryNodeChild(node, 'enc')
1134
- await sendRetryRequest(node, !encNode)
1135
- }
1136
- catch (retryErr) {
1137
- logger.error({ retryErr }, 'Failed to send retry after error handling')
1138
- }
1315
+
1316
+ catch (retryErr) {
1317
+ logger.error({ retryErr }, 'Failed to send retry after error handling')
1139
1318
  }
1140
- })
1319
+ }
1320
+
1321
+ await sendMessageAck(node, NACK_REASONS.UnhandledError)
1322
+ })
1323
+ }
1324
+
1325
+ else {
1326
+ if (messageRetryManager && msg.key.id) {
1327
+ messageRetryManager.cancelPendingPhoneRequest(msg.key.id)
1141
1328
  }
1142
1329
 
1143
- else {
1330
+ const isNewsletter = isJidNewsletter(msg.key.remoteJid)
1331
+
1332
+ if (!isNewsletter) {
1144
1333
  // no type in the receipt => message delivered
1145
1334
  let type = undefined
1146
-
1147
1335
  let participant = msg.key.participant
1148
1336
 
1149
1337
  if (category === 'peer') {
@@ -1156,7 +1344,7 @@ const makeMessagesRecvSocket = (config) => {
1156
1344
  type = 'sender'
1157
1345
 
1158
1346
  // need to specially handle this case
1159
- if (WABinary_1.isLidUser(msg.key.remoteJid) || WABinary_1.isLidUser(msg.key.remoteJidAlt)) {
1347
+ if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1160
1348
  participant = author // TODO: investigate sending receipts to LIDs and not PNs
1161
1349
  }
1162
1350
  }
@@ -1164,124 +1352,60 @@ const makeMessagesRecvSocket = (config) => {
1164
1352
  else if (!sendActiveReceipts) {
1165
1353
  type = 'inactive'
1166
1354
  }
1355
+
1167
1356
  await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type)
1168
1357
 
1169
1358
  // send ack for history message
1170
- const isAnyHistoryMsg = Utils_1.getHistoryMsg(msg.message)
1359
+ const isAnyHistoryMsg = getHistoryMsg(msg.message)
1360
+
1171
1361
  if (isAnyHistoryMsg) {
1172
- const jid = WABinary_1.jidNormalizedUser(msg.key.remoteJid)
1173
- await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync')
1362
+ const jid = jidNormalizedUser(msg.key.remoteJid)
1363
+ await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync') // TODO: investigate
1174
1364
  }
1175
1365
  }
1176
- Utils_1.cleanMessage(msg, authState.creds.me.id)
1177
- await sendMessageAck(node)
1178
- await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify')
1179
- })
1180
- ])
1366
+
1367
+ else {
1368
+ await sendMessageAck(node)
1369
+ logger.debug({ key: msg.key }, 'processed newsletter message without receipts')
1370
+ }
1371
+ }
1372
+
1373
+ cleanMessage(msg, authState.creds.me.id, authState.creds.me.lid)
1374
+
1375
+ await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify')
1376
+ })
1181
1377
  }
1182
1378
 
1183
1379
  catch (error) {
1184
- logger.error({ error, node }, 'error in handling message')
1380
+ logger.error({ error, node: binaryNodeToString(node) }, 'error in handling message')
1185
1381
  }
1186
1382
  }
1187
1383
 
1188
- const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
1189
- if (!authState.creds.me?.id) {
1190
- throw new boom_1.Boom('Not authenticated')
1191
- }
1192
-
1193
- const pdoMessage = {
1194
- historySyncOnDemandRequest: {
1195
- chatJid: oldestMsgKey.remoteJid,
1196
- oldestMsgFromMe: oldestMsgKey.fromMe,
1197
- oldestMsgId: oldestMsgKey.id,
1198
- oldestMsgTimestampMs: oldestMsgTimestamp,
1199
- onDemandMsgCount: count
1200
- },
1201
- peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
1202
- }
1203
-
1204
- return sendPeerDataOperationMessage(pdoMessage)
1205
- }
1206
-
1207
- const requestPlaceholderResend = async (messageKey) => {
1208
- if (!authState.creds.me?.id) {
1209
- throw new boom_1.Boom('Not authenticated')
1210
- }
1211
-
1212
- if (placeholderResendCache.get(messageKey?.id)) {
1213
- logger.debug({ messageKey }, 'already requested resend')
1214
- return
1215
- }
1216
-
1217
- else {
1218
- placeholderResendCache.set(messageKey?.id, true)
1219
- }
1220
-
1221
- await Utils_1.delay(5000)
1222
-
1223
- if (!placeholderResendCache.get(messageKey?.id)) {
1224
- logger.debug({ messageKey }, 'message received while resend requested')
1225
- return 'RESOLVED'
1226
- }
1384
+ const handleCall = async (node) => {
1385
+ const { attrs } = node
1386
+ const [infoChild] = getAllBinaryNodeChildren(node)
1387
+ const status = getCallStatusFromNode(infoChild)
1227
1388
 
1228
- const pdoMessage = {
1229
- placeholderMessageResendRequest: [{
1230
- messageKey
1231
- }],
1232
- peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
1389
+ if (!infoChild) {
1390
+ throw new Boom('Missing call info in call node')
1233
1391
  }
1234
1392
 
1235
- setTimeout(() => {
1236
- if (placeholderResendCache.get(messageKey?.id)) {
1237
- logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline')
1238
- placeholderResendCache.del(messageKey?.id)
1239
- }
1240
- }, 15000)
1241
-
1242
- return sendPeerDataOperationMessage(pdoMessage)
1243
- }
1244
-
1245
- const handleCall = async (node) => {
1246
- let status
1247
-
1248
- const { attrs } = node
1249
- const [infoChild] = WABinary_1.getAllBinaryNodeChildren(node)
1250
1393
  const callId = infoChild.attrs['call-id']
1251
1394
  const from = infoChild.attrs.from || infoChild.attrs['call-creator']
1252
- status = Utils_1.getCallStatusFromNode(infoChild)
1253
-
1254
- if (WABinary_1.isLidUser(from) && infoChild.tag === 'relaylatency') {
1255
- const verify = await callOfferCache.get(callId)
1256
-
1257
- if (!verify) {
1258
- status = 'offer'
1259
-
1260
- const callLid = {
1261
- chatId: attrs.from,
1262
- from,
1263
- id: callId,
1264
- date: new Date(+attrs.t * 1000),
1265
- offline: !!attrs.offline,
1266
- status
1267
- }
1268
- await callOfferCache.set(callId, callLid)
1269
- }
1270
- }
1271
-
1272
1395
  const call = {
1273
1396
  chatId: attrs.from,
1274
1397
  from,
1275
1398
  id: callId,
1276
1399
  date: new Date(+attrs.t * 1000),
1277
1400
  offline: !!attrs.offline,
1278
- status,
1401
+ status
1279
1402
  }
1280
1403
 
1281
1404
  if (status === 'offer') {
1282
- call.isVideo = !!WABinary_1.getBinaryNodeChild(infoChild, 'video')
1405
+ call.isVideo = !!getBinaryNodeChild(infoChild, 'video')
1283
1406
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid']
1284
1407
  call.groupJid = infoChild.attrs['group-jid']
1408
+
1285
1409
  await callOfferCache.set(call.id, call)
1286
1410
  }
1287
1411
 
@@ -1304,7 +1428,8 @@ const makeMessagesRecvSocket = (config) => {
1304
1428
  }
1305
1429
 
1306
1430
  const handleBadAck = async ({ attrs }) => {
1307
- const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id, newsletter_server_id: attrs?.server_id }
1431
+ const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id }
1432
+
1308
1433
  // WARNING: REFRAIN FROM ENABLING THIS FOR NOW. IT WILL CAUSE A LOOP
1309
1434
  // // current hypothesis is that if pash is sent in the ack
1310
1435
  // // it means -- the message hasn't reached all devices yet
@@ -1326,13 +1451,25 @@ const makeMessagesRecvSocket = (config) => {
1326
1451
  {
1327
1452
  key,
1328
1453
  update: {
1329
- status: Types_1.WAMessageStatus.ERROR,
1330
- messageStubParameters: [
1331
- attrs.error
1332
- ]
1454
+ status: WAMessageStatus.ERROR,
1455
+ messageStubParameters: [attrs.error]
1333
1456
  }
1334
1457
  }
1335
1458
  ])
1459
+
1460
+ // resend the message with device_fanout=false, use at your own risk
1461
+ // if (attrs.error === '475') {
1462
+ // const msg = await getMessage(key)
1463
+ // if (msg) {
1464
+ // await relayMessage(key.remoteJid!, msg, {
1465
+ // messageId: key.id!,
1466
+ // useUserDevicesCache: false,
1467
+ // additionalAttributes: {
1468
+ // device_fanout: 'false'
1469
+ // }
1470
+ // })
1471
+ // }
1472
+ // }
1336
1473
  }
1337
1474
  }
1338
1475
 
@@ -1342,12 +1479,17 @@ const makeMessagesRecvSocket = (config) => {
1342
1479
  ev.buffer()
1343
1480
  await execTask()
1344
1481
  ev.flush()
1482
+
1345
1483
  function execTask() {
1346
- return exec(node, false)
1347
- .catch(err => onUnexpectedError(err, identifier))
1484
+ return exec(node, false).catch(err => onUnexpectedError(err, identifier))
1348
1485
  }
1349
1486
  }
1350
1487
 
1488
+ /** Yields control to the event loop to prevent blocking */
1489
+ const yieldToEventLoop = () => {
1490
+ return new Promise(resolve => setImmediate(resolve))
1491
+ }
1492
+
1351
1493
  const makeOfflineNodeProcessor = () => {
1352
1494
  const nodeProcessorMap = new Map([
1353
1495
  ['message', handleMessage],
@@ -1357,8 +1499,12 @@ const makeMessagesRecvSocket = (config) => {
1357
1499
  ])
1358
1500
 
1359
1501
  const nodes = []
1502
+
1360
1503
  let isProcessing = false
1361
1504
 
1505
+ // Number of nodes to process before yielding to event loop
1506
+ const BATCH_SIZE = 10
1507
+
1362
1508
  const enqueue = (type, node) => {
1363
1509
  nodes.push({ type, node })
1364
1510
 
@@ -1369,17 +1515,32 @@ const makeMessagesRecvSocket = (config) => {
1369
1515
  isProcessing = true
1370
1516
 
1371
1517
  const promise = async () => {
1518
+ let processedInBatch = 0
1519
+
1372
1520
  while (nodes.length && ws.isOpen) {
1373
1521
  const { type, node } = nodes.shift()
1374
1522
  const nodeProcessor = nodeProcessorMap.get(type)
1523
+
1375
1524
  if (!nodeProcessor) {
1376
1525
  onUnexpectedError(new Error(`unknown offline node type: ${type}`), 'processing offline node')
1377
1526
  continue
1378
1527
  }
1528
+
1379
1529
  await nodeProcessor(node)
1530
+
1531
+ processedInBatch++
1532
+
1533
+ // Yield to event loop after processing a batch
1534
+ // This prevents blocking the event loop for too long when there are many offline nodes
1535
+ if (processedInBatch >= BATCH_SIZE) {
1536
+ processedInBatch = 0
1537
+ await yieldToEventLoop()
1538
+ }
1380
1539
  }
1540
+
1381
1541
  isProcessing = false
1382
1542
  }
1543
+
1383
1544
  promise().catch(error => onUnexpectedError(error, 'processing offline nodes'))
1384
1545
  }
1385
1546
 
@@ -1388,7 +1549,7 @@ const makeMessagesRecvSocket = (config) => {
1388
1549
 
1389
1550
  const offlineNodeProcessor = makeOfflineNodeProcessor()
1390
1551
 
1391
- const processNode = (type, node, identifier, exec) => {
1552
+ const processNode = async (type, node, identifier, exec) => {
1392
1553
  const isOffline = !!node.attrs.offline
1393
1554
 
1394
1555
  if (isOffline) {
@@ -1396,28 +1557,36 @@ const makeMessagesRecvSocket = (config) => {
1396
1557
  }
1397
1558
 
1398
1559
  else {
1399
- processNodeWithBuffer(node, identifier, exec)
1560
+ await processNodeWithBuffer(node, identifier, exec)
1400
1561
  }
1401
1562
  }
1402
1563
 
1403
1564
  // recv a message
1404
- ws.on('CB:message', (node) => {
1405
- processNode('message', node, 'processing message', handleMessage)
1565
+ ws.on('CB:message', async (node) => {
1566
+ await processNode('message', node, 'processing message', handleMessage)
1406
1567
  })
1568
+
1407
1569
  ws.on('CB:call', async (node) => {
1408
- processNode('call', node, 'handling call', handleCall)
1570
+ await processNode('call', node, 'handling call', handleCall)
1409
1571
  })
1410
- ws.on('CB:receipt', node => {
1411
- processNode('receipt', node, 'handling receipt', handleReceipt)
1572
+
1573
+ ws.on('CB:receipt', async (node) => {
1574
+ await processNode('receipt', node, 'handling receipt', handleReceipt)
1412
1575
  })
1576
+
1413
1577
  ws.on('CB:notification', async (node) => {
1414
- processNode('notification', node, 'handling notification', handleNotification)
1578
+ await processNode('notification', node, 'handling notification', handleNotification)
1415
1579
  })
1580
+
1416
1581
  ws.on('CB:ack,class:message', (node) => {
1417
- handleBadAck(node)
1418
- .catch(error => onUnexpectedError(error, 'handling bad ack'))
1582
+ handleBadAck(node).catch(error => onUnexpectedError(error, 'handling bad ack'))
1419
1583
  })
1420
- ev.on('call', ([call]) => {
1584
+
1585
+ ev.on('call', async ([call]) => {
1586
+ if (!call) {
1587
+ return;
1588
+ }
1589
+
1421
1590
  // missed call + group call notification message generation
1422
1591
  if (call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
1423
1592
  const msg = {
@@ -1426,25 +1595,28 @@ const makeMessagesRecvSocket = (config) => {
1426
1595
  id: call.id,
1427
1596
  fromMe: false
1428
1597
  },
1429
- messageTimestamp: Utils_1.unixTimestampSeconds(call.date),
1598
+ messageTimestamp: unixTimestampSeconds(call.date)
1430
1599
  }
1431
1600
 
1432
1601
  if (call.status === 'timeout') {
1433
1602
  if (call.isGroup) {
1434
- msg.messageStubType = call.isVideo ? Types_1.WAMessageStubType.CALL_MISSED_GROUP_VIDEO : Types_1.WAMessageStubType.CALL_MISSED_GROUP_VOICE
1603
+ msg.messageStubType = call.isVideo
1604
+ ? WAMessageStubType.CALL_MISSED_GROUP_VIDEO
1605
+ : WAMessageStubType.CALL_MISSED_GROUP_VOICE
1435
1606
  }
1436
1607
 
1437
1608
  else {
1438
- msg.messageStubType = call.isVideo ? Types_1.WAMessageStubType.CALL_MISSED_VIDEO : Types_1.WAMessageStubType.CALL_MISSED_VOICE
1609
+ msg.messageStubType = call.isVideo ? WAMessageStubType.CALL_MISSED_VIDEO : WAMessageStubType.CALL_MISSED_VOICE
1439
1610
  }
1440
1611
  }
1441
1612
 
1442
1613
  else {
1443
- msg.message = { call: { callKey: Buffer.from(call.id) } }
1614
+ msg.message = { call: { callKey: Buffer.from(call.id) } };
1444
1615
  }
1445
1616
 
1446
- const protoMsg = WAProto_1.proto.WebMessageInfo.fromObject(msg)
1447
- upsertMessage(protoMsg, call.offline ? 'append' : 'notify')
1617
+ const protoMsg = proto.WebMessageInfo.fromObject(msg)
1618
+
1619
+ await upsertMessage(protoMsg, call.offline ? 'append' : 'notify')
1448
1620
  }
1449
1621
  })
1450
1622