From 2f8c101bcfc22d19cd3468b0f10a490e735c43c5 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Wed, 26 Jun 2024 22:40:31 +0800 Subject: [PATCH] feat: add fediverse timeline --- lib/routes/fediverse/namespace.ts | 6 +++ lib/routes/fediverse/timeline.ts | 73 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 lib/routes/fediverse/namespace.ts create mode 100644 lib/routes/fediverse/timeline.ts diff --git a/lib/routes/fediverse/namespace.ts b/lib/routes/fediverse/namespace.ts new file mode 100644 index 000000000..e13a372f4 --- /dev/null +++ b/lib/routes/fediverse/namespace.ts @@ -0,0 +1,6 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Fediverse', + url: 'fediverse.observer', +}; diff --git a/lib/routes/fediverse/timeline.ts b/lib/routes/fediverse/timeline.ts new file mode 100644 index 000000000..dfc13c6b3 --- /dev/null +++ b/lib/routes/fediverse/timeline.ts @@ -0,0 +1,73 @@ +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import { Route } from '@/types'; + +import { parseDate } from '@/utils/parse-date'; +import ofetch from '@/utils/ofetch'; + +export const route: Route = { + path: '/timeline/:account', + categories: ['social-media', 'popular'], + example: '/fediverse/timeline/Mastodon@mastodon.social', + parameters: { account: 'username@domain' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Timeline', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { + const account = ctx.req.param('account'); + const domain = account.split('@')[1]; + const username = account.split('@')[0]; + + if (!domain || !username) { + throw new InvalidParameterError('Invalid account'); + } + + const requestOptions = { + headers: { + Accept: 'application/activity+json', + }, + }; + + const acc = await ofetch(`https://${domain}/.well-known/webfinger?resource=acct:${account}`, requestOptions); + + const jsonLink = acc.links.find((link) => link.rel === 'self')?.href; + const link = acc.links.find((link) => link.rel === 'http://webfinger.net/rel/profile-page')?.href; + + const self = await ofetch(jsonLink, requestOptions); + + const outbox = await ofetch(self.outbox, requestOptions); + const firstOutbox = await ofetch(outbox.first, requestOptions); + + const items = firstOutbox.orderedItems; + + return { + title: `${self.name || self.preferredUsername} (Fediverse@${account})`, + description: self.summary, + image: self.icon?.url || self.image?.url, + link, + item: items.map((item) => { + const object = + typeof item.object === 'string' + ? { + content: item.object, + } + : item.object; + return { + title: object.content, + description: `${object.content}\n${object.attachment?.map((attachment) => ``).join('\n') || ''}`, + link: item.url, + pubDate: parseDate(item.published), + guid: item.id, + }; + }), + }; +}