mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-30 01:06:11 +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.
|
||||
- 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.
|
||||
- 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
|
||||
|
||||
|
||||
@@ -591,6 +591,44 @@ describe("monitorTelegramProvider (grammY)", () => {
|
||||
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 () => {
|
||||
await monitorTelegramProvider({
|
||||
token: "tok",
|
||||
|
||||
@@ -373,6 +373,9 @@ export async function monitorTelegramProvider(opts: MonitorTelegramOpts = {}) {
|
||||
throw err;
|
||||
}
|
||||
const isConflict = isGetUpdatesConflict(err);
|
||||
if (isConflict) {
|
||||
webhookCleared = false;
|
||||
}
|
||||
const isRecoverable = isRecoverableTelegramNetworkError(err, { context: "polling" });
|
||||
if (!isConflict && !isRecoverable) {
|
||||
throw err;
|
||||
|
||||
Reference in New Issue
Block a user