rsshub
Version:
Make RSS Great Again!
93 lines (91 loc) • 3.9 kB
JavaScript
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 };