diff --git a/extensions/telegram/src/bot.fetch-abort.test.ts b/extensions/telegram/src/bot.fetch-abort.test.ts index 258215d4c6d..4d1d253107d 100644 --- a/extensions/telegram/src/bot.fetch-abort.test.ts +++ b/extensions/telegram/src/bot.fetch-abort.test.ts @@ -1,8 +1,18 @@ import { describe, expect, it, vi } from "vitest"; -import { botCtorSpy } from "./bot.create-telegram-bot.test-harness.js"; -import { createTelegramBot } from "./bot.js"; import { getTelegramNetworkErrorOrigin } from "./network-errors.js"; +const { botCtorSpy, telegramBotRuntimeForTest } = + await import("./bot.create-telegram-bot.test-harness.js"); +const { createTelegramBot, setTelegramBotRuntimeForTest } = await import("./bot.js"); +const { setBotHandlersRuntimeForTest } = await import("./bot-handlers.runtime.js"); +const { setBotMessageDispatchRuntimeForTest } = await import("./bot-message-dispatch.js"); +const { setBotNativeCommandsRuntimeForTest } = await import("./bot-native-commands.js"); + +setTelegramBotRuntimeForTest(telegramBotRuntimeForTest); +setBotHandlersRuntimeForTest(); +setBotMessageDispatchRuntimeForTest(); +setBotNativeCommandsRuntimeForTest(); + function createWrappedTelegramClientFetch(proxyFetch: typeof fetch) { const shutdown = new AbortController(); botCtorSpy.mockClear(); diff --git a/extensions/telegram/src/bot.runtime.ts b/extensions/telegram/src/bot.runtime.ts new file mode 100644 index 00000000000..edcb833c369 --- /dev/null +++ b/extensions/telegram/src/bot.runtime.ts @@ -0,0 +1,4 @@ +export { sequentialize } from "@grammyjs/runner"; +export { apiThrottler } from "@grammyjs/transformer-throttler"; +export { Bot } from "grammy"; +export type { ApiClientOptions } from "grammy"; diff --git a/extensions/telegram/src/bot.ts b/extensions/telegram/src/bot.ts index 450c68b4aad..617a1231798 100644 --- a/extensions/telegram/src/bot.ts +++ b/extensions/telegram/src/bot.ts @@ -1,7 +1,3 @@ -import { sequentialize } from "@grammyjs/runner"; -import { apiThrottler } from "@grammyjs/transformer-throttler"; -import type { ApiClientOptions } from "grammy"; -import { Bot } from "grammy"; import { resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime"; import { resolveThreadBindingIdleTimeoutMsForChannel, @@ -37,6 +33,7 @@ import { resolveTelegramUpdateId, type TelegramUpdateKeyContext, } from "./bot-updates.js"; +import { apiThrottler, Bot, sequentialize, type ApiClientOptions } from "./bot.runtime.js"; import { buildTelegramGroupPeerId, resolveTelegramStreamMode } from "./bot/helpers.js"; import { resolveTelegramTransport, type TelegramTransport } from "./fetch.js"; import { tagTelegramNetworkError } from "./network-errors.js"; @@ -71,6 +68,26 @@ export type TelegramBotOptions = { export { getTelegramSequentialKey }; +type TelegramBotRuntime = { + Bot: typeof Bot; + sequentialize: typeof sequentialize; + apiThrottler: typeof apiThrottler; + loadConfig: typeof loadConfig; +}; + +const DEFAULT_TELEGRAM_BOT_RUNTIME: TelegramBotRuntime = { + Bot, + sequentialize, + apiThrottler, + loadConfig, +}; + +let telegramBotRuntimeForTest: TelegramBotRuntime | undefined; + +export function setTelegramBotRuntimeForTest(runtime?: TelegramBotRuntime): void { + telegramBotRuntimeForTest = runtime; +} + type TelegramFetchInput = Parameters>[0]; type TelegramFetchInit = Parameters>[1]; type GlobalFetchInput = Parameters[0]; @@ -105,8 +122,9 @@ function extractTelegramApiMethod(input: TelegramFetchInput): string | null { } export function createTelegramBot(opts: TelegramBotOptions) { + const botRuntime = telegramBotRuntimeForTest ?? DEFAULT_TELEGRAM_BOT_RUNTIME; const runtime: RuntimeEnv = opts.runtime ?? createNonExitingRuntime(); - const cfg = opts.config ?? loadConfig(); + const cfg = opts.config ?? botRuntime.loadConfig(); const account = resolveTelegramAccount({ cfg, accountId: opts.accountId, @@ -220,8 +238,8 @@ export function createTelegramBot(opts: TelegramBotOptions) { } : undefined; - const bot = new Bot(opts.token, client ? { client } : undefined); - bot.api.config.use(apiThrottler()); + const bot = new botRuntime.Bot(opts.token, client ? { client } : undefined); + bot.api.config.use(botRuntime.apiThrottler()); // Catch all errors from bot middleware to prevent unhandled rejections bot.catch((err) => { runtime.error?.(danger(`telegram bot error: ${formatUncaughtError(err)}`)); @@ -296,7 +314,7 @@ export function createTelegramBot(opts: TelegramBotOptions) { } }); - bot.use(sequentialize(getTelegramSequentialKey)); + bot.use(botRuntime.sequentialize(getTelegramSequentialKey)); const rawUpdateLogger = createSubsystemLogger("gateway/channels/telegram/raw-update"); const MAX_RAW_UPDATE_CHARS = 8000;