import sanitizeHtml from "sanitize-html"; import { request } from "undici"; const MASTODON_HOST = "https://nyan.lol"; const TIMELINE_API = `${MASTODON_HOST}/api/v1/timelines/home?limit=40`; const VERIFY_API = `${MASTODON_HOST}/api/v1/accounts/verify_credentials`; export async function getTimeline(token) { const { body } = await request(TIMELINE_API, { method: "GET", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, }); const json = await body.json(); return json; } export async function getUser(token) { const { body } = await request(VERIFY_API, { method: "GET", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, }); const json = await body.json(); return json; } function filter(timeline) { return timeline.filter( ({ in_reply_to_id, in_reply_to_account_id, reblog, filtered, muted }) => { return !( in_reply_to_id || in_reply_to_account_id || reblog || filtered.length > 0 || muted ); }, ); } function buildHtml({ content, card, favourites_count, media_attachments, reblogs_count, replies_count, }) { const mediaHtml = media_attachments .map(({ type, url, description }) => { if (type === "image") { return `${description}`; } if (type === "video") { return ``; } }) .join("
"); const cardHtml = card ? `
${ card.image ? `${card.image_description}
` : "" }${card.title ? `${card.title}` : ""}
` : ""; const statsHtml = ``; const sanitizedContent = sanitizeHtml(content, { allowedTags: ["b", "i", "em", "strong", "a", "span", "br"], allowedAttributes: { a: ["href", "title"], img: ["src", "alt"], }, }); return `

${sanitizedContent}

${mediaHtml}${cardHtml}${statsHtml}`; } function processTimeline(timeline) { const cleanTimeline = filter(timeline); return cleanTimeline.map((status) => { const content = status.content; const summary = sanitizeHtml(content, { allowedTags: [], allowedAttributes: {}, }); const name = status.account.display_name || status.account.acct; const firstImage = status.media_attachments.find( ({ type }) => type === "image", )?.[0]?.url; const content_html = buildHtml(status); return { author: { name, url: status.account.url, avatar: status.account.avatar, }, date_published: status.created_at, date_modified: status.edited_at, external_url: status?.card?.url, id: status.url, image: firstImage, summary, tags: status.tags?.map(({ name }) => name), title: name, url: status.url, content_html, }; }); } export function buildJsonFeed(user, timeline) { const items = processTimeline(timeline); return { version: "https://jsonfeed.org/version/1.1", title: `${user.username}'s Timeline`, description: "A personalized Mastodon feed", home_page_url: user.url, items, }; }