| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- // Pure validation + templating helpers for 3x-ui's Telegram bot settings.
- // Grounded in internal/web/service/tgbot/tgbot.go (admin ids parsed with
- // strconv.ParseInt(_,10,64); token handed to telego.NewBot → api.telegram.org)
- // and the panel's tg* settings (tgRunTime uses robfig/cron). No React/DOM.
- export interface BotConfig {
- token: string;
- adminIds: string; // raw comma-separated input
- runTime: string; // @daily | @every 8h | 5/6-field cron
- }
- export interface TokenValidation {
- valid: boolean;
- botId?: string;
- error?: string;
- }
- export interface AdminIdsResult {
- ids: number[];
- invalid: string[];
- }
- export type CronKind = 'macro' | 'every' | 'cron' | 'invalid';
- export interface CronValidation {
- valid: boolean;
- kind: CronKind;
- error?: string;
- }
- // BotFather tokens are <bot-id>:<35+ char secret of [A-Za-z0-9_-]>.
- const TOKEN_RE = /^(\d+):[A-Za-z0-9_-]{35,}$/;
- export function validateBotToken(token: string): TokenValidation {
- const t = token.trim();
- if (!t) return { valid: false, error: 'Token is empty.' };
- const m = TOKEN_RE.exec(t);
- if (!m) {
- return { valid: false, error: 'Expected the BotFather format <bot-id>:<35+ char secret>.' };
- }
- return { valid: true, botId: m[1] };
- }
- export function parseAdminIds(raw: string): AdminIdsResult {
- const ids: number[] = [];
- const invalid: string[] = [];
- for (const part of raw.split(',').map((s) => s.trim()).filter(Boolean)) {
- // Telegram chat ids are integers; group/channel ids are negative.
- if (/^-?\d+$/.test(part)) ids.push(Number(part));
- else invalid.push(part);
- }
- return { ids, invalid };
- }
- // robfig/cron predefined macros (note: @reboot is NOT supported).
- const CRON_MACROS = new Set([
- '@yearly',
- '@annually',
- '@monthly',
- '@weekly',
- '@daily',
- '@midnight',
- '@hourly',
- ]);
- // Go duration: one or more <number><unit> chunks (ns, us/µs, ms, s, m, h).
- const GO_DURATION_RE = /^(\d+(\.\d+)?(ns|us|µs|ms|s|m|h))+$/;
- export function validateRunTime(s: string): CronValidation {
- const v = s.trim();
- if (!v) return { valid: false, kind: 'invalid', error: 'Schedule is empty.' };
- if (v.startsWith('@every ')) {
- const dur = v.slice('@every '.length).trim();
- if (GO_DURATION_RE.test(dur)) return { valid: true, kind: 'every' };
- return { valid: false, kind: 'invalid', error: `Invalid @every duration: "${dur}".` };
- }
- if (v.startsWith('@')) {
- if (CRON_MACROS.has(v)) return { valid: true, kind: 'macro' };
- return { valid: false, kind: 'invalid', error: `Unknown macro: "${v}".` };
- }
- const fields = v.split(/\s+/);
- if (fields.length === 5 || fields.length === 6) return { valid: true, kind: 'cron' };
- return {
- valid: false,
- kind: 'invalid',
- error: 'Use a 5/6-field cron, an @macro (e.g. @daily), or @every <duration>.',
- };
- }
- export function telegramApiBase(token: string): string {
- return `https://api.telegram.org/bot${token.trim()}`;
- }
- export function renderMessageTemplate(tpl: string, vars: Record<string, string>): string {
- return tpl.replace(/\{\{\s*(\w+)\s*\}\}/g, (_match, key: string) =>
- key in vars ? vars[key] : `{{${key}}}`,
- );
- }
- export function buildBotConfigSummary(c: BotConfig): {
- tgBotEnable: boolean;
- tgBotToken: string;
- tgBotChatId: string;
- tgRunTime: string;
- } {
- const { ids } = parseAdminIds(c.adminIds);
- return {
- tgBotEnable: true,
- tgBotToken: c.token.trim(),
- tgBotChatId: ids.join(','),
- tgRunTime: c.runTime.trim(),
- };
- }
|