@fer2809fl/baileys 7.0.2 → 7.0.5
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 +409 -32
- package/lib/Defaults/index.js +2 -1
- package/lib/Modded/message_builder.js +2356 -0
- package/lib/Signal/libsignal.js +19 -0
- package/lib/Socket/business.d.ts +213 -532
- package/lib/Socket/business.js +359 -401
- package/lib/Socket/chats.d.ts +30 -2
- package/lib/Socket/chats.js +295 -40
- package/lib/Socket/groups.d.ts +16 -0
- package/lib/Socket/groups.js +101 -18
- package/lib/Socket/index.d.ts +17 -0
- package/lib/Socket/messages-recv.js +18 -1
- package/lib/Socket/messages-send.d.ts +18 -0
- package/lib/Socket/messages-send.js +50 -2
- package/lib/Socket/newsletter.js +3 -3
- package/lib/Socket/socket.js +50 -6
- package/lib/Types/Newsletter.d.ts +13 -4
- package/lib/Types/Newsletter.js +12 -2
- package/lib/Utils/anti-ban.d.ts +41 -0
- package/lib/Utils/anti-ban.js +182 -0
- package/lib/Utils/banner.d.ts +8 -0
- package/lib/Utils/banner.js +76 -0
- package/lib/Utils/bot-utils.d.ts +57 -0
- package/lib/Utils/bot-utils.js +241 -0
- package/lib/Utils/chat-utils.d.ts +34 -4
- package/lib/Utils/chat-utils.js +80 -20
- package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/enhanced-cache.d.ts +40 -0
- package/lib/Utils/enhanced-cache.js +242 -0
- package/lib/Utils/enhanced-logger.d.ts +41 -0
- package/lib/Utils/enhanced-logger.js +185 -0
- package/lib/Utils/event-buffer.js +49 -1
- package/lib/Utils/index.d.ts +14 -0
- package/lib/Utils/index.js +14 -0
- package/lib/Utils/lid-utils.d.ts +139 -0
- package/lib/Utils/lid-utils.js +503 -0
- package/lib/Utils/message-queue.d.ts +47 -0
- package/lib/Utils/message-queue.js +226 -0
- package/lib/Utils/message-retry-manager.js +40 -0
- package/lib/Utils/rich-message-utils.d.ts +21 -0
- package/lib/Utils/rich-message-utils.js +229 -0
- package/lib/Utils/rich-messages.d.ts +52 -0
- package/lib/Utils/rich-messages.js +185 -0
- package/lib/Utils/scheduled-messages.d.ts +122 -0
- package/lib/Utils/scheduled-messages.js +289 -0
- package/lib/Utils/smart-reconnect.d.ts +48 -0
- package/lib/Utils/smart-reconnect.js +207 -0
- package/lib/Utils/use-sqlite-auth-state.d.ts +11 -0
- package/lib/Utils/use-sqlite-auth-state.js +95 -0
- package/lib/VoIP/audio-feeder.d.ts +15 -0
- package/lib/VoIP/audio-feeder.js +132 -0
- package/lib/VoIP/index.js +277 -0
- package/lib/VoIP/relay-transport.d.ts +43 -0
- package/lib/VoIP/relay-transport.js +559 -0
- package/lib/VoIP/signaling.js +594 -0
- package/lib/VoIP/types.d.ts +69 -0
- package/lib/VoIP/types.js +17 -0
- package/lib/VoIP/wasm-engine.d.ts +103 -0
- package/lib/VoIP/wasm-engine.js +1214 -0
- package/lib/VoIP/worker-bootstrap.js +1042 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
- package/lib/WAUSync/Protocols/index.js +1 -0
- package/lib/WAUSync/USyncQuery.js +5 -1
- package/lib/assets/wasm/loader.js +5 -0
- package/lib/assets/wasm/whatsapp.wasm +0 -0
- package/lib/assets/wasm/worker-modules.js +273 -0
- package/lib/index.d.ts +41 -0
- package/lib/index.js +3 -0
- package/package.json +15 -7
- package/lib/Defaults/index.d.ts.map +0 -1
- package/lib/Defaults/index.js.map +0 -1
- package/lib/Signal/Group/ciphertext-message.d.ts.map +0 -1
- package/lib/Signal/Group/ciphertext-message.js.map +0 -1
- package/lib/Signal/Group/group-session-builder.d.ts.map +0 -1
- package/lib/Signal/Group/group-session-builder.js.map +0 -1
- package/lib/Signal/Group/group_cipher.d.ts.map +0 -1
- package/lib/Signal/Group/group_cipher.js.map +0 -1
- package/lib/Signal/Group/index.d.ts.map +0 -1
- package/lib/Signal/Group/index.js.map +0 -1
- package/lib/Signal/Group/keyhelper.d.ts.map +0 -1
- package/lib/Signal/Group/keyhelper.js.map +0 -1
- package/lib/Signal/Group/sender-chain-key.d.ts.map +0 -1
- package/lib/Signal/Group/sender-chain-key.js.map +0 -1
- package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-distribution-message.js.map +0 -1
- package/lib/Signal/Group/sender-key-message.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-message.js.map +0 -1
- package/lib/Signal/Group/sender-key-name.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-name.js.map +0 -1
- package/lib/Signal/Group/sender-key-record.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-record.js.map +0 -1
- package/lib/Signal/Group/sender-key-state.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-state.js.map +0 -1
- package/lib/Signal/Group/sender-message-key.d.ts.map +0 -1
- package/lib/Signal/Group/sender-message-key.js.map +0 -1
- package/lib/Signal/libsignal.d.ts.map +0 -1
- package/lib/Signal/libsignal.js.map +0 -1
- package/lib/Signal/lid-mapping.d.ts.map +0 -1
- package/lib/Signal/lid-mapping.js.map +0 -1
- package/lib/Socket/Client/index.d.ts.map +0 -1
- package/lib/Socket/Client/index.js.map +0 -1
- package/lib/Socket/Client/types.d.ts.map +0 -1
- package/lib/Socket/Client/types.js.map +0 -1
- package/lib/Socket/Client/websocket.d.ts.map +0 -1
- package/lib/Socket/Client/websocket.js.map +0 -1
- package/lib/Socket/business.d.ts.map +0 -1
- package/lib/Socket/business.js.map +0 -1
- package/lib/Socket/chats.d.ts.map +0 -1
- package/lib/Socket/chats.js.map +0 -1
- package/lib/Socket/communities.d.ts.map +0 -1
- package/lib/Socket/communities.js.map +0 -1
- package/lib/Socket/dugong.d.ts.map +0 -1
- package/lib/Socket/dugong.js.map +0 -1
- package/lib/Socket/groups.d.ts.map +0 -1
- package/lib/Socket/groups.js.map +0 -1
- package/lib/Socket/index.d.ts.map +0 -1
- package/lib/Socket/index.js.map +0 -1
- package/lib/Socket/messages-recv.d.ts.map +0 -1
- package/lib/Socket/messages-recv.js.map +0 -1
- package/lib/Socket/messages-send.d.ts.map +0 -1
- package/lib/Socket/messages-send.js.map +0 -1
- package/lib/Socket/mex.d.ts.map +0 -1
- package/lib/Socket/mex.js.map +0 -1
- package/lib/Socket/newsletter.d.ts.map +0 -1
- package/lib/Socket/newsletter.js.map +0 -1
- package/lib/Socket/socket.d.ts.map +0 -1
- package/lib/Socket/socket.js.map +0 -1
- package/lib/Types/Auth.d.ts.map +0 -1
- package/lib/Types/Auth.js.map +0 -1
- package/lib/Types/Bussines.d.ts.map +0 -1
- package/lib/Types/Bussines.js.map +0 -1
- package/lib/Types/Call.d.ts.map +0 -1
- package/lib/Types/Call.js.map +0 -1
- package/lib/Types/Chat.d.ts.map +0 -1
- package/lib/Types/Chat.js.map +0 -1
- package/lib/Types/Contact.d.ts.map +0 -1
- package/lib/Types/Contact.js.map +0 -1
- package/lib/Types/Events.d.ts.map +0 -1
- package/lib/Types/Events.js.map +0 -1
- package/lib/Types/GroupMetadata.d.ts.map +0 -1
- package/lib/Types/GroupMetadata.js.map +0 -1
- package/lib/Types/Label.d.ts.map +0 -1
- package/lib/Types/Label.js.map +0 -1
- package/lib/Types/LabelAssociation.d.ts.map +0 -1
- package/lib/Types/LabelAssociation.js.map +0 -1
- package/lib/Types/Message.d.ts.map +0 -1
- package/lib/Types/Message.js.map +0 -1
- package/lib/Types/Newsletter.d.ts.map +0 -1
- package/lib/Types/Newsletter.js.map +0 -1
- package/lib/Types/Product.d.ts.map +0 -1
- package/lib/Types/Product.js.map +0 -1
- package/lib/Types/Signal.d.ts.map +0 -1
- package/lib/Types/Signal.js.map +0 -1
- package/lib/Types/Socket.d.ts.map +0 -1
- package/lib/Types/Socket.js.map +0 -1
- package/lib/Types/State.d.ts.map +0 -1
- package/lib/Types/State.js.map +0 -1
- package/lib/Types/USync.d.ts.map +0 -1
- package/lib/Types/USync.js.map +0 -1
- package/lib/Types/index.d.ts.map +0 -1
- package/lib/Types/index.js.map +0 -1
- package/lib/Utils/auth-utils.d.ts.map +0 -1
- package/lib/Utils/auth-utils.js.map +0 -1
- package/lib/Utils/browser-utils.d.ts.map +0 -1
- package/lib/Utils/browser-utils.js.map +0 -1
- package/lib/Utils/business.d.ts.map +0 -1
- package/lib/Utils/business.js.map +0 -1
- package/lib/Utils/chat-utils.d.ts.map +0 -1
- package/lib/Utils/chat-utils.js.map +0 -1
- package/lib/Utils/crypto.d.ts.map +0 -1
- package/lib/Utils/crypto.js.map +0 -1
- package/lib/Utils/decode-wa-message.d.ts.map +0 -1
- package/lib/Utils/decode-wa-message.js.map +0 -1
- package/lib/Utils/event-buffer.d.ts.map +0 -1
- package/lib/Utils/event-buffer.js.map +0 -1
- package/lib/Utils/generics.d.ts.map +0 -1
- package/lib/Utils/generics.js.map +0 -1
- package/lib/Utils/history.d.ts.map +0 -1
- package/lib/Utils/history.js.map +0 -1
- package/lib/Utils/identity-change-handler.d.ts.map +0 -1
- package/lib/Utils/identity-change-handler.js.map +0 -1
- package/lib/Utils/index.d.ts.map +0 -1
- package/lib/Utils/index.js.map +0 -1
- package/lib/Utils/link-preview.d.ts.map +0 -1
- package/lib/Utils/link-preview.js.map +0 -1
- package/lib/Utils/logger.d.ts.map +0 -1
- package/lib/Utils/logger.js.map +0 -1
- package/lib/Utils/lt-hash.d.ts.map +0 -1
- package/lib/Utils/lt-hash.js.map +0 -1
- package/lib/Utils/make-mutex.d.ts.map +0 -1
- package/lib/Utils/make-mutex.js.map +0 -1
- package/lib/Utils/message-retry-manager.d.ts.map +0 -1
- package/lib/Utils/message-retry-manager.js.map +0 -1
- package/lib/Utils/messages-media.d.ts.map +0 -1
- package/lib/Utils/messages-media.js.map +0 -1
- package/lib/Utils/messages.d.ts.map +0 -1
- package/lib/Utils/messages.js.map +0 -1
- package/lib/Utils/noise-handler.d.ts.map +0 -1
- package/lib/Utils/noise-handler.js.map +0 -1
- package/lib/Utils/offline-node-processor.d.ts.map +0 -1
- package/lib/Utils/offline-node-processor.js.map +0 -1
- package/lib/Utils/pre-key-manager.d.ts.map +0 -1
- package/lib/Utils/pre-key-manager.js.map +0 -1
- package/lib/Utils/process-message.d.ts.map +0 -1
- package/lib/Utils/process-message.js.map +0 -1
- package/lib/Utils/reporting-utils.d.ts.map +0 -1
- package/lib/Utils/reporting-utils.js.map +0 -1
- package/lib/Utils/rich-messages.d.ts.map +0 -1
- package/lib/Utils/rich-messages.js.map +0 -1
- package/lib/Utils/signal.d.ts.map +0 -1
- package/lib/Utils/signal.js.map +0 -1
- package/lib/Utils/stanza-ack.d.ts.map +0 -1
- package/lib/Utils/stanza-ack.js.map +0 -1
- package/lib/Utils/sync-action-utils.d.ts.map +0 -1
- package/lib/Utils/sync-action-utils.js.map +0 -1
- package/lib/Utils/tc-token-utils.d.ts.map +0 -1
- package/lib/Utils/tc-token-utils.js.map +0 -1
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +0 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +0 -1
- package/lib/Utils/validate-connection.d.ts.map +0 -1
- package/lib/Utils/validate-connection.js.map +0 -1
- package/lib/WABinary/constants.d.ts.map +0 -1
- package/lib/WABinary/constants.js.map +0 -1
- package/lib/WABinary/decode.d.ts.map +0 -1
- package/lib/WABinary/decode.js.map +0 -1
- package/lib/WABinary/encode.d.ts.map +0 -1
- package/lib/WABinary/encode.js.map +0 -1
- package/lib/WABinary/generic-utils.d.ts.map +0 -1
- package/lib/WABinary/generic-utils.js.map +0 -1
- package/lib/WABinary/index.d.ts.map +0 -1
- package/lib/WABinary/index.js.map +0 -1
- package/lib/WABinary/jid-utils.d.ts.map +0 -1
- package/lib/WABinary/jid-utils.js.map +0 -1
- package/lib/WABinary/types.d.ts.map +0 -1
- package/lib/WABinary/types.js.map +0 -1
- package/lib/WAM/BinaryInfo.d.ts.map +0 -1
- package/lib/WAM/BinaryInfo.js.map +0 -1
- package/lib/WAM/constants.d.ts.map +0 -1
- package/lib/WAM/constants.js.map +0 -1
- package/lib/WAM/encode.d.ts.map +0 -1
- package/lib/WAM/encode.js.map +0 -1
- package/lib/WAM/index.d.ts.map +0 -1
- package/lib/WAM/index.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/index.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/index.js.map +0 -1
- package/lib/WAUSync/USyncQuery.d.ts.map +0 -1
- package/lib/WAUSync/USyncQuery.js.map +0 -1
- package/lib/WAUSync/USyncUser.d.ts.map +0 -1
- package/lib/WAUSync/USyncUser.js.map +0 -1
- package/lib/WAUSync/index.d.ts.map +0 -1
- package/lib/WAUSync/index.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { DisconnectReason } from '../Types/index.js';
|
|
2
|
+
|
|
3
|
+
const RECONNECT_CONFIG = {
|
|
4
|
+
MAX_RETRIES: 15,
|
|
5
|
+
INITIAL_DELAY: 1000,
|
|
6
|
+
MAX_DELAY: 300000,
|
|
7
|
+
BACKOFF_FACTOR: 1.5,
|
|
8
|
+
JITTER_FACTOR: 0.3,
|
|
9
|
+
STABLE_CONNECTION_TIME: 60000,
|
|
10
|
+
RECOVERABLE_REASONS: [
|
|
11
|
+
DisconnectReason.connectionClosed,
|
|
12
|
+
DisconnectReason.connectionLost,
|
|
13
|
+
DisconnectReason.connectionReplaced,
|
|
14
|
+
DisconnectReason.timedOut,
|
|
15
|
+
DisconnectReason.restartRequired
|
|
16
|
+
],
|
|
17
|
+
FATAL_REASONS: [
|
|
18
|
+
DisconnectReason.loggedOut,
|
|
19
|
+
DisconnectReason.badSession,
|
|
20
|
+
DisconnectReason.multideviceMismatch
|
|
21
|
+
]
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
class SmartReconnect {
|
|
25
|
+
constructor(logger, config = {}) {
|
|
26
|
+
this.logger = logger;
|
|
27
|
+
this.config = { ...RECONNECT_CONFIG, ...config };
|
|
28
|
+
this.retryCount = 0;
|
|
29
|
+
this.lastConnectTime = null;
|
|
30
|
+
this.lastDisconnectReason = null;
|
|
31
|
+
this.connectionStable = false;
|
|
32
|
+
this.reconnectTimer = null;
|
|
33
|
+
this.healthCheckInterval = null;
|
|
34
|
+
this.stats = {
|
|
35
|
+
totalReconnects: 0,
|
|
36
|
+
successfulReconnects: 0,
|
|
37
|
+
failedReconnects: 0,
|
|
38
|
+
lastSuccessfulConnect: null,
|
|
39
|
+
averageDowntime: 0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
calculateDelay() {
|
|
44
|
+
const baseDelay = this.config.INITIAL_DELAY * Math.pow(this.config.BACKOFF_FACTOR, this.retryCount);
|
|
45
|
+
const cappedDelay = Math.min(baseDelay, this.config.MAX_DELAY);
|
|
46
|
+
const jitter = cappedDelay * this.config.JITTER_FACTOR * (Math.random() - 0.5);
|
|
47
|
+
return Math.floor(cappedDelay + jitter);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
shouldReconnect(reason, statusCode) {
|
|
51
|
+
if (this.config.FATAL_REASONS.includes(statusCode)) {
|
|
52
|
+
this.logger?.warn({ reason, statusCode }, 'Fatal disconnect reason - not reconnecting');
|
|
53
|
+
return { shouldReconnect: false, reason: 'Fatal error - session invalidated' };
|
|
54
|
+
}
|
|
55
|
+
if (this.retryCount >= this.config.MAX_RETRIES) {
|
|
56
|
+
this.logger?.error({ retryCount: this.retryCount }, 'Max retries exceeded');
|
|
57
|
+
return { shouldReconnect: false, reason: 'Max retries exceeded' };
|
|
58
|
+
}
|
|
59
|
+
if (this.config.RECOVERABLE_REASONS.includes(statusCode)) {
|
|
60
|
+
return { shouldReconnect: true, delay: this.calculateDelay() };
|
|
61
|
+
}
|
|
62
|
+
return { shouldReconnect: true, delay: this.calculateDelay() * 2 };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async handleDisconnect(reason, statusCode, reconnectCallback) {
|
|
66
|
+
this.lastDisconnectReason = { reason, statusCode, time: Date.now() };
|
|
67
|
+
this.connectionStable = false;
|
|
68
|
+
const decision = this.shouldReconnect(reason, statusCode);
|
|
69
|
+
if (!decision.shouldReconnect) {
|
|
70
|
+
this.logger?.info({ reason: decision.reason }, 'Not attempting reconnection');
|
|
71
|
+
return { reconnecting: false, reason: decision.reason };
|
|
72
|
+
}
|
|
73
|
+
this.retryCount++;
|
|
74
|
+
this.stats.totalReconnects++;
|
|
75
|
+
const delay = decision.delay;
|
|
76
|
+
this.logger?.info({ attempt: this.retryCount, maxRetries: this.config.MAX_RETRIES, delay, reason }, 'Scheduling reconnection');
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
79
|
+
try {
|
|
80
|
+
await reconnectCallback();
|
|
81
|
+
this.onSuccessfulReconnect();
|
|
82
|
+
resolve({ reconnecting: true, success: true });
|
|
83
|
+
} catch (error) {
|
|
84
|
+
this.stats.failedReconnects++;
|
|
85
|
+
this.logger?.error({ error }, 'Reconnection failed');
|
|
86
|
+
resolve({ reconnecting: true, success: false, error });
|
|
87
|
+
}
|
|
88
|
+
}, delay);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
onSuccessfulReconnect() {
|
|
93
|
+
const previousRetries = this.retryCount;
|
|
94
|
+
this.retryCount = 0;
|
|
95
|
+
this.lastConnectTime = Date.now();
|
|
96
|
+
this.stats.successfulReconnects++;
|
|
97
|
+
this.stats.lastSuccessfulConnect = new Date().toISOString();
|
|
98
|
+
this.logger?.info({ previousRetries }, 'Successfully reconnected');
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
if (this.lastConnectTime && Date.now() - this.lastConnectTime >= this.config.STABLE_CONNECTION_TIME) {
|
|
101
|
+
this.connectionStable = true;
|
|
102
|
+
this.logger?.debug('Connection marked as stable');
|
|
103
|
+
}
|
|
104
|
+
}, this.config.STABLE_CONNECTION_TIME);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
cancelPendingReconnect() {
|
|
108
|
+
if (this.reconnectTimer) {
|
|
109
|
+
clearTimeout(this.reconnectTimer);
|
|
110
|
+
this.reconnectTimer = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
reset() {
|
|
115
|
+
this.retryCount = 0;
|
|
116
|
+
this.lastDisconnectReason = null;
|
|
117
|
+
this.cancelPendingReconnect();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getStats() {
|
|
121
|
+
return {
|
|
122
|
+
...this.stats,
|
|
123
|
+
currentRetryCount: this.retryCount,
|
|
124
|
+
isStable: this.connectionStable,
|
|
125
|
+
lastDisconnect: this.lastDisconnectReason
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
startHealthCheck(pingCallback, interval = 30000) {
|
|
130
|
+
this.stopHealthCheck();
|
|
131
|
+
this.healthCheckInterval = setInterval(async () => {
|
|
132
|
+
try {
|
|
133
|
+
await pingCallback();
|
|
134
|
+
this.logger?.debug('Health check passed');
|
|
135
|
+
} catch (error) {
|
|
136
|
+
this.logger?.warn({ error }, 'Health check failed');
|
|
137
|
+
}
|
|
138
|
+
}, interval);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
stopHealthCheck() {
|
|
142
|
+
if (this.healthCheckInterval) {
|
|
143
|
+
clearInterval(this.healthCheckInterval);
|
|
144
|
+
this.healthCheckInterval = null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
cleanup() {
|
|
149
|
+
this.cancelPendingReconnect();
|
|
150
|
+
this.stopHealthCheck();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function createConnectionHandler(sock, logger, options = {}) {
|
|
155
|
+
const reconnect = new SmartReconnect(logger, options);
|
|
156
|
+
return {
|
|
157
|
+
reconnect,
|
|
158
|
+
async handleConnectionUpdate(update, startSock) {
|
|
159
|
+
const { connection, lastDisconnect, qr } = update;
|
|
160
|
+
if (connection === 'close') {
|
|
161
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
162
|
+
const reason = lastDisconnect?.error?.message || 'Unknown';
|
|
163
|
+
const result = await reconnect.handleDisconnect(reason, statusCode, startSock);
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
if (connection === 'open') {
|
|
167
|
+
reconnect.onSuccessfulReconnect();
|
|
168
|
+
return { connected: true };
|
|
169
|
+
}
|
|
170
|
+
if (qr) {
|
|
171
|
+
reconnect.reset();
|
|
172
|
+
return { qr };
|
|
173
|
+
}
|
|
174
|
+
return update;
|
|
175
|
+
},
|
|
176
|
+
getStatus() {
|
|
177
|
+
return {
|
|
178
|
+
stats: reconnect.getStats(),
|
|
179
|
+
isHealthy: reconnect.connectionStable && reconnect.retryCount === 0
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function withRetry(fn, options = {}) {
|
|
186
|
+
const { maxRetries = 3, delay = 1000, backoff = 2, onRetry = null } = options;
|
|
187
|
+
let lastError;
|
|
188
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
189
|
+
try {
|
|
190
|
+
return await fn();
|
|
191
|
+
} catch (error) {
|
|
192
|
+
lastError = error;
|
|
193
|
+
if (attempt === maxRetries) throw error;
|
|
194
|
+
const waitTime = delay * Math.pow(backoff, attempt);
|
|
195
|
+
if (onRetry) onRetry(error, attempt + 1, waitTime);
|
|
196
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
throw lastError;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export {
|
|
203
|
+
RECONNECT_CONFIG,
|
|
204
|
+
SmartReconnect,
|
|
205
|
+
createConnectionHandler,
|
|
206
|
+
withRetry
|
|
207
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuthenticationState } from "../Types/index.js";
|
|
2
|
+
|
|
3
|
+
export declare function useSqliteAuthState(opts: {
|
|
4
|
+
dbPath?: string;
|
|
5
|
+
database?: any;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
state: AuthenticationState;
|
|
8
|
+
saveCreds: () => Promise<void>;
|
|
9
|
+
clearKeys: () => Promise<void>;
|
|
10
|
+
close: () => void;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { proto } from '../../WAProto/index.js';
|
|
2
|
+
import { initAuthCreds } from './auth-utils.js';
|
|
3
|
+
import { BufferJSON } from './generics.js';
|
|
4
|
+
|
|
5
|
+
const CREDS_ROW_KEY = '__creds__';
|
|
6
|
+
|
|
7
|
+
const CREATE_SCHEMA_SQL = `
|
|
8
|
+
CREATE TABLE IF NOT EXISTS creds (
|
|
9
|
+
key TEXT PRIMARY KEY,
|
|
10
|
+
value TEXT NOT NULL
|
|
11
|
+
);
|
|
12
|
+
CREATE TABLE IF NOT EXISTS signal_keys (
|
|
13
|
+
type TEXT NOT NULL,
|
|
14
|
+
id TEXT NOT NULL,
|
|
15
|
+
value TEXT NOT NULL,
|
|
16
|
+
PRIMARY KEY (type, id)
|
|
17
|
+
);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS signal_keys_type_idx ON signal_keys(type);
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
async function useSqliteAuthState(opts) {
|
|
22
|
+
let Database;
|
|
23
|
+
try {
|
|
24
|
+
Database = (await import('better-sqlite3')).default;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
throw new Error('`better-sqlite3` is required for `useSqliteAuthState`. Install with: npm install better-sqlite3');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const db = opts.database || new Database(opts.dbPath);
|
|
30
|
+
db.pragma('journal_mode = WAL');
|
|
31
|
+
db.pragma('synchronous = NORMAL');
|
|
32
|
+
db.exec(CREATE_SCHEMA_SQL);
|
|
33
|
+
|
|
34
|
+
const stmts = {
|
|
35
|
+
credsSelect: db.prepare('SELECT value FROM creds WHERE key = ?'),
|
|
36
|
+
credsUpsert: db.prepare('INSERT INTO creds (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value'),
|
|
37
|
+
keySelect: db.prepare('SELECT value FROM signal_keys WHERE type = ? AND id = ?'),
|
|
38
|
+
keyUpsert: db.prepare('INSERT INTO signal_keys (type, id, value) VALUES (?, ?, ?) ON CONFLICT(type, id) DO UPDATE SET value = excluded.value'),
|
|
39
|
+
keyDelete: db.prepare('DELETE FROM signal_keys WHERE type = ? AND id = ?'),
|
|
40
|
+
keyListIds: db.prepare('SELECT id FROM signal_keys WHERE type = ?'),
|
|
41
|
+
keyList: db.prepare('SELECT id, value FROM signal_keys WHERE type = ?'),
|
|
42
|
+
clearKeys: db.prepare('DELETE FROM signal_keys')
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const loadCreds = () => {
|
|
46
|
+
const row = stmts.credsSelect.get(CREDS_ROW_KEY);
|
|
47
|
+
if (!row) return initAuthCreds();
|
|
48
|
+
return JSON.parse(row.value, BufferJSON.reviver);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const persistCreds = creds => {
|
|
52
|
+
stmts.credsUpsert.run(CREDS_ROW_KEY, JSON.stringify(creds, BufferJSON.replacer));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const creds = loadCreds();
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
state: {
|
|
59
|
+
creds,
|
|
60
|
+
keys: {
|
|
61
|
+
get: async (type, ids) => {
|
|
62
|
+
const data = {};
|
|
63
|
+
for (const id of ids) {
|
|
64
|
+
const row = stmts.keySelect.get(type, id);
|
|
65
|
+
if (row) {
|
|
66
|
+
let value = JSON.parse(row.value, BufferJSON.reviver);
|
|
67
|
+
if (type === 'app-state-sync-key' && value) {
|
|
68
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
69
|
+
}
|
|
70
|
+
data[id] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return data;
|
|
74
|
+
},
|
|
75
|
+
set: async data => {
|
|
76
|
+
const writeTx = db.transaction(() => {
|
|
77
|
+
for (const category in data) {
|
|
78
|
+
for (const id in data[category]) {
|
|
79
|
+
const value = data[category][id];
|
|
80
|
+
if (value) stmts.keyUpsert.run(category, id, JSON.stringify(value, BufferJSON.replacer));
|
|
81
|
+
else stmts.keyDelete.run(category, id);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
writeTx();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
saveCreds: async () => { persistCreds(creds); },
|
|
90
|
+
clearKeys: async () => { stmts.clearKeys.run(); },
|
|
91
|
+
close: () => { db.close(); }
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { useSqliteAuthState };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class AudioFeeder {
|
|
2
|
+
#private;
|
|
3
|
+
private readonly sampleRate;
|
|
4
|
+
private readonly channels;
|
|
5
|
+
private readonly framesPerChunk;
|
|
6
|
+
private readonly onChunk;
|
|
7
|
+
private readonly source;
|
|
8
|
+
droppedChunks: number;
|
|
9
|
+
underflowChunks: number;
|
|
10
|
+
bytesProduced: number;
|
|
11
|
+
chunksEmitted: number;
|
|
12
|
+
constructor(sampleRate: number, channels: number, framesPerChunk: number, onChunk: (chunk: Float32Array) => void, source?: string);
|
|
13
|
+
start: () => void;
|
|
14
|
+
stop: () => void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio feeder.
|
|
3
|
+
*
|
|
4
|
+
* Spawns ffmpeg to decode `source` into f32le PCM at the requested rate, then
|
|
5
|
+
* meters frames out at chunk-cadence to the WASM uplink.
|
|
6
|
+
*
|
|
7
|
+
* @author ShellTear
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
const LOW_WATERMARK_CHUNKS = 16;
|
|
11
|
+
const MAX_QUEUED_CHUNKS = 1024;
|
|
12
|
+
const DEFAULT_WARMUP_MS = 500;
|
|
13
|
+
export class AudioFeeder {
|
|
14
|
+
sampleRate;
|
|
15
|
+
channels;
|
|
16
|
+
framesPerChunk;
|
|
17
|
+
onChunk;
|
|
18
|
+
source;
|
|
19
|
+
#proc = null;
|
|
20
|
+
#pending = Buffer.alloc(0);
|
|
21
|
+
#queue = [];
|
|
22
|
+
#emitTimer = null;
|
|
23
|
+
#nextEmitAtMs = 0;
|
|
24
|
+
#warmupUntilMs = 0;
|
|
25
|
+
droppedChunks = 0;
|
|
26
|
+
underflowChunks = 0;
|
|
27
|
+
bytesProduced = 0;
|
|
28
|
+
chunksEmitted = 0;
|
|
29
|
+
constructor(sampleRate, channels, framesPerChunk, onChunk, source = "silence") {
|
|
30
|
+
this.sampleRate = sampleRate;
|
|
31
|
+
this.channels = channels;
|
|
32
|
+
this.framesPerChunk = framesPerChunk;
|
|
33
|
+
this.onChunk = onChunk;
|
|
34
|
+
this.source = source;
|
|
35
|
+
}
|
|
36
|
+
start = () => {
|
|
37
|
+
if (this.#proc)
|
|
38
|
+
return;
|
|
39
|
+
const chunkSamples = this.framesPerChunk * this.channels;
|
|
40
|
+
const chunkBytes = chunkSamples * Float32Array.BYTES_PER_ELEMENT;
|
|
41
|
+
const chunkIntervalMs = (this.framesPerChunk / this.sampleRate) * 1000;
|
|
42
|
+
const inputArgs = this.#resolveInputArgs();
|
|
43
|
+
this.#proc = spawn("ffmpeg", [
|
|
44
|
+
"-hide_banner",
|
|
45
|
+
"-loglevel", "error",
|
|
46
|
+
"-thread_queue_size", "512",
|
|
47
|
+
...inputArgs,
|
|
48
|
+
"-f", "f32le",
|
|
49
|
+
"-ac", String(this.channels),
|
|
50
|
+
"-ar", String(this.sampleRate),
|
|
51
|
+
"pipe:1",
|
|
52
|
+
]);
|
|
53
|
+
this.#proc.stdout.on("data", (chunk) => {
|
|
54
|
+
this.#pending = Buffer.concat([this.#pending, chunk]);
|
|
55
|
+
while (this.#pending.length >= chunkBytes) {
|
|
56
|
+
if (this.#queue.length >= MAX_QUEUED_CHUNKS) {
|
|
57
|
+
this.#proc?.stdout.pause();
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
const frame = this.#pending.subarray(0, chunkBytes);
|
|
61
|
+
this.#pending = this.#pending.subarray(chunkBytes);
|
|
62
|
+
const out = new Float32Array(chunkSamples);
|
|
63
|
+
out.set(new Float32Array(frame.buffer, frame.byteOffset, chunkSamples));
|
|
64
|
+
this.bytesProduced += chunkBytes;
|
|
65
|
+
this.#queue.push(out);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
this.#proc.stderr.on("data", (chunk) => {
|
|
69
|
+
process.stderr.write(`[AudioFeeder] ${chunk.toString().trim()}\n`);
|
|
70
|
+
});
|
|
71
|
+
this.#proc.on("exit", (code) => {
|
|
72
|
+
if (code !== 0 && code !== null) {
|
|
73
|
+
process.stderr.write(`[AudioFeeder] ffmpeg exited with code=${code}\n`);
|
|
74
|
+
}
|
|
75
|
+
this.#proc = null;
|
|
76
|
+
});
|
|
77
|
+
this.#nextEmitAtMs = 0;
|
|
78
|
+
this.#warmupUntilMs = Date.now() + DEFAULT_WARMUP_MS;
|
|
79
|
+
this.#scheduleNext(chunkSamples, chunkIntervalMs);
|
|
80
|
+
};
|
|
81
|
+
stop = () => {
|
|
82
|
+
if (this.#emitTimer) {
|
|
83
|
+
clearTimeout(this.#emitTimer);
|
|
84
|
+
this.#emitTimer = null;
|
|
85
|
+
}
|
|
86
|
+
this.#proc?.kill("SIGTERM");
|
|
87
|
+
this.#proc = null;
|
|
88
|
+
this.#pending = Buffer.alloc(0);
|
|
89
|
+
this.#queue = [];
|
|
90
|
+
this.#warmupUntilMs = 0;
|
|
91
|
+
};
|
|
92
|
+
#resolveInputArgs = () => {
|
|
93
|
+
if (!this.source || this.source === "silence") {
|
|
94
|
+
return ["-f", "lavfi", "-i", `aevalsrc=0:d=3600:s=${this.sampleRate}`];
|
|
95
|
+
}
|
|
96
|
+
if (this.source.startsWith("lavfi:")) {
|
|
97
|
+
return ["-f", "lavfi", "-i", this.source.slice("lavfi:".length)];
|
|
98
|
+
}
|
|
99
|
+
return ["-i", this.source];
|
|
100
|
+
};
|
|
101
|
+
#scheduleNext = (chunkSamples, chunkIntervalMs) => {
|
|
102
|
+
if (!this.#proc)
|
|
103
|
+
return;
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
if (this.#nextEmitAtMs === 0)
|
|
106
|
+
this.#nextEmitAtMs = now;
|
|
107
|
+
const delayMs = Math.max(0, this.#nextEmitAtMs - now);
|
|
108
|
+
this.#emitTimer = setTimeout(() => {
|
|
109
|
+
this.#emitTimer = null;
|
|
110
|
+
if (this.#queue.length < LOW_WATERMARK_CHUNKS && Date.now() < this.#warmupUntilMs) {
|
|
111
|
+
this.#nextEmitAtMs = Date.now() + 10;
|
|
112
|
+
this.#scheduleNext(chunkSamples, chunkIntervalMs);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.#flushOne(chunkSamples);
|
|
116
|
+
this.#nextEmitAtMs += chunkIntervalMs;
|
|
117
|
+
this.#scheduleNext(chunkSamples, chunkIntervalMs);
|
|
118
|
+
}, delayMs);
|
|
119
|
+
};
|
|
120
|
+
#flushOne = (chunkSamples) => {
|
|
121
|
+
let nextChunk = this.#queue.shift();
|
|
122
|
+
if (!nextChunk) {
|
|
123
|
+
nextChunk = new Float32Array(chunkSamples);
|
|
124
|
+
this.underflowChunks += 1;
|
|
125
|
+
}
|
|
126
|
+
this.chunksEmitted += 1;
|
|
127
|
+
this.onChunk(nextChunk);
|
|
128
|
+
if (this.#proc?.stdout.isPaused() && this.#queue.length <= MAX_QUEUED_CHUNKS / 4) {
|
|
129
|
+
this.#proc.stdout.resume();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|