violetics 7.0.12-alpha → 7.0.14-alpha
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/lib/Socket/Client/websocket.js +10 -55
- package/lib/Socket/chats.js +5 -4
- package/lib/Socket/messages-recv.js +26 -25
- package/lib/Socket/messages-send.js +9 -7
- package/lib/Socket/socket.js +76 -65
- package/lib/Types/index.js +3 -0
- package/lib/Utils/auth-utils.js +5 -5
- package/lib/Utils/event-buffer.js +25 -8
- package/lib/Utils/generics.js +4 -1
- package/lib/Utils/identity-change-handler.js +1 -1
- package/lib/Utils/noise-handler.js +12 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ export class WebSocketClient extends AbstractSocketClient {
|
|
|
5
5
|
constructor() {
|
|
6
6
|
super(...arguments);
|
|
7
7
|
this.socket = null;
|
|
8
|
+
this.socketListeners = new Map();
|
|
8
9
|
this.reconnectAttempts = 0;
|
|
9
10
|
this.maxReconnectAttempts = this.config.maxReconnectAttempts || 5;
|
|
10
11
|
this.reconnectDelay = this.config.reconnectDelay || 1000;
|
|
@@ -24,10 +25,9 @@ export class WebSocketClient extends AbstractSocketClient {
|
|
|
24
25
|
return this.socket?.readyState === WebSocket.CONNECTING;
|
|
25
26
|
}
|
|
26
27
|
connect() {
|
|
27
|
-
if (this.socket
|
|
28
|
+
if (this.socket) {
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
|
-
this.reconnecting = false;
|
|
31
31
|
this.socket = new WebSocket(this.url, {
|
|
32
32
|
origin: DEFAULT_ORIGIN,
|
|
33
33
|
headers: this.config.options?.headers,
|
|
@@ -38,74 +38,29 @@ export class WebSocketClient extends AbstractSocketClient {
|
|
|
38
38
|
this.socket.setMaxListeners(0);
|
|
39
39
|
const events = ['close', 'error', 'upgrade', 'message', 'open', 'ping', 'pong', 'unexpected-response'];
|
|
40
40
|
for (const event of events) {
|
|
41
|
-
|
|
41
|
+
const handler = (...args) => this.emit(event, ...args);
|
|
42
|
+
this.socketListeners.set(event, handler);
|
|
43
|
+
this.socket?.on(event, handler);
|
|
42
44
|
}
|
|
43
|
-
|
|
44
|
-
// Handle close to attempt reconnect
|
|
45
|
-
this.socket.on('close', (code, reason) => {
|
|
46
|
-
this.emit('close', code, reason);
|
|
47
|
-
this.attemptReconnect(code, reason);
|
|
48
|
-
});
|
|
49
45
|
}
|
|
50
|
-
|
|
51
|
-
async attemptReconnect(code, reason) {
|
|
52
|
-
// Don't reconnect if intentionally closed or max attempts reached
|
|
53
|
-
if (this.reconnecting || code === 1000 || this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
this.reconnecting = true;
|
|
58
|
-
const delay = Math.min(
|
|
59
|
-
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
|
|
60
|
-
this.maxReconnectDelay
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
this.emit('reconnecting', {
|
|
64
|
-
attempt: this.reconnectAttempts + 1,
|
|
65
|
-
maxAttempts: this.maxReconnectAttempts,
|
|
66
|
-
delay,
|
|
67
|
-
code,
|
|
68
|
-
reason: reason?.toString() || 'Connection closed'
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
72
|
-
|
|
73
|
-
this.reconnectAttempts++;
|
|
74
|
-
this.reconnecting = false;
|
|
75
|
-
|
|
76
|
-
// Reconnect
|
|
77
|
-
this.connect();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
46
|
async close() {
|
|
81
|
-
this.reconnecting = false;
|
|
82
|
-
this.reconnectAttempts = 0;
|
|
83
|
-
|
|
84
47
|
if (!this.socket) {
|
|
85
48
|
return;
|
|
86
49
|
}
|
|
50
|
+
for (const [event, handler] of this.socketListeners) {
|
|
51
|
+
this.socket.removeListener(event, handler);
|
|
52
|
+
}
|
|
53
|
+
this.socketListeners.clear();
|
|
87
54
|
const closePromise = new Promise(resolve => {
|
|
88
55
|
this.socket?.once('close', resolve);
|
|
89
56
|
});
|
|
90
|
-
this.socket.close(
|
|
57
|
+
this.socket.close();
|
|
91
58
|
await closePromise;
|
|
92
59
|
this.socket = null;
|
|
93
60
|
}
|
|
94
|
-
|
|
95
61
|
send(str, cb) {
|
|
96
62
|
this.socket?.send(str, cb);
|
|
97
63
|
return Boolean(this.socket);
|
|
98
64
|
}
|
|
99
|
-
|
|
100
|
-
get connectionStats() {
|
|
101
|
-
return {
|
|
102
|
-
isOpen: this.isOpen,
|
|
103
|
-
isClosed: this.isClosed,
|
|
104
|
-
isConnecting: this.isConnecting,
|
|
105
|
-
reconnectAttempts: this.reconnectAttempts,
|
|
106
|
-
maxReconnectAttempts: this.maxReconnectAttempts,
|
|
107
|
-
isReconnecting: this.reconnecting
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
65
|
}
|
|
111
66
|
//# sourceMappingURL=websocket.js.map
|
package/lib/Socket/chats.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
2
|
import { Boom } from '@hapi/boom';
|
|
3
3
|
import { proto } from '../../WAProto/index.js';
|
|
4
4
|
import { DEFAULT_CACHE_TTLS, PROCESSABLE_HISTORY_TYPES } from '../Defaults/index.js';
|
|
@@ -29,9 +29,10 @@ export const makeChatsSocket = (config) => {
|
|
|
29
29
|
// Timeout for AwaitingInitialSync state
|
|
30
30
|
let awaitingSyncTimeout;
|
|
31
31
|
const placeholderResendCache = config.placeholderResendCache ||
|
|
32
|
-
new
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
new LRUCache({
|
|
33
|
+
max: 5000,
|
|
34
|
+
ttl: DEFAULT_CACHE_TTLS.MSG_RETRY * 1000, // 1 hour
|
|
35
|
+
allowStale: false
|
|
35
36
|
});
|
|
36
37
|
if (!config.placeholderResendCache) {
|
|
37
38
|
config.placeholderResendCache = placeholderResendCache;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
2
|
import { Boom } from '@hapi/boom';
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
4
|
import Long from 'long';
|
|
@@ -18,31 +18,28 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
18
18
|
/** this mutex ensures that each retryRequest will wait for the previous one to finish */
|
|
19
19
|
const retryMutex = makeMutex();
|
|
20
20
|
const msgRetryCache = config.msgRetryCounterCache ||
|
|
21
|
-
new
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
maxKeys: 10000 // Limit to prevent memory bloat
|
|
21
|
+
new LRUCache({
|
|
22
|
+
max: 10000,
|
|
23
|
+
ttl: DEFAULT_CACHE_TTLS.MSG_RETRY * 1000,
|
|
24
|
+
allowStale: false
|
|
26
25
|
});
|
|
27
26
|
const callOfferCache = config.callOfferCache ||
|
|
28
|
-
new
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
maxKeys: 5000
|
|
27
|
+
new LRUCache({
|
|
28
|
+
max: 5000,
|
|
29
|
+
ttl: DEFAULT_CACHE_TTLS.CALL_OFFER * 1000,
|
|
30
|
+
allowStale: false
|
|
33
31
|
});
|
|
34
32
|
const placeholderResendCache = config.placeholderResendCache ||
|
|
35
|
-
new
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
maxKeys: 5000
|
|
33
|
+
new LRUCache({
|
|
34
|
+
max: 5000,
|
|
35
|
+
ttl: DEFAULT_CACHE_TTLS.MSG_RETRY * 1000,
|
|
36
|
+
allowStale: false
|
|
40
37
|
});
|
|
41
38
|
// Debounce identity-change session refreshes per JID to avoid bursts
|
|
42
|
-
const identityAssertDebounce = new
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
const identityAssertDebounce = new LRUCache({
|
|
40
|
+
max: 1000,
|
|
41
|
+
ttl: 10000,
|
|
42
|
+
allowStale: false
|
|
46
43
|
});
|
|
47
44
|
let sendActiveReceipts = false;
|
|
48
45
|
const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
|
|
@@ -1677,12 +1674,16 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1677
1674
|
sendActiveReceipts = isOnline;
|
|
1678
1675
|
logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
|
|
1679
1676
|
}
|
|
1680
|
-
// Clean up caches on disconnect
|
|
1677
|
+
// Clean up caches on disconnect - LRU cache will expire naturally, but we can reset
|
|
1681
1678
|
if (connection === 'close' || connection === 'loggedOut') {
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1679
|
+
try {
|
|
1680
|
+
// LRU cache doesn't have clear(), but TTL handles expiration
|
|
1681
|
+
// We can reset by creating new cache instances if needed
|
|
1682
|
+
// For now just log that disconnection happened
|
|
1683
|
+
logger.debug('connection closed, caches will expire via TTL');
|
|
1684
|
+
} catch (e) {
|
|
1685
|
+
logger.warn({ err: e }, 'error in cache cleanup');
|
|
1686
|
+
}
|
|
1686
1687
|
}
|
|
1687
1688
|
});
|
|
1688
1689
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
2
|
import { Boom } from '@hapi/boom';
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
4
|
import { proto } from '../../WAProto/index.js';
|
|
@@ -20,13 +20,15 @@ export const makeMessagesSocket = (config) => {
|
|
|
20
20
|
const sock = makeNewsletterSocket(config);
|
|
21
21
|
const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral } = sock;
|
|
22
22
|
const userDevicesCache = config.userDevicesCache ||
|
|
23
|
-
new
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
new LRUCache({
|
|
24
|
+
max: 5000,
|
|
25
|
+
ttl: DEFAULT_CACHE_TTLS.USER_DEVICES * 1000, // 5 minutes
|
|
26
|
+
allowStale: false
|
|
26
27
|
});
|
|
27
|
-
const peerSessionsCache = new
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const peerSessionsCache = new LRUCache({
|
|
29
|
+
max: 5000,
|
|
30
|
+
ttl: DEFAULT_CACHE_TTLS.USER_DEVICES * 1000,
|
|
31
|
+
allowStale: false
|
|
30
32
|
});
|
|
31
33
|
// Initialize message retry manager if enabled
|
|
32
34
|
const messageRetryManager = enableRecentMessageCache ? new MessageRetryManager(logger, maxMsgRetryCount) : null;
|
package/lib/Socket/socket.js
CHANGED
|
@@ -280,6 +280,8 @@ export const makeSocket = (config) => {
|
|
|
280
280
|
let keepAliveReq;
|
|
281
281
|
let qrTimer;
|
|
282
282
|
let closed = false;
|
|
283
|
+
let consecutivePingFailures = 0;
|
|
284
|
+
const MAX_PING_FAILURES = 3;
|
|
283
285
|
/** log & process any unexpected errors */
|
|
284
286
|
const onUnexpectedError = (err, msg) => {
|
|
285
287
|
logger.error({ err }, `unexpected error in '${msg}'`);
|
|
@@ -490,44 +492,32 @@ export const makeSocket = (config) => {
|
|
|
490
492
|
return;
|
|
491
493
|
}
|
|
492
494
|
closed = true;
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const isLogout = disconnectReason === DisconnectReason.loggedOut;
|
|
496
|
-
|
|
497
|
-
logger.info({
|
|
498
|
-
trace: error?.stack,
|
|
499
|
-
reason: disconnectReason,
|
|
500
|
-
isLogout
|
|
501
|
-
}, error ? 'connection errored' : 'connection closed');
|
|
502
|
-
|
|
503
|
-
clearInterval(keepAliveReq);
|
|
495
|
+
logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed');
|
|
496
|
+
clearTimeout(keepAliveReq);
|
|
504
497
|
clearTimeout(qrTimer);
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
ws.
|
|
511
|
-
|
|
512
|
-
// Only close websocket if not reconnecting
|
|
513
|
-
if (!ws.reconnecting && !ws.isClosed && !ws.isClosing) {
|
|
498
|
+
consecutivePingFailures = 0;
|
|
499
|
+
ws.removeAllListeners();
|
|
500
|
+
if (ev.isBuffering()) {
|
|
501
|
+
ev.flush();
|
|
502
|
+
}
|
|
503
|
+
if (!ws.isClosed && !ws.isClosing) {
|
|
514
504
|
try {
|
|
515
|
-
await
|
|
505
|
+
await Promise.race([
|
|
506
|
+
ws.close(),
|
|
507
|
+
new Promise(resolve => setTimeout(resolve, 5000))
|
|
508
|
+
]);
|
|
516
509
|
}
|
|
517
510
|
catch { }
|
|
518
511
|
}
|
|
519
|
-
|
|
520
|
-
// Emit final connection update
|
|
521
512
|
ev.emit('connection.update', {
|
|
522
|
-
connection:
|
|
513
|
+
connection: 'close',
|
|
523
514
|
lastDisconnect: {
|
|
524
515
|
error,
|
|
525
516
|
date: new Date()
|
|
526
|
-
}
|
|
527
|
-
isOnline: false
|
|
517
|
+
}
|
|
528
518
|
});
|
|
529
|
-
|
|
530
|
-
ev.removeAllListeners(
|
|
519
|
+
noise.destroy();
|
|
520
|
+
ev.removeAllListeners();
|
|
531
521
|
};
|
|
532
522
|
const waitForSocketOpen = async () => {
|
|
533
523
|
if (ws.isOpen) {
|
|
@@ -550,42 +540,57 @@ export const makeSocket = (config) => {
|
|
|
550
540
|
ws.off('error', onClose);
|
|
551
541
|
});
|
|
552
542
|
};
|
|
553
|
-
const startKeepAliveRequest = () =>
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
543
|
+
const startKeepAliveRequest = () => {
|
|
544
|
+
const scheduleNextPing = () => {
|
|
545
|
+
keepAliveReq = setTimeout(async () => {
|
|
546
|
+
if (closed) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (!lastDateRecv) {
|
|
550
|
+
lastDateRecv = new Date();
|
|
551
|
+
}
|
|
552
|
+
const diff = Date.now() - lastDateRecv.getTime();
|
|
553
|
+
if (diff > keepAliveIntervalMs * 2 + 5000) {
|
|
554
|
+
logger.warn({ diff, keepAliveIntervalMs }, 'connection silent for too long');
|
|
555
|
+
void end(new Boom('Connection was lost', { statusCode: DisconnectReason.connectionLost }));
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (ws.isOpen) {
|
|
559
|
+
try {
|
|
560
|
+
await query({
|
|
561
|
+
tag: 'iq',
|
|
562
|
+
attrs: {
|
|
563
|
+
id: generateMessageTag(),
|
|
564
|
+
to: S_WHATSAPP_NET,
|
|
565
|
+
type: 'get',
|
|
566
|
+
xmlns: 'w:p'
|
|
567
|
+
},
|
|
568
|
+
content: [{ tag: 'ping', attrs: {} }]
|
|
569
|
+
});
|
|
570
|
+
consecutivePingFailures = 0;
|
|
571
|
+
}
|
|
572
|
+
catch (err) {
|
|
573
|
+
consecutivePingFailures++;
|
|
574
|
+
logger.error({ trace: err.stack, consecutivePingFailures, maxFailures: MAX_PING_FAILURES }, 'error in sending keep alive');
|
|
575
|
+
if (consecutivePingFailures >= MAX_PING_FAILURES) {
|
|
576
|
+
logger.warn('max ping failures reached, terminating connection');
|
|
577
|
+
void end(new Boom('Connection was lost (ping failures)', {
|
|
578
|
+
statusCode: DisconnectReason.connectionLost
|
|
579
|
+
}));
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
logger.warn('keep alive called when WS not open');
|
|
586
|
+
}
|
|
587
|
+
if (!closed) {
|
|
588
|
+
scheduleNextPing();
|
|
589
|
+
}
|
|
590
|
+
}, keepAliveIntervalMs);
|
|
591
|
+
};
|
|
592
|
+
scheduleNextPing();
|
|
593
|
+
};
|
|
589
594
|
/** i have no idea why this exists. pls enlighten me */
|
|
590
595
|
const sendPassiveIq = (tag) => query({
|
|
591
596
|
tag: 'iq',
|
|
@@ -1013,7 +1018,13 @@ export const makeSocket = (config) => {
|
|
|
1013
1018
|
waitForConnectionUpdate: bindWaitForConnectionUpdate(ev),
|
|
1014
1019
|
sendWAMBuffer,
|
|
1015
1020
|
executeUSyncQuery,
|
|
1016
|
-
onWhatsApp
|
|
1021
|
+
onWhatsApp,
|
|
1022
|
+
get connectionHealth() {
|
|
1023
|
+
return {
|
|
1024
|
+
lastMessageReceived: lastDateRecv,
|
|
1025
|
+
consecutivePingFailures
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1017
1028
|
};
|
|
1018
1029
|
};
|
|
1019
1030
|
/**
|
package/lib/Types/index.js
CHANGED
|
@@ -23,5 +23,8 @@ export var DisconnectReason;
|
|
|
23
23
|
DisconnectReason[DisconnectReason["multideviceMismatch"] = 411] = "multideviceMismatch";
|
|
24
24
|
DisconnectReason[DisconnectReason["forbidden"] = 403] = "forbidden";
|
|
25
25
|
DisconnectReason[DisconnectReason["unavailableService"] = 503] = "unavailableService";
|
|
26
|
+
DisconnectReason[DisconnectReason["streamReplaced"] = 405] = "streamReplaced";
|
|
27
|
+
DisconnectReason[DisconnectReason["conflict"] = 409] = "conflict";
|
|
28
|
+
DisconnectReason[DisconnectReason["preconditionFailed"] = 412] = "preconditionFailed";
|
|
26
29
|
})(DisconnectReason || (DisconnectReason = {}));
|
|
27
30
|
//# sourceMappingURL=index.js.map
|
package/lib/Utils/auth-utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
2
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
3
3
|
import { Mutex } from 'async-mutex';
|
|
4
4
|
import { randomBytes } from 'crypto';
|
|
@@ -15,10 +15,10 @@ import { PreKeyManager } from './pre-key-manager.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export function makeCacheableSignalKeyStore(store, logger, _cache) {
|
|
17
17
|
const cache = _cache ||
|
|
18
|
-
new
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
new LRUCache({
|
|
19
|
+
max: 5000,
|
|
20
|
+
ttl: DEFAULT_CACHE_TTLS.SIGNAL_STORE * 1000, // 5 minutes
|
|
21
|
+
allowStale: false
|
|
22
22
|
});
|
|
23
23
|
// Mutex for protecting cache operations
|
|
24
24
|
const cacheMutex = new Mutex();
|
|
@@ -30,9 +30,8 @@ export const makeEventBuffer = (logger, config = {}) => {
|
|
|
30
30
|
let bufferTimeout = null;
|
|
31
31
|
let flushPendingTimeout = null;
|
|
32
32
|
let bufferCount = 0;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const MAX_HISTORY_CACHE_SIZE = config.maxHistoryCacheSize || 15000;
|
|
33
|
+
let activeBufferedTimeouts = new Set();
|
|
34
|
+
const MAX_HISTORY_CACHE_SIZE = config.maxHistoryCacheSize || 10000;
|
|
36
35
|
const BUFFER_TIMEOUT_MS = config.bufferTimeoutMs || 30000;
|
|
37
36
|
const MAX_BUFFER_COUNT = config.maxBufferCount || 10000;
|
|
38
37
|
// take the generic event and fire it as a baileys event
|
|
@@ -152,13 +151,14 @@ export const makeEventBuffer = (logger, config = {}) => {
|
|
|
152
151
|
buffer();
|
|
153
152
|
try {
|
|
154
153
|
const result = await work(...args);
|
|
155
|
-
// If this is the only buffer, flush after a small delay
|
|
156
154
|
if (bufferCount === 1) {
|
|
157
|
-
setTimeout(() => {
|
|
155
|
+
const t = setTimeout(() => {
|
|
156
|
+
activeBufferedTimeouts.delete(t);
|
|
158
157
|
if (isBuffering && bufferCount === 1) {
|
|
159
158
|
flush();
|
|
160
159
|
}
|
|
161
|
-
}, 100);
|
|
160
|
+
}, 100);
|
|
161
|
+
activeBufferedTimeouts.add(t);
|
|
162
162
|
}
|
|
163
163
|
return result;
|
|
164
164
|
}
|
|
@@ -168,7 +168,6 @@ export const makeEventBuffer = (logger, config = {}) => {
|
|
|
168
168
|
finally {
|
|
169
169
|
bufferCount = Math.max(0, bufferCount - 1);
|
|
170
170
|
if (bufferCount === 0) {
|
|
171
|
-
// Only schedule ONE timeout, not 10,000
|
|
172
171
|
if (!flushPendingTimeout) {
|
|
173
172
|
flushPendingTimeout = setTimeout(flush, 100);
|
|
174
173
|
}
|
|
@@ -178,7 +177,25 @@ export const makeEventBuffer = (logger, config = {}) => {
|
|
|
178
177
|
},
|
|
179
178
|
on: (...args) => ev.on(...args),
|
|
180
179
|
off: (...args) => ev.off(...args),
|
|
181
|
-
removeAllListeners: (...args) =>
|
|
180
|
+
removeAllListeners: (...args) => {
|
|
181
|
+
if (bufferTimeout) {
|
|
182
|
+
clearTimeout(bufferTimeout);
|
|
183
|
+
bufferTimeout = null;
|
|
184
|
+
}
|
|
185
|
+
if (flushPendingTimeout) {
|
|
186
|
+
clearTimeout(flushPendingTimeout);
|
|
187
|
+
flushPendingTimeout = null;
|
|
188
|
+
}
|
|
189
|
+
for (const t of activeBufferedTimeouts) {
|
|
190
|
+
clearTimeout(t);
|
|
191
|
+
}
|
|
192
|
+
activeBufferedTimeouts.clear();
|
|
193
|
+
isBuffering = false;
|
|
194
|
+
bufferCount = 0;
|
|
195
|
+
historyCache.clear();
|
|
196
|
+
data = makeBufferData();
|
|
197
|
+
return ev.removeAllListeners(...args);
|
|
198
|
+
}
|
|
182
199
|
};
|
|
183
200
|
};
|
|
184
201
|
const makeBufferData = () => {
|
package/lib/Utils/generics.js
CHANGED
|
@@ -281,7 +281,10 @@ export const getStatusFromReceiptType = (type) => {
|
|
|
281
281
|
return status;
|
|
282
282
|
};
|
|
283
283
|
const CODE_MAP = {
|
|
284
|
-
conflict: DisconnectReason.connectionReplaced
|
|
284
|
+
conflict: DisconnectReason.connectionReplaced,
|
|
285
|
+
'stream-replaced': DisconnectReason.streamReplaced,
|
|
286
|
+
conflict: DisconnectReason.conflict,
|
|
287
|
+
'precondition-failed': DisconnectReason.preconditionFailed
|
|
285
288
|
};
|
|
286
289
|
/**
|
|
287
290
|
* Stream errors generally provide a reason, map that to a baileys DisconnectReason
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
2
|
import { areJidsSameUser, getBinaryNodeChild, jidDecode } from '../WABinary/index.js';
|
|
3
3
|
import { isStringNullOrEmpty } from './generics.js';
|
|
4
4
|
export async function handleIdentityChange(node, ctx) {
|
|
@@ -5,6 +5,7 @@ import { decodeBinaryNode } from '../WABinary/index.js';
|
|
|
5
5
|
import { aesDecryptGCM, aesEncryptGCM, Curve, hkdf, sha256 } from './crypto.js';
|
|
6
6
|
const IV_LENGTH = 12;
|
|
7
7
|
const EMPTY_BUFFER = Buffer.alloc(0);
|
|
8
|
+
const MAX_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB max buffer cap
|
|
8
9
|
const generateIV = (counter) => {
|
|
9
10
|
const iv = new ArrayBuffer(IV_LENGTH);
|
|
10
11
|
new DataView(iv).setUint32(8, counter);
|
|
@@ -194,7 +195,18 @@ export const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publi
|
|
|
194
195
|
else {
|
|
195
196
|
inBytes = Buffer.concat([inBytes, newData]);
|
|
196
197
|
}
|
|
198
|
+
if (inBytes.length > MAX_BUFFER_SIZE) {
|
|
199
|
+
logger.error({ bufferSize: inBytes.length }, 'noise handler buffer exceeded max size, clearing');
|
|
200
|
+
inBytes = Buffer.alloc(0);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
197
203
|
await processData(onFrame);
|
|
204
|
+
},
|
|
205
|
+
destroy: () => {
|
|
206
|
+
inBytes = Buffer.alloc(0);
|
|
207
|
+
transport = null;
|
|
208
|
+
pendingOnFrame = null;
|
|
209
|
+
isWaitingForTransport = false;
|
|
198
210
|
}
|
|
199
211
|
};
|
|
200
212
|
};
|