@inetafrica/open-claudia 2.6.43 → 2.6.44
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/channels/telegram/adapter.js +47 -15
- package/package.json +1 -1
|
@@ -19,11 +19,16 @@ class TelegramAdapter {
|
|
|
19
19
|
this.token = token;
|
|
20
20
|
this.ownerChatId = ownerChatId;
|
|
21
21
|
this.chatIds = chatIds || [];
|
|
22
|
+
// Own the HTTP(S) agent so we can drop dead keep-alive sockets on reconnect.
|
|
23
|
+
this._agent = new https.Agent({ keepAlive: true });
|
|
22
24
|
this.bot = new TelegramBot(token, {
|
|
23
25
|
polling: { autoStart: false, params: { timeout: 30 } },
|
|
26
|
+
request: { agent: this._agent },
|
|
24
27
|
});
|
|
25
28
|
this._listeners = { message: new Set(), action: new Set() };
|
|
26
29
|
this._reconnectTimer = null;
|
|
30
|
+
this._recoveryTimer = null;
|
|
31
|
+
this._reconnectAttempts = 0;
|
|
27
32
|
this._wireInbound();
|
|
28
33
|
}
|
|
29
34
|
|
|
@@ -45,9 +50,49 @@ class TelegramAdapter {
|
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
async stop() {
|
|
53
|
+
if (this._reconnectTimer) { clearTimeout(this._reconnectTimer); this._reconnectTimer = null; }
|
|
54
|
+
if (this._recoveryTimer) { clearTimeout(this._recoveryTimer); this._recoveryTimer = null; }
|
|
48
55
|
try { await this.bot.stopPolling(); } catch (e) {}
|
|
49
56
|
}
|
|
50
57
|
|
|
58
|
+
// Recover from a wedged poll loop. The library reuses one keep-alive socket;
|
|
59
|
+
// after a sleep/wake or network blip that socket dies and every poll then
|
|
60
|
+
// ETIMEDOUTs forever — restarting polling alone reuses the same dead socket.
|
|
61
|
+
// So we destroy the agent (forcing a fresh connection), back off, and if we
|
|
62
|
+
// still can't recover after several tries we exit so launchd (KeepAlive)
|
|
63
|
+
// relaunches a fully clean process.
|
|
64
|
+
_scheduleReconnect() {
|
|
65
|
+
if (this._reconnectTimer) return; // one cycle in flight; swallow the flood
|
|
66
|
+
if (this._recoveryTimer) { clearTimeout(this._recoveryTimer); this._recoveryTimer = null; }
|
|
67
|
+
this._reconnectAttempts += 1;
|
|
68
|
+
const MAX = 6;
|
|
69
|
+
if (this._reconnectAttempts > MAX) {
|
|
70
|
+
console.error(`Polling unrecoverable after ${MAX} attempts — exiting for supervisor restart.`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const delay = Math.min(30000, 1000 * 2 ** this._reconnectAttempts); // 2s,4s,8s,16s,30s,30s
|
|
74
|
+
console.log(`Network lost. Reconnecting in ${Math.round(delay / 1000)}s (attempt ${this._reconnectAttempts}/${MAX})...`);
|
|
75
|
+
this._reconnectTimer = setTimeout(async () => {
|
|
76
|
+
this._reconnectTimer = null;
|
|
77
|
+
try {
|
|
78
|
+
try { await this.bot.stopPolling(); } catch (e) {}
|
|
79
|
+
try { this._agent.destroy(); } catch (e) {} // drop the dead pooled socket
|
|
80
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
81
|
+
await this.bot.startPolling();
|
|
82
|
+
console.log("Polling restarted.");
|
|
83
|
+
// No fresh error within 30s ⇒ genuinely recovered; reset the counter.
|
|
84
|
+
this._recoveryTimer = setTimeout(() => {
|
|
85
|
+
this._recoveryTimer = null;
|
|
86
|
+
if (this._reconnectAttempts) console.log("Connection healthy again.");
|
|
87
|
+
this._reconnectAttempts = 0;
|
|
88
|
+
}, 30000);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.error("Reconnect attempt failed:", e.message);
|
|
91
|
+
this._scheduleReconnect(); // back off and retry; don't die on first miss
|
|
92
|
+
}
|
|
93
|
+
}, delay);
|
|
94
|
+
}
|
|
95
|
+
|
|
51
96
|
_wireInbound() {
|
|
52
97
|
this.bot.on("polling_error", (err) => {
|
|
53
98
|
const msg = err.message || "";
|
|
@@ -56,21 +101,8 @@ class TelegramAdapter {
|
|
|
56
101
|
console.error("Another instance is polling. Exiting.");
|
|
57
102
|
process.exit(1);
|
|
58
103
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
console.log("Network lost. Reconnecting in 10s...");
|
|
62
|
-
this._reconnectTimer = setTimeout(async () => {
|
|
63
|
-
this._reconnectTimer = null;
|
|
64
|
-
try {
|
|
65
|
-
await this.bot.stopPolling();
|
|
66
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
67
|
-
await this.bot.startPolling();
|
|
68
|
-
console.log("Reconnected.");
|
|
69
|
-
} catch (e) {
|
|
70
|
-
console.error("Reconnect failed:", e.message);
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
}, 10000);
|
|
104
|
+
if (/ETIMEDOUT|ECONNRESET|ENOTFOUND|ENETUNREACH|EAI_AGAIN|EFATAL|socket hang up/i.test(msg)) {
|
|
105
|
+
this._scheduleReconnect();
|
|
74
106
|
}
|
|
75
107
|
});
|
|
76
108
|
|
package/package.json
CHANGED