import type { ChannelMeta } from "./plugins/types.js"; // Channel docking: add new channels here (order + meta + aliases), then // register the plugin in src/channels/plugins/index.ts and keep protocol IDs in sync. export const CHAT_CHANNEL_ORDER = [ "telegram", "whatsapp", "discord", "slack", "signal", "imessage", ] as const; export type ChatChannelId = (typeof CHAT_CHANNEL_ORDER)[number]; export const CHANNEL_IDS = [...CHAT_CHANNEL_ORDER] as const; export const DEFAULT_CHAT_CHANNEL: ChatChannelId = "whatsapp"; export type ChatChannelMeta = ChannelMeta; const WEBSITE_URL = "https://clawd.bot"; const CHAT_CHANNEL_META: Record = { telegram: { id: "telegram", label: "Telegram", selectionLabel: "Telegram (Bot API)", docsPath: "/channels/telegram", docsLabel: "telegram", blurb: "simplest way to get started — register a bot with @BotFather and get going.", selectionDocsPrefix: "", selectionDocsOmitLabel: true, selectionExtras: [WEBSITE_URL], }, whatsapp: { id: "whatsapp", label: "WhatsApp", selectionLabel: "WhatsApp (QR link)", docsPath: "/channels/whatsapp", docsLabel: "whatsapp", blurb: "works with your own number; recommend a separate phone + eSIM.", }, discord: { id: "discord", label: "Discord", selectionLabel: "Discord (Bot API)", docsPath: "/channels/discord", docsLabel: "discord", blurb: "very well supported right now.", }, slack: { id: "slack", label: "Slack", selectionLabel: "Slack (Socket Mode)", docsPath: "/channels/slack", docsLabel: "slack", blurb: "supported (Socket Mode).", }, signal: { id: "signal", label: "Signal", selectionLabel: "Signal (signal-cli)", docsPath: "/channels/signal", docsLabel: "signal", blurb: 'signal-cli linked device; more setup (David Reagans: "Hop on Discord.").', }, imessage: { id: "imessage", label: "iMessage", selectionLabel: "iMessage (imsg)", docsPath: "/channels/imessage", docsLabel: "imessage", blurb: "this is still a work in progress.", }, }; export const CHAT_CHANNEL_ALIASES: Record = { imsg: "imessage", }; const normalizeChannelKey = (raw?: string | null): string | undefined => { const normalized = raw?.trim().toLowerCase(); return normalized || undefined; }; export function listChatChannels(): ChatChannelMeta[] { return CHAT_CHANNEL_ORDER.map((id) => CHAT_CHANNEL_META[id]); } export function listChatChannelAliases(): string[] { return Object.keys(CHAT_CHANNEL_ALIASES); } export function getChatChannelMeta(id: ChatChannelId): ChatChannelMeta { return CHAT_CHANNEL_META[id]; } export function normalizeChatChannelId(raw?: string | null): ChatChannelId | null { const normalized = normalizeChannelKey(raw); if (!normalized) return null; const resolved = CHAT_CHANNEL_ALIASES[normalized] ?? normalized; return CHAT_CHANNEL_ORDER.includes(resolved as ChatChannelId) ? (resolved as ChatChannelId) : null; } // Channel docking: prefer this helper in shared code. Importing from // `src/channels/plugins/*` can eagerly load channel implementations. export function normalizeChannelId(raw?: string | null): ChatChannelId | null { return normalizeChatChannelId(raw); } export function formatChannelPrimerLine(meta: ChatChannelMeta): string { return `${meta.label}: ${meta.blurb}`; } export function formatChannelSelectionLine( meta: ChatChannelMeta, docsLink: (path: string, label?: string) => string, ): string { const docsPrefix = meta.selectionDocsPrefix ?? "Docs:"; const docsLabel = meta.docsLabel ?? meta.id; const docs = meta.selectionDocsOmitLabel ? docsLink(meta.docsPath) : docsLink(meta.docsPath, docsLabel); const extras = (meta.selectionExtras ?? []).filter(Boolean).join(" "); return `${meta.label} — ${meta.blurb} ${docsPrefix ? `${docsPrefix} ` : ""}${docs}${extras ? ` ${extras}` : ""}`; }