fix(telegram): address rich fallback proof review

This commit is contained in:
snowzlmbot
2026-07-07 00:33:01 +08:00
committed by Ayaan Zaidi
parent 982328218b
commit 2f98e1fdb5
6 changed files with 40 additions and 12 deletions

View File

@@ -913,6 +913,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
);
const pipelineArgs = expectRecordFields(mockCallArg(createChannelMessageReplyPipeline), {});
const typing = expectRecordFields(pipelineArgs.typing, {});
expect(typing.maxConsecutiveFailures).toBe(5);
await (typing.start as () => Promise<void>)();
expect(sendChatAction).toHaveBeenCalledWith(-1003774691294, "typing", {
message_thread_id: 3731,

View File

@@ -156,6 +156,8 @@ import { clipTelegramProgressText } from "./truncate.js";
export { resetTelegramReplyFenceForTests };
// Telegram sendChatAction can fail transiently; keep the tolerance scoped to this transport.
const TELEGRAM_MAX_CONSECUTIVE_TYPING_FAILURES = 5;
const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
const silentReplyDispatchLogger = createSubsystemLogger("telegram/silent-reply-dispatch");
@@ -2154,6 +2156,7 @@ export const dispatchTelegramMessage = async ({
accountId: route.accountId,
typing: {
start: sendTyping,
maxConsecutiveFailures: TELEGRAM_MAX_CONSECUTIVE_TYPING_FAILURES,
onStartError: (err) => {
logTypingFailure({
log: logVerbose,

View File

@@ -212,6 +212,12 @@ function createRichEntityInvalidError(entity = "EMAIL", operation = "sendRichMes
);
}
function createRichContentRequiredError(operation = "sendRichMessage") {
return new Error(
`GrammyError: Call to '${operation}' failed! (400: Bad Request: RICH_MESSAGE_CONTENT_REQUIRED)`,
);
}
function createHtmlParseError(operation = "sendMessage") {
return new Error(
`GrammyError: Call to '${operation}' failed! (400: Bad Request: can't parse entities: Can't find end of the entity)`,

View File

@@ -292,6 +292,12 @@ function createRichEntityInvalidError(entity = "EMAIL", operation = "sendRichMes
);
}
function createRichContentRequiredError(operation = "sendRichMessage"): Error {
return new Error(
`GrammyError: Call to '${operation}' failed! (400: Bad Request: RICH_MESSAGE_CONTENT_REQUIRED)`,
);
}
function createHtmlParseError(operation = "sendMessage"): Error {
return new Error(
`GrammyError: Call to '${operation}' failed! (400: Bad Request: can't parse entities: Can't find end of the entity)`,

View File

@@ -183,6 +183,26 @@ describe("createTypingCallbacks", () => {
expect(start).toHaveBeenCalledTimes(2);
expect(onStartError).toHaveBeenCalledTimes(2);
await vi.advanceTimersByTimeAsync(9_000);
expect(start).toHaveBeenCalledTimes(2);
});
});
it("honors an explicit higher consecutive failure breaker", async () => {
await withFakeTimers(async () => {
const { start, onStartError, callbacks } = createTypingHarness({
start: vi.fn().mockRejectedValue(new Error("gone")),
maxConsecutiveFailures: 5,
});
await callbacks.onReplyStart();
await flushMicrotasks();
expect(start).toHaveBeenCalledTimes(1);
expect(onStartError).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(3_000);
expect(start).toHaveBeenCalledTimes(2);
expect(onStartError).toHaveBeenCalledTimes(2);
await vi.advanceTimersByTimeAsync(3_000);
expect(start).toHaveBeenCalledTimes(3);
expect(onStartError).toHaveBeenCalledTimes(3);
@@ -220,19 +240,11 @@ describe("createTypingCallbacks", () => {
expect(onStartError).toHaveBeenCalledTimes(2);
await vi.advanceTimersByTimeAsync(3_000);
expect(start).toHaveBeenCalledTimes(3);
expect(onStartError).toHaveBeenCalledTimes(3);
await vi.advanceTimersByTimeAsync(3_000);
expect(start).toHaveBeenCalledTimes(4);
expect(onStartError).toHaveBeenCalledTimes(4);
await vi.advanceTimersByTimeAsync(3_000);
expect(start).toHaveBeenCalledTimes(5);
expect(onStartError).toHaveBeenCalledTimes(5);
expect(start).toHaveBeenCalledTimes(2);
expect(onStartError).toHaveBeenCalledTimes(2);
await vi.advanceTimersByTimeAsync(9_000);
expect(start).toHaveBeenCalledTimes(5);
expect(start).toHaveBeenCalledTimes(2);
});
});

View File

@@ -25,7 +25,7 @@ export type CreateTypingCallbacksParams = {
maxDurationMs?: number;
};
const DEFAULT_MAX_CONSECUTIVE_TYPING_FAILURES = 5;
const DEFAULT_MAX_CONSECUTIVE_TYPING_FAILURES = 2;
function resolvePositiveIntegerOption(value: number | undefined, fallback: number): number {
const parsed = parseFiniteNumber(value);