Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jajbshjahavahh
GitHub Repository: jajbshjahavahh/Gojo-Satoru
Path: blob/master/node_modules/@adiwajshing/baileys/lib/Socket/chats.js
2593 views
1
"use strict";
2
Object.defineProperty(exports, "__esModule", { value: true });
3
exports.makeChatsSocket = void 0;
4
const boom_1 = require("@hapi/boom");
5
const WAProto_1 = require("../../WAProto");
6
const Utils_1 = require("../Utils");
7
const make_mutex_1 = require("../Utils/make-mutex");
8
const WABinary_1 = require("../WABinary");
9
const messages_send_1 = require("./messages-send");
10
const MAX_SYNC_ATTEMPTS = 5;
11
const makeChatsSocket = (config) => {
12
const { logger } = config;
13
const sock = messages_send_1.makeMessagesSocket(config);
14
const { ev, ws, authState, generateMessageTag, sendNode, query, fetchPrivacySettings, } = sock;
15
const mutationMutex = make_mutex_1.makeMutex();
16
const getAppStateSyncKey = async (keyId) => {
17
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
18
return key;
19
};
20
const interactiveQuery = async (userNodes, queryNode) => {
21
const result = await query({
22
tag: 'iq',
23
attrs: {
24
to: WABinary_1.S_WHATSAPP_NET,
25
type: 'get',
26
xmlns: 'usync',
27
},
28
content: [
29
{
30
tag: 'usync',
31
attrs: {
32
sid: generateMessageTag(),
33
mode: 'query',
34
last: 'true',
35
index: '0',
36
context: 'interactive',
37
},
38
content: [
39
{
40
tag: 'query',
41
attrs: {},
42
content: [queryNode]
43
},
44
{
45
tag: 'list',
46
attrs: {},
47
content: userNodes
48
}
49
]
50
}
51
],
52
});
53
const usyncNode = WABinary_1.getBinaryNodeChild(result, 'usync');
54
const listNode = WABinary_1.getBinaryNodeChild(usyncNode, 'list');
55
const users = WABinary_1.getBinaryNodeChildren(listNode, 'user');
56
return users;
57
};
58
const onWhatsApp = async (...jids) => {
59
const results = await interactiveQuery([
60
{
61
tag: 'user',
62
attrs: {},
63
content: jids.map(jid => ({
64
tag: 'contact',
65
attrs: {},
66
content: `+${jid}`
67
}))
68
}
69
], { tag: 'contact', attrs: {} });
70
return results.map(user => {
71
const contact = WABinary_1.getBinaryNodeChild(user, 'contact');
72
return { exists: contact.attrs.type === 'in', jid: user.attrs.jid };
73
}).filter(item => item.exists);
74
};
75
const fetchStatus = async (jid) => {
76
const [result] = await interactiveQuery([{ tag: 'user', attrs: { jid } }], { tag: 'status', attrs: {} });
77
if (result) {
78
const status = WABinary_1.getBinaryNodeChild(result, 'status');
79
return {
80
status: status.content.toString(),
81
setAt: new Date(+status.attrs.t * 1000)
82
};
83
}
84
};
85
const updateProfilePicture = async (jid, content) => {
86
const { img } = await Utils_1.generateProfilePicture(content);
87
await query({
88
tag: 'iq',
89
attrs: {
90
to: WABinary_1.jidNormalizedUser(jid),
91
type: 'set',
92
xmlns: 'w:profile:picture'
93
},
94
content: [
95
{
96
tag: 'picture',
97
attrs: { type: 'image' },
98
content: img
99
}
100
]
101
});
102
};
103
const fetchBlocklist = async () => {
104
var _a, _b;
105
const result = await query({
106
tag: 'iq',
107
attrs: {
108
xmlns: 'blocklist',
109
to: WABinary_1.S_WHATSAPP_NET,
110
type: 'get'
111
}
112
});
113
const child = (_a = result.content) === null || _a === void 0 ? void 0 : _a[0];
114
return (_b = child.content) === null || _b === void 0 ? void 0 : _b.map(i => i.attrs.jid);
115
};
116
const updateBlockStatus = async (jid, action) => {
117
await query({
118
tag: 'iq',
119
attrs: {
120
xmlns: 'blocklist',
121
to: WABinary_1.S_WHATSAPP_NET,
122
type: 'set'
123
},
124
content: [
125
{
126
tag: 'item',
127
attrs: {
128
action,
129
jid
130
}
131
}
132
]
133
});
134
};
135
const getBusinessProfile = async (jid) => {
136
var _a, _b;
137
const results = await query({
138
tag: 'iq',
139
attrs: {
140
to: 's.whatsapp.net',
141
xmlns: 'w:biz',
142
type: 'get'
143
},
144
content: [{
145
tag: 'business_profile',
146
attrs: { v: '244' },
147
content: [{
148
tag: 'profile',
149
attrs: { jid }
150
}]
151
}]
152
});
153
const profiles = WABinary_1.getBinaryNodeChild(WABinary_1.getBinaryNodeChild(results, 'business_profile'), 'profile');
154
if (!profiles) {
155
// if not bussines
156
if (logger.level === 'trace') {
157
logger.trace({ jid }, 'Not bussines');
158
}
159
return;
160
}
161
const address = WABinary_1.getBinaryNodeChild(profiles, 'address');
162
const description = WABinary_1.getBinaryNodeChild(profiles, 'description');
163
const website = WABinary_1.getBinaryNodeChild(profiles, 'website');
164
const email = WABinary_1.getBinaryNodeChild(profiles, 'email');
165
const category = WABinary_1.getBinaryNodeChild(WABinary_1.getBinaryNodeChild(profiles, 'categories'), 'category');
166
const business_hours = WABinary_1.getBinaryNodeChild(profiles, 'business_hours');
167
const business_hours_config = business_hours && WABinary_1.getBinaryNodeChildren(business_hours, 'business_hours_config');
168
return {
169
wid: (_a = profiles.attrs) === null || _a === void 0 ? void 0 : _a.jid,
170
address: address === null || address === void 0 ? void 0 : address.content.toString(),
171
description: description === null || description === void 0 ? void 0 : description.content.toString(),
172
website: [website === null || website === void 0 ? void 0 : website.content.toString()],
173
email: email === null || email === void 0 ? void 0 : email.content.toString(),
174
category: category === null || category === void 0 ? void 0 : category.content.toString(),
175
business_hours: {
176
timezone: (_b = business_hours === null || business_hours === void 0 ? void 0 : business_hours.attrs) === null || _b === void 0 ? void 0 : _b.timezone,
177
business_config: business_hours_config === null || business_hours_config === void 0 ? void 0 : business_hours_config.map(({ attrs }) => attrs)
178
}
179
};
180
};
181
const updateAccountSyncTimestamp = async (fromTimestamp) => {
182
logger.info({ fromTimestamp }, 'requesting account sync');
183
await sendNode({
184
tag: 'iq',
185
attrs: {
186
to: WABinary_1.S_WHATSAPP_NET,
187
type: 'set',
188
xmlns: 'urn:xmpp:whatsapp:dirty',
189
id: generateMessageTag(),
190
},
191
content: [
192
{
193
tag: 'clean',
194
attrs: {
195
type: 'account_sync',
196
timestamp: fromTimestamp.toString(),
197
}
198
}
199
]
200
});
201
};
202
const resyncAppState = async (collections) => {
203
const appStateChunk = { totalMutations: [], collectionsToHandle: [] };
204
// we use this to determine which events to fire
205
// otherwise when we resync from scratch -- all notifications will fire
206
const initialVersionMap = {};
207
await authState.keys.transaction(async () => {
208
var _a;
209
const collectionsToHandle = new Set(collections);
210
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
211
const attemptsMap = {};
212
// keep executing till all collections are done
213
// sometimes a single patch request will not return all the patches (God knows why)
214
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
215
while (collectionsToHandle.size) {
216
const states = {};
217
const nodes = [];
218
for (const name of collectionsToHandle) {
219
const result = await authState.keys.get('app-state-sync-version', [name]);
220
let state = result[name];
221
if (state) {
222
if (typeof initialVersionMap[name] === 'undefined') {
223
initialVersionMap[name] = state.version;
224
}
225
}
226
else {
227
state = Utils_1.newLTHashState();
228
}
229
states[name] = state;
230
logger.info(`resyncing ${name} from v${state.version}`);
231
nodes.push({
232
tag: 'collection',
233
attrs: {
234
name,
235
version: state.version.toString(),
236
// return snapshot if being synced from scratch
237
return_snapshot: (!state.version).toString()
238
}
239
});
240
}
241
const result = await query({
242
tag: 'iq',
243
attrs: {
244
to: WABinary_1.S_WHATSAPP_NET,
245
xmlns: 'w:sync:app:state',
246
type: 'set'
247
},
248
content: [
249
{
250
tag: 'sync',
251
attrs: {},
252
content: nodes
253
}
254
]
255
});
256
const decoded = await Utils_1.extractSyncdPatches(result); // extract from binary node
257
for (const key in decoded) {
258
const name = key;
259
const { patches, hasMorePatches, snapshot } = decoded[name];
260
try {
261
if (snapshot) {
262
const { state: newState, mutations } = await Utils_1.decodeSyncdSnapshot(name, snapshot, getAppStateSyncKey, initialVersionMap[name]);
263
states[name] = newState;
264
logger.info(`restored state of ${name} from snapshot to v${newState.version} with ${mutations.length} mutations`);
265
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
266
appStateChunk.totalMutations.push(...mutations);
267
}
268
// only process if there are syncd patches
269
if (patches.length) {
270
const { newMutations, state: newState } = await Utils_1.decodePatches(name, patches, states[name], getAppStateSyncKey, initialVersionMap[name]);
271
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
272
logger.info(`synced ${name} to v${newState.version}`);
273
if (newMutations.length) {
274
logger.trace({ newMutations, name }, 'recv new mutations');
275
}
276
appStateChunk.totalMutations.push(...newMutations);
277
}
278
if (hasMorePatches) {
279
logger.info(`${name} has more patches...`);
280
}
281
else { // collection is done with sync
282
collectionsToHandle.delete(name);
283
}
284
}
285
catch (error) {
286
logger.info({ name, error: error.stack }, 'failed to sync state from version, removing and trying from scratch');
287
await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
288
// increment number of retries
289
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
290
// if retry attempts overshoot
291
// or key not found
292
if (attemptsMap[name] >= MAX_SYNC_ATTEMPTS || ((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404) {
293
// stop retrying
294
collectionsToHandle.delete(name);
295
}
296
}
297
}
298
}
299
});
300
processSyncActions(appStateChunk.totalMutations);
301
return appStateChunk;
302
};
303
/**
304
* fetch the profile picture of a user/group
305
* type = "preview" for a low res picture
306
* type = "image for the high res picture"
307
*/
308
const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
309
var _a;
310
jid = WABinary_1.jidNormalizedUser(jid);
311
const result = await query({
312
tag: 'iq',
313
attrs: {
314
to: jid,
315
type: 'get',
316
xmlns: 'w:profile:picture'
317
},
318
content: [
319
{ tag: 'picture', attrs: { type, query: 'url' } }
320
]
321
}, timeoutMs);
322
const child = WABinary_1.getBinaryNodeChild(result, 'picture');
323
return (_a = child === null || child === void 0 ? void 0 : child.attrs) === null || _a === void 0 ? void 0 : _a.url;
324
};
325
const sendPresenceUpdate = async (type, toJid) => {
326
const me = authState.creds.me;
327
if (type === 'available' || type === 'unavailable') {
328
await sendNode({
329
tag: 'presence',
330
attrs: {
331
name: me.name,
332
type
333
}
334
});
335
}
336
else {
337
await sendNode({
338
tag: 'chatstate',
339
attrs: {
340
from: me.id,
341
to: toJid,
342
},
343
content: [
344
{ tag: type, attrs: {} }
345
]
346
});
347
}
348
};
349
const presenceSubscribe = (toJid) => (sendNode({
350
tag: 'presence',
351
attrs: {
352
to: toJid,
353
id: generateMessageTag(),
354
type: 'subscribe'
355
}
356
}));
357
const handlePresenceUpdate = ({ tag, attrs, content }) => {
358
let presence;
359
const jid = attrs.from;
360
const participant = attrs.participant || attrs.from;
361
if (tag === 'presence') {
362
presence = {
363
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
364
lastSeen: attrs.t ? +attrs.t : undefined
365
};
366
}
367
else if (Array.isArray(content)) {
368
const [firstChild] = content;
369
let type = firstChild.tag;
370
if (type === 'paused') {
371
type = 'available';
372
}
373
presence = { lastKnownPresence: type };
374
}
375
else {
376
logger.error({ tag, attrs, content }, 'recv invalid presence node');
377
}
378
if (presence) {
379
ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
380
}
381
};
382
const resyncMainAppState = async () => {
383
logger.debug('resyncing main app state');
384
await (mutationMutex.mutex(() => resyncAppState([
385
'critical_block',
386
'critical_unblock_low',
387
'regular_high',
388
'regular_low',
389
'regular'
390
]))
391
.catch(err => (logger.warn({ trace: err.stack }, 'failed to sync app state'))));
392
};
393
const processSyncActions = (actions) => {
394
var _a, _b, _c, _d, _e;
395
const updates = {};
396
const contactUpdates = {};
397
const msgDeletes = [];
398
for (const { syncAction: { value: action }, index: [_, id, msgId, fromMe] } of actions) {
399
const update = { id };
400
if (action === null || action === void 0 ? void 0 : action.muteAction) {
401
update.mute = ((_a = action.muteAction) === null || _a === void 0 ? void 0 : _a.muted) ?
402
Utils_1.toNumber(action.muteAction.muteEndTimestamp) :
403
undefined;
404
}
405
else if (action === null || action === void 0 ? void 0 : action.archiveChatAction) {
406
update.archive = !!((_b = action.archiveChatAction) === null || _b === void 0 ? void 0 : _b.archived);
407
}
408
else if (action === null || action === void 0 ? void 0 : action.markChatAsReadAction) {
409
update.unreadCount = !!((_c = action.markChatAsReadAction) === null || _c === void 0 ? void 0 : _c.read) ? 0 : -1;
410
}
411
else if (action === null || action === void 0 ? void 0 : action.clearChatAction) {
412
msgDeletes.push({
413
remoteJid: id,
414
id: msgId,
415
fromMe: fromMe === '1'
416
});
417
}
418
else if (action === null || action === void 0 ? void 0 : action.contactAction) {
419
contactUpdates[id] = {
420
...(contactUpdates[id] || {}),
421
id,
422
name: action.contactAction.fullName
423
};
424
}
425
else if (action === null || action === void 0 ? void 0 : action.pushNameSetting) {
426
const me = {
427
...authState.creds.me,
428
name: (_d = action === null || action === void 0 ? void 0 : action.pushNameSetting) === null || _d === void 0 ? void 0 : _d.name
429
};
430
ev.emit('creds.update', { me });
431
}
432
else if (action === null || action === void 0 ? void 0 : action.pinAction) {
433
update.pin = ((_e = action.pinAction) === null || _e === void 0 ? void 0 : _e.pinned) ? Utils_1.toNumber(action.timestamp) : undefined;
434
}
435
else {
436
logger.warn({ action, id }, 'unprocessable update');
437
}
438
if (Object.keys(update).length > 1) {
439
updates[update.id] = {
440
...(updates[update.id] || {}),
441
...update
442
};
443
}
444
}
445
if (Object.values(updates).length) {
446
ev.emit('chats.update', Object.values(updates));
447
}
448
if (Object.values(contactUpdates).length) {
449
ev.emit('contacts.upsert', Object.values(contactUpdates));
450
}
451
if (msgDeletes.length) {
452
ev.emit('messages.delete', { keys: msgDeletes });
453
}
454
};
455
const appPatch = async (patchCreate) => {
456
const name = patchCreate.type;
457
const myAppStateKeyId = authState.creds.myAppStateKeyId;
458
if (!myAppStateKeyId) {
459
throw new boom_1.Boom('App state key not present!', { statusCode: 400 });
460
}
461
await mutationMutex.mutex(async () => {
462
logger.debug({ patch: patchCreate }, 'applying app patch');
463
await resyncAppState([name]);
464
const { [name]: initial } = await authState.keys.get('app-state-sync-version', [name]);
465
const { patch, state } = await Utils_1.encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
466
const node = {
467
tag: 'iq',
468
attrs: {
469
to: WABinary_1.S_WHATSAPP_NET,
470
type: 'set',
471
xmlns: 'w:sync:app:state'
472
},
473
content: [
474
{
475
tag: 'sync',
476
attrs: {},
477
content: [
478
{
479
tag: 'collection',
480
attrs: {
481
name,
482
version: (state.version - 1).toString(),
483
return_snapshot: 'false'
484
},
485
content: [
486
{
487
tag: 'patch',
488
attrs: {},
489
content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
490
}
491
]
492
}
493
]
494
}
495
]
496
};
497
await query(node);
498
await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
499
if (config.emitOwnEvents) {
500
const result = await Utils_1.decodePatches(name, [{ ...patch, version: { version: state.version }, }], initial, getAppStateSyncKey);
501
processSyncActions(result.newMutations);
502
}
503
});
504
};
505
/** sending abt props may fix QR scan fail if server expects */
506
const fetchAbt = async () => {
507
const abtNode = await query({
508
tag: 'iq',
509
attrs: {
510
to: WABinary_1.S_WHATSAPP_NET,
511
xmlns: 'abt',
512
type: 'get',
513
id: generateMessageTag(),
514
},
515
content: [
516
{ tag: 'props', attrs: { protocol: '1' } }
517
]
518
});
519
const propsNode = WABinary_1.getBinaryNodeChild(abtNode, 'props');
520
let props = {};
521
if (propsNode) {
522
props = WABinary_1.reduceBinaryNodeToDictionary(propsNode, 'prop');
523
}
524
logger.debug('fetched abt');
525
return props;
526
};
527
/** sending non-abt props may fix QR scan fail if server expects */
528
const fetchProps = async () => {
529
const resultNode = await query({
530
tag: 'iq',
531
attrs: {
532
to: WABinary_1.S_WHATSAPP_NET,
533
xmlns: 'w',
534
type: 'get',
535
id: generateMessageTag(),
536
},
537
content: [
538
{ tag: 'props', attrs: {} }
539
]
540
});
541
const propsNode = WABinary_1.getBinaryNodeChild(resultNode, 'props');
542
let props = {};
543
if (propsNode) {
544
props = WABinary_1.reduceBinaryNodeToDictionary(propsNode, 'prop');
545
}
546
logger.debug('fetched props');
547
return props;
548
};
549
/**
550
* modify a chat -- mark unread, read etc.
551
* lastMessages must be sorted in reverse chronologically
552
* requires the last messages till the last message received; required for archive & unread
553
*/
554
const chatModify = (mod, jid) => {
555
const patch = Utils_1.chatModificationToAppPatch(mod, jid);
556
return appPatch(patch);
557
};
558
ws.on('CB:presence', handlePresenceUpdate);
559
ws.on('CB:chatstate', handlePresenceUpdate);
560
ws.on('CB:ib,,dirty', async (node) => {
561
const { attrs } = WABinary_1.getBinaryNodeChild(node, 'dirty');
562
const type = attrs.type;
563
switch (type) {
564
case 'account_sync':
565
let { lastAccountSyncTimestamp } = authState.creds;
566
if (lastAccountSyncTimestamp) {
567
await updateAccountSyncTimestamp(lastAccountSyncTimestamp);
568
}
569
lastAccountSyncTimestamp = +attrs.timestamp;
570
ev.emit('creds.update', { lastAccountSyncTimestamp });
571
break;
572
default:
573
logger.info({ node }, 'received unknown sync');
574
break;
575
}
576
});
577
ws.on('CB:notification,type:server_sync', (node) => {
578
const update = WABinary_1.getBinaryNodeChild(node, 'collection');
579
if (update) {
580
const name = update.attrs.name;
581
mutationMutex.mutex(async () => {
582
await resyncAppState([name])
583
.catch(err => logger.error({ trace: err.stack, node }, 'failed to sync state'));
584
});
585
}
586
});
587
ev.on('connection.update', ({ connection }) => {
588
if (connection === 'open') {
589
sendPresenceUpdate('available');
590
fetchBlocklist();
591
fetchPrivacySettings();
592
fetchAbt();
593
fetchProps();
594
}
595
});
596
return {
597
...sock,
598
appPatch,
599
sendPresenceUpdate,
600
presenceSubscribe,
601
profilePictureUrl,
602
onWhatsApp,
603
fetchBlocklist,
604
fetchStatus,
605
updateProfilePicture,
606
updateBlockStatus,
607
getBusinessProfile,
608
resyncAppState,
609
chatModify,
610
resyncMainAppState,
611
};
612
};
613
exports.makeChatsSocket = makeChatsSocket;
614
615