fix(telegram): run outbound message hooks in reply delivery path

This commit is contained in:
KimGLee
2026-03-03 14:29:33 +08:00
committed by Ayaan Zaidi
parent 4aa548cf7d
commit 6d118ab815

View File

@@ -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 };