UNPKG

rsshub

Version:
120 lines (104 loc) 4.72 kB
import { Route, DataItem } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import path from 'node:path'; import logger from '@/utils/logger'; // Define the main route path export const route: Route = { path: '/', categories: ['programming'], example: '/transformer-circuits', parameters: {}, radar: [ { source: ['transformer-circuits.pub/'], target: '/', }, ], name: 'Articles', maintainers: ['shinmohuang'], handler, }; async function handler() { const rootUrl = 'https://transformer-circuits.pub'; // Fetch the main page const response = await ofetch(rootUrl); const $ = load(response); // Get all article links and basic info const articlePromises = $('.toc a') .toArray() .map((item) => { const $item = $(item); const currentElement = $item; const dateElement = $item.prevAll('.date').first(); const currentDate = dateElement.text().trim(); // Check if this is an article (either paper or note) if (currentElement.hasClass('paper') || currentElement.hasClass('note')) { const articleType = currentElement.hasClass('paper') ? 'Paper' : 'Note'; // Extract title and metadata from the list page const title = currentElement.find('h3').text().trim(); let author = ''; const byline = currentElement.find('.byline'); if (byline.length) { author = byline.text().trim(); } const description = currentElement.find('.description').text().trim(); const href = currentElement.attr('href'); const articleUrl = href ? (href.startsWith('http') ? href : `${rootUrl}/${href}`) : rootUrl; // Cache the whole article object instead of just the content return cache.tryGet(articleUrl, async () => { const fullContent = await fetchArticleContent(articleUrl); return { title, link: articleUrl, pubDate: parseDate(currentDate, 'MMMM YYYY'), author, description: fullContent || `${articleType}: ${description}`, category: ['AI', 'Machine Learning', 'Anthropic', 'Transformer Circuits'], }; }); } return null; }); // Wait for all article fetches to complete const articlesWithContent = (await Promise.all(articlePromises)).filter(Boolean) as DataItem[]; return { title: 'Transformer Circuits Thread', link: rootUrl, item: articlesWithContent, description: 'Research on reverse engineering transformer language models into human-understandable programs', }; } // Function to fetch and parse article content async function fetchArticleContent(url) { try { const response = await ofetch(url); const $ = load(response); // Remove navigation and other unnecessary elements $('.article-header, .tooltip, modal, script, style, d-front-matter, .visual-toc').remove(); // Get the main content - d-article is the custom tag used by this site let content = $('d-article').html(); // If d-article not found, try more specific fallback selectors if (!content) { // Try to find content in common article containers, but avoid selecting the entire body content = $('main article, .article-content, .post-content, .content-area').html() || $('.content, .article, .post').html() || $('main').html(); // If still no content found, log a warning if (!content) { logger.warn(`No suitable content container found for ${url}`); content = `<p>Could not extract content. Please visit <a href="${url}">the original page</a>.</p>`; } } // Create an HTML fragment (not a full document) for the RSS description return art(path.join(__dirname, 'templates/article.art'), { content, link: url, }); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Error fetching article content from ${url}: ${errorMessage}`); return null; // Return null on error, we'll fall back to description } }