Telegram: stop bot on polling teardown

This commit is contained in:
liuxiaopai-ai
2026-03-02 09:30:26 +08:00
committed by Peter Steinberger
parent 666a4763ee
commit 042d06a19b
3 changed files with 33 additions and 1 deletions

View File

@@ -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({

View File

@@ -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();
}
};