@badzz88/baileys 8.5.3 → 8.5.4
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.
- package/README.md +1 -1
- package/WAProto/WAProto.proto +1256 -75
- package/WAProto/index.d.ts +36042 -19649
- package/WAProto/index.js +181990 -135645
- package/engine-requirements.js +7 -15
- package/lib/Defaults/index.js +22 -17
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +39 -59
- package/lib/Signal/lid-mapping.js +28 -10
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +3 -11
- package/lib/Socket/chats.js +668 -319
- package/lib/Socket/communities.js +85 -89
- package/lib/Socket/graphql.js +435 -228
- package/lib/Socket/groups.js +132 -211
- package/lib/Socket/index.js +9 -7
- package/lib/Socket/interactive-handler.js +117 -60
- package/lib/Socket/interop.js +56 -175
- package/lib/Socket/managed-account.js +79 -48
- package/lib/Socket/messages-recv.js +898 -340
- package/lib/Socket/messages-send.js +462 -160
- package/lib/Socket/mex.js +40 -53
- package/lib/Socket/newsletter.js +291 -295
- package/lib/Socket/privacy.js +77 -265
- package/lib/Socket/registration.js +164 -157
- package/lib/Socket/socket.js +181 -199
- package/lib/Socket/username.js +144 -227
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +25 -141
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Newsletter.js +96 -65
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +52 -57
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +2 -6
- package/lib/Utils/chat-utils.js +102 -38
- package/lib/Utils/crypto.js +54 -84
- package/lib/Utils/decode-wa-message.js +316 -83
- package/lib/Utils/event-buffer.js +37 -61
- package/lib/Utils/generics.js +157 -103
- package/lib/Utils/group-history.js +3 -12
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +1 -5
- package/lib/Utils/link-preview.js +3 -8
- package/lib/Utils/lt-hash.js +2 -35
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-inspect.js +393 -0
- package/lib/Utils/message-retry-manager.js +27 -92
- package/lib/Utils/messages-media.js +159 -130
- package/lib/Utils/messages.js +144 -566
- package/lib/Utils/meta-ai-msmsg.js +62 -202
- package/lib/Utils/noise-handler.js +19 -21
- package/lib/Utils/offline-node-processor.js +3 -8
- package/lib/Utils/pre-key-manager.js +13 -23
- package/lib/Utils/process-message.js +197 -106
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/signal.js +28 -63
- package/lib/Utils/stanza-ack.js +39 -12
- package/lib/Utils/sync-action-utils.js +4 -7
- package/lib/Utils/tc-token-utils.js +20 -25
- package/lib/Utils/use-multi-file-auth-state.js +30 -14
- package/lib/Utils/validate-connection.js +59 -19
- package/lib/WABinary/decode.js +50 -16
- package/lib/WABinary/encode.js +54 -224
- package/lib/WABinary/generic-utils.js +7 -97
- package/lib/WABinary/jid-utils.js +27 -14
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +58 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +13 -5
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -4
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +20 -7
- package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +865 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -2
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +2 -1
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/USyncBackoff.js +26 -0
- package/lib/WAUSync/USyncQuery.js +77 -6
- package/lib/WAUSync/USyncUser.js +16 -8
- package/lib/WAUSync/index.js +1 -0
- package/lib/antiban.js +608 -854
- package/lib/index.js +1 -1
- package/package.json +16 -20
- package/lib/Socket/luxu.js +0 -387
- package/lib/Socket/text-router.js +0 -83
- package/lib/Store/keyed-db.js +0 -108
- package/lib/Types/Mex.js +0 -112
- package/lib/Utils/command-loader.d.ts +0 -31
- package/lib/Utils/command-loader.js +0 -100
- package/lib/Utils/companion-reg-client-utils.js +0 -42
- package/lib/Utils/curve25519-js.js +0 -275
- package/lib/Utils/native-bridge.js +0 -82
- package/lib/Utils/session-pool.d.ts +0 -16
- package/lib/Utils/session-pool.js +0 -94
- package/lib/Utils/sticker.d.ts +0 -17
- package/lib/Utils/sticker.js +0 -145
- package/lib/Utils/view-once-cache.d.ts +0 -9
- package/lib/Utils/view-once-cache.js +0 -79
package/lib/Socket/chats.js
CHANGED
|
@@ -15,6 +15,7 @@ const State_1 = require('../Types/State')
|
|
|
15
15
|
const Utils_1 = require('../Utils')
|
|
16
16
|
const make_mutex_1 = require('../Utils/make-mutex')
|
|
17
17
|
const process_message_1 = __importDefault(require('../Utils/process-message'))
|
|
18
|
+
const { unwrapSecretEncryptedMessage } = require('../Utils/process-message')
|
|
18
19
|
const tc_token_utils_1 = require('../Utils/tc-token-utils')
|
|
19
20
|
const WABinary_1 = require('../WABinary')
|
|
20
21
|
const WAUSync_1 = require('../WAUSync')
|
|
@@ -62,45 +63,47 @@ const makeChatsSocket = config => {
|
|
|
62
63
|
} = sock
|
|
63
64
|
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping)
|
|
64
65
|
let privacySettings
|
|
65
|
-
|
|
66
|
+
|
|
66
67
|
const serverProps = {
|
|
67
|
-
|
|
68
|
+
|
|
68
69
|
privacyTokenOn1to1: true,
|
|
69
|
-
|
|
70
|
+
|
|
70
71
|
profilePicPrivacyToken: true,
|
|
71
|
-
|
|
72
|
+
|
|
72
73
|
lidTrustedTokenIssueToLid: false
|
|
73
74
|
}
|
|
74
75
|
let syncState = State_1.SyncState.Connecting
|
|
75
|
-
|
|
76
|
+
|
|
76
77
|
const messageMutex = (0, make_mutex_1.makeMutex)()
|
|
77
|
-
|
|
78
|
+
|
|
78
79
|
const receiptMutex = (0, make_mutex_1.makeMutex)()
|
|
79
|
-
|
|
80
|
+
|
|
80
81
|
const appStatePatchMutex = (0, make_mutex_1.makeMutex)()
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
const notificationMutex = (0, make_mutex_1.makeMutex)()
|
|
83
|
-
|
|
84
|
+
|
|
84
85
|
let awaitingSyncTimeout
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
const historySyncStatus = {
|
|
87
88
|
initialBootstrapComplete: false,
|
|
88
89
|
recentSyncComplete: false
|
|
89
90
|
}
|
|
90
91
|
let historySyncPausedTimeout
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
|
|
93
|
+
|
|
93
94
|
const blockedCollections = new Set()
|
|
95
|
+
|
|
96
|
+
const _nlServerIdCache = new Map()
|
|
94
97
|
const placeholderResendCache =
|
|
95
98
|
config.placeholderResendCache ||
|
|
96
99
|
new node_cache_1.default({
|
|
97
|
-
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
100
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
98
101
|
useClones: false
|
|
99
102
|
})
|
|
100
103
|
if (!config.placeholderResendCache) {
|
|
101
104
|
config.placeholderResendCache = placeholderResendCache
|
|
102
105
|
}
|
|
103
|
-
|
|
106
|
+
|
|
104
107
|
const getAppStateSyncKey = async keyId => {
|
|
105
108
|
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId])
|
|
106
109
|
return key
|
|
@@ -116,11 +119,67 @@ const makeChatsSocket = config => {
|
|
|
116
119
|
},
|
|
117
120
|
content: [{ tag: 'privacy', attrs: {} }]
|
|
118
121
|
})
|
|
119
|
-
|
|
122
|
+
const privacyNode = content?.[0]
|
|
123
|
+
privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(privacyNode, 'category')
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
const onlinePrivacy =
|
|
128
|
+
privacySettings['online'] ||
|
|
129
|
+
(0, WABinary_1.getBinaryNodeChild)(privacyNode, 'online_privacy_setting')?.attrs?.value ||
|
|
130
|
+
'all'
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
const enhancedBlockEnabled =
|
|
134
|
+
privacyNode?.attrs?.enhanced_block_enabled === 'true' ||
|
|
135
|
+
(0, WABinary_1.getBinaryNodeChild)(privacyNode, 'enhanced_block')?.attrs?.enabled === 'true'
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
const mdPrivacyV2 =
|
|
139
|
+
privacyNode?.attrs?.md_privacy_v2 === 'true' ||
|
|
140
|
+
(0, WABinary_1.getBinaryNodeChild)(privacyNode, 'md_privacy_v2')?.attrs?.value === 'true'
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
const syncdClearChatDeleteChatEnabled =
|
|
144
|
+
privacyNode?.attrs?.syncd_clear_chat_delete_chat_enabled === 'true' ||
|
|
145
|
+
(0, WABinary_1.getBinaryNodeChild)(privacyNode, 'syncd_clear_chat')?.attrs?.delete_chat_enabled === 'true'
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
const syncdAntiTamperingEnabled =
|
|
149
|
+
privacyNode?.attrs?.syncd_anti_tampering_fatal_exception_enabled === 'true' ||
|
|
150
|
+
(0, WABinary_1.getBinaryNodeChild)(privacyNode, 'syncd_anti_tampering')?.attrs?.fatal_exception_enabled ===
|
|
151
|
+
'true'
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
const keyRotationEnabled =
|
|
155
|
+
privacyNode?.attrs?.syncd_key_rotation_enabled === 'true' ||
|
|
156
|
+
(0, WABinary_1.getBinaryNodeChild)(privacyNode, 'syncd_key_rotation')?.attrs?.enabled === 'true'
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
ev.emit('settings.update', {
|
|
160
|
+
onlinePrivacy,
|
|
161
|
+
enhancedBlockEnabled,
|
|
162
|
+
mdPrivacyV2,
|
|
163
|
+
syncdClearChatDeleteChatEnabled,
|
|
164
|
+
...(syncdAntiTamperingEnabled ? { syncdAntiTamperingEnabled: true } : {}),
|
|
165
|
+
keyRotationEnabled
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
ev.emit('creds.update', {
|
|
170
|
+
privacySettings: {
|
|
171
|
+
onlinePrivacy,
|
|
172
|
+
enhancedBlockEnabled,
|
|
173
|
+
mdPrivacyV2,
|
|
174
|
+
syncdClearChatDeleteChatEnabled,
|
|
175
|
+
syncdAntiTamperingEnabled,
|
|
176
|
+
keyRotationEnabled
|
|
177
|
+
}
|
|
178
|
+
})
|
|
120
179
|
}
|
|
121
180
|
return privacySettings
|
|
122
181
|
}
|
|
123
|
-
|
|
182
|
+
|
|
124
183
|
const privacyQuery = async (name, value) => {
|
|
125
184
|
await query({
|
|
126
185
|
tag: 'iq',
|
|
@@ -161,11 +220,7 @@ const makeChatsSocket = config => {
|
|
|
161
220
|
const updateStatusPrivacy = async value => {
|
|
162
221
|
await privacyQuery('status', value)
|
|
163
222
|
}
|
|
164
|
-
|
|
165
|
-
* Fetch status privacy settings via `xmlns="status"` IQ GET.
|
|
166
|
-
* Returns the current distribution type and any custom lists.
|
|
167
|
-
* Source: GetStatusPrivacyJob.java, feature flag 3843 controls retry.
|
|
168
|
-
*/
|
|
223
|
+
|
|
169
224
|
const getStatusPrivacy = async () => {
|
|
170
225
|
const result = await query({
|
|
171
226
|
tag: 'iq',
|
|
@@ -179,31 +234,24 @@ const makeChatsSocket = config => {
|
|
|
179
234
|
const privacyNode = result?.content?.[0]
|
|
180
235
|
if (!privacyNode) return null
|
|
181
236
|
const lists = []
|
|
182
|
-
for (const listNode of
|
|
237
|
+
for (const listNode of privacyNode.content || []) {
|
|
183
238
|
const { type, id, listname, emoji, selected, deleted } = listNode.attrs || {}
|
|
184
239
|
const members = (listNode.content || []).map(u => u.attrs?.jid).filter(Boolean)
|
|
185
240
|
lists.push({ type, id, listname, emoji, selected: selected === 'true', deleted: deleted === 'true', members })
|
|
186
241
|
}
|
|
187
242
|
return lists
|
|
188
243
|
}
|
|
189
|
-
|
|
190
|
-
* Set status privacy via `xmlns="status"` IQ SET.
|
|
191
|
-
* Supports simple distribution types and contact-level whitelist/blacklist/customlist.
|
|
192
|
-
*
|
|
193
|
-
* @param {'contacts'|'whitelist'|'blacklist'|'null'} type - Distribution type
|
|
194
|
-
* @param {string[]} [jids] - JIDs for the list (whitelist/blacklist)
|
|
195
|
-
* @param {Array<{id: string, listname: string, emoji?: string, selected?: boolean, deleted?: boolean, members?: string[]}>} [customLists]
|
|
196
|
-
*/
|
|
244
|
+
|
|
197
245
|
const setStatusPrivacy = async (type, jids = [], customLists = []) => {
|
|
198
246
|
const content = []
|
|
199
|
-
|
|
247
|
+
|
|
200
248
|
const mainList = {
|
|
201
249
|
tag: 'list',
|
|
202
250
|
attrs: { type },
|
|
203
251
|
content: jids.map(jid => ({ tag: 'user', attrs: { jid }, content: [] }))
|
|
204
252
|
}
|
|
205
253
|
content.push(mainList)
|
|
206
|
-
|
|
254
|
+
|
|
207
255
|
for (const cl of customLists) {
|
|
208
256
|
const attrs = { type: 'customlist', id: cl.id, listname: cl.listname }
|
|
209
257
|
if (cl.emoji) attrs.emoji = cl.emoji
|
|
@@ -249,12 +297,7 @@ const makeChatsSocket = config => {
|
|
|
249
297
|
]
|
|
250
298
|
})
|
|
251
299
|
}
|
|
252
|
-
|
|
253
|
-
* Fetch broadcast list quota from the server.
|
|
254
|
-
* Source: BroadcastListQuotaProtocol.java — IQ xmlns="w:biz", 32s timeout.
|
|
255
|
-
*
|
|
256
|
-
* Returns: { messagesLeft, totalLimit, isHeavySender, startTs, endTs, resetTs }
|
|
257
|
-
*/
|
|
300
|
+
|
|
258
301
|
const fetchBroadcastListQuota = async () => {
|
|
259
302
|
const result = await query(
|
|
260
303
|
{
|
|
@@ -272,12 +315,35 @@ const makeChatsSocket = config => {
|
|
|
272
315
|
const timeframeNode = (0, WABinary_1.getBinaryNodeChild)(result, 'timeframe')
|
|
273
316
|
if (!limitsNode) return null
|
|
274
317
|
return {
|
|
275
|
-
messagesLeft: parseInt(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
318
|
+
messagesLeft: parseInt(
|
|
319
|
+
limitsNode.attrs?.messages_left ??
|
|
320
|
+
(0, WABinary_1.getBinaryNodeChild)(limitsNode, 'messages_left')?.content ??
|
|
321
|
+
'0',
|
|
322
|
+
10
|
|
323
|
+
),
|
|
324
|
+
totalLimit: parseInt(
|
|
325
|
+
limitsNode.attrs?.total_limit ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'total_limit')?.content ?? '0',
|
|
326
|
+
10
|
|
327
|
+
),
|
|
328
|
+
isHeavySender:
|
|
329
|
+
(limitsNode.attrs?.is_heavy_sender ??
|
|
330
|
+
(0, WABinary_1.getBinaryNodeChild)(limitsNode, 'is_heavy_sender')?.content) === 'true',
|
|
331
|
+
startTs: parseInt(
|
|
332
|
+
timeframeNode?.attrs?.start_ts_s ??
|
|
333
|
+
(0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'start_ts_s')?.content ??
|
|
334
|
+
'0',
|
|
335
|
+
10
|
|
336
|
+
),
|
|
337
|
+
endTs: parseInt(
|
|
338
|
+
timeframeNode?.attrs?.end_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'end_ts_s')?.content ?? '0',
|
|
339
|
+
10
|
|
340
|
+
),
|
|
341
|
+
resetTs: parseInt(
|
|
342
|
+
timeframeNode?.attrs?.reset_ts_s ??
|
|
343
|
+
(0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'reset_ts_s')?.content ??
|
|
344
|
+
'0',
|
|
345
|
+
10
|
|
346
|
+
)
|
|
281
347
|
}
|
|
282
348
|
}
|
|
283
349
|
|
|
@@ -312,11 +378,7 @@ const makeChatsSocket = config => {
|
|
|
312
378
|
}
|
|
313
379
|
return botList
|
|
314
380
|
}
|
|
315
|
-
|
|
316
|
-
* Get the global chat-blocking status (block messages from unknown accounts).
|
|
317
|
-
* Ported from WhatsApp Web's WASmaxPsaChatBlockGetRPC (xmlns `w:comms:chat`).
|
|
318
|
-
* @returns {Promise<'blocked' | 'unblocked' | undefined>}
|
|
319
|
-
*/
|
|
381
|
+
|
|
320
382
|
const getChatBlockingStatus = async () => {
|
|
321
383
|
const result = await query({
|
|
322
384
|
tag: 'iq',
|
|
@@ -332,12 +394,7 @@ const makeChatsSocket = config => {
|
|
|
332
394
|
(0, WABinary_1.getBinaryNodeChild)((0, WABinary_1.getBinaryNodeChild)(result, 'query'), 'blocking')
|
|
333
395
|
return blocking?.attrs?.status
|
|
334
396
|
}
|
|
335
|
-
|
|
336
|
-
* Set the global chat-blocking status (block messages from unknown accounts).
|
|
337
|
-
* Ported from WhatsApp Web's WASmaxPsaChatBlockSetRPC (xmlns `w:comms:chat`).
|
|
338
|
-
* @param {'block' | 'unblock'} action
|
|
339
|
-
* @returns {Promise<'blocked' | 'unblocked' | undefined>} the resulting status
|
|
340
|
-
*/
|
|
397
|
+
|
|
341
398
|
const updateChatBlockingStatus = async action => {
|
|
342
399
|
const result = await query({
|
|
343
400
|
tag: 'iq',
|
|
@@ -351,12 +408,7 @@ const makeChatsSocket = config => {
|
|
|
351
408
|
const blocking = (0, WABinary_1.getBinaryNodeChild)(result, 'blocking')
|
|
352
409
|
return blocking?.attrs?.status
|
|
353
410
|
}
|
|
354
|
-
|
|
355
|
-
* Get the user's pending TOS disclosures / notices.
|
|
356
|
-
* Ported from WhatsApp Web's WASmaxUserNoticeGetDisclosuresRPC (xmlns `tos`).
|
|
357
|
-
* @param {number} [t] last-seen disclosure timestamp
|
|
358
|
-
* @returns {Promise<Array<Record<string, string>>>} the `<notice>` attributes
|
|
359
|
-
*/
|
|
411
|
+
|
|
360
412
|
const getUserDisclosures = async (t = 0) => {
|
|
361
413
|
const result = await query({
|
|
362
414
|
tag: 'iq',
|
|
@@ -365,18 +417,65 @@ const makeChatsSocket = config => {
|
|
|
365
417
|
})
|
|
366
418
|
return (0, WABinary_1.getBinaryNodeChildren)(result, 'notice').map(n => ({ ...n.attrs }))
|
|
367
419
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
420
|
+
|
|
421
|
+
const acceptTosNotice = async (noticeId, result = '105') => {
|
|
422
|
+
await query({
|
|
423
|
+
tag: 'iq',
|
|
424
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'tos', type: 'set' },
|
|
425
|
+
content: [{ tag: 'trackable', attrs: { id: String(noticeId), result: String(result) } }]
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const reportSpam = async (jid, messages, spamFlow = 'contact_info_report', subject) => {
|
|
430
|
+
const msgNodes = (messages || []).map(m => ({
|
|
431
|
+
tag: 'message',
|
|
432
|
+
attrs: {
|
|
433
|
+
from: String(jid),
|
|
434
|
+
t: String(m.t),
|
|
435
|
+
id: String(m.id)
|
|
436
|
+
}
|
|
437
|
+
}))
|
|
438
|
+
await query({
|
|
439
|
+
tag: 'iq',
|
|
440
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'spam', type: 'set' },
|
|
441
|
+
content: [
|
|
442
|
+
{
|
|
443
|
+
tag: 'spam_list',
|
|
444
|
+
attrs: {
|
|
445
|
+
jid: String(jid),
|
|
446
|
+
spam_flow: String(spamFlow),
|
|
447
|
+
...(subject ? { subject: String(subject) } : {})
|
|
448
|
+
},
|
|
449
|
+
content: msgNodes
|
|
450
|
+
}
|
|
451
|
+
]
|
|
452
|
+
})
|
|
453
|
+
}
|
|
454
|
+
|
|
371
455
|
const getOptOutList = async () => {
|
|
372
456
|
return query({
|
|
373
457
|
tag: 'iq',
|
|
374
458
|
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'optoutlist', type: 'get' }
|
|
375
459
|
})
|
|
376
460
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
461
|
+
|
|
462
|
+
const signPrivateCredential = async blindedCredential => {
|
|
463
|
+
const result = await query({
|
|
464
|
+
tag: 'iq',
|
|
465
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'privatestats', type: 'get' },
|
|
466
|
+
content: [
|
|
467
|
+
{
|
|
468
|
+
tag: 'sign_credential',
|
|
469
|
+
attrs: { version: '1' },
|
|
470
|
+
content: [{ tag: 'blinded_credential', attrs: {}, content: blindedCredential }]
|
|
471
|
+
}
|
|
472
|
+
]
|
|
473
|
+
})
|
|
474
|
+
const signedNode = (0, WABinary_1.getBinaryNodeChild)(result, 'sign_credential')
|
|
475
|
+
const signedBytes = (0, WABinary_1.getBinaryNodeChild)(signedNode, 'signed_credential')
|
|
476
|
+
return signedBytes?.content instanceof Uint8Array ? Buffer.from(signedBytes.content) : undefined
|
|
477
|
+
}
|
|
478
|
+
|
|
380
479
|
const getPushConfig = async () => {
|
|
381
480
|
const result = await query({
|
|
382
481
|
tag: 'iq',
|
|
@@ -385,11 +484,7 @@ const makeChatsSocket = config => {
|
|
|
385
484
|
})
|
|
386
485
|
return (0, WABinary_1.getBinaryNodeChild)(result, 'settings')
|
|
387
486
|
}
|
|
388
|
-
|
|
389
|
-
* Set push-notification config (`urn:xmpp:whatsapp:push`). Mainly web push —
|
|
390
|
-
* pass the FCM-style config (platform / endpoint / auth / p256dh).
|
|
391
|
-
* @param {Record<string, string>} config
|
|
392
|
-
*/
|
|
487
|
+
|
|
393
488
|
const setPushConfig = async config => {
|
|
394
489
|
await query({
|
|
395
490
|
tag: 'iq',
|
|
@@ -402,7 +497,7 @@ const makeChatsSocket = config => {
|
|
|
402
497
|
for (const jid of jids) {
|
|
403
498
|
usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid))
|
|
404
499
|
}
|
|
405
|
-
const result = await
|
|
500
|
+
const result = await executeUSyncQuery(usyncQuery)
|
|
406
501
|
if (result) {
|
|
407
502
|
return result.list
|
|
408
503
|
}
|
|
@@ -412,12 +507,12 @@ const makeChatsSocket = config => {
|
|
|
412
507
|
for (const jid of jids) {
|
|
413
508
|
usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid))
|
|
414
509
|
}
|
|
415
|
-
const result = await
|
|
510
|
+
const result = await executeUSyncQuery(usyncQuery)
|
|
416
511
|
if (result) {
|
|
417
512
|
return result.list
|
|
418
513
|
}
|
|
419
514
|
}
|
|
420
|
-
|
|
515
|
+
|
|
421
516
|
const updateProfilePicture = async (jid, content, dimensions) => {
|
|
422
517
|
let targetJid
|
|
423
518
|
if (!jid) {
|
|
@@ -426,7 +521,7 @@ const makeChatsSocket = config => {
|
|
|
426
521
|
)
|
|
427
522
|
}
|
|
428
523
|
if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
|
|
429
|
-
targetJid = (0, WABinary_1.jidNormalizedUser)(jid)
|
|
524
|
+
targetJid = (0, WABinary_1.jidNormalizedUser)(jid)
|
|
430
525
|
} else {
|
|
431
526
|
targetJid = undefined
|
|
432
527
|
}
|
|
@@ -448,7 +543,7 @@ const makeChatsSocket = config => {
|
|
|
448
543
|
]
|
|
449
544
|
})
|
|
450
545
|
}
|
|
451
|
-
|
|
546
|
+
|
|
452
547
|
const removeProfilePicture = async jid => {
|
|
453
548
|
let targetJid
|
|
454
549
|
if (!jid) {
|
|
@@ -457,7 +552,7 @@ const makeChatsSocket = config => {
|
|
|
457
552
|
)
|
|
458
553
|
}
|
|
459
554
|
if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
|
|
460
|
-
targetJid = (0, WABinary_1.jidNormalizedUser)(jid)
|
|
555
|
+
targetJid = (0, WABinary_1.jidNormalizedUser)(jid)
|
|
461
556
|
} else {
|
|
462
557
|
targetJid = undefined
|
|
463
558
|
}
|
|
@@ -471,7 +566,7 @@ const makeChatsSocket = config => {
|
|
|
471
566
|
}
|
|
472
567
|
})
|
|
473
568
|
}
|
|
474
|
-
|
|
569
|
+
|
|
475
570
|
const updateProfileStatus = async status => {
|
|
476
571
|
await query({
|
|
477
572
|
tag: 'iq',
|
|
@@ -492,17 +587,24 @@ const makeChatsSocket = config => {
|
|
|
492
587
|
const updateProfileName = async name => {
|
|
493
588
|
await chatModify({ pushNameSetting: name }, '')
|
|
494
589
|
}
|
|
495
|
-
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
const fetchBlocklist = async (dhash = null) => {
|
|
496
595
|
const result = await query({
|
|
497
596
|
tag: 'iq',
|
|
498
597
|
attrs: {
|
|
499
598
|
xmlns: 'blocklist',
|
|
500
599
|
to: WABinary_1.S_WHATSAPP_NET,
|
|
501
600
|
type: 'get'
|
|
502
|
-
}
|
|
601
|
+
},
|
|
602
|
+
content: dhash ? [{ tag: 'item', attrs: { dhash } }] : null
|
|
503
603
|
})
|
|
504
604
|
const listNode = (0, WABinary_1.getBinaryNodeChild)(result, 'list')
|
|
505
|
-
|
|
605
|
+
const jids = (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item').map(n => n.attrs.jid)
|
|
606
|
+
ev.emit('blocklist.set', { blocklist: jids })
|
|
607
|
+
return jids
|
|
506
608
|
}
|
|
507
609
|
const updateBlockStatus = async (jid, action) => {
|
|
508
610
|
await query({
|
|
@@ -524,26 +626,67 @@ const makeChatsSocket = config => {
|
|
|
524
626
|
})
|
|
525
627
|
}
|
|
526
628
|
const getBusinessProfile = async jid => {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
const [results, verifiedNameResult] = await Promise.all([
|
|
633
|
+
query({
|
|
634
|
+
tag: 'iq',
|
|
635
|
+
attrs: {
|
|
636
|
+
to: 's.whatsapp.net',
|
|
637
|
+
xmlns: 'w:biz',
|
|
638
|
+
type: 'get'
|
|
639
|
+
},
|
|
640
|
+
content: [
|
|
641
|
+
{
|
|
642
|
+
tag: 'business_profile',
|
|
643
|
+
attrs: { v: '116' },
|
|
644
|
+
content: [
|
|
645
|
+
{
|
|
646
|
+
tag: 'profile',
|
|
647
|
+
attrs: { jid }
|
|
648
|
+
}
|
|
649
|
+
]
|
|
650
|
+
}
|
|
651
|
+
]
|
|
652
|
+
}),
|
|
653
|
+
query({
|
|
654
|
+
tag: 'iq',
|
|
655
|
+
attrs: {
|
|
656
|
+
to: 's.whatsapp.net',
|
|
657
|
+
xmlns: 'w:biz',
|
|
658
|
+
type: 'get'
|
|
659
|
+
},
|
|
660
|
+
content: [
|
|
661
|
+
{
|
|
662
|
+
tag: 'verified_name',
|
|
663
|
+
attrs: { jid }
|
|
664
|
+
}
|
|
665
|
+
]
|
|
666
|
+
})
|
|
667
|
+
])
|
|
668
|
+
|
|
669
|
+
const verifiedNameNode = (0, WABinary_1.getBinaryNodeChild)(verifiedNameResult, 'verified_name')
|
|
670
|
+
let verifiedNameCert
|
|
671
|
+
if (verifiedNameNode) {
|
|
672
|
+
const certNode = (0, WABinary_1.getBinaryNodeChild)(verifiedNameNode, 'certificate')
|
|
673
|
+
if (certNode) {
|
|
674
|
+
const detailsNode = (0, WABinary_1.getBinaryNodeChild)(certNode, 'details')
|
|
675
|
+
const localizedNames = (0, WABinary_1.getBinaryNodeChildren)(certNode, 'localized_name').map(n => ({
|
|
676
|
+
lg: n.attrs?.lg,
|
|
677
|
+
lc: n.attrs?.lc,
|
|
678
|
+
verifiedName: n.attrs?.verified_name || n.content?.toString()
|
|
679
|
+
}))
|
|
680
|
+
verifiedNameCert = {
|
|
681
|
+
certSerial: certNode.attrs?.serial ? +certNode.attrs.serial : undefined,
|
|
682
|
+
issuer: certNode.attrs?.issuer,
|
|
683
|
+
details: {
|
|
684
|
+
verifiedName: detailsNode?.attrs?.verified_name || detailsNode?.content?.toString(),
|
|
685
|
+
localizedNames
|
|
686
|
+
}
|
|
544
687
|
}
|
|
545
|
-
|
|
546
|
-
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
547
690
|
const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, 'business_profile')
|
|
548
691
|
const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, 'profile')
|
|
549
692
|
if (profiles) {
|
|
@@ -560,9 +703,31 @@ const makeChatsSocket = config => {
|
|
|
560
703
|
? (0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config')
|
|
561
704
|
: undefined
|
|
562
705
|
const websiteStr = website?.content?.toString()
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
const catalogStatus = {
|
|
709
|
+
exists: profiles.attrs?.catalog_exists === 'true' || profiles.attrs?.catalog_exists === true || false,
|
|
710
|
+
sendAll: profiles.attrs?.catalog_send_all === 'true' || profiles.attrs?.catalog_send_all === true || false
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
const cartEnabled =
|
|
715
|
+
profiles.attrs?.cart_enabled !== undefined
|
|
716
|
+
? profiles.attrs.cart_enabled === 'true' || profiles.attrs.cart_enabled === true
|
|
717
|
+
: undefined
|
|
718
|
+
const webCartEnabled =
|
|
719
|
+
profiles.attrs?.web_cart_enabled !== undefined
|
|
720
|
+
? profiles.attrs.web_cart_enabled === 'true' || profiles.attrs.web_cart_enabled === true
|
|
721
|
+
: undefined
|
|
722
|
+
const webCartOnOff = profiles.attrs?.web_cart_on_off
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
const commerceExperience = profiles.attrs?.commerce_experience
|
|
726
|
+
|
|
563
727
|
return {
|
|
564
728
|
wid: profiles.attrs?.jid,
|
|
565
729
|
address: address?.content?.toString(),
|
|
730
|
+
businessAddress: address?.content?.toString(),
|
|
566
731
|
description: description?.content?.toString() || '',
|
|
567
732
|
website: websiteStr ? [websiteStr] : [],
|
|
568
733
|
email: email?.content?.toString(),
|
|
@@ -570,7 +735,24 @@ const makeChatsSocket = config => {
|
|
|
570
735
|
business_hours: {
|
|
571
736
|
timezone: businessHours?.attrs?.timezone,
|
|
572
737
|
business_config: businessHoursConfig?.map(({ attrs }) => attrs)
|
|
573
|
-
}
|
|
738
|
+
},
|
|
739
|
+
businessHours: businessHoursConfig
|
|
740
|
+
? {
|
|
741
|
+
timezone: businessHours?.attrs?.timezone ?? null,
|
|
742
|
+
config: businessHoursConfig.map(({ attrs }) => ({
|
|
743
|
+
dayOfWeek: attrs?.day_of_week ?? null,
|
|
744
|
+
openTime: attrs?.open_time ?? null,
|
|
745
|
+
closeTime: attrs?.close_time ?? null,
|
|
746
|
+
mode: attrs?.mode ?? null
|
|
747
|
+
}))
|
|
748
|
+
}
|
|
749
|
+
: null,
|
|
750
|
+
catalogStatus,
|
|
751
|
+
cartEnabled,
|
|
752
|
+
webCartEnabled,
|
|
753
|
+
webCartOnOff,
|
|
754
|
+
commerceExperience,
|
|
755
|
+
verifiedNameCert
|
|
574
756
|
}
|
|
575
757
|
}
|
|
576
758
|
}
|
|
@@ -605,6 +787,13 @@ const makeChatsSocket = config => {
|
|
|
605
787
|
isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined,
|
|
606
788
|
logger
|
|
607
789
|
)
|
|
790
|
+
|
|
791
|
+
const _nctSalt = mutation?.syncAction?.value?.nctSaltSyncAction?.salt
|
|
792
|
+
if (_nctSalt?.length) {
|
|
793
|
+
authState.keys
|
|
794
|
+
.set({ 'nct-salt': { default: Buffer.from(_nctSalt) } })
|
|
795
|
+
.catch(err => logger.debug({ err: err?.message }, 'nct: failed to persist salt'))
|
|
796
|
+
}
|
|
608
797
|
}
|
|
609
798
|
}
|
|
610
799
|
}
|
|
@@ -612,24 +801,24 @@ const makeChatsSocket = config => {
|
|
|
612
801
|
const appStateSyncKeyCache = new Map()
|
|
613
802
|
const getCachedAppStateSyncKey = async keyId => {
|
|
614
803
|
if (appStateSyncKeyCache.has(keyId)) {
|
|
615
|
-
return appStateSyncKeyCache.get(keyId)
|
|
804
|
+
return appStateSyncKeyCache.get(keyId)
|
|
616
805
|
}
|
|
617
806
|
const key = await getAppStateSyncKey(keyId)
|
|
618
807
|
appStateSyncKeyCache.set(keyId, key ?? null)
|
|
619
808
|
return key
|
|
620
809
|
}
|
|
621
|
-
|
|
622
|
-
|
|
810
|
+
|
|
811
|
+
|
|
623
812
|
const initialVersionMap = {}
|
|
624
813
|
const globalMutationMap = {}
|
|
625
814
|
await authState.keys.transaction(async () => {
|
|
626
815
|
const collectionsToHandle = new Set(collections)
|
|
627
|
-
|
|
816
|
+
|
|
628
817
|
const attemptsMap = {}
|
|
629
818
|
const forceSnapshotCollections = new Set()
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
|
|
633
822
|
while (collectionsToHandle.size) {
|
|
634
823
|
const states = {}
|
|
635
824
|
const nodes = []
|
|
@@ -637,7 +826,7 @@ const makeChatsSocket = config => {
|
|
|
637
826
|
const result = await authState.keys.get('app-state-sync-version', [name])
|
|
638
827
|
let state = result[name]
|
|
639
828
|
if (state) {
|
|
640
|
-
if (
|
|
829
|
+
if (initialVersionMap[name] === undefined) {
|
|
641
830
|
initialVersionMap[name] = state.version
|
|
642
831
|
}
|
|
643
832
|
} else {
|
|
@@ -654,7 +843,7 @@ const makeChatsSocket = config => {
|
|
|
654
843
|
attrs: {
|
|
655
844
|
name,
|
|
656
845
|
version: state.version.toString(),
|
|
657
|
-
|
|
846
|
+
|
|
658
847
|
return_snapshot: (shouldForceSnapshot || !state.version).toString()
|
|
659
848
|
}
|
|
660
849
|
})
|
|
@@ -674,7 +863,7 @@ const makeChatsSocket = config => {
|
|
|
674
863
|
}
|
|
675
864
|
]
|
|
676
865
|
})
|
|
677
|
-
|
|
866
|
+
|
|
678
867
|
const decoded = await (0, Utils_1.extractSyncdPatches)(result, config?.options)
|
|
679
868
|
for (const key in decoded) {
|
|
680
869
|
const name = key
|
|
@@ -693,7 +882,7 @@ const makeChatsSocket = config => {
|
|
|
693
882
|
logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`)
|
|
694
883
|
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } })
|
|
695
884
|
}
|
|
696
|
-
|
|
885
|
+
|
|
697
886
|
if (patches.length) {
|
|
698
887
|
const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(
|
|
699
888
|
name,
|
|
@@ -713,7 +902,7 @@ const makeChatsSocket = config => {
|
|
|
713
902
|
if (hasMorePatches) {
|
|
714
903
|
logger.info(`${name} has more patches...`)
|
|
715
904
|
} else {
|
|
716
|
-
|
|
905
|
+
|
|
717
906
|
collectionsToHandle.delete(name)
|
|
718
907
|
}
|
|
719
908
|
} catch (error) {
|
|
@@ -755,18 +944,15 @@ const makeChatsSocket = config => {
|
|
|
755
944
|
onMutation(globalMutationMap[key])
|
|
756
945
|
}
|
|
757
946
|
})
|
|
758
|
-
|
|
759
|
-
* fetch the profile picture of a user/group
|
|
760
|
-
* type = "preview" for a low res picture
|
|
761
|
-
* type = "image" for the high res picture URL (adds query="url")
|
|
762
|
-
* type = "avatar" for the avatar variant (no query attr, confirmed from interop logs)
|
|
763
|
-
*/
|
|
947
|
+
|
|
764
948
|
const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
|
|
765
|
-
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
const picAttrs = { type, query: 'url' }
|
|
766
952
|
const baseContent = [{ tag: 'picture', attrs: picAttrs }]
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
|
|
770
956
|
const normalizedJid = (0, WABinary_1.jidNormalizedUser)(jid)
|
|
771
957
|
const isUserJid = (0, WABinary_1.isPnUser)(normalizedJid) || (0, WABinary_1.isLidUser)(normalizedJid)
|
|
772
958
|
const me = authState.creds.me
|
|
@@ -821,13 +1007,7 @@ const makeChatsSocket = config => {
|
|
|
821
1007
|
const child = (0, WABinary_1.getBinaryNodeChild)(result, 'link_create')
|
|
822
1008
|
return child?.attrs?.token
|
|
823
1009
|
}
|
|
824
|
-
|
|
825
|
-
* Toggle the waiting room on an existing call link.
|
|
826
|
-
* Ported from WhatsApp Web's WASmaxVoipWaitingRoomToggleCallLinkRPC.
|
|
827
|
-
* @param {string} linkToken the call link token (from createCallLink)
|
|
828
|
-
* @param {boolean} enabled
|
|
829
|
-
* @param {'audio' | 'video'} [media]
|
|
830
|
-
*/
|
|
1010
|
+
|
|
831
1011
|
const toggleCallLinkWaitingRoom = async (linkToken, enabled, media = 'audio') => {
|
|
832
1012
|
const result = await query({
|
|
833
1013
|
tag: 'call',
|
|
@@ -879,25 +1059,120 @@ const makeChatsSocket = config => {
|
|
|
879
1059
|
})
|
|
880
1060
|
}
|
|
881
1061
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1062
|
+
|
|
1063
|
+
const storePrivacyTokens = async entries => {
|
|
1064
|
+
if (!entries?.length) return
|
|
1065
|
+
const write = {}
|
|
1066
|
+
for (const { jid, privacyToken, privacyModeTs } of entries) {
|
|
1067
|
+
if (!jid || !privacyToken?.length) continue
|
|
1068
|
+
write[jid] = {
|
|
1069
|
+
token: Buffer.isBuffer(privacyToken) ? privacyToken : Buffer.from(privacyToken),
|
|
1070
|
+
ts: String(privacyModeTs || '')
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (Object.keys(write).length) {
|
|
1074
|
+
await authState.keys.set({ 'privacy-token': write })
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
const executeUSyncQuery = async usyncQuery => {
|
|
1080
|
+
const result = await sock.executeUSyncQuery(usyncQuery)
|
|
1081
|
+
if (!result) return result
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
const privacyEntries = []
|
|
1085
|
+
const blockedContacts = []
|
|
1086
|
+
for (const entry of [...(result.list || []), ...(result.sideList || [])]) {
|
|
1087
|
+
if (entry.privacy?.token) {
|
|
1088
|
+
privacyEntries.push({
|
|
1089
|
+
jid: entry.id,
|
|
1090
|
+
privacyToken: entry.privacy.token,
|
|
1091
|
+
privacyModeTs: entry.privacy.modeTs
|
|
1092
|
+
})
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
if (entry.isBlockedByContact) {
|
|
1096
|
+
blockedContacts.push({ id: entry.id, isBlockedByContact: true })
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
if (privacyEntries.length) {
|
|
1100
|
+
storePrivacyTokens(privacyEntries).catch(err => logger.warn({ err }, 'failed to store privacy tokens from usync'))
|
|
1101
|
+
}
|
|
1102
|
+
if (blockedContacts.length) {
|
|
1103
|
+
ev.emit('contacts.update', blockedContacts)
|
|
1104
|
+
}
|
|
1105
|
+
return result
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
const presenceSubscribe = async (toJid, { presenceType, presenceName, groupJid } = {}) => {
|
|
1110
|
+
|
|
888
1111
|
const normalizedToJid = (0, WABinary_1.jidNormalizedUser)(toJid)
|
|
889
1112
|
const isUserJid = (0, WABinary_1.isPnUser)(normalizedToJid) || (0, WABinary_1.isLidUser)(normalizedToJid)
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
const presenceAttrs = {
|
|
1116
|
+
to: toJid,
|
|
1117
|
+
id: generateMessageTag(),
|
|
1118
|
+
type: 'subscribe'
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
const presenceContent = []
|
|
1123
|
+
|
|
1124
|
+
|
|
1125
|
+
if (isUserJid) {
|
|
1126
|
+
try {
|
|
1127
|
+
const storageJid = await (0, tc_token_utils_1.resolveTcTokenJid)(normalizedToJid, getLIDForPN)
|
|
1128
|
+
const tcTokenData = await authState.keys.get('tctoken', [storageJid])
|
|
1129
|
+
const entry = tcTokenData?.[storageJid]
|
|
1130
|
+
const tcTokenBuffer = entry?.token
|
|
1131
|
+
if (tcTokenBuffer?.length && !(0, tc_token_utils_1.isTcTokenExpired)(entry?.timestamp)) {
|
|
1132
|
+
presenceAttrs.tc_token = tcTokenBuffer.toString('base64')
|
|
1133
|
+
}
|
|
1134
|
+
} catch (e) {
|
|
1135
|
+
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
try {
|
|
1142
|
+
const privTokenData = await authState.keys.get('privacy-token', [normalizedToJid])
|
|
1143
|
+
const privEntry = privTokenData?.[normalizedToJid]
|
|
1144
|
+
if (privEntry?.token?.length) {
|
|
1145
|
+
const privTokenAttrs = {}
|
|
1146
|
+
if (privEntry.ts) privTokenAttrs.t = privEntry.ts
|
|
1147
|
+
presenceContent.push({
|
|
1148
|
+
tag: 'privacy_token',
|
|
1149
|
+
attrs: privTokenAttrs,
|
|
1150
|
+
content: privEntry.token
|
|
1151
|
+
})
|
|
1152
|
+
}
|
|
1153
|
+
} catch (e) {
|
|
1154
|
+
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
if (presenceType) {
|
|
1160
|
+
presenceAttrs.presenceType = presenceType
|
|
1161
|
+
}
|
|
1162
|
+
if (presenceName) {
|
|
1163
|
+
presenceAttrs.presenceName = presenceName
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
if (groupJid) {
|
|
1168
|
+
presenceAttrs.context = 'group'
|
|
1169
|
+
presenceAttrs.group_jid = (0, WABinary_1.jidNormalizedUser)(groupJid)
|
|
1170
|
+
}
|
|
1171
|
+
|
|
893
1172
|
return sendNode({
|
|
894
1173
|
tag: 'presence',
|
|
895
|
-
attrs:
|
|
896
|
-
|
|
897
|
-
id: generateMessageTag(),
|
|
898
|
-
type: 'subscribe'
|
|
899
|
-
},
|
|
900
|
-
content: tcTokenContent
|
|
1174
|
+
attrs: presenceAttrs,
|
|
1175
|
+
content: presenceContent.length ? presenceContent : undefined
|
|
901
1176
|
})
|
|
902
1177
|
}
|
|
903
1178
|
const handlePresenceUpdate = ({ tag, attrs, content }) => {
|
|
@@ -996,9 +1271,9 @@ const makeChatsSocket = config => {
|
|
|
996
1271
|
}
|
|
997
1272
|
}
|
|
998
1273
|
}
|
|
999
|
-
|
|
1274
|
+
|
|
1000
1275
|
const fetchProps = async () => {
|
|
1001
|
-
|
|
1276
|
+
|
|
1002
1277
|
const resultNode = await query({
|
|
1003
1278
|
tag: 'iq',
|
|
1004
1279
|
attrs: {
|
|
@@ -1020,13 +1295,13 @@ const makeChatsSocket = config => {
|
|
|
1020
1295
|
let props = {}
|
|
1021
1296
|
if (propsNode) {
|
|
1022
1297
|
if (propsNode.attrs?.hash) {
|
|
1023
|
-
|
|
1298
|
+
|
|
1024
1299
|
authState.creds.lastPropHash = propsNode?.attrs?.hash
|
|
1025
1300
|
ev.emit('creds.update', authState.creds)
|
|
1026
1301
|
}
|
|
1027
1302
|
props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, 'prop')
|
|
1028
1303
|
}
|
|
1029
|
-
|
|
1304
|
+
|
|
1030
1305
|
const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages']
|
|
1031
1306
|
if (privacyTokenProp !== undefined) {
|
|
1032
1307
|
serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1'
|
|
@@ -1042,18 +1317,12 @@ const makeChatsSocket = config => {
|
|
|
1042
1317
|
logger.debug({ serverProps }, 'fetched props')
|
|
1043
1318
|
return props
|
|
1044
1319
|
}
|
|
1045
|
-
|
|
1046
|
-
* modify a chat -- mark unread, read etc.
|
|
1047
|
-
* lastMessages must be sorted in reverse chronologically
|
|
1048
|
-
* requires the last messages till the last message received; required for archive & unread
|
|
1049
|
-
*/
|
|
1320
|
+
|
|
1050
1321
|
const chatModify = (mod, jid) => {
|
|
1051
1322
|
const patch = (0, Utils_1.chatModificationToAppPatch)(mod, jid)
|
|
1052
1323
|
return appPatch(patch)
|
|
1053
1324
|
}
|
|
1054
|
-
|
|
1055
|
-
* Enable/Disable link preview privacy, not related to baileys link preview generation
|
|
1056
|
-
*/
|
|
1325
|
+
|
|
1057
1326
|
const updateDisableLinkPreviewsPrivacy = isPreviewsDisabled => {
|
|
1058
1327
|
return chatModify(
|
|
1059
1328
|
{
|
|
@@ -1062,9 +1331,7 @@ const makeChatsSocket = config => {
|
|
|
1062
1331
|
''
|
|
1063
1332
|
)
|
|
1064
1333
|
}
|
|
1065
|
-
|
|
1066
|
-
* Star or Unstar a message
|
|
1067
|
-
*/
|
|
1334
|
+
|
|
1068
1335
|
const star = (jid, messages, star) => {
|
|
1069
1336
|
return chatModify(
|
|
1070
1337
|
{
|
|
@@ -1076,9 +1343,7 @@ const makeChatsSocket = config => {
|
|
|
1076
1343
|
jid
|
|
1077
1344
|
)
|
|
1078
1345
|
}
|
|
1079
|
-
|
|
1080
|
-
* Add or Edit Contact
|
|
1081
|
-
*/
|
|
1346
|
+
|
|
1082
1347
|
const addOrEditContact = (jid, contact) => {
|
|
1083
1348
|
return chatModify(
|
|
1084
1349
|
{
|
|
@@ -1087,9 +1352,7 @@ const makeChatsSocket = config => {
|
|
|
1087
1352
|
jid
|
|
1088
1353
|
)
|
|
1089
1354
|
}
|
|
1090
|
-
|
|
1091
|
-
* Remove Contact
|
|
1092
|
-
*/
|
|
1355
|
+
|
|
1093
1356
|
const removeContact = jid => {
|
|
1094
1357
|
return chatModify(
|
|
1095
1358
|
{
|
|
@@ -1098,9 +1361,7 @@ const makeChatsSocket = config => {
|
|
|
1098
1361
|
jid
|
|
1099
1362
|
)
|
|
1100
1363
|
}
|
|
1101
|
-
|
|
1102
|
-
* Adds label
|
|
1103
|
-
*/
|
|
1364
|
+
|
|
1104
1365
|
const addLabel = (jid, labels) => {
|
|
1105
1366
|
return chatModify(
|
|
1106
1367
|
{
|
|
@@ -1111,9 +1372,7 @@ const makeChatsSocket = config => {
|
|
|
1111
1372
|
jid
|
|
1112
1373
|
)
|
|
1113
1374
|
}
|
|
1114
|
-
|
|
1115
|
-
* Adds label for the chats
|
|
1116
|
-
*/
|
|
1375
|
+
|
|
1117
1376
|
const addChatLabel = (jid, labelId) => {
|
|
1118
1377
|
return chatModify(
|
|
1119
1378
|
{
|
|
@@ -1124,9 +1383,7 @@ const makeChatsSocket = config => {
|
|
|
1124
1383
|
jid
|
|
1125
1384
|
)
|
|
1126
1385
|
}
|
|
1127
|
-
|
|
1128
|
-
* Removes label for the chat
|
|
1129
|
-
*/
|
|
1386
|
+
|
|
1130
1387
|
const removeChatLabel = (jid, labelId) => {
|
|
1131
1388
|
return chatModify(
|
|
1132
1389
|
{
|
|
@@ -1137,9 +1394,7 @@ const makeChatsSocket = config => {
|
|
|
1137
1394
|
jid
|
|
1138
1395
|
)
|
|
1139
1396
|
}
|
|
1140
|
-
|
|
1141
|
-
* Adds label for the message
|
|
1142
|
-
*/
|
|
1397
|
+
|
|
1143
1398
|
const addMessageLabel = (jid, messageId, labelId) => {
|
|
1144
1399
|
return chatModify(
|
|
1145
1400
|
{
|
|
@@ -1151,9 +1406,7 @@ const makeChatsSocket = config => {
|
|
|
1151
1406
|
jid
|
|
1152
1407
|
)
|
|
1153
1408
|
}
|
|
1154
|
-
|
|
1155
|
-
* Removes label for the message
|
|
1156
|
-
*/
|
|
1409
|
+
|
|
1157
1410
|
const removeMessageLabel = (jid, messageId, labelId) => {
|
|
1158
1411
|
return chatModify(
|
|
1159
1412
|
{
|
|
@@ -1165,9 +1418,7 @@ const makeChatsSocket = config => {
|
|
|
1165
1418
|
jid
|
|
1166
1419
|
)
|
|
1167
1420
|
}
|
|
1168
|
-
|
|
1169
|
-
* Add or Edit Quick Reply
|
|
1170
|
-
*/
|
|
1421
|
+
|
|
1171
1422
|
const addOrEditQuickReply = quickReply => {
|
|
1172
1423
|
return chatModify(
|
|
1173
1424
|
{
|
|
@@ -1176,9 +1427,7 @@ const makeChatsSocket = config => {
|
|
|
1176
1427
|
''
|
|
1177
1428
|
)
|
|
1178
1429
|
}
|
|
1179
|
-
|
|
1180
|
-
* Remove Quick Reply
|
|
1181
|
-
*/
|
|
1430
|
+
|
|
1182
1431
|
const removeQuickReply = timestamp => {
|
|
1183
1432
|
return chatModify(
|
|
1184
1433
|
{
|
|
@@ -1187,34 +1436,49 @@ const makeChatsSocket = config => {
|
|
|
1187
1436
|
''
|
|
1188
1437
|
)
|
|
1189
1438
|
}
|
|
1190
|
-
|
|
1191
|
-
* Rename an AI chat thread
|
|
1192
|
-
*/
|
|
1439
|
+
|
|
1193
1440
|
const renameAIThread = (jid, title) => {
|
|
1194
1441
|
return chatModify({ aiThreadRename: { title } }, jid)
|
|
1195
1442
|
}
|
|
1196
|
-
|
|
1197
|
-
* Pin or unpin a message in a thread/AI chat
|
|
1198
|
-
*/
|
|
1443
|
+
|
|
1199
1444
|
const pinThreadMessage = (jid, messageId, pinned = true) => {
|
|
1200
1445
|
return chatModify({ threadPin: { pinned, messageId } }, jid)
|
|
1201
1446
|
}
|
|
1202
|
-
|
|
1203
|
-
* Toggle AI private processing (end-to-end encrypted AI processing)
|
|
1204
|
-
*/
|
|
1447
|
+
|
|
1205
1448
|
const updatePrivateProcessingSetting = enabled => {
|
|
1206
1449
|
return chatModify({ privateProcessingSetting: enabled ? 'enabled' : 'disabled' }, '')
|
|
1207
1450
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1451
|
+
|
|
1452
|
+
const updateAIFeatures = (status = 'enabled', replyMode = 'suggestions') => {
|
|
1453
|
+
const statusEnum =
|
|
1454
|
+
require('../../WAProto/index.js').proto.SyncActionValue.MaibaAIFeaturesControlAction.MaibaAIFeatureStatus
|
|
1455
|
+
const replyModeEnum =
|
|
1456
|
+
require('../../WAProto/index.js').proto.SyncActionValue.MaibaAIFeaturesControlAction.MaibaAIReplyMode
|
|
1457
|
+
const statusMap = {
|
|
1458
|
+
enabled: statusEnum.ENABLED,
|
|
1459
|
+
enabled_has_learning: statusEnum.ENABLED_HAS_LEARNING,
|
|
1460
|
+
disabled: statusEnum.DISABLED
|
|
1461
|
+
}
|
|
1462
|
+
const replyModeMap = {
|
|
1463
|
+
muted: replyModeEnum.MUTED,
|
|
1464
|
+
ai_agent: replyModeEnum.AI_AGENT,
|
|
1465
|
+
suggestions: replyModeEnum.SUGGESTIONS
|
|
1466
|
+
}
|
|
1467
|
+
return chatModify(
|
|
1468
|
+
{
|
|
1469
|
+
maibaAIFeatures: {
|
|
1470
|
+
status: statusMap[status] ?? statusEnum.ENABLED,
|
|
1471
|
+
replyMode: replyModeMap[replyMode] ?? replyModeEnum.SUGGESTIONS
|
|
1472
|
+
}
|
|
1473
|
+
},
|
|
1474
|
+
''
|
|
1475
|
+
)
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1212
1478
|
const updateBioPrivacy = async value => {
|
|
1213
1479
|
await privacyQuery('about', value)
|
|
1214
1480
|
}
|
|
1215
|
-
|
|
1216
|
-
* Block a bot JID
|
|
1217
|
-
*/
|
|
1481
|
+
|
|
1218
1482
|
const blockBot = async botJid => {
|
|
1219
1483
|
await query({
|
|
1220
1484
|
tag: 'iq',
|
|
@@ -1231,71 +1495,63 @@ const makeChatsSocket = config => {
|
|
|
1231
1495
|
]
|
|
1232
1496
|
})
|
|
1233
1497
|
}
|
|
1234
|
-
|
|
1235
|
-
* Unblock a bot JID
|
|
1236
|
-
*/
|
|
1498
|
+
|
|
1237
1499
|
const unblockBot = async botJid => {
|
|
1238
1500
|
await updateBlockStatus(botJid, 'unblock')
|
|
1239
1501
|
}
|
|
1240
|
-
|
|
1241
|
-
* Mute or unmute a contact's status updates
|
|
1242
|
-
*/
|
|
1502
|
+
|
|
1243
1503
|
const muteContactStatus = (jid, muted = true) => {
|
|
1244
1504
|
return chatModify({ muteStatus: { muted } }, jid)
|
|
1245
1505
|
}
|
|
1246
|
-
|
|
1247
|
-
* Add or remove a contact from favorites
|
|
1248
|
-
*/
|
|
1506
|
+
|
|
1249
1507
|
const toggleFavorite = (jid, isFavorite = true) => {
|
|
1250
1508
|
return chatModify({ favorite: { isFavorite } }, jid)
|
|
1251
1509
|
}
|
|
1252
|
-
|
|
1253
|
-
* Reorder labels
|
|
1254
|
-
* @param sortedLabelIds ordered array of label IDs
|
|
1255
|
-
*/
|
|
1510
|
+
|
|
1256
1511
|
const reorderLabels = sortedLabelIds => {
|
|
1257
1512
|
return chatModify({ reorderLabel: { sortedLabelIds } }, '')
|
|
1258
1513
|
}
|
|
1259
|
-
|
|
1260
|
-
* Delete an individual call log entry
|
|
1261
|
-
*/
|
|
1514
|
+
|
|
1262
1515
|
const deleteCallLog = (callId, jid = '') => {
|
|
1263
1516
|
return chatModify({ deleteCallLog: { callId } }, jid)
|
|
1264
1517
|
}
|
|
1265
|
-
|
|
1266
|
-
* Create or update a note/draft for a chat
|
|
1267
|
-
*/
|
|
1518
|
+
|
|
1268
1519
|
const setChatNote = (jid, note) => {
|
|
1269
1520
|
return chatModify({ noteEdit: { note } }, jid)
|
|
1270
1521
|
}
|
|
1271
|
-
|
|
1272
|
-
* Delete the note/draft for a chat
|
|
1273
|
-
*/
|
|
1522
|
+
|
|
1274
1523
|
const deleteChatNote = jid => {
|
|
1275
1524
|
return chatModify({ noteEdit: { note: '', deleted: true } }, jid)
|
|
1276
1525
|
}
|
|
1277
|
-
|
|
1278
|
-
* Explicitly mark a chat as unread (shows unread dot even if read)
|
|
1279
|
-
*/
|
|
1526
|
+
|
|
1280
1527
|
const markChatAsUnread = (jid, lastMessages) => {
|
|
1281
1528
|
return chatModify({ markAsUnread: true, lastMessages }, jid)
|
|
1282
1529
|
}
|
|
1283
|
-
|
|
1284
|
-
* Set per-chat ephemeral message duration
|
|
1285
|
-
* @param duration seconds (0 = off, 86400 = 1d, 604800 = 7d, 7776000 = 90d)
|
|
1286
|
-
*/
|
|
1530
|
+
|
|
1287
1531
|
const setChatEphemeral = (jid, duration) => {
|
|
1288
1532
|
return chatModify({ setChatEphemeral: duration }, jid)
|
|
1289
1533
|
}
|
|
1290
|
-
|
|
1291
|
-
* Silence a chat (mute without timestamp = permanent, or provide until timestamp)
|
|
1292
|
-
*/
|
|
1534
|
+
|
|
1293
1535
|
const silenceChat = (jid, silent = true, until = null) => {
|
|
1294
1536
|
return chatModify({ silenceChat: { silent, until } }, jid)
|
|
1295
1537
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1538
|
+
|
|
1539
|
+
const clearChat = (jid, lastMessages) => {
|
|
1540
|
+
return chatModify({ clear: true, lastMessages }, jid)
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
const pinChat = (jid, pinned = true) => {
|
|
1544
|
+
return chatModify({ pin: { pinned } }, jid)
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
const starMessages = (jid, messageIds, starred = true) => {
|
|
1548
|
+
return chatModify({ star: { messages: messageIds, star: starred } }, jid)
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
const setQuickReply = (text, shortcut) => {
|
|
1552
|
+
return chatModify({ quickReply: { text, shortcut } }, '')
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1299
1555
|
const fetchBotProfiles = async jids => {
|
|
1300
1556
|
const { USyncQuery, USyncUser, USyncBotProfileProtocol } = require('../WAUSync')
|
|
1301
1557
|
const q = new USyncQuery()
|
|
@@ -1303,37 +1559,25 @@ const makeChatsSocket = config => {
|
|
|
1303
1559
|
for (const jid of jids) {
|
|
1304
1560
|
q.withUser(new USyncUser().withId(jid))
|
|
1305
1561
|
}
|
|
1306
|
-
const result = await
|
|
1562
|
+
const result = await executeUSyncQuery(q)
|
|
1307
1563
|
return result?.list || []
|
|
1308
1564
|
}
|
|
1309
|
-
|
|
1310
|
-
* Lock or unlock a chat with an optional secret code
|
|
1311
|
-
*/
|
|
1565
|
+
|
|
1312
1566
|
const updateChatLock = (jid, locked) => {
|
|
1313
1567
|
return chatModify({ chatLock: { locked } }, jid)
|
|
1314
1568
|
}
|
|
1315
|
-
|
|
1316
|
-
* Set a custom wallpaper for a chat
|
|
1317
|
-
* @param jid the chat JID
|
|
1318
|
-
* @param wallpaper wallpaper data or null to remove
|
|
1319
|
-
*/
|
|
1569
|
+
|
|
1320
1570
|
const updateChatWallpaper = (jid, wallpaper) => {
|
|
1321
1571
|
if (!wallpaper) {
|
|
1322
1572
|
return chatModify({ wallpaper: { remove: true } }, jid)
|
|
1323
1573
|
}
|
|
1324
1574
|
return chatModify({ wallpaper }, jid)
|
|
1325
1575
|
}
|
|
1326
|
-
|
|
1327
|
-
* Set media visibility (auto-download) for a chat
|
|
1328
|
-
* @param jid the chat JID
|
|
1329
|
-
* @param visibility 'default' | 'on' | 'off'
|
|
1330
|
-
*/
|
|
1576
|
+
|
|
1331
1577
|
const updateChatMediaVisibility = (jid, visibility) => {
|
|
1332
1578
|
return chatModify({ mediaVisibility: visibility }, jid)
|
|
1333
1579
|
}
|
|
1334
|
-
|
|
1335
|
-
* Fetch details for a specific bot by JID
|
|
1336
|
-
*/
|
|
1580
|
+
|
|
1337
1581
|
const getBotProfile = async botJid => {
|
|
1338
1582
|
const resp = await query({
|
|
1339
1583
|
tag: 'iq',
|
|
@@ -1358,15 +1602,16 @@ const makeChatsSocket = config => {
|
|
|
1358
1602
|
description: botNode.attrs.description
|
|
1359
1603
|
}
|
|
1360
1604
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1605
|
+
|
|
1606
|
+
const fetchABProps = async (protocol = '1', hash = '', refreshId = null, group = null) => {
|
|
1607
|
+
const propAttrs =
|
|
1608
|
+
group != null
|
|
1609
|
+
? { group: String(group) }
|
|
1610
|
+
: {
|
|
1611
|
+
protocol,
|
|
1612
|
+
...(hash ? { hash } : {}),
|
|
1613
|
+
...(refreshId != null ? { refresh_id: String(refreshId) } : {})
|
|
1614
|
+
}
|
|
1370
1615
|
const result = await query({
|
|
1371
1616
|
tag: 'iq',
|
|
1372
1617
|
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'abt', type: 'get' },
|
|
@@ -1376,29 +1621,20 @@ const makeChatsSocket = config => {
|
|
|
1376
1621
|
if (!propsNode) return {}
|
|
1377
1622
|
return (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, 'prop')
|
|
1378
1623
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
* Mirrors CompanionDeviceRemovalJob — xmlns="md", child tag "remove-companion-device".
|
|
1382
|
-
* reason: "user_initiated" | "server_initiated" | any WA-defined reason string.
|
|
1383
|
-
*/
|
|
1384
|
-
const removeCompanionDevice = async (keyIndex, reason = 'user_initiated') => {
|
|
1624
|
+
|
|
1625
|
+
const removeCompanionDevice = async (jid, reason = 'user_initiated') => {
|
|
1385
1626
|
await query({
|
|
1386
1627
|
tag: 'iq',
|
|
1387
1628
|
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'md', type: 'set' },
|
|
1388
1629
|
content: [
|
|
1389
1630
|
{
|
|
1390
1631
|
tag: 'remove-companion-device',
|
|
1391
|
-
attrs: {
|
|
1632
|
+
attrs: { jid, reason }
|
|
1392
1633
|
}
|
|
1393
1634
|
]
|
|
1394
1635
|
})
|
|
1395
1636
|
}
|
|
1396
|
-
|
|
1397
|
-
* Push an updated key-index-list to the server (multi-device key announcement).
|
|
1398
|
-
* Mirrors KeyIndexListJob — xmlns="md", child tag "key-index-list" with a binary proto body.
|
|
1399
|
-
* ts: unix timestamp seconds (string or number).
|
|
1400
|
-
* content: Buffer — serialized proto KeyIndexList.
|
|
1401
|
-
*/
|
|
1637
|
+
|
|
1402
1638
|
const updateKeyIndexList = async (ts, content) => {
|
|
1403
1639
|
await query({
|
|
1404
1640
|
tag: 'iq',
|
|
@@ -1412,11 +1648,39 @@ const makeChatsSocket = config => {
|
|
|
1412
1648
|
]
|
|
1413
1649
|
})
|
|
1414
1650
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1651
|
+
|
|
1652
|
+
const sendKeyIndexList = async () => {
|
|
1653
|
+
const account = authState.creds.account
|
|
1654
|
+
const signedIdentityKey = authState.creds.signedIdentityKey
|
|
1655
|
+
if (!account?.details || !signedIdentityKey?.private) {
|
|
1656
|
+
logger.debug('no ADV account / signed identity key, skipping key-index-list')
|
|
1657
|
+
return
|
|
1658
|
+
}
|
|
1659
|
+
const deviceIdentity = index_js_1.proto.ADVDeviceIdentity.decode(account.details)
|
|
1660
|
+
const keyIndex = deviceIdentity.keyIndex || 0
|
|
1661
|
+
const accountType = deviceIdentity.accountType ?? index_js_1.proto.ADVEncryptionType.E2EE
|
|
1662
|
+
const isHosted = accountType === index_js_1.proto.ADVEncryptionType.HOSTED
|
|
1663
|
+
const ts = Math.floor(Date.now() / 1000)
|
|
1664
|
+
const details = index_js_1.proto.ADVKeyIndexList.encode({
|
|
1665
|
+
rawId: deviceIdentity.rawId || 0,
|
|
1666
|
+
timestamp: ts,
|
|
1667
|
+
currentIndex: keyIndex,
|
|
1668
|
+
validIndexes: [keyIndex],
|
|
1669
|
+
accountType
|
|
1670
|
+
}).finish()
|
|
1671
|
+
const sigPrefix = isHosted
|
|
1672
|
+
? Defaults_1.WA_ADV_HOSTED_KEY_INDEX_LIST_SIG_PREFIX
|
|
1673
|
+
: Defaults_1.WA_ADV_KEY_INDEX_LIST_SIG_PREFIX
|
|
1674
|
+
const accountSignature = Utils_1.Curve.sign(signedIdentityKey.private, Buffer.concat([sigPrefix, details]))
|
|
1675
|
+
const signedKeyIndexList = { details, accountSignature }
|
|
1676
|
+
if (isHosted && account.accountSignatureKey?.length) {
|
|
1677
|
+
signedKeyIndexList.accountSignatureKey = account.accountSignatureKey
|
|
1678
|
+
}
|
|
1679
|
+
const content = index_js_1.proto.ADVSignedKeyIndexList.encode(signedKeyIndexList).finish()
|
|
1680
|
+
await updateKeyIndexList(ts, Buffer.from(content))
|
|
1681
|
+
logger.info({ ts, keyIndex }, 'sent key-index-list')
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1420
1684
|
const fetchMediaConn = async (lastId = null) => {
|
|
1421
1685
|
const attrs = {}
|
|
1422
1686
|
if (lastId != null) attrs.last_id = String(lastId)
|
|
@@ -1427,10 +1691,7 @@ const makeChatsSocket = config => {
|
|
|
1427
1691
|
})
|
|
1428
1692
|
return (0, WABinary_1.getBinaryNodeChild)(result, 'media_conn') || null
|
|
1429
1693
|
}
|
|
1430
|
-
|
|
1431
|
-
* Delete a broadcast list by ID.
|
|
1432
|
-
* Mirrors BroadcastListDeleteJob — xmlns="w:b", wraps <delete><list id="..."/></delete>.
|
|
1433
|
-
*/
|
|
1694
|
+
|
|
1434
1695
|
const deleteBroadcastList = async listId => {
|
|
1435
1696
|
await query({
|
|
1436
1697
|
tag: 'iq',
|
|
@@ -1444,11 +1705,7 @@ const makeChatsSocket = config => {
|
|
|
1444
1705
|
]
|
|
1445
1706
|
})
|
|
1446
1707
|
}
|
|
1447
|
-
|
|
1448
|
-
* Fetch a QR code from the server (e.g. for linked-device linking).
|
|
1449
|
-
* Mirrors QRCodeFetcher — xmlns="w:qr", type="get".
|
|
1450
|
-
* addressingMode: "lid" | undefined — set to "lid" for LID-addressed QR.
|
|
1451
|
-
*/
|
|
1708
|
+
|
|
1452
1709
|
const fetchQRCode = async (code, addressingMode = null) => {
|
|
1453
1710
|
const attrs = { code }
|
|
1454
1711
|
if (addressingMode) attrs.addressing_mode = addressingMode
|
|
@@ -1459,15 +1716,13 @@ const makeChatsSocket = config => {
|
|
|
1459
1716
|
})
|
|
1460
1717
|
return (0, WABinary_1.getBinaryNodeChild)(result, 'qr') || null
|
|
1461
1718
|
}
|
|
1462
|
-
|
|
1463
|
-
* Confirm or deny a device-logout request from the server.
|
|
1464
|
-
* Mirrors AccountDefenceDeviceLogoutJob — xmlns="w:account_defence", smax_id=87.
|
|
1465
|
-
* approve: true to confirm the logout, false to deny.
|
|
1466
|
-
*/
|
|
1719
|
+
|
|
1467
1720
|
const confirmDeviceLogout = async (id, approve = true) => {
|
|
1468
1721
|
await query({
|
|
1469
1722
|
tag: 'iq',
|
|
1470
1723
|
attrs: {
|
|
1724
|
+
|
|
1725
|
+
id: String(id),
|
|
1471
1726
|
to: WABinary_1.S_WHATSAPP_NET,
|
|
1472
1727
|
xmlns: 'w:account_defence',
|
|
1473
1728
|
type: 'set',
|
|
@@ -1481,14 +1736,78 @@ const makeChatsSocket = config => {
|
|
|
1481
1736
|
]
|
|
1482
1737
|
})
|
|
1483
1738
|
}
|
|
1484
|
-
|
|
1485
|
-
* queries need to be fired on connection open
|
|
1486
|
-
* help ensure parity with WA Web
|
|
1487
|
-
* */
|
|
1739
|
+
|
|
1488
1740
|
const executeInitQueries = async () => {
|
|
1489
|
-
|
|
1741
|
+
|
|
1742
|
+
|
|
1743
|
+
const shouldInitInterop = authState.creds.interopEnabled !== false
|
|
1744
|
+
const [, , , abProps] = await Promise.all([
|
|
1745
|
+
fetchProps(),
|
|
1746
|
+
fetchBlocklist(),
|
|
1747
|
+
fetchPrivacySettings(),
|
|
1748
|
+
fetchABProps(),
|
|
1749
|
+
...(shouldInitInterop ? [initInterop()] : [])
|
|
1750
|
+
])
|
|
1751
|
+
const INTEROP_FLAGS = ['stella_interop_enabled', 'stella_ios_enabled']
|
|
1752
|
+
for (const flag of INTEROP_FLAGS) {
|
|
1753
|
+
if (flag in abProps) {
|
|
1754
|
+
const enabled = abProps[flag] === 'true' || abProps[flag] === '1'
|
|
1755
|
+
ev.emit('interop.feature-update', { feature: flag, enabled })
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
const abCredsUpdate = {}
|
|
1760
|
+
if ('stella_interop_enabled' in abProps) {
|
|
1761
|
+
abCredsUpdate.interopEnabled =
|
|
1762
|
+
abProps['stella_interop_enabled'] === 'true' || abProps['stella_interop_enabled'] === '1'
|
|
1763
|
+
}
|
|
1764
|
+
if ('stella_ios_enabled' in abProps) {
|
|
1765
|
+
abCredsUpdate.interopIosEnabled =
|
|
1766
|
+
abProps['stella_ios_enabled'] === 'true' || abProps['stella_ios_enabled'] === '1'
|
|
1767
|
+
}
|
|
1768
|
+
if ('md_privacy_v2' in abProps) {
|
|
1769
|
+
abCredsUpdate.mdPrivacyV2 = abProps['md_privacy_v2'] === 'true' || abProps['md_privacy_v2'] === '1'
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
const MEDIA_AB_PROPS = [
|
|
1773
|
+
'hd_image_dual_upload',
|
|
1774
|
+
'hd_video_dual_upload',
|
|
1775
|
+
'hevc_video_dual_upload',
|
|
1776
|
+
'partial_pjpeg_enabled',
|
|
1777
|
+
'multi_scan_pjpeg',
|
|
1778
|
+
'media_poll'
|
|
1779
|
+
]
|
|
1780
|
+
const mediaAbProps = {}
|
|
1781
|
+
for (const prop of MEDIA_AB_PROPS) {
|
|
1782
|
+
if (prop in abProps) {
|
|
1783
|
+
mediaAbProps[prop] = abProps[prop] === 'true' || abProps[prop] === '1'
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
if (Object.keys(mediaAbProps).length) {
|
|
1787
|
+
abCredsUpdate.mediaAbProps = { ...(authState.creds.mediaAbProps || {}), ...mediaAbProps }
|
|
1788
|
+
}
|
|
1789
|
+
if (Object.keys(abCredsUpdate).length) {
|
|
1790
|
+
ev.emit('creds.update', abCredsUpdate)
|
|
1791
|
+
}
|
|
1490
1792
|
}
|
|
1491
1793
|
const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
|
|
1794
|
+
|
|
1795
|
+
if (msg.newsletter && msg.newsletter_server_id && msg.key?.id) {
|
|
1796
|
+
_nlServerIdCache.set(msg.key.id, msg.newsletter_server_id)
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
if (msg.message) {
|
|
1800
|
+
const ci = Object.values(msg.message).find(v => v?.contextInfo)?.contextInfo
|
|
1801
|
+
if (ci?.stanzaId && (0, WABinary_1.isJidNewsletter)(ci.remoteJid)) {
|
|
1802
|
+
const sid = _nlServerIdCache.get(ci.stanzaId)
|
|
1803
|
+
if (sid != null) msg.quotedNewsletterServerId = sid
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
if (msg.message?.secretEncryptedMessage || msg.message?.editedMessage?.message?.secretEncryptedMessage) {
|
|
1807
|
+
|
|
1808
|
+
|
|
1809
|
+
await unwrapSecretEncryptedMessage(msg, { creds: authState.creds, getMessage, logger })
|
|
1810
|
+
}
|
|
1492
1811
|
ev.emit('messages.upsert', { messages: [msg], type })
|
|
1493
1812
|
if (!!msg.pushName) {
|
|
1494
1813
|
let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid
|
|
@@ -1496,7 +1815,7 @@ const makeChatsSocket = config => {
|
|
|
1496
1815
|
if (!msg.key.fromMe) {
|
|
1497
1816
|
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }])
|
|
1498
1817
|
}
|
|
1499
|
-
|
|
1818
|
+
|
|
1500
1819
|
if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
|
1501
1820
|
ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } })
|
|
1502
1821
|
}
|
|
@@ -1507,7 +1826,7 @@ const makeChatsSocket = config => {
|
|
|
1507
1826
|
: false
|
|
1508
1827
|
if (historyMsg && shouldProcessHistoryMsg) {
|
|
1509
1828
|
const syncType = historyMsg.syncType
|
|
1510
|
-
|
|
1829
|
+
|
|
1511
1830
|
if (
|
|
1512
1831
|
syncType === index_js_1.proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
|
|
1513
1832
|
!historySyncStatus.initialBootstrapComplete
|
|
@@ -1519,7 +1838,7 @@ const makeChatsSocket = config => {
|
|
|
1519
1838
|
explicit: true
|
|
1520
1839
|
})
|
|
1521
1840
|
}
|
|
1522
|
-
|
|
1841
|
+
|
|
1523
1842
|
if (
|
|
1524
1843
|
syncType === index_js_1.proto.HistorySync.HistorySyncType.RECENT &&
|
|
1525
1844
|
historyMsg.progress === 100 &&
|
|
@@ -1534,7 +1853,7 @@ const makeChatsSocket = config => {
|
|
|
1534
1853
|
explicit: true
|
|
1535
1854
|
})
|
|
1536
1855
|
}
|
|
1537
|
-
|
|
1856
|
+
|
|
1538
1857
|
if (syncType === index_js_1.proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
|
|
1539
1858
|
clearTimeout(historySyncPausedTimeout)
|
|
1540
1859
|
historySyncPausedTimeout = setTimeout(() => {
|
|
@@ -1550,7 +1869,7 @@ const makeChatsSocket = config => {
|
|
|
1550
1869
|
}, Defaults_1.HISTORY_SYNC_PAUSED_TIMEOUT_MS)
|
|
1551
1870
|
}
|
|
1552
1871
|
}
|
|
1553
|
-
|
|
1872
|
+
|
|
1554
1873
|
if (historyMsg && syncState === State_1.SyncState.AwaitingInitialSync) {
|
|
1555
1874
|
if (awaitingSyncTimeout) {
|
|
1556
1875
|
clearTimeout(awaitingSyncTimeout)
|
|
@@ -1559,7 +1878,7 @@ const makeChatsSocket = config => {
|
|
|
1559
1878
|
if (shouldProcessHistoryMsg) {
|
|
1560
1879
|
syncState = State_1.SyncState.Syncing
|
|
1561
1880
|
logger.info('Transitioned to Syncing state')
|
|
1562
|
-
|
|
1881
|
+
|
|
1563
1882
|
} else {
|
|
1564
1883
|
syncState = State_1.SyncState.Online
|
|
1565
1884
|
logger.info('History sync skipped, transitioning to Online state and flushing buffer')
|
|
@@ -1568,11 +1887,11 @@ const makeChatsSocket = config => {
|
|
|
1568
1887
|
}
|
|
1569
1888
|
const doAppStateSync = async () => {
|
|
1570
1889
|
if (syncState === State_1.SyncState.Syncing) {
|
|
1571
|
-
|
|
1890
|
+
|
|
1572
1891
|
blockedCollections.clear()
|
|
1573
1892
|
logger.info('Doing app state sync')
|
|
1574
1893
|
await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true)
|
|
1575
|
-
|
|
1894
|
+
|
|
1576
1895
|
syncState = State_1.SyncState.Online
|
|
1577
1896
|
logger.info('App state sync complete, transitioning to Online state and flushing buffer')
|
|
1578
1897
|
ev.flush()
|
|
@@ -1598,7 +1917,7 @@ const makeChatsSocket = config => {
|
|
|
1598
1917
|
getMessage
|
|
1599
1918
|
})
|
|
1600
1919
|
])
|
|
1601
|
-
|
|
1920
|
+
|
|
1602
1921
|
if (msg.message?.protocolMessage?.appStateSyncKeyShare && syncState === State_1.SyncState.Syncing) {
|
|
1603
1922
|
logger.info('App state sync key arrived, triggering app state sync')
|
|
1604
1923
|
await doAppStateSync()
|
|
@@ -1606,6 +1925,20 @@ const makeChatsSocket = config => {
|
|
|
1606
1925
|
})
|
|
1607
1926
|
ws.on('CB:presence', handlePresenceUpdate)
|
|
1608
1927
|
ws.on('CB:chatstate', handlePresenceUpdate)
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
|
|
1931
|
+
ws.on('CB:iq,xmlns:w:account_defence', async node => {
|
|
1932
|
+
const deviceLogout = (0, WABinary_1.getBinaryNodeChild)(node, 'device_logout')
|
|
1933
|
+
if (!deviceLogout) return
|
|
1934
|
+
const id = node.attrs.id
|
|
1935
|
+
logger.info({ id }, 'received account_defence device_logout challenge, approving')
|
|
1936
|
+
try {
|
|
1937
|
+
await confirmDeviceLogout(id, true)
|
|
1938
|
+
} catch (error) {
|
|
1939
|
+
onUnexpectedError(error, 'account_defence device_logout approve')
|
|
1940
|
+
}
|
|
1941
|
+
})
|
|
1609
1942
|
ws.on('CB:ib,,dirty', async node => {
|
|
1610
1943
|
const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty')
|
|
1611
1944
|
const type = attrs.type
|
|
@@ -1621,7 +1954,7 @@ const makeChatsSocket = config => {
|
|
|
1621
1954
|
}
|
|
1622
1955
|
break
|
|
1623
1956
|
case 'groups':
|
|
1624
|
-
|
|
1957
|
+
|
|
1625
1958
|
break
|
|
1626
1959
|
default:
|
|
1627
1960
|
logger.info({ node }, 'received unknown sync')
|
|
@@ -1641,6 +1974,9 @@ const makeChatsSocket = config => {
|
|
|
1641
1974
|
sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error =>
|
|
1642
1975
|
onUnexpectedError(error, 'presence update requests')
|
|
1643
1976
|
)
|
|
1977
|
+
|
|
1978
|
+
|
|
1979
|
+
sendKeyIndexList().catch(error => onUnexpectedError(error, 'send key-index-list'))
|
|
1644
1980
|
}
|
|
1645
1981
|
if (!receivedPendingNotifications || syncState !== State_1.SyncState.Connecting) {
|
|
1646
1982
|
return
|
|
@@ -1663,9 +1999,9 @@ const makeChatsSocket = config => {
|
|
|
1663
1999
|
setTimeout(() => ev.flush(), 0)
|
|
1664
2000
|
return
|
|
1665
2001
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
2002
|
+
|
|
2003
|
+
|
|
2004
|
+
|
|
1669
2005
|
if (authState.creds.accountSyncCounter > 0) {
|
|
1670
2006
|
logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.')
|
|
1671
2007
|
syncState = State_1.SyncState.Online
|
|
@@ -1681,18 +2017,18 @@ const makeChatsSocket = config => {
|
|
|
1681
2017
|
logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer')
|
|
1682
2018
|
syncState = State_1.SyncState.Online
|
|
1683
2019
|
ev.flush()
|
|
1684
|
-
|
|
2020
|
+
|
|
1685
2021
|
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1
|
|
1686
2022
|
ev.emit('creds.update', { accountSyncCounter })
|
|
1687
2023
|
}
|
|
1688
2024
|
}, 20000)
|
|
1689
2025
|
})
|
|
1690
|
-
|
|
2026
|
+
|
|
1691
2027
|
ev.on('creds.update', ({ myAppStateKeyId }) => {
|
|
1692
2028
|
if (!myAppStateKeyId || blockedCollections.size === 0) {
|
|
1693
2029
|
return
|
|
1694
2030
|
}
|
|
1695
|
-
|
|
2031
|
+
|
|
1696
2032
|
if (syncState === State_1.SyncState.Syncing) {
|
|
1697
2033
|
blockedCollections.clear()
|
|
1698
2034
|
return
|
|
@@ -1718,9 +2054,12 @@ const makeChatsSocket = config => {
|
|
|
1718
2054
|
getChatBlockingStatus,
|
|
1719
2055
|
updateChatBlockingStatus,
|
|
1720
2056
|
getUserDisclosures,
|
|
2057
|
+
acceptTosNotice,
|
|
2058
|
+
reportSpam,
|
|
1721
2059
|
getOptOutList,
|
|
1722
2060
|
getPushConfig,
|
|
1723
2061
|
setPushConfig,
|
|
2062
|
+
signPrivateCredential,
|
|
1724
2063
|
messageMutex,
|
|
1725
2064
|
receiptMutex,
|
|
1726
2065
|
appStatePatchMutex,
|
|
@@ -1769,6 +2108,7 @@ const makeChatsSocket = config => {
|
|
|
1769
2108
|
renameAIThread,
|
|
1770
2109
|
pinThreadMessage,
|
|
1771
2110
|
updatePrivateProcessingSetting,
|
|
2111
|
+
updateAIFeatures,
|
|
1772
2112
|
updateBioPrivacy,
|
|
1773
2113
|
blockBot,
|
|
1774
2114
|
unblockBot,
|
|
@@ -1782,6 +2122,10 @@ const makeChatsSocket = config => {
|
|
|
1782
2122
|
markChatAsUnread,
|
|
1783
2123
|
setChatEphemeral,
|
|
1784
2124
|
silenceChat,
|
|
2125
|
+
clearChat,
|
|
2126
|
+
pinChat,
|
|
2127
|
+
starMessages,
|
|
2128
|
+
setQuickReply,
|
|
1785
2129
|
fetchBotProfiles,
|
|
1786
2130
|
updateChatLock,
|
|
1787
2131
|
updateChatWallpaper,
|
|
@@ -1810,7 +2154,12 @@ const makeChatsSocket = config => {
|
|
|
1810
2154
|
fetchMediaConn,
|
|
1811
2155
|
deleteBroadcastList,
|
|
1812
2156
|
fetchQRCode,
|
|
1813
|
-
confirmDeviceLogout
|
|
2157
|
+
confirmDeviceLogout,
|
|
2158
|
+
updateKeyIndexList,
|
|
2159
|
+
sendKeyIndexList,
|
|
2160
|
+
storePrivacyTokens,
|
|
2161
|
+
executeUSyncQuery,
|
|
2162
|
+
newsletterServerIdCache: _nlServerIdCache
|
|
1814
2163
|
}
|
|
1815
2164
|
}
|
|
1816
2165
|
exports.makeChatsSocket = makeChatsSocket
|