mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-13 23:26:39 +00:00
fix(telegram): reset webhook cleanup latch after polling 409 conflicts (#39205, thanks @amittell)
Co-authored-by: amittell <mittell@me.com>
This commit is contained in:
@@ -285,6 +285,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Gateway/systemd service restart hardening: clear stale gateway listeners by explicit run-port before service bind, add restart stale-pid port-override support, tune systemd start/stop/exit handling, and disable detached child mode only in service-managed runtime so cgroup stop semantics clean up descendants reliably. (#38463) Thanks @spirittechie.
|
- Gateway/systemd service restart hardening: clear stale gateway listeners by explicit run-port before service bind, add restart stale-pid port-override support, tune systemd start/stop/exit handling, and disable detached child mode only in service-managed runtime so cgroup stop semantics clean up descendants reliably. (#38463) Thanks @spirittechie.
|
||||||
- Discord/plugin native command aliases: let plugins declare provider-specific slash names so native Discord registration can avoid built-in command collisions; the bundled Talk voice plugin now uses `/talkvoice` natively on Discord while keeping text `/voice`.
|
- Discord/plugin native command aliases: let plugins declare provider-specific slash names so native Discord registration can avoid built-in command collisions; the bundled Talk voice plugin now uses `/talkvoice` natively on Discord while keeping text `/voice`.
|
||||||
- Daemon/Windows schtasks status normalization: derive runtime state from locale-neutral numeric `Last Run Result` codes only (without language string matching) and surface unknown when numeric result data is unavailable, preventing locale-specific misclassification drift. (#39153) Thanks @scoootscooob.
|
- Daemon/Windows schtasks status normalization: derive runtime state from locale-neutral numeric `Last Run Result` codes only (without language string matching) and surface unknown when numeric result data is unavailable, preventing locale-specific misclassification drift. (#39153) Thanks @scoootscooob.
|
||||||
|
- Telegram/polling conflict recovery: reset the polling `webhookCleared` latch on `getUpdates` 409 conflicts so webhook cleanup re-runs on restart cycles and polling avoids infinite conflict loops. (#39205) Thanks @amittell.
|
||||||
|
|
||||||
## 2026.3.2
|
## 2026.3.2
|
||||||
|
|
||||||
|
|||||||
@@ -591,6 +591,44 @@ describe("monitorTelegramProvider (grammY)", () => {
|
|||||||
expect(api.getUpdates).not.toHaveBeenCalled();
|
expect(api.getUpdates).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("resets webhookCleared latch on 409 conflict so deleteWebhook re-runs", async () => {
|
||||||
|
const abort = new AbortController();
|
||||||
|
api.deleteWebhook.mockReset();
|
||||||
|
api.deleteWebhook.mockResolvedValue(true);
|
||||||
|
|
||||||
|
const conflictError = Object.assign(
|
||||||
|
new Error("Conflict: terminated by other getUpdates request"),
|
||||||
|
{
|
||||||
|
error_code: 409,
|
||||||
|
method: "getUpdates",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let pollingCycle = 0;
|
||||||
|
runSpy
|
||||||
|
// First cycle: throw 409 conflict
|
||||||
|
.mockImplementationOnce(() =>
|
||||||
|
makeRunnerStub({
|
||||||
|
task: () => {
|
||||||
|
pollingCycle++;
|
||||||
|
return Promise.reject(conflictError);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
// Second cycle: succeed then abort
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
pollingCycle++;
|
||||||
|
return makeAbortRunner(abort);
|
||||||
|
});
|
||||||
|
|
||||||
|
await monitorTelegramProvider({ token: "tok", abortSignal: abort.signal });
|
||||||
|
|
||||||
|
// deleteWebhook should be called twice: once on initial cleanup, once after 409 reset
|
||||||
|
expect(api.deleteWebhook).toHaveBeenCalledTimes(2);
|
||||||
|
expect(pollingCycle).toBe(2);
|
||||||
|
expect(runSpy).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
it("falls back to configured webhookSecret when not passed explicitly", async () => {
|
it("falls back to configured webhookSecret when not passed explicitly", async () => {
|
||||||
await monitorTelegramProvider({
|
await monitorTelegramProvider({
|
||||||
token: "tok",
|
token: "tok",
|
||||||
|
|||||||
@@ -373,6 +373,9 @@ export async function monitorTelegramProvider(opts: MonitorTelegramOpts = {}) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const isConflict = isGetUpdatesConflict(err);
|
const isConflict = isGetUpdatesConflict(err);
|
||||||
|
if (isConflict) {
|
||||||
|
webhookCleared = false;
|
||||||
|
}
|
||||||
const isRecoverable = isRecoverableTelegramNetworkError(err, { context: "polling" });
|
const isRecoverable = isRecoverableTelegramNetworkError(err, { context: "polling" });
|
||||||
if (!isConflict && !isRecoverable) {
|
if (!isConflict && !isRecoverable) {
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
Reference in New Issue
Block a user