@bellaxchuu/flowred 0.8.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 (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +214 -0
  3. package/WAProto/GenerateStatics.sh +3 -0
  4. package/WAProto/WAProto.proto +6902 -0
  5. package/WAProto/fix-imports.js +85 -0
  6. package/WAProto/index.js +242946 -0
  7. package/engine-requirements.js +10 -0
  8. package/lib/Defaults/index.js +190 -0
  9. package/lib/Defaults/phonenumber-mcc.json +223 -0
  10. package/lib/Signal/Group/ciphertext-message.js +15 -0
  11. package/lib/Signal/Group/group-session-builder.js +92 -0
  12. package/lib/Signal/Group/group_cipher.js +89 -0
  13. package/lib/Signal/Group/index.js +136 -0
  14. package/lib/Signal/Group/keyhelper.js +73 -0
  15. package/lib/Signal/Group/sender-chain-key.js +32 -0
  16. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  17. package/lib/Signal/Group/sender-key-message.js +69 -0
  18. package/lib/Signal/Group/sender-key-name.js +50 -0
  19. package/lib/Signal/Group/sender-key-record.js +44 -0
  20. package/lib/Signal/Group/sender-key-state.js +97 -0
  21. package/lib/Signal/Group/sender-message-key.js +30 -0
  22. package/lib/Signal/libsignal.js +490 -0
  23. package/lib/Signal/lid-mapping.js +262 -0
  24. package/lib/Socket/Client/index.js +30 -0
  25. package/lib/Socket/Client/types.js +13 -0
  26. package/lib/Socket/Client/websocket.js +62 -0
  27. package/lib/Socket/aigroups.js +240 -0
  28. package/lib/Socket/business.js +422 -0
  29. package/lib/Socket/chats.js +1816 -0
  30. package/lib/Socket/communities.js +549 -0
  31. package/lib/Socket/graphql.js +656 -0
  32. package/lib/Socket/groups.js +764 -0
  33. package/lib/Socket/index.js +39 -0
  34. package/lib/Socket/interactive-handler.js +522 -0
  35. package/lib/Socket/interop.js +549 -0
  36. package/lib/Socket/luxu.js +387 -0
  37. package/lib/Socket/managed-account.js +183 -0
  38. package/lib/Socket/messages-recv.js +2440 -0
  39. package/lib/Socket/messages-send.js +1853 -0
  40. package/lib/Socket/mex.js +60 -0
  41. package/lib/Socket/newsletter.js +820 -0
  42. package/lib/Socket/privacy.js +449 -0
  43. package/lib/Socket/registration.js +427 -0
  44. package/lib/Socket/socket.js +1092 -0
  45. package/lib/Socket/text-router.js +83 -0
  46. package/lib/Socket/username.js +243 -0
  47. package/lib/Store/index.js +36 -0
  48. package/lib/Store/keyed-db.js +108 -0
  49. package/lib/Store/make-cache-manager-store.js +90 -0
  50. package/lib/Store/make-in-memory-store.js +604 -0
  51. package/lib/Store/make-ordered-dictionary.js +81 -0
  52. package/lib/Store/object-repository.js +29 -0
  53. package/lib/Types/Auth.js +38 -0
  54. package/lib/Types/Bussines.js +2 -0
  55. package/lib/Types/Call.js +2 -0
  56. package/lib/Types/Chat.js +4 -0
  57. package/lib/Types/Contact.js +2 -0
  58. package/lib/Types/Events.js +2 -0
  59. package/lib/Types/GroupMetadata.js +2 -0
  60. package/lib/Types/Label.js +27 -0
  61. package/lib/Types/LabelAssociation.js +9 -0
  62. package/lib/Types/Message.js +95 -0
  63. package/lib/Types/Mex.js +112 -0
  64. package/lib/Types/Newsletter.js +121 -0
  65. package/lib/Types/Product.js +2 -0
  66. package/lib/Types/Signal.js +2 -0
  67. package/lib/Types/Socket.js +2 -0
  68. package/lib/Types/State.js +70 -0
  69. package/lib/Types/USync.js +2 -0
  70. package/lib/Types/index.js +55 -0
  71. package/lib/Utils/auth-utils.js +306 -0
  72. package/lib/Utils/browser-utils.js +114 -0
  73. package/lib/Utils/business.js +247 -0
  74. package/lib/Utils/chat-utils.js +1208 -0
  75. package/lib/Utils/command-loader.js +100 -0
  76. package/lib/Utils/companion-reg-client-utils.js +42 -0
  77. package/lib/Utils/consumer-application.js +107 -0
  78. package/lib/Utils/crypto.js +155 -0
  79. package/lib/Utils/curve25519-js.js +275 -0
  80. package/lib/Utils/decode-wa-message.js +560 -0
  81. package/lib/Utils/event-buffer.js +607 -0
  82. package/lib/Utils/generics.js +563 -0
  83. package/lib/Utils/group-history.js +60 -0
  84. package/lib/Utils/history.js +244 -0
  85. package/lib/Utils/identity-change-handler.js +52 -0
  86. package/lib/Utils/index.js +57 -0
  87. package/lib/Utils/jid-display-normalization.js +218 -0
  88. package/lib/Utils/link-preview.js +143 -0
  89. package/lib/Utils/logger.js +9 -0
  90. package/lib/Utils/lt-hash.js +39 -0
  91. package/lib/Utils/make-mutex.js +36 -0
  92. package/lib/Utils/message-composer.js +479 -0
  93. package/lib/Utils/message-retry-manager.js +270 -0
  94. package/lib/Utils/messages-media.js +896 -0
  95. package/lib/Utils/messages.js +2904 -0
  96. package/lib/Utils/meta-ai-msmsg.js +262 -0
  97. package/lib/Utils/native-bridge.js +82 -0
  98. package/lib/Utils/noise-handler.js +196 -0
  99. package/lib/Utils/offline-node-processor.js +42 -0
  100. package/lib/Utils/pre-key-manager.js +107 -0
  101. package/lib/Utils/process-message.js +1048 -0
  102. package/lib/Utils/reporting-utils.js +262 -0
  103. package/lib/Utils/session-pool.js +94 -0
  104. package/lib/Utils/signal.js +224 -0
  105. package/lib/Utils/stanza-ack.js +40 -0
  106. package/lib/Utils/sticker.js +145 -0
  107. package/lib/Utils/sync-action-utils.js +54 -0
  108. package/lib/Utils/tc-token-utils.js +161 -0
  109. package/lib/Utils/use-multi-file-auth-state.js +121 -0
  110. package/lib/Utils/validate-connection.js +219 -0
  111. package/lib/Utils/view-once-cache.js +79 -0
  112. package/lib/Utils/voip-rekey.js +22 -0
  113. package/lib/WABinary/constants.js +1304 -0
  114. package/lib/WABinary/decode.js +343 -0
  115. package/lib/WABinary/encode.js +228 -0
  116. package/lib/WABinary/generic-utils.js +240 -0
  117. package/lib/WABinary/index.js +33 -0
  118. package/lib/WABinary/jid-utils.js +361 -0
  119. package/lib/WABinary/types.js +2 -0
  120. package/lib/WAM/BinaryInfo.js +13 -0
  121. package/lib/WAM/constants.js +39486 -0
  122. package/lib/WAM/encode.js +142 -0
  123. package/lib/WAM/index.js +31 -0
  124. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +55 -0
  125. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  126. package/lib/WAUSync/Protocols/USyncContactProtocol.js +54 -0
  127. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  128. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  129. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +59 -0
  130. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +31 -0
  131. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
  132. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
  133. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +39 -0
  134. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  135. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +27 -0
  136. package/lib/WAUSync/Protocols/index.js +39 -0
  137. package/lib/WAUSync/USyncQuery.js +133 -0
  138. package/lib/WAUSync/USyncUser.js +50 -0
  139. package/lib/WAUSync/index.js +31 -0
  140. package/lib/antiban.js +4637 -0
  141. package/lib/index.js +45 -0
  142. package/package.json +107 -0
@@ -0,0 +1,1208 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.processSyncAction =
4
+ exports.chatModificationToAppPatch =
5
+ exports.decodePatches =
6
+ exports.decodeSyncdSnapshot =
7
+ exports.downloadExternalPatch =
8
+ exports.downloadExternalBlob =
9
+ exports.extractSyncdPatches =
10
+ exports.decodeSyncdPatch =
11
+ exports.decodeSyncdMutations =
12
+ exports.encodeSyncdPatch =
13
+ exports.newLTHashState =
14
+ void 0
15
+ const boom_1 = require('@hapi/boom')
16
+ const index_js_1 = require('../../WAProto/index.js')
17
+ const LabelAssociation_1 = require('../Types/LabelAssociation')
18
+ const WABinary_1 = require('../WABinary')
19
+ const crypto_1 = require('./crypto')
20
+ const generics_1 = require('./generics')
21
+ const lt_hash_1 = require('./lt-hash')
22
+ const messages_media_1 = require('./messages-media')
23
+ const sync_action_utils_1 = require('./sync-action-utils')
24
+ const mutationKeys = keydata => {
25
+ // app-state mutation keys are just HKDF-expanded sub-keys of a single 32-byte
26
+ // key, no elliptic-curve math involved -- this is the standard derivation
27
+ // used across the Baileys ecosystem.
28
+ const expanded = (0, crypto_1.hkdf)(keydata, 160, { info: 'WhatsApp Mutation Keys' })
29
+ return {
30
+ indexKey: expanded.subarray(0, 32),
31
+ valueEncryptionKey: expanded.subarray(32, 64),
32
+ valueMacKey: expanded.subarray(64, 96),
33
+ snapshotMacKey: expanded.subarray(96, 128),
34
+ patchMacKey: expanded.subarray(128, 160)
35
+ }
36
+ }
37
+ const generateMac = (operation, data, keyId, key) => {
38
+ const opByte = operation === index_js_1.proto.SyncdMutation.SyncdOperation.SET ? 0x01 : 0x02
39
+ const keyIdBuffer = typeof keyId === 'string' ? Buffer.from(keyId, 'base64') : keyId
40
+ const keyData = new Uint8Array(1 + keyIdBuffer.length)
41
+ keyData[0] = opByte
42
+ keyData.set(keyIdBuffer, 1)
43
+ const last = new Uint8Array(8)
44
+ last[7] = keyData.length
45
+ const total = new Uint8Array(keyData.length + data.length + last.length)
46
+ total.set(keyData, 0)
47
+ total.set(data, keyData.length)
48
+ total.set(last, keyData.length + data.length)
49
+ const hmac = (0, crypto_1.hmacSign)(total, key, 'sha512')
50
+ return hmac.subarray(0, 32)
51
+ }
52
+ const to64BitNetworkOrder = e => {
53
+ const buff = Buffer.alloc(8)
54
+ buff.writeUint32BE(e, 4)
55
+ return buff
56
+ }
57
+ const makeLtHashGenerator = ({ indexValueMap, hash }) => {
58
+ indexValueMap = { ...indexValueMap }
59
+ const addBuffs = []
60
+ const subBuffs = []
61
+ return {
62
+ mix: ({ indexMac, valueMac, operation }) => {
63
+ const indexMacBase64 = Buffer.from(indexMac).toString('base64')
64
+ const prevOp = indexValueMap[indexMacBase64]
65
+ if (operation === index_js_1.proto.SyncdMutation.SyncdOperation.REMOVE) {
66
+ if (!prevOp) {
67
+ throw new boom_1.Boom('tried remove, but no previous op', { data: { indexMac, valueMac } })
68
+ }
69
+ // remove from index value mac, since this mutation is erased
70
+ delete indexValueMap[indexMacBase64]
71
+ } else {
72
+ addBuffs.push(valueMac)
73
+ // add this index into the history map
74
+ indexValueMap[indexMacBase64] = { valueMac }
75
+ }
76
+ if (prevOp) {
77
+ subBuffs.push(prevOp.valueMac)
78
+ }
79
+ },
80
+ finish: () => {
81
+ const result = lt_hash_1.LT_HASH_ANTI_TAMPERING.subtractThenAdd(hash, subBuffs, addBuffs)
82
+ return {
83
+ hash: Buffer.from(result),
84
+ indexValueMap
85
+ }
86
+ }
87
+ }
88
+ }
89
+ const generateSnapshotMac = (lthash, version, name, key) => {
90
+ const total = Buffer.concat([lthash, to64BitNetworkOrder(version), Buffer.from(name, 'utf-8')])
91
+ return (0, crypto_1.hmacSign)(total, key, 'sha256')
92
+ }
93
+ const generatePatchMac = (snapshotMac, valueMacs, version, type, key) => {
94
+ const total = Buffer.concat([snapshotMac, ...valueMacs, to64BitNetworkOrder(version), Buffer.from(type, 'utf-8')])
95
+ return (0, crypto_1.hmacSign)(total, key)
96
+ }
97
+ const newLTHashState = () => ({ version: 0, hash: Buffer.alloc(128), indexValueMap: {} })
98
+ exports.newLTHashState = newLTHashState
99
+ exports.makeLtHashGenerator = makeLtHashGenerator
100
+ const ensureLTHashStateVersion = state => {
101
+ if (typeof state.version !== 'number' || isNaN(state.version)) {
102
+ state.version = 0
103
+ }
104
+ return state
105
+ }
106
+ exports.ensureLTHashStateVersion = ensureLTHashStateVersion
107
+ exports.MAX_SYNC_ATTEMPTS = 2
108
+ const isMissingKeyError = error => {
109
+ return error?.data?.isMissingKey === true
110
+ }
111
+ exports.isMissingKeyError = isMissingKeyError
112
+ const isAppStateSyncIrrecoverable = (error, attempts) => {
113
+ return attempts >= exports.MAX_SYNC_ATTEMPTS || error?.name === 'TypeError'
114
+ }
115
+ exports.isAppStateSyncIrrecoverable = isAppStateSyncIrrecoverable
116
+ const encodeSyncdPatch = async (
117
+ { type, index, syncAction, apiVersion, operation },
118
+ myAppStateKeyId,
119
+ state,
120
+ getAppStateSyncKey
121
+ ) => {
122
+ const key = !!myAppStateKeyId ? await getAppStateSyncKey(myAppStateKeyId) : undefined
123
+ if (!key) {
124
+ throw new boom_1.Boom(`myAppStateKey ("${myAppStateKeyId}") not present`, { statusCode: 404 })
125
+ }
126
+ const encKeyId = Buffer.from(myAppStateKeyId, 'base64')
127
+ state = { ...state, indexValueMap: { ...state.indexValueMap } }
128
+ const indexBuffer = Buffer.from(JSON.stringify(index))
129
+ const dataProto = index_js_1.proto.SyncActionData.fromObject({
130
+ index: indexBuffer,
131
+ value: syncAction,
132
+ padding: new Uint8Array(0),
133
+ version: apiVersion
134
+ })
135
+ const encoded = index_js_1.proto.SyncActionData.encode(dataProto).finish()
136
+ const keyValue = mutationKeys(key.keyData)
137
+ const encValue = (0, crypto_1.aesEncrypt)(encoded, keyValue.valueEncryptionKey)
138
+ const valueMac = generateMac(operation, encValue, encKeyId, keyValue.valueMacKey)
139
+ const indexMac = (0, crypto_1.hmacSign)(indexBuffer, keyValue.indexKey)
140
+ // update LT hash
141
+ const generator = makeLtHashGenerator(state)
142
+ generator.mix({ indexMac, valueMac, operation })
143
+ Object.assign(state, generator.finish())
144
+ state.version += 1
145
+ const snapshotMac = generateSnapshotMac(state.hash, state.version, type, keyValue.snapshotMacKey)
146
+ const patch = {
147
+ patchMac: generatePatchMac(snapshotMac, [valueMac], state.version, type, keyValue.patchMacKey),
148
+ snapshotMac: snapshotMac,
149
+ keyId: { id: encKeyId },
150
+ mutations: [
151
+ {
152
+ operation: operation,
153
+ record: {
154
+ index: {
155
+ blob: indexMac
156
+ },
157
+ value: {
158
+ blob: Buffer.concat([encValue, valueMac])
159
+ },
160
+ keyId: { id: encKeyId }
161
+ }
162
+ }
163
+ ]
164
+ }
165
+ const base64Index = indexMac.toString('base64')
166
+ state.indexValueMap[base64Index] = { valueMac }
167
+ return { patch, state }
168
+ }
169
+ exports.encodeSyncdPatch = encodeSyncdPatch
170
+ const decodeSyncdMutations = async (msgMutations, initialState, getAppStateSyncKey, onMutation, validateMacs) => {
171
+ const ltGenerator = makeLtHashGenerator(initialState)
172
+ const derivedKeyCache = new Map()
173
+ // indexKey used to HMAC sign record.index.blob
174
+ // valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32]
175
+ // the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId
176
+ for (const msgMutation of msgMutations) {
177
+ // if it's a syncdmutation, get the operation property
178
+ // otherwise, if it's only a record -- it'll be a SET mutation
179
+ const operation =
180
+ 'operation' in msgMutation ? msgMutation.operation : index_js_1.proto.SyncdMutation.SyncdOperation.SET
181
+ const record = 'record' in msgMutation && !!msgMutation.record ? msgMutation.record : msgMutation
182
+ const key = await getKey(record.keyId.id)
183
+ const content = record.value.blob
184
+ const encContent = content.subarray(0, -32)
185
+ const ogValueMac = content.subarray(-32)
186
+ if (validateMacs) {
187
+ const contentHmac = generateMac(operation, encContent, record.keyId.id, key.valueMacKey)
188
+ if (Buffer.compare(contentHmac, ogValueMac) !== 0) {
189
+ throw new boom_1.Boom('HMAC content verification failed')
190
+ }
191
+ }
192
+ const result = (0, crypto_1.aesDecrypt)(encContent, key.valueEncryptionKey)
193
+ const syncAction = index_js_1.proto.SyncActionData.decode(result)
194
+ if (validateMacs) {
195
+ const hmac = (0, crypto_1.hmacSign)(syncAction.index, key.indexKey)
196
+ if (Buffer.compare(hmac, record.index.blob) !== 0) {
197
+ throw new boom_1.Boom('HMAC index verification failed')
198
+ }
199
+ }
200
+ const indexStr = Buffer.from(syncAction.index).toString()
201
+ onMutation({ syncAction, index: JSON.parse(indexStr) })
202
+ ltGenerator.mix({
203
+ indexMac: record.index.blob,
204
+ valueMac: ogValueMac,
205
+ operation: operation
206
+ })
207
+ }
208
+ return ltGenerator.finish()
209
+ async function getKey(keyId) {
210
+ const base64Key = Buffer.from(keyId).toString('base64')
211
+ const cached = derivedKeyCache.get(base64Key)
212
+ if (cached) {
213
+ return cached
214
+ }
215
+ const keyEnc = await getAppStateSyncKey(base64Key)
216
+ if (!keyEnc) {
217
+ throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`, {
218
+ statusCode: 404,
219
+ data: { msgMutations }
220
+ })
221
+ }
222
+ const keys = mutationKeys(keyEnc.keyData)
223
+ derivedKeyCache.set(base64Key, keys)
224
+ return keys
225
+ }
226
+ }
227
+ exports.decodeSyncdMutations = decodeSyncdMutations
228
+ const decodeSyncdPatch = async (msg, name, initialState, getAppStateSyncKey, onMutation, validateMacs) => {
229
+ if (validateMacs) {
230
+ const base64Key = Buffer.from(msg.keyId.id).toString('base64')
231
+ const mainKeyObj = await getAppStateSyncKey(base64Key)
232
+ if (!mainKeyObj) {
233
+ throw new boom_1.Boom(`failed to find key "${base64Key}" to decode patch`, { statusCode: 404, data: { msg } })
234
+ }
235
+ const mainKey = mutationKeys(mainKeyObj.keyData)
236
+ const mutationmacs = msg.mutations.map(mutation => mutation.record.value.blob.slice(-32))
237
+ const patchMac = generatePatchMac(
238
+ msg.snapshotMac,
239
+ mutationmacs,
240
+ (0, generics_1.toNumber)(msg.version.version),
241
+ name,
242
+ mainKey.patchMacKey
243
+ )
244
+ if (Buffer.compare(patchMac, msg.patchMac) !== 0) {
245
+ throw new boom_1.Boom('Invalid patch mac')
246
+ }
247
+ }
248
+ const result = await (0, exports.decodeSyncdMutations)(
249
+ msg.mutations,
250
+ initialState,
251
+ getAppStateSyncKey,
252
+ onMutation,
253
+ validateMacs
254
+ )
255
+ return result
256
+ }
257
+ exports.decodeSyncdPatch = decodeSyncdPatch
258
+ const extractSyncdPatches = async (result, options) => {
259
+ const syncNode = (0, WABinary_1.getBinaryNodeChild)(result, 'sync')
260
+ const collectionNodes = (0, WABinary_1.getBinaryNodeChildren)(syncNode, 'collection')
261
+ const final = {}
262
+ await Promise.all(
263
+ collectionNodes.map(async collectionNode => {
264
+ const patchesNode = (0, WABinary_1.getBinaryNodeChild)(collectionNode, 'patches')
265
+ const patches = (0, WABinary_1.getBinaryNodeChildren)(patchesNode || collectionNode, 'patch')
266
+ const snapshotNode = (0, WABinary_1.getBinaryNodeChild)(collectionNode, 'snapshot')
267
+ const syncds = []
268
+ const name = collectionNode.attrs.name
269
+ const hasMorePatches = collectionNode.attrs.has_more_patches === 'true'
270
+ let snapshot = undefined
271
+ if (snapshotNode && !!snapshotNode.content) {
272
+ if (!Buffer.isBuffer(snapshotNode)) {
273
+ snapshotNode.content = Buffer.from(Object.values(snapshotNode.content))
274
+ }
275
+ const blobRef = index_js_1.proto.ExternalBlobReference.decode(snapshotNode.content)
276
+ const data = await (0, exports.downloadExternalBlob)(blobRef, options)
277
+ snapshot = index_js_1.proto.SyncdSnapshot.decode(data)
278
+ }
279
+ for (let { content } of patches) {
280
+ if (content) {
281
+ if (!Buffer.isBuffer(content)) {
282
+ content = Buffer.from(Object.values(content))
283
+ }
284
+ const syncd = index_js_1.proto.SyncdPatch.decode(content)
285
+ if (!syncd.version) {
286
+ syncd.version = { version: +collectionNode.attrs.version + 1 }
287
+ }
288
+ syncds.push(syncd)
289
+ }
290
+ }
291
+ final[name] = { patches: syncds, hasMorePatches, snapshot }
292
+ })
293
+ )
294
+ return final
295
+ }
296
+ exports.extractSyncdPatches = extractSyncdPatches
297
+ const downloadExternalBlob = async (blob, options) => {
298
+ const stream = await (0, messages_media_1.downloadContentFromMessage)(blob, 'md-app-state', { options })
299
+ const bufferArray = []
300
+ for await (const chunk of stream) {
301
+ bufferArray.push(chunk)
302
+ }
303
+ return Buffer.concat(bufferArray)
304
+ }
305
+ exports.downloadExternalBlob = downloadExternalBlob
306
+ const downloadExternalPatch = async (blob, options) => {
307
+ const buffer = await (0, exports.downloadExternalBlob)(blob, options)
308
+ const syncData = index_js_1.proto.SyncdMutations.decode(buffer)
309
+ return syncData
310
+ }
311
+ exports.downloadExternalPatch = downloadExternalPatch
312
+ const decodeSyncdSnapshot = async (name, snapshot, getAppStateSyncKey, minimumVersionNumber, validateMacs = true) => {
313
+ const newState = (0, exports.newLTHashState)()
314
+ newState.version = (0, generics_1.toNumber)(snapshot.version.version)
315
+ const mutationMap = {}
316
+ const areMutationsRequired = typeof minimumVersionNumber === 'undefined' || newState.version > minimumVersionNumber
317
+ const { hash, indexValueMap } = await (0, exports.decodeSyncdMutations)(
318
+ snapshot.records,
319
+ newState,
320
+ getAppStateSyncKey,
321
+ areMutationsRequired
322
+ ? mutation => {
323
+ const index = mutation.syncAction.index?.toString()
324
+ mutationMap[index] = mutation
325
+ }
326
+ : () => {},
327
+ validateMacs
328
+ )
329
+ newState.hash = hash
330
+ newState.indexValueMap = indexValueMap
331
+ if (validateMacs) {
332
+ const base64Key = Buffer.from(snapshot.keyId.id).toString('base64')
333
+ const keyEnc = await getAppStateSyncKey(base64Key)
334
+ if (!keyEnc) {
335
+ throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`)
336
+ }
337
+ const result = mutationKeys(keyEnc.keyData)
338
+ const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey)
339
+ if (Buffer.compare(snapshot.mac, computedSnapshotMac) !== 0) {
340
+ throw new boom_1.Boom(`failed to verify LTHash at ${newState.version} of ${name} from snapshot`)
341
+ }
342
+ }
343
+ return {
344
+ state: newState,
345
+ mutationMap
346
+ }
347
+ }
348
+ exports.decodeSyncdSnapshot = decodeSyncdSnapshot
349
+ const decodePatches = async (
350
+ name,
351
+ syncds,
352
+ initial,
353
+ getAppStateSyncKey,
354
+ options,
355
+ minimumVersionNumber,
356
+ logger,
357
+ validateMacs = true
358
+ ) => {
359
+ const newState = {
360
+ ...initial,
361
+ indexValueMap: { ...initial.indexValueMap }
362
+ }
363
+ const mutationMap = {}
364
+ for (const syncd of syncds) {
365
+ const { version, keyId, snapshotMac } = syncd
366
+ if (syncd.externalMutations) {
367
+ logger?.trace({ name, version }, 'downloading external patch')
368
+ const ref = await (0, exports.downloadExternalPatch)(syncd.externalMutations, options)
369
+ logger?.debug({ name, version, mutations: ref.mutations.length }, 'downloaded external patch')
370
+ syncd.mutations?.push(...ref.mutations)
371
+ }
372
+ const patchVersion = (0, generics_1.toNumber)(version.version)
373
+ newState.version = patchVersion
374
+ const shouldMutate = typeof minimumVersionNumber === 'undefined' || patchVersion > minimumVersionNumber
375
+ const decodeResult = await (0, exports.decodeSyncdPatch)(
376
+ syncd,
377
+ name,
378
+ newState,
379
+ getAppStateSyncKey,
380
+ shouldMutate
381
+ ? mutation => {
382
+ const index = mutation.syncAction.index?.toString()
383
+ mutationMap[index] = mutation
384
+ }
385
+ : () => {},
386
+ true
387
+ )
388
+ newState.hash = decodeResult.hash
389
+ newState.indexValueMap = decodeResult.indexValueMap
390
+ if (validateMacs) {
391
+ const base64Key = Buffer.from(keyId.id).toString('base64')
392
+ const keyEnc = await getAppStateSyncKey(base64Key)
393
+ if (!keyEnc) {
394
+ throw new boom_1.Boom(`failed to find key "${base64Key}" to decode mutation`)
395
+ }
396
+ const result = mutationKeys(keyEnc.keyData)
397
+ const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey)
398
+ if (Buffer.compare(snapshotMac, computedSnapshotMac) !== 0) {
399
+ throw new boom_1.Boom(`failed to verify LTHash at ${newState.version} of ${name}`)
400
+ }
401
+ }
402
+ // clear memory used up by the mutations
403
+ syncd.mutations = []
404
+ }
405
+ return { state: newState, mutationMap }
406
+ }
407
+ exports.decodePatches = decodePatches
408
+ const chatModificationToAppPatch = (mod, jid) => {
409
+ const OP = index_js_1.proto.SyncdMutation.SyncdOperation
410
+ const getMessageRange = lastMessages => {
411
+ let messageRange
412
+ if (Array.isArray(lastMessages)) {
413
+ const lastMsg = lastMessages[lastMessages.length - 1]
414
+ messageRange = {
415
+ lastMessageTimestamp: lastMsg?.messageTimestamp,
416
+ messages: lastMessages?.length
417
+ ? lastMessages.map(m => {
418
+ if (!m.key?.id || !m.key?.remoteJid) {
419
+ throw new boom_1.Boom('Incomplete key', { statusCode: 400, data: m })
420
+ }
421
+ if ((0, WABinary_1.isJidGroup)(m.key.remoteJid) && !m.key.fromMe && !m.key.participant) {
422
+ throw new boom_1.Boom('Expected not from me message to have participant', { statusCode: 400, data: m })
423
+ }
424
+ if (!m.messageTimestamp || !(0, generics_1.toNumber)(m.messageTimestamp)) {
425
+ throw new boom_1.Boom('Missing timestamp in last message list', { statusCode: 400, data: m })
426
+ }
427
+ if (m.key.participant) {
428
+ m.key.participant = (0, WABinary_1.jidNormalizedUser)(m.key.participant)
429
+ }
430
+ return m
431
+ })
432
+ : undefined
433
+ }
434
+ } else {
435
+ messageRange = lastMessages
436
+ }
437
+ return messageRange
438
+ }
439
+ let patch
440
+ if ('mute' in mod) {
441
+ patch = {
442
+ syncAction: {
443
+ muteAction: {
444
+ muted: !!mod.mute,
445
+ muteEndTimestamp: mod.mute || undefined
446
+ }
447
+ },
448
+ index: ['mute', jid],
449
+ type: 'regular_high',
450
+ apiVersion: 2,
451
+ operation: OP.SET
452
+ }
453
+ } else if ('archive' in mod) {
454
+ patch = {
455
+ syncAction: {
456
+ archiveChatAction: {
457
+ archived: !!mod.archive,
458
+ messageRange: getMessageRange(mod.lastMessages)
459
+ }
460
+ },
461
+ index: ['archive', jid],
462
+ type: 'regular_low',
463
+ apiVersion: 3,
464
+ operation: OP.SET
465
+ }
466
+ } else if ('markRead' in mod) {
467
+ patch = {
468
+ syncAction: {
469
+ markChatAsReadAction: {
470
+ read: mod.markRead,
471
+ messageRange: getMessageRange(mod.lastMessages)
472
+ }
473
+ },
474
+ index: ['markChatAsRead', jid],
475
+ type: 'regular_low',
476
+ apiVersion: 3,
477
+ operation: OP.SET
478
+ }
479
+ } else if ('deleteForMe' in mod) {
480
+ const { timestamp, key, deleteMedia } = mod.deleteForMe
481
+ patch = {
482
+ syncAction: {
483
+ deleteMessageForMeAction: {
484
+ deleteMedia,
485
+ messageTimestamp: timestamp
486
+ }
487
+ },
488
+ index: ['deleteMessageForMe', jid, key.id, key.fromMe ? '1' : '0', '0'],
489
+ type: 'regular_high',
490
+ apiVersion: 3,
491
+ operation: OP.SET
492
+ }
493
+ } else if ('clear' in mod) {
494
+ patch = {
495
+ syncAction: {
496
+ clearChatAction: {
497
+ messageRange: getMessageRange(mod.lastMessages)
498
+ }
499
+ },
500
+ index: ['clearChat', jid, '1' /*the option here is 0 when keep starred messages is enabled*/, '0'],
501
+ type: 'regular_high',
502
+ apiVersion: 6,
503
+ operation: OP.SET
504
+ }
505
+ } else if ('pin' in mod) {
506
+ patch = {
507
+ syncAction: {
508
+ pinAction: {
509
+ pinned: !!mod.pin
510
+ }
511
+ },
512
+ index: ['pin_v1', jid],
513
+ type: 'regular_low',
514
+ apiVersion: 5,
515
+ operation: OP.SET
516
+ }
517
+ } else if ('contact' in mod) {
518
+ patch = {
519
+ syncAction: {
520
+ contactAction: mod.contact || {}
521
+ },
522
+ index: ['contact', jid],
523
+ type: 'critical_unblock_low',
524
+ apiVersion: 2,
525
+ operation: mod.contact ? OP.SET : OP.REMOVE
526
+ }
527
+ } else if ('disableLinkPreviews' in mod) {
528
+ patch = {
529
+ syncAction: {
530
+ privacySettingDisableLinkPreviewsAction: mod.disableLinkPreviews || {}
531
+ },
532
+ index: ['setting_disableLinkPreviews'],
533
+ type: 'regular',
534
+ apiVersion: 8,
535
+ operation: OP.SET
536
+ }
537
+ } else if ('star' in mod) {
538
+ const key = mod.star.messages[0]
539
+ patch = {
540
+ syncAction: {
541
+ starAction: {
542
+ starred: !!mod.star.star
543
+ }
544
+ },
545
+ index: ['star', jid, key.id, key.fromMe ? '1' : '0', '0'],
546
+ type: 'regular_low',
547
+ apiVersion: 2,
548
+ operation: OP.SET
549
+ }
550
+ } else if ('delete' in mod) {
551
+ patch = {
552
+ syncAction: {
553
+ deleteChatAction: {
554
+ messageRange: getMessageRange(mod.lastMessages)
555
+ }
556
+ },
557
+ index: ['deleteChat', jid, '1'],
558
+ type: 'regular_high',
559
+ apiVersion: 6,
560
+ operation: OP.SET
561
+ }
562
+ } else if ('pushNameSetting' in mod) {
563
+ patch = {
564
+ syncAction: {
565
+ pushNameSetting: {
566
+ name: mod.pushNameSetting
567
+ }
568
+ },
569
+ index: ['setting_pushName'],
570
+ type: 'critical_block',
571
+ apiVersion: 1,
572
+ operation: OP.SET
573
+ }
574
+ } else if ('quickReply' in mod) {
575
+ patch = {
576
+ syncAction: {
577
+ quickReplyAction: {
578
+ count: 0,
579
+ deleted: mod.quickReply.deleted || false,
580
+ keywords: [],
581
+ message: mod.quickReply.message || '',
582
+ shortcut: mod.quickReply.shortcut || ''
583
+ }
584
+ },
585
+ index: ['quick_reply', mod.quickReply.timestamp || String(Math.floor(Date.now() / 1000))],
586
+ type: 'regular',
587
+ apiVersion: 2,
588
+ operation: OP.SET
589
+ }
590
+ } else if ('addLabel' in mod) {
591
+ patch = {
592
+ syncAction: {
593
+ labelEditAction: {
594
+ name: mod.addLabel.name,
595
+ color: mod.addLabel.color,
596
+ predefinedId: mod.addLabel.predefinedId,
597
+ deleted: mod.addLabel.deleted
598
+ }
599
+ },
600
+ index: ['label_edit', mod.addLabel.id],
601
+ type: 'regular',
602
+ apiVersion: 3,
603
+ operation: OP.SET
604
+ }
605
+ } else if ('addChatLabel' in mod) {
606
+ patch = {
607
+ syncAction: {
608
+ labelAssociationAction: {
609
+ labeled: true
610
+ }
611
+ },
612
+ index: [LabelAssociation_1.LabelAssociationType.Chat, mod.addChatLabel.labelId, jid],
613
+ type: 'regular',
614
+ apiVersion: 3,
615
+ operation: OP.SET
616
+ }
617
+ } else if ('removeChatLabel' in mod) {
618
+ patch = {
619
+ syncAction: {
620
+ labelAssociationAction: {
621
+ labeled: false
622
+ }
623
+ },
624
+ index: [LabelAssociation_1.LabelAssociationType.Chat, mod.removeChatLabel.labelId, jid],
625
+ type: 'regular',
626
+ apiVersion: 3,
627
+ operation: OP.SET
628
+ }
629
+ } else if ('addMessageLabel' in mod) {
630
+ patch = {
631
+ syncAction: {
632
+ labelAssociationAction: {
633
+ labeled: true
634
+ }
635
+ },
636
+ index: [
637
+ LabelAssociation_1.LabelAssociationType.Message,
638
+ mod.addMessageLabel.labelId,
639
+ jid,
640
+ mod.addMessageLabel.messageId,
641
+ '0',
642
+ '0'
643
+ ],
644
+ type: 'regular',
645
+ apiVersion: 3,
646
+ operation: OP.SET
647
+ }
648
+ } else if ('removeMessageLabel' in mod) {
649
+ patch = {
650
+ syncAction: {
651
+ labelAssociationAction: {
652
+ labeled: false
653
+ }
654
+ },
655
+ index: [
656
+ LabelAssociation_1.LabelAssociationType.Message,
657
+ mod.removeMessageLabel.labelId,
658
+ jid,
659
+ mod.removeMessageLabel.messageId,
660
+ '0',
661
+ '0'
662
+ ],
663
+ type: 'regular',
664
+ apiVersion: 3,
665
+ operation: OP.SET
666
+ }
667
+ } else if ('markAsUnread' in mod) {
668
+ patch = {
669
+ syncAction: {
670
+ markChatAsReadAction: {
671
+ read: false,
672
+ messageRange: getMessageRange(mod.lastMessages)
673
+ }
674
+ },
675
+ index: ['markChatAsRead', jid],
676
+ type: 'regular_low',
677
+ apiVersion: 3,
678
+ operation: OP.SET
679
+ }
680
+ } else if ('setChatEphemeral' in mod) {
681
+ const duration =
682
+ typeof mod.setChatEphemeral === 'number' ? mod.setChatEphemeral : (mod.setChatEphemeral?.duration ?? 0)
683
+ patch = {
684
+ syncAction: {
685
+ chatEphemeralAction: {
686
+ ephemeralExpiration: duration,
687
+ ephemeralSettingTimestamp: Date.now()
688
+ }
689
+ },
690
+ index: ['ephemeral', jid],
691
+ type: 'regular',
692
+ apiVersion: 1,
693
+ operation: OP.SET
694
+ }
695
+ } else if ('silenceChat' in mod) {
696
+ patch = {
697
+ syncAction: {
698
+ muteAction: {
699
+ muted: mod.silenceChat.silent !== false,
700
+ muteEndTimestamp: mod.silenceChat.until ?? null
701
+ }
702
+ },
703
+ index: ['mute', jid],
704
+ type: 'regular_high',
705
+ apiVersion: 2,
706
+ operation: OP.SET
707
+ }
708
+ } else if ('muteStatus' in mod) {
709
+ patch = {
710
+ syncAction: {
711
+ userStatusMuteAction: { muted: mod.muteStatus.muted !== false }
712
+ },
713
+ index: ['user_status_mute', jid],
714
+ type: 'regular',
715
+ apiVersion: 1,
716
+ operation: OP.SET
717
+ }
718
+ } else if ('favorite' in mod) {
719
+ patch = {
720
+ syncAction: {
721
+ favoritesAction: { isFavorite: !!mod.favorite.isFavorite }
722
+ },
723
+ index: ['favorites', jid],
724
+ type: 'regular',
725
+ apiVersion: 1,
726
+ operation: OP.SET
727
+ }
728
+ } else if ('reorderLabel' in mod) {
729
+ patch = {
730
+ syncAction: {
731
+ labelReorderingAction: { sortedLabelIds: mod.reorderLabel.sortedLabelIds || [] }
732
+ },
733
+ index: ['label_reordering'],
734
+ type: 'regular',
735
+ apiVersion: 1,
736
+ operation: OP.SET
737
+ }
738
+ } else if ('deleteCallLog' in mod) {
739
+ patch = {
740
+ syncAction: {
741
+ deleteIndividualCallLog: { callId: mod.deleteCallLog.callId || mod.deleteCallLog }
742
+ },
743
+ index: ['delete_call_log', jid],
744
+ type: 'regular',
745
+ apiVersion: 1,
746
+ operation: OP.SET
747
+ }
748
+ } else if ('noteEdit' in mod) {
749
+ patch = {
750
+ syncAction: {
751
+ noteEditAction: { note: mod.noteEdit.note || mod.noteEdit, deleted: mod.noteEdit.deleted ?? false }
752
+ },
753
+ index: ['note_edit', jid],
754
+ type: 'regular',
755
+ apiVersion: 1,
756
+ operation: OP.SET
757
+ }
758
+ } else if ('aiThreadRename' in mod) {
759
+ patch = {
760
+ syncAction: {
761
+ aiThreadRenameAction: {
762
+ newTitle: mod.aiThreadRename.title || mod.aiThreadRename
763
+ }
764
+ },
765
+ index: ['ai_thread_rename', jid],
766
+ type: 'regular_high',
767
+ apiVersion: 1,
768
+ operation: OP.SET
769
+ }
770
+ } else if ('threadPin' in mod) {
771
+ patch = {
772
+ syncAction: {
773
+ threadPinAction: {
774
+ pinned: !!mod.threadPin.pinned
775
+ }
776
+ },
777
+ index: ['thread_pin', jid, mod.threadPin.messageId || ''],
778
+ type: 'regular_low',
779
+ apiVersion: 1,
780
+ operation: OP.SET
781
+ }
782
+ } else if ('chatLock' in mod) {
783
+ patch = {
784
+ syncAction: {
785
+ lockChatAction: {
786
+ locked: mod.chatLock.locked !== false
787
+ }
788
+ },
789
+ index: ['lock_chat', jid],
790
+ type: 'regular_high',
791
+ apiVersion: 1,
792
+ operation: OP.SET
793
+ }
794
+ } else if ('wallpaper' in mod) {
795
+ const wallpaperData = mod.wallpaper || {}
796
+ patch = {
797
+ syncAction: {
798
+ chatCustomImageWallpaper: wallpaperData.remove
799
+ ? {}
800
+ : {
801
+ directPath: wallpaperData.directPath,
802
+ mediaKey: wallpaperData.mediaKey,
803
+ fileEncSha256: wallpaperData.fileEncSha256,
804
+ fileSha256: wallpaperData.fileSha256,
805
+ dimLevel: wallpaperData.dimLevel ?? 0
806
+ }
807
+ },
808
+ index: ['chat_custom_image_wallpaper', jid],
809
+ type: 'regular',
810
+ apiVersion: 1,
811
+ operation: wallpaperData.remove ? OP.REMOVE : OP.SET
812
+ }
813
+ } else if ('mediaVisibility' in mod) {
814
+ const visibilityEnum = index_js_1.proto.MediaVisibility
815
+ const visMap = { default: visibilityEnum.DEFAULT, off: visibilityEnum.OFF, on: visibilityEnum.ON }
816
+ const visibility =
817
+ typeof mod.mediaVisibility === 'string'
818
+ ? (visMap[mod.mediaVisibility.toLowerCase()] ?? visibilityEnum.DEFAULT)
819
+ : (mod.mediaVisibility ?? visibilityEnum.DEFAULT)
820
+ patch = {
821
+ syncAction: {
822
+ mediaVisibilityAction: { visibility }
823
+ },
824
+ index: ['media_visibility', jid],
825
+ type: 'regular',
826
+ apiVersion: 1,
827
+ operation: OP.SET
828
+ }
829
+ } else if ('privateProcessingSetting' in mod) {
830
+ const statusEnum = index_js_1.proto.SyncActionValue.PrivateProcessingSettingAction.PrivateProcessingStatus
831
+ const statusMap = { enabled: statusEnum.ENABLED, disabled: statusEnum.DISABLED }
832
+ const status =
833
+ typeof mod.privateProcessingSetting === 'string'
834
+ ? (statusMap[mod.privateProcessingSetting.toLowerCase()] ?? statusEnum.ENABLED)
835
+ : (mod.privateProcessingSetting.status ??
836
+ (mod.privateProcessingSetting.enabled ? statusEnum.ENABLED : statusEnum.DISABLED))
837
+ patch = {
838
+ syncAction: {
839
+ privateProcessingSettingAction: { privateProcessingStatus: status }
840
+ },
841
+ index: ['setting_private_processing'],
842
+ type: 'regular',
843
+ apiVersion: 1,
844
+ operation: OP.SET
845
+ }
846
+ } else {
847
+ throw new boom_1.Boom('not supported')
848
+ }
849
+ patch.syncAction.timestamp = Date.now()
850
+ return patch
851
+ }
852
+ exports.chatModificationToAppPatch = chatModificationToAppPatch
853
+ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
854
+ const isInitialSync = !!initialSyncOpts
855
+ const accountSettings = initialSyncOpts?.accountSettings
856
+ logger?.trace({ syncAction, initialSync: !!initialSyncOpts }, 'processing sync action')
857
+ const {
858
+ syncAction: { value: action },
859
+ index: [type, id, msgId, fromMe]
860
+ } = syncAction
861
+ if (action?.muteAction) {
862
+ ev.emit('chats.update', [
863
+ {
864
+ id,
865
+ muteEndTime: action.muteAction?.muted ? (0, generics_1.toNumber)(action.muteAction.muteEndTimestamp) : null,
866
+ conditional: getChatUpdateConditional(id, undefined)
867
+ }
868
+ ])
869
+ } else if (action?.archiveChatAction || type === 'archive' || type === 'unarchive') {
870
+ // okay so we've to do some annoying computation here
871
+ // when we're initially syncing the app state
872
+ // there are a few cases we need to handle
873
+ // 1. if the account unarchiveChats setting is true
874
+ // a. if the chat is archived, and no further messages have been received -- simple, keep archived
875
+ // b. if the chat was archived, and the user received messages from the other person afterwards
876
+ // then the chat should be marked unarchved --
877
+ // we compare the timestamp of latest message from the other person to determine this
878
+ // 2. if the account unarchiveChats setting is false -- then it doesn't matter,
879
+ // it'll always take an app state action to mark in unarchived -- which we'll get anyway
880
+ const archiveAction = action?.archiveChatAction
881
+ const isArchived = archiveAction ? archiveAction.archived : type === 'archive'
882
+ // // basically we don't need to fire an "archive" update if the chat is being marked unarchvied
883
+ // // this only applies for the initial sync
884
+ // if(isInitialSync && !isArchived) {
885
+ // isArchived = false
886
+ // }
887
+ const msgRange = !accountSettings?.unarchiveChats ? undefined : archiveAction?.messageRange
888
+ // logger?.debug({ chat: id, syncAction }, 'message range archive')
889
+ ev.emit('chats.update', [
890
+ {
891
+ id,
892
+ archived: isArchived,
893
+ conditional: getChatUpdateConditional(id, msgRange)
894
+ }
895
+ ])
896
+ } else if (action?.markChatAsReadAction) {
897
+ const markReadAction = action.markChatAsReadAction
898
+ // basically we don't need to fire an "read" update if the chat is being marked as read
899
+ // because the chat is read by default
900
+ // this only applies for the initial sync
901
+ const isNullUpdate = isInitialSync && markReadAction.read
902
+ ev.emit('chats.update', [
903
+ {
904
+ id,
905
+ unreadCount: isNullUpdate ? null : !!markReadAction?.read ? 0 : -1,
906
+ conditional: getChatUpdateConditional(id, markReadAction?.messageRange)
907
+ }
908
+ ])
909
+ } else if (action?.deleteMessageForMeAction || type === 'deleteMessageForMe') {
910
+ ev.emit('messages.delete', {
911
+ keys: [
912
+ {
913
+ remoteJid: id,
914
+ id: msgId,
915
+ fromMe: fromMe === '1'
916
+ }
917
+ ]
918
+ })
919
+ } else if (action?.contactAction) {
920
+ const results = (0, sync_action_utils_1.processContactAction)(action.contactAction, id, logger)
921
+ ;(0, sync_action_utils_1.emitSyncActionResults)(ev, results)
922
+ } else if (action?.pushNameSetting) {
923
+ const name = action?.pushNameSetting?.name
924
+ if (name && me?.name !== name) {
925
+ ev.emit('creds.update', { me: { ...me, name } })
926
+ }
927
+ } else if (action?.pinAction) {
928
+ ev.emit('chats.update', [
929
+ {
930
+ id,
931
+ pinned: action.pinAction?.pinned ? (0, generics_1.toNumber)(action.timestamp) : null,
932
+ conditional: getChatUpdateConditional(id, undefined)
933
+ }
934
+ ])
935
+ } else if (action?.unarchiveChatsSetting) {
936
+ const unarchiveChats = !!action.unarchiveChatsSetting.unarchiveChats
937
+ ev.emit('creds.update', { accountSettings: { unarchiveChats } })
938
+ logger?.info(`archive setting updated => '${action.unarchiveChatsSetting.unarchiveChats}'`)
939
+ if (accountSettings) {
940
+ accountSettings.unarchiveChats = unarchiveChats
941
+ }
942
+ } else if (action?.starAction || type === 'star') {
943
+ let starred = action?.starAction?.starred
944
+ if (typeof starred !== 'boolean') {
945
+ starred = syncAction.index[syncAction.index.length - 1] === '1'
946
+ }
947
+ ev.emit('messages.update', [
948
+ {
949
+ key: { remoteJid: id, id: msgId, fromMe: fromMe === '1' },
950
+ update: { starred }
951
+ }
952
+ ])
953
+ } else if (action?.deleteChatAction || type === 'deleteChat') {
954
+ if (!isInitialSync) {
955
+ ev.emit('chats.delete', [id])
956
+ }
957
+ } else if (action?.labelEditAction) {
958
+ const { name, color, deleted, predefinedId } = action.labelEditAction
959
+ ev.emit('labels.edit', {
960
+ id: id,
961
+ name: name,
962
+ color: color,
963
+ deleted: deleted,
964
+ predefinedId: predefinedId ? String(predefinedId) : undefined
965
+ })
966
+ } else if (action?.labelAssociationAction) {
967
+ ev.emit('labels.association', {
968
+ type: action.labelAssociationAction.labeled ? 'add' : 'remove',
969
+ association:
970
+ type === LabelAssociation_1.LabelAssociationType.Chat
971
+ ? {
972
+ type: LabelAssociation_1.LabelAssociationType.Chat,
973
+ chatId: syncAction.index[2],
974
+ labelId: syncAction.index[1]
975
+ }
976
+ : {
977
+ type: LabelAssociation_1.LabelAssociationType.Message,
978
+ chatId: syncAction.index[2],
979
+ messageId: syncAction.index[3],
980
+ labelId: syncAction.index[1]
981
+ }
982
+ })
983
+ } else if (action?.localeSetting?.locale) {
984
+ ev.emit('settings.update', { setting: 'locale', value: action.localeSetting.locale })
985
+ } else if (action?.timeFormatAction) {
986
+ ev.emit('settings.update', { setting: 'timeFormat', value: action.timeFormatAction })
987
+ } else if (action?.pnForLidChatAction) {
988
+ if (action.pnForLidChatAction.pnJid) {
989
+ ev.emit('lid-mapping.update', { lid: id, pn: action.pnForLidChatAction.pnJid })
990
+ }
991
+ } else if (action?.privacySettingRelayAllCalls) {
992
+ ev.emit('settings.update', {
993
+ setting: 'privacySettingRelayAllCalls',
994
+ value: action.privacySettingRelayAllCalls
995
+ })
996
+ } else if (action?.statusPrivacy) {
997
+ ev.emit('settings.update', { setting: 'statusPrivacy', value: action.statusPrivacy })
998
+ } else if (action?.lockChatAction) {
999
+ ev.emit('chats.lock', { id: id, locked: !!action.lockChatAction.locked })
1000
+ } else if (action?.privacySettingDisableLinkPreviewsAction) {
1001
+ ev.emit('settings.update', {
1002
+ setting: 'disableLinkPreviews',
1003
+ value: action.privacySettingDisableLinkPreviewsAction
1004
+ })
1005
+ } else if (action?.notificationActivitySettingAction?.notificationActivitySetting) {
1006
+ ev.emit('settings.update', {
1007
+ setting: 'notificationActivitySetting',
1008
+ value: action.notificationActivitySettingAction.notificationActivitySetting
1009
+ })
1010
+ } else if (action?.lidContactAction) {
1011
+ ev.emit('contacts.upsert', [
1012
+ {
1013
+ id: id,
1014
+ name:
1015
+ action.lidContactAction.fullName ||
1016
+ action.lidContactAction.firstName ||
1017
+ action.lidContactAction.username ||
1018
+ undefined,
1019
+ username: action.lidContactAction.username || undefined,
1020
+ lid: id,
1021
+ phoneNumber: undefined
1022
+ }
1023
+ ])
1024
+ } else if (action?.privacySettingChannelsPersonalisedRecommendationAction) {
1025
+ ev.emit('settings.update', {
1026
+ setting: 'channelsPersonalisedRecommendation',
1027
+ value: action.privacySettingChannelsPersonalisedRecommendationAction
1028
+ })
1029
+ } else if (action?.aiThreadRenameAction) {
1030
+ ev.emit('chats.update', [
1031
+ {
1032
+ id,
1033
+ name: action.aiThreadRenameAction.newTitle,
1034
+ conditional: getChatUpdateConditional(id, undefined)
1035
+ }
1036
+ ])
1037
+ } else if (action?.threadPinAction) {
1038
+ ev.emit('chats.update', [
1039
+ {
1040
+ id,
1041
+ pinned: action.threadPinAction.pinned ? (0, generics_1.toNumber)(action.timestamp) : null,
1042
+ conditional: getChatUpdateConditional(id, undefined)
1043
+ }
1044
+ ])
1045
+ } else if (action?.privateProcessingSettingAction) {
1046
+ ev.emit('settings.update', {
1047
+ setting: 'privateProcessing',
1048
+ value: action.privateProcessingSettingAction
1049
+ })
1050
+ } else if (action?.settingsSyncAction) {
1051
+ ev.emit('settings.update', {
1052
+ setting: 'clientSettings',
1053
+ value: action.settingsSyncAction
1054
+ })
1055
+ } else if (action?.musicUserIdAction) {
1056
+ ev.emit('settings.update', {
1057
+ setting: 'musicUserId',
1058
+ value: action.musicUserIdAction
1059
+ })
1060
+ } else if (action?.avatarUpdatedAction) {
1061
+ ev.emit('settings.update', {
1062
+ setting: 'avatarUpdated',
1063
+ value: action.avatarUpdatedAction
1064
+ })
1065
+ } else if (action?.recentEmojiWeightsAction) {
1066
+ ev.emit('settings.update', {
1067
+ setting: 'recentEmojiWeights',
1068
+ value: action.recentEmojiWeightsAction?.weights || []
1069
+ })
1070
+ } else if (action?.stickerAction) {
1071
+ ev.emit('settings.update', { setting: 'stickerSync', value: action.stickerAction })
1072
+ } else if (action?.removeRecentStickerAction) {
1073
+ ev.emit('settings.update', { setting: 'removedRecentSticker', value: action.removeRecentStickerAction })
1074
+ } else if (action?.userStatusMuteAction) {
1075
+ ev.emit('contacts.update', [{ id, statusMuted: !!action.userStatusMuteAction.muted }])
1076
+ } else if (action?.chatAssignment) {
1077
+ ev.emit('chats.update', [
1078
+ {
1079
+ id,
1080
+ chatAssignment: action.chatAssignment,
1081
+ conditional: getChatUpdateConditional(id, undefined)
1082
+ }
1083
+ ])
1084
+ } else if (action?.chatAssignmentOpenedStatus) {
1085
+ ev.emit('chats.update', [
1086
+ {
1087
+ id,
1088
+ chatAssignmentOpened: !!action.chatAssignmentOpenedStatus.opened,
1089
+ conditional: getChatUpdateConditional(id, undefined)
1090
+ }
1091
+ ])
1092
+ } else if (action?.callLogAction) {
1093
+ ev.emit('settings.update', { setting: 'callLog', value: action.callLogAction })
1094
+ } else if (action?.deleteIndividualCallLog) {
1095
+ ev.emit('settings.update', { setting: 'callLogDeleted', value: action.deleteIndividualCallLog })
1096
+ } else if (action?.labelReorderingAction) {
1097
+ ev.emit('labels.reorder', { labelIds: action.labelReorderingAction.sortedLabelIds || [] })
1098
+ } else if (action?.paymentInfoAction) {
1099
+ ev.emit('settings.update', { setting: 'paymentInfo', value: action.paymentInfoAction })
1100
+ } else if (action?.noteEditAction) {
1101
+ ev.emit('chats.update', [
1102
+ { id, draftNote: action.noteEditAction, conditional: getChatUpdateConditional(id, undefined) }
1103
+ ])
1104
+ } else if (action?.favoritesAction) {
1105
+ ev.emit('contacts.update', [{ id, favorite: !!action.favoritesAction.isFavorite }])
1106
+ } else if (action?.usernameChatStartMode) {
1107
+ ev.emit('settings.update', { setting: 'usernameChatStartMode', value: action.usernameChatStartMode })
1108
+ } else if (action?.maibaAiFeaturesControlAction) {
1109
+ ev.emit('settings.update', { setting: 'aiFeatures', value: action.maibaAiFeaturesControlAction })
1110
+ } else if (action?.statusPostOptInNotificationPreferencesAction) {
1111
+ ev.emit('settings.update', {
1112
+ setting: 'statusPostOptInNotifications',
1113
+ value: action.statusPostOptInNotificationPreferencesAction
1114
+ })
1115
+ } else if (action?.subscriptionsSyncV2Action) {
1116
+ ev.emit('settings.update', { setting: 'subscriptions', value: action.subscriptionsSyncV2Action })
1117
+ } else if (action?.newsletterSavedInterestsAction) {
1118
+ ev.emit('settings.update', { setting: 'newsletterSavedInterests', value: action.newsletterSavedInterestsAction })
1119
+ } else if (action?.interactiveMessageAction) {
1120
+ ev.emit('settings.update', { setting: 'interactiveMessageAction', value: action.interactiveMessageAction })
1121
+ } else if (action?.outContactAction) {
1122
+ const results = (0, sync_action_utils_1.processContactAction)(
1123
+ {
1124
+ fullName: action.outContactAction.fullName,
1125
+ firstName: action.outContactAction.firstName
1126
+ },
1127
+ id,
1128
+ logger
1129
+ )
1130
+ ;(0, sync_action_utils_1.emitSyncActionResults)(ev, results)
1131
+ } else if (action?.businessBroadcastListAction) {
1132
+ ev.emit('settings.update', { setting: 'broadcastList', value: action.businessBroadcastListAction })
1133
+ } else if (action?.customerDataAction) {
1134
+ ev.emit('chats.update', [
1135
+ { id, customerData: action.customerDataAction, conditional: getChatUpdateConditional(id, undefined) }
1136
+ ])
1137
+ } else if (action?.autoOrganizeBusinessChatSetting) {
1138
+ ev.emit('settings.update', { setting: 'autoOrganizeBusinessChat', value: action.autoOrganizeBusinessChatSetting })
1139
+ } else if (action?.chatLockSettings) {
1140
+ ev.emit('chats.update', [
1141
+ { id, chatLockSettings: action.chatLockSettings, conditional: getChatUpdateConditional(id, undefined) }
1142
+ ])
1143
+ } else if (action?.agentAction) {
1144
+ ev.emit('settings.update', { setting: 'agentAction', value: action.agentAction })
1145
+ } else if (action?.nuxAction) {
1146
+ ev.emit('settings.update', { setting: 'nux', value: action.nuxAction })
1147
+ } else if (action?.quickReplyAction) {
1148
+ ev.emit('settings.update', { setting: 'quickReply', value: action.quickReplyAction })
1149
+ } else if (action?.keyExpiration) {
1150
+ ev.emit('settings.update', { setting: 'keyExpiration', value: action.keyExpiration })
1151
+ } else if (action?.primaryFeature) {
1152
+ ev.emit('settings.update', { setting: 'primaryFeature', value: action.primaryFeature })
1153
+ } else if (action?.androidUnsupportedActions) {
1154
+ ev.emit('settings.update', { setting: 'androidUnsupported', value: action.androidUnsupportedActions })
1155
+ } else if (action?.subscriptionAction) {
1156
+ ev.emit('settings.update', { setting: 'subscription', value: action.subscriptionAction })
1157
+ } else if (action?.primaryVersionAction) {
1158
+ ev.emit('settings.update', { setting: 'primaryVersion', value: action.primaryVersionAction })
1159
+ } else if (action?.marketingMessageAction) {
1160
+ ev.emit('settings.update', { setting: 'marketingMessage', value: action.marketingMessageAction })
1161
+ } else if (action?.marketingMessageBroadcastAction) {
1162
+ ev.emit('settings.update', { setting: 'marketingMessageBroadcast', value: action.marketingMessageBroadcastAction })
1163
+ } else if (action?.externalWebBetaAction) {
1164
+ ev.emit('settings.update', { setting: 'externalWebBeta', value: action.externalWebBetaAction })
1165
+ } else if (action?.botWelcomeRequestAction) {
1166
+ ev.emit('settings.update', { setting: 'botWelcomeRequest', value: action.botWelcomeRequestAction })
1167
+ } else if (action?.customPaymentMethodsAction) {
1168
+ ev.emit('settings.update', { setting: 'customPaymentMethods', value: action.customPaymentMethodsAction })
1169
+ } else if (action?.wamoUserIdentifierAction) {
1170
+ ev.emit('settings.update', { setting: 'wamoUserIdentifier', value: action.wamoUserIdentifierAction })
1171
+ } else if (action?.merchantPaymentPartnerAction) {
1172
+ ev.emit('settings.update', { setting: 'merchantPaymentPartner', value: action.merchantPaymentPartnerAction })
1173
+ } else if (action?.waffleAccountLinkStateAction) {
1174
+ ev.emit('settings.update', { setting: 'waffleAccountLinkState', value: action.waffleAccountLinkStateAction })
1175
+ } else if (action?.ctwaPerCustomerDataSharingAction) {
1176
+ ev.emit('settings.update', { setting: 'ctwaDataSharing', value: action.ctwaPerCustomerDataSharingAction })
1177
+ } else if (action?.paymentTosAction) {
1178
+ ev.emit('settings.update', { setting: 'paymentTos', value: action.paymentTosAction })
1179
+ } else if (action?.detectedOutcomesStatusAction) {
1180
+ ev.emit('settings.update', { setting: 'detectedOutcomesStatus', value: action.detectedOutcomesStatusAction })
1181
+ } else if (action?.nctSaltSyncAction) {
1182
+ ev.emit('settings.update', { setting: 'nctSaltSync', value: action.nctSaltSyncAction })
1183
+ } else if (action?.businessBroadcastCampaignAction) {
1184
+ ev.emit('settings.update', { setting: 'businessBroadcastCampaign', value: action.businessBroadcastCampaignAction })
1185
+ } else if (action?.businessBroadcastInsightsAction) {
1186
+ ev.emit('settings.update', { setting: 'businessBroadcastInsights', value: action.businessBroadcastInsightsAction })
1187
+ } else if (action?.bizAiSettingsNudgeAction) {
1188
+ ev.emit('settings.update', { setting: 'bizAiSettingsNudge', value: action.bizAiSettingsNudgeAction })
1189
+ } else {
1190
+ logger?.debug({ syncAction, id }, 'unprocessable update')
1191
+ }
1192
+ function getChatUpdateConditional(id, msgRange) {
1193
+ return isInitialSync
1194
+ ? data => {
1195
+ const chat = data.historySets.chats[id] || data.chatUpserts[id]
1196
+ if (chat) {
1197
+ return msgRange ? isValidPatchBasedOnMessageRange(chat, msgRange) : true
1198
+ }
1199
+ }
1200
+ : undefined
1201
+ }
1202
+ function isValidPatchBasedOnMessageRange(chat, msgRange) {
1203
+ const lastMsgTimestamp = Number(msgRange?.lastMessageTimestamp || msgRange?.lastSystemMessageTimestamp || 0)
1204
+ const chatLastMsgTimestamp = Number(chat?.lastMessageRecvTimestamp || 0)
1205
+ return lastMsgTimestamp >= chatLastMsgTimestamp
1206
+ }
1207
+ }
1208
+ exports.processSyncAction = processSyncAction