mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-29 03:03:35 +00:00
fix(telegram): use idempotent retry context for delete/reaction (#96612)
reactMessageTelegram and deleteMessageTelegram passed context: "send" to isRecoverableTelegramNetworkError, which disables message-snippet matching (allowMessageMatch defaults to false only for "send"). Both operations are idempotent (setMessageReaction / deleteMessage are safe to repeat), yet a transient snippet-only network error (e.g. "socket hang up", "undici network error" with no error code) was not retried — stricter than polling/webhook/ unknown, which all default allowMessageMatch to true. Users saw spurious reaction/delete failures on transient network errors. Add delete | react to TelegramNetworkErrorContext (additive) and use them at the two callers. The helper default (context !== "send") is unchanged, so delete/react now match polling/webhook/unknown. sendMessage keeps "send". Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -139,6 +139,16 @@ describe("isRecoverableTelegramNetworkError", () => {
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "polling" })).toBe(true);
|
||||
});
|
||||
|
||||
it("treats delete/react (idempotent) contexts like polling, not send", () => {
|
||||
const undiciSnippetErr = new Error("Undici: socket failure");
|
||||
// delete and react are idempotent Telegram operations; a transient
|
||||
// snippet-only error must be retried (allowMessageMatch defaults true),
|
||||
// matching polling/webhook. send stays strict as the regression guard.
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "delete" })).toBe(true);
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "react" })).toBe(true);
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "send" })).toBe(false);
|
||||
});
|
||||
|
||||
it("treats grammY failed-after envelope errors as recoverable in send context", () => {
|
||||
expect(
|
||||
isRecoverableTelegramNetworkError(
|
||||
|
||||
@@ -141,7 +141,13 @@ export function isTelegramMisdirectedRequestError(err: unknown): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
export type TelegramNetworkErrorContext = "polling" | "send" | "webhook" | "unknown";
|
||||
export type TelegramNetworkErrorContext =
|
||||
| "polling"
|
||||
| "send"
|
||||
| "webhook"
|
||||
| "delete"
|
||||
| "react"
|
||||
| "unknown";
|
||||
export type TelegramNetworkErrorOrigin = {
|
||||
method?: string | null;
|
||||
url?: string | null;
|
||||
|
||||
@@ -1219,7 +1219,7 @@ export async function reactMessageTelegram(
|
||||
account,
|
||||
retry: opts.retry,
|
||||
verbose: opts.verbose,
|
||||
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
||||
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "react" }),
|
||||
});
|
||||
const remove = opts.remove === true;
|
||||
const trimmedEmoji = emoji.trim();
|
||||
@@ -1276,7 +1276,7 @@ export async function deleteMessageTelegram(
|
||||
account,
|
||||
retry: opts.retry,
|
||||
verbose: opts.verbose,
|
||||
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
||||
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "delete" }),
|
||||
});
|
||||
try {
|
||||
await requestWithDiag(() => api.deleteMessage(chatId, messageId), "deleteMessage", {
|
||||
|
||||
Reference in New Issue
Block a user