UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

486 lines (485 loc) 19.6 kB
import { r as createLazyRuntimeModule } from "./lazy-runtime-CgCh8H_K.js"; import { r as logVerbose, t as danger } from "./globals-CTaGxEqj.js"; import "./channel-outbound-Dw9bfhFK.js"; import { y as resolveChannelStreamingBlockEnabled } from "./streaming-B_uLiLJZ.js"; import { i as PLUGIN_COMMAND_DISPATCH } from "./plugin-command-runtime-do7b4BK-.js"; import "./runtime-env-0Q-gCyUb.js"; import { i as isChannelPartialDeliveryError } from "./delivery-result-jWGDGh5u.js"; import { t as resolveMarkdownTableMode } from "./markdown-tables-BI-3mmWv.js"; import { h as resolveStorePath } from "./session-store-runtime-Btld46Zi.js"; import "./channel-inbound-DreWTphY.js"; import "./plugin-command-runtime-B63jfdvH.js"; import "./markdown-table-runtime-DksCEWwc.js"; import { t as resolveNativeCommandSessionTargets } from "./native-command-session-targets-QmDJndPx.js"; import "./command-auth-native-DJzZkbeX.js"; import { a as normalizeDmAllowFromWithStore, o as resolveTelegramEffectiveDmPolicy, t as expandTelegramAllowFromWithAccessGroups } from "./access-groups-BCvoVMVk.js"; import { o as resolveTelegramAccount } from "./accounts-CgIQ5mFD.js"; import { d as shouldSuppressLocalTelegramExecApprovalPrompt } from "./exec-approvals-CmtRI1wa.js"; import { D as buildSenderName, b as resolveTelegramMessageThreadSpec, c as buildTelegramThreadParams, d as extractTelegramForumFlag, g as resolveTelegramForumFlag, h as resolveTelegramCommandAuthorization, m as resolveTelegramBotHasTopicsEnabled, p as isTelegramCommandsAllowFromConfigured, r as buildTelegramGroupFrom, s as buildTelegramRoutingTarget, v as resolveTelegramGroupAllowFromContext } from "./helpers-C9zWfRKz.js"; import { a as resolveTelegramTargetSession, i as resolveTelegramConversationRoute, t as buildTelegramConversationRouteContext } from "./conversation-route-3KrSriUe.js"; import { K as resolveTelegramGroupPromptSettings, W as resolveTelegramDirectToolPolicy } from "./text-chunk-limit-C3EsGj6Q.js"; import { t as resolveTelegramAccountOwnerAgentId } from "./account-owner-7zwE7Wu9.js"; import { n as getTopicName, o as resolveTopicNameCacheScope } from "./topic-name-cache-0bursfP6.js"; import { mt as evaluateTelegramGroupPolicyAccess, pt as evaluateTelegramGroupBaseAccess } from "./send-BXHKghr-.js"; import { t as withTelegramApiErrorLogging } from "./api-logging-aN8dtVY1.js"; import { T as resolveTelegramCommandIngressAuthorization, n as resolveTelegramMessageTurnSettings } from "./bot-message-CEeR_5za.js"; import { t as defaultTelegramNativeCommandDeps } from "./bot-native-command-deps.runtime.js"; //#region extensions/telegram/src/bot-native-command-dispatch.ts const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again."; const loadTelegramNativeCommandDeliveryRuntime = createLazyRuntimeModule(() => import("./bot-native-commands.delivery.runtime.js")); const loadTelegramNativeCommandRuntime = createLazyRuntimeModule(() => import("./bot-native-commands.runtime.js")); async function resolveTelegramNativeCommandThreadContext(params) { const { msg, bot } = params; const chatId = msg.chat.id; const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup"; const getChat = typeof bot.api.getChat === "function" ? bot.api.getChat.bind(bot.api) : void 0; const isForum = msg.chat.is_direct_messages === true ? false : await resolveTelegramForumFlag({ chatId, chatType: msg.chat.type, isGroup, isForum: extractTelegramForumFlag(msg.chat), isTopicMessage: msg.is_topic_message, getChat }); const threadSpec = resolveTelegramMessageThreadSpec(msg, isForum); return { chatId, isGroup, isForum, threadSpec, threadParams: buildTelegramThreadParams(threadSpec) }; } async function resolveTelegramCommandAuth(params) { const { msg, bot, cfg, accountId, telegramCfg, requireAuth } = params; const { chatId, isGroup, isForum, threadSpec, threadParams } = await resolveTelegramNativeCommandThreadContext({ msg, bot }); const senderId = msg.from?.id ? String(msg.from.id) : ""; const senderUsername = msg.from?.username ?? ""; const commandsAllowFromConfigured = isTelegramCommandsAllowFromConfigured(cfg); const preContextCommandsAllowFromAccess = commandsAllowFromConfigured ? resolveTelegramCommandAuthorization({ cfg, accountId, chatId, isGroup, threadSpec, senderId, senderUsername }) : null; const { resolvedThreadId, dmThreadId, storeAllowFrom, groupConfig, topicConfig, groupAllowOverride, effectiveGroupAllow, hasGroupAllowOverride } = await resolveTelegramGroupAllowFromContext({ cfg, chatId, accountId, dmPolicy: telegramCfg.dmPolicy, allowFrom: params.allowFrom, senderId, isGroup, threadSpec, groupAllowFrom: params.groupAllowFrom, skipPairingStoreRead: Boolean(preContextCommandsAllowFromAccess?.isAuthorizedSender), readChannelAllowFromStore: params.readChannelAllowFromStore, resolveTelegramGroupConfig: params.resolveTelegramGroupConfig }); const effectiveDmPolicy = resolveTelegramEffectiveDmPolicy({ isGroup, groupConfig, dmPolicy: telegramCfg.dmPolicy }); const requireTopic = !isGroup && groupConfig && "requireTopic" in groupConfig ? groupConfig.requireTopic : void 0; if (!isGroup && requireTopic === true && dmThreadId == null) { logVerbose(`Blocked telegram command in DM ${chatId}: requireTopic=true but no topic present`); return null; } const commandsAllowFromAccess = commandsAllowFromConfigured ? resolveTelegramCommandAuthorization({ cfg, accountId, chatId, isGroup, threadSpec, senderId, senderUsername }) : null; const ownerAccess = resolveTelegramCommandAuthorization({ cfg, accountId, chatId, isGroup, threadSpec, senderId, senderUsername }); const sendAuthMessage = async (text) => { await withTelegramApiErrorLogging({ operation: "sendMessage", fn: () => bot.api.sendMessage(chatId, text, threadParams ?? {}) }); return null; }; const rejectNotAuthorized = async () => await sendAuthMessage("You are not authorized to use this command."); const baseAccess = evaluateTelegramGroupBaseAccess({ isGroup, groupConfig, topicConfig, hasGroupAllowOverride, effectiveGroupAllow, senderId, senderUsername, enforceAllowOverride: requireAuth, requireSenderForAllowOverride: true }); if (!baseAccess.allowed) { if (baseAccess.reason === "group-disabled") { logVerbose(`Blocked telegram command in group ${chatId} (group disabled)`); return null; } if (baseAccess.reason === "topic-disabled") { logVerbose(`Blocked telegram command in topic ${chatId} (${resolvedThreadId ?? "unknown"}) (topic disabled)`); return null; } return await rejectNotAuthorized(); } const policyAccess = evaluateTelegramGroupPolicyAccess({ isGroup, chatId, cfg, telegramCfg, topicConfig, groupConfig, effectiveGroupAllow, senderId, senderUsername, resolveGroupPolicy: params.resolveGroupPolicy, enforcePolicy: true, enforceAllowlistAuthorization: requireAuth && !commandsAllowFromConfigured, allowEmptyAllowlistEntries: true, requireSenderForAllowlistAuthorization: true, checkChatAllowlist: true }); if (!policyAccess.allowed) { if (policyAccess.reason === "group-policy-disabled") { logVerbose("Blocked telegram command (groupPolicy: disabled)"); return null; } if (policyAccess.reason === "group-policy-allowlist-no-sender" || policyAccess.reason === "group-policy-allowlist-unauthorized") return await rejectNotAuthorized(); if (policyAccess.reason === "group-chat-not-allowed") { logVerbose(`Blocked telegram command in group ${chatId} (group not allowed)`); return null; } } const expandedDmAllowFrom = await expandTelegramAllowFromWithAccessGroups({ cfg, allowFrom: groupAllowOverride ?? params.allowFrom, accountId, senderId }); const dmAllow = normalizeDmAllowFromWithStore({ allowFrom: expandedDmAllowFrom, storeAllowFrom: isGroup ? [] : storeAllowFrom, dmPolicy: effectiveDmPolicy }); const commandAuthorized = commandsAllowFromConfigured ? Boolean(commandsAllowFromAccess?.isAuthorizedSender) : (await resolveTelegramCommandIngressAuthorization({ accountId, cfg, dmPolicy: effectiveDmPolicy, isGroup, chatId, resolvedThreadId, senderId, effectiveDmAllow: dmAllow, effectiveGroupAllow, ownerAccess, eventKind: "native-command" })).authorized; if (requireAuth && !commandAuthorized) return await rejectNotAuthorized(); return { chatId, isGroup, isForum, resolvedThreadId, senderId, senderUsername, groupConfig, topicConfig, threadSpec, commandAuthorized, senderIsOwner: ownerAccess.senderIsOwner }; } async function prepareTelegramCommandDispatch(params) { const telegramDeps = params.telegramDeps ?? defaultTelegramNativeCommandDeps; const runtimeCfg = telegramDeps.getRuntimeConfig(); const runtimeTelegramCfg = resolveTelegramAccount({ cfg: runtimeCfg, accountId: params.accountId }).config; const turnSettings = resolveTelegramMessageTurnSettings({ accountId: params.accountId, cfg: runtimeCfg, telegramCfg: runtimeTelegramCfg, opts: params.opts }); const auth = await resolveTelegramCommandAuth({ msg: params.msg, bot: params.bot, cfg: runtimeCfg, accountId: params.accountId, telegramCfg: runtimeTelegramCfg, readChannelAllowFromStore: telegramDeps.readChannelAllowFromStore, allowFrom: turnSettings.allowFrom, groupAllowFrom: turnSettings.groupAllowFrom, resolveGroupPolicy: params.resolveGroupPolicy, resolveTelegramGroupConfig: params.resolveTelegramGroupConfig, requireAuth: params.requireAuth }); if (!auth) return null; const { route, bindingMode } = resolveTelegramConversationRoute({ cfg: runtimeCfg, accountId: params.accountId, chatId: auth.chatId, isGroup: auth.isGroup, threadSpec: auth.threadSpec, senderId: auth.senderId, topicAgentId: auth.topicConfig?.agentId }); const nativeCommandRuntime = await loadTelegramNativeCommandRuntime(); if (bindingMode.kind === "configured") { const ensured = await nativeCommandRuntime.ensureConfiguredBindingRouteReady({ cfg: runtimeCfg, bindingResolution: bindingMode.binding }); if (!ensured.ok) { logVerbose(`telegram native command: configured ACP binding unavailable for topic ${bindingMode.binding.record.conversation.conversationId}: ${ensured.error}`); await withTelegramApiErrorLogging({ operation: "sendMessage", runtime: params.runtime, fn: () => params.bot.api.sendMessage(auth.chatId, "Configured ACP binding is unavailable right now. Please try again.", buildTelegramThreadParams(auth.threadSpec) ?? {}) }); return null; } } const mediaLocalRoots = nativeCommandRuntime.getAgentScopedMediaLocalRoots(runtimeCfg, route.agentId); const tableMode = resolveMarkdownTableMode({ cfg: runtimeCfg, channel: "telegram", accountId: route.accountId, supportsBlockTables: true }); const chunkMode = nativeCommandRuntime.resolveChunkMode(runtimeCfg, "telegram", route.accountId); const targetSessionKey = resolveTelegramTargetSession({ cfg: runtimeCfg, route, chatId: auth.chatId, isGroup: auth.isGroup, senderId: auth.senderId, dmThreadId: auth.threadSpec.scope === "dm" ? auth.threadSpec.id : void 0, botHasTopicsEnabled: resolveTelegramBotHasTopicsEnabled(params.botUser) }); const buildDeliveryBaseOptions = (keys) => ({ cfg: runtimeCfg, ownerAgentId: params.opts.ownerAgentId, chatId: String(auth.chatId), accountId: route.accountId, sessionKeyForInternalHooks: keys?.sessionKeyForInternalHooks, policySessionKey: keys?.policySessionKey, mirrorIsGroup: auth.isGroup, mirrorGroupId: auth.isGroup ? String(auth.chatId) : void 0, token: params.opts.token, runtime: params.runtime, bot: params.bot, mediaLocalRoots, mediaMaxBytes: params.mediaMaxBytes, replyToMode: turnSettings.replyToMode, textLimit: turnSettings.textLimit, thread: auth.threadSpec, tableMode, chunkMode, linkPreview: runtimeTelegramCfg.linkPreview, richMessages: runtimeTelegramCfg.richMessages }); return { ...params, telegramDeps, runtimeCfg, runtimeTelegramCfg, turnSettings, ...auth, threadSpec: auth.threadSpec, threadParams: buildTelegramThreadParams(auth.threadSpec), route, mediaLocalRoots, targetSessionKey, nativeCommandRuntime, buildDeliveryBaseOptions, loadDeliveryRuntime: loadTelegramNativeCommandDeliveryRuntime }; } async function dispatchTelegramBuiltinTurn(params) { const { dispatch } = params; const { skillFilter, groupSystemPrompt } = resolveTelegramGroupPromptSettings({ groupConfig: dispatch.groupConfig, topicConfig: dispatch.topicConfig }); const { sessionKey: commandSessionKey, commandTargetSessionKey } = resolveNativeCommandSessionTargets({ agentId: dispatch.route.agentId, sessionPrefix: "telegram:slash", userId: String(dispatch.senderId || dispatch.chatId), targetSessionKey: dispatch.targetSessionKey }); let topicName; if (dispatch.isForum && dispatch.resolvedThreadId != null) try { const storePath = resolveStorePath(dispatch.runtimeCfg.session?.store, { agentId: dispatch.opts.ownerAgentId ?? resolveTelegramAccountOwnerAgentId({ cfg: dispatch.runtimeCfg, accountId: dispatch.route.accountId }) }); topicName = await getTopicName(dispatch.chatId, dispatch.resolvedThreadId, resolveTopicNameCacheScope(storePath)); } catch {} const conversationLabel = dispatch.isGroup ? dispatch.msg.chat.title ? `${dispatch.msg.chat.title} id:${dispatch.chatId}` : `group:${dispatch.chatId}` : buildSenderName(dispatch.msg) ?? String(dispatch.senderId || dispatch.chatId); const ctxPayload = dispatch.nativeCommandRuntime.finalizeInboundContext({ Body: params.prompt, BodyForAgent: params.prompt, RawBody: params.prompt, CommandBody: params.prompt, CommandArgs: params.commandArgs, From: dispatch.isGroup ? buildTelegramGroupFrom(dispatch.chatId, dispatch.threadSpec) : `telegram:${dispatch.chatId}`, To: `slash:${dispatch.senderId || dispatch.chatId}`, ChatType: dispatch.isGroup ? "group" : "direct", ...buildTelegramConversationRouteContext(dispatch), ConversationToolPolicy: dispatch.isGroup ? void 0 : resolveTelegramDirectToolPolicy({ directConfig: dispatch.groupConfig, senderId: dispatch.senderId, senderName: buildSenderName(dispatch.msg), senderUsername: dispatch.senderUsername }), ConversationLabel: conversationLabel, GroupSubject: dispatch.isGroup ? dispatch.msg.chat.title ?? void 0 : void 0, GroupSystemPrompt: dispatch.isGroup || !dispatch.isGroup && dispatch.groupConfig ? groupSystemPrompt : void 0, SenderName: buildSenderName(dispatch.msg), SenderId: dispatch.senderId || void 0, SenderUsername: dispatch.senderUsername || void 0, Surface: "telegram", Provider: "telegram", MessageSid: String(dispatch.msg.message_id), Timestamp: dispatch.msg.date ? dispatch.msg.date * 1e3 : void 0, WasMentioned: true, CommandAuthorized: dispatch.commandAuthorized, CommandTurn: { kind: "native", source: "native", authorized: dispatch.commandAuthorized, body: params.prompt }, CommandSource: "native", SessionKey: commandSessionKey, AccountId: dispatch.route.accountId, CommandTargetSessionKey: commandTargetSessionKey, MessageThreadId: dispatch.threadSpec.id, IsForum: dispatch.isForum, TopicName: dispatch.isForum && topicName ? topicName : void 0, OriginatingChannel: "telegram", OriginatingTo: buildTelegramRoutingTarget(dispatch.chatId, dispatch.threadSpec) }); const deliveryState = { delivered: false, skippedNonSilent: 0, failedNonSilent: 0 }; let finalReplyOutcome; let recordSessionMetaTask; const deliveryBaseOptions = dispatch.buildDeliveryBaseOptions({ sessionKeyForInternalHooks: commandSessionKey, policySessionKey: commandTargetSessionKey }); const { deliverReplies } = await dispatch.loadDeliveryRuntime(); const turnPlan = { cfg: dispatch.runtimeCfg, channel: "telegram", accountId: dispatch.route.accountId, route: { agentId: dispatch.route.agentId, sessionKey: commandSessionKey }, ctxPayload, dispatchReplyFromConfig: dispatch.opts.dispatchReplyFromConfig, record: { sessionKey: commandTargetSessionKey, trackSessionMetaTask: (task) => { recordSessionMetaTask = task; }, onRecordError: (error) => dispatch.runtime.error?.(danger(`telegram slash: failed updating session meta: ${String(error)}`)) }, afterRecord: async () => { await recordSessionMetaTask; }, replyPipeline: {}, dispatcherOptions: { beforeDeliver: async (payload) => payload, onSkip: (_payload, info) => { if (info.reason !== "silent") deliveryState.skippedNonSilent += 1; } }, delivery: { deliverWithProviderMessageSending: async (payload, info) => { if (shouldSuppressLocalTelegramExecApprovalPrompt({ cfg: dispatch.runtimeCfg, accountId: dispatch.route.accountId, payload })) { deliveryState.delivered = true; return { visibleReplySent: false, suppression: { reason: "no_visible_result" } }; } const targetedPayload = payload.replyToId ? payload : { ...payload, replyToId: String(dispatch.msg.message_id) }; const result = await deliverReplies({ replies: [info.bindPendingFinalDelivery ? info.bindPendingFinalDelivery(targetedPayload) : targetedPayload], ...deliveryBaseOptions, silent: dispatch.runtimeTelegramCfg.silentErrorReplies === true && payload.isError === true, onPlatformSendDispatch: info.onPlatformSendDispatch, assertPlatformSendAuthorized: info.assertPlatformSendAuthorized }); if (result.delivered) deliveryState.delivered = true; return result.delivered ? { visibleReplySent: true } : { visibleReplySent: false, suppression: { reason: "no_visible_result" } }; }, onDelivered: (_payload, info, result) => { const reason = result?.suppression?.reason; if (info.kind === "final" && result?.visibleReplySent) finalReplyOutcome = "accepted"; if (info.kind === "final" && finalReplyOutcome !== "failed" && (reason === "cancelled_by_reply_payload_sending_hook" || reason === "empty_after_reply_payload_sending_hook")) finalReplyOutcome = "suppressed"; }, onError: (error, info) => { deliveryState.failedNonSilent += 1; const partialDelivery = isChannelPartialDeliveryError(error); if (partialDelivery) { deliveryState.delivered = true; logVerbose("telegram slash reply partially delivered before failure"); } if (info.kind === "final") finalReplyOutcome = partialDelivery ? "accepted" : "failed"; dispatch.runtime.error?.(danger(`telegram slash ${info.kind} reply failed: ${String(error)}`)); } }, replyOptions: { skillFilter, disableBlockStreaming: (() => { const enabled = resolveChannelStreamingBlockEnabled(dispatch.runtimeTelegramCfg); return typeof enabled === "boolean" ? !enabled : void 0; })(), [PLUGIN_COMMAND_DISPATCH]: { kind: "non-plugin" } } }; const turnResult = await (dispatch.telegramDeps.dispatchChannelInboundTurn ?? defaultTelegramNativeCommandDeps.dispatchChannelInboundTurn)(turnPlan); if (!deliveryState.delivered && finalReplyOutcome !== "suppressed" && (deliveryState.skippedNonSilent > 0 || deliveryState.failedNonSilent > 0) && (!turnResult.dispatched || turnResult.dispatchResult.sourceReplyDeliveryMode !== "message_tool_only" || deliveryState.failedNonSilent > 0)) await deliverReplies({ replies: [{ text: EMPTY_RESPONSE_FALLBACK }], ...deliveryBaseOptions }); return false; } //#endregion export { prepareTelegramCommandDispatch as n, dispatchTelegramBuiltinTurn as t };