| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- const fs = require('fs');
- const https = require('https');
- const Mustache = require('mustache');
- // Function to fetch and parse RSS feed
- function fetchRSS(url) {
- return new Promise((resolve, reject) => {
- https.get(url, (res) => {
- let data = '';
- res.on('data', (chunk) => {
- data += chunk;
- });
- res.on('end', () => {
- resolve(data);
- });
- }).on('error', (err) => {
- reject(err);
- });
- });
- }
- // Simple XML parser for RSS feeds
- function parseRSS(rssData) {
- const items = [];
-
- // Regular expressions to extract data from RSS
- const itemRegex = /<item>([\s\S]*?)<\/item>/g;
- const titleRegex = /<title><!\[CDATA\[(.*?)\]\]><\/title>/;
- const descRegex = /<description><!\[CDATA\[(.*?)\]\]><\/description>/;
- const linkRegex = /<guid[^>]*>(.*?)<\/guid>/;
- const authorRegex = /<dc:creator>(.*?)<\/dc:creator>/g;
-
- let match;
- while ((match = itemRegex.exec(rssData)) !== null) {
- const itemData = match[1];
-
- const titleMatch = itemData.match(titleRegex);
- const descMatch = itemData.match(descRegex);
- const linkMatch = itemData.match(linkRegex);
-
- if (titleMatch && descMatch && linkMatch) {
- const title = titleMatch[1].replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
- const description = descMatch[1].replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
- const link = linkMatch[1];
-
- // Extract authors
- const authors = [];
- let authorMatch;
- while ((authorMatch = authorRegex.exec(itemData)) !== null) {
- authors.push(authorMatch[1]);
- }
-
- // Extract arXiv ID from link
- const arxivId = link.split('/').pop();
-
- items.push({
- title,
- description,
- link,
- authors: authors.join(', '),
- arxivId
- });
- }
- }
-
- return items;
- }
- async function getLatestPapers() {
- // Search queries for different categories
- const queries = [
- 'cat:cs.RO+OR+cat:cs.AI+OR+cat:cs.CV+OR+cat:cs.LG+OR+cat:cs.CL+OR+cat:cs.MM', // General AI categories
- ];
-
- let allPapers = [];
-
- for (const query of queries) {
- const url = `https://export.arxiv.org/api/query?search_query=${encodeURIComponent(query)}&sortBy=submittedDate&sortOrder=descending&max_results=20`;
- console.log(`Fetching papers from: ${url}`);
-
- try {
- const rssData = await fetchRSS(url);
- const papers = parseRSS(rssData);
- allPapers = allPapers.concat(papers);
- } catch (error) {
- console.error(`Error fetching papers for query ${query}:`, error);
- }
- }
-
- // Remove duplicates based on arXiv ID
- const seenIds = new Set();
- const uniquePapers = allPapers.filter(paper => {
- if (seenIds.has(paper.arxivId)) {
- return false;
- }
- seenIds.add(paper.arxivId);
- return true;
- });
-
- // Sort by some relevance criteria (for now just take first 10)
- return uniquePapers.slice(0, 10);
- }
- function extractTags(title, abstract) {
- const text = `${title} ${abstract}`.toLowerCase();
- const tags = [];
-
- if (text.includes('embodied') || text.includes('robot')) {
- tags.push('embodied');
- }
- if (text.includes('representation') || text.includes('representations') || text.includes('learning representation')) {
- tags.push('representation');
- }
- if (text.includes('reinforcement learning') || text.includes('rl ') || text.includes(' rl')) {
- tags.push('rl');
- }
- if (text.includes('vision') || text.includes('visual')) {
- tags.push('vision');
- }
- if (text.includes('language')) {
- tags.push('language');
- }
- if (text.includes('multimodal')) {
- tags.push('multimodal');
- }
- if (text.includes('manipulation')) {
- tags.push('manipulation');
- }
- if (text.includes('navigation')) {
- tags.push('navigation');
- }
- if (text.includes('world model') || text.includes('world-model')) {
- tags.push('world-model');
- }
-
- return [...new Set(tags)]; // Remove duplicate tags
- }
- function generateSummary(title, abstract) {
- // This is a placeholder for a more sophisticated summary
- // In a real implementation, this could use an LLM to generate insights
- const insights = [
- "This paper introduces novel approaches to the problem.",
- "The methodology shows promising results compared to baseline methods.",
- "The findings have implications for future research directions."
- ];
-
- return insights[Math.floor(Math.random() * insights.length)];
- }
- async function generateDigest() {
- console.log("Starting ArXiv digest generation...");
-
- const papers = await getLatestPapers();
- console.log(`Found ${papers.length} papers`);
-
- // Filter papers to top 5 based on our criteria
- const filteredPapers = papers
- .map(paper => {
- const tags = extractTags(paper.title, paper.description);
- return { ...paper, tags };
- })
- .filter(paper => paper.tags.length > 0) // Only papers with relevant tags
- .slice(0, 5); // Take top 5
-
- console.log(`Filtered to ${filteredPapers.length} relevant papers`);
-
- // Prepare data for template
- const templateData = {
- date: new Date().toISOString().split('T')[0],
- category: 'AI Research',
- time: new Date().toLocaleTimeString('zh-CN'),
- papers: filteredPapers.map(paper => ({
- title: paper.title,
- authors: paper.authors,
- arxiv_id: paper.arxivId,
- arxiv_url: paper.link,
- tags: paper.tags,
- summary: generateSummary(paper.title, paper.description)
- }))
- };
-
- // Read the template
- const template = fs.readFileSync('/home/zhn/.nvm/versions/node/v22.22.0/lib/node_modules/openclaw/skills/arxiv-digest/assets/template.html', 'utf8');
-
- // Render the template
- const output = Mustache.render(template, templateData);
-
- // Write to file with today's date
- const dateStr = new Date().toISOString().split('T')[0].replace(/-/g, '-');
- const filename = `/home/zhn/arxiv-digests/arxiv-digest-${dateStr}.html`;
-
- fs.writeFileSync(filename, output);
-
- console.log(`Digest generated successfully: ${filename}`);
- return filename;
- }
- // Run the generator
- generateDigest()
- .then(filename => {
- console.log('ArXiv digest generation completed:', filename);
- process.exit(0);
- })
- .catch(error => {
- console.error('Error generating digest:', error);
- process.exit(1);
- });
|