


























@@ -67,6 +67,7 @@ export type TelegramApiOverride = Partial<TelegramApi>;
6767type TelegramSendMessageParams = Parameters<TelegramApi["sendMessage"]>[2];
6868type TelegramSendPollParams = Parameters<TelegramApi["sendPoll"]>[3];
6969type TelegramEditMessageTextParams = Parameters<TelegramApi["editMessageText"]>[3];
70+type TelegramEditMessageCaptionParams = Parameters<TelegramApi["editMessageCaption"]>[2];
7071type TelegramCreateForumTopicParams = NonNullable<Parameters<TelegramApi["createForumTopic"]>[2]>;
7172type TelegramThreadScopedParams = {
7273message_thread_id?: number;
@@ -230,6 +231,7 @@ function logTelegramOutboundSendOk(params: TelegramOutboundSuccessLogParams): vo
230231const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
231232const MESSAGE_NOT_MODIFIED_RE =
232233/400:\s*Bad Request:\s*message is not modified|MESSAGE_NOT_MODIFIED/i;
234+const MESSAGE_HAS_NO_TEXT_RE = /400:\s*Bad Request:\s*there is no text in the message to edit/i;
233235const MESSAGE_DELETE_NOOP_RE =
234236/message to delete not found|message can't be deleted|MESSAGE_ID_INVALID|MESSAGE_DELETE_FORBIDDEN/i;
235237const CHAT_NOT_FOUND_RE = /400: Bad Request: chat not found/i;
@@ -422,6 +424,10 @@ function isTelegramMessageNotModifiedError(err: unknown): boolean {
422424return MESSAGE_NOT_MODIFIED_RE.test(formatErrorMessage(err));
423425}
424426427+function isTelegramMessageHasNoTextError(err: unknown): boolean {
428+return MESSAGE_HAS_NO_TEXT_RE.test(formatErrorMessage(err));
429+}
430+425431function isTelegramMessageDeleteNoopError(err: unknown): boolean {
426432return MESSAGE_DELETE_NOOP_RE.test(formatErrorMessage(err));
427433}
@@ -1316,6 +1322,8 @@ type TelegramEditOpts = {
13161322linkPreview?: boolean;
13171323/** Inline keyboard buttons (reply markup). Pass empty array to remove buttons. */
13181324buttons?: TelegramInlineButtons;
1325+/** Use Telegram's media-caption edit endpoint, or fall back to it when text edits target media. */
1326+editMode?: "text" | "caption" | "auto";
13191327/** Resolved runtime config from the command or gateway boundary. */
13201328cfg: OpenClawConfig;
13211329};
@@ -1429,43 +1437,90 @@ export async function editMessageTelegram(
14291437const builtKeyboard = shouldTouchButtons ? buildInlineKeyboard(opts.buttons) : undefined;
14301438const replyMarkup = shouldTouchButtons ? (builtKeyboard ?? { inline_keyboard: [] }) : undefined;
143114391432-const editParams: TelegramEditMessageTextParams = {
1440+const textEditParams: TelegramEditMessageTextParams = {
14331441parse_mode: "HTML",
14341442};
14351443if (opts.linkPreview === false) {
1436-editParams.link_preview_options = { is_disabled: true };
1444+textEditParams.link_preview_options = { is_disabled: true };
14371445}
14381446if (replyMarkup !== undefined) {
1439-editParams.reply_markup = replyMarkup;
1447+textEditParams.reply_markup = replyMarkup;
14401448}
1441-const plainParams: TelegramEditMessageTextParams = {};
1449+const plainTextParams: TelegramEditMessageTextParams = {};
14421450if (opts.linkPreview === false) {
1443-plainParams.link_preview_options = { is_disabled: true };
1451+plainTextParams.link_preview_options = { is_disabled: true };
1452+}
1453+if (replyMarkup !== undefined) {
1454+plainTextParams.reply_markup = replyMarkup;
14441455}
1456+const captionEditParams: TelegramEditMessageCaptionParams = {
1457+caption: htmlText,
1458+parse_mode: "HTML",
1459+};
14451460if (replyMarkup !== undefined) {
1446-plainParams.reply_markup = replyMarkup;
1461+captionEditParams.reply_markup = replyMarkup;
1462+}
1463+const plainCaptionParams: TelegramEditMessageCaptionParams = {
1464+caption: plainText,
1465+};
1466+if (replyMarkup !== undefined) {
1467+plainCaptionParams.reply_markup = replyMarkup;
14471468}
144814691449-try {
1450-await withTelegramHtmlParseFallback({
1470+const performTextEdit = () =>
1471+withTelegramHtmlParseFallback({
14511472label: "editMessage",
14521473verbose: opts.verbose,
14531474requestHtml: (retryLabel) =>
14541475requestWithEditShouldLog(
1455-() => api.editMessageText(chatId, messageId, htmlText, editParams),
1476+() => api.editMessageText(chatId, messageId, htmlText, textEditParams),
14561477retryLabel,
14571478(err) => !isTelegramMessageNotModifiedError(err),
14581479),
14591480requestPlain: (retryLabel) =>
14601481requestWithEditShouldLog(
14611482() =>
1462-Object.keys(plainParams).length > 0
1463- ? api.editMessageText(chatId, messageId, plainText, plainParams)
1483+Object.keys(plainTextParams).length > 0
1484+ ? api.editMessageText(chatId, messageId, plainText, plainTextParams)
14641485 : api.editMessageText(chatId, messageId, plainText),
14651486retryLabel,
14661487(plainErr) => !isTelegramMessageNotModifiedError(plainErr),
14671488),
14681489});
1490+1491+const performCaptionEdit = () =>
1492+withTelegramHtmlParseFallback({
1493+label: "editMessageCaption",
1494+verbose: opts.verbose,
1495+requestHtml: (retryLabel) =>
1496+requestWithEditShouldLog(
1497+() => api.editMessageCaption(chatId, messageId, captionEditParams),
1498+retryLabel,
1499+(err) => !isTelegramMessageNotModifiedError(err),
1500+),
1501+requestPlain: (retryLabel) =>
1502+requestWithEditShouldLog(
1503+() => api.editMessageCaption(chatId, messageId, plainCaptionParams),
1504+retryLabel,
1505+(plainErr) => !isTelegramMessageNotModifiedError(plainErr),
1506+),
1507+});
1508+1509+try {
1510+const editMode = opts.editMode ?? "text";
1511+if (editMode === "caption") {
1512+await performCaptionEdit();
1513+} else {
1514+try {
1515+await performTextEdit();
1516+} catch (err) {
1517+if (editMode === "auto" && isTelegramMessageHasNoTextError(err)) {
1518+await performCaptionEdit();
1519+} else {
1520+throw err;
1521+}
1522+}
1523+}
14691524} catch (err) {
14701525if (isTelegramMessageNotModifiedError(err)) {
14711526// no-op: Telegram reports message content unchanged, treat as success
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。