@poolzin/pool-bot 2026.4.37 → 2026.4.38
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/dist/build-info.json +3 -3
- package/dist/gateway/server-methods/channels.d.ts.map +1 -1
- package/dist/gateway/server-methods/channels.js +7 -0
- package/dist/infra/heartbeat-monitor.js +11 -11
- package/dist/telegram/retry-logic.d.ts +2 -2
- package/dist/telegram/retry-logic.d.ts.map +1 -1
- package/dist/telegram/retry-logic.js +15 -15
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../../src/gateway/server-methods/channels.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAA0B,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAa5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEhF,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,SAAS,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,EAAE,qBAAqB,CAAC;IAC/B,MAAM,EAAE,aAAa,CAAC;CACvB,GAAG,OAAO,CAAC,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../../src/gateway/server-methods/channels.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAA0B,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAa5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEhF,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,SAAS,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,EAAE,qBAAqB,CAAC;IAC/B,MAAM,EAAE,aAAa,CAAC;CACvB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA+BhC;AAED,eAAO,MAAM,gBAAgB,EAAE,sBA6N9B,CAAC"}
|
|
@@ -14,6 +14,9 @@ export async function logoutChannelAccount(params) {
|
|
|
14
14
|
params.plugin.config.listAccountIds(params.cfg)[0] ||
|
|
15
15
|
DEFAULT_ACCOUNT_ID;
|
|
16
16
|
const account = params.plugin.config.resolveAccount(params.cfg, resolvedAccountId);
|
|
17
|
+
if (!account) {
|
|
18
|
+
throw new Error(`Account '${resolvedAccountId}' not found in channel config`);
|
|
19
|
+
}
|
|
17
20
|
await params.context.stopChannel(params.channelId, resolvedAccountId);
|
|
18
21
|
const result = await params.plugin.gateway?.logoutAccount?.({
|
|
19
22
|
cfg: params.cfg,
|
|
@@ -82,6 +85,8 @@ export const channelsHandlers = {
|
|
|
82
85
|
const resolvedAccounts = {};
|
|
83
86
|
for (const accountId of accountIds) {
|
|
84
87
|
const account = plugin.config.resolveAccount(cfg, accountId);
|
|
88
|
+
if (!account)
|
|
89
|
+
continue;
|
|
85
90
|
const enabled = isAccountEnabled(plugin, account);
|
|
86
91
|
resolvedAccounts[accountId] = account;
|
|
87
92
|
let probeResult;
|
|
@@ -159,6 +164,8 @@ export const channelsHandlers = {
|
|
|
159
164
|
for (const plugin of plugins) {
|
|
160
165
|
const { accounts, defaultAccountId, defaultAccount, resolvedAccounts } = await buildChannelAccounts(plugin.id);
|
|
161
166
|
const fallbackAccount = resolvedAccounts[defaultAccountId] ?? plugin.config.resolveAccount(cfg, defaultAccountId);
|
|
167
|
+
if (!fallbackAccount)
|
|
168
|
+
continue;
|
|
162
169
|
const summary = plugin.status?.buildChannelSummary
|
|
163
170
|
? await plugin.status.buildChannelSummary({
|
|
164
171
|
account: fallbackAccount,
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Heartbeat health monitoring and auto-recovery.
|
|
3
3
|
* Detects when heartbeat stops running and triggers recovery.
|
|
4
4
|
*/
|
|
5
|
-
import { readFileSync, existsSync } from
|
|
6
|
-
import { join } from
|
|
7
|
-
import { createSubsystemLogger } from
|
|
8
|
-
const log = createSubsystemLogger(
|
|
5
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
8
|
+
const log = createSubsystemLogger("heartbeat-monitor");
|
|
9
9
|
const HEARTBEAT_MAX_AGE_MS = 90 * 60 * 1000; // 90 minutes (should run every 30min)
|
|
10
10
|
const RECOVERY_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour between recovery attempts
|
|
11
11
|
let monitorState = {
|
|
@@ -17,12 +17,12 @@ let monitorState = {
|
|
|
17
17
|
* Load heartbeat state file.
|
|
18
18
|
*/
|
|
19
19
|
function loadHeartbeatState(workspaceDir) {
|
|
20
|
-
const statePath = join(workspaceDir,
|
|
20
|
+
const statePath = join(workspaceDir, "memory", "heartbeat-state.json");
|
|
21
21
|
if (!existsSync(statePath)) {
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
try {
|
|
25
|
-
const content = readFileSync(statePath,
|
|
25
|
+
const content = readFileSync(statePath, "utf-8");
|
|
26
26
|
return JSON.parse(content);
|
|
27
27
|
}
|
|
28
28
|
catch {
|
|
@@ -51,7 +51,7 @@ function isHeartbeatStale(state) {
|
|
|
51
51
|
* Trigger recovery action.
|
|
52
52
|
*/
|
|
53
53
|
async function triggerRecovery() {
|
|
54
|
-
log.info(
|
|
54
|
+
log.info("[heartbeat-monitor] Triggering recovery action...");
|
|
55
55
|
// In a full implementation, this would:
|
|
56
56
|
// 1. Send alert to admin channel
|
|
57
57
|
// 2. Try to restart heartbeat cron job
|
|
@@ -74,7 +74,7 @@ export function monitorHeartbeatHealth(workspaceDir) {
|
|
|
74
74
|
const stale = isHeartbeatStale(state);
|
|
75
75
|
if (stale) {
|
|
76
76
|
monitorState.consecutiveFailures++;
|
|
77
|
-
const lastAudit = state?.lastSecurityAudit ||
|
|
77
|
+
const lastAudit = state?.lastSecurityAudit || "never";
|
|
78
78
|
log.warn(`[heartbeat-monitor] Heartbeat is STALE! Last audit: ${lastAudit}. Failures: ${monitorState.consecutiveFailures}`);
|
|
79
79
|
// Trigger recovery if:
|
|
80
80
|
// 1. Consecutive failures >= 2 (heartbeat missed 2+ cycles)
|
|
@@ -85,13 +85,13 @@ export function monitorHeartbeatHealth(workspaceDir) {
|
|
|
85
85
|
return {
|
|
86
86
|
healthy: false,
|
|
87
87
|
lastAudit,
|
|
88
|
-
action:
|
|
88
|
+
action: "recovery_triggered",
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
return {
|
|
92
92
|
healthy: false,
|
|
93
93
|
lastAudit,
|
|
94
|
-
action: monitorState.consecutiveFailures >= 2 ?
|
|
94
|
+
action: monitorState.consecutiveFailures >= 2 ? "recovery_pending" : "monitoring",
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
// Heartbeat is healthy
|
|
@@ -124,5 +124,5 @@ export function resetMonitorState() {
|
|
|
124
124
|
lastRecovery: 0,
|
|
125
125
|
consecutiveFailures: 0,
|
|
126
126
|
};
|
|
127
|
-
log.info(
|
|
127
|
+
log.info("[heartbeat-monitor] Monitor state reset");
|
|
128
128
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Telegram API retry logic with exponential backoff.
|
|
3
3
|
* Prevents transient network errors from breaking message delivery.
|
|
4
4
|
*/
|
|
5
|
-
import { Context } from
|
|
5
|
+
import { Context } from "grammy";
|
|
6
6
|
/**
|
|
7
7
|
* Execute a Telegram API call with retry logic.
|
|
8
8
|
*/
|
|
@@ -17,7 +17,7 @@ export declare function sendMessageWithRetry(ctx: Context, text: string, log?: (
|
|
|
17
17
|
/**
|
|
18
18
|
* Send chat action with retry logic.
|
|
19
19
|
*/
|
|
20
|
-
export declare function sendChatActionWithRetry(ctx: Context, action:
|
|
20
|
+
export declare function sendChatActionWithRetry(ctx: Context, action: "typing" | "upload_photo" | "record_video" | "upload_video" | "record_voice" | "upload_voice" | "upload_document" | "find_location" | "record_video_note", log?: (msg: string) => void): Promise<void>;
|
|
21
21
|
/**
|
|
22
22
|
* Edit message with retry logic.
|
|
23
23
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-logic.d.ts","sourceRoot":"","sources":["../../src/telegram/retry-logic.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMjC;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EACrD,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"retry-logic.d.ts","sourceRoot":"","sources":["../../src/telegram/retry-logic.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAMjC;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EACrD,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,CAAC,CAAC,CA+DZ;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,MAAM,EACF,QAAQ,GACR,cAAc,GACd,cAAc,GACd,cAAc,GACd,cAAc,GACd,cAAc,GACd,iBAAiB,GACjB,eAAe,GACf,mBAAmB,EACvB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC3B,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
|
@@ -18,20 +18,20 @@ export async function withRetry(operation, context, log) {
|
|
|
18
18
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
19
19
|
const errorMsg = lastError.message.toLowerCase();
|
|
20
20
|
// Don't retry on certain errors
|
|
21
|
-
if (errorMsg.includes(
|
|
22
|
-
errorMsg.includes(
|
|
23
|
-
errorMsg.includes(
|
|
24
|
-
errorMsg.includes(
|
|
21
|
+
if (errorMsg.includes("bot was blocked") ||
|
|
22
|
+
errorMsg.includes("chat not found") ||
|
|
23
|
+
errorMsg.includes("user not found") ||
|
|
24
|
+
errorMsg.includes("message not modified")) {
|
|
25
25
|
throw lastError;
|
|
26
26
|
}
|
|
27
27
|
// Network errors, rate limits - retry
|
|
28
|
-
const isRetryable = errorMsg.includes(
|
|
29
|
-
errorMsg.includes(
|
|
30
|
-
errorMsg.includes(
|
|
31
|
-
errorMsg.includes(
|
|
32
|
-
errorMsg.includes(
|
|
33
|
-
errorMsg.includes(
|
|
34
|
-
errorMsg.includes(
|
|
28
|
+
const isRetryable = errorMsg.includes("network") ||
|
|
29
|
+
errorMsg.includes("fetch") ||
|
|
30
|
+
errorMsg.includes("timeout") ||
|
|
31
|
+
errorMsg.includes("rate limit") ||
|
|
32
|
+
errorMsg.includes("429") ||
|
|
33
|
+
errorMsg.includes("50") || // 500, 502, 503, etc.
|
|
34
|
+
errorMsg.includes("sendchataction");
|
|
35
35
|
if (!isRetryable) {
|
|
36
36
|
throw lastError;
|
|
37
37
|
}
|
|
@@ -44,7 +44,7 @@ export async function withRetry(operation, context, log) {
|
|
|
44
44
|
else {
|
|
45
45
|
console.warn(logMsg);
|
|
46
46
|
}
|
|
47
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
47
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
// All retries exhausted
|
|
@@ -58,18 +58,18 @@ export async function withRetry(operation, context, log) {
|
|
|
58
58
|
* Send message with retry logic.
|
|
59
59
|
*/
|
|
60
60
|
export async function sendMessageWithRetry(ctx, text, log) {
|
|
61
|
-
await withRetry(() => ctx.reply(text), { action:
|
|
61
|
+
await withRetry(() => ctx.reply(text), { action: "sendMessage", chatId: ctx.chat?.id }, log);
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
64
|
* Send chat action with retry logic.
|
|
65
65
|
*/
|
|
66
66
|
export async function sendChatActionWithRetry(ctx, action, log) {
|
|
67
|
-
await withRetry(() => ctx.api.sendChatAction(ctx.chat.id, action), { action:
|
|
67
|
+
await withRetry(() => ctx.api.sendChatAction(ctx.chat.id, action), { action: "sendChatAction", chatId: ctx.chat?.id }, log);
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Edit message with retry logic.
|
|
71
71
|
*/
|
|
72
72
|
export async function editMessageWithRetry(_ctx, _messageId, _text, _log) {
|
|
73
73
|
// Placeholder - implement based on actual API
|
|
74
|
-
throw new Error(
|
|
74
|
+
throw new Error("Not implemented");
|
|
75
75
|
}
|