openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
683 lines (682 loc) • 28 kB
JavaScript
import { p as readStringValue } from "./string-coerce-mnp54Vah.js";
import { j as resolveTimerTimeoutMs } from "./number-coercion-CJQ8TR--.js";
import { i as formatErrorMessage } from "./errors-BXgSefBE.js";
import { d as registerUnhandledRejectionHandler, u as registerUncaughtExceptionHandler } from "./unhandled-rejections-CifjyMHp.js";
import { i as getRuntimeConfig } from "./io-Gi7-pyU-.js";
import { t as resolveAgentMaxConcurrent } from "./agent-limits-DGV0ALs8.js";
import { r as fetchWithTimeout } from "./fetch-timeout-CDaw0X8V.js";
import { i as normalizeMessageChannel } from "./message-channel-normalize-BLLf0ubu.js";
import { j as resolveExecApprovalRequestAllowedDecisions } from "./exec-approvals-C6M6SGY3.js";
import { a as optionalPositiveIntegerSchema } from "./typebox-DCO1JbMn.js";
import "./error-runtime-C8vbtAJt.js";
import { r as makeProxyFetch } from "./proxy-fetch-Ii-XBOip.js";
import "./number-runtime-DBLVDypr.js";
import "./runtime-config-snapshot-CDoFLjqb.js";
import { t as waitForAbortSignal } from "./abort-signal-BAyXz5Zx.js";
import "./runtime-env-D_2q8-VK.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import "./text-utility-runtime-BZ3jGr-e.js";
import { t as resolveTelegramAllowedUpdates } from "./allowed-updates-C8V4-A3j.js";
import { r as resolveTelegramTransport, t as resolveTelegramApiBase } from "./fetch-DVxN6WLs.js";
import { a as isRecoverableTelegramNetworkError, l as isTelegramPollingNetworkError } from "./request-timeouts-DYFbgXTJ.js";
import "./routing-CQ63ICPX.js";
import { c as buildExecApprovalPendingReplyPayload } from "./exec-approval-reply-BXMsffTv.js";
import { t as CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "./approval-handler-adapter-runtime-DLWtIqHq.js";
import { t as resolveExecApprovalCommandDisplay } from "./exec-approval-command-display-BYD_3EWn.js";
import { r as registerChannelRuntimeContext } from "./channel-runtime-context-Bx08uB94.js";
import "./approval-reply-runtime-CtMUsKCW.js";
import "./model-session-runtime-XUhYGuhe.js";
import "./ssrf-runtime-BOGN5pUi.js";
import { a as listTokenSourcedAccounts, i as createUnionActionGate, r as resolveReactionMessageId } from "./channel-actions-BOfvC-Gl.js";
import { t as extractToolSend } from "./tool-send-DzTXHBeR.js";
import { c as resolveTelegramPollActionGateState, o as resolveTelegramAccount, r as listTelegramAccountIds, t as createTelegramActionGate } from "./accounts-blCxAL9j.js";
import { t as inspectTelegramAccount } from "./account-inspect-BHv-ag3o.js";
import { t as isTelegramInlineButtonsEnabled } from "./inline-buttons-4xnM1eKI.js";
import { a as isTelegramExecApprovalHandlerConfigured, i as isTelegramExecApprovalClientEnabled } from "./exec-approvals-CgxkwQwD.js";
import { t as normalizeTelegramBotInfo } from "./bot-info-Bv59m7ss.js";
import { t as fingerprintTelegramBotToken } from "./token-fingerprint-5R81vEJi.js";
import { Type } from "typebox";
//#region extensions/telegram/src/message-tool-schema.ts
function createTelegramPollExtraToolSchemas() {
return {
pollDurationSeconds: optionalPositiveIntegerSchema(),
pollAnonymous: Type.Optional(Type.Boolean()),
pollPublic: Type.Optional(Type.Boolean())
};
}
//#endregion
//#region extensions/telegram/src/channel-actions.ts
let telegramActionRuntimePromise = null;
async function loadTelegramActionRuntime() {
telegramActionRuntimePromise ??= import("./action-runtime-CWv4t37O.js");
return await telegramActionRuntimePromise;
}
const telegramMessageActionRuntime = { handleTelegramAction: async (...args) => {
const { handleTelegramAction } = await loadTelegramActionRuntime();
return await handleTelegramAction(...args);
} };
const TELEGRAM_MESSAGE_ACTION_MAP = {
delete: "deleteMessage",
edit: "editMessage",
poll: "poll",
react: "react",
send: "sendMessage",
sticker: "sendSticker",
"sticker-search": "searchSticker",
"topic-create": "createForumTopic",
"topic-edit": "editForumTopic"
};
function resolveTelegramMessageActionName(action) {
return TELEGRAM_MESSAGE_ACTION_MAP[action];
}
function resolveTelegramActionDiscovery(cfg) {
const accounts = listTokenSourcedAccounts(listTelegramAccountIds(cfg).map((accountId) => inspectTelegramAccount({
cfg,
accountId
})).filter((account) => account.enabled && account.configured));
if (accounts.length === 0) return null;
const unionGate = createUnionActionGate(accounts, (account) => createTelegramActionGate({
cfg,
accountId: account.accountId
}));
return {
isEnabled: (key, defaultValue = true) => unionGate(key, defaultValue),
pollEnabled: accounts.some((account) => {
return resolveTelegramPollActionGateState(createTelegramActionGate({
cfg,
accountId: account.accountId
})).enabled;
}),
buttonsEnabled: accounts.some((account) => isTelegramInlineButtonsEnabled({
cfg,
accountId: account.accountId
}))
};
}
function resolveScopedTelegramActionDiscovery(params) {
if (!params.accountId) return resolveTelegramActionDiscovery(params.cfg);
const account = inspectTelegramAccount({
cfg: params.cfg,
accountId: params.accountId
});
if (!account.enabled || !account.configured || account.tokenSource === "none") return null;
const gate = createTelegramActionGate({
cfg: params.cfg,
accountId: account.accountId
});
return {
isEnabled: (key, defaultValue = true) => gate(key, defaultValue),
pollEnabled: resolveTelegramPollActionGateState(gate).enabled,
buttonsEnabled: isTelegramInlineButtonsEnabled({
cfg: params.cfg,
accountId: account.accountId
})
};
}
function describeTelegramMessageTool({ cfg, accountId }) {
const discovery = resolveScopedTelegramActionDiscovery({
cfg,
accountId
});
if (!discovery) return {
actions: [],
capabilities: [],
schema: null
};
const actions = new Set(["send"]);
if (discovery.pollEnabled) actions.add("poll");
if (discovery.isEnabled("reactions")) actions.add("react");
if (discovery.isEnabled("deleteMessage")) actions.add("delete");
if (discovery.isEnabled("editMessage")) actions.add("edit");
if (discovery.isEnabled("sticker", false)) {
actions.add("sticker");
actions.add("sticker-search");
}
if (discovery.isEnabled("createForumTopic")) actions.add("topic-create");
if (discovery.isEnabled("editForumTopic")) actions.add("topic-edit");
const schema = [];
if (discovery.pollEnabled) schema.push({
properties: createTelegramPollExtraToolSchemas(),
visibility: "all-configured"
});
return {
actions: Array.from(actions),
capabilities: discovery.buttonsEnabled ? ["presentation", "delivery-pin"] : ["delivery-pin"],
schema
};
}
const telegramMessageActions = {
describeMessageTool: describeTelegramMessageTool,
resolveExecutionMode: () => "gateway",
resolveCliActionRequest: ({ action, args }) => {
if (action !== "thread-create") return {
action,
args
};
const { threadName, ...rest } = args;
return {
action: "topic-create",
args: {
...rest,
name: readStringValue(threadName)
}
};
},
extractToolSend: ({ args }) => {
return extractToolSend(args, "sendMessage");
},
handleAction: async ({ action, params, cfg, accountId, mediaLocalRoots, mediaReadFile, sessionKey, inboundEventKind, toolContext, gatewayClientScopes }) => {
const telegramAction = resolveTelegramMessageActionName(action);
if (!telegramAction) throw new Error(`Unsupported Telegram action: ${action}`);
return await telegramMessageActionRuntime.handleTelegramAction({
...params,
action: telegramAction,
accountId: accountId ?? void 0,
...action === "react" ? { messageId: resolveReactionMessageId({
args: params,
toolContext
}) } : {}
}, cfg, {
mediaLocalRoots,
mediaReadFile,
sessionKey,
inboundEventKind,
gatewayClientScopes
});
}
};
//#endregion
//#region extensions/telegram/src/exec-approval-forwarding.ts
function shouldSuppressTelegramExecApprovalForwardingFallback(params) {
if ((normalizeMessageChannel(params.target.channel) ?? params.target.channel) !== "telegram") return false;
if (normalizeMessageChannel(params.request.request.turnSourceChannel ?? "") !== "telegram") return false;
const accountId = params.target.accountId?.trim() || params.request.request.turnSourceAccountId?.trim();
return isTelegramExecApprovalClientEnabled({
cfg: params.cfg,
accountId
});
}
function buildTelegramExecApprovalPendingPayload(params) {
return buildExecApprovalPendingReplyPayload({
approvalId: params.request.id,
approvalSlug: params.request.id.slice(0, 8),
approvalCommandId: params.request.id,
warningText: params.request.request.warningText ?? void 0,
command: resolveExecApprovalCommandDisplay(params.request.request).commandText,
cwd: params.request.request.cwd ?? void 0,
host: params.request.request.host === "node" ? "node" : "gateway",
nodeId: params.request.request.nodeId ?? void 0,
allowedDecisions: resolveExecApprovalRequestAllowedDecisions(params.request.request),
expiresAtMs: params.request.expiresAtMs,
nowMs: params.nowMs
});
}
//#endregion
//#region extensions/telegram/src/polling-lease.ts
const TELEGRAM_POLLING_LEASES_KEY = Symbol.for("openclaw.telegram.pollingLeases");
const DEFAULT_TELEGRAM_POLLING_LEASE_WAIT_MS = 5e3;
function pollingLeaseRegistry() {
const proc = process;
proc[TELEGRAM_POLLING_LEASES_KEY] ??= /* @__PURE__ */ new Map();
return proc[TELEGRAM_POLLING_LEASES_KEY];
}
function createDuplicatePollingError(params) {
const ageMs = Math.max(0, Date.now() - params.existing.startedAt);
const ageSeconds = Math.round(ageMs / 1e3);
return /* @__PURE__ */ new Error(`Telegram polling already active for bot token ${params.tokenFingerprint} on account "${params.existing.accountId}" (${ageSeconds}s old); refusing duplicate poller for account "${params.accountId}". Stop the existing OpenClaw gateway/poller or use a different bot token.`);
}
async function waitForPreviousRelease(params) {
if (params.signal?.aborted) return "aborted";
if (params.waitMs <= 0) return "timeout";
let timer;
let abortListener;
try {
const waitMs = resolveTimerTimeoutMs(params.waitMs, DEFAULT_TELEGRAM_POLLING_LEASE_WAIT_MS, 0);
const timeout = new Promise((resolve) => {
timer = setTimeout(() => resolve("timeout"), waitMs);
timer.unref?.();
});
const aborted = new Promise((resolve) => {
abortListener = () => resolve("aborted");
params.signal?.addEventListener("abort", abortListener, { once: true });
});
const released = params.done.then(() => "released");
return await Promise.race([
released,
timeout,
aborted
]);
} finally {
if (timer) clearTimeout(timer);
if (abortListener) params.signal?.removeEventListener("abort", abortListener);
}
}
function createLease(params) {
let resolveDone;
const done = new Promise((resolve) => {
resolveDone = resolve;
});
const owner = Symbol(`telegram-polling:${params.accountId}`);
const entry = {
accountId: params.accountId,
abortSignal: params.abortSignal,
done,
owner,
resolveDone,
startedAt: Date.now()
};
params.registry.set(params.tokenFingerprint, entry);
let released = false;
return {
tokenFingerprint: params.tokenFingerprint,
waitedForPrevious: params.waitedForPrevious,
replacedStoppingPrevious: params.replacedStoppingPrevious,
release: () => {
if (released) return;
released = true;
if (params.registry.get(params.tokenFingerprint)?.owner === owner) params.registry.delete(params.tokenFingerprint);
resolveDone();
}
};
}
async function acquireTelegramPollingLease(opts) {
const registry = pollingLeaseRegistry();
const fingerprint = fingerprintTelegramBotToken(opts.token);
const waitMs = opts.waitMs ?? DEFAULT_TELEGRAM_POLLING_LEASE_WAIT_MS;
let waitedForPrevious = false;
for (;;) {
const existing = registry.get(fingerprint);
if (!existing) return createLease({
accountId: opts.accountId,
abortSignal: opts.abortSignal,
registry,
tokenFingerprint: fingerprint,
waitedForPrevious,
replacedStoppingPrevious: false
});
if (!existing.abortSignal?.aborted) throw createDuplicatePollingError({
accountId: opts.accountId,
existing,
tokenFingerprint: fingerprint
});
waitedForPrevious = true;
const waitResult = await waitForPreviousRelease({
done: existing.done,
signal: opts.abortSignal,
waitMs
});
if (waitResult === "aborted") throw new Error(`Telegram polling start aborted while waiting for previous poller for bot token ${fingerprint} to stop.`);
if (registry.get(fingerprint) !== existing) continue;
if (waitResult === "released") continue;
return createLease({
accountId: opts.accountId,
abortSignal: opts.abortSignal,
registry,
tokenFingerprint: fingerprint,
waitedForPrevious,
replacedStoppingPrevious: true
});
}
}
async function releaseStoppedTelegramPollingLease(opts) {
const registry = pollingLeaseRegistry();
const fingerprint = fingerprintTelegramBotToken(opts.token);
const existing = registry.get(fingerprint);
if (!existing || existing.accountId !== opts.accountId) return false;
if (!existing.abortSignal?.aborted) return false;
if (await waitForPreviousRelease({
done: existing.done,
waitMs: opts.waitMs ?? DEFAULT_TELEGRAM_POLLING_LEASE_WAIT_MS
}) === "released" || registry.get(fingerprint) !== existing) return false;
registry.delete(fingerprint);
existing.resolveDone();
return true;
}
//#endregion
//#region extensions/telegram/src/monitor.ts
function createTelegramRunnerOptions(cfg) {
return {
sink: { concurrency: resolveAgentMaxConcurrent(cfg) },
runner: {
fetch: {
timeout: 30,
allowed_updates: resolveTelegramAllowedUpdates()
},
silent: true,
maxRetryTime: 3600 * 1e3,
retryInterval: "exponential"
}
};
}
function normalizePersistedUpdateId(value) {
if (value === null) return null;
if (!Number.isSafeInteger(value) || value < 0) return null;
return value;
}
const TELEGRAM_OFFSET_ROTATION_LABELS = {
"bot-id-changed": "bot identity change",
"legacy-state": "legacy update offset",
"token-rotated": "token rotation"
};
function formatTelegramOffsetRotationMessage(accountId, info) {
const previousLabel = info.previousBotId ?? "(legacy unscoped offset)";
return `[telegram] Detected ${TELEGRAM_OFFSET_ROTATION_LABELS[info.reason]} for account "${accountId}" (was ${previousLabel}, now ${info.currentBotId}); discarding stale update offset ${info.staleLastUpdateId} and starting fresh.`;
}
/** Check if error is a Grammy HttpError (used to scope unhandled rejection handling) */
const isGrammyHttpError = (err) => {
if (!err || typeof err !== "object") return false;
return err.name === "HttpError";
};
let telegramMonitorPollingRuntimePromise;
async function loadTelegramMonitorPollingRuntime() {
telegramMonitorPollingRuntimePromise ??= import("./monitor-polling.runtime.js");
return await telegramMonitorPollingRuntimePromise;
}
let telegramMonitorWebhookRuntimePromise;
async function loadTelegramMonitorWebhookRuntime() {
telegramMonitorWebhookRuntimePromise ??= import("./monitor-webhook.runtime.js");
return await telegramMonitorWebhookRuntimePromise;
}
async function monitorTelegramProvider(opts = {}) {
const logInfo = (line) => (opts.runtime?.log ?? console.log)(line);
const logError = (line) => (opts.runtime?.error ?? console.error)(line);
const log = (line) => {
if (line.includes("[telegram][diag]")) {
logInfo(line);
return;
}
logError(line);
};
let pollingSession;
const handlePollingNetworkFailure = (err, label) => {
const isNetworkError = isRecoverableTelegramNetworkError(err, { context: "polling" });
const isTelegramPollingError = isTelegramPollingNetworkError(err);
const activeRunner = pollingSession?.activeRunner;
if (isNetworkError && isTelegramPollingError && activeRunner && activeRunner.isRunning()) {
pollingSession?.markForceRestarted();
pollingSession?.markTransportDirty();
pollingSession?.abortActiveFetch();
activeRunner.stop().catch(() => {});
log("[telegram][diag] marking transport dirty after polling network failure");
log(`[telegram] Restarting polling after ${label}: ${formatErrorMessage(err)}`);
return true;
}
if (isGrammyHttpError(err) && isNetworkError && isTelegramPollingError) {
log(`[telegram] Suppressed network error: ${formatErrorMessage(err)}`);
return true;
}
return false;
};
const unregisterUnhandledRejectionHandler = registerUnhandledRejectionHandler((err) => handlePollingNetworkFailure(err, "unhandled network error"));
const unregisterUncaughtExceptionHandler = registerUncaughtExceptionHandler((err) => handlePollingNetworkFailure(err, "uncaught network error"));
try {
const cfg = opts.config ?? getRuntimeConfig();
const account = resolveTelegramAccount({
cfg,
accountId: opts.accountId
});
const token = opts.token?.trim() || account.token;
if (!token) throw new Error(`Telegram bot token missing for account "${account.accountId}" (set channels.telegram.accounts.${account.accountId}.botToken/tokenFile or TELEGRAM_BOT_TOKEN for default).`);
const proxyFetch = opts.proxyFetch ?? (account.config.proxy ? makeProxyFetch(account.config.proxy) : void 0);
if (opts.useWebhook) {
const { startTelegramWebhook } = await loadTelegramMonitorWebhookRuntime();
if (isTelegramExecApprovalHandlerConfigured({
cfg,
accountId: account.accountId
})) registerChannelRuntimeContext({
channelRuntime: opts.channelRuntime,
channelId: "telegram",
accountId: account.accountId,
capability: CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY,
context: { token },
abortSignal: opts.abortSignal
});
await startTelegramWebhook({
token,
accountId: account.accountId,
config: cfg,
path: opts.webhookPath,
port: opts.webhookPort,
secret: opts.webhookSecret ?? account.config.webhookSecret,
host: opts.webhookHost ?? account.config.webhookHost,
runtime: opts.runtime,
fetch: proxyFetch,
abortSignal: opts.abortSignal,
publicUrl: opts.webhookUrl,
webhookCertPath: opts.webhookCertPath,
setStatus: opts.setStatus
});
await waitForAbortSignal(opts.abortSignal);
return;
}
const { TelegramPollingSession, deleteTelegramUpdateOffset, readTelegramUpdateOffset, writeTelegramUpdateOffset } = await loadTelegramMonitorPollingRuntime();
const pollingLease = await acquireTelegramPollingLease({
token,
accountId: account.accountId,
abortSignal: opts.abortSignal
});
if (pollingLease.waitedForPrevious) log(`[telegram][diag] waited for previous polling session for bot token ${pollingLease.tokenFingerprint} before starting account "${account.accountId}".`);
if (pollingLease.replacedStoppingPrevious) log(`[telegram][diag] previous polling session for bot token ${pollingLease.tokenFingerprint} did not stop within the lease wait; starting a replacement for account "${account.accountId}".`);
try {
if (isTelegramExecApprovalHandlerConfigured({
cfg,
accountId: account.accountId
})) registerChannelRuntimeContext({
channelRuntime: opts.channelRuntime,
channelId: "telegram",
accountId: account.accountId,
capability: CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY,
context: { token },
abortSignal: opts.abortSignal
});
const persistedOffsetRaw = await readTelegramUpdateOffset({
accountId: account.accountId,
botToken: token,
onRotationDetected: async (info) => {
log(formatTelegramOffsetRotationMessage(account.accountId, info));
try {
await deleteTelegramUpdateOffset({ accountId: account.accountId });
} catch (err) {
logError(`telegram: failed to delete stale update offset after rotation: ${String(err)}`);
}
}
});
let lastUpdateId = normalizePersistedUpdateId(persistedOffsetRaw);
if (persistedOffsetRaw !== null && lastUpdateId === null) log(`[telegram] Ignoring invalid persisted update offset (${String(persistedOffsetRaw)}); starting without offset confirmation.`);
const persistUpdateId = async (updateId) => {
const normalizedUpdateId = normalizePersistedUpdateId(updateId);
if (normalizedUpdateId === null) {
log(`[telegram] Ignoring invalid update_id value: ${String(updateId)}`);
return;
}
if (lastUpdateId !== null && normalizedUpdateId <= lastUpdateId) return;
lastUpdateId = normalizedUpdateId;
try {
await writeTelegramUpdateOffset({
accountId: account.accountId,
updateId: normalizedUpdateId,
botToken: token
});
} catch (err) {
logError(`telegram: failed to persist update offset: ${String(err)}`);
}
};
const createTelegramTransportForPolling = () => resolveTelegramTransport(proxyFetch, { network: account.config.network });
const telegramTransport = createTelegramTransportForPolling();
pollingSession = new TelegramPollingSession({
token,
config: cfg,
accountId: account.accountId,
runtime: opts.runtime,
proxyFetch,
botInfo: opts.botInfo,
abortSignal: opts.abortSignal,
runnerOptions: createTelegramRunnerOptions(cfg),
getLastUpdateId: () => lastUpdateId,
persistUpdateId,
log,
telegramTransport,
createTelegramTransport: createTelegramTransportForPolling,
stallThresholdMs: account.config.pollingStallThresholdMs,
setStatus: opts.setStatus,
isolatedIngress: {
enabled: opts.isolatedIngress?.enabled ?? true,
apiRoot: account.config.apiRoot,
timeoutSeconds: account.config.timeoutSeconds,
proxy: account.config.proxy,
network: account.config.network
}
});
await pollingSession.runUntilAbort();
} finally {
pollingLease.release();
}
} finally {
unregisterUnhandledRejectionHandler();
unregisterUncaughtExceptionHandler();
}
}
//#endregion
//#region extensions/telegram/src/probe.ts
const probeTransportCache = /* @__PURE__ */ new Map();
const MAX_PROBE_TRANSPORT_CACHE_SIZE = 64;
function resetTelegramProbeFetcherCacheForTests() {
probeTransportCache.clear();
}
function resolveProbeOptions(proxyOrOptions) {
if (!proxyOrOptions) return;
if (typeof proxyOrOptions === "string") return { proxyUrl: proxyOrOptions };
return proxyOrOptions;
}
function shouldUseProbeTransportCache() {
return !process.env.VITEST && true;
}
function buildProbeTransportCacheKey(token, options) {
const cacheIdentity = options?.accountId?.trim() || token;
const cacheIdentityKind = options?.accountId?.trim() ? "account" : "token";
const proxyKey = options?.proxyUrl?.trim() ?? "";
const autoSelectFamily = options?.network?.autoSelectFamily;
return `${cacheIdentityKind}:${cacheIdentity}::${proxyKey}::${typeof autoSelectFamily === "boolean" ? String(autoSelectFamily) : "default"}::${options?.network?.dnsResultOrder ?? "default"}::${options?.apiRoot?.trim() ?? ""}`;
}
function setCachedProbeTransport(cacheKey, transport) {
probeTransportCache.set(cacheKey, transport);
if (probeTransportCache.size > MAX_PROBE_TRANSPORT_CACHE_SIZE) {
const oldestKey = probeTransportCache.keys().next().value;
if (oldestKey !== void 0) probeTransportCache.delete(oldestKey);
}
return transport;
}
function resolveProbeTransport(token, options) {
const cacheKey = shouldUseProbeTransportCache() ? buildProbeTransportCacheKey(token, options) : null;
if (cacheKey) {
const cached = probeTransportCache.get(cacheKey);
if (cached) return cached;
}
const proxyUrl = options?.proxyUrl?.trim();
const transport = resolveTelegramTransport(proxyUrl ? makeProxyFetch(proxyUrl) : void 0, { network: options?.network });
if (cacheKey) return setCachedProbeTransport(cacheKey, transport);
return transport;
}
function normalizeBoolean(value) {
return typeof value === "boolean" ? value : null;
}
async function probeTelegram(token, timeoutMs, proxyOrOptions) {
const started = Date.now();
const timeoutBudgetMs = Math.max(1, Math.floor(timeoutMs));
const deadlineMs = started + timeoutBudgetMs;
const options = resolveProbeOptions(proxyOrOptions);
const includeWebhookInfo = options?.includeWebhookInfo !== false;
const transport = resolveProbeTransport(token, options);
const fetcher = transport.fetch;
const base = `${resolveTelegramApiBase(options?.apiRoot)}/bot${token}`;
const retryDelayMs = Math.max(50, Math.min(1e3, Math.floor(timeoutBudgetMs / 5)));
const resolveRemainingBudgetMs = () => Math.max(0, deadlineMs - Date.now());
const result = {
ok: false,
status: null,
error: null,
elapsedMs: 0
};
try {
let meRes = null;
let fetchError = null;
for (let i = 0; i < 3; i++) {
const remainingBudgetMs = resolveRemainingBudgetMs();
if (remainingBudgetMs <= 0) break;
try {
meRes = await fetchWithTimeout(`${base}/getMe`, {}, Math.max(1, Math.min(timeoutBudgetMs, remainingBudgetMs)), fetcher);
break;
} catch (err) {
fetchError = err;
transport.forceFallback?.("probe timeout/network error");
if (i < 2) {
const remainingAfterAttemptMs = resolveRemainingBudgetMs();
if (remainingAfterAttemptMs <= 0) break;
const delayMs = Math.min(retryDelayMs, remainingAfterAttemptMs);
if (delayMs > 0) await new Promise((resolve) => {
setTimeout(resolve, delayMs);
});
}
}
}
if (!meRes) throw toLintErrorObject(fetchError ?? /* @__PURE__ */ new Error(`probe timed out after ${timeoutBudgetMs}ms`), "Non-Error thrown");
const meJson = await meRes.json();
if (!meRes.ok || !meJson?.ok) {
result.status = meRes.status;
result.error = meJson?.description ?? `getMe failed (${meRes.status})`;
return {
...result,
elapsedMs: Date.now() - started
};
}
const botInfo = normalizeTelegramBotInfo(meJson.result);
const bot = meJson.result && typeof meJson.result === "object" ? meJson.result : {};
if (botInfo) result.botInfo = botInfo;
result.bot = {
id: typeof bot.id === "number" ? bot.id : null,
isBot: normalizeBoolean(bot.is_bot),
firstName: typeof bot.first_name === "string" ? bot.first_name : null,
username: typeof bot.username === "string" ? bot.username : null,
canJoinGroups: normalizeBoolean(bot.can_join_groups),
canReadAllGroupMessages: normalizeBoolean(bot.can_read_all_group_messages),
canManageBots: normalizeBoolean(bot.can_manage_bots),
supportsInlineQueries: normalizeBoolean(bot.supports_inline_queries),
canConnectToBusiness: normalizeBoolean(bot.can_connect_to_business),
hasMainWebApp: normalizeBoolean(bot.has_main_web_app),
hasTopicsEnabled: normalizeBoolean(bot.has_topics_enabled),
allowsUsersToCreateTopics: normalizeBoolean(bot.allows_users_to_create_topics)
};
if (includeWebhookInfo) try {
const webhookRemainingBudgetMs = resolveRemainingBudgetMs();
if (webhookRemainingBudgetMs > 0) {
const webhookRes = await fetchWithTimeout(`${base}/getWebhookInfo`, {}, Math.max(1, Math.min(timeoutBudgetMs, webhookRemainingBudgetMs)), fetcher);
const webhookJson = await webhookRes.json();
if (webhookRes.ok && webhookJson?.ok) result.webhook = {
url: webhookJson.result?.url ?? null,
hasCustomCert: webhookJson.result?.has_custom_certificate ?? null
};
}
} catch {}
result.ok = true;
result.status = null;
result.error = null;
result.elapsedMs = Date.now() - started;
return result;
} catch (err) {
return {
...result,
status: err instanceof Response ? err.status : result.status,
error: formatErrorMessage(err),
elapsedMs: Date.now() - started
};
}
}
function toLintErrorObject(value, fallbackMessage) {
if (value instanceof Error) return value;
if (typeof value === "string") return new Error(value);
const error = new Error(fallbackMessage, { cause: value });
if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
return error;
}
//#endregion
export { buildTelegramExecApprovalPendingPayload as a, releaseStoppedTelegramPollingLease as i, resetTelegramProbeFetcherCacheForTests as n, shouldSuppressTelegramExecApprovalForwardingFallback as o, monitorTelegramProvider as r, telegramMessageActions as s, probeTelegram as t };