1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
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<Config, Payload> = {
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,
})),
};
},
};
|