telegram-user: load mtcute deps via ESM

This commit is contained in:
Muhammed Mukhthar CM 2026-01-27 00:34:23 +00:00
parent 525c148959
commit a3184b920a
4 changed files with 54 additions and 22 deletions

View File

@ -1,26 +1,39 @@
import { BaseTelegramClient, NodePlatform, TelegramClient } from "@mtcute/node"; type MtcuteNode = typeof import("@mtcute/node");
class ClawdbotTelegramUserPlatform extends NodePlatform { let mtcuteNodePromise: Promise<MtcuteNode> | null = null;
// mtcute's NodePlatform.beforeExit installs SIGINT/SIGTERM handlers that re-send the signal,
// which can race with Clawdbot's graceful shutdown and close sqlite while writes are pending. async function loadMtcuteNode(): Promise<MtcuteNode> {
// We only hook into process exit events (no signal handlers) and rely on Clawdbot to stop cleanly. mtcuteNodePromise ??= import("@mtcute/node");
override beforeExit(fn: () => void): () => void { return mtcuteNodePromise;
const onBeforeExit = () => fn();
const onExit = () => fn();
process.once("beforeExit", onBeforeExit);
process.once("exit", onExit);
return () => {
process.off("beforeExit", onBeforeExit);
process.off("exit", onExit);
};
}
} }
export function createTelegramUserClient(params: { export async function createTelegramUserClient(params: {
apiId: number; apiId: number;
apiHash: string; apiHash: string;
storagePath: string; storagePath: string;
}) { }): Promise<import("@mtcute/node").TelegramClient> {
// When loaded via jiti (plugin loader), dependencies often resolve through the "require" export
// condition. mtcute prints a deprecation warning from its CommonJS bundle. Dynamic import forces
// the "import" condition (ESM), eliminating the warning.
const { BaseTelegramClient, TelegramClient, NodePlatform } = await loadMtcuteNode();
class ClawdbotTelegramUserPlatform extends NodePlatform {
// mtcute's default NodePlatform.beforeExit installs SIGINT/SIGTERM handlers that re-send the
// signal, which can race with Clawdbot's graceful shutdown and close sqlite while writes are
// pending. We only hook into process exit events (no signal handlers) and rely on Clawdbot to
// stop cleanly.
override beforeExit(fn: () => void): () => void {
const onBeforeExit = () => fn();
const onExit = () => fn();
process.once("beforeExit", onBeforeExit);
process.once("exit", onExit);
return () => {
process.off("beforeExit", onBeforeExit);
process.off("exit", onExit);
};
}
}
const client = new BaseTelegramClient({ const client = new BaseTelegramClient({
apiId: params.apiId, apiId: params.apiId,
apiHash: params.apiHash, apiHash: params.apiHash,

View File

@ -39,7 +39,7 @@ export async function loginTelegramUser(params: {
}) { }) {
const { apiId, apiHash, storagePath, runtime } = params; const { apiId, apiHash, storagePath, runtime } = params;
ensureTelegramUserSessionDir({ sessionPath: storagePath }); ensureTelegramUserSessionDir({ sessionPath: storagePath });
const client = createTelegramUserClient({ apiId, apiHash, storagePath }); const client = await createTelegramUserClient({ apiId, apiHash, storagePath });
let lastUrl = ""; let lastUrl = "";
const passwordEnv = process.env.TELEGRAM_USER_PASSWORD?.trim() || undefined; const passwordEnv = process.env.TELEGRAM_USER_PASSWORD?.trim() || undefined;

View File

@ -1,5 +1,4 @@
import fs from "node:fs"; import fs from "node:fs";
import { Dispatcher, filters } from "@mtcute/dispatcher";
import type { RuntimeEnv } from "clawdbot/plugin-sdk"; import type { RuntimeEnv } from "clawdbot/plugin-sdk";
import { createTelegramUserClient } from "../client.js"; import { createTelegramUserClient } from "../client.js";
@ -10,6 +9,15 @@ import { setActiveTelegramUserClient } from "../active-client.js";
import { createTelegramUserMessageHandler } from "./handler.js"; import { createTelegramUserMessageHandler } from "./handler.js";
import type { CoreConfig } from "../types.js"; import type { CoreConfig } from "../types.js";
type MtcuteDispatcher = typeof import("@mtcute/dispatcher");
let mtcuteDispatcherPromise: Promise<MtcuteDispatcher> | null = null;
async function loadMtcuteDispatcher(): Promise<MtcuteDispatcher> {
mtcuteDispatcherPromise ??= import("@mtcute/dispatcher");
return mtcuteDispatcherPromise;
}
export type MonitorTelegramUserOpts = { export type MonitorTelegramUserOpts = {
runtime?: RuntimeEnv; runtime?: RuntimeEnv;
abortSignal?: AbortSignal; abortSignal?: AbortSignal;
@ -48,7 +56,7 @@ export async function monitorTelegramUserProvider(opts: MonitorTelegramUserOpts
"Telegram user session missing. Run `clawdbot channels login --channel telegram-user` first.", "Telegram user session missing. Run `clawdbot channels login --channel telegram-user` first.",
); );
} }
const client = createTelegramUserClient({ apiId, apiHash, storagePath }); const client = await createTelegramUserClient({ apiId, apiHash, storagePath });
setActiveTelegramUserClient(client); setActiveTelegramUserClient(client);
const stop = async () => { const stop = async () => {
@ -66,6 +74,7 @@ export async function monitorTelegramUserProvider(opts: MonitorTelegramUserOpts
await client.start(); await client.start();
const { Dispatcher, filters } = await loadMtcuteDispatcher();
const dispatcher = Dispatcher.for(client); const dispatcher = Dispatcher.for(client);
const self = await client.getMe().catch(() => undefined); const self = await client.getMe().catch(() => undefined);
const handleMessage = createTelegramUserMessageHandler({ const handleMessage = createTelegramUserMessageHandler({

View File

@ -1,6 +1,5 @@
import fs from "node:fs"; import fs from "node:fs";
import type { TelegramClient } from "@mtcute/node"; import type { TelegramClient } from "@mtcute/node";
import { InputMedia } from "@mtcute/core";
import type { PollInput } from "clawdbot/plugin-sdk"; import type { PollInput } from "clawdbot/plugin-sdk";
import { getTelegramUserRuntime } from "./runtime.js"; import { getTelegramUserRuntime } from "./runtime.js";
@ -14,6 +13,15 @@ export type TelegramUserSendResult = {
chatId: string; chatId: string;
}; };
type MtcuteCore = typeof import("@mtcute/core");
let mtcuteCorePromise: Promise<MtcuteCore> | null = null;
async function loadMtcuteCore(): Promise<MtcuteCore> {
mtcuteCorePromise ??= import("@mtcute/core");
return mtcuteCorePromise;
}
type NormalizedPollInput = { type NormalizedPollInput = {
question: string; question: string;
options: string[]; options: string[];
@ -129,7 +137,7 @@ async function resolveClient(params: {
"Telegram user session missing. Run `clawdbot channels login --channel telegram-user` first.", "Telegram user session missing. Run `clawdbot channels login --channel telegram-user` first.",
); );
} }
const client = createTelegramUserClient({ apiId, apiHash, storagePath }); const client = await createTelegramUserClient({ apiId, apiHash, storagePath });
await client.start(); await client.start();
return { client, stopOnDone: true }; return { client, stopOnDone: true };
} }
@ -180,6 +188,7 @@ export async function sendMediaTelegramUser(
accountId: opts.accountId, accountId: opts.accountId,
}); });
try { try {
const { InputMedia } = await loadMtcuteCore();
const resolved = resolveTargetAndThread(to, opts.threadId); const resolved = resolveTargetAndThread(to, opts.threadId);
const target = resolveTelegramUserPeer(resolved.target); const target = resolveTelegramUserPeer(resolved.target);
const media = await getTelegramUserRuntime().media.loadWebMedia(opts.mediaUrl, opts.maxBytes); const media = await getTelegramUserRuntime().media.loadWebMedia(opts.mediaUrl, opts.maxBytes);
@ -220,6 +229,7 @@ export async function sendPollTelegramUser(
accountId: opts.accountId, accountId: opts.accountId,
}); });
try { try {
const { InputMedia } = await loadMtcuteCore();
const resolved = resolveTargetAndThread(to, opts.threadId); const resolved = resolveTargetAndThread(to, opts.threadId);
const target = resolveTelegramUserPeer(resolved.target); const target = resolveTelegramUserPeer(resolved.target);
const normalized = normalizePollInput(poll); const normalized = normalizePollInput(poll);