Telegram: lazy load send runtime from entrypoints

This commit is contained in:
Peter Steinberger
2026-04-07 14:14:54 +01:00
parent 47563305a2
commit 33e93e2a07
5 changed files with 53 additions and 25 deletions

View File

@@ -1,11 +1,15 @@
import { describe, expect, it } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import entry from "./index.js";
import setupEntry from "./setup-entry.js";
describe("telegram bundled entries", () => {
it("loads the channel plugin without importing the broad api barrel", () => {
const plugin = entry.loadChannelPlugin();
expect(plugin.id).toBe("telegram");
beforeEach(() => {
vi.useRealTimers();
});
it("declares the channel entry without importing the broad api barrel", () => {
expect(entry.id).toBe("telegram");
expect(entry.name).toBe("Telegram");
});
it("loads the setup plugin without importing the broad api barrel", () => {

View File

@@ -3,7 +3,7 @@ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entr
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./channel-plugin-api.js",
specifier: "./src/channel.setup.js",
exportName: "telegramSetupPlugin",
},
secrets: {

View File

@@ -60,7 +60,6 @@ import * as probeModule from "./probe.js";
import { resolveTelegramReactionLevel } from "./reaction-level.js";
import { getTelegramRuntime } from "./runtime.js";
import { collectTelegramSecurityAuditFindings } from "./security-audit.js";
import { sendMessageTelegram, sendPollTelegram, sendTypingTelegram } from "./send.js";
import { resolveTelegramSessionConversation } from "./session-conversation.js";
import { telegramSetupAdapter } from "./setup-core.js";
import { telegramSetupWizard } from "./setup-surface.js";
@@ -82,7 +81,14 @@ import { buildTelegramThreadingToolContext } from "./threading-tool-context.js";
import { resolveTelegramToken } from "./token.js";
import { parseTelegramTopicConversation } from "./topic-conversation.js";
type TelegramSendFn = typeof sendMessageTelegram;
type TelegramSendFn = typeof import("./send.js").sendMessageTelegram;
let telegramSendModulePromise: Promise<typeof import("./send.js")> | undefined;
async function loadTelegramSendModule() {
telegramSendModulePromise ??= import("./send.js");
return await telegramSendModulePromise;
}
type TelegramSendOptions = NonNullable<Parameters<TelegramSendFn>[2]>;
@@ -121,11 +127,11 @@ function getOptionalTelegramRuntime() {
}
}
function resolveTelegramSend(deps?: OutboundSendDeps): TelegramSendFn {
async function resolveTelegramSend(deps?: OutboundSendDeps): Promise<TelegramSendFn> {
return (
resolveOutboundSendDep<TelegramSendFn>(deps, "telegram") ??
getOptionalTelegramRuntime()?.channel?.telegram?.sendMessageTelegram ??
sendMessageTelegram
(await loadTelegramSendModule()).sendMessageTelegram
);
}
@@ -175,7 +181,7 @@ async function sendTelegramOutbound(params: {
silent?: boolean | null;
gatewayClientScopes?: readonly string[] | null;
}) {
const send = resolveTelegramSend(params.deps);
const send = await resolveTelegramSend(params.deps);
return await send(
params.to,
params.text,
@@ -961,7 +967,8 @@ export const telegramPlugin = createChatChannelPlugin({
if (!token) {
throw new Error("telegram token not configured");
}
await resolveTelegramSend()(id, message, { token, accountId });
const send = await resolveTelegramSend();
await send(id, message, { token, accountId });
},
},
},
@@ -1004,6 +1011,7 @@ export const telegramPlugin = createChatChannelPlugin({
: typeof target.threadId === "string"
? Number.parseInt(target.threadId, 10)
: undefined;
const { sendTypingTelegram } = await loadTelegramSendModule();
await sendTypingTelegram(target.to, {
cfg,
accountId: target.accountId ?? undefined,
@@ -1030,7 +1038,7 @@ export const telegramPlugin = createChatChannelPlugin({
forceDocument,
gatewayClientScopes,
}) => {
const send = resolveTelegramSend(deps);
const send = await resolveTelegramSend(deps);
const result = await sendTelegramPayloadMessages({
send,
to,
@@ -1108,15 +1116,17 @@ export const telegramPlugin = createChatChannelPlugin({
silent,
isAnonymous,
gatewayClientScopes,
}) =>
await sendPollTelegram(to, poll, {
}) => {
const { sendPollTelegram } = await loadTelegramSendModule();
return await sendPollTelegram(to, poll, {
cfg,
accountId: accountId ?? undefined,
messageThreadId: parseTelegramThreadId(threadId),
silent: silent ?? undefined,
isAnonymous: isAnonymous ?? undefined,
gatewayClientScopes,
}),
});
},
},
},
});

View File

@@ -18,21 +18,27 @@ import type { TelegramInlineButtons } from "./button-types.js";
import { resolveTelegramInlineButtons } from "./button-types.js";
import { markdownToTelegramHtmlChunks } from "./format.js";
import { parseTelegramReplyToMessageId, parseTelegramThreadId } from "./outbound-params.js";
import { sendMessageTelegram } from "./send.js";
export const TELEGRAM_TEXT_CHUNK_LIMIT = 4000;
type TelegramSendFn = typeof sendMessageTelegram;
type TelegramSendFn = typeof import("./send.js").sendMessageTelegram;
type TelegramSendOpts = Parameters<TelegramSendFn>[2];
function resolveTelegramSendContext(params: {
let telegramSendModulePromise: Promise<typeof import("./send.js")> | undefined;
async function loadTelegramSendModule() {
telegramSendModulePromise ??= import("./send.js");
return await telegramSendModulePromise;
}
async function resolveTelegramSendContext(params: {
cfg: NonNullable<TelegramSendOpts>["cfg"];
deps?: OutboundSendDeps;
accountId?: string | null;
replyToId?: string | null;
threadId?: string | number | null;
gatewayClientScopes?: readonly string[];
}): {
}): Promise<{
send: TelegramSendFn;
baseOpts: {
cfg: NonNullable<TelegramSendOpts>["cfg"];
@@ -43,9 +49,10 @@ function resolveTelegramSendContext(params: {
accountId?: string;
gatewayClientScopes?: readonly string[];
};
} {
}> {
const send =
resolveOutboundSendDep<TelegramSendFn>(params.deps, "telegram") ?? sendMessageTelegram;
resolveOutboundSendDep<TelegramSendFn>(params.deps, "telegram") ??
(await loadTelegramSendModule()).sendMessageTelegram;
return {
send,
baseOpts: {
@@ -126,7 +133,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
threadId,
gatewayClientScopes,
}) => {
const { send, baseOpts } = resolveTelegramSendContext({
const { send, baseOpts } = await resolveTelegramSendContext({
cfg,
deps,
accountId,
@@ -152,7 +159,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
forceDocument,
gatewayClientScopes,
}) => {
const { send, baseOpts } = resolveTelegramSendContext({
const { send, baseOpts } = await resolveTelegramSendContext({
cfg,
deps,
accountId,
@@ -182,7 +189,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
forceDocument,
gatewayClientScopes,
}) => {
const { send, baseOpts } = resolveTelegramSendContext({
const { send, baseOpts } = await resolveTelegramSendContext({
cfg,
deps,
accountId,

View File

@@ -18,7 +18,6 @@ import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { createForumTopicTelegram } from "./send.js";
import { resolveTelegramToken } from "./token.js";
const DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS = 24 * 60 * 60 * 1000;
@@ -26,6 +25,13 @@ const DEFAULT_THREAD_BINDING_MAX_AGE_MS = 0;
const THREAD_BINDINGS_SWEEP_INTERVAL_MS = 60_000;
const STORE_VERSION = 1;
let telegramSendModulePromise: Promise<typeof import("./send.js")> | undefined;
async function loadTelegramSendModule() {
telegramSendModulePromise ??= import("./send.js");
return await telegramSendModulePromise;
}
type TelegramBindingTargetKind = "subagent" | "acp";
export type TelegramThreadBindingRecord = {
@@ -593,6 +599,7 @@ export function createTelegramThreadBindingManager(
if (!tokenResolution.token) {
return null;
}
const { createForumTopicTelegram } = await loadTelegramSendModule();
const result = await createForumTopicTelegram(chatId, threadName, {
cfg,
token: tokenResolution.token,