diff --git a/src/telegram/bot/delivery.replies.ts b/src/telegram/bot/delivery.replies.ts index 71d0a82f6e5..5573b2916f5 100644 --- a/src/telegram/bot/delivery.replies.ts +++ b/src/telegram/bot/delivery.replies.ts @@ -7,6 +7,7 @@ import { danger, logVerbose } from "../../globals.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { buildOutboundMediaLoadOptions } from "../../media/load-options.js"; import { isGifMedia, kindFromMime } from "../../media/mime.js"; +import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js"; import type { RuntimeEnv } from "../../runtime.js"; import { loadWebMedia } from "../../web/media.js"; import type { TelegramInlineButtons } from "../button-types.js"; @@ -445,12 +446,16 @@ export async function deliverReplies(params: { hasReplied: false, hasDelivered: false, }; + const hookRunner = getGlobalHookRunner(); + const hasMessageSendingHooks = hookRunner?.hasHooks("message_sending") ?? false; + const hasMessageSentHooks = hookRunner?.hasHooks("message_sent") ?? false; const chunkText = buildChunkTextResolver({ textLimit: params.textLimit, chunkMode: params.chunkMode ?? "length", tableMode: params.tableMode, }); - for (const reply of params.replies) { + for (const originalReply of params.replies) { + let reply = originalReply; const hasMedia = Boolean(reply?.mediaUrl) || (reply?.mediaUrls?.length ?? 0) > 0; if (!reply?.text && !hasMedia) { if (reply?.audioAsVoice) { @@ -460,52 +465,107 @@ export async function deliverReplies(params: { params.runtime.error?.(danger("reply missing text/media")); continue; } - const replyToId = - params.replyToMode === "off" ? undefined : resolveTelegramReplyId(reply.replyToId); - const mediaList = reply.mediaUrls?.length - ? reply.mediaUrls - : reply.mediaUrl - ? [reply.mediaUrl] - : []; - const telegramData = reply.channelData?.telegram as - | { buttons?: TelegramInlineButtons } - | undefined; - const replyMarkup = buildInlineKeyboard(telegramData?.buttons); - if (mediaList.length === 0) { - await deliverTextReply({ - bot: params.bot, - chatId: params.chatId, - runtime: params.runtime, - thread: params.thread, - chunkText, - replyText: reply.text || "", - replyMarkup, - replyQuoteText: params.replyQuoteText, - linkPreview: params.linkPreview, - replyToId, - replyToMode: params.replyToMode, - progress, - }); - continue; + + const rawContent = reply.text || ""; + if (hasMessageSendingHooks) { + const hookResult = await hookRunner?.runMessageSending( + { + to: params.chatId, + content: rawContent, + }, + { + channelId: "telegram", + conversationId: params.chatId, + }, + ); + if (hookResult?.cancel) { + continue; + } + if (typeof hookResult?.content === "string" && hookResult.content !== rawContent) { + reply = { ...reply, text: hookResult.content }; + } + } + + const contentForSentHook = reply.text || ""; + + try { + const replyToId = + params.replyToMode === "off" ? undefined : resolveTelegramReplyId(reply.replyToId); + const mediaList = reply.mediaUrls?.length + ? reply.mediaUrls + : reply.mediaUrl + ? [reply.mediaUrl] + : []; + const telegramData = reply.channelData?.telegram as + | { buttons?: TelegramInlineButtons } + | undefined; + const replyMarkup = buildInlineKeyboard(telegramData?.buttons); + if (mediaList.length === 0) { + await deliverTextReply({ + bot: params.bot, + chatId: params.chatId, + runtime: params.runtime, + thread: params.thread, + chunkText, + replyText: reply.text || "", + replyMarkup, + replyQuoteText: params.replyQuoteText, + linkPreview: params.linkPreview, + replyToId, + replyToMode: params.replyToMode, + progress, + }); + } else { + await deliverMediaReply({ + reply, + mediaList, + bot: params.bot, + chatId: params.chatId, + runtime: params.runtime, + thread: params.thread, + tableMode: params.tableMode, + mediaLocalRoots: params.mediaLocalRoots, + chunkText, + onVoiceRecording: params.onVoiceRecording, + linkPreview: params.linkPreview, + replyQuoteText: params.replyQuoteText, + replyMarkup, + replyToId, + replyToMode: params.replyToMode, + progress, + }); + } + + if (hasMessageSentHooks) { + void hookRunner?.runMessageSent( + { + to: params.chatId, + content: contentForSentHook, + success: true, + }, + { + channelId: "telegram", + conversationId: params.chatId, + }, + ); + } + } catch (error) { + if (hasMessageSentHooks) { + void hookRunner?.runMessageSent( + { + to: params.chatId, + content: contentForSentHook, + success: false, + error: error instanceof Error ? error.message : String(error), + }, + { + channelId: "telegram", + conversationId: params.chatId, + }, + ); + } + throw error; } - await deliverMediaReply({ - reply, - mediaList, - bot: params.bot, - chatId: params.chatId, - runtime: params.runtime, - thread: params.thread, - tableMode: params.tableMode, - mediaLocalRoots: params.mediaLocalRoots, - chunkText, - onVoiceRecording: params.onVoiceRecording, - linkPreview: params.linkPreview, - replyQuoteText: params.replyQuoteText, - replyMarkup, - replyToId, - replyToMode: params.replyToMode, - progress, - }); } return { delivered: progress.hasDelivered };