
























11// Telegram plugin module implements bot handlers behavior.
22import { randomUUID } from "node:crypto";
3+import path from "node:path";
34import type { Message, ReactionTypeEmoji } from "grammy/types";
45import { parseExecApprovalCommandText } from "openclaw/plugin-sdk/approval-reply-runtime";
56import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-helpers";
@@ -160,6 +161,39 @@ import {
160161} from "./network-errors.js";
161162import { buildInlineKeyboard } from "./send.js";
162163164+function resolveTelegramPromptMediaPath(mediaPath: string): string | undefined {
165+const toInboundMediaPath = (id: string): string | undefined => {
166+if (
167+!id ||
168+id === "." ||
169+id === ".." ||
170+id.includes("/") ||
171+id.includes("\\") ||
172+id.includes("\0")
173+) {
174+return undefined;
175+}
176+return `media://inbound/${encodeURIComponent(id)}`;
177+};
178+const decodeInboundMediaId = (id: string): string | undefined => {
179+try {
180+return decodeURIComponent(id);
181+} catch {
182+return undefined;
183+}
184+};
185+const canonicalMatch = /^media:\/\/inbound\/([^/\\]+)$/i.exec(mediaPath);
186+if (canonicalMatch?.[1]) {
187+const id = decodeInboundMediaId(canonicalMatch[1]);
188+return id ? toInboundMediaPath(id) : undefined;
189+}
190+const normalized = mediaPath.replace(/\\/g, "/");
191+if (!normalized.includes("/media/inbound/")) {
192+return undefined;
193+}
194+return toInboundMediaPath(path.posix.basename(normalized));
195+}
196+163197export const registerTelegramHandlers = ({
164198 cfg,
165199 accountId,
@@ -1109,16 +1143,21 @@ export const registerTelegramHandlers = ({
11091143media?: TelegramMediaRef,
11101144): TelegramReplyChainEntry => {
11111145const { sourceMessage: _sourceMessage, ...entry } = node;
1146+if (!media?.path) {
1147+return entry;
1148+}
1149+const { mediaRef: _mediaRef, ...entryWithoutProviderMediaRef } = entry;
11121150return {
1113- ...entry,
1114-...(media?.path ? { mediaPath: media.path } : {}),
1151+ ...entryWithoutProviderMediaRef,
1152+mediaPath: media.path,
11151153 ...(media?.contentType ? { mediaType: media.contentType } : {}),
11161154};
11171155};
1118115611191157const toPromptContextMessage = (
11201158node: TelegramCachedMessageNode,
11211159flags?: { replyTarget?: boolean },
1160+media?: TelegramMediaRef,
11221161) => ({
11231162message_id: node.messageId,
11241163thread_id: node.threadId,
@@ -1127,8 +1166,9 @@ export const registerTelegramHandlers = ({
11271166sender_username: node.senderUsername,
11281167timestamp_ms: node.timestamp,
11291168body: node.body,
1130-media_type: node.mediaType,
1131-media_ref: node.mediaRef,
1169+media_type: media?.contentType ?? node.mediaType,
1170+media_path: media?.path,
1171+media_ref: media?.path ? undefined : node.mediaRef,
11321172reply_to_id: node.replyToId,
11331173is_reply_target: flags?.replyTarget === true ? true : undefined,
11341174});
@@ -1137,6 +1177,7 @@ export const registerTelegramHandlers = ({
11371177msg: Message,
11381178replyChainNodes: TelegramCachedMessageNode[],
11391179options?: TelegramMessageContextOptions,
1180+mediaByMessageId?: ReadonlyMap<string, TelegramMediaRef>,
11401181): Promise<TelegramPromptContextEntry[]> => {
11411182const messageId = typeof msg.message_id === "number" ? String(msg.message_id) : undefined;
11421183const currentNode = await messageCache.get({
@@ -1168,7 +1209,11 @@ export const registerTelegramHandlers = ({
11681209order: "chronological",
11691210relation: "selected_for_current_message",
11701211messages: conversationContext.map((entry) =>
1171-toPromptContextMessage(entry.node, { replyTarget: entry.isReplyTarget }),
1212+toPromptContextMessage(
1213+entry.node,
1214+{ replyTarget: entry.isReplyTarget },
1215+entry.node.messageId ? mediaByMessageId?.get(entry.node.messageId) : undefined,
1216+),
11721217),
11731218},
11741219},
@@ -1240,10 +1285,39 @@ export const registerTelegramHandlers = ({
12401285params.ctx,
12411286replyChainNodes,
12421287);
1288+const promptContextMediaByMessageId = new Map<string, TelegramMediaRef>();
1289+const currentMessageId =
1290+typeof params.msg.message_id === "number" ? String(params.msg.message_id) : undefined;
1291+const currentMedia = params.allMedia[0];
1292+const currentPromptMediaPath = currentMedia?.path
1293+ ? resolveTelegramPromptMediaPath(currentMedia.path)
1294+ : undefined;
1295+if (currentMessageId && currentMedia && currentPromptMediaPath) {
1296+promptContextMediaByMessageId.set(currentMessageId, {
1297+ ...currentMedia,
1298+path: currentPromptMediaPath,
1299+});
1300+}
1301+const isGroupConversation =
1302+params.msg.chat.type === "group" || params.msg.chat.type === "supergroup";
1303+if (!isGroupConversation) {
1304+for (const entry of replyChain) {
1305+const promptMediaPath = entry.mediaPath
1306+ ? resolveTelegramPromptMediaPath(entry.mediaPath)
1307+ : undefined;
1308+if (entry.messageId && entry.mediaPath && promptMediaPath) {
1309+promptContextMediaByMessageId.set(entry.messageId, {
1310+path: promptMediaPath,
1311+ ...(entry.mediaType ? { contentType: entry.mediaType } : {}),
1312+});
1313+}
1314+}
1315+}
12431316const promptContext = await buildPromptContextForMessage(
12441317params.msg,
12451318replyChainNodes,
12461319params.options,
1320+promptContextMediaByMessageId,
12471321);
12481322const result = await processMessage(
12491323params.ctx,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。