mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
Telegram: stop bot on polling teardown
This commit is contained in:
committed by
Peter Steinberger
parent
666a4763ee
commit
042d06a19b
@@ -122,6 +122,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/Subagents sandbox inheritance: block sandboxed sessions from spawning cross-agent subagents that would run unsandboxed, preventing runtime sandbox downgrade via `sessions_spawn agentId`. Thanks @tdjackey for reporting.
|
||||
- Security/Workspace safe writes: harden `writeFileWithinRoot` against symlink-retarget TOCTOU races by opening existing files without truncation, creating missing files with exclusive create, deferring truncation until post-open identity+boundary validation, and removing out-of-root create artifacts on blocked races; added regression tests for truncate/create race paths. This ships in the next npm release (`2026.3.1`). Thanks @tdjackey for reporting.
|
||||
- Control UI/Cron editor: include `{ mode: "none" }` in `cron.update` patches when editing an existing job and selecting “Result delivery = None (internal)”, so saved jobs no longer keep stale announce delivery mode. Fixes #31075.
|
||||
- Telegram/Restart polling teardown: stop the Telegram bot instance when a polling cycle exits so in-process SIGUSR1 restarts fully tear down old long-poll loops before restart, reducing post-restart `getUpdates` 409 conflict storms. Fixes #31107.
|
||||
- Security/Node metadata policy: harden node platform classification against Unicode confusables and switch unknown platform defaults to a conservative allowlist that excludes `system.run`/`system.which` unless explicitly allowlisted, preventing metadata canonicalization drift from broadening node command permissions. Thanks @tdjackey for reporting.
|
||||
- Plugins/Discovery precedence: load bundled plugins before auto-discovered global extensions so bundled channel plugins win duplicate-ID resolution by default (explicit `plugins.load.paths` overrides remain highest precedence), with loader regression coverage. Landed from contributor PR #29710 by @Sid-Qin. Thanks @Sid-Qin.
|
||||
- Discord/Reconnect integrity: release Discord message listener lane immediately while preserving serialized handler execution, add HELLO-stall resume-first recovery with bounded fresh-identify fallback after repeated stalls, and extend lifecycle/listener regression coverage for forced reconnect scenarios. Landed from contributor PR #29508 by @cgdusek. Thanks @cgdusek.
|
||||
|
||||
@@ -59,6 +59,10 @@ const { createTelegramBotErrors } = vi.hoisted(() => ({
|
||||
createTelegramBotErrors: [] as unknown[],
|
||||
}));
|
||||
|
||||
const { createdBotStops } = vi.hoisted(() => ({
|
||||
createdBotStops: [] as Array<ReturnType<typeof vi.fn<() => void>>>,
|
||||
}));
|
||||
|
||||
const { computeBackoff, sleepWithAbort } = vi.hoisted(() => ({
|
||||
computeBackoff: vi.fn(() => 0),
|
||||
sleepWithAbort: vi.fn(async () => undefined),
|
||||
@@ -111,6 +115,8 @@ vi.mock("./bot.js", () => ({
|
||||
if (nextError) {
|
||||
throw nextError;
|
||||
}
|
||||
const stop = vi.fn<() => void>();
|
||||
createdBotStops.push(stop);
|
||||
handlers.message = async (ctx: MockCtx) => {
|
||||
const chatId = ctx.message.chat.id;
|
||||
const isGroup = ctx.message.chat.type !== "private";
|
||||
@@ -128,7 +134,7 @@ vi.mock("./bot.js", () => ({
|
||||
api,
|
||||
me: { username: "mybot" },
|
||||
init: initSpy,
|
||||
stop: vi.fn(),
|
||||
stop,
|
||||
start: vi.fn(),
|
||||
};
|
||||
},
|
||||
@@ -179,6 +185,7 @@ describe("monitorTelegramProvider (grammY)", () => {
|
||||
registerUnhandledRejectionHandlerMock.mockClear();
|
||||
resetUnhandledRejection();
|
||||
createTelegramBotErrors.length = 0;
|
||||
createdBotStops.length = 0;
|
||||
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
});
|
||||
|
||||
@@ -382,6 +389,22 @@ describe("monitorTelegramProvider (grammY)", () => {
|
||||
expect(runSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("stops bot instance when polling cycle exits", async () => {
|
||||
const abort = new AbortController();
|
||||
runSpy.mockImplementationOnce(() =>
|
||||
makeRunnerStub({
|
||||
task: async () => {
|
||||
abort.abort();
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await monitorTelegramProvider({ token: "tok", abortSignal: abort.signal });
|
||||
|
||||
expect(createdBotStops.length).toBe(1);
|
||||
expect(createdBotStops[0]).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("surfaces non-recoverable errors", async () => {
|
||||
runSpy.mockImplementationOnce(() =>
|
||||
makeRunnerStub({
|
||||
|
||||
@@ -270,6 +270,13 @@ export async function monitorTelegramProvider(opts: MonitorTelegramOpts = {}) {
|
||||
});
|
||||
return stopPromise;
|
||||
};
|
||||
const stopBot = () => {
|
||||
return Promise.resolve(bot.stop())
|
||||
.then(() => undefined)
|
||||
.catch(() => {
|
||||
// Bot may already be stopped by runner stop/abort paths.
|
||||
});
|
||||
};
|
||||
const stopOnAbort = () => {
|
||||
if (opts.abortSignal?.aborted) {
|
||||
void stopRunner();
|
||||
@@ -309,6 +316,7 @@ export async function monitorTelegramProvider(opts: MonitorTelegramOpts = {}) {
|
||||
} finally {
|
||||
opts.abortSignal?.removeEventListener("abort", stopOnAbort);
|
||||
await stopRunner();
|
||||
await stopBot();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user