UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

389 lines (388 loc) 14.5 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { t as formatCliCommand } from "./command-format-C7YfyMTd.js"; import { n as normalizeAccountId, t as DEFAULT_ACCOUNT_ID } from "./account-id-CETVCrTz.js"; import { l as hasConfiguredSecretInput } from "./types.secrets-kC0nOetj.js"; import { t as defineChannelSetupContract } from "./setup-contract-RbJSZiOH.js"; import { n as getChatChannelMeta } from "./chat-meta-tsXGPguN.js"; import { t as formatDocsLink } from "./links-ClIwBcy4.js"; import { a as createScopedChannelConfigAdapter, t as adaptScopedAccountAccessor } from "./channel-config-helpers-Djn-Fycf.js"; import "./string-coerce-runtime-GQa0ehRA.js"; import { t as createSetupTranslator } from "./i18n-hynzGFbD.js"; import { n as applySetupAccountConfigPatch, r as createEnvPatchedAccountSetupAdapter } from "./setup-helpers-DpWE8O4k.js"; import { N as splitSetupEntries, a as createAllowFromSection, b as patchChannelConfigForAccount, j as setSetupChannelEnabled, s as createStandardChannelSetupStatus, w as promptResolvedAllowFrom } from "./setup-wizard-helpers-DYc3t3kF.js"; import { n as defineTokenCredential } from "./setup-credential-7-0D5aTk.js"; import "./setup-BosJMgvw.js"; import "./setup-runtime-BWfBnPr_.js"; import "./channel-setup-Cf-_b9tx.js"; import "./setup-tools-CFDKgzqn.js"; import { t as formatAllowFromLowercase } from "./allow-from-BPE6WaOO.js"; import { t as createChannelDmPolicy } from "./channel-dm-policy-BjL13kLU.js"; import "./channel-plugin-common-B4Vaopy4.js"; import { t as mergeTelegramAccountConfig } from "./account-config-B05_z8M7.js"; import { a as resolveDefaultTelegramAccountId, o as resolveTelegramAccount, r as listTelegramAccountIds } from "./accounts-CgIQ5mFD.js"; import { t as isNumericTelegramSenderUserId } from "./allow-from-Byf7JKVc.js"; import { r as inspectTelegramAccount } from "./account-inspect-3WXhWjZW.js"; import { n as singleAccountKeysToMove, t as namedAccountPromotionKeys } from "./setup-contract-CDcIs5O0.js"; import { t as TelegramChannelConfigSchema } from "./config-schema-B7IsjGN7.js"; import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DocTX-iV.js"; //#region extensions/telegram/src/config-adapter.ts const TELEGRAM_CHANNEL$1 = "telegram"; function resolveTelegramConfigAccessorAccount(params) { const accountId = normalizeAccountId(params.accountId ?? resolveDefaultTelegramAccountId(params.cfg)); return { config: mergeTelegramAccountConfig(params.cfg, accountId) }; } const telegramConfigAdapter = createScopedChannelConfigAdapter({ sectionKey: TELEGRAM_CHANNEL$1, listAccountIds: listTelegramAccountIds, resolveAccount: adaptScopedAccountAccessor(resolveTelegramAccount), resolveAccessorAccount: resolveTelegramConfigAccessorAccount, inspectAccount: adaptScopedAccountAccessor(inspectTelegramAccount), defaultAccountId: resolveDefaultTelegramAccountId, clearBaseFields: [ "botToken", "tokenFile", "name" ], resolveAllowFrom: (account) => account.config.allowFrom, formatAllowFrom: (allowFrom) => formatAllowFromLowercase({ allowFrom, stripPrefixRe: /^(telegram|tg):/i }), resolveDefaultTo: (account) => account.config.defaultTo }); function createTelegramPluginConfig() { return { ...telegramConfigAdapter, hasConfiguredState: ({ env }) => typeof env?.TELEGRAM_BOT_TOKEN === "string" && env.TELEGRAM_BOT_TOKEN.trim().length > 0, isConfigured: (account, cfg) => { const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); return inspected.configured && Boolean(inspected.token.trim()); }, unconfiguredReason: (account, cfg) => { const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); return inspected.stateReason ?? (inspected.tokenStatus === "configured_unavailable" ? `not configured: token ${inspected.tokenSource} is configured but unavailable` : "not configured"); }, describeAccount: (account, cfg) => { const inspected = inspectTelegramAccount({ cfg, accountId: account.accountId }); return { accountId: account.accountId, name: account.name, enabled: account.enabled, configured: inspected.configured, tokenSource: inspected.tokenSource, tokenStatus: inspected.tokenStatus }; } }; } //#endregion //#region extensions/telegram/src/setup-core.ts const t$1 = createSetupTranslator(); const channel$2 = "telegram"; function getTelegramTokenHelpLines() { return [ t$1("wizard.telegram.tokenHelpOpenBotFather"), t$1("wizard.telegram.tokenHelpNewBot"), t$1("wizard.telegram.tokenHelpCopyToken"), t$1("wizard.telegram.tokenHelpWebApp", { url: "https://t.me/BotFather?startapp" }), t$1("wizard.telegram.tokenEnvTip"), t$1("wizard.channels.docs", { link: formatDocsLink("/telegram") }), t$1("wizard.telegram.website", { url: "https://openclaw.ai" }) ]; } function getTelegramUserIdHelpLines() { return [ t$1("wizard.telegram.userIdHelpLogs", { command: formatCliCommand("openclaw logs --follow") }), t$1("wizard.telegram.userIdHelpGetUpdates"), t$1("wizard.telegram.userIdHelpThirdParty"), t$1("wizard.channels.docs", { link: formatDocsLink("/telegram") }), t$1("wizard.telegram.website", { url: "https://openclaw.ai" }) ]; } function normalizeTelegramAllowFromInput(raw) { return raw.trim().replace(/^(telegram|tg):/i, "").trim(); } function parseTelegramAllowFromId(raw) { const stripped = normalizeTelegramAllowFromInput(raw); return isNumericTelegramSenderUserId(stripped) ? stripped : null; } async function promptTelegramAllowFromForAccount(params) { const accountId = params.accountId ?? resolveDefaultTelegramAccountId(params.cfg); const resolved = resolveTelegramAccount({ cfg: params.cfg, accountId }); await params.prompter.note(getTelegramUserIdHelpLines().join("\n"), t$1("wizard.telegram.userIdTitle")); const unique = await promptResolvedAllowFrom({ prompter: params.prompter, existing: resolved.config.allowFrom ?? [], message: t$1("wizard.telegram.allowFromPrompt"), placeholder: "123456789", label: t$1("wizard.telegram.allowlistTitle"), parseInputs: splitSetupEntries, parseId: parseTelegramAllowFromId, invalidWithoutTokenNote: t$1("wizard.telegram.allowFromInvalid"), resolveEntries: async ({ entries }) => entries.map((entry) => { const id = parseTelegramAllowFromId(entry); return { input: entry, resolved: Boolean(id), id }; }) }); return patchChannelConfigForAccount({ cfg: params.cfg, channel: channel$2, accountId, patch: { dmPolicy: "allowlist", allowFrom: unique }, setupSurface: telegramSetupAdapter }); } const telegramSetupAdapter = { ...createEnvPatchedAccountSetupAdapter({ channelKey: channel$2, defaultAccountOnlyEnvError: "TELEGRAM_BOT_TOKEN can only be used for the default account.", missingCredentialError: "Telegram requires token or --token-file (or --use-env).", hasCredentials: (input) => Boolean(input.token || input.tokenFile), buildPatch: (input) => input.tokenFile ? { tokenFile: input.tokenFile } : input.token ? { botToken: input.token } : {} }), singleAccountKeysToMove, namedAccountPromotionKeys }; const telegramSetupContract = defineChannelSetupContract({ fields: { token: { kind: "string", sensitive: true, cli: { flags: "--token <token>", description: "Telegram bot token" } }, tokenFile: { kind: "string", sensitive: true, cli: { flags: "--token-file <path>", description: "Telegram bot token file" } }, useEnv: { kind: "boolean", cli: { flags: "--use-env", description: "Use TELEGRAM_BOT_TOKEN" }, envVars: ["TELEGRAM_BOT_TOKEN"] } }, legacyAdapter: telegramSetupAdapter }); //#endregion //#region extensions/telegram/src/setup-surface.helpers.ts const channel$1 = "telegram"; function ensureTelegramDefaultGroupMentionGate(cfg, accountId) { const resolved = resolveTelegramAccount({ cfg, accountId }); const wildcardGroup = resolved.config.groups?.["*"]; if (wildcardGroup?.requireMention !== void 0) return cfg; return patchChannelConfigForAccount({ cfg, channel: channel$1, accountId, patch: { groups: { ...resolved.config.groups, "*": { ...wildcardGroup, requireMention: true } } }, setupSurface: telegramSetupAdapter }); } function shouldShowTelegramDmAccessWarning(cfg, accountId) { const merged = mergeTelegramAccountConfig(cfg, accountId); const policy = merged.dmPolicy ?? "pairing"; const hasAllowFrom = Array.isArray(merged.allowFrom) && merged.allowFrom.some((entry) => normalizeOptionalString(String(entry))); return policy === "pairing" && !hasAllowFrom; } function buildTelegramDmAccessWarningLines(accountId) { const configBase = accountId === "default" ? "channels.telegram" : `channels.telegram.accounts.${accountId}`; return [ "Your bot is using DM policy: pairing.", "Any Telegram user who discovers the bot can send pairing requests.", "For private use, configure an allowlist with your Telegram user id:", " " + formatCliCommand(`openclaw config set ${configBase}.dmPolicy "allowlist"`), " " + formatCliCommand(`openclaw config set ${configBase}.allowFrom '["YOUR_USER_ID"]'`), `Docs: ${formatDocsLink("/channels/pairing", "channels/pairing")}` ]; } const telegramSetupDmPolicy = createChannelDmPolicy({ label: "Telegram", channel: channel$1, resolveAccount: (cfg, accountId) => { const resolvedAccountId = accountId ?? resolveDefaultTelegramAccountId(cfg); return { accountId: resolvedAccountId, config: mergeTelegramAccountConfig(cfg, resolvedAccountId) }; }, applyPatch: ({ cfg, requestedAccountId, account, patch }) => requestedAccountId == null && account.accountId !== "default" ? applySetupAccountConfigPatch({ cfg, channelKey: channel$1, accountId: account.accountId, patch }) : patchChannelConfigForAccount({ cfg, channel: channel$1, accountId: account.accountId, patch, setupSurface: telegramSetupAdapter }), promptAllowFrom: promptTelegramAllowFromForAccount }); //#endregion //#region extensions/telegram/src/setup-surface.ts const t = createSetupTranslator(); const channel = "telegram"; const telegramSetupWizard = { channel, status: createStandardChannelSetupStatus({ channelLabel: "Telegram", configuredLabel: t("wizard.channels.statusConfigured"), unconfiguredLabel: t("wizard.channels.statusNeedsToken"), configuredHint: t("wizard.channels.statusRecommendedConfigured"), unconfiguredHint: t("wizard.channels.statusRecommendedNewcomerFriendly"), configuredScore: 1, unconfiguredScore: 10, resolveConfigured: ({ cfg, accountId }) => (accountId ? [accountId] : listTelegramAccountIds(cfg)).some((resolvedAccountId) => { return inspectTelegramAccount({ cfg, accountId: resolvedAccountId }).configured; }) }), prepare: async ({ cfg, accountId, credentialValues }) => ({ cfg: ensureTelegramDefaultGroupMentionGate(cfg, accountId), credentialValues }), credentials: [defineTokenCredential({ inputKey: "token", configKey: "botToken", configuredFields: ["botToken", "tokenFile"], providerHint: channel, credentialLabel: t("wizard.telegram.botToken"), preferredEnvVar: "TELEGRAM_BOT_TOKEN", helpTitle: t("wizard.telegram.botToken"), helpLines: getTelegramTokenHelpLines(), envPrompt: t("wizard.telegram.tokenEnvPrompt"), keepPrompt: t("wizard.telegram.tokenKeepPrompt"), inputPrompt: t("wizard.telegram.tokenInputPrompt"), allowEnv: ({ accountId }) => accountId === DEFAULT_ACCOUNT_ID, resolveAccount: ({ cfg, accountId }) => resolveTelegramAccount({ cfg, accountId }), hasConfiguredValue: (account) => hasConfiguredSecretInput(account.config.botToken) || Boolean(account.config.tokenFile?.trim()), resolvedValue: (account) => normalizeOptionalString(account.token), envValue: ({ accountId }) => accountId === "default" ? normalizeOptionalString(process.env.TELEGRAM_BOT_TOKEN) : void 0 })], allowFrom: createAllowFromSection({ helpTitle: t("wizard.telegram.userIdTitle"), helpLines: getTelegramUserIdHelpLines(), message: t("wizard.telegram.allowFromPrompt"), placeholder: "123456789", invalidWithoutCredentialNote: t("wizard.telegram.allowFromInvalid"), parseInputs: splitSetupEntries, parseId: parseTelegramAllowFromId, resolveEntries: async ({ entries }) => entries.map((entry) => { const id = parseTelegramAllowFromId(entry); return { input: entry, resolved: Boolean(id), id }; }), apply: async ({ cfg, accountId, allowFrom }) => patchChannelConfigForAccount({ cfg, channel, accountId, patch: { dmPolicy: "allowlist", allowFrom }, setupSurface: telegramSetupAdapter }) }), finalize: async ({ cfg, accountId, prompter }) => { if (!shouldShowTelegramDmAccessWarning(cfg, accountId)) return; await prompter.note(buildTelegramDmAccessWarningLines(accountId).join("\n"), "Telegram DM access warning"); }, dmPolicy: telegramSetupDmPolicy, disable: (cfg) => setSetupChannelEnabled(cfg, channel, false) }; //#endregion //#region extensions/telegram/src/setup-plugin.ts const TELEGRAM_CHANNEL = "telegram"; function createTelegramSetupPluginBase(params) { return { id: TELEGRAM_CHANNEL, setupContract: params.setupContract, meta: { ...getChatChannelMeta(TELEGRAM_CHANNEL), quickstartAllowFrom: true }, setupWizard: params.setupWizard, capabilities: { chatTypes: [ "direct", "group", "channel", "thread" ], reactions: true, threads: true, media: true, tts: { voice: { synthesisTarget: "voice-note", captionedFinalText: true } }, polls: true, nativeCommands: true, blockStreaming: true }, reload: { configPrefixes: ["channels.telegram"], noopPrefixes: ["messages.inbound", "messages.ackReactionScope"] }, configSchema: TelegramChannelConfigSchema, config: createTelegramPluginConfig(), secrets: { secretTargetRegistryEntries, collectRuntimeConfigAssignments } }; } //#endregion //#region extensions/telegram/src/channel.setup.ts const telegramSetupPlugin = { ...createTelegramSetupPluginBase({ setupWizard: telegramSetupWizard, setupContract: telegramSetupContract }) }; //#endregion export { resolveTelegramConfigAccessorAccount as a, telegramSetupContract as i, createTelegramSetupPluginBase as n, telegramConfigAdapter as o, telegramSetupWizard as r, telegramSetupPlugin as t };