import type { SourceModule } from "../registry"; import { MastodonConfig, MastodonPayload, type MastodonConfig as Config, type MastodonPayload as Payload, } from "@/lib/schemas/sources/mastodon"; const stripHtml = (html: string) => html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim(); export const mastodonModule: SourceModule = { kind: "mastodon", label: "Mastodon", keyless: false, defaultRefreshSeconds: 600, configSchema: MastodonConfig, payloadSchema: MastodonPayload, isConfigured: (_c, env) => Boolean(env.MASTODON_INSTANCE && env.MASTODON_TOKEN), async fetch({ config, env, signal }) { const instance = env.MASTODON_INSTANCE?.replace(/\/$/, ""); const token = env.MASTODON_TOKEN; if (!instance || !token) throw new Error("Set MASTODON_INSTANCE + MASTODON_TOKEN"); const url = new URL(`${instance}/api/v1/notifications`); url.searchParams.set("limit", String(config.limit)); for (const t of ["mention", "reblog", "favourite"]) url.searchParams.append("types[]", t); const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` }, signal, }); if (!res.ok) throw new Error(`Mastodon ${res.status}`); const j = (await res.json()) as Array<{ type: string; created_at: string; account: { acct: string }; status?: { content: string; url: string }; }>; return { notifications: j.map((n) => ({ type: n.type, account: `@${n.account.acct}`, text: n.status ? stripHtml(n.status.content).slice(0, 200) : "", url: n.status?.url ?? null, createdAt: n.created_at, })), }; }, };