UNPKG

rsshub

Version:
93 lines (91 loc) 3.9 kB
import "./esm-shims-CzJ_djXG.mjs"; import { t as config } from "./config-C37vj7VH.mjs"; import { t as ViewType } from "./types-D84BRIt4.mjs"; import "./dist-BInvbO1W.mjs"; import "./logger-Czu8UMNd.mjs"; import { t as ofetch_default } from "./ofetch-BIyrKU3Y.mjs"; import { t as parseDate } from "./parse-date-BrP7mxXf.mjs"; import { t as invalid_parameter_default } from "./invalid-parameter-rr4AgGpp.mjs"; import { t as config_not_found_default } from "./config-not-found-Dyp3RlZZ.mjs"; import { t as rss_parser_default } from "./rss-parser-Dtop7M8f.mjs"; //#region lib/routes/fediverse/timeline.ts const route = { path: "/timeline/:account", categories: ["social-media"], view: ViewType.SocialMedia, 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", "pseudoyu"], handler }; const allowedDomain = new Set([ "mastodon.social", "pawoo.net", config.mastodon.apiHost ].filter(Boolean)); const activityPubTypes = new Set(["application/activity+json", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""]); 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 invalid_parameter_default("Invalid account"); if (!config.feature.allow_user_supply_unsafe_domain && !allowedDomain.has(domain.toLowerCase())) throw new config_not_found_default(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); const requestOptions = { headers: { Accept: "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" } }; const acc = await ofetch_default(`https://${domain}/.well-known/webfinger?resource=acct:${account}`, { headers: { Accept: "application/jrd+json" } }); const jsonLink = acc.links.find((link$1) => link$1.rel === "self" && activityPubTypes.has(link$1.type))?.href; const link = acc.links.find((link$1) => link$1.rel === "http://webfinger.net/rel/profile-page")?.href; const officialFeed = await rss_parser_default.parseURL(`${link}.rss`); if (officialFeed) return { title: `${officialFeed.title} (Fediverse@${account})`, description: officialFeed.description, image: officialFeed.image?.url, link: officialFeed.link, item: officialFeed.items.map((item) => ({ title: item.title, description: item.content, link: item.link, pubDate: item.pubDate ? parseDate(item.pubDate) : null, guid: item.guid })) }; const self = await ofetch_default(jsonLink, requestOptions); const items = (await ofetch_default((await ofetch_default(self.outbox, requestOptions)).first, requestOptions)).orderedItems; const itemResolvers = []; for (const item of items) { if (![ "Announce", "Create", "Update" ].includes(item.type)) continue; if (typeof item.object === "string") itemResolvers.push((async (item$1) => { item$1.object = await ofetch_default(item$1.object, requestOptions); return item$1; })(item)); else itemResolvers.push(Promise.resolve(item)); } const resolvedItems = await Promise.all(itemResolvers); return { title: `${self.name || self.preferredUsername} (Fediverse@${account})`, description: self.summary, image: self.icon?.url || self.image?.url, link, item: resolvedItems.map((item) => ({ title: item.object.content.replaceAll(/<[^<]*>/g, ""), description: `${item.object.content}\n${item.object.attachment?.map((attachment) => `<img src="${attachment.url}" width="${attachment.width}" height="${attachment.height}" />`).join("\n") || ""}`, link: item.object.url, pubDate: parseDate(item.published), guid: item.id })) }; } //#endregion export { route };