@poolzin/pool-bot 2026.2.21 → 2026.2.23
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/CHANGELOG.md +25 -0
- package/dist/agents/api-key-rotation.js +47 -0
- package/dist/agents/apply-patch-update.js +19 -9
- package/dist/agents/apply-patch.js +72 -47
- package/dist/agents/bash-tools.exec.js +141 -559
- package/dist/agents/cli-backends.js +49 -6
- package/dist/agents/cli-runner/helpers.js +69 -152
- package/dist/agents/cli-runner.js +70 -19
- package/dist/agents/identity.js +20 -1
- package/dist/agents/image-sanitization.js +9 -0
- package/dist/agents/live-auth-keys.js +123 -26
- package/dist/agents/live-model-filter.js +13 -4
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-forward-compat.js +60 -23
- package/dist/agents/model-selection.js +134 -41
- package/dist/agents/pi-auth-json.js +2 -2
- package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
- package/dist/agents/pi-embedded-helpers/errors.js +140 -15
- package/dist/agents/pi-embedded-helpers/images.js +22 -12
- package/dist/agents/pi-embedded-helpers.js +2 -2
- package/dist/agents/pi-embedded-runner/abort.js +10 -3
- package/dist/agents/pi-embedded-runner/compact.js +230 -32
- package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
- package/dist/agents/pi-embedded-runner/google.js +109 -19
- package/dist/agents/pi-embedded-runner/history.js +35 -17
- package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
- package/dist/agents/pi-embedded-runner/run/images.js +81 -55
- package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
- package/dist/agents/pi-embedded-runner/run.js +193 -25
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
- package/dist/agents/pi-embedded-runner/runs.js +17 -8
- package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
- package/dist/agents/pi-embedded-runner.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
- package/dist/agents/pi-embedded-subscribe.js +37 -0
- package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
- package/dist/agents/pi-model-discovery.js +9 -2
- package/dist/agents/pi-tool-definition-adapter.js +60 -8
- package/dist/agents/pi-tools.before-tool-call.js +1 -1
- package/dist/agents/pi-tools.js +113 -94
- package/dist/agents/pi-tools.read.js +337 -38
- package/dist/agents/poolbot-tools.js +14 -5
- package/dist/agents/sandbox/docker.js +10 -5
- package/dist/agents/sandbox/registry.js +96 -46
- package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
- package/dist/agents/sandbox-paths.js +43 -10
- package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
- package/dist/agents/session-tool-result-guard.js +39 -39
- package/dist/agents/session-transcript-repair.js +36 -33
- package/dist/agents/session-write-lock.js +62 -44
- package/dist/agents/skills/frontmatter.js +49 -88
- package/dist/agents/skills/workspace.js +335 -28
- package/dist/agents/subagent-announce.js +508 -174
- package/dist/agents/subagent-registry.js +45 -4
- package/dist/agents/subagent-spawn.js +16 -33
- package/dist/agents/system-prompt-report.js +27 -10
- package/dist/agents/system-prompt.js +26 -32
- package/dist/agents/tool-call-id.js +69 -17
- package/dist/agents/tool-display-common.js +1 -1
- package/dist/agents/tool-images.js +64 -31
- package/dist/agents/tools/canvas-tool.js +17 -11
- package/dist/agents/tools/common.js +37 -19
- package/dist/agents/tools/cron-tool.js +40 -38
- package/dist/agents/tools/gateway.js +70 -2
- package/dist/agents/tools/message-tool.js +181 -40
- package/dist/agents/tools/nodes-tool.js +128 -36
- package/dist/agents/tools/nodes-utils.js +12 -38
- package/dist/agents/tools/session-status-tool.js +24 -71
- package/dist/agents/tools/sessions-helpers.js +38 -210
- package/dist/agents/tools/sessions-spawn-tool.js +28 -198
- package/dist/agents/tools/telegram-actions.js +58 -7
- package/dist/agents/tools/web-fetch-utils.js +112 -7
- package/dist/agents/tools/web-fetch.js +279 -175
- package/dist/agents/tools/web-shared.js +71 -8
- package/dist/agents/usage.js +25 -16
- package/dist/auto-reply/commands-registry.data.js +85 -11
- package/dist/auto-reply/dispatch.js +40 -21
- package/dist/auto-reply/reply/abort.js +102 -33
- package/dist/auto-reply/reply/commands-core.js +82 -33
- package/dist/auto-reply/reply/commands-export-session.js +1 -1
- package/dist/auto-reply/reply/commands-info.js +41 -12
- package/dist/auto-reply/reply/commands-subagents.js +352 -100
- package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
- package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
- package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
- package/dist/auto-reply/reply/inbound-meta.js +12 -1
- package/dist/auto-reply/reply/mentions.js +18 -11
- package/dist/auto-reply/reply/normalize-reply.js +17 -8
- package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
- package/dist/auto-reply/reply/session.js +102 -21
- package/dist/auto-reply/reply/streaming-directives.js +16 -5
- package/dist/auto-reply/status.js +73 -50
- package/dist/browser/extension-relay.js +3 -3
- package/dist/browser/http-auth.js +1 -1
- package/dist/browser/paths.js +2 -2
- package/dist/build-info.json +3 -3
- package/dist/channels/allowlist-match.js +20 -0
- package/dist/channels/allowlists/resolve-utils.js +65 -2
- package/dist/channels/chat-type.js +8 -4
- package/dist/channels/dock.js +127 -35
- package/dist/channels/draft-stream-loop.js +6 -2
- package/dist/channels/plugins/actions/telegram.js +42 -18
- package/dist/channels/plugins/allowlist-match.js +1 -1
- package/dist/channels/plugins/group-mentions.js +51 -41
- package/dist/channels/plugins/message-action-names.js +2 -0
- package/dist/channels/plugins/message-actions.js +24 -5
- package/dist/channels/plugins/normalize/discord.js +26 -4
- package/dist/channels/plugins/normalize/signal.js +35 -22
- package/dist/channels/plugins/onboarding/helpers.js +8 -26
- package/dist/channels/plugins/outbound/imessage.js +15 -14
- package/dist/channels/registry.js +20 -7
- package/dist/cli/acp-cli.js +7 -5
- package/dist/cli/browser-cli-extension.js +25 -12
- package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
- package/dist/cli/browser-cli-state.js +101 -145
- package/dist/cli/command-options.js +28 -0
- package/dist/cli/completion-cli.js +6 -6
- package/dist/cli/cron-cli/register.cron-add.js +25 -1
- package/dist/cli/cron-cli/register.cron-edit.js +44 -0
- package/dist/cli/cron-cli/shared.js +7 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
- package/dist/cli/daemon-cli/lifecycle.js +23 -247
- package/dist/cli/daemon-cli/register-service-commands.js +25 -4
- package/dist/cli/daemon-cli.js +1 -0
- package/dist/cli/devices-cli.js +33 -20
- package/dist/cli/gateway-cli/register.js +37 -105
- package/dist/cli/gateway-cli/run.js +49 -11
- package/dist/cli/nodes-camera.js +59 -4
- package/dist/cli/nodes-cli/register.camera.js +27 -24
- package/dist/cli/nodes-cli/rpc.js +21 -38
- package/dist/cli/qr-cli.js +2 -2
- package/dist/cli/skills-cli.format.js +2 -2
- package/dist/cli/update-cli/progress.js +2 -2
- package/dist/cli/update-cli/restart-helper.js +28 -7
- package/dist/cli/update-cli/shared.js +7 -7
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +14 -8
- package/dist/cli/update-cli/wizard.js +2 -2
- package/dist/cli/update-cli.js +21 -1027
- package/dist/commands/auth-choice.apply.anthropic.js +10 -2
- package/dist/commands/channels/add-mutators.js +3 -35
- package/dist/commands/channels/add.js +39 -51
- package/dist/commands/config-validation.js +1 -1
- package/dist/commands/configure.gateway-auth.js +52 -15
- package/dist/commands/configure.gateway.js +84 -40
- package/dist/commands/doctor-completion.js +3 -3
- package/dist/commands/doctor-config-flow.js +536 -16
- package/dist/commands/doctor-gateway-services.js +103 -79
- package/dist/commands/doctor-memory-search.js +9 -9
- package/dist/commands/doctor-platform-notes.js +57 -30
- package/dist/commands/doctor-prompter.js +26 -15
- package/dist/commands/doctor-session-locks.js +1 -1
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/model-picker.js +120 -95
- package/dist/commands/models/set.js +2 -21
- package/dist/commands/models/shared.js +65 -37
- package/dist/commands/onboard-helpers.js +81 -39
- package/dist/commands/openai-codex-oauth.js +1 -1
- package/dist/commands/sessions.js +52 -53
- package/dist/commands/status.summary.js +52 -34
- package/dist/commands/test-wizard-helpers.js +2 -2
- package/dist/config/defaults.js +79 -42
- package/dist/config/group-policy.js +50 -18
- package/dist/config/includes.js +37 -10
- package/dist/config/schema.help.js +5 -4
- package/dist/config/schema.hints.js +2 -2
- package/dist/config/schema.labels.js +1 -0
- package/dist/config/sessions/group.js +12 -11
- package/dist/config/sessions/paths.js +137 -11
- package/dist/config/sessions/store.js +185 -65
- package/dist/config/sessions/types.js +15 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/telegram-custom-commands.js +3 -2
- package/dist/config/types.js +2 -0
- package/dist/config/zod-schema.agent-defaults.js +6 -27
- package/dist/config/zod-schema.agent-runtime.js +171 -79
- package/dist/config/zod-schema.providers-core.js +138 -65
- package/dist/config/zod-schema.session.js +49 -22
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/run.js +224 -57
- package/dist/cron/normalize.js +48 -45
- package/dist/cron/run-log.js +14 -0
- package/dist/cron/service/jobs.js +190 -28
- package/dist/cron/service/normalize.js +29 -11
- package/dist/cron/service/store.js +30 -44
- package/dist/cron/service/timer.js +182 -96
- package/dist/cron/service.js +3 -0
- package/dist/cron/stagger.js +37 -0
- package/dist/daemon/inspect.js +132 -92
- package/dist/daemon/runtime-paths.js +25 -4
- package/dist/daemon/service-audit.js +47 -16
- package/dist/discord/accounts.js +23 -20
- package/dist/discord/monitor/agent-components.js +1115 -219
- package/dist/discord/monitor/allow-list.js +114 -34
- package/dist/discord/monitor/listeners.js +204 -97
- package/dist/discord/monitor/message-handler.js +21 -10
- package/dist/discord/monitor/message-handler.preflight.js +195 -101
- package/dist/discord/monitor/message-handler.process.js +384 -123
- package/dist/discord/monitor/message-utils.js +86 -23
- package/dist/discord/monitor/native-command.js +77 -57
- package/dist/discord/monitor/provider.js +122 -117
- package/dist/discord/monitor/reply-context.js +20 -16
- package/dist/discord/monitor/reply-delivery.js +40 -8
- package/dist/discord/monitor/rest-fetch.js +22 -0
- package/dist/discord/monitor/threading.js +117 -24
- package/dist/discord/send.js +2 -1
- package/dist/discord/send.outbound.js +124 -11
- package/dist/discord/send.shared.js +112 -72
- package/dist/discord/voice-message.js +3 -3
- package/dist/gateway/auth.js +119 -44
- package/dist/gateway/call.js +76 -34
- package/dist/gateway/channel-health-monitor.js +57 -50
- package/dist/gateway/client.js +63 -29
- package/dist/gateway/control-ui-contract.js +1 -1
- package/dist/gateway/gateway-config-prompts.shared.js +2 -2
- package/dist/gateway/net.js +109 -1
- package/dist/gateway/protocol/index.js +5 -8
- package/dist/gateway/protocol/schema/agent.js +19 -1
- package/dist/gateway/protocol/schema/channels.js +21 -0
- package/dist/gateway/protocol/schema/cron.js +43 -30
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
- package/dist/gateway/protocol/schema/sessions.js +5 -1
- package/dist/gateway/protocol/schema.js +0 -1
- package/dist/gateway/server/presence-events.js +12 -0
- package/dist/gateway/server/ws-connection/message-handler.js +203 -212
- package/dist/gateway/server/ws-connection.js +58 -21
- package/dist/gateway/server-broadcast.js +18 -13
- package/dist/gateway/server-cron.js +177 -10
- package/dist/gateway/server-methods/agent-job.js +131 -38
- package/dist/gateway/server-methods/send.js +60 -14
- package/dist/gateway/server-methods/sessions.js +160 -96
- package/dist/gateway/server-methods/system.js +5 -7
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +24 -8
- package/dist/gateway/server-node-events.js +278 -68
- package/dist/gateway/session-utils.fs.js +316 -75
- package/dist/gateway/session-utils.js +224 -70
- package/dist/gateway/sessions-patch.js +63 -20
- package/dist/gateway/test-temp-config.js +1 -1
- package/dist/gateway/tools-invoke-http.js +118 -70
- package/dist/gateway/ws-log.js +135 -107
- package/dist/hooks/frontmatter.js +36 -82
- package/dist/hooks/install.js +149 -139
- package/dist/hooks/internal-hooks.js +29 -4
- package/dist/hooks/plugin-hooks.js +2 -1
- package/dist/imessage/monitor/deliver.js +10 -4
- package/dist/imessage/monitor/monitor-provider.js +138 -375
- package/dist/imessage/monitor/runtime.js +4 -8
- package/dist/imessage/send.js +65 -19
- package/dist/infra/exec-approvals-allowlist.js +7 -0
- package/dist/infra/exec-approvals.js +35 -920
- package/dist/infra/exec-safe-bin-trust.js +64 -0
- package/dist/infra/heartbeat-runner.js +207 -134
- package/dist/infra/heartbeat-wake.js +183 -22
- package/dist/infra/install-source-utils.js +47 -0
- package/dist/infra/net/ssrf.js +170 -36
- package/dist/infra/outbound/deliver.js +224 -58
- package/dist/infra/outbound/message-action-spec.js +12 -5
- package/dist/infra/outbound/outbound-session.js +27 -25
- package/dist/infra/poolbot-root.js +32 -22
- package/dist/infra/ports.js +14 -11
- package/dist/infra/skills-remote.js +48 -37
- package/dist/infra/system-events.js +25 -11
- package/dist/infra/system-presence.js +26 -33
- package/dist/infra/tmp-poolbot-dir.js +81 -2
- package/dist/infra/wsl.js +37 -1
- package/dist/line/bot-message-context.js +163 -191
- package/dist/logging/subsystem.js +59 -22
- package/dist/markdown/ir.js +124 -50
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/runner.entries.js +42 -25
- package/dist/media-understanding/runner.js +53 -488
- package/dist/memory/embeddings-gemini.js +53 -38
- package/dist/memory/manager-embedding-ops.js +48 -69
- package/dist/pairing/pairing-store.js +178 -119
- package/dist/plugin-sdk/index.js +34 -6
- package/dist/plugins/hooks.js +135 -14
- package/dist/plugins/install.js +190 -152
- package/dist/polls.js +11 -0
- package/dist/routing/resolve-route.js +190 -56
- package/dist/routing/session-key.js +38 -22
- package/dist/runtime.js +35 -9
- package/dist/security/audit-channel.js +1 -1
- package/dist/sessions/session-key-utils.js +29 -11
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-types.js +1 -0
- package/dist/shared/string-normalization.js +15 -0
- package/dist/signal/monitor/event-handler.js +68 -36
- package/dist/signal/send.js +29 -37
- package/dist/slack/monitor/allow-list.js +10 -11
- package/dist/slack/monitor/commands.js +14 -3
- package/dist/slack/monitor/events/interactions.js +4 -4
- package/dist/slack/monitor/media.js +224 -16
- package/dist/slack/monitor/message-handler/dispatch.js +247 -13
- package/dist/slack/monitor/message-handler/prepare.js +128 -45
- package/dist/slack/monitor/slash.js +357 -144
- package/dist/slack/streaming.js +77 -0
- package/dist/telegram/accounts.js +40 -13
- package/dist/telegram/allowed-updates.js +3 -0
- package/dist/telegram/bot/delivery.js +129 -66
- package/dist/telegram/bot/helpers.js +136 -122
- package/dist/telegram/bot-handlers.js +600 -339
- package/dist/telegram/bot-message-context.js +115 -73
- package/dist/telegram/bot-message-dispatch.js +235 -104
- package/dist/telegram/bot-native-command-menu.js +3 -1
- package/dist/telegram/bot-native-commands.js +213 -193
- package/dist/telegram/bot.js +24 -132
- package/dist/telegram/draft-stream.js +84 -75
- package/dist/telegram/format.js +150 -6
- package/dist/telegram/send.js +415 -255
- package/dist/telegram/targets.js +21 -2
- package/dist/telegram/update-offset-store.js +19 -3
- package/dist/terminal/restore.js +5 -2
- package/dist/test-utils/fetch-mock.js +5 -0
- package/dist/version.js +18 -5
- package/dist/web/auto-reply/monitor/broadcast.js +7 -3
- package/dist/web/auto-reply/monitor/on-message.js +6 -3
- package/dist/web/inbound/media.js +34 -8
- package/dist/web/inbound/monitor.js +34 -17
- package/dist/web/inbound/send-api.js +18 -17
- package/dist/web/outbound.js +12 -5
- package/dist/wizard/clack-prompter.js +40 -7
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/device-pair/index.ts +2 -2
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/irc/src/accounts.ts +1 -1
- package/extensions/irc/src/onboarding.ts +4 -4
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +10 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +10 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +10 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +10 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +10 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +10 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +10 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/skills/apple-reminders/SKILL.md +100 -49
- package/skills/coding-agent/SKILL.md +34 -28
- package/skills/github/SKILL.md +131 -16
- package/skills/imsg/SKILL.md +112 -15
- package/skills/openhue/SKILL.md +101 -19
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
- package/dist/agents/openclaw-tools.js +0 -151
- package/dist/agents/tool-security.js +0 -96
- package/dist/gateway/url-validation.js +0 -94
- package/dist/infra/openclaw-root.js +0 -109
- package/dist/infra/tmp-openclaw-dir.js +0 -81
- package/dist/media/path-sanitization.js +0 -78
package/dist/telegram/send.js
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
import { Bot, HttpError, InputFile } from "grammy";
|
|
2
2
|
import { loadConfig } from "../config/config.js";
|
|
3
|
+
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
|
3
4
|
import { logVerbose } from "../globals.js";
|
|
4
5
|
import { recordChannelActivity } from "../infra/channel-activity.js";
|
|
5
|
-
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
|
6
|
-
import { formatErrorMessage, formatUncaughtError } from "../infra/errors.js";
|
|
7
6
|
import { isDiagnosticFlagEnabled } from "../infra/diagnostic-flags.js";
|
|
7
|
+
import { formatErrorMessage, formatUncaughtError } from "../infra/errors.js";
|
|
8
8
|
import { createTelegramRetryRunner } from "../infra/retry-policy.js";
|
|
9
9
|
import { redactSensitiveText } from "../logging/redact.js";
|
|
10
10
|
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
11
11
|
import { mediaKindFromMime } from "../media/constants.js";
|
|
12
12
|
import { isGifMedia } from "../media/mime.js";
|
|
13
|
+
import { normalizePollInput } from "../polls.js";
|
|
13
14
|
import { loadWebMedia } from "../web/media.js";
|
|
14
15
|
import { resolveTelegramAccount } from "./accounts.js";
|
|
16
|
+
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
|
17
|
+
import { buildTelegramThreadParams } from "./bot/helpers.js";
|
|
18
|
+
import { splitTelegramCaption } from "./caption.js";
|
|
15
19
|
import { resolveTelegramFetch } from "./fetch.js";
|
|
16
|
-
import { makeProxyFetch } from "./proxy.js";
|
|
17
20
|
import { renderTelegramHtmlText } from "./format.js";
|
|
18
|
-
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
|
19
21
|
import { isRecoverableTelegramNetworkError } from "./network-errors.js";
|
|
20
|
-
import {
|
|
22
|
+
import { makeProxyFetch } from "./proxy.js";
|
|
21
23
|
import { recordSentMessage } from "./sent-message-cache.js";
|
|
22
24
|
import { parseTelegramTarget, stripTelegramInternalPrefixes } from "./targets.js";
|
|
23
25
|
import { resolveTelegramVoiceSend } from "./voice.js";
|
|
24
|
-
import { buildTelegramThreadParams } from "./bot/helpers.js";
|
|
25
26
|
const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
|
|
26
27
|
const THREAD_NOT_FOUND_RE = /400:\s*Bad Request:\s*message thread not found/i;
|
|
28
|
+
const MESSAGE_NOT_MODIFIED_RE = /400:\s*Bad Request:\s*message is not modified|MESSAGE_NOT_MODIFIED/i;
|
|
29
|
+
const CHAT_NOT_FOUND_RE = /400: Bad Request: chat not found/i;
|
|
27
30
|
const diagLogger = createSubsystemLogger("telegram/diagnostic");
|
|
28
31
|
function createTelegramHttpLogger(cfg) {
|
|
29
32
|
const enabled = isDiagnosticFlagEnabled("telegram.http", cfg);
|
|
@@ -31,8 +34,9 @@ function createTelegramHttpLogger(cfg) {
|
|
|
31
34
|
return () => { };
|
|
32
35
|
}
|
|
33
36
|
return (label, err) => {
|
|
34
|
-
if (!(err instanceof HttpError))
|
|
37
|
+
if (!(err instanceof HttpError)) {
|
|
35
38
|
return;
|
|
39
|
+
}
|
|
36
40
|
const detail = redactSensitiveText(formatUncaughtError(err.error ?? err));
|
|
37
41
|
diagLogger.warn(`telegram http error (${label}): ${detail}`);
|
|
38
42
|
};
|
|
@@ -55,8 +59,9 @@ function resolveTelegramClientOptions(account) {
|
|
|
55
59
|
: undefined;
|
|
56
60
|
}
|
|
57
61
|
function resolveToken(explicit, params) {
|
|
58
|
-
if (explicit?.trim())
|
|
62
|
+
if (explicit?.trim()) {
|
|
59
63
|
return explicit.trim();
|
|
64
|
+
}
|
|
60
65
|
if (!params.token) {
|
|
61
66
|
throw new Error(`Telegram bot token missing for account "${params.accountId}" (set channels.telegram.accounts.${params.accountId}.botToken/tokenFile or TELEGRAM_BOT_TOKEN for default).`);
|
|
62
67
|
}
|
|
@@ -64,8 +69,9 @@ function resolveToken(explicit, params) {
|
|
|
64
69
|
}
|
|
65
70
|
function normalizeChatId(to) {
|
|
66
71
|
const trimmed = to.trim();
|
|
67
|
-
if (!trimmed)
|
|
72
|
+
if (!trimmed) {
|
|
68
73
|
throw new Error("Recipient is required for Telegram sends");
|
|
74
|
+
}
|
|
69
75
|
// Common internal prefixes that sometimes leak into outbound sends.
|
|
70
76
|
// - ctx.To uses `telegram:<id>`
|
|
71
77
|
// - group sessions often use `telegram:group:<id>`
|
|
@@ -74,17 +80,22 @@ function normalizeChatId(to) {
|
|
|
74
80
|
// (Invite links like `t.me/+...` are not resolvable via Bot API.)
|
|
75
81
|
const m = /^https?:\/\/t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized) ??
|
|
76
82
|
/^t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized);
|
|
77
|
-
if (m?.[1])
|
|
83
|
+
if (m?.[1]) {
|
|
78
84
|
normalized = `@${m[1]}`;
|
|
79
|
-
|
|
85
|
+
}
|
|
86
|
+
if (!normalized) {
|
|
80
87
|
throw new Error("Recipient is required for Telegram sends");
|
|
81
|
-
|
|
88
|
+
}
|
|
89
|
+
if (normalized.startsWith("@")) {
|
|
82
90
|
return normalized;
|
|
83
|
-
|
|
91
|
+
}
|
|
92
|
+
if (/^-?\d+$/.test(normalized)) {
|
|
84
93
|
return normalized;
|
|
94
|
+
}
|
|
85
95
|
// If the user passed a username without `@`, assume they meant a public chat/channel.
|
|
86
|
-
if (/^[A-Za-z0-9_]{5,}$/i.test(normalized))
|
|
96
|
+
if (/^[A-Za-z0-9_]{5,}$/i.test(normalized)) {
|
|
87
97
|
return `@${normalized}`;
|
|
98
|
+
}
|
|
88
99
|
return normalized;
|
|
89
100
|
}
|
|
90
101
|
function normalizeMessageId(raw) {
|
|
@@ -97,117 +108,192 @@ function normalizeMessageId(raw) {
|
|
|
97
108
|
throw new Error("Message id is required for Telegram actions");
|
|
98
109
|
}
|
|
99
110
|
const parsed = Number.parseInt(value, 10);
|
|
100
|
-
if (Number.isFinite(parsed))
|
|
111
|
+
if (Number.isFinite(parsed)) {
|
|
101
112
|
return parsed;
|
|
113
|
+
}
|
|
102
114
|
}
|
|
103
115
|
throw new Error("Message id is required for Telegram actions");
|
|
104
116
|
}
|
|
105
117
|
function isTelegramThreadNotFoundError(err) {
|
|
106
118
|
return THREAD_NOT_FOUND_RE.test(formatErrorMessage(err));
|
|
107
119
|
}
|
|
120
|
+
function isTelegramMessageNotModifiedError(err) {
|
|
121
|
+
return MESSAGE_NOT_MODIFIED_RE.test(formatErrorMessage(err));
|
|
122
|
+
}
|
|
108
123
|
function hasMessageThreadIdParam(params) {
|
|
109
|
-
if (!params)
|
|
124
|
+
if (!params) {
|
|
110
125
|
return false;
|
|
126
|
+
}
|
|
111
127
|
const value = params.message_thread_id;
|
|
112
|
-
if (typeof value === "number")
|
|
128
|
+
if (typeof value === "number") {
|
|
113
129
|
return Number.isFinite(value);
|
|
114
|
-
|
|
130
|
+
}
|
|
131
|
+
if (typeof value === "string") {
|
|
115
132
|
return value.trim().length > 0;
|
|
133
|
+
}
|
|
116
134
|
return false;
|
|
117
135
|
}
|
|
118
136
|
function removeMessageThreadIdParam(params) {
|
|
119
|
-
if (!params || !hasMessageThreadIdParam(params))
|
|
137
|
+
if (!params || !hasMessageThreadIdParam(params)) {
|
|
120
138
|
return params;
|
|
139
|
+
}
|
|
121
140
|
const next = { ...params };
|
|
122
141
|
delete next.message_thread_id;
|
|
123
142
|
return Object.keys(next).length > 0 ? next : undefined;
|
|
124
143
|
}
|
|
144
|
+
function isTelegramHtmlParseError(err) {
|
|
145
|
+
return PARSE_ERR_RE.test(formatErrorMessage(err));
|
|
146
|
+
}
|
|
147
|
+
function buildTelegramThreadReplyParams(params) {
|
|
148
|
+
const messageThreadId = params.messageThreadId != null ? params.messageThreadId : params.targetMessageThreadId;
|
|
149
|
+
const threadScope = params.chatType === "direct" ? "dm" : "forum";
|
|
150
|
+
// Never blanket-strip DM message_thread_id by chat-id sign.
|
|
151
|
+
// Telegram supports DM topics; stripping silently misroutes topic replies.
|
|
152
|
+
// Keep thread id and rely on thread-not-found retry fallback for plain DMs.
|
|
153
|
+
const threadSpec = messageThreadId != null ? { id: messageThreadId, scope: threadScope } : undefined;
|
|
154
|
+
const threadIdParams = buildTelegramThreadParams(threadSpec);
|
|
155
|
+
const threadParams = threadIdParams ? { ...threadIdParams } : {};
|
|
156
|
+
if (params.replyToMessageId != null) {
|
|
157
|
+
const replyToMessageId = Math.trunc(params.replyToMessageId);
|
|
158
|
+
if (params.quoteText?.trim()) {
|
|
159
|
+
threadParams.reply_parameters = {
|
|
160
|
+
message_id: replyToMessageId,
|
|
161
|
+
quote: params.quoteText.trim(),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
threadParams.reply_to_message_id = replyToMessageId;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return threadParams;
|
|
169
|
+
}
|
|
170
|
+
async function withTelegramHtmlParseFallback(params) {
|
|
171
|
+
try {
|
|
172
|
+
return await params.requestHtml(params.label);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (!isTelegramHtmlParseError(err)) {
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
if (params.verbose) {
|
|
179
|
+
console.warn(`telegram ${params.label} failed with HTML parse error, retrying as plain text: ${formatErrorMessage(err)}`);
|
|
180
|
+
}
|
|
181
|
+
return await params.requestPlain(`${params.label}-plain`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function resolveTelegramApiContext(opts) {
|
|
185
|
+
const cfg = opts.cfg ?? loadConfig();
|
|
186
|
+
const account = resolveTelegramAccount({
|
|
187
|
+
cfg,
|
|
188
|
+
accountId: opts.accountId,
|
|
189
|
+
});
|
|
190
|
+
const token = resolveToken(opts.token, account);
|
|
191
|
+
const client = resolveTelegramClientOptions(account);
|
|
192
|
+
const api = (opts.api ?? new Bot(token, client ? { client } : undefined).api);
|
|
193
|
+
return { cfg, account, api };
|
|
194
|
+
}
|
|
195
|
+
function createTelegramRequestWithDiag(params) {
|
|
196
|
+
const request = createTelegramRetryRunner({
|
|
197
|
+
retry: params.retry,
|
|
198
|
+
configRetry: params.account.config.retry,
|
|
199
|
+
verbose: params.verbose,
|
|
200
|
+
...(params.shouldRetry ? { shouldRetry: params.shouldRetry } : {}),
|
|
201
|
+
});
|
|
202
|
+
const logHttpError = createTelegramHttpLogger(params.cfg);
|
|
203
|
+
return (fn, label, options) => {
|
|
204
|
+
const runRequest = () => request(fn, label);
|
|
205
|
+
const call = params.useApiErrorLogging === false
|
|
206
|
+
? runRequest()
|
|
207
|
+
: withTelegramApiErrorLogging({
|
|
208
|
+
operation: label ?? "request",
|
|
209
|
+
fn: runRequest,
|
|
210
|
+
...(options?.shouldLog ? { shouldLog: options.shouldLog } : {}),
|
|
211
|
+
});
|
|
212
|
+
return call.catch((err) => {
|
|
213
|
+
logHttpError(label ?? "request", err);
|
|
214
|
+
throw err;
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function wrapTelegramChatNotFoundError(err, params) {
|
|
219
|
+
if (!CHAT_NOT_FOUND_RE.test(formatErrorMessage(err))) {
|
|
220
|
+
return err;
|
|
221
|
+
}
|
|
222
|
+
return new Error([
|
|
223
|
+
`Telegram send failed: chat not found (chat_id=${params.chatId}).`,
|
|
224
|
+
"Likely: bot not started in DM, bot removed from group/channel, group migrated (new -100… id), or wrong bot token.",
|
|
225
|
+
`Input was: ${JSON.stringify(params.input)}.`,
|
|
226
|
+
].join(" "));
|
|
227
|
+
}
|
|
228
|
+
async function withTelegramThreadFallback(params, label, verbose, attempt) {
|
|
229
|
+
try {
|
|
230
|
+
return await attempt(params, label);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
// Do not widen this fallback to cover "chat not found".
|
|
234
|
+
// chat-not-found is routing/auth/membership/token; stripping thread IDs hides root cause.
|
|
235
|
+
if (!hasMessageThreadIdParam(params) || !isTelegramThreadNotFoundError(err)) {
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
if (verbose) {
|
|
239
|
+
console.warn(`telegram ${label} failed with message_thread_id, retrying without thread: ${formatErrorMessage(err)}`);
|
|
240
|
+
}
|
|
241
|
+
const retriedParams = removeMessageThreadIdParam(params);
|
|
242
|
+
return await attempt(retriedParams, `${label}-threadless`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function createRequestWithChatNotFound(params) {
|
|
246
|
+
return async (fn, label) => params.requestWithDiag(fn, label).catch((err) => {
|
|
247
|
+
throw wrapTelegramChatNotFoundError(err, {
|
|
248
|
+
chatId: params.chatId,
|
|
249
|
+
input: params.input,
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
}
|
|
125
253
|
export function buildInlineKeyboard(buttons) {
|
|
126
|
-
if (!buttons?.length)
|
|
254
|
+
if (!buttons?.length) {
|
|
127
255
|
return undefined;
|
|
256
|
+
}
|
|
128
257
|
const rows = buttons
|
|
129
258
|
.map((row) => row
|
|
130
259
|
.filter((button) => button?.text && button?.callback_data)
|
|
131
260
|
.map((button) => ({
|
|
132
261
|
text: button.text,
|
|
133
262
|
callback_data: button.callback_data,
|
|
263
|
+
...(button.style ? { style: button.style } : {}),
|
|
134
264
|
})))
|
|
135
265
|
.filter((row) => row.length > 0);
|
|
136
|
-
if (rows.length === 0)
|
|
266
|
+
if (rows.length === 0) {
|
|
137
267
|
return undefined;
|
|
268
|
+
}
|
|
138
269
|
return { inline_keyboard: rows };
|
|
139
270
|
}
|
|
140
271
|
export async function sendMessageTelegram(to, text, opts = {}) {
|
|
141
|
-
const cfg =
|
|
142
|
-
const account = resolveTelegramAccount({
|
|
143
|
-
cfg,
|
|
144
|
-
accountId: opts.accountId,
|
|
145
|
-
});
|
|
146
|
-
const token = resolveToken(opts.token, account);
|
|
272
|
+
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
|
147
273
|
const target = parseTelegramTarget(to);
|
|
148
274
|
const chatId = normalizeChatId(target.chatId);
|
|
149
|
-
// Use provided api or create a new Bot instance. The nullish coalescing
|
|
150
|
-
// operator ensures api is always defined (Bot.api is always non-null).
|
|
151
|
-
const client = resolveTelegramClientOptions(account);
|
|
152
|
-
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
|
|
153
275
|
const mediaUrl = opts.mediaUrl?.trim();
|
|
154
276
|
const replyMarkup = buildInlineKeyboard(opts.buttons);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (quoteText) {
|
|
163
|
-
threadParams.reply_parameters = {
|
|
164
|
-
message_id: Math.trunc(opts.replyToMessageId),
|
|
165
|
-
quote: quoteText,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
threadParams.reply_to_message_id = Math.trunc(opts.replyToMessageId);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
277
|
+
const threadParams = buildTelegramThreadReplyParams({
|
|
278
|
+
targetMessageThreadId: target.messageThreadId,
|
|
279
|
+
messageThreadId: opts.messageThreadId,
|
|
280
|
+
chatType: target.chatType,
|
|
281
|
+
replyToMessageId: opts.replyToMessageId,
|
|
282
|
+
quoteText: opts.quoteText,
|
|
283
|
+
});
|
|
172
284
|
const hasThreadParams = Object.keys(threadParams).length > 0;
|
|
173
|
-
const
|
|
285
|
+
const requestWithDiag = createTelegramRequestWithDiag({
|
|
286
|
+
cfg,
|
|
287
|
+
account,
|
|
174
288
|
retry: opts.retry,
|
|
175
|
-
configRetry: account.config.retry,
|
|
176
289
|
verbose: opts.verbose,
|
|
177
290
|
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
|
178
291
|
});
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}).catch((err) => {
|
|
184
|
-
logHttpError(label ?? "request", err);
|
|
185
|
-
throw err;
|
|
292
|
+
const requestWithChatNotFound = createRequestWithChatNotFound({
|
|
293
|
+
requestWithDiag,
|
|
294
|
+
chatId,
|
|
295
|
+
input: to,
|
|
186
296
|
});
|
|
187
|
-
const wrapChatNotFound = (err) => {
|
|
188
|
-
if (!/400: Bad Request: chat not found/i.test(formatErrorMessage(err)))
|
|
189
|
-
return err;
|
|
190
|
-
return new Error([
|
|
191
|
-
`Telegram send failed: chat not found (chat_id=${chatId}).`,
|
|
192
|
-
"Likely: bot not started in DM, bot removed from group/channel, group migrated (new -100… id), or wrong bot token.",
|
|
193
|
-
`Input was: ${JSON.stringify(to)}.`,
|
|
194
|
-
].join(" "));
|
|
195
|
-
};
|
|
196
|
-
const sendWithThreadFallback = async (params, label, attempt) => {
|
|
197
|
-
try {
|
|
198
|
-
return await attempt(params, label);
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
if (!hasMessageThreadIdParam(params) || !isTelegramThreadNotFoundError(err)) {
|
|
202
|
-
throw err;
|
|
203
|
-
}
|
|
204
|
-
if (opts.verbose) {
|
|
205
|
-
console.warn(`telegram ${label} failed with message_thread_id, retrying without thread: ${formatErrorMessage(err)}`);
|
|
206
|
-
}
|
|
207
|
-
const retriedParams = removeMessageThreadIdParam(params);
|
|
208
|
-
return await attempt(retriedParams, `${label}-threadless`);
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
297
|
const textMode = opts.textMode ?? "markdown";
|
|
212
298
|
const tableMode = resolveMarkdownTableMode({
|
|
213
299
|
cfg,
|
|
@@ -219,7 +305,7 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
219
305
|
const linkPreviewEnabled = account.config.linkPreview ?? true;
|
|
220
306
|
const linkPreviewOptions = linkPreviewEnabled ? undefined : { is_disabled: true };
|
|
221
307
|
const sendTelegramText = async (rawText, params, fallbackText) => {
|
|
222
|
-
return await
|
|
308
|
+
return await withTelegramThreadFallback(params, "message", opts.verbose, async (effectiveParams, label) => {
|
|
223
309
|
const htmlText = renderHtmlText(rawText);
|
|
224
310
|
const baseParams = effectiveParams ? { ...effectiveParams } : {};
|
|
225
311
|
if (linkPreviewOptions) {
|
|
@@ -231,31 +317,26 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
231
317
|
...baseParams,
|
|
232
318
|
...(opts.silent === true ? { disable_notification: true } : {}),
|
|
233
319
|
};
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (opts.verbose) {
|
|
240
|
-
console.warn(`telegram HTML parse failed, retrying as plain text: ${errText}`);
|
|
241
|
-
}
|
|
242
|
-
const fallback = fallbackText ?? rawText;
|
|
320
|
+
return await withTelegramHtmlParseFallback({
|
|
321
|
+
label,
|
|
322
|
+
verbose: opts.verbose,
|
|
323
|
+
requestHtml: (retryLabel) => requestWithChatNotFound(() => api.sendMessage(chatId, htmlText, sendParams), retryLabel),
|
|
324
|
+
requestPlain: (retryLabel) => {
|
|
243
325
|
const plainParams = hasBaseParams
|
|
244
326
|
? baseParams
|
|
245
327
|
: undefined;
|
|
246
|
-
return
|
|
247
|
-
? api.sendMessage(chatId,
|
|
248
|
-
: api.sendMessage(chatId,
|
|
249
|
-
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
throw wrapChatNotFound(err);
|
|
328
|
+
return requestWithChatNotFound(() => plainParams
|
|
329
|
+
? api.sendMessage(chatId, fallbackText ?? rawText, plainParams)
|
|
330
|
+
: api.sendMessage(chatId, fallbackText ?? rawText), retryLabel);
|
|
331
|
+
},
|
|
253
332
|
});
|
|
254
|
-
return res;
|
|
255
333
|
});
|
|
256
334
|
};
|
|
257
335
|
if (mediaUrl) {
|
|
258
|
-
const media = await loadWebMedia(mediaUrl,
|
|
336
|
+
const media = await loadWebMedia(mediaUrl, {
|
|
337
|
+
maxBytes: opts.maxBytes,
|
|
338
|
+
localRoots: opts.mediaLocalRoots,
|
|
339
|
+
});
|
|
259
340
|
const kind = mediaKindFromMime(media.contentType ?? undefined);
|
|
260
341
|
const isGif = isGifMedia({
|
|
261
342
|
contentType: media.contentType,
|
|
@@ -267,7 +348,6 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
267
348
|
let caption;
|
|
268
349
|
let followUpText;
|
|
269
350
|
if (isVideoNote) {
|
|
270
|
-
// Video notes don't support captions; send any text as follow-up.
|
|
271
351
|
caption = undefined;
|
|
272
352
|
followUpText = text.trim() ? text : undefined;
|
|
273
353
|
}
|
|
@@ -291,52 +371,56 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
291
371
|
...baseMediaParams,
|
|
292
372
|
...(opts.silent === true ? { disable_notification: true } : {}),
|
|
293
373
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
result = await sendWithThreadFallback(mediaParams, "photo", async (effectiveParams, label) => requestWithDiag(() => api.sendPhoto(chatId, file, effectiveParams), label).catch((err) => {
|
|
302
|
-
throw wrapChatNotFound(err);
|
|
303
|
-
}));
|
|
304
|
-
}
|
|
305
|
-
else if (kind === "video") {
|
|
306
|
-
if (isVideoNote) {
|
|
307
|
-
result = await sendWithThreadFallback(mediaParams, "video_note", async (effectiveParams, label) => requestWithDiag(() => api.sendVideoNote(chatId, file, effectiveParams), label).catch((err) => {
|
|
308
|
-
throw wrapChatNotFound(err);
|
|
309
|
-
}));
|
|
374
|
+
const sendMedia = async (label, sender) => await withTelegramThreadFallback(mediaParams, label, opts.verbose, async (effectiveParams, retryLabel) => requestWithChatNotFound(() => sender(effectiveParams), retryLabel));
|
|
375
|
+
const mediaSender = (() => {
|
|
376
|
+
if (isGif) {
|
|
377
|
+
return {
|
|
378
|
+
label: "animation",
|
|
379
|
+
sender: (effectiveParams) => api.sendAnimation(chatId, file, effectiveParams),
|
|
380
|
+
};
|
|
310
381
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
382
|
+
if (kind === "image") {
|
|
383
|
+
return {
|
|
384
|
+
label: "photo",
|
|
385
|
+
sender: (effectiveParams) => api.sendPhoto(chatId, file, effectiveParams),
|
|
386
|
+
};
|
|
315
387
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}));
|
|
388
|
+
if (kind === "video") {
|
|
389
|
+
if (isVideoNote) {
|
|
390
|
+
return {
|
|
391
|
+
label: "video_note",
|
|
392
|
+
sender: (effectiveParams) => api.sendVideoNote(chatId, file, effectiveParams),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
label: "video",
|
|
397
|
+
sender: (effectiveParams) => api.sendVideo(chatId, file, effectiveParams),
|
|
398
|
+
};
|
|
328
399
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
400
|
+
if (kind === "audio") {
|
|
401
|
+
const { useVoice } = resolveTelegramVoiceSend({
|
|
402
|
+
wantsVoice: opts.asVoice === true, // default false (backward compatible)
|
|
403
|
+
contentType: media.contentType,
|
|
404
|
+
fileName,
|
|
405
|
+
logFallback: logVerbose,
|
|
406
|
+
});
|
|
407
|
+
if (useVoice) {
|
|
408
|
+
return {
|
|
409
|
+
label: "voice",
|
|
410
|
+
sender: (effectiveParams) => api.sendVoice(chatId, file, effectiveParams),
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
label: "audio",
|
|
415
|
+
sender: (effectiveParams) => api.sendAudio(chatId, file, effectiveParams),
|
|
416
|
+
};
|
|
333
417
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
418
|
+
return {
|
|
419
|
+
label: "document",
|
|
420
|
+
sender: (effectiveParams) => api.sendDocument(chatId, file, effectiveParams),
|
|
421
|
+
};
|
|
422
|
+
})();
|
|
423
|
+
const result = await sendMedia(mediaSender.label, mediaSender.sender);
|
|
340
424
|
const mediaMessageId = String(result?.message_id ?? "unknown");
|
|
341
425
|
const resolvedChatId = String(result?.chat?.id ?? chatId);
|
|
342
426
|
if (result?.message_id) {
|
|
@@ -387,30 +471,16 @@ export async function sendMessageTelegram(to, text, opts = {}) {
|
|
|
387
471
|
return { messageId, chatId: String(res?.chat?.id ?? chatId) };
|
|
388
472
|
}
|
|
389
473
|
export async function reactMessageTelegram(chatIdInput, messageIdInput, emoji, opts = {}) {
|
|
390
|
-
const cfg =
|
|
391
|
-
const account = resolveTelegramAccount({
|
|
392
|
-
cfg,
|
|
393
|
-
accountId: opts.accountId,
|
|
394
|
-
});
|
|
395
|
-
const token = resolveToken(opts.token, account);
|
|
474
|
+
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
|
396
475
|
const chatId = normalizeChatId(String(chatIdInput));
|
|
397
476
|
const messageId = normalizeMessageId(messageIdInput);
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
477
|
+
const requestWithDiag = createTelegramRequestWithDiag({
|
|
478
|
+
cfg,
|
|
479
|
+
account,
|
|
401
480
|
retry: opts.retry,
|
|
402
|
-
configRetry: account.config.retry,
|
|
403
481
|
verbose: opts.verbose,
|
|
404
482
|
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
|
405
483
|
});
|
|
406
|
-
const logHttpError = createTelegramHttpLogger(cfg);
|
|
407
|
-
const requestWithDiag = (fn, label) => withTelegramApiErrorLogging({
|
|
408
|
-
operation: label ?? "request",
|
|
409
|
-
fn: () => request(fn, label),
|
|
410
|
-
}).catch((err) => {
|
|
411
|
-
logHttpError(label ?? "request", err);
|
|
412
|
-
throw err;
|
|
413
|
-
});
|
|
414
484
|
const remove = opts.remove === true;
|
|
415
485
|
const trimmedEmoji = emoji.trim();
|
|
416
486
|
// Build the reaction array. We cast emoji to the grammY union type since
|
|
@@ -421,62 +491,47 @@ export async function reactMessageTelegram(chatIdInput, messageIdInput, emoji, o
|
|
|
421
491
|
if (typeof api.setMessageReaction !== "function") {
|
|
422
492
|
throw new Error("Telegram reactions are unavailable in this bot API.");
|
|
423
493
|
}
|
|
424
|
-
|
|
494
|
+
try {
|
|
495
|
+
await requestWithDiag(() => api.setMessageReaction(chatId, messageId, reactions), "reaction");
|
|
496
|
+
}
|
|
497
|
+
catch (err) {
|
|
498
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
499
|
+
if (/REACTION_INVALID/i.test(msg)) {
|
|
500
|
+
return { ok: false, warning: `Reaction unavailable: ${trimmedEmoji}` };
|
|
501
|
+
}
|
|
502
|
+
throw err;
|
|
503
|
+
}
|
|
425
504
|
return { ok: true };
|
|
426
505
|
}
|
|
427
506
|
export async function deleteMessageTelegram(chatIdInput, messageIdInput, opts = {}) {
|
|
428
|
-
const cfg =
|
|
429
|
-
const account = resolveTelegramAccount({
|
|
430
|
-
cfg,
|
|
431
|
-
accountId: opts.accountId,
|
|
432
|
-
});
|
|
433
|
-
const token = resolveToken(opts.token, account);
|
|
507
|
+
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
|
434
508
|
const chatId = normalizeChatId(String(chatIdInput));
|
|
435
509
|
const messageId = normalizeMessageId(messageIdInput);
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
510
|
+
const requestWithDiag = createTelegramRequestWithDiag({
|
|
511
|
+
cfg,
|
|
512
|
+
account,
|
|
439
513
|
retry: opts.retry,
|
|
440
|
-
configRetry: account.config.retry,
|
|
441
514
|
verbose: opts.verbose,
|
|
442
515
|
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
|
443
516
|
});
|
|
444
|
-
const logHttpError = createTelegramHttpLogger(cfg);
|
|
445
|
-
const requestWithDiag = (fn, label) => withTelegramApiErrorLogging({
|
|
446
|
-
operation: label ?? "request",
|
|
447
|
-
fn: () => request(fn, label),
|
|
448
|
-
}).catch((err) => {
|
|
449
|
-
logHttpError(label ?? "request", err);
|
|
450
|
-
throw err;
|
|
451
|
-
});
|
|
452
517
|
await requestWithDiag(() => api.deleteMessage(chatId, messageId), "deleteMessage");
|
|
453
518
|
logVerbose(`[telegram] Deleted message ${messageId} from chat ${chatId}`);
|
|
454
519
|
return { ok: true };
|
|
455
520
|
}
|
|
456
521
|
export async function editMessageTelegram(chatIdInput, messageIdInput, text, opts = {}) {
|
|
457
|
-
const cfg
|
|
458
|
-
|
|
459
|
-
cfg,
|
|
460
|
-
accountId: opts.accountId,
|
|
522
|
+
const { cfg, account, api } = resolveTelegramApiContext({
|
|
523
|
+
...opts,
|
|
524
|
+
cfg: opts.cfg,
|
|
461
525
|
});
|
|
462
|
-
const token = resolveToken(opts.token, account);
|
|
463
526
|
const chatId = normalizeChatId(String(chatIdInput));
|
|
464
527
|
const messageId = normalizeMessageId(messageIdInput);
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
528
|
+
const requestWithDiag = createTelegramRequestWithDiag({
|
|
529
|
+
cfg,
|
|
530
|
+
account,
|
|
468
531
|
retry: opts.retry,
|
|
469
|
-
configRetry: account.config.retry,
|
|
470
532
|
verbose: opts.verbose,
|
|
471
533
|
});
|
|
472
|
-
const
|
|
473
|
-
const requestWithDiag = (fn, label) => withTelegramApiErrorLogging({
|
|
474
|
-
operation: label ?? "request",
|
|
475
|
-
fn: () => request(fn, label),
|
|
476
|
-
}).catch((err) => {
|
|
477
|
-
logHttpError(label ?? "request", err);
|
|
478
|
-
throw err;
|
|
479
|
-
});
|
|
534
|
+
const requestWithEditShouldLog = (fn, label, shouldLog) => requestWithDiag(fn, label, shouldLog ? { shouldLog } : undefined);
|
|
480
535
|
const textMode = opts.textMode ?? "markdown";
|
|
481
536
|
const tableMode = resolveMarkdownTableMode({
|
|
482
537
|
cfg,
|
|
@@ -494,26 +549,37 @@ export async function editMessageTelegram(chatIdInput, messageIdInput, text, opt
|
|
|
494
549
|
const editParams = {
|
|
495
550
|
parse_mode: "HTML",
|
|
496
551
|
};
|
|
552
|
+
if (opts.linkPreview === false) {
|
|
553
|
+
editParams.link_preview_options = { is_disabled: true };
|
|
554
|
+
}
|
|
497
555
|
if (replyMarkup !== undefined) {
|
|
498
556
|
editParams.reply_markup = replyMarkup;
|
|
499
557
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
558
|
+
const plainParams = {};
|
|
559
|
+
if (opts.linkPreview === false) {
|
|
560
|
+
plainParams.link_preview_options = { is_disabled: true };
|
|
561
|
+
}
|
|
562
|
+
if (replyMarkup !== undefined) {
|
|
563
|
+
plainParams.reply_markup = replyMarkup;
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
await withTelegramHtmlParseFallback({
|
|
567
|
+
label: "editMessage",
|
|
568
|
+
verbose: opts.verbose,
|
|
569
|
+
requestHtml: (retryLabel) => requestWithEditShouldLog(() => api.editMessageText(chatId, messageId, htmlText, editParams), retryLabel, (err) => !isTelegramMessageNotModifiedError(err)),
|
|
570
|
+
requestPlain: (retryLabel) => requestWithEditShouldLog(() => Object.keys(plainParams).length > 0
|
|
512
571
|
? api.editMessageText(chatId, messageId, text, plainParams)
|
|
513
|
-
: api.editMessageText(chatId, messageId, text),
|
|
572
|
+
: api.editMessageText(chatId, messageId, text), retryLabel, (plainErr) => !isTelegramMessageNotModifiedError(plainErr)),
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
if (isTelegramMessageNotModifiedError(err)) {
|
|
577
|
+
// no-op: Telegram reports message content unchanged, treat as success
|
|
514
578
|
}
|
|
515
|
-
|
|
516
|
-
|
|
579
|
+
else {
|
|
580
|
+
throw err;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
517
583
|
logVerbose(`[telegram] Edited message ${messageId} in chat ${chatId}`);
|
|
518
584
|
return { ok: true, messageId: String(messageId), chatId };
|
|
519
585
|
}
|
|
@@ -539,70 +605,164 @@ export async function sendStickerTelegram(to, fileId, opts = {}) {
|
|
|
539
605
|
if (!fileId?.trim()) {
|
|
540
606
|
throw new Error("Telegram sticker file_id is required");
|
|
541
607
|
}
|
|
608
|
+
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
|
609
|
+
const target = parseTelegramTarget(to);
|
|
610
|
+
const chatId = normalizeChatId(target.chatId);
|
|
611
|
+
const threadParams = buildTelegramThreadReplyParams({
|
|
612
|
+
targetMessageThreadId: target.messageThreadId,
|
|
613
|
+
messageThreadId: opts.messageThreadId,
|
|
614
|
+
chatType: target.chatType,
|
|
615
|
+
replyToMessageId: opts.replyToMessageId,
|
|
616
|
+
});
|
|
617
|
+
const hasThreadParams = Object.keys(threadParams).length > 0;
|
|
618
|
+
const requestWithDiag = createTelegramRequestWithDiag({
|
|
619
|
+
cfg,
|
|
620
|
+
account,
|
|
621
|
+
retry: opts.retry,
|
|
622
|
+
verbose: opts.verbose,
|
|
623
|
+
useApiErrorLogging: false,
|
|
624
|
+
});
|
|
625
|
+
const requestWithChatNotFound = createRequestWithChatNotFound({
|
|
626
|
+
requestWithDiag,
|
|
627
|
+
chatId,
|
|
628
|
+
input: to,
|
|
629
|
+
});
|
|
630
|
+
const stickerParams = hasThreadParams ? threadParams : undefined;
|
|
631
|
+
const result = await withTelegramThreadFallback(stickerParams, "sticker", opts.verbose, async (effectiveParams, label) => requestWithChatNotFound(() => api.sendSticker(chatId, fileId.trim(), effectiveParams), label));
|
|
632
|
+
const messageId = String(result?.message_id ?? "unknown");
|
|
633
|
+
const resolvedChatId = String(result?.chat?.id ?? chatId);
|
|
634
|
+
if (result?.message_id) {
|
|
635
|
+
recordSentMessage(chatId, result.message_id);
|
|
636
|
+
}
|
|
637
|
+
recordChannelActivity({
|
|
638
|
+
channel: "telegram",
|
|
639
|
+
accountId: account.accountId,
|
|
640
|
+
direction: "outbound",
|
|
641
|
+
});
|
|
642
|
+
return { messageId, chatId: resolvedChatId };
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Send a poll to a Telegram chat.
|
|
646
|
+
* @param to - Chat ID or username (e.g., "123456789" or "@username")
|
|
647
|
+
* @param poll - Poll input with question, options, maxSelections, and optional durationHours
|
|
648
|
+
* @param opts - Optional configuration
|
|
649
|
+
*/
|
|
650
|
+
export async function sendPollTelegram(to, poll, opts = {}) {
|
|
651
|
+
const { cfg, account, api } = resolveTelegramApiContext(opts);
|
|
652
|
+
const target = parseTelegramTarget(to);
|
|
653
|
+
const chatId = normalizeChatId(target.chatId);
|
|
654
|
+
// Normalize the poll input (validates question, options, maxSelections)
|
|
655
|
+
const normalizedPoll = normalizePollInput(poll, { maxOptions: 10 });
|
|
656
|
+
const threadParams = buildTelegramThreadReplyParams({
|
|
657
|
+
targetMessageThreadId: target.messageThreadId,
|
|
658
|
+
messageThreadId: opts.messageThreadId,
|
|
659
|
+
chatType: target.chatType,
|
|
660
|
+
replyToMessageId: opts.replyToMessageId,
|
|
661
|
+
});
|
|
662
|
+
// Build poll options as simple strings (Grammy accepts string[] or InputPollOption[])
|
|
663
|
+
const pollOptions = normalizedPoll.options;
|
|
664
|
+
const requestWithDiag = createTelegramRequestWithDiag({
|
|
665
|
+
cfg,
|
|
666
|
+
account,
|
|
667
|
+
retry: opts.retry,
|
|
668
|
+
verbose: opts.verbose,
|
|
669
|
+
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
|
670
|
+
});
|
|
671
|
+
const requestWithChatNotFound = createRequestWithChatNotFound({
|
|
672
|
+
requestWithDiag,
|
|
673
|
+
chatId,
|
|
674
|
+
input: to,
|
|
675
|
+
});
|
|
676
|
+
const durationSeconds = normalizedPoll.durationSeconds;
|
|
677
|
+
if (durationSeconds === undefined && normalizedPoll.durationHours !== undefined) {
|
|
678
|
+
throw new Error("Telegram poll durationHours is not supported. Use durationSeconds (5-600) instead.");
|
|
679
|
+
}
|
|
680
|
+
if (durationSeconds !== undefined && (durationSeconds < 5 || durationSeconds > 600)) {
|
|
681
|
+
throw new Error("Telegram poll durationSeconds must be between 5 and 600");
|
|
682
|
+
}
|
|
683
|
+
// Build poll parameters following Grammy's api.sendPoll signature
|
|
684
|
+
// sendPoll(chat_id, question, options, other?, signal?)
|
|
685
|
+
const pollParams = {
|
|
686
|
+
allows_multiple_answers: normalizedPoll.maxSelections > 1,
|
|
687
|
+
is_anonymous: opts.isAnonymous ?? true,
|
|
688
|
+
...(durationSeconds !== undefined ? { open_period: durationSeconds } : {}),
|
|
689
|
+
...(Object.keys(threadParams).length > 0 ? threadParams : {}),
|
|
690
|
+
...(opts.silent === true ? { disable_notification: true } : {}),
|
|
691
|
+
};
|
|
692
|
+
const result = await withTelegramThreadFallback(pollParams, "poll", opts.verbose, async (effectiveParams, label) => requestWithChatNotFound(() => api.sendPoll(chatId, normalizedPoll.question, pollOptions, effectiveParams), label));
|
|
693
|
+
const messageId = String(result?.message_id ?? "unknown");
|
|
694
|
+
const resolvedChatId = String(result?.chat?.id ?? chatId);
|
|
695
|
+
const pollId = result?.poll?.id;
|
|
696
|
+
if (result?.message_id) {
|
|
697
|
+
recordSentMessage(chatId, result.message_id);
|
|
698
|
+
}
|
|
699
|
+
recordChannelActivity({
|
|
700
|
+
channel: "telegram",
|
|
701
|
+
accountId: account.accountId,
|
|
702
|
+
direction: "outbound",
|
|
703
|
+
});
|
|
704
|
+
return { messageId, chatId: resolvedChatId, pollId };
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Create a forum topic in a Telegram supergroup.
|
|
708
|
+
* Requires the bot to have `can_manage_topics` permission.
|
|
709
|
+
*
|
|
710
|
+
* @param chatId - Supergroup chat ID
|
|
711
|
+
* @param name - Topic name (1-128 characters)
|
|
712
|
+
* @param opts - Optional configuration
|
|
713
|
+
*/
|
|
714
|
+
export async function createForumTopicTelegram(chatId, name, opts = {}) {
|
|
715
|
+
if (!name?.trim()) {
|
|
716
|
+
throw new Error("Forum topic name is required");
|
|
717
|
+
}
|
|
718
|
+
const trimmedName = name.trim();
|
|
719
|
+
if (trimmedName.length > 128) {
|
|
720
|
+
throw new Error("Forum topic name must be 128 characters or fewer");
|
|
721
|
+
}
|
|
542
722
|
const cfg = loadConfig();
|
|
543
723
|
const account = resolveTelegramAccount({
|
|
544
724
|
cfg,
|
|
545
725
|
accountId: opts.accountId,
|
|
546
726
|
});
|
|
547
727
|
const token = resolveToken(opts.token, account);
|
|
548
|
-
|
|
549
|
-
|
|
728
|
+
// Accept topic-qualified targets (e.g. telegram:group:<id>:topic:<thread>)
|
|
729
|
+
// but createForumTopic must always target the base supergroup chat id.
|
|
730
|
+
const target = parseTelegramTarget(chatId);
|
|
731
|
+
const normalizedChatId = normalizeChatId(target.chatId);
|
|
550
732
|
const client = resolveTelegramClientOptions(account);
|
|
551
733
|
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
|
|
552
|
-
const messageThreadId = opts.messageThreadId != null ? opts.messageThreadId : target.messageThreadId;
|
|
553
|
-
const threadIdParams = buildTelegramThreadParams(messageThreadId);
|
|
554
|
-
const threadParams = threadIdParams ? { ...threadIdParams } : {};
|
|
555
|
-
if (opts.replyToMessageId != null) {
|
|
556
|
-
threadParams.reply_to_message_id = Math.trunc(opts.replyToMessageId);
|
|
557
|
-
}
|
|
558
|
-
const hasThreadParams = Object.keys(threadParams).length > 0;
|
|
559
734
|
const request = createTelegramRetryRunner({
|
|
560
735
|
retry: opts.retry,
|
|
561
736
|
configRetry: account.config.retry,
|
|
562
737
|
verbose: opts.verbose,
|
|
738
|
+
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
|
563
739
|
});
|
|
564
740
|
const logHttpError = createTelegramHttpLogger(cfg);
|
|
565
|
-
const requestWithDiag = (fn, label) =>
|
|
741
|
+
const requestWithDiag = (fn, label) => withTelegramApiErrorLogging({
|
|
742
|
+
operation: label ?? "request",
|
|
743
|
+
fn: () => request(fn, label),
|
|
744
|
+
}).catch((err) => {
|
|
566
745
|
logHttpError(label ?? "request", err);
|
|
567
746
|
throw err;
|
|
568
747
|
});
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
`Input was: ${JSON.stringify(to)}.`,
|
|
576
|
-
].join(" "));
|
|
577
|
-
};
|
|
578
|
-
const sendWithStickerThreadFallback = async (params, label, attempt) => {
|
|
579
|
-
try {
|
|
580
|
-
return await attempt(params, label);
|
|
581
|
-
}
|
|
582
|
-
catch (err) {
|
|
583
|
-
if (!hasMessageThreadIdParam(params) || !isTelegramThreadNotFoundError(err)) {
|
|
584
|
-
throw err;
|
|
585
|
-
}
|
|
586
|
-
if (opts.verbose) {
|
|
587
|
-
console.warn(`telegram ${label} failed with message_thread_id, retrying without thread: ${formatErrorMessage(err)}`);
|
|
588
|
-
}
|
|
589
|
-
const retriedParams = removeMessageThreadIdParam(params);
|
|
590
|
-
return await attempt(retriedParams, `${label}-threadless`);
|
|
591
|
-
}
|
|
592
|
-
};
|
|
593
|
-
const stickerParams = hasThreadParams ? threadParams : undefined;
|
|
594
|
-
const result = await sendWithStickerThreadFallback(stickerParams, "sticker", async (effectiveParams, label) => requestWithDiag(() => api.sendSticker(chatId, fileId.trim(), effectiveParams), label).catch((err) => {
|
|
595
|
-
throw wrapChatNotFound(err);
|
|
596
|
-
}));
|
|
597
|
-
const messageId = String(result?.message_id ?? "unknown");
|
|
598
|
-
const resolvedChatId = String(result?.chat?.id ?? chatId);
|
|
599
|
-
if (result?.message_id) {
|
|
600
|
-
recordSentMessage(chatId, result.message_id);
|
|
748
|
+
const extra = {};
|
|
749
|
+
if (opts.iconColor != null) {
|
|
750
|
+
extra.icon_color = opts.iconColor;
|
|
751
|
+
}
|
|
752
|
+
if (opts.iconCustomEmojiId?.trim()) {
|
|
753
|
+
extra.icon_custom_emoji_id = opts.iconCustomEmojiId.trim();
|
|
601
754
|
}
|
|
755
|
+
const hasExtra = Object.keys(extra).length > 0;
|
|
756
|
+
const result = await requestWithDiag(() => api.createForumTopic(normalizedChatId, trimmedName, hasExtra ? extra : undefined), "createForumTopic");
|
|
757
|
+
const topicId = result.message_thread_id;
|
|
602
758
|
recordChannelActivity({
|
|
603
759
|
channel: "telegram",
|
|
604
760
|
accountId: account.accountId,
|
|
605
761
|
direction: "outbound",
|
|
606
762
|
});
|
|
607
|
-
return {
|
|
763
|
+
return {
|
|
764
|
+
topicId,
|
|
765
|
+
name: result.name ?? trimmedName,
|
|
766
|
+
chatId: normalizedChatId,
|
|
767
|
+
};
|
|
608
768
|
}
|