rsshub
Version:
Make RSS Great Again!
89 lines (88 loc) • 3.68 kB
JavaScript
import { t as rofetch } from "./ofetch-C3ts-Hud.mjs";
import { t as config } from "./config-CCmw1BNE.mjs";
import "./types-DNj6Etbg.mjs";
import { t as parseDate } from "./parse-date-Cr0wQjV_.mjs";
import { t as InvalidParameterError } from "./invalid-parameter-CGxhjomn.mjs";
import { t as ConfigNotFoundError } from "./config-not-found-fQ5mxvDU.mjs";
import { t as parser } from "./rss-parser-CmTb9lkc.mjs";
//#region lib/routes/fediverse/timeline.ts
const route = {
path: "/timeline/:account",
categories: ["social-media"],
view: 1,
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 = /* @__PURE__ */ 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("@", 2)[1];
const username = account.split("@", 1)[0];
if (!domain || !username) throw new InvalidParameterError("Invalid account");
if (!config.feature.allow_user_supply_unsafe_domain && !allowedDomain.has(domain.toLowerCase())) throw new ConfigNotFoundError(`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 rofetch(`https://${domain}/.well-known/webfinger?resource=acct:${account}`, { headers: { Accept: "application/jrd+json" } });
const jsonLink = acc.links.find((link) => link.rel === "self" && activityPubTypes.has(link.type))?.href;
const link = acc.links.find((link) => link.rel === "http://webfinger.net/rel/profile-page")?.href;
const officialFeed = await parser.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 rofetch(jsonLink, requestOptions);
const items = (await rofetch((await rofetch(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) => {
item.object = await rofetch(item.object, requestOptions);
return item;
})(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 };