fix: guard malformed Telegram replies and pass hook accountId

This commit is contained in:
Ayaan Zaidi 2026-03-03 17:00:40 +05:30 committed by Ayaan Zaidi
parent 5f95f46070
commit 1ded5cc9a9
4 changed files with 62 additions and 3 deletions

View File

@ -385,6 +385,7 @@ export const dispatchTelegramMessage = async ({
};
const deliveryBaseOptions = {
chatId: String(chatId),
accountId: route.accountId,
token: opts.token,
runtime,
bot,

View File

@ -445,12 +445,14 @@ export const registerTelegramNativeCommands = ({
};
const buildCommandDeliveryBaseOptions = (params: {
chatId: string | number;
accountId: string;
mediaLocalRoots?: readonly string[];
threadSpec: ReturnType<typeof resolveTelegramThreadSpec>;
tableMode: ReturnType<typeof resolveMarkdownTableMode>;
chunkMode: ReturnType<typeof resolveChunkMode>;
}) => ({
chatId: String(params.chatId),
accountId: params.accountId,
token: opts.token,
runtime,
bot,
@ -513,6 +515,7 @@ export const registerTelegramNativeCommands = ({
});
const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
chatId,
accountId: route.accountId,
mediaLocalRoots,
threadSpec,
tableMode,
@ -726,7 +729,7 @@ export const registerTelegramNativeCommands = ({
return;
}
const { senderId, commandAuthorized, isGroup, isForum, resolvedThreadId } = auth;
const { threadSpec, mediaLocalRoots, tableMode, chunkMode } =
const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } =
resolveCommandRuntimeContext({
msg,
isGroup,
@ -735,6 +738,7 @@ export const registerTelegramNativeCommands = ({
});
const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
chatId,
accountId: route.accountId,
mediaLocalRoots,
threadSpec,
tableMode,

View File

@ -428,6 +428,7 @@ async function deliverMediaReply(params: {
export async function deliverReplies(params: {
replies: ReplyPayload[];
chatId: string;
accountId?: string;
token: string;
runtime: RuntimeEnv;
bot: Bot;
@ -459,9 +460,9 @@ export async function deliverReplies(params: {
});
for (const originalReply of params.replies) {
let reply = originalReply;
const mediaList = reply.mediaUrls?.length
const mediaList = reply?.mediaUrls?.length
? reply.mediaUrls
: reply.mediaUrl
: reply?.mediaUrl
? [reply.mediaUrl]
: [];
const hasMedia = mediaList.length > 0;
@ -488,6 +489,7 @@ export async function deliverReplies(params: {
},
{
channelId: "telegram",
accountId: params.accountId,
conversationId: params.chatId,
},
);
@ -555,6 +557,7 @@ export async function deliverReplies(params: {
},
{
channelId: "telegram",
accountId: params.accountId,
conversationId: params.chatId,
},
);
@ -570,6 +573,7 @@ export async function deliverReplies(params: {
},
{
channelId: "telegram",
accountId: params.accountId,
conversationId: params.chatId,
},
);

View File

@ -126,6 +126,22 @@ describe("deliverReplies", () => {
expect(runtime.error).not.toHaveBeenCalled();
});
it("skips malformed replies and continues with valid entries", async () => {
const runtime = createRuntime(false);
const sendMessage = vi.fn().mockResolvedValue({ message_id: 1, chat: { id: "123" } });
const bot = createBot({ sendMessage });
await deliverWith({
replies: [undefined, { text: "hello" }] as unknown as DeliverRepliesParams["replies"],
runtime,
bot,
});
expect(runtime.error).toHaveBeenCalledTimes(1);
expect(sendMessage).toHaveBeenCalledTimes(1);
expect(sendMessage.mock.calls[0]?.[1]).toBe("hello");
});
it("reports message_sent success=false when hooks blank out a text-only reply", async () => {
messageHookRunner.hasHooks.mockImplementation(
(name: string) => name === "message_sending" || name === "message_sent",
@ -149,6 +165,40 @@ describe("deliverReplies", () => {
);
});
it("passes accountId into message hooks", async () => {
messageHookRunner.hasHooks.mockImplementation(
(name: string) => name === "message_sending" || name === "message_sent",
);
const runtime = createRuntime(false);
const sendMessage = vi.fn().mockResolvedValue({ message_id: 9, chat: { id: "123" } });
const bot = createBot({ sendMessage });
await deliverWith({
accountId: "work",
replies: [{ text: "hello" }],
runtime,
bot,
});
expect(messageHookRunner.runMessageSending).toHaveBeenCalledWith(
expect.any(Object),
expect.objectContaining({
channelId: "telegram",
accountId: "work",
conversationId: "123",
}),
);
expect(messageHookRunner.runMessageSent).toHaveBeenCalledWith(
expect.objectContaining({ success: true }),
expect.objectContaining({
channelId: "telegram",
accountId: "work",
conversationId: "123",
}),
);
});
it("passes media metadata to message_sending hooks", async () => {
messageHookRunner.hasHooks.mockImplementation((name: string) => name === "message_sending");