"use client"; import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import clsx from "clsx"; type TelegramGatewayView = { enabled: boolean; mode: "webhook" | "polling"; botTokenConfigured: boolean; botUsername?: string; dmPolicy: "allowlist" | "open" | "disabled"; allowFrom: string[]; groupPolicy: "allowlist" | "open" | "disabled"; groups: string[]; requireMentionInGroups: boolean; defaultProjectId: string; groupProjectRoutes: Array<{ chatId: string; threadId?: number; projectId: string; label?: string }>; webhookSecretConfigured: boolean; webhookUrl?: string; lastConfiguredAt?: string; lastConfiguredBy?: string; lastError?: string; processedUpdateCount: number; }; type Draft = { enabled: boolean; mode: "webhook" | "polling"; botToken: string; dmPolicy: "allowlist" | "open" | "disabled"; allowFromText: string; groupPolicy: "allowlist" | "open" | "disabled"; groupsText: string; requireMentionInGroups: boolean; defaultProjectId: string; groupProjectRoutesText: string; webhookSecret: string; webhookUrl: string; }; function draftFromView(view: TelegramGatewayView): Draft { return { enabled: view.enabled, mode: view.mode, botToken: "", dmPolicy: view.dmPolicy, allowFromText: view.allowFrom.join("\n"), groupPolicy: view.groupPolicy, groupsText: view.groups.join("\n"), requireMentionInGroups: view.requireMentionInGroups, defaultProjectId: view.defaultProjectId, groupProjectRoutesText: formatGroupProjectRoutes(view.groupProjectRoutes), webhookSecret: "", webhookUrl: view.webhookUrl ?? "", }; } function parseLines(value: string) { return value .split(/\r?\n/) .map((item) => item.trim()) .filter(Boolean); } function formatGroupProjectRoutes(routes: TelegramGatewayView["groupProjectRoutes"]) { return routes .map((route) => { const chatPart = route.threadId != null ? `${route.chatId}#${route.threadId}` : route.chatId; return [chatPart, route.projectId, route.label].filter(Boolean).join(" "); }) .join("\n"); } function parseGroupProjectRoutes(value: string) { return parseLines(value) .map((line) => { const [chatAndTopic, projectId, ...labelParts] = line.split(/\s+/); if (!chatAndTopic || !projectId) { return null; } const [chatId, threadIdRaw] = chatAndTopic.split("#"); const threadId = Number(threadIdRaw); return { chatId, ...(threadIdRaw && Number.isFinite(threadId) ? { threadId } : {}), projectId, ...(labelParts.length > 0 ? { label: labelParts.join(" ") } : {}), }; }) .filter((route): route is { chatId: string; threadId?: number; projectId: string; label?: string } => Boolean(route?.chatId && route.projectId), ); } function SectionTitle({ title, note }: { title: string; note?: string }) { return (
{title}
{note ?
{note}
: null}
); } function TextField({ label, value, onChange, placeholder, secret = false, }: { label: string; value: string; onChange: (value: string) => void; placeholder?: string; secret?: boolean; }) { return ( ); } function TextAreaField({ label, value, onChange, placeholder, }: { label: string; value: string; onChange: (value: string) => void; placeholder?: string; }) { return (