@poolzin/pool-bot 2026.3.25 → 2026.3.27
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/agents/model-fallback.js +5 -4
- package/dist/agents/tools/common.js +16 -201
- package/dist/auto-reply/auto-reply/reply/agent-runner-execution.js +502 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-helpers.js +65 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-memory.js +160 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-payloads.js +85 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-utils.js +101 -0
- package/dist/auto-reply/auto-reply/reply/bash-command.js +338 -0
- package/dist/auto-reply/auto-reply/reply/block-streaming.js +91 -0
- package/dist/auto-reply/auto-reply/reply/commands-approve.js +88 -0
- package/dist/auto-reply/auto-reply/reply/commands-bash.js +26 -0
- package/dist/auto-reply/auto-reply/reply/commands-compact.js +107 -0
- package/dist/auto-reply/auto-reply/reply/commands-config.js +241 -0
- package/dist/auto-reply/auto-reply/reply/commands-context-report.js +295 -0
- package/dist/auto-reply/auto-reply/reply/commands-context.js +30 -0
- package/dist/auto-reply/auto-reply/reply/commands-core.js +151 -0
- package/dist/auto-reply/auto-reply/reply/commands-export-session.js +163 -0
- package/dist/auto-reply/auto-reply/reply/commands-info.js +184 -0
- package/dist/auto-reply/auto-reply/reply/commands-models.js +299 -0
- package/dist/auto-reply/auto-reply/reply/commands-plugin.js +35 -0
- package/dist/auto-reply/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-slash-parse.js +31 -0
- package/dist/auto-reply/auto-reply/reply/commands-status.js +178 -0
- package/dist/auto-reply/auto-reply/reply/commands-subagents.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-system-prompt.js +117 -0
- package/dist/auto-reply/auto-reply/reply/commands-tts.js +231 -0
- package/dist/auto-reply/auto-reply/reply/directive-handling.impl.js +380 -0
- package/dist/auto-reply/auto-reply/reply/followup-runner.js +227 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-apply.js +201 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-utils.js +54 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives.js +332 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-inline-actions.js +258 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-run.js +297 -0
- package/dist/auto-reply/auto-reply/reply/groups.js +102 -0
- package/dist/auto-reply/auto-reply/reply/mentions.js +129 -0
- package/dist/auto-reply/auto-reply/reply/reply-delivery.js +92 -0
- package/dist/auto-reply/auto-reply/reply/reply-directives.js +30 -0
- package/dist/auto-reply/auto-reply/reply/reply-dispatcher.js +152 -0
- package/dist/auto-reply/auto-reply/reply/reply-elevated.js +166 -0
- package/dist/auto-reply/auto-reply/reply/reply-inline.js +28 -0
- package/dist/auto-reply/auto-reply/reply/reply-payloads.js +114 -0
- package/dist/auto-reply/auto-reply/reply/reply-reference.js +36 -0
- package/dist/auto-reply/auto-reply/reply/reply-tags.js +13 -0
- package/dist/auto-reply/auto-reply/reply/reply-threading.js +41 -0
- package/dist/auto-reply/auto-reply/reply/session-updates.js +233 -0
- package/dist/auto-reply/auto-reply/reply/stage-sandbox-media.js +146 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +2 -17772
- package/dist/canvas-host/a2ui/index.html +1 -307
- package/dist/channels/channels/directory-config.js +185 -0
- package/dist/channels/channels/discord/handle-action.guild-admin.js +332 -0
- package/dist/channels/channels/discord/handle-action.js +165 -0
- package/dist/channels/channels/discord.js +413 -0
- package/dist/channels/channels/dock.js +436 -0
- package/dist/channels/channels/index.js +51 -0
- package/dist/channels/channels/plugins/outbound/discord.js +101 -0
- package/dist/channels/channels/whatsapp.js +17 -0
- package/dist/channels/plugins/types.js +1 -1
- package/dist/channels/run-state-machine.js +7 -0
- package/dist/commands/models/auth.js +47 -1
- package/dist/commands-subagents/action-agents.js +44 -0
- package/dist/commands-subagents/action-focus.js +64 -0
- package/dist/commands-subagents/action-help.js +4 -0
- package/dist/commands-subagents/action-info.js +45 -0
- package/dist/commands-subagents/action-kill.js +60 -0
- package/dist/commands-subagents/action-list.js +44 -0
- package/dist/commands-subagents/action-log.js +29 -0
- package/dist/commands-subagents/action-send.js +119 -0
- package/dist/commands-subagents/action-spawn.js +52 -0
- package/dist/commands-subagents/action-unfocus.js +30 -0
- package/dist/commands-subagents/shared.js +303 -0
- package/dist/config/config.js +1 -8
- package/dist/config/types.secrets.js +61 -0
- package/dist/control-ui/assets/{index-D7shnQwQ.js → index-umCsvrWy.js} +884 -741
- package/dist/control-ui/assets/index-umCsvrWy.js.map +1 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js +2 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js.map +1 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js +2 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js.map +1 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js +2 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/method-scopes.js +9 -1
- package/dist/gateway/node-pending-work.js +142 -0
- package/dist/gateway/protocol/index.js +5 -1
- package/dist/gateway/protocol/schema/nodes.js +18 -0
- package/dist/gateway/server-methods/nodes-pending.js +96 -0
- package/dist/gateway/server-methods-list.js +4 -0
- package/dist/gateway/server-methods.js +2 -0
- package/dist/imessage/channel.js +253 -0
- package/dist/imessage/monitor/echo-cache.js +70 -0
- package/dist/imessage/monitor/loop-rate-limiter.js +51 -0
- package/dist/imessage/monitor/reflection-guard.js +50 -0
- package/dist/imessage/monitor/sanitize-outbound.js +25 -0
- package/dist/imessage/monitor/self-chat-cache.js +75 -0
- package/dist/imessage/runtime.js +3 -0
- package/dist/infra/exec-approval-reply.js +7 -0
- package/dist/infra/tmp-openclaw-dir.js +84 -0
- package/dist/pairing/pairing-challenge.js +15 -0
- package/dist/plugin-sdk/account-id.d.ts +1 -0
- package/dist/plugin-sdk/agent-media-payload.d.ts +12 -0
- package/dist/plugin-sdk/allow-from.d.ts +27 -0
- package/dist/plugin-sdk/command-auth.d.ts +25 -0
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/config-paths.d.ts +6 -0
- package/dist/plugin-sdk/file-lock.d.ts +16 -0
- package/dist/plugin-sdk/index.d.ts +428 -0
- package/dist/plugin-sdk/index.js +237 -103
- package/dist/plugin-sdk/json-store.d.ts +5 -0
- package/dist/plugin-sdk/keyed-async-queue.d.ts +12 -0
- package/dist/plugin-sdk/onboarding.d.ts +11 -0
- package/dist/plugin-sdk/provider-auth-result.d.ts +14 -0
- package/dist/plugin-sdk/slack-message-actions.d.ts +11 -0
- package/dist/plugin-sdk/status-helpers.d.ts +25 -0
- package/dist/plugin-sdk/temp-path.d.ts +12 -0
- package/dist/plugin-sdk/text-chunking.d.ts +1 -0
- package/dist/plugin-sdk/tool-send.d.ts +4 -0
- package/dist/plugin-sdk/webhook-path.d.ts +6 -0
- package/dist/plugin-sdk/webhook-targets.d.ts +23 -0
- package/dist/plugin-sdk/windows-spawn.d.ts +39 -0
- package/dist/plugin-sdk-internal/accounts.js +6 -0
- package/dist/plugin-sdk-internal/discord.js +23 -0
- package/dist/plugin-sdk-internal/imessage.js +13 -0
- package/dist/plugin-sdk-internal/setup.js +9 -0
- package/dist/plugin-sdk-internal/signal.js +13 -0
- package/dist/plugin-sdk-internal/slack.js +22 -0
- package/dist/plugin-sdk-internal/telegram.js +32 -0
- package/dist/plugin-sdk-internal/whatsapp.js +29 -0
- package/dist/routing/session-key.js +4 -185
- package/dist/shared/pid-alive.js +2 -61
- package/dist/shared/process-scoped-map.js +5 -7
- package/dist/signal/channel.js +264 -0
- package/dist/signal/monitor/access-policy.js +60 -0
- package/dist/signal/runtime.js +3 -0
- package/dist/slack/account-inspect.js +135 -0
- package/dist/slack/blocks-input.js +7 -38
- package/dist/slack/channel.js +394 -0
- package/dist/slack/interactive-replies.js +28 -0
- package/dist/slack/monitor/channel-type.js +31 -0
- package/dist/slack/monitor/dm-auth.js +49 -0
- package/dist/slack/monitor/events/interactions.modal.js +137 -0
- package/dist/slack/monitor/events/message-subtype-handlers.js +68 -0
- package/dist/slack/monitor/events/system-event-context.js +29 -0
- package/dist/slack/monitor/events/system-event-test-harness.js +41 -0
- package/dist/slack/monitor/external-arg-menu-store.js +46 -0
- package/dist/slack/monitor/message-handler/prepare-content.js +69 -0
- package/dist/slack/monitor/message-handler/prepare-thread-context.js +91 -0
- package/dist/slack/monitor/message-handler/prepare.test-helpers.js +55 -0
- package/dist/slack/monitor/reconnect-policy.js +78 -0
- package/dist/slack/monitor/slash-commands.runtime.js +1 -0
- package/dist/slack/monitor/slash-dispatch.runtime.js +9 -0
- package/dist/slack/monitor/slash-skill-commands.runtime.js +1 -0
- package/dist/slack/resolve-allowlist-common.js +36 -0
- package/dist/slack/runtime.js +3 -0
- package/dist/slack/sent-thread-cache.js +61 -0
- package/dist/slack/truncate.js +10 -0
- package/dist/telegram/account-inspect.js +175 -0
- package/dist/telegram/allow-from.js +10 -0
- package/dist/telegram/api-fetch.js +18 -0
- package/dist/telegram/approval-buttons.js +30 -0
- package/dist/telegram/audit-membership-runtime.js +61 -0
- package/dist/telegram/bot/delivery.replies.js +508 -0
- package/dist/telegram/bot/delivery.resolve-media.js +227 -0
- package/dist/telegram/bot/delivery.send.js +132 -0
- package/dist/telegram/bot/reply-threading.js +46 -0
- package/dist/telegram/bot-message-context.body.js +186 -0
- package/dist/telegram/bot-message-context.session.js +207 -0
- package/dist/telegram/bot-message-context.types.js +1 -0
- package/dist/telegram/bot-native-commands.test-helpers.js +117 -0
- package/dist/telegram/bot.media.e2e-harness.js +81 -0
- package/dist/telegram/bot.media.test-utils.js +81 -0
- package/dist/telegram/channel-actions.js +225 -0
- package/dist/telegram/channel.js +515 -0
- package/dist/telegram/conversation-route.js +107 -0
- package/dist/telegram/delivery.js +2 -0
- package/dist/telegram/delivery.replies.js +508 -0
- package/dist/telegram/dm-access.js +86 -0
- package/dist/telegram/draft-stream.test-helpers.js +62 -0
- package/dist/telegram/exec-approvals-handler.js +281 -0
- package/dist/telegram/exec-approvals.js +62 -0
- package/dist/telegram/forum-service-message.js +22 -0
- package/dist/telegram/group-config-helpers.js +10 -0
- package/dist/telegram/lane-delivery-state.js +19 -0
- package/dist/telegram/lane-delivery-text-deliverer.js +357 -0
- package/dist/telegram/lane-delivery.js +2 -0
- package/dist/telegram/normalize.js +37 -0
- package/dist/telegram/onboarding.js +192 -0
- package/dist/telegram/outbound-adapter.js +100 -0
- package/dist/telegram/polling-session.js +275 -0
- package/dist/telegram/runtime.js +3 -0
- package/dist/telegram/sendchataction-401-backoff.js +71 -0
- package/dist/telegram/sequential-key.js +46 -0
- package/dist/telegram/status-issues.js +105 -0
- package/dist/telegram/target-writeback.js +165 -0
- package/dist/telegram/thread-bindings.js +560 -0
- package/dist/utils.js +32 -257
- package/dist/wizard/prompts.js +5 -5
- package/extensions/feishu/src/policy.ts +1 -1
- package/extensions/firecrawl/index.test.ts +82 -0
- package/extensions/firecrawl/index.ts +20 -0
- package/extensions/firecrawl/openclaw.plugin.json +8 -0
- package/extensions/firecrawl/package.json +12 -0
- package/extensions/firecrawl/src/config.ts +159 -0
- package/extensions/firecrawl/src/firecrawl-client.ts +446 -0
- package/extensions/firecrawl/src/firecrawl-scrape-tool.ts +89 -0
- package/extensions/firecrawl/src/firecrawl-search-provider.ts +63 -0
- package/extensions/firecrawl/src/firecrawl-search-tool.ts +76 -0
- package/package.json +1 -1
- package/dist/.buildstamp +0 -1
- package/dist/acp/bindings-store.js +0 -209
- package/dist/acp/control-plane/runtime-cache.js +0 -54
- package/dist/acp/control-plane/runtime-options.js +0 -215
- package/dist/acp/control-plane/session-actor-queue.js +0 -36
- package/dist/acp/index.js +0 -2
- package/dist/acp/runtime/errors.js +0 -47
- package/dist/acp/runtime/registry.js +0 -86
- package/dist/acp/secret-file.js +0 -22
- package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +0 -23
- package/dist/agents/bash-process-registry.test-helpers.js +0 -29
- package/dist/agents/bash-tools.exec-approval-request.js +0 -20
- package/dist/agents/bash-tools.exec-host-gateway.js +0 -240
- package/dist/agents/bash-tools.exec-host-node.js +0 -235
- package/dist/agents/checkpoint-manager.js +0 -290
- package/dist/agents/claude-cli-runner.js +0 -3
- package/dist/agents/error-classifier.js +0 -251
- package/dist/agents/live-model-filter.js +0 -84
- package/dist/agents/nvidia-models.js +0 -228
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +0 -34
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +0 -156
- package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +0 -30
- package/dist/agents/provider/config-loader.js +0 -76
- package/dist/agents/provider/index.js +0 -15
- package/dist/agents/provider/models-dev.js +0 -129
- package/dist/agents/provider/session-binding.js +0 -376
- package/dist/agents/queued-file-writer.js +0 -22
- package/dist/agents/skills/bundled-context.js +0 -23
- package/dist/agents/skills/security.js +0 -211
- package/dist/agents/skills/tools-dir.js +0 -9
- package/dist/agents/skills-install-download.js +0 -290
- package/dist/agents/skills-install-output.js +0 -30
- package/dist/agents/skills-install.download-test-utils.js +0 -36
- package/dist/agents/skills.test-helpers.js +0 -13
- package/dist/agents/subagent-announce-reliability.js +0 -160
- package/dist/agents/subagent-registry.mocks.shared.js +0 -12
- package/dist/agents/test-helpers/assistant-message-fixtures.js +0 -29
- package/dist/agents/test-helpers/fast-coding-tools.js +0 -1
- package/dist/agents/test-helpers/fast-core-tools.js +0 -8
- package/dist/agents/test-helpers/fast-tool-stubs.js +0 -18
- package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +0 -74
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +0 -27
- package/dist/agents/tool-display-common.js +0 -915
- package/dist/agents/tool-policy-shared.js +0 -108
- package/dist/agents/tool-policy.conformance.js +0 -14
- package/dist/agents/tool-result-truncation.js +0 -299
- package/dist/agents/tools/cron-tool.test-helpers.js +0 -12
- package/dist/agents/tools/discord-actions-moderation-shared.js +0 -27
- package/dist/agents/tools/discord-actions-presence.js +0 -78
- package/dist/control-ui/assets/index-D7shnQwQ.js.map +0 -1
- package/dist/discord/discord-improvements.js +0 -167
- package/dist/discord/index.js +0 -2
- package/dist/hooks/bundled/boot-md/HOOK.md +0 -19
- package/dist/hooks/bundled/command-logger/HOOK.md +0 -122
- package/dist/hooks/bundled/session-memory/HOOK.md +0 -86
- package/dist/hooks/bundled/soul-evil/HOOK.md +0 -71
- package/dist/whatsapp/normalize.js +0 -66
- package/dist/whatsapp/resolve-outbound-target.js +0 -42
- /package/dist/{acp/runtime/types.js → auto-reply/auto-reply/reply/commands-types.js} +0 -0
- /package/dist/{agents/pi-embedded-payloads.js → slack/account-surface-fields.js} +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { resolvePayloadMediaUrls, sendPayloadMediaSequence, } from "../../../src/channels/plugins/outbound/direct-text-media.js";
|
|
2
|
+
import { resolveOutboundSendDep, } from "../../../src/infra/outbound/send-deps.js";
|
|
3
|
+
import { markdownToTelegramHtmlChunks } from "./format.js";
|
|
4
|
+
import { parseTelegramReplyToMessageId, parseTelegramThreadId } from "./outbound-params.js";
|
|
5
|
+
import { sendMessageTelegram } from "./send.js";
|
|
6
|
+
function resolveTelegramSendContext(params) {
|
|
7
|
+
const send = resolveOutboundSendDep(params.deps, "telegram") ?? sendMessageTelegram;
|
|
8
|
+
return {
|
|
9
|
+
send,
|
|
10
|
+
baseOpts: {
|
|
11
|
+
verbose: false,
|
|
12
|
+
textMode: "html",
|
|
13
|
+
cfg: params.cfg,
|
|
14
|
+
messageThreadId: parseTelegramThreadId(params.threadId),
|
|
15
|
+
replyToMessageId: parseTelegramReplyToMessageId(params.replyToId),
|
|
16
|
+
accountId: params.accountId ?? undefined,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export async function sendTelegramPayloadMessages(params) {
|
|
21
|
+
const telegramData = params.payload.channelData?.telegram;
|
|
22
|
+
const quoteText = typeof telegramData?.quoteText === "string" ? telegramData.quoteText : undefined;
|
|
23
|
+
const text = params.payload.text ?? "";
|
|
24
|
+
const mediaUrls = resolvePayloadMediaUrls(params.payload);
|
|
25
|
+
const payloadOpts = {
|
|
26
|
+
...params.baseOpts,
|
|
27
|
+
quoteText,
|
|
28
|
+
};
|
|
29
|
+
if (mediaUrls.length === 0) {
|
|
30
|
+
return await params.send(params.to, text, {
|
|
31
|
+
...payloadOpts,
|
|
32
|
+
buttons: telegramData?.buttons,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// Telegram allows reply_markup on media; attach buttons only to the first send.
|
|
36
|
+
const finalResult = await sendPayloadMediaSequence({
|
|
37
|
+
text,
|
|
38
|
+
mediaUrls,
|
|
39
|
+
send: async ({ text, mediaUrl, isFirst }) => await params.send(params.to, text, {
|
|
40
|
+
...payloadOpts,
|
|
41
|
+
mediaUrl,
|
|
42
|
+
...(isFirst ? { buttons: telegramData?.buttons } : {}),
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
return finalResult ?? { messageId: "unknown", chatId: params.to };
|
|
46
|
+
}
|
|
47
|
+
export const telegramOutbound = {
|
|
48
|
+
deliveryMode: "direct",
|
|
49
|
+
chunker: markdownToTelegramHtmlChunks,
|
|
50
|
+
chunkerMode: "markdown",
|
|
51
|
+
textChunkLimit: 4000,
|
|
52
|
+
sendText: async ({ cfg, to, text, accountId, deps, replyToId, threadId }) => {
|
|
53
|
+
const { send, baseOpts } = resolveTelegramSendContext({
|
|
54
|
+
cfg,
|
|
55
|
+
deps,
|
|
56
|
+
accountId,
|
|
57
|
+
replyToId,
|
|
58
|
+
threadId,
|
|
59
|
+
});
|
|
60
|
+
const result = await send(to, text, {
|
|
61
|
+
...baseOpts,
|
|
62
|
+
});
|
|
63
|
+
return { channel: "telegram", ...result };
|
|
64
|
+
},
|
|
65
|
+
sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId, deps, replyToId, threadId, forceDocument, }) => {
|
|
66
|
+
const { send, baseOpts } = resolveTelegramSendContext({
|
|
67
|
+
cfg,
|
|
68
|
+
deps,
|
|
69
|
+
accountId,
|
|
70
|
+
replyToId,
|
|
71
|
+
threadId,
|
|
72
|
+
});
|
|
73
|
+
const result = await send(to, text, {
|
|
74
|
+
...baseOpts,
|
|
75
|
+
mediaUrl,
|
|
76
|
+
mediaLocalRoots,
|
|
77
|
+
forceDocument: forceDocument ?? false,
|
|
78
|
+
});
|
|
79
|
+
return { channel: "telegram", ...result };
|
|
80
|
+
},
|
|
81
|
+
sendPayload: async ({ cfg, to, payload, mediaLocalRoots, accountId, deps, replyToId, threadId, }) => {
|
|
82
|
+
const { send, baseOpts } = resolveTelegramSendContext({
|
|
83
|
+
cfg,
|
|
84
|
+
deps,
|
|
85
|
+
accountId,
|
|
86
|
+
replyToId,
|
|
87
|
+
threadId,
|
|
88
|
+
});
|
|
89
|
+
const result = await sendTelegramPayloadMessages({
|
|
90
|
+
send,
|
|
91
|
+
to,
|
|
92
|
+
payload,
|
|
93
|
+
baseOpts: {
|
|
94
|
+
...baseOpts,
|
|
95
|
+
mediaLocalRoots,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
return { channel: "telegram", ...result };
|
|
99
|
+
},
|
|
100
|
+
};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { run } from "@grammyjs/runner";
|
|
2
|
+
import { computeBackoff, sleepWithAbort } from "../../../src/infra/backoff.js";
|
|
3
|
+
import { formatErrorMessage } from "../../../src/infra/errors.js";
|
|
4
|
+
import { formatDurationPrecise } from "../../../src/infra/format-time/format-duration.ts";
|
|
5
|
+
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
|
6
|
+
import { createTelegramBot } from "./bot.js";
|
|
7
|
+
import { isRecoverableTelegramNetworkError } from "./network-errors.js";
|
|
8
|
+
const TELEGRAM_POLL_RESTART_POLICY = {
|
|
9
|
+
initialMs: 2000,
|
|
10
|
+
maxMs: 30_000,
|
|
11
|
+
factor: 1.8,
|
|
12
|
+
jitter: 0.25,
|
|
13
|
+
};
|
|
14
|
+
const POLL_STALL_THRESHOLD_MS = 90_000;
|
|
15
|
+
const POLL_WATCHDOG_INTERVAL_MS = 30_000;
|
|
16
|
+
const POLL_STOP_GRACE_MS = 15_000;
|
|
17
|
+
const waitForGracefulStop = async (stop) => {
|
|
18
|
+
let timer;
|
|
19
|
+
try {
|
|
20
|
+
await Promise.race([
|
|
21
|
+
stop(),
|
|
22
|
+
new Promise((resolve) => {
|
|
23
|
+
timer = setTimeout(resolve, POLL_STOP_GRACE_MS);
|
|
24
|
+
timer.unref?.();
|
|
25
|
+
}),
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
if (timer) {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export class TelegramPollingSession {
|
|
35
|
+
opts;
|
|
36
|
+
#restartAttempts = 0;
|
|
37
|
+
#webhookCleared = false;
|
|
38
|
+
#forceRestarted = false;
|
|
39
|
+
#activeRunner;
|
|
40
|
+
#activeFetchAbort;
|
|
41
|
+
constructor(opts) {
|
|
42
|
+
this.opts = opts;
|
|
43
|
+
}
|
|
44
|
+
get activeRunner() {
|
|
45
|
+
return this.#activeRunner;
|
|
46
|
+
}
|
|
47
|
+
markForceRestarted() {
|
|
48
|
+
this.#forceRestarted = true;
|
|
49
|
+
}
|
|
50
|
+
abortActiveFetch() {
|
|
51
|
+
this.#activeFetchAbort?.abort();
|
|
52
|
+
}
|
|
53
|
+
async runUntilAbort() {
|
|
54
|
+
while (!this.opts.abortSignal?.aborted) {
|
|
55
|
+
const bot = await this.#createPollingBot();
|
|
56
|
+
if (!bot) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const cleanupState = await this.#ensureWebhookCleanup(bot);
|
|
60
|
+
if (cleanupState === "retry") {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (cleanupState === "exit") {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const state = await this.#runPollingCycle(bot);
|
|
67
|
+
if (state === "exit") {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async #waitBeforeRestart(buildLine) {
|
|
73
|
+
this.#restartAttempts += 1;
|
|
74
|
+
const delayMs = computeBackoff(TELEGRAM_POLL_RESTART_POLICY, this.#restartAttempts);
|
|
75
|
+
const delay = formatDurationPrecise(delayMs);
|
|
76
|
+
this.opts.log(buildLine(delay));
|
|
77
|
+
try {
|
|
78
|
+
await sleepWithAbort(delayMs, this.opts.abortSignal);
|
|
79
|
+
}
|
|
80
|
+
catch (sleepErr) {
|
|
81
|
+
if (this.opts.abortSignal?.aborted) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
throw sleepErr;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
async #waitBeforeRetryOnRecoverableSetupError(err, logPrefix) {
|
|
89
|
+
if (this.opts.abortSignal?.aborted) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (!isRecoverableTelegramNetworkError(err, { context: "unknown" })) {
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
return this.#waitBeforeRestart((delay) => `${logPrefix}: ${formatErrorMessage(err)}; retrying in ${delay}.`);
|
|
96
|
+
}
|
|
97
|
+
async #createPollingBot() {
|
|
98
|
+
const fetchAbortController = new AbortController();
|
|
99
|
+
this.#activeFetchAbort = fetchAbortController;
|
|
100
|
+
try {
|
|
101
|
+
return createTelegramBot({
|
|
102
|
+
token: this.opts.token,
|
|
103
|
+
runtime: this.opts.runtime,
|
|
104
|
+
proxyFetch: this.opts.proxyFetch,
|
|
105
|
+
config: this.opts.config,
|
|
106
|
+
accountId: this.opts.accountId,
|
|
107
|
+
fetchAbortSignal: fetchAbortController.signal,
|
|
108
|
+
updateOffset: {
|
|
109
|
+
lastUpdateId: this.opts.getLastUpdateId(),
|
|
110
|
+
onUpdateId: this.opts.persistUpdateId,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
await this.#waitBeforeRetryOnRecoverableSetupError(err, "Telegram setup network error");
|
|
116
|
+
if (this.#activeFetchAbort === fetchAbortController) {
|
|
117
|
+
this.#activeFetchAbort = undefined;
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async #ensureWebhookCleanup(bot) {
|
|
123
|
+
if (this.#webhookCleared) {
|
|
124
|
+
return "ready";
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
await withTelegramApiErrorLogging({
|
|
128
|
+
operation: "deleteWebhook",
|
|
129
|
+
runtime: this.opts.runtime,
|
|
130
|
+
fn: () => bot.api.deleteWebhook({ drop_pending_updates: false }),
|
|
131
|
+
});
|
|
132
|
+
this.#webhookCleared = true;
|
|
133
|
+
return "ready";
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const shouldRetry = await this.#waitBeforeRetryOnRecoverableSetupError(err, "Telegram webhook cleanup failed");
|
|
137
|
+
return shouldRetry ? "retry" : "exit";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async #confirmPersistedOffset(bot) {
|
|
141
|
+
const lastUpdateId = this.opts.getLastUpdateId();
|
|
142
|
+
if (lastUpdateId === null || lastUpdateId >= Number.MAX_SAFE_INTEGER) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
await bot.api.getUpdates({ offset: lastUpdateId + 1, limit: 1, timeout: 0 });
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Non-fatal: runner middleware still skips duplicates via shouldSkipUpdate.
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async #runPollingCycle(bot) {
|
|
153
|
+
await this.#confirmPersistedOffset(bot);
|
|
154
|
+
let lastGetUpdatesAt = Date.now();
|
|
155
|
+
bot.api.config.use((prev, method, payload, signal) => {
|
|
156
|
+
if (method === "getUpdates") {
|
|
157
|
+
lastGetUpdatesAt = Date.now();
|
|
158
|
+
}
|
|
159
|
+
return prev(method, payload, signal);
|
|
160
|
+
});
|
|
161
|
+
const runner = run(bot, this.opts.runnerOptions);
|
|
162
|
+
this.#activeRunner = runner;
|
|
163
|
+
const fetchAbortController = this.#activeFetchAbort;
|
|
164
|
+
let stopPromise;
|
|
165
|
+
let stalledRestart = false;
|
|
166
|
+
let forceCycleTimer;
|
|
167
|
+
let forceCycleResolve;
|
|
168
|
+
const forceCyclePromise = new Promise((resolve) => {
|
|
169
|
+
forceCycleResolve = resolve;
|
|
170
|
+
});
|
|
171
|
+
const stopRunner = () => {
|
|
172
|
+
fetchAbortController?.abort();
|
|
173
|
+
stopPromise ??= Promise.resolve(runner.stop())
|
|
174
|
+
.then(() => undefined)
|
|
175
|
+
.catch(() => {
|
|
176
|
+
// Runner may already be stopped by abort/retry paths.
|
|
177
|
+
});
|
|
178
|
+
return stopPromise;
|
|
179
|
+
};
|
|
180
|
+
const stopBot = () => {
|
|
181
|
+
return Promise.resolve(bot.stop())
|
|
182
|
+
.then(() => undefined)
|
|
183
|
+
.catch(() => {
|
|
184
|
+
// Bot may already be stopped by runner stop/abort paths.
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
const stopOnAbort = () => {
|
|
188
|
+
if (this.opts.abortSignal?.aborted) {
|
|
189
|
+
void stopRunner();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const watchdog = setInterval(() => {
|
|
193
|
+
if (this.opts.abortSignal?.aborted) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const elapsed = Date.now() - lastGetUpdatesAt;
|
|
197
|
+
if (elapsed > POLL_STALL_THRESHOLD_MS && runner.isRunning()) {
|
|
198
|
+
stalledRestart = true;
|
|
199
|
+
this.opts.log(`[telegram] Polling stall detected (no getUpdates for ${formatDurationPrecise(elapsed)}); forcing restart.`);
|
|
200
|
+
void stopRunner();
|
|
201
|
+
void stopBot();
|
|
202
|
+
if (!forceCycleTimer) {
|
|
203
|
+
forceCycleTimer = setTimeout(() => {
|
|
204
|
+
if (this.opts.abortSignal?.aborted) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.opts.log(`[telegram] Polling runner stop timed out after ${formatDurationPrecise(POLL_STOP_GRACE_MS)}; forcing restart cycle.`);
|
|
208
|
+
forceCycleResolve?.();
|
|
209
|
+
}, POLL_STOP_GRACE_MS);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}, POLL_WATCHDOG_INTERVAL_MS);
|
|
213
|
+
this.opts.abortSignal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
214
|
+
try {
|
|
215
|
+
await Promise.race([runner.task(), forceCyclePromise]);
|
|
216
|
+
if (this.opts.abortSignal?.aborted) {
|
|
217
|
+
return "exit";
|
|
218
|
+
}
|
|
219
|
+
const reason = stalledRestart
|
|
220
|
+
? "polling stall detected"
|
|
221
|
+
: this.#forceRestarted
|
|
222
|
+
? "unhandled network error"
|
|
223
|
+
: "runner stopped (maxRetryTime exceeded or graceful stop)";
|
|
224
|
+
this.#forceRestarted = false;
|
|
225
|
+
const shouldRestart = await this.#waitBeforeRestart((delay) => `Telegram polling runner stopped (${reason}); restarting in ${delay}.`);
|
|
226
|
+
return shouldRestart ? "continue" : "exit";
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
this.#forceRestarted = false;
|
|
230
|
+
if (this.opts.abortSignal?.aborted) {
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
233
|
+
const isConflict = isGetUpdatesConflict(err);
|
|
234
|
+
if (isConflict) {
|
|
235
|
+
this.#webhookCleared = false;
|
|
236
|
+
}
|
|
237
|
+
const isRecoverable = isRecoverableTelegramNetworkError(err, { context: "polling" });
|
|
238
|
+
if (!isConflict && !isRecoverable) {
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
const reason = isConflict ? "getUpdates conflict" : "network error";
|
|
242
|
+
const errMsg = formatErrorMessage(err);
|
|
243
|
+
const shouldRestart = await this.#waitBeforeRestart((delay) => `Telegram ${reason}: ${errMsg}; retrying in ${delay}.`);
|
|
244
|
+
return shouldRestart ? "continue" : "exit";
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
clearInterval(watchdog);
|
|
248
|
+
if (forceCycleTimer) {
|
|
249
|
+
clearTimeout(forceCycleTimer);
|
|
250
|
+
}
|
|
251
|
+
this.opts.abortSignal?.removeEventListener("abort", stopOnAbort);
|
|
252
|
+
await waitForGracefulStop(stopRunner);
|
|
253
|
+
await waitForGracefulStop(stopBot);
|
|
254
|
+
this.#activeRunner = undefined;
|
|
255
|
+
if (this.#activeFetchAbort === fetchAbortController) {
|
|
256
|
+
this.#activeFetchAbort = undefined;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const isGetUpdatesConflict = (err) => {
|
|
262
|
+
if (!err || typeof err !== "object") {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
const typed = err;
|
|
266
|
+
const errorCode = typed.error_code ?? typed.errorCode;
|
|
267
|
+
if (errorCode !== 409) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
const haystack = [typed.method, typed.description, typed.message]
|
|
271
|
+
.filter((value) => typeof value === "string")
|
|
272
|
+
.join(" ")
|
|
273
|
+
.toLowerCase();
|
|
274
|
+
return haystack.includes("getupdates");
|
|
275
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { computeBackoff, sleepWithAbort } from "../../../src/infra/backoff.js";
|
|
2
|
+
const BACKOFF_POLICY = {
|
|
3
|
+
initialMs: 1000,
|
|
4
|
+
maxMs: 300_000, // 5 minutes
|
|
5
|
+
factor: 2,
|
|
6
|
+
jitter: 0.1,
|
|
7
|
+
};
|
|
8
|
+
function is401Error(error) {
|
|
9
|
+
if (!error) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const message = error instanceof Error ? error.message : JSON.stringify(error);
|
|
13
|
+
return message.includes("401") || message.toLowerCase().includes("unauthorized");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a GLOBAL (per-account) handler for sendChatAction that tracks 401 errors
|
|
17
|
+
* across all message contexts. This prevents the infinite loop that caused Telegram
|
|
18
|
+
* to delete bots (issue #27092).
|
|
19
|
+
*
|
|
20
|
+
* When a 401 occurs, exponential backoff is applied (1s → 2s → 4s → ... → 5min).
|
|
21
|
+
* After maxConsecutive401 failures (default 10), all sendChatAction calls are
|
|
22
|
+
* suspended until reset() is called.
|
|
23
|
+
*/
|
|
24
|
+
export function createTelegramSendChatActionHandler({ sendChatActionFn, logger, maxConsecutive401 = 10, }) {
|
|
25
|
+
let consecutive401Failures = 0;
|
|
26
|
+
let suspended = false;
|
|
27
|
+
const reset = () => {
|
|
28
|
+
consecutive401Failures = 0;
|
|
29
|
+
suspended = false;
|
|
30
|
+
};
|
|
31
|
+
const sendChatAction = async (chatId, action, threadParams) => {
|
|
32
|
+
if (suspended) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (consecutive401Failures > 0) {
|
|
36
|
+
const backoffMs = computeBackoff(BACKOFF_POLICY, consecutive401Failures);
|
|
37
|
+
logger(`sendChatAction backoff: waiting ${backoffMs}ms before retry ` +
|
|
38
|
+
`(failure ${consecutive401Failures}/${maxConsecutive401})`);
|
|
39
|
+
await sleepWithAbort(backoffMs);
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
await sendChatActionFn(chatId, action, threadParams);
|
|
43
|
+
// Success: reset failure counter
|
|
44
|
+
if (consecutive401Failures > 0) {
|
|
45
|
+
logger(`sendChatAction recovered after ${consecutive401Failures} consecutive 401 failures`);
|
|
46
|
+
consecutive401Failures = 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (is401Error(error)) {
|
|
51
|
+
consecutive401Failures++;
|
|
52
|
+
if (consecutive401Failures >= maxConsecutive401) {
|
|
53
|
+
suspended = true;
|
|
54
|
+
logger(`CRITICAL: sendChatAction suspended after ${consecutive401Failures} consecutive 401 errors. ` +
|
|
55
|
+
`Bot token is likely invalid. Telegram may DELETE the bot if requests continue. ` +
|
|
56
|
+
`Replace the token and restart: openclaw channels restart telegram`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
logger(`sendChatAction 401 error (${consecutive401Failures}/${maxConsecutive401}). ` +
|
|
60
|
+
`Retrying with exponential backoff.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
sendChatAction,
|
|
68
|
+
isSuspended: () => suspended,
|
|
69
|
+
reset,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isAbortRequestText } from "../../../src/auto-reply/reply/abort.js";
|
|
2
|
+
import { isBtwRequestText } from "../../../src/auto-reply/reply/btw-command.js";
|
|
3
|
+
import { resolveTelegramForumThreadId } from "./bot/helpers.js";
|
|
4
|
+
export function getTelegramSequentialKey(ctx) {
|
|
5
|
+
const reaction = ctx.update?.message_reaction;
|
|
6
|
+
if (reaction?.chat?.id) {
|
|
7
|
+
return `telegram:${reaction.chat.id}`;
|
|
8
|
+
}
|
|
9
|
+
const msg = ctx.message ??
|
|
10
|
+
ctx.channelPost ??
|
|
11
|
+
ctx.editedChannelPost ??
|
|
12
|
+
ctx.update?.message ??
|
|
13
|
+
ctx.update?.edited_message ??
|
|
14
|
+
ctx.update?.channel_post ??
|
|
15
|
+
ctx.update?.edited_channel_post ??
|
|
16
|
+
ctx.update?.callback_query?.message;
|
|
17
|
+
const chatId = msg?.chat?.id ?? ctx.chat?.id;
|
|
18
|
+
const rawText = msg?.text ?? msg?.caption;
|
|
19
|
+
const botUsername = ctx.me?.username;
|
|
20
|
+
if (isAbortRequestText(rawText, botUsername ? { botUsername } : undefined)) {
|
|
21
|
+
if (typeof chatId === "number") {
|
|
22
|
+
return `telegram:${chatId}:control`;
|
|
23
|
+
}
|
|
24
|
+
return "telegram:control";
|
|
25
|
+
}
|
|
26
|
+
if (isBtwRequestText(rawText, botUsername ? { botUsername } : undefined)) {
|
|
27
|
+
const messageId = msg?.message_id;
|
|
28
|
+
if (typeof chatId === "number" && typeof messageId === "number") {
|
|
29
|
+
return `telegram:${chatId}:btw:${messageId}`;
|
|
30
|
+
}
|
|
31
|
+
if (typeof chatId === "number") {
|
|
32
|
+
return `telegram:${chatId}:btw`;
|
|
33
|
+
}
|
|
34
|
+
return "telegram:btw";
|
|
35
|
+
}
|
|
36
|
+
const isGroup = msg?.chat?.type === "group" || msg?.chat?.type === "supergroup";
|
|
37
|
+
const messageThreadId = msg?.message_thread_id;
|
|
38
|
+
const isForum = msg?.chat?.is_forum;
|
|
39
|
+
const threadId = isGroup
|
|
40
|
+
? resolveTelegramForumThreadId({ isForum, messageThreadId })
|
|
41
|
+
: messageThreadId;
|
|
42
|
+
if (typeof chatId === "number") {
|
|
43
|
+
return threadId != null ? `telegram:${chatId}:topic:${threadId}` : `telegram:${chatId}`;
|
|
44
|
+
}
|
|
45
|
+
return "telegram:unknown";
|
|
46
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { appendMatchMetadata, asString, isRecord, resolveEnabledConfiguredAccountId, } from "../../../src/channels/plugins/status-issues/shared.js";
|
|
2
|
+
function readTelegramAccountStatus(value) {
|
|
3
|
+
if (!isRecord(value)) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
return {
|
|
7
|
+
accountId: value.accountId,
|
|
8
|
+
enabled: value.enabled,
|
|
9
|
+
configured: value.configured,
|
|
10
|
+
allowUnmentionedGroups: value.allowUnmentionedGroups,
|
|
11
|
+
audit: value.audit,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function readTelegramGroupMembershipAuditSummary(value) {
|
|
15
|
+
if (!isRecord(value)) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
const unresolvedGroups = typeof value.unresolvedGroups === "number" && Number.isFinite(value.unresolvedGroups)
|
|
19
|
+
? value.unresolvedGroups
|
|
20
|
+
: undefined;
|
|
21
|
+
const hasWildcardUnmentionedGroups = typeof value.hasWildcardUnmentionedGroups === "boolean"
|
|
22
|
+
? value.hasWildcardUnmentionedGroups
|
|
23
|
+
: undefined;
|
|
24
|
+
const groupsRaw = value.groups;
|
|
25
|
+
const groups = Array.isArray(groupsRaw)
|
|
26
|
+
? groupsRaw
|
|
27
|
+
.map((entry) => {
|
|
28
|
+
if (!isRecord(entry)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const chatId = asString(entry.chatId);
|
|
32
|
+
if (!chatId) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const ok = typeof entry.ok === "boolean" ? entry.ok : undefined;
|
|
36
|
+
const status = asString(entry.status) ?? null;
|
|
37
|
+
const error = asString(entry.error) ?? null;
|
|
38
|
+
const matchKey = asString(entry.matchKey) ?? undefined;
|
|
39
|
+
const matchSource = asString(entry.matchSource) ?? undefined;
|
|
40
|
+
return { chatId, ok, status, error, matchKey, matchSource };
|
|
41
|
+
})
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
: undefined;
|
|
44
|
+
return { unresolvedGroups, hasWildcardUnmentionedGroups, groups };
|
|
45
|
+
}
|
|
46
|
+
export function collectTelegramStatusIssues(accounts) {
|
|
47
|
+
const issues = [];
|
|
48
|
+
for (const entry of accounts) {
|
|
49
|
+
const account = readTelegramAccountStatus(entry);
|
|
50
|
+
if (!account) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const accountId = resolveEnabledConfiguredAccountId(account);
|
|
54
|
+
if (!accountId) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (account.allowUnmentionedGroups === true) {
|
|
58
|
+
issues.push({
|
|
59
|
+
channel: "telegram",
|
|
60
|
+
accountId,
|
|
61
|
+
kind: "config",
|
|
62
|
+
message: "Config allows unmentioned group messages (requireMention=false). Telegram Bot API privacy mode will block most group messages unless disabled.",
|
|
63
|
+
fix: "In BotFather run /setprivacy → Disable for this bot (then restart the gateway).",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const audit = readTelegramGroupMembershipAuditSummary(account.audit);
|
|
67
|
+
if (audit.hasWildcardUnmentionedGroups === true) {
|
|
68
|
+
issues.push({
|
|
69
|
+
channel: "telegram",
|
|
70
|
+
accountId,
|
|
71
|
+
kind: "config",
|
|
72
|
+
message: 'Telegram groups config uses "*" with requireMention=false; membership probing is not possible without explicit group IDs.',
|
|
73
|
+
fix: "Add explicit numeric group ids under channels.telegram.groups (or per-account groups) to enable probing.",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (audit.unresolvedGroups && audit.unresolvedGroups > 0) {
|
|
77
|
+
issues.push({
|
|
78
|
+
channel: "telegram",
|
|
79
|
+
accountId,
|
|
80
|
+
kind: "config",
|
|
81
|
+
message: `Some configured Telegram groups are not numeric IDs (unresolvedGroups=${audit.unresolvedGroups}). Membership probe can only check numeric group IDs.`,
|
|
82
|
+
fix: "Use numeric chat IDs (e.g. -100...) as keys in channels.telegram.groups for requireMention=false groups.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
for (const group of audit.groups ?? []) {
|
|
86
|
+
if (group.ok === true) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const status = group.status ? ` status=${group.status}` : "";
|
|
90
|
+
const err = group.error ? `: ${group.error}` : "";
|
|
91
|
+
const baseMessage = `Group ${group.chatId} not reachable by bot.${status}${err}`;
|
|
92
|
+
issues.push({
|
|
93
|
+
channel: "telegram",
|
|
94
|
+
accountId,
|
|
95
|
+
kind: "runtime",
|
|
96
|
+
message: appendMatchMetadata(baseMessage, {
|
|
97
|
+
matchKey: group.matchKey,
|
|
98
|
+
matchSource: group.matchSource,
|
|
99
|
+
}),
|
|
100
|
+
fix: "Invite the bot to the group, then DM the bot once (/start) and restart the gateway.",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return issues;
|
|
105
|
+
}
|