Initial commit: Daily tech newsletter worker

A Node.js/TypeScript worker that sends daily AI-generated summaries
of tech news from X/Twitter via Nitter RSS feeds.

Features:
- Fetches tweets from 40+ curated tech accounts via Nitter RSS
- Filters and categorizes by topic (AI/ML, SWE, General Tech)
- Generates AI summaries using OpenRouter (Claude/GPT-4)
- Sends professional HTML email via Brevo SMTP
- Runs on cron schedule inside Docker container
- Instance rotation for Nitter reliability
- Graceful degradation if AI fails

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-11 20:23:38 +00:00
commit b3643fd5b0
23 changed files with 2091 additions and 0 deletions

60
src/config/accounts.ts Normal file
View File

@@ -0,0 +1,60 @@
import type { TechAccount } from '../types/index.js';
export const TECH_ACCOUNTS: TechAccount[] = [
// ===========================================
// AI / Machine Learning
// ===========================================
{ username: 'karpathy', displayName: 'Andrej Karpathy', category: 'ai_ml', priority: 'high' },
{ username: 'ylecun', displayName: 'Yann LeCun', category: 'ai_ml', priority: 'high' },
{ username: 'AndrewYNg', displayName: 'Andrew Ng', category: 'ai_ml', priority: 'high' },
{ username: 'sama', displayName: 'Sam Altman', category: 'ai_ml', priority: 'high' },
{ username: 'demaboris', displayName: 'Demis Hassabis', category: 'ai_ml', priority: 'high' },
{ username: 'goodaboris', displayName: 'Ian Goodfellow', category: 'ai_ml', priority: 'medium' },
{ username: 'fchollet', displayName: 'François Chollet', category: 'ai_ml', priority: 'high' },
{ username: 'EMostaque', displayName: 'Emad Mostaque', category: 'ai_ml', priority: 'medium' },
{ username: 'JimFan', displayName: 'Jim Fan', category: 'ai_ml', priority: 'high' },
{ username: 'gaborig', displayName: 'George Hotz', category: 'ai_ml', priority: 'medium' },
{ username: 'ClaudeMcAI', displayName: 'Claude (Anthropic)', category: 'ai_ml', priority: 'medium' },
{ username: 'OpenAI', displayName: 'OpenAI', category: 'ai_ml', priority: 'high' },
{ username: 'AnthropicAI', displayName: 'Anthropic', category: 'ai_ml', priority: 'high' },
{ username: 'huggingface', displayName: 'Hugging Face', category: 'ai_ml', priority: 'high' },
// ===========================================
// Software Engineering / Dev Tools
// ===========================================
{ username: 'ThePrimeagen', displayName: 'ThePrimeagen', category: 'swe', priority: 'high' },
{ username: 'kelseyhightower', displayName: 'Kelsey Hightower', category: 'swe', priority: 'high' },
{ username: 'mitchellh', displayName: 'Mitchell Hashimoto', category: 'swe', priority: 'high' },
{ username: 'tjholowaychuk', displayName: 'TJ Holowaychuk', category: 'swe', priority: 'medium' },
{ username: 'addyosmani', displayName: 'Addy Osmani', category: 'swe', priority: 'medium' },
{ username: 'sarah_edo', displayName: 'Sarah Drasner', category: 'swe', priority: 'medium' },
{ username: 'dan_abramov', displayName: 'Dan Abramov', category: 'swe', priority: 'high' },
{ username: 'swyx', displayName: 'swyx', category: 'swe', priority: 'medium' },
{ username: 'kentcdodds', displayName: 'Kent C. Dodds', category: 'swe', priority: 'medium' },
{ username: 'taborwang', displayName: 'Tanner Linsley', category: 'swe', priority: 'medium' },
{ username: 'raaborig', displayName: 'Ryan Dahl', category: 'swe', priority: 'high' },
{ username: 'vercel', displayName: 'Vercel', category: 'swe', priority: 'medium' },
{ username: 'github', displayName: 'GitHub', category: 'swe', priority: 'medium' },
// ===========================================
// General Tech / Startups
// ===========================================
{ username: 'levelsio', displayName: 'Pieter Levels', category: 'general_tech', priority: 'high' },
{ username: 'paulg', displayName: 'Paul Graham', category: 'general_tech', priority: 'high' },
{ username: 'naval', displayName: 'Naval Ravikant', category: 'general_tech', priority: 'high' },
{ username: 'elaborig', displayName: 'Elon Musk', category: 'general_tech', priority: 'medium' },
{ username: 'jason', displayName: 'Jason Calacanis', category: 'general_tech', priority: 'medium' },
{ username: 'balajis', displayName: 'Balaji Srinivasan', category: 'general_tech', priority: 'medium' },
{ username: 'pmarca', displayName: 'Marc Andreessen', category: 'general_tech', priority: 'medium' },
{ username: 'aborig', displayName: 'DHH', category: 'general_tech', priority: 'high' },
{ username: 'benedictevans', displayName: 'Benedict Evans', category: 'general_tech', priority: 'medium' },
{ username: 'jason_f', displayName: 'Jason Fried', category: 'general_tech', priority: 'medium' },
];
export function getAccountsByCategory(category: TechAccount['category']): TechAccount[] {
return TECH_ACCOUNTS.filter((account) => account.category === category);
}
export function getHighPriorityAccounts(): TechAccount[] {
return TECH_ACCOUNTS.filter((account) => account.priority === 'high');
}