@realvare/based 2.7.62 → 2.7.71
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 +1062 -282
- package/WAProto/WAProto.proto +1073 -244
- package/WAProto/index.d.ts +16282 -8183
- package/WAProto/index.js +76605 -50628
- package/engine-requirements.js +10 -10
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.d.ts +4 -2
- package/lib/Defaults/index.js +8 -6
- package/lib/Signal/Group/ciphertext-message.d.ts +1 -1
- package/lib/Signal/Group/ciphertext-message.js +1 -1
- package/lib/Signal/Group/sender-message-key.d.ts +1 -1
- package/lib/Signal/Group/sender-message-key.js +1 -1
- package/lib/Signal/libsignal.d.ts +1 -1
- package/lib/Socket/business.d.ts +1 -1
- package/lib/Socket/business.js +1 -1
- package/lib/Socket/chats.d.ts +4 -1
- package/lib/Socket/chats.js +213 -36
- package/lib/Socket/groups.js +87 -15
- package/lib/Socket/index.js +9 -0
- package/lib/Socket/messages-interactive.js +259 -0
- package/lib/Socket/messages-recv.js +1473 -1228
- package/lib/Socket/messages-send.js +437 -469
- package/lib/Socket/socket.js +143 -26
- package/lib/Socket/usync.js +57 -4
- package/lib/Store/make-in-memory-store.js +28 -15
- package/lib/Types/Auth.d.ts +4 -0
- package/lib/Types/Message.d.ts +316 -6
- package/lib/Types/Message.js +1 -1
- package/lib/Types/Socket.d.ts +2 -0
- package/lib/Utils/cache-manager.d.ts +16 -0
- package/lib/Utils/cache-manager.js +22 -5
- package/lib/Utils/chat-utils.js +17 -13
- package/lib/Utils/decode-wa-message.js +1 -11
- package/lib/Utils/event-buffer.js +103 -2
- package/lib/Utils/generics.js +5 -6
- package/lib/Utils/index.d.ts +5 -0
- package/lib/Utils/index.js +3 -0
- package/lib/Utils/jid-validation.d.ts +2 -0
- package/lib/Utils/jid-validation.js +43 -10
- package/lib/Utils/link-preview.js +38 -28
- package/lib/Utils/messages-media.d.ts +1 -1
- package/lib/Utils/messages-media.js +22 -53
- package/lib/Utils/messages.js +653 -65
- package/lib/Utils/performance-config.d.ts +2 -0
- package/lib/Utils/performance-config.js +16 -7
- package/lib/Utils/process-message.js +124 -12
- package/lib/Utils/rate-limiter.js +15 -20
- package/lib/WABinary/generic-utils.js +5 -1
- package/lib/WABinary/jid-utils.d.ts +1 -0
- package/lib/WABinary/jid-utils.js +265 -5
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +75 -5
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +59 -6
- package/lib/WAUSync/USyncQuery.js +64 -6
- package/lib/index.d.ts +1 -0
- package/lib/index.js +5 -4
- package/package.json +10 -15
- package/WAProto/index.ts.ts +0 -53473
package/engine-requirements.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const major = parseInt(process.versions.node.split('.')[0], 10);
|
|
2
|
-
|
|
3
|
-
if (major < 20) {
|
|
4
|
-
console.error(
|
|
5
|
-
`\n❌ This package requires Node.js 20+ to run reliably.\n` +
|
|
6
|
-
` You are using Node.js ${process.versions.node}.\n` +
|
|
7
|
-
` Please upgrade to Node.js 20+ to proceed.\n`
|
|
8
|
-
);
|
|
9
|
-
process.exit(1);
|
|
10
|
-
}
|
|
1
|
+
const major = parseInt(process.versions.node.split('.')[0], 10);
|
|
2
|
+
|
|
3
|
+
if (major < 20) {
|
|
4
|
+
console.error(
|
|
5
|
+
`\n❌ This package requires Node.js 20+ to run reliably.\n` +
|
|
6
|
+
` You are using Node.js ${process.versions.node}.\n` +
|
|
7
|
+
` Please upgrade to Node.js 20+ to proceed.\n`
|
|
8
|
+
);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
package/lib/Defaults/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const URL_REGEX: RegExp;
|
|
|
15
15
|
export declare const WA_CERT_DETAILS: {
|
|
16
16
|
SERIAL: number;
|
|
17
17
|
};
|
|
18
|
-
export declare const PROCESSABLE_HISTORY_TYPES: proto.Message.
|
|
18
|
+
export declare const PROCESSABLE_HISTORY_TYPES: proto.Message.HistorySyncType[];
|
|
19
19
|
export declare const DEFAULT_CONNECTION_CONFIG: SocketConfig;
|
|
20
20
|
export declare const MEDIA_PATH_MAP: {
|
|
21
21
|
[T in MediaType]?: string;
|
|
@@ -48,4 +48,6 @@ export declare const DEFAULT_CACHE_TTLS: {
|
|
|
48
48
|
MSG_RETRY: number;
|
|
49
49
|
CALL_OFFER: number;
|
|
50
50
|
USER_DEVICES: number;
|
|
51
|
-
|
|
51
|
+
LID_JID: number;
|
|
52
|
+
PROFILE_PIC: number;
|
|
53
|
+
};
|
package/lib/Defaults/index.js
CHANGED
|
@@ -18,18 +18,18 @@ exports.WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60;
|
|
|
18
18
|
exports.NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0';
|
|
19
19
|
exports.DICT_VERSION = 3;
|
|
20
20
|
exports.KEY_BUNDLE_TYPE = Buffer.from([5]);
|
|
21
|
-
exports.NOISE_WA_HEADER = Buffer.from([87, 65, 6, exports.DICT_VERSION]); // last
|
|
21
|
+
exports.NOISE_WA_HEADER = Buffer.from([87, 65, 6, exports.DICT_VERSION]); // last is "DICT_VERSION"
|
|
22
22
|
/** from: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url */
|
|
23
23
|
exports.URL_REGEX = /https:\/\/(?![^:@\/\s]+:[^:@\/\s]+@)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:\d+)?(\/[^\s]*)?/g;
|
|
24
24
|
exports.WA_CERT_DETAILS = {
|
|
25
25
|
SERIAL: 0,
|
|
26
26
|
};
|
|
27
27
|
exports.PROCESSABLE_HISTORY_TYPES = [
|
|
28
|
-
WAProto_1.proto.Message.
|
|
29
|
-
WAProto_1.proto.Message.
|
|
30
|
-
WAProto_1.proto.Message.
|
|
31
|
-
WAProto_1.proto.Message.
|
|
32
|
-
WAProto_1.proto.Message.
|
|
28
|
+
WAProto_1.proto.Message.HistorySyncType.INITIAL_BOOTSTRAP,
|
|
29
|
+
WAProto_1.proto.Message.HistorySyncType.PUSH_NAME,
|
|
30
|
+
WAProto_1.proto.Message.HistorySyncType.RECENT,
|
|
31
|
+
WAProto_1.proto.Message.HistorySyncType.FULL,
|
|
32
|
+
WAProto_1.proto.Message.HistorySyncType.ON_DEMAND,
|
|
33
33
|
];
|
|
34
34
|
exports.DEFAULT_CONNECTION_CONFIG = {
|
|
35
35
|
version: baileys_version_json_1.version,
|
|
@@ -103,4 +103,6 @@ exports.DEFAULT_CACHE_TTLS = {
|
|
|
103
103
|
MSG_RETRY: 60 * 60, // 1 hour
|
|
104
104
|
CALL_OFFER: 5 * 60, // 5 minutes
|
|
105
105
|
USER_DEVICES: 5 * 60, // 5 minutes
|
|
106
|
+
LID_JID: 5 * 60, // 5 minutes
|
|
107
|
+
PROFILE_PIC: 60 * 60, // 1 hour
|
|
106
108
|
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SignalAuthState } from '../Types';
|
|
2
2
|
import { SignalRepository } from '../Types/Signal';
|
|
3
|
-
export declare function makeLibSignalRepository(auth: SignalAuthState): SignalRepository;
|
|
3
|
+
export declare function makeLibSignalRepository(auth: SignalAuthState): SignalRepository;
|
package/lib/Socket/business.d.ts
CHANGED
|
@@ -169,4 +169,4 @@ export declare const makeBusinessSocket: (config: SocketConfig) => {
|
|
|
169
169
|
requestPairingCode: (phoneNumber: any, pairKey?: string) => Promise<string>;
|
|
170
170
|
waitForConnectionUpdate: (check: (u: Partial<import("../Types").ConnectionState>) => Promise<boolean | undefined>, timeoutMs?: number) => Promise<void>;
|
|
171
171
|
sendWAMBuffer: (wamBuffer: Buffer) => Promise<any>;
|
|
172
|
-
};
|
|
172
|
+
};
|
package/lib/Socket/business.js
CHANGED
package/lib/Socket/chats.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
|
|
|
28
28
|
updateProfileStatus: (status: string) => Promise<void>;
|
|
29
29
|
updateProfileName: (name: string) => Promise<void>;
|
|
30
30
|
updateBlockStatus: (jid: string, action: "block" | "unblock") => Promise<void>;
|
|
31
|
+
reportUser: (jid: string, reason?: "spam" | "harassment" | "impersonation" | "scam" | "other", waitForAckMs?: number) => Promise<boolean>;
|
|
32
|
+
reportMessage: (jid: string, messageId: string, reason?: "spam" | "harassment" | "impersonation" | "scam" | "other", participantJid?: string, waitForAckMs?: number) => Promise<boolean>;
|
|
33
|
+
reportAndBlockUser: (jid: string, reason?: "spam" | "harassment" | "impersonation" | "scam" | "other", waitForAckMs?: number) => Promise<boolean>;
|
|
31
34
|
updateCallPrivacy: (value: WAPrivacyCallValue) => Promise<void>;
|
|
32
35
|
updateMessagesPrivacy: (value: WAPrivacyMessagesValue) => Promise<void>;
|
|
33
36
|
updateLastSeenPrivacy: (value: WAPrivacyValue) => Promise<void>;
|
|
@@ -79,4 +82,4 @@ export declare const makeChatsSocket: (config: SocketConfig) => {
|
|
|
79
82
|
requestPairingCode: (phoneNumber: any, pairKey?: string) => Promise<string>;
|
|
80
83
|
waitForConnectionUpdate: (check: (u: Partial<import("../Types").ConnectionState>) => Promise<boolean | undefined>, timeoutMs?: number) => Promise<void>;
|
|
81
84
|
sendWAMBuffer: (wamBuffer: Buffer) => Promise<any>;
|
|
82
|
-
};
|
|
85
|
+
};
|
package/lib/Socket/chats.js
CHANGED
|
@@ -15,7 +15,9 @@ const process_message_1 = __importDefault(require("../Utils/process-message"));
|
|
|
15
15
|
const WABinary_1 = require("../WABinary");
|
|
16
16
|
const WAUSync_1 = require("../WAUSync");
|
|
17
17
|
const usync_1 = require("./usync");
|
|
18
|
+
const retry_1 = require("../Utils/retry");
|
|
18
19
|
const MAX_SYNC_ATTEMPTS = 2;
|
|
20
|
+
const APP_STATE_RESYNC_COOLDOWN_MS = 60000;
|
|
19
21
|
const makeChatsSocket = (config) => {
|
|
20
22
|
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config;
|
|
21
23
|
const sock = (0, usync_1.makeUSyncSocket)(config);
|
|
@@ -23,6 +25,8 @@ const makeChatsSocket = (config) => {
|
|
|
23
25
|
let privacySettings;
|
|
24
26
|
let needToFlushWithAppStateSync = false;
|
|
25
27
|
let pendingAppStateSync = false;
|
|
28
|
+
let lastSelfPresenceType;
|
|
29
|
+
let lastSelfPresenceTs = 0;
|
|
26
30
|
/** this mutex ensures that the notifications (receipts, messages etc.) are processed in order */
|
|
27
31
|
const processingMutex = (0, make_mutex_1.makeMutex)();
|
|
28
32
|
const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
|
|
@@ -32,12 +36,15 @@ const makeChatsSocket = (config) => {
|
|
|
32
36
|
if (!config.placeholderResendCache) {
|
|
33
37
|
config.placeholderResendCache = placeholderResendCache;
|
|
34
38
|
}
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const profilePictureUrlCache = config.profilePictureUrlCache || new node_cache_1.default({
|
|
40
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.PROFILE_PIC,
|
|
41
|
+
useClones: false
|
|
42
|
+
});
|
|
43
|
+
if (!config.profilePictureUrlCache) {
|
|
44
|
+
config.profilePictureUrlCache = profilePictureUrlCache;
|
|
45
|
+
}
|
|
46
|
+
const inFlightProfilePictureUrl = new Map();
|
|
47
|
+
const resolveJid = WABinary_1.resolveJid;
|
|
41
48
|
/** helper function to fetch the given app state sync key */
|
|
42
49
|
const getAppStateSyncKey = async (keyId) => {
|
|
43
50
|
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
|
|
@@ -293,6 +300,110 @@ const makeChatsSocket = (config) => {
|
|
|
293
300
|
]
|
|
294
301
|
});
|
|
295
302
|
};
|
|
303
|
+
const reportUser = async (jid, reason = 'spam', waitForAckMs = 5000) => {
|
|
304
|
+
jid = resolveJid(jid);
|
|
305
|
+
const reasonCodes = {
|
|
306
|
+
'spam': 1,
|
|
307
|
+
'harassment': 2,
|
|
308
|
+
'impersonation': 3,
|
|
309
|
+
'scam': 4,
|
|
310
|
+
'other': 5
|
|
311
|
+
};
|
|
312
|
+
const reasonCode = reasonCodes[reason] || 1;
|
|
313
|
+
const reportTo = WABinary_1.S_WHATSAPP_NET.replace(/^@/, '');
|
|
314
|
+
const node = {
|
|
315
|
+
tag: 'iq',
|
|
316
|
+
attrs: {
|
|
317
|
+
id: generateMessageTag(),
|
|
318
|
+
xmlns: 'w:report',
|
|
319
|
+
to: reportTo,
|
|
320
|
+
type: 'set'
|
|
321
|
+
},
|
|
322
|
+
content: [
|
|
323
|
+
{
|
|
324
|
+
tag: 'user',
|
|
325
|
+
attrs: {
|
|
326
|
+
jid,
|
|
327
|
+
reason: reasonCode.toString()
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
]
|
|
331
|
+
};
|
|
332
|
+
logger.debug({ node }, 'sending report user node');
|
|
333
|
+
try {
|
|
334
|
+
await query(node, waitForAckMs);
|
|
335
|
+
logger.info({ jid, reason }, 'reported user (ack received)');
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
const statusCode = err?.output?.statusCode || err?.statusCode;
|
|
340
|
+
const isTimeout = statusCode === 408 || (err?.message && err.message.toLowerCase().includes('timed out'));
|
|
341
|
+
if (isTimeout) {
|
|
342
|
+
logger.warn({ jid, reason }, 'reported user (no ack; request likely sent)');
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
throw err;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const reportMessage = async (jid, messageId, reason = 'spam', participantJid, waitForAckMs = 5000) => {
|
|
349
|
+
jid = resolveJid(jid);
|
|
350
|
+
if (participantJid) {
|
|
351
|
+
participantJid = resolveJid(participantJid);
|
|
352
|
+
}
|
|
353
|
+
const reasonCodes = {
|
|
354
|
+
'spam': 1,
|
|
355
|
+
'harassment': 2,
|
|
356
|
+
'impersonation': 3,
|
|
357
|
+
'scam': 4,
|
|
358
|
+
'other': 5
|
|
359
|
+
};
|
|
360
|
+
const reasonCode = reasonCodes[reason] || 1;
|
|
361
|
+
const reportTo = WABinary_1.S_WHATSAPP_NET.replace(/^@/, '');
|
|
362
|
+
const msgAttrs = {
|
|
363
|
+
id: messageId,
|
|
364
|
+
reason: reasonCode.toString()
|
|
365
|
+
};
|
|
366
|
+
if (participantJid) {
|
|
367
|
+
msgAttrs.participant = participantJid;
|
|
368
|
+
}
|
|
369
|
+
const node = {
|
|
370
|
+
tag: 'iq',
|
|
371
|
+
attrs: {
|
|
372
|
+
id: generateMessageTag(),
|
|
373
|
+
xmlns: 'w:report',
|
|
374
|
+
to: reportTo,
|
|
375
|
+
type: 'set'
|
|
376
|
+
},
|
|
377
|
+
content: [
|
|
378
|
+
{
|
|
379
|
+
tag: 'message',
|
|
380
|
+
attrs: msgAttrs
|
|
381
|
+
}
|
|
382
|
+
]
|
|
383
|
+
};
|
|
384
|
+
logger.debug({ node }, 'sending report message node');
|
|
385
|
+
try {
|
|
386
|
+
await query(node, waitForAckMs);
|
|
387
|
+
logger.info({ jid, messageId, reason, participantJid }, 'reported message (ack received)');
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
const statusCode = err?.output?.statusCode || err?.statusCode;
|
|
392
|
+
const isTimeout = statusCode === 408 || (err?.message && err.message.toLowerCase().includes('timed out'));
|
|
393
|
+
if (isTimeout) {
|
|
394
|
+
logger.warn({ jid, messageId, reason, participantJid }, 'reported message (no ack; request likely sent)');
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
throw err;
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const reportAndBlockUser = async (jid, reason = 'spam', waitForAckMs = 5000) => {
|
|
401
|
+
jid = resolveJid(jid);
|
|
402
|
+
const ack = await reportUser(jid, reason, waitForAckMs);
|
|
403
|
+
await updateBlockStatus(jid, 'block');
|
|
404
|
+
logger.info({ jid, reason, ack }, 'reported and blocked user');
|
|
405
|
+
return ack;
|
|
406
|
+
};
|
|
296
407
|
const getBusinessProfile = async (jid) => {
|
|
297
408
|
jid = resolveJid(jid);
|
|
298
409
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
@@ -367,14 +478,23 @@ const makeChatsSocket = (config) => {
|
|
|
367
478
|
}
|
|
368
479
|
};
|
|
369
480
|
};
|
|
481
|
+
const appStateResyncCooldown = new Map();
|
|
370
482
|
const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
|
|
483
|
+
const now = Date.now();
|
|
484
|
+
const collectionsToSync = collections.filter(name => {
|
|
485
|
+
const until = appStateResyncCooldown.get(name) || 0;
|
|
486
|
+
return until <= now;
|
|
487
|
+
});
|
|
488
|
+
if (!collectionsToSync.length) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
371
491
|
// we use this to determine which events to fire
|
|
372
492
|
// otherwise when we resync from scratch -- all notifications will fire
|
|
373
493
|
const initialVersionMap = {};
|
|
374
494
|
const globalMutationMap = {};
|
|
375
495
|
await authState.keys.transaction(async () => {
|
|
376
496
|
var _a;
|
|
377
|
-
const collectionsToHandle = new Set(
|
|
497
|
+
const collectionsToHandle = new Set(collectionsToSync);
|
|
378
498
|
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
|
379
499
|
const attemptsMap = {};
|
|
380
500
|
// keep executing till all collections are done
|
|
@@ -457,6 +577,9 @@ const makeChatsSocket = (config) => {
|
|
|
457
577
|
|| error.name === 'TypeError';
|
|
458
578
|
logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
|
|
459
579
|
await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
|
|
580
|
+
if (((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404) {
|
|
581
|
+
appStateResyncCooldown.set(name, Date.now() + APP_STATE_RESYNC_COOLDOWN_MS);
|
|
582
|
+
}
|
|
460
583
|
// increment number of retries
|
|
461
584
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
|
|
462
585
|
if (isIrrecoverableError) {
|
|
@@ -481,42 +604,86 @@ const makeChatsSocket = (config) => {
|
|
|
481
604
|
jid = resolveJid(jid);
|
|
482
605
|
var _a;
|
|
483
606
|
jid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
607
|
+
const cacheKey = `${jid}:${type}`;
|
|
608
|
+
const cached = profilePictureUrlCache.get(cacheKey);
|
|
609
|
+
if (typeof cached !== 'undefined') {
|
|
610
|
+
return cached || undefined;
|
|
611
|
+
}
|
|
612
|
+
const inFlight = inFlightProfilePictureUrl.get(cacheKey);
|
|
613
|
+
if (inFlight) {
|
|
614
|
+
return inFlight;
|
|
615
|
+
}
|
|
616
|
+
const fetchPromise = (async () => {
|
|
617
|
+
const result = await query({
|
|
618
|
+
tag: 'iq',
|
|
619
|
+
attrs: {
|
|
620
|
+
target: jid,
|
|
621
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
622
|
+
type: 'get',
|
|
623
|
+
xmlns: 'w:profile:picture'
|
|
624
|
+
},
|
|
625
|
+
content: [
|
|
626
|
+
{ tag: 'picture', attrs: { type, query: 'url' } }
|
|
627
|
+
]
|
|
628
|
+
}, timeoutMs);
|
|
629
|
+
const child = (0, WABinary_1.getBinaryNodeChild)(result, 'picture');
|
|
630
|
+
const url = (_a = child === null || child === void 0 ? void 0 : child.attrs) === null || _a === void 0 ? void 0 : _a.url;
|
|
631
|
+
profilePictureUrlCache.set(cacheKey, url || null);
|
|
632
|
+
return url;
|
|
633
|
+
})();
|
|
634
|
+
inFlightProfilePictureUrl.set(cacheKey, fetchPromise);
|
|
635
|
+
try {
|
|
636
|
+
return await fetchPromise;
|
|
637
|
+
}
|
|
638
|
+
finally {
|
|
639
|
+
inFlightProfilePictureUrl.delete(cacheKey);
|
|
640
|
+
}
|
|
498
641
|
};
|
|
499
642
|
const sendPresenceUpdate = async (type, toJid) => {
|
|
500
643
|
toJid = resolveJid(toJid);
|
|
501
644
|
const me = authState.creds.me;
|
|
645
|
+
const retryOptions = {
|
|
646
|
+
retries: 2,
|
|
647
|
+
baseMs: 1000,
|
|
648
|
+
maxMs: 5000,
|
|
649
|
+
jitter: true,
|
|
650
|
+
timeoutPerAttemptMs: 10000,
|
|
651
|
+
shouldRetry: (err) => {
|
|
652
|
+
if (err.message.includes('WebSocket is not open')) {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
var _a;
|
|
656
|
+
const status = ((_a = err.output) === null || _a === void 0 ? void 0 : _a.statusCode) || (err === null || err === void 0 ? void 0 : err.statusCode);
|
|
657
|
+
return !status || (status >= 500 || status === 408 || status === 429);
|
|
658
|
+
},
|
|
659
|
+
onRetry: (err, n) => logger === null || logger === void 0 ? void 0 : logger.warn({ err, attempt: n }, 'retrying presence update')
|
|
660
|
+
};
|
|
502
661
|
if (type === 'available' || type === 'unavailable') {
|
|
503
662
|
if (!me.name) {
|
|
504
663
|
logger.warn('no name present, ignoring presence update request...');
|
|
505
664
|
return;
|
|
506
665
|
}
|
|
666
|
+
if (!toJid) {
|
|
667
|
+
const now = Date.now();
|
|
668
|
+
if (lastSelfPresenceType === type && (now - lastSelfPresenceTs) < 3000) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
lastSelfPresenceType = type;
|
|
672
|
+
lastSelfPresenceTs = now;
|
|
673
|
+
}
|
|
507
674
|
ev.emit('connection.update', { isOnline: type === 'available' });
|
|
508
|
-
await sendNode({
|
|
675
|
+
await (0, retry_1.retryWithBackoff)(() => sendNode({
|
|
509
676
|
tag: 'presence',
|
|
510
677
|
attrs: {
|
|
511
678
|
name: me.name.replace(/@/g, ''),
|
|
512
679
|
type
|
|
513
680
|
}
|
|
514
|
-
});
|
|
681
|
+
}), retryOptions);
|
|
515
682
|
}
|
|
516
683
|
else {
|
|
517
684
|
const { server } = (0, WABinary_1.jidDecode)(toJid);
|
|
518
685
|
const isLid = server === 'lid';
|
|
519
|
-
await sendNode({
|
|
686
|
+
await (0, retry_1.retryWithBackoff)(() => sendNode({
|
|
520
687
|
tag: 'chatstate',
|
|
521
688
|
attrs: {
|
|
522
689
|
from: isLid ? me.lid : me.id,
|
|
@@ -528,7 +695,7 @@ const makeChatsSocket = (config) => {
|
|
|
528
695
|
attrs: type === 'recording' ? { media: 'audio' } : {}
|
|
529
696
|
}
|
|
530
697
|
]
|
|
531
|
-
});
|
|
698
|
+
}), retryOptions);
|
|
532
699
|
}
|
|
533
700
|
};
|
|
534
701
|
/**
|
|
@@ -754,22 +921,29 @@ const makeChatsSocket = (config) => {
|
|
|
754
921
|
const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
|
|
755
922
|
var _a, _b, _c;
|
|
756
923
|
try {
|
|
757
|
-
const
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
924
|
+
const r0 = msg?.key?.remoteJid;
|
|
925
|
+
const p0 = msg?.key?.participant;
|
|
926
|
+
|
|
927
|
+
if (r0) {
|
|
928
|
+
const rd = (0, WABinary_1.jidDecode)(r0);
|
|
761
929
|
if (rd?.user) {
|
|
762
|
-
|
|
763
|
-
msg.key.remoteLid = lid;
|
|
764
|
-
msg.key.remoteJidNormalized = (0, WABinary_1.jidNormalizedUser)(r);
|
|
930
|
+
// preserve LID form (original if provided, otherwise derived)
|
|
931
|
+
msg.key.remoteLid = (0, WABinary_1.isLid)(r0) ? r0 : (0, WABinary_1.jidEncode)(rd.user, 'lid');
|
|
765
932
|
}
|
|
933
|
+
// always expose a standard jid to consumers
|
|
934
|
+
msg.key.remoteJid = (0, WABinary_1.jidNormalizedUser)((0, WABinary_1.lidToJid)(r0));
|
|
935
|
+
msg.key.remoteJidNormalized = msg.key.remoteJid;
|
|
766
936
|
}
|
|
767
|
-
|
|
768
|
-
|
|
937
|
+
|
|
938
|
+
if (p0) {
|
|
939
|
+
const pd = (0, WABinary_1.jidDecode)(p0);
|
|
769
940
|
if (pd?.user) {
|
|
770
|
-
|
|
771
|
-
msg.key.participantLid = lidP;
|
|
941
|
+
msg.key.participantLid = (0, WABinary_1.isLid)(p0) ? p0 : (0, WABinary_1.jidEncode)(pd.user, 'lid');
|
|
772
942
|
}
|
|
943
|
+
msg.key.participant = (0, WABinary_1.jidNormalizedUser)((0, WABinary_1.lidToJid)(p0));
|
|
944
|
+
}
|
|
945
|
+
if (msg === null || msg === void 0 ? void 0 : msg.participant) {
|
|
946
|
+
msg.participant = (0, WABinary_1.jidNormalizedUser)((0, WABinary_1.lidToJid)(msg.participant));
|
|
773
947
|
}
|
|
774
948
|
} catch {}
|
|
775
949
|
ev.emit('messages.upsert', { messages: [msg], type });
|
|
@@ -894,6 +1068,9 @@ const makeChatsSocket = (config) => {
|
|
|
894
1068
|
updateProfileStatus,
|
|
895
1069
|
updateProfileName,
|
|
896
1070
|
updateBlockStatus,
|
|
1071
|
+
reportUser,
|
|
1072
|
+
reportMessage,
|
|
1073
|
+
reportAndBlockUser,
|
|
897
1074
|
updateCallPrivacy,
|
|
898
1075
|
updateMessagesPrivacy,
|
|
899
1076
|
updateLastSeenPrivacy,
|
package/lib/Socket/groups.js
CHANGED
|
@@ -6,15 +6,14 @@ const Types_1 = require("../Types");
|
|
|
6
6
|
const Utils_1 = require("../Utils");
|
|
7
7
|
const WABinary_1 = require("../WABinary");
|
|
8
8
|
const chats_1 = require("./chats");
|
|
9
|
+
const { getPerformanceConfig } = require('../Utils/performance-config');
|
|
10
|
+
const { CacheManager } = require('../Utils/cache-manager');
|
|
9
11
|
const makeGroupsSocket = (config) => {
|
|
10
12
|
const sock = (0, chats_1.makeChatsSocket)(config);
|
|
11
13
|
const { authState, ev, query, upsertMessage } = sock;
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
return jid;
|
|
17
|
-
};
|
|
14
|
+
const inFlightGroupMetadata = new Map();
|
|
15
|
+
const forbiddenGroupMetadataUntil = new Map();
|
|
16
|
+
const resolveJid = WABinary_1.resolveJid;
|
|
18
17
|
const groupQuery = async (jid, type, content) => (query({
|
|
19
18
|
tag: 'iq',
|
|
20
19
|
attrs: {
|
|
@@ -25,16 +24,72 @@ const makeGroupsSocket = (config) => {
|
|
|
25
24
|
content
|
|
26
25
|
}));
|
|
27
26
|
const groupMetadata = async (jid) => {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
const config = getPerformanceConfig();
|
|
28
|
+
|
|
29
|
+
const forbiddenUntil = forbiddenGroupMetadataUntil.get(jid);
|
|
30
|
+
if (forbiddenUntil && forbiddenUntil > Date.now()) {
|
|
31
|
+
const err = new Error('forbidden');
|
|
32
|
+
err.data = 403;
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
else if (forbiddenUntil) {
|
|
36
|
+
forbiddenGroupMetadataUntil.delete(jid);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check cache first
|
|
40
|
+
const cached = CacheManager.get('groupMetadataCache', jid);
|
|
41
|
+
if (cached) {
|
|
42
|
+
config.logger?.trace({ jid }, 'using cached group metadata');
|
|
43
|
+
return cached;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// If a fetch is already in progress for this group, reuse it
|
|
47
|
+
const inFlight = inFlightGroupMetadata.get(jid);
|
|
48
|
+
if (inFlight) {
|
|
49
|
+
return inFlight;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const fetchPromise = (async () => {
|
|
53
|
+
// Fetch from server with rate limiting and retry logic
|
|
54
|
+
let retryCount = 0;
|
|
55
|
+
const maxRetries = 3;
|
|
56
|
+
const baseDelay = 1000; // 1 second
|
|
57
|
+
|
|
58
|
+
while (retryCount <= maxRetries) {
|
|
59
|
+
try {
|
|
60
|
+
const result = await groupQuery(jid, 'get', [{ tag: 'query', attrs: { request: 'interactive' } }]);
|
|
61
|
+
const metadata = (0, exports.extractGroupMetadata)(result);
|
|
62
|
+
|
|
63
|
+
// Cache the result
|
|
64
|
+
CacheManager.set('groupMetadataCache', jid, metadata, config.cache.groupMetadataCache.ttl);
|
|
65
|
+
|
|
66
|
+
return metadata;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const statusCode = error?.output?.statusCode || error?.data;
|
|
69
|
+
if (statusCode === 403 || error?.message === 'forbidden') {
|
|
70
|
+
const cooldownMs = 5 * 60 * 1000;
|
|
71
|
+
forbiddenGroupMetadataUntil.set(jid, Date.now() + cooldownMs);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
if (error.message === 'rate-overlimit' && retryCount < maxRetries) {
|
|
75
|
+
const delay = baseDelay * Math.pow(2, retryCount); // Exponential backoff
|
|
76
|
+
config.logger?.warn({ jid, retryCount, delay }, 'Rate limit hit, retrying group metadata fetch');
|
|
77
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
78
|
+
retryCount++;
|
|
79
|
+
} else {
|
|
80
|
+
// Re-throw non-rate-limit errors or if max retries exceeded
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
34
83
|
}
|
|
35
84
|
}
|
|
85
|
+
})();
|
|
86
|
+
|
|
87
|
+
inFlightGroupMetadata.set(jid, fetchPromise);
|
|
88
|
+
try {
|
|
89
|
+
return await fetchPromise;
|
|
90
|
+
} finally {
|
|
91
|
+
inFlightGroupMetadata.delete(jid);
|
|
36
92
|
}
|
|
37
|
-
return metadata;
|
|
38
93
|
};
|
|
39
94
|
const groupFetchAllParticipating = async () => {
|
|
40
95
|
const result = await query({
|
|
@@ -79,6 +134,23 @@ const makeGroupsSocket = (config) => {
|
|
|
79
134
|
await groupFetchAllParticipating();
|
|
80
135
|
await sock.cleanDirtyBits('groups');
|
|
81
136
|
});
|
|
137
|
+
|
|
138
|
+
// Cache invalidation on group events
|
|
139
|
+
ev.on('groups.update', async (groups) => {
|
|
140
|
+
for (const group of groups) {
|
|
141
|
+
CacheManager.del('groupMetadataCache', group.id);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
ev.on('group-participants.update', async (update) => {
|
|
146
|
+
CacheManager.del('groupMetadataCache', update.id);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
ev.on('groups.upsert', async (groups) => {
|
|
150
|
+
for (const group of groups) {
|
|
151
|
+
CacheManager.del('groupMetadataCache', group.id);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
82
154
|
return {
|
|
83
155
|
...sock,
|
|
84
156
|
groupQuery,
|
|
@@ -339,7 +411,7 @@ const extractGroupMetadata = (result) => {
|
|
|
339
411
|
participants: (0, WABinary_1.getBinaryNodeChildren)(group, 'participant').map(({ attrs }) => {
|
|
340
412
|
return {
|
|
341
413
|
id: attrs.jid,
|
|
342
|
-
jid: attrs.phone_number || attrs.jid,
|
|
414
|
+
jid: attrs.phone_number || (0, WABinary_1.lidToJid)(attrs.jid),
|
|
343
415
|
lid: attrs.lid || ((0, WABinary_1.isLid)(attrs.jid) ? attrs.jid : undefined),
|
|
344
416
|
admin: (attrs.type || null),
|
|
345
417
|
};
|
|
@@ -348,4 +420,4 @@ const extractGroupMetadata = (result) => {
|
|
|
348
420
|
};
|
|
349
421
|
return metadata;
|
|
350
422
|
};
|
|
351
|
-
exports.extractGroupMetadata = extractGroupMetadata;
|
|
423
|
+
exports.extractGroupMetadata = extractGroupMetadata;
|
package/lib/Socket/index.js
CHANGED
|
@@ -8,10 +8,19 @@ const Defaults_1 = require("../Defaults");
|
|
|
8
8
|
const business_1 = require("./business");
|
|
9
9
|
// export the last socket layer
|
|
10
10
|
const makeWASocket = (config) => {
|
|
11
|
+
const inputConfig = config;
|
|
12
|
+
const hasBrowserOverride = Object.prototype.hasOwnProperty.call(inputConfig, 'browser');
|
|
11
13
|
config = {
|
|
12
14
|
...Defaults_1.DEFAULT_CONNECTION_CONFIG,
|
|
13
15
|
...config,
|
|
14
16
|
};
|
|
17
|
+
const creds = config.auth?.creds;
|
|
18
|
+
if (!hasBrowserOverride && creds?.browser) {
|
|
19
|
+
config.browser = creds.browser;
|
|
20
|
+
}
|
|
21
|
+
else if (creds && !creds.browser) {
|
|
22
|
+
creds.browser = config.browser;
|
|
23
|
+
}
|
|
15
24
|
if (!config.lidCache) {
|
|
16
25
|
config.lidCache = new node_cache_1.default({
|
|
17
26
|
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.LID_JID,
|