Spaces:
Paused
Paused
| import type { Bot, Context } from "grammy"; | |
| import type { CommandArgs } from "../auto-reply/commands-registry.js"; | |
| import type { OpenClawConfig } from "../config/config.js"; | |
| import type { ChannelGroupPolicy } from "../config/group-policy.js"; | |
| import type { | |
| ReplyToMode, | |
| TelegramAccountConfig, | |
| TelegramGroupConfig, | |
| TelegramTopicConfig, | |
| } from "../config/types.js"; | |
| import type { RuntimeEnv } from "../runtime.js"; | |
| import { resolveEffectiveMessagesConfig } from "../agents/identity.js"; | |
| import { resolveChunkMode } from "../auto-reply/chunk.js"; | |
| import { | |
| buildCommandTextFromArgs, | |
| findCommandByNativeName, | |
| listNativeCommandSpecs, | |
| listNativeCommandSpecsForConfig, | |
| parseCommandArgs, | |
| resolveCommandArgMenu, | |
| } from "../auto-reply/commands-registry.js"; | |
| import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js"; | |
| import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js"; | |
| import { listSkillCommandsForAgents } from "../auto-reply/skill-commands.js"; | |
| import { resolveCommandAuthorizedFromAuthorizers } from "../channels/command-gating.js"; | |
| import { resolveMarkdownTableMode } from "../config/markdown-tables.js"; | |
| import { resolveTelegramCustomCommands } from "../config/telegram-custom-commands.js"; | |
| import { | |
| normalizeTelegramCommandName, | |
| TELEGRAM_COMMAND_NAME_PATTERN, | |
| } from "../config/telegram-custom-commands.js"; | |
| import { danger, logVerbose } from "../globals.js"; | |
| import { getChildLogger } from "../logging.js"; | |
| import { readChannelAllowFromStore } from "../pairing/pairing-store.js"; | |
| import { | |
| executePluginCommand, | |
| getPluginCommandSpecs, | |
| matchPluginCommand, | |
| } from "../plugins/commands.js"; | |
| import { resolveAgentRoute } from "../routing/resolve-route.js"; | |
| import { resolveThreadSessionKeys } from "../routing/session-key.js"; | |
| import { withTelegramApiErrorLogging } from "./api-logging.js"; | |
| import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js"; | |
| import { TelegramUpdateKeyContext } from "./bot-updates.js"; | |
| import { TelegramBotOptions } from "./bot.js"; | |
| import { deliverReplies } from "./bot/delivery.js"; | |
| import { | |
| buildSenderName, | |
| buildTelegramGroupFrom, | |
| buildTelegramGroupPeerId, | |
| resolveTelegramForumThreadId, | |
| } from "./bot/helpers.js"; | |
| import { buildInlineKeyboard } from "./send.js"; | |
| const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again."; | |
| type TelegramNativeCommandContext = Context & { match?: string }; | |
| type TelegramCommandAuthResult = { | |
| chatId: number; | |
| isGroup: boolean; | |
| isForum: boolean; | |
| resolvedThreadId?: number; | |
| senderId: string; | |
| senderUsername: string; | |
| groupConfig?: TelegramGroupConfig; | |
| topicConfig?: TelegramTopicConfig; | |
| commandAuthorized: boolean; | |
| }; | |
| export type RegisterTelegramHandlerParams = { | |
| cfg: OpenClawConfig; | |
| accountId: string; | |
| bot: Bot; | |
| mediaMaxBytes: number; | |
| opts: TelegramBotOptions; | |
| runtime: RuntimeEnv; | |
| telegramCfg: TelegramAccountConfig; | |
| groupAllowFrom?: Array<string | number>; | |
| resolveGroupPolicy: (chatId: string | number) => ChannelGroupPolicy; | |
| resolveTelegramGroupConfig: ( | |
| chatId: string | number, | |
| messageThreadId?: number, | |
| ) => { groupConfig?: TelegramGroupConfig; topicConfig?: TelegramTopicConfig }; | |
| shouldSkipUpdate: (ctx: TelegramUpdateKeyContext) => boolean; | |
| processMessage: ( | |
| ctx: unknown, | |
| allMedia: Array<{ path: string; contentType?: string }>, | |
| storeAllowFrom: string[], | |
| options?: { | |
| messageIdOverride?: string; | |
| forceWasMentioned?: boolean; | |
| }, | |
| ) => Promise<void>; | |
| logger: ReturnType<typeof getChildLogger>; | |
| }; | |
| type RegisterTelegramNativeCommandsParams = { | |
| bot: Bot; | |
| cfg: OpenClawConfig; | |
| runtime: RuntimeEnv; | |
| accountId: string; | |
| telegramCfg: TelegramAccountConfig; | |
| allowFrom?: Array<string | number>; | |
| groupAllowFrom?: Array<string | number>; | |
| replyToMode: ReplyToMode; | |
| textLimit: number; | |
| useAccessGroups: boolean; | |
| nativeEnabled: boolean; | |
| nativeSkillsEnabled: boolean; | |
| nativeDisabledExplicit: boolean; | |
| resolveGroupPolicy: (chatId: string | number) => ChannelGroupPolicy; | |
| resolveTelegramGroupConfig: ( | |
| chatId: string | number, | |
| messageThreadId?: number, | |
| ) => { groupConfig?: TelegramGroupConfig; topicConfig?: TelegramTopicConfig }; | |
| shouldSkipUpdate: (ctx: TelegramUpdateKeyContext) => boolean; | |
| opts: { token: string }; | |
| }; | |
| async function resolveTelegramCommandAuth(params: { | |
| msg: NonNullable<TelegramNativeCommandContext["message"]>; | |
| bot: Bot; | |
| cfg: OpenClawConfig; | |
| telegramCfg: TelegramAccountConfig; | |
| allowFrom?: Array<string | number>; | |
| groupAllowFrom?: Array<string | number>; | |
| useAccessGroups: boolean; | |
| resolveGroupPolicy: (chatId: string | number) => ChannelGroupPolicy; | |
| resolveTelegramGroupConfig: ( | |
| chatId: string | number, | |
| messageThreadId?: number, | |
| ) => { groupConfig?: TelegramGroupConfig; topicConfig?: TelegramTopicConfig }; | |
| requireAuth: boolean; | |
| }): Promise<TelegramCommandAuthResult | null> { | |
| const { | |
| msg, | |
| bot, | |
| cfg, | |
| telegramCfg, | |
| allowFrom, | |
| groupAllowFrom, | |
| useAccessGroups, | |
| resolveGroupPolicy, | |
| resolveTelegramGroupConfig, | |
| requireAuth, | |
| } = params; | |
| const chatId = msg.chat.id; | |
| const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup"; | |
| const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id; | |
| const isForum = (msg.chat as { is_forum?: boolean }).is_forum === true; | |
| const resolvedThreadId = resolveTelegramForumThreadId({ | |
| isForum, | |
| messageThreadId, | |
| }); | |
| const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []); | |
| const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId); | |
| const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom); | |
| const effectiveGroupAllow = normalizeAllowFromWithStore({ | |
| allowFrom: groupAllowOverride ?? groupAllowFrom, | |
| storeAllowFrom, | |
| }); | |
| const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined"; | |
| const senderIdRaw = msg.from?.id; | |
| const senderId = senderIdRaw ? String(senderIdRaw) : ""; | |
| const senderUsername = msg.from?.username ?? ""; | |
| if (isGroup && groupConfig?.enabled === false) { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| fn: () => bot.api.sendMessage(chatId, "This group is disabled."), | |
| }); | |
| return null; | |
| } | |
| if (isGroup && topicConfig?.enabled === false) { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| fn: () => bot.api.sendMessage(chatId, "This topic is disabled."), | |
| }); | |
| return null; | |
| } | |
| if (requireAuth && isGroup && hasGroupAllowOverride) { | |
| if ( | |
| senderIdRaw == null || | |
| !isSenderAllowed({ | |
| allow: effectiveGroupAllow, | |
| senderId: String(senderIdRaw), | |
| senderUsername, | |
| }) | |
| ) { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."), | |
| }); | |
| return null; | |
| } | |
| } | |
| if (isGroup && useAccessGroups) { | |
| const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; | |
| const groupPolicy = telegramCfg.groupPolicy ?? defaultGroupPolicy ?? "open"; | |
| if (groupPolicy === "disabled") { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| fn: () => bot.api.sendMessage(chatId, "Telegram group commands are disabled."), | |
| }); | |
| return null; | |
| } | |
| if (groupPolicy === "allowlist" && requireAuth) { | |
| if ( | |
| senderIdRaw == null || | |
| !isSenderAllowed({ | |
| allow: effectiveGroupAllow, | |
| senderId: String(senderIdRaw), | |
| senderUsername, | |
| }) | |
| ) { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."), | |
| }); | |
| return null; | |
| } | |
| } | |
| const groupAllowlist = resolveGroupPolicy(chatId); | |
| if (groupAllowlist.allowlistEnabled && !groupAllowlist.allowed) { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| fn: () => bot.api.sendMessage(chatId, "This group is not allowed."), | |
| }); | |
| return null; | |
| } | |
| } | |
| const dmAllow = normalizeAllowFromWithStore({ | |
| allowFrom: allowFrom, | |
| storeAllowFrom, | |
| }); | |
| const senderAllowed = isSenderAllowed({ | |
| allow: dmAllow, | |
| senderId, | |
| senderUsername, | |
| }); | |
| const commandAuthorized = resolveCommandAuthorizedFromAuthorizers({ | |
| useAccessGroups, | |
| authorizers: [{ configured: dmAllow.hasEntries, allowed: senderAllowed }], | |
| modeWhenAccessGroupsOff: "configured", | |
| }); | |
| if (requireAuth && !commandAuthorized) { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| fn: () => bot.api.sendMessage(chatId, "You are not authorized to use this command."), | |
| }); | |
| return null; | |
| } | |
| return { | |
| chatId, | |
| isGroup, | |
| isForum, | |
| resolvedThreadId, | |
| senderId, | |
| senderUsername, | |
| groupConfig, | |
| topicConfig, | |
| commandAuthorized, | |
| }; | |
| } | |
| export const registerTelegramNativeCommands = ({ | |
| bot, | |
| cfg, | |
| runtime, | |
| accountId, | |
| telegramCfg, | |
| allowFrom, | |
| groupAllowFrom, | |
| replyToMode, | |
| textLimit, | |
| useAccessGroups, | |
| nativeEnabled, | |
| nativeSkillsEnabled, | |
| nativeDisabledExplicit, | |
| resolveGroupPolicy, | |
| resolveTelegramGroupConfig, | |
| shouldSkipUpdate, | |
| opts, | |
| }: RegisterTelegramNativeCommandsParams) => { | |
| const boundRoute = | |
| nativeEnabled && nativeSkillsEnabled | |
| ? resolveAgentRoute({ cfg, channel: "telegram", accountId }) | |
| : null; | |
| const boundAgentIds = | |
| boundRoute && boundRoute.matchedBy.startsWith("binding.") ? [boundRoute.agentId] : null; | |
| const skillCommands = | |
| nativeEnabled && nativeSkillsEnabled | |
| ? listSkillCommandsForAgents(boundAgentIds ? { cfg, agentIds: boundAgentIds } : { cfg }) | |
| : []; | |
| const nativeCommands = nativeEnabled | |
| ? listNativeCommandSpecsForConfig(cfg, { | |
| skillCommands, | |
| provider: "telegram", | |
| }) | |
| : []; | |
| const reservedCommands = new Set( | |
| listNativeCommandSpecs().map((command) => command.name.toLowerCase()), | |
| ); | |
| for (const command of skillCommands) { | |
| reservedCommands.add(command.name.toLowerCase()); | |
| } | |
| const customResolution = resolveTelegramCustomCommands({ | |
| commands: telegramCfg.customCommands, | |
| reservedCommands, | |
| }); | |
| for (const issue of customResolution.issues) { | |
| runtime.error?.(danger(issue.message)); | |
| } | |
| const customCommands = customResolution.commands; | |
| const pluginCommandSpecs = getPluginCommandSpecs(); | |
| const pluginCommands: Array<{ command: string; description: string }> = []; | |
| const existingCommands = new Set( | |
| [ | |
| ...nativeCommands.map((command) => command.name), | |
| ...customCommands.map((command) => command.command), | |
| ].map((command) => command.toLowerCase()), | |
| ); | |
| const pluginCommandNames = new Set<string>(); | |
| for (const spec of pluginCommandSpecs) { | |
| const normalized = normalizeTelegramCommandName(spec.name); | |
| if (!normalized || !TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) { | |
| runtime.error?.( | |
| danger( | |
| `Plugin command "/${spec.name}" is invalid for Telegram (use a-z, 0-9, underscore; max 32 chars).`, | |
| ), | |
| ); | |
| continue; | |
| } | |
| const description = spec.description.trim(); | |
| if (!description) { | |
| runtime.error?.(danger(`Plugin command "/${normalized}" is missing a description.`)); | |
| continue; | |
| } | |
| if (existingCommands.has(normalized)) { | |
| runtime.error?.( | |
| danger(`Plugin command "/${normalized}" conflicts with an existing Telegram command.`), | |
| ); | |
| continue; | |
| } | |
| if (pluginCommandNames.has(normalized)) { | |
| runtime.error?.(danger(`Plugin command "/${normalized}" is duplicated.`)); | |
| continue; | |
| } | |
| pluginCommandNames.add(normalized); | |
| existingCommands.add(normalized); | |
| pluginCommands.push({ command: normalized, description }); | |
| } | |
| const allCommands: Array<{ command: string; description: string }> = [ | |
| ...nativeCommands.map((command) => ({ | |
| command: command.name, | |
| description: command.description, | |
| })), | |
| ...pluginCommands, | |
| ...customCommands, | |
| ]; | |
| if (allCommands.length > 0) { | |
| withTelegramApiErrorLogging({ | |
| operation: "setMyCommands", | |
| runtime, | |
| fn: () => bot.api.setMyCommands(allCommands), | |
| }).catch(() => {}); | |
| if (typeof (bot as unknown as { command?: unknown }).command !== "function") { | |
| logVerbose("telegram: bot.command unavailable; skipping native handlers"); | |
| } else { | |
| for (const command of nativeCommands) { | |
| bot.command(command.name, async (ctx: TelegramNativeCommandContext) => { | |
| const msg = ctx.message; | |
| if (!msg) { | |
| return; | |
| } | |
| if (shouldSkipUpdate(ctx)) { | |
| return; | |
| } | |
| const auth = await resolveTelegramCommandAuth({ | |
| msg, | |
| bot, | |
| cfg, | |
| telegramCfg, | |
| allowFrom, | |
| groupAllowFrom, | |
| useAccessGroups, | |
| resolveGroupPolicy, | |
| resolveTelegramGroupConfig, | |
| requireAuth: true, | |
| }); | |
| if (!auth) { | |
| return; | |
| } | |
| const { | |
| chatId, | |
| isGroup, | |
| isForum, | |
| resolvedThreadId, | |
| senderId, | |
| senderUsername, | |
| groupConfig, | |
| topicConfig, | |
| commandAuthorized, | |
| } = auth; | |
| const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id; | |
| const threadIdForSend = isGroup ? resolvedThreadId : messageThreadId; | |
| const commandDefinition = findCommandByNativeName(command.name, "telegram"); | |
| const rawText = ctx.match?.trim() ?? ""; | |
| const commandArgs = commandDefinition | |
| ? parseCommandArgs(commandDefinition, rawText) | |
| : rawText | |
| ? ({ raw: rawText } satisfies CommandArgs) | |
| : undefined; | |
| const prompt = commandDefinition | |
| ? buildCommandTextFromArgs(commandDefinition, commandArgs) | |
| : rawText | |
| ? `/${command.name} ${rawText}` | |
| : `/${command.name}`; | |
| const menu = commandDefinition | |
| ? resolveCommandArgMenu({ | |
| command: commandDefinition, | |
| args: commandArgs, | |
| cfg, | |
| }) | |
| : null; | |
| if (menu && commandDefinition) { | |
| const title = | |
| menu.title ?? | |
| `Choose ${menu.arg.description || menu.arg.name} for /${commandDefinition.nativeName ?? commandDefinition.key}.`; | |
| const rows: Array<Array<{ text: string; callback_data: string }>> = []; | |
| for (let i = 0; i < menu.choices.length; i += 2) { | |
| const slice = menu.choices.slice(i, i + 2); | |
| rows.push( | |
| slice.map((choice) => { | |
| const args: CommandArgs = { | |
| values: { [menu.arg.name]: choice.value }, | |
| }; | |
| return { | |
| text: choice.label, | |
| callback_data: buildCommandTextFromArgs(commandDefinition, args), | |
| }; | |
| }), | |
| ); | |
| } | |
| const replyMarkup = buildInlineKeyboard(rows); | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| runtime, | |
| fn: () => | |
| bot.api.sendMessage(chatId, title, { | |
| ...(replyMarkup ? { reply_markup: replyMarkup } : {}), | |
| ...(threadIdForSend != null ? { message_thread_id: threadIdForSend } : {}), | |
| }), | |
| }); | |
| return; | |
| } | |
| const route = resolveAgentRoute({ | |
| cfg, | |
| channel: "telegram", | |
| accountId, | |
| peer: { | |
| kind: isGroup ? "group" : "dm", | |
| id: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId), | |
| }, | |
| }); | |
| const baseSessionKey = route.sessionKey; | |
| // DMs: use raw messageThreadId for thread sessions (not resolvedThreadId which is for forums) | |
| const dmThreadId = !isGroup ? messageThreadId : undefined; | |
| const threadKeys = | |
| dmThreadId != null | |
| ? resolveThreadSessionKeys({ | |
| baseSessionKey, | |
| threadId: String(dmThreadId), | |
| }) | |
| : null; | |
| const sessionKey = threadKeys?.sessionKey ?? baseSessionKey; | |
| const tableMode = resolveMarkdownTableMode({ | |
| cfg, | |
| channel: "telegram", | |
| accountId: route.accountId, | |
| }); | |
| const skillFilter = firstDefined(topicConfig?.skills, groupConfig?.skills); | |
| const systemPromptParts = [ | |
| groupConfig?.systemPrompt?.trim() || null, | |
| topicConfig?.systemPrompt?.trim() || null, | |
| ].filter((entry): entry is string => Boolean(entry)); | |
| const groupSystemPrompt = | |
| systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined; | |
| const conversationLabel = isGroup | |
| ? msg.chat.title | |
| ? `${msg.chat.title} id:${chatId}` | |
| : `group:${chatId}` | |
| : (buildSenderName(msg) ?? String(senderId || chatId)); | |
| const ctxPayload = finalizeInboundContext({ | |
| Body: prompt, | |
| RawBody: prompt, | |
| CommandBody: prompt, | |
| CommandArgs: commandArgs, | |
| From: isGroup ? buildTelegramGroupFrom(chatId, resolvedThreadId) : `telegram:${chatId}`, | |
| To: `slash:${senderId || chatId}`, | |
| ChatType: isGroup ? "group" : "direct", | |
| ConversationLabel: conversationLabel, | |
| GroupSubject: isGroup ? (msg.chat.title ?? undefined) : undefined, | |
| GroupSystemPrompt: isGroup ? groupSystemPrompt : undefined, | |
| SenderName: buildSenderName(msg), | |
| SenderId: senderId || undefined, | |
| SenderUsername: senderUsername || undefined, | |
| Surface: "telegram", | |
| MessageSid: String(msg.message_id), | |
| Timestamp: msg.date ? msg.date * 1000 : undefined, | |
| WasMentioned: true, | |
| CommandAuthorized: commandAuthorized, | |
| CommandSource: "native" as const, | |
| SessionKey: `telegram:slash:${senderId || chatId}`, | |
| AccountId: route.accountId, | |
| CommandTargetSessionKey: sessionKey, | |
| MessageThreadId: threadIdForSend, | |
| IsForum: isForum, | |
| // Originating context for sub-agent announce routing | |
| OriginatingChannel: "telegram" as const, | |
| OriginatingTo: `telegram:${chatId}`, | |
| }); | |
| const disableBlockStreaming = | |
| typeof telegramCfg.blockStreaming === "boolean" | |
| ? !telegramCfg.blockStreaming | |
| : undefined; | |
| const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId); | |
| const deliveryState = { | |
| delivered: false, | |
| skippedNonSilent: 0, | |
| }; | |
| await dispatchReplyWithBufferedBlockDispatcher({ | |
| ctx: ctxPayload, | |
| cfg, | |
| dispatcherOptions: { | |
| responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix, | |
| deliver: async (payload, _info) => { | |
| const result = await deliverReplies({ | |
| replies: [payload], | |
| chatId: String(chatId), | |
| token: opts.token, | |
| runtime, | |
| bot, | |
| replyToMode, | |
| textLimit, | |
| messageThreadId: threadIdForSend, | |
| tableMode, | |
| chunkMode, | |
| linkPreview: telegramCfg.linkPreview, | |
| }); | |
| if (result.delivered) { | |
| deliveryState.delivered = true; | |
| } | |
| }, | |
| onSkip: (_payload, info) => { | |
| if (info.reason !== "silent") { | |
| deliveryState.skippedNonSilent += 1; | |
| } | |
| }, | |
| onError: (err, info) => { | |
| runtime.error?.(danger(`telegram slash ${info.kind} reply failed: ${String(err)}`)); | |
| }, | |
| }, | |
| replyOptions: { | |
| skillFilter, | |
| disableBlockStreaming, | |
| }, | |
| }); | |
| if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) { | |
| await deliverReplies({ | |
| replies: [{ text: EMPTY_RESPONSE_FALLBACK }], | |
| chatId: String(chatId), | |
| token: opts.token, | |
| runtime, | |
| bot, | |
| replyToMode, | |
| textLimit, | |
| messageThreadId: threadIdForSend, | |
| tableMode, | |
| chunkMode, | |
| linkPreview: telegramCfg.linkPreview, | |
| }); | |
| } | |
| }); | |
| } | |
| for (const pluginCommand of pluginCommands) { | |
| bot.command(pluginCommand.command, async (ctx: TelegramNativeCommandContext) => { | |
| const msg = ctx.message; | |
| if (!msg) { | |
| return; | |
| } | |
| if (shouldSkipUpdate(ctx)) { | |
| return; | |
| } | |
| const chatId = msg.chat.id; | |
| const rawText = ctx.match?.trim() ?? ""; | |
| const commandBody = `/${pluginCommand.command}${rawText ? ` ${rawText}` : ""}`; | |
| const match = matchPluginCommand(commandBody); | |
| if (!match) { | |
| await withTelegramApiErrorLogging({ | |
| operation: "sendMessage", | |
| runtime, | |
| fn: () => bot.api.sendMessage(chatId, "Command not found."), | |
| }); | |
| return; | |
| } | |
| const auth = await resolveTelegramCommandAuth({ | |
| msg, | |
| bot, | |
| cfg, | |
| telegramCfg, | |
| allowFrom, | |
| groupAllowFrom, | |
| useAccessGroups, | |
| resolveGroupPolicy, | |
| resolveTelegramGroupConfig, | |
| requireAuth: match.command.requireAuth !== false, | |
| }); | |
| if (!auth) { | |
| return; | |
| } | |
| const { resolvedThreadId, senderId, commandAuthorized, isGroup } = auth; | |
| const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id; | |
| const threadIdForSend = isGroup ? resolvedThreadId : messageThreadId; | |
| const result = await executePluginCommand({ | |
| command: match.command, | |
| args: match.args, | |
| senderId, | |
| channel: "telegram", | |
| isAuthorizedSender: commandAuthorized, | |
| commandBody, | |
| config: cfg, | |
| }); | |
| const tableMode = resolveMarkdownTableMode({ | |
| cfg, | |
| channel: "telegram", | |
| accountId, | |
| }); | |
| const chunkMode = resolveChunkMode(cfg, "telegram", accountId); | |
| await deliverReplies({ | |
| replies: [result], | |
| chatId: String(chatId), | |
| token: opts.token, | |
| runtime, | |
| bot, | |
| replyToMode, | |
| textLimit, | |
| messageThreadId: threadIdForSend, | |
| tableMode, | |
| chunkMode, | |
| linkPreview: telegramCfg.linkPreview, | |
| }); | |
| }); | |
| } | |
| } | |
| } else if (nativeDisabledExplicit) { | |
| withTelegramApiErrorLogging({ | |
| operation: "setMyCommands", | |
| runtime, | |
| fn: () => bot.api.setMyCommands([]), | |
| }).catch(() => {}); | |
| } | |
| }; | |