NUS - Daily News Curator
An AI-powered daily news curation system that helps you stay informed without the noise. NUS (plural of the Greek letter ν, "nu") fetches news from quality sources, uses Claude AI to categorize and filter articles, and generates a mobile-friendly daily digest.
Features
-
AI-Powered Curation: Claude API analyzes and categorizes articles into:
- Must Know: Critical news and events
- Sports Context: Sports news with "why people care" explanations
- Tech & Tools: Technology and productivity news
- Fun Stuff: Interesting diversions and cultural content
-
Smart Filtering:
- Clickbait detection and removal
- Duplicate article elimination
- Balanced perspective (anti-bubble)
- Quality source prioritization
-
Mobile-Friendly:
- Responsive design works on all devices
- Bookmark-able daily page
- Offline-capable with localStorage
- Read/unread tracking
- Save for later functionality
-
Low Maintenance:
- GitHub Actions automation
- Static site hosting on GitHub Pages
- No server infrastructure needed
- Easy customization via config files
Quick Start
1. Clone and Setup
git clone <your-repo-url>
cd nus
cp .env.example .env2. Get Anthropic API Key
- Sign up at Anthropic Console
- Go to API Keys and create a new key
- Add to
.env:ANTHROPIC_API_KEY=your_api_key_here
3. Install Dependencies
# Install uv if you don't have it
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install project dependencies
uv sync4. Test Locally
# Generate a test digest
uv run generate-digest
# View the output
open docs/index.html5. Configure GitHub Actions
- Go to your GitHub repository settings
- Navigate to Secrets and variables → Actions
- Add a new secret:
- Name:
ANTHROPIC_API_KEY - Value: Your Anthropic API key
- Name:
6. Enable GitHub Pages
- Go to repository Settings → Pages
- Set source to GitHub Actions
- The workflow will automatically deploy to
https://<username>.github.io/nus/
Configuration
RSS Feeds
Edit config/feeds.json to add/remove news sources:
[
{
"name": "Your Source",
"url": "https://example.com/feed.xml",
"enabled": true,
"priority": 10
}
]AI Prompt
Edit prompts/categorization.md to adjust how Claude categorizes articles. This is where you can:
- Fine-tune category definitions
- Adjust clickbait detection criteria
- Change the "why people care" framing for sports
- Modify balance/bubble-avoidance instructions
Environment Variables
All settings can be overridden in .env:
# API Configuration
ANTHROPIC_API_KEY=your_key
CLAUDE_MODEL=claude-3-5-sonnet-20241022
MAX_TOKENS=1024
TEMPERATURE=0.3
# Performance
MAX_CONCURRENT_FEEDS=20
MAX_CONCURRENT_API_CALLS=5
FETCH_TIMEOUT=30
# Features
FILTER_CLICKBAIT=true
DEDUPLICATE_ARTICLES=trueSchedule
Edit .github/workflows/daily-digest.yml to change the schedule:
on:
schedule:
# Run at 7 AM UTC (2 AM Pacific, 5 AM Eastern)
- cron: '0 7 * * *'Use crontab.guru to generate cron schedules.
Usage
Automatic Daily Generation
Once configured, GitHub Actions will automatically:
- Run at your scheduled time
- Fetch and analyze articles
- Generate the HTML digest
- Deploy to GitHub Pages
- Commit the changes to your repo
Manual Trigger
You can manually trigger a digest from GitHub:
- Go to Actions tab
- Select "Generate Daily News Digest"
- Click Run workflow
Local Development
# Full digest generation
uv run generate-digest
# Test mode (fewer API calls)
uv run test-local
# View the output
open docs/index.htmlBookmarking
Add this to your phone's home screen or browser bookmarks:
https://<username>.github.io/nus/
How It Works
- Fetch: Async fetching of RSS feeds from configured sources (httpx + asyncio)
- Analyze: Claude API categorizes articles and detects clickbait
- Filter: Removes duplicates, clickbait, and low-quality content
- Render: Generates static HTML with Jinja2 templates
- Deploy: GitHub Actions publishes to GitHub Pages
- Track: Client-side JavaScript uses localStorage for read/save state
Project Structure
nus/
├── .github/workflows/ # GitHub Actions
├── config/ # RSS feeds configuration
├── prompts/ # Claude prompt templates
├── src/nus/ # Python source code
│ ├── main.py # Entry point
│ ├── fetcher.py # RSS fetching
│ ├── analyzer.py # Claude integration
│ ├── orchestrator.py # Workflow coordination
│ └── ...
├── templates/ # HTML templates
├── docs/ # Generated output (GitHub Pages)
└── pyproject.toml # Project configuration
Customization
Adding New Categories
- Edit
src/nus/models.pyto add to theCategoryenum - Update
prompts/categorization.mdwith the new category definition - Adjust
templates/digest.htmlstyling if needed
Changing Appearance
Edit templates/digest.html:
- Modify CSS variables in
:rootfor colors - Adjust layout/structure in HTML
- Add new JavaScript features
Adjusting AI Behavior
The AI prompt in prompts/categorization.md is the key to customization:
- Make it more/less strict about clickbait
- Adjust category criteria
- Add domain-specific knowledge
- Tune the balance requirements
Troubleshooting
GitHub Action Fails
- Check that
ANTHROPIC_API_KEYis set in repository secrets - Verify API key has sufficient credits
- Check workflow logs for specific errors
No Articles Generated
- Verify RSS feed URLs are accessible
- Check that feeds are enabled in
config/feeds.json - Look at logs for fetch errors
Articles Not Categorized Correctly
- Adjust the prompt in
prompts/categorization.md - Increase
TEMPERATUREfor more creative categorization - Add examples to the prompt
Read/Save State Not Persisting
- This uses localStorage, which is per-browser
- Clearing browser data will reset state
- State doesn't sync across devices (by design for privacy)
Cost Estimate
Using Claude 3.5 Sonnet:
- ~100 articles/day × $0.003/1K tokens ×
500 tokens/article = **$0.15/day** - Monthly: ~$4.50
- New accounts get $5 in free credits
Tips to reduce costs:
- Reduce number of feeds
- Lower
MAX_CONCURRENT_API_CALLS - Use Claude 3 Haiku for cheaper analysis (~70% less)
License
MIT
Contributing
This is a personal project, but feel free to fork and customize for your own use!