openclaw-matrix-multiaccounts/src/channels/plugins/onboarding/telegram.ts
Peter Steinberger c379191f80 chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
2026-01-14 15:02:19 +00:00

269 lines
7.8 KiB
TypeScript

import type { ClawdbotConfig } from "../../../config/config.js";
import type { DmPolicy } from "../../../config/types.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import {
listTelegramAccountIds,
resolveDefaultTelegramAccountId,
resolveTelegramAccount,
} from "../../../telegram/accounts.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "telegram" as const;
function setTelegramDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy) {
const allowFrom =
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.telegram?.allowFrom) : undefined;
return {
...cfg,
channels: {
...cfg.channels,
telegram: {
...cfg.channels?.telegram,
dmPolicy,
...(allowFrom ? { allowFrom } : {}),
},
},
};
}
async function noteTelegramTokenHelp(prompter: WizardPrompter): Promise<void> {
await prompter.note(
[
"1) Open Telegram and chat with @BotFather",
"2) Run /newbot (or /mybots)",
"3) Copy the token (looks like 123456:ABC...)",
"Tip: you can also set TELEGRAM_BOT_TOKEN in your env.",
`Docs: ${formatDocsLink("/telegram")}`,
"Website: https://clawd.bot",
].join("\n"),
"Telegram bot token",
);
}
async function promptTelegramAllowFrom(params: {
cfg: ClawdbotConfig;
prompter: WizardPrompter;
accountId: string;
}): Promise<ClawdbotConfig> {
const { cfg, prompter, accountId } = params;
const resolved = resolveTelegramAccount({ cfg, accountId });
const existingAllowFrom = resolved.config.allowFrom ?? [];
const entry = await prompter.text({
message: "Telegram allowFrom (user id)",
placeholder: "123456789",
initialValue: existingAllowFrom[0] ? String(existingAllowFrom[0]) : undefined,
validate: (value) => {
const raw = String(value ?? "").trim();
if (!raw) return "Required";
if (!/^\d+$/.test(raw)) return "Use a numeric Telegram user id";
return undefined;
},
});
const normalized = String(entry).trim();
const merged = [
...existingAllowFrom.map((item) => String(item).trim()).filter(Boolean),
normalized,
];
const unique = [...new Set(merged)];
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
telegram: {
...cfg.channels?.telegram,
enabled: true,
dmPolicy: "allowlist",
allowFrom: unique,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
telegram: {
...cfg.channels?.telegram,
enabled: true,
accounts: {
...cfg.channels?.telegram?.accounts,
[accountId]: {
...cfg.channels?.telegram?.accounts?.[accountId],
enabled: cfg.channels?.telegram?.accounts?.[accountId]?.enabled ?? true,
dmPolicy: "allowlist",
allowFrom: unique,
},
},
},
},
};
}
const dmPolicy: ChannelOnboardingDmPolicy = {
label: "Telegram",
channel,
policyKey: "channels.telegram.dmPolicy",
allowFromKey: "channels.telegram.allowFrom",
getCurrent: (cfg) => cfg.channels?.telegram?.dmPolicy ?? "pairing",
setPolicy: (cfg, policy) => setTelegramDmPolicy(cfg, policy),
};
export const telegramOnboardingAdapter: ChannelOnboardingAdapter = {
channel,
getStatus: async ({ cfg }) => {
const configured = listTelegramAccountIds(cfg).some((accountId) =>
Boolean(resolveTelegramAccount({ cfg, accountId }).token),
);
return {
channel,
configured,
statusLines: [`Telegram: ${configured ? "configured" : "needs token"}`],
selectionHint: configured ? "recommended · configured" : "recommended · newcomer-friendly",
quickstartScore: configured ? 1 : 10,
};
},
configure: async ({
cfg,
prompter,
accountOverrides,
shouldPromptAccountIds,
forceAllowFrom,
}) => {
const telegramOverride = accountOverrides.telegram?.trim();
const defaultTelegramAccountId = resolveDefaultTelegramAccountId(cfg);
let telegramAccountId = telegramOverride
? normalizeAccountId(telegramOverride)
: defaultTelegramAccountId;
if (shouldPromptAccountIds && !telegramOverride) {
telegramAccountId = await promptAccountId({
cfg,
prompter,
label: "Telegram",
currentId: telegramAccountId,
listAccountIds: listTelegramAccountIds,
defaultAccountId: defaultTelegramAccountId,
});
}
let next = cfg;
const resolvedAccount = resolveTelegramAccount({
cfg: next,
accountId: telegramAccountId,
});
const accountConfigured = Boolean(resolvedAccount.token);
const allowEnv = telegramAccountId === DEFAULT_ACCOUNT_ID;
const canUseEnv = allowEnv && Boolean(process.env.TELEGRAM_BOT_TOKEN?.trim());
const hasConfigToken = Boolean(
resolvedAccount.config.botToken || resolvedAccount.config.tokenFile,
);
let token: string | null = null;
if (!accountConfigured) {
await noteTelegramTokenHelp(prompter);
}
if (canUseEnv && !resolvedAccount.config.botToken) {
const keepEnv = await prompter.confirm({
message: "TELEGRAM_BOT_TOKEN detected. Use env var?",
initialValue: true,
});
if (keepEnv) {
next = {
...next,
channels: {
...next.channels,
telegram: {
...next.channels?.telegram,
enabled: true,
},
},
};
} else {
token = String(
await prompter.text({
message: "Enter Telegram bot token",
validate: (value) => (value?.trim() ? undefined : "Required"),
}),
).trim();
}
} else if (hasConfigToken) {
const keep = await prompter.confirm({
message: "Telegram token already configured. Keep it?",
initialValue: true,
});
if (!keep) {
token = String(
await prompter.text({
message: "Enter Telegram bot token",
validate: (value) => (value?.trim() ? undefined : "Required"),
}),
).trim();
}
} else {
token = String(
await prompter.text({
message: "Enter Telegram bot token",
validate: (value) => (value?.trim() ? undefined : "Required"),
}),
).trim();
}
if (token) {
if (telegramAccountId === DEFAULT_ACCOUNT_ID) {
next = {
...next,
channels: {
...next.channels,
telegram: {
...next.channels?.telegram,
enabled: true,
botToken: token,
},
},
};
} else {
next = {
...next,
channels: {
...next.channels,
telegram: {
...next.channels?.telegram,
enabled: true,
accounts: {
...next.channels?.telegram?.accounts,
[telegramAccountId]: {
...next.channels?.telegram?.accounts?.[telegramAccountId],
enabled: next.channels?.telegram?.accounts?.[telegramAccountId]?.enabled ?? true,
botToken: token,
},
},
},
},
};
}
}
if (forceAllowFrom) {
next = await promptTelegramAllowFrom({
cfg: next,
prompter,
accountId: telegramAccountId,
});
}
return { cfg: next, accountId: telegramAccountId };
},
dmPolicy,
disable: (cfg) => ({
...cfg,
channels: {
...cfg.channels,
telegram: { ...cfg.channels?.telegram, enabled: false },
},
}),
};