Skip to content

Commit a956ab8

Browse files
committed
refactor(telegram): centralize edit error classification in network-errors
1 parent 3b78d41 commit a956ab8

3 files changed

Lines changed: 34 additions & 19 deletions

File tree

extensions/telegram/src/bot-handlers.runtime.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ import {
145145
type ProviderInfo,
146146
} from "./model-buttons.js";
147147
import { parseTelegramOpaqueCallbackData } from "./native-command-callback-data.js";
148+
import {
149+
isTelegramEditTargetMissingError,
150+
isTelegramMessageHasNoTextError,
151+
} from "./network-errors.js";
148152
import { buildInlineKeyboard } from "./send.js";
149153

150154
export const registerTelegramHandlers = ({
@@ -1303,11 +1307,8 @@ export const registerTelegramHandlers = ({
13031307
}
13041308
}
13051309

1306-
const TELEGRAM_PERMANENT_CALLBACK_EDIT_ERROR_RE =
1307-
/400:\s*Bad Request:\s*message to edit not found|400:\s*Bad Request:\s*there is no text in the message to edit|MESSAGE_ID_INVALID|400:\s*Bad Request:\s*message can't be edited/i;
1308-
13091310
const isPermanentTelegramCallbackEditError = (err: unknown): boolean =>
1310-
TELEGRAM_PERMANENT_CALLBACK_EDIT_ERROR_RE.test(String(err));
1311+
isTelegramEditTargetMissingError(err) || isTelegramMessageHasNoTextError(err);
13111312

13121313
const resolveTelegramEventAuthorizationContext = async (params: {
13131314
chatId: number;

extensions/telegram/src/network-errors.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ function hasTelegramErrorCode(err: unknown, matches: (code: number) => boolean):
225225
return false;
226226
}
227227

228-
function hasTelegramRetryAfter(err: unknown): boolean {
228+
/** Reads Telegram's flood-control retry_after hint (in ms) from any error nesting shape. */
229+
export function readTelegramRetryAfterMs(err: unknown): number | undefined {
229230
for (const candidate of collectTelegramErrorCandidates(err)) {
230231
if (!candidate || typeof candidate !== "object") {
231232
continue;
@@ -250,10 +251,10 @@ function hasTelegramRetryAfter(err: unknown): boolean {
250251
?.retry_after
251252
: undefined;
252253
if (typeof retryAfter === "number" && Number.isFinite(retryAfter)) {
253-
return true;
254+
return retryAfter * 1000;
254255
}
255256
}
256-
return false;
257+
return undefined;
257258
}
258259

259260
/** Returns true for HTTP 5xx server errors (error may have been processed). */
@@ -264,10 +265,32 @@ export function isTelegramServerError(err: unknown): boolean {
264265
export function isTelegramRateLimitError(err: unknown): boolean {
265266
return (
266267
hasTelegramErrorCode(err, (code) => code === 429) ||
267-
(hasTelegramRetryAfter(err) && /(?:^|\b)429\b|too many requests/i.test(formatErrorMessage(err)))
268+
(readTelegramRetryAfterMs(err) !== undefined &&
269+
/(?:^|\b)429\b|too many requests/i.test(formatErrorMessage(err)))
268270
);
269271
}
270272

273+
const MESSAGE_NOT_MODIFIED_RE =
274+
/400:\s*Bad Request:\s*message is not modified|MESSAGE_NOT_MODIFIED/i;
275+
const MESSAGE_HAS_NO_TEXT_RE = /400:\s*Bad Request:\s*there is no text in the message to edit/i;
276+
const EDIT_TARGET_MISSING_RE =
277+
/400:\s*Bad Request:\s*message to edit not found|400:\s*Bad Request:\s*message can't be edited|MESSAGE_ID_INVALID/i;
278+
279+
/** True when Telegram rejected an edit because the content is unchanged; the message already shows the requested text. */
280+
export function isTelegramMessageNotModifiedError(err: unknown): boolean {
281+
return MESSAGE_NOT_MODIFIED_RE.test(formatErrorMessage(err));
282+
}
283+
284+
/** True when the edit target has no text body (e.g. media message needing a caption edit). */
285+
export function isTelegramMessageHasNoTextError(err: unknown): boolean {
286+
return MESSAGE_HAS_NO_TEXT_RE.test(formatErrorMessage(err));
287+
}
288+
289+
/** True when the edit target is gone or locked (deleted message, invalid id); retrying the same edit cannot succeed. */
290+
export function isTelegramEditTargetMissingError(err: unknown): boolean {
291+
return EDIT_TARGET_MISSING_RE.test(formatErrorMessage(err));
292+
}
293+
271294
/** Returns true for HTTP 4xx client errors (Telegram explicitly rejected, not applied). */
272295
export function isTelegramClientRejection(err: unknown): boolean {
273296
return hasTelegramErrorCode(err, (code) => code >= 400 && code < 500);

extensions/telegram/src/send.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import { buildInlineKeyboard } from "./inline-keyboard.js";
2929
import {
3030
isRecoverableTelegramNetworkError,
3131
isSafeToRetrySendError,
32+
isTelegramMessageHasNoTextError,
33+
isTelegramMessageNotModifiedError,
3234
isTelegramRateLimitError,
3335
isTelegramServerError,
3436
} from "./network-errors.js";
@@ -230,9 +232,6 @@ function logTelegramOutboundSendOk(params: TelegramOutboundSuccessLogParams): vo
230232
}
231233

232234
const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
233-
const MESSAGE_NOT_MODIFIED_RE =
234-
/400:\s*Bad Request:\s*message is not modified|MESSAGE_NOT_MODIFIED/i;
235-
const MESSAGE_HAS_NO_TEXT_RE = /400:\s*Bad Request:\s*there is no text in the message to edit/i;
236235
const MESSAGE_DELETE_NOOP_RE =
237236
/message to delete not found|message can't be deleted|MESSAGE_ID_INVALID|MESSAGE_DELETE_FORBIDDEN/i;
238237
const CHAT_NOT_FOUND_RE = /400: Bad Request: chat not found/i;
@@ -422,14 +421,6 @@ function normalizeMessageId(raw: string | number): number {
422421
throw new Error("Message id is required for Telegram actions");
423422
}
424423

425-
function isTelegramMessageNotModifiedError(err: unknown): boolean {
426-
return MESSAGE_NOT_MODIFIED_RE.test(formatErrorMessage(err));
427-
}
428-
429-
function isTelegramMessageHasNoTextError(err: unknown): boolean {
430-
return MESSAGE_HAS_NO_TEXT_RE.test(formatErrorMessage(err));
431-
}
432-
433424
function isTelegramMessageDeleteNoopError(err: unknown): boolean {
434425
return MESSAGE_DELETE_NOOP_RE.test(formatErrorMessage(err));
435426
}

0 commit comments

Comments
 (0)