Log Telegram outbound delivery success

This commit is contained in:
clawsweeper
2026-05-17 19:35:00 +00:00
parent c2ba5c41d4
commit b860487aef
2 changed files with 59 additions and 9 deletions

View File

@@ -2052,6 +2052,43 @@ describe("sendMessageTelegram", () => {
expect(logs).not.toContain(body);
});
it("logs threadless outbound text delivery after missing-thread fallback", async () => {
const logFile = captureInfoLogs();
const chatId = "-1001234567890";
const body = "fallback reply body should stay private";
const threadErr = new Error("400: Bad Request: message thread not found");
const sendMessage = vi
.fn()
.mockRejectedValueOnce(threadErr)
.mockResolvedValueOnce({
message_id: 322,
chat: { id: chatId },
});
const api = { sendMessage } as unknown as {
sendMessage: typeof sendMessage;
};
await sendMessageTelegram(`telegram:group:${chatId}:topic:271`, body, {
cfg: TELEGRAM_TEST_CFG,
token: "tok",
accountId: "ops",
api,
});
expect(sendMessage).toHaveBeenNthCalledWith(1, chatId, body, {
parse_mode: "HTML",
message_thread_id: 271,
});
expect(sendMessage).toHaveBeenNthCalledWith(2, chatId, body, {
parse_mode: "HTML",
});
const logs = capturedLogText(logFile);
expect(logs).toContain("outbound send ok");
expect(logs).toContain("messageId=322");
expect(logs).not.toContain("threadId=271");
expect(logs).not.toContain(body);
});
it("logs successful outbound media delivery without caption or media location", async () => {
const logFile = captureInfoLogs();
const chatId = "123";
@@ -2125,6 +2162,7 @@ describe("sendMessageTelegram", () => {
});
it("retries media sends without message_thread_id when thread is missing", async () => {
const logFile = captureInfoLogs();
const chatId = "-100123";
const threadErr = new Error("400: Bad Request: message thread not found");
const sendPhoto = vi
@@ -2172,6 +2210,10 @@ describe("sendMessageTelegram", () => {
},
);
expect(res.messageId).toBe("59");
const logs = capturedLogText(logFile);
expect(logs).toContain("outbound send ok");
expect(logs).toContain("messageId=59");
expect(logs).not.toContain("threadId=271");
});
it("defaults outbound media uploads to 100MB", async () => {

View File

@@ -584,9 +584,9 @@ async function withTelegramThreadFallback<
verbose: boolean | undefined,
allowThreadlessRetry: boolean,
attempt: (effectiveParams: TParams, effectiveLabel: string) => Promise<T>,
): Promise<T> {
): Promise<{ result: T; acceptedParams: TParams }> {
try {
return await attempt(params, label);
return { result: await attempt(params, label), acceptedParams: params };
} catch (err) {
// Do not widen this fallback to cover "chat not found".
// chat-not-found is routing/auth/membership/token; stripping thread IDs hides root cause.
@@ -603,7 +603,10 @@ async function withTelegramThreadFallback<
);
}
const retriedParams = removeMessageThreadIdParam(params);
return await attempt(retriedParams, `${label}-threadless`);
return {
result: await attempt(retriedParams, `${label}-threadless`),
acceptedParams: retriedParams,
};
}
}
@@ -763,17 +766,22 @@ export async function sendMessageTelegram(
): Promise<{ messageId: string; chatId: string }> => {
let lastMessageId = "";
let lastChatId = chatId;
let lastAcceptedParams: TelegramThreadScopedParams | undefined;
let sentChunkCount = 0;
for (let index = 0; index < chunks.length; index += 1) {
const chunk = chunks[index];
if (!chunk) {
continue;
}
const res = await sendTelegramTextChunk(chunk, buildTextParams(index === chunks.length - 1));
const { result: res, acceptedParams } = await sendTelegramTextChunk(
chunk,
buildTextParams(index === chunks.length - 1),
);
const messageId = resolveTelegramMessageIdOrThrow(res, context);
recordSentMessage(chatId, messageId, cfg);
lastMessageId = String(messageId);
lastChatId = String(res?.chat?.id ?? chatId);
lastAcceptedParams = acceptedParams;
sentChunkCount += 1;
}
if (lastMessageId) {
@@ -783,7 +791,7 @@ export async function sendMessageTelegram(
messageId: lastMessageId,
operation: "sendMessage",
deliveryKind: "text",
messageThreadId: threadParams.message_thread_id,
messageThreadId: lastAcceptedParams?.message_thread_id,
replyToMessageId: opts.replyToMessageId,
silent: opts.silent,
chunkCount: sentChunkCount,
@@ -1015,7 +1023,7 @@ export async function sendMessageTelegram(
};
})();
const result = await sendMedia(mediaSender.label, mediaSender.sender);
const { result, acceptedParams } = await sendMedia(mediaSender.label, mediaSender.sender);
const mediaMessageId = resolveTelegramMessageIdOrThrow(result, "media send");
const resolvedChatId = String(result?.chat?.id ?? chatId);
recordSentMessage(chatId, mediaMessageId, cfg);
@@ -1028,7 +1036,7 @@ export async function sendMessageTelegram(
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join("")}`,
deliveryKind: mediaSender.label,
messageThreadId: threadParams.message_thread_id,
messageThreadId: acceptedParams?.message_thread_id,
replyToMessageId: opts.replyToMessageId,
silent: opts.silent,
});
@@ -1598,7 +1606,7 @@ export async function sendStickerTelegram(
const stickerParams = hasThreadParams ? threadParams : undefined;
const result = await withTelegramThreadFallback(
const { result } = await withTelegramThreadFallback(
stickerParams,
"sticker",
opts.verbose,
@@ -1706,7 +1714,7 @@ export async function sendPollTelegram(
...(opts.silent === true ? { disable_notification: true } : {}),
};
const result = await withTelegramThreadFallback(
const { result } = await withTelegramThreadFallback(
pollParams,
"poll",
opts.verbose,