@badzz88/baileys 8.4.6 → 8.4.9

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