rsshub
Version:
Make RSS Great Again!
128 lines (109 loc) • 4.57 kB
text/typescript
import { Route, DataItem, Data } from '@/types';
import ofetch from '@/utils/ofetch';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import cache from '@/utils/cache';
const ROOT_URL = 'https://bfl.ai'; // 根 URL 定义为常量
/**
* 辅助函数:获取并解析单个公告详情页,提取正文内容,并使用缓存。
*/
const fetchDescription = (item: DataItem): Promise<DataItem> =>
cache.tryGet(item.link!, async () => {
const detailPageHtml = await ofetch(item.link!, {
// 不再手动指定 User-Agent,让 RSSHub 自行处理
});
const $detailPage = load(detailPageHtml);
const detailContentSelector = 'div.max-w-3xl.mx-auto.px-6';
const fullDescription = $detailPage(detailContentSelector).html()?.trim();
// 将从列表页获取的 item 与详情页的描述合并后返回
// 整个对象将被缓存
return {
...item,
description: fullDescription || item.description, // 如果获取不到全文,则回退到列表页的摘要
};
});
/**
* 主路由处理函数
*/
async function handler(): Promise<Data> {
const listPageUrl = `${ROOT_URL}/announcements`;
const listPageHtml = await ofetch(listPageUrl); // 不再手动指定 User-Agent
const $ = load(listPageHtml);
const feedTitle = $('head title').text().trim() || 'BFL AI Announcements';
const feedDescription = $('head meta[name="description"]').attr('content')?.trim() || 'Latest announcements from Black Forest Labs (bfl.ai).';
const listItemsSelector = 'div.flex.flex-col.max-w-3xl.mx-auto.space-y-8 > a[href^="/announcements/"]';
const announcementLinks = $(listItemsSelector);
// 从列表页初步提取每个条目的信息
const preliminaryItems: DataItem[] = announcementLinks
.toArray()
.map((anchorElement) => {
const $anchor = $(anchorElement);
const relativeLink = $anchor.attr('href');
const link = relativeLink ? `${ROOT_URL}${relativeLink}` : undefined;
const title = $anchor.find('h2[class*="text-xl"]').text().trim();
const $timeElement = $anchor.find('time');
const datetimeAttr = $timeElement.attr('datetime');
const timeText = $timeElement.text().trim();
const pubDate = datetimeAttr ? parseDate(datetimeAttr) : timeText ? parseDate(timeText) : undefined;
const summaryDescription = $anchor.find('p[class*="line-clamp-3"]').html()?.trim() || '';
const author = 'Black Forest Labs';
// 只有包含有效标题和链接的条目才被认为是初步有效的
if (!title || !link) {
return null;
}
// 构造初步的 item 对象
const preliminaryItem: DataItem = {
title,
link,
description: summaryDescription,
author,
};
if (pubDate) {
preliminaryItem.pubDate = pubDate.toUTCString();
}
return preliminaryItem;
})
.filter((item): item is DataItem => item !== null && item.link !== undefined);
// 并行获取所有文章的完整描述
const items: DataItem[] = await Promise.all(preliminaryItems.map((item) => fetchDescription(item)));
return {
title: feedTitle,
link: listPageUrl,
description: feedDescription,
item: items,
language: 'en',
};
}
/**
* 定义并导出RSSHub路由对象
*/
export const route: Route = {
// 路径相对于命名空间 /bfl,所以完整路径是 /bfl/announcements
path: '/announcements',
// 按照要求,只指定一个分类
categories: ['multimedia'],
example: '/bfl/announcements',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['bfl.ai/announcements'],
// target 也要相应修改
target: '/announcements',
title: 'Announcements',
},
],
name: 'Announcements',
maintainers: ['thirteenkai'],
handler,
// url 不包含协议名
url: 'bfl.ai/announcements',
description: 'Fetches the latest announcements from Black Forest Labs (bfl.ai). Provides full article content by default with caching.',
};