🦊 Digest OSS Hacker News — vocalino e testo, ogni 12 ore
Un sistema completamente gratuito e riproducibile che:
- raccoglie tutti i progetti open source apparsi su Hacker News nelle ultime 12 ore
- legge il README vero di ogni progetto prima di descriverlo (niente descrizioni dal titolo!)
- scrive un digest testuale in italiano e lo invia su un canale/bot Telegram
- genera un vocalino "raccontato" (stile podcast, non elenco) con TUTTI i progetti, uno per uno, e lo consegna come messaggio vocale
Dove gira: n8n (testo + checkpoint) e Hermes Agent (audio) su un VPS con Docker. Nessun servizio a pagamento: le uniche chiavi necessarie sono gratuite (Google AI Studio, un bot Telegram, API n8n).
Perché "tutti i progetti"? Perché il filtro è: Show HN + qualsiasi storia con link a GitHub/GitLab/Codeberg + news open source con ≥3 punti. Non si scelgono i "migliori": si perdono zero news, e la selezione la fai tu mentre ascolti.
Architettura in un disegno
┌──────────────┐ ogni 12h ┌──────────────────────────────────────────┐
│ Hacker News │────────────▶│ n8n workflow "Digest OSS Hacker News" │
│ (Algolia API)│ │ 1. Legge il checkpoint (data table) │
└──────────────┘ │ 2. Scarica Show HN + storie dal last run │
│ 3. Filtra OSS (repo link, keyword) │
│ 4. SCARICA IL README di ogni progetto │
│ 5. Gemini Flash: digest italiano │
│ (descrizioni basate sul README) │
│ 6. Invia su Telegram (bot) │
│ 7. Checkpoint avanti (append-only) │
└──────────────┬───────────────────────────┘
│ 20 min dopo
▼
┌──────────────────────────────────────────┐
│ Hermes cron "Digest OSS audio raccontato"│
│ 1. Prende il digest dall'esecuzione n8n │
│ 2. Scrive la NARRAZIONE (stile podcast) │
│ 3. Gemini TTS → MP3 (segmenti + cache) │
│ 4. Consegna il vocalino in chat │
└──────────────────────────────────────────┘
Principi chiave (quelli che rendono il sistema affidabile):
- Checkpoint anti-buchi: un timestamp dell'ultimo momento elaborato vive in una data table di n8n in modalità append-only. Se tutto si rompe (VPS giù, API sovraccarica), il checkpoint non avanza e alla ripresa il digest riprende da dove era. Zero news perse, verificato in produzione.
- Verifica README: ogni descrizione è basata sul testo reale del repository. Se il README non è raggiungibile, la voce è marcata
[non verificato]. - Ridondanza Gemini: il modello principale sovraccarico? La chiamata passa automaticamente a
flash-lite. Quota TTS esaurita? Il generatore audio fa cascata tra 4 modelli TTS. - Cache dei segmenti audio: ogni pezzo di vocalino generato viene salvato su disco. Riavvii e crash non fanno rifare nulla.
Prerequisiti
| Cosa | Dove | Costo |
|---|---|---|
| Server con Docker + n8n | il tuo VPS | già tuo |
| API key Gemini | aistudio.google.com → "Get API key" | gratis (1.500 req/giorno sui modelli Flash) |
| Bot Telegram | @BotFather → /newbot |
gratis |
| Chat ID tuo | scrivi al tuo bot e leggi getUpdates (vedi sotto) |
gratis |
| API key n8n | n8n → Settings → n8n API → crea chiave | gratis |
| Hermes Agent | hermes-agent sullo stesso VPS | open source |
Per scoprire il tuo chat ID Telegram: scrivi un messaggio al tuo bot, poi
curl "https://api.telegram.org/bot<TOKEN>/getUpdates" | grep -o '"id":[0-9]*' | head -1
Installazione (30 minuti circa)
1. La data table del checkpoint in n8n
Con la tua API key n8n:
curl -X POST https://TUO-N8N/api/v1/data-tables \
-H "X-N8N-API-KEY: CHIAVE" -H "Content-Type: application/json" \
-d '{"name": "hn_checkpoint", "columns": [{"name": "sinceEpoch", "type": "number"}]}'
Segna l'id dalla risposta. Poi inserisci la prima riga (parti da "12 ore fa"):
SINCE=$(( $(date +%s) - 43200 ))
curl -X POST https://TUO-N8N/api/v1/data-tables/<TABLE_ID>/rows \
-H "X-N8N-API-KEY: CHIAVE" -H "Content-Type: application/json" \
-d "{\"data\": [{\"sinceEpoch\": $SINCE}]}"
2. Il workflow
- In n8n: Import from File →
n8n/workflow-digest-oss.json - Apri il nodo Credenziali (Set) e compila:
gemini_key— la tua chiave Google AI Studiotelegram_token— token del bottelegram_chat_id— il tuo chat IDn8n_key— la stessa chiave API n8n (il workflow legge la sua data table via REST API; il nodo DataTable nativo è rotto su n8n con task runner — vedi Pitfall #5)
- Apri i nodi Leggi checkpoint e Aggiorna checkpoint e sostituisci l'ID
IL_TUO_TABLE_IDcon il TABLE_ID del punto 1 - Attiva il workflow. Per il test: metti un cron one-shot a ~10 minuti nel futuro (in ora locale del tuo n8n — controlla il fuso!) e attendi l'esecuzione
Test riuscito quando: esecuzione verde in n8n → messaggi sul bot Telegram → nuova riga nella data table.
3. Le chiavi per la parte audio (Hermes)
Crea hn-oss-digest/creds.json (fuori dal git!) nel file system di Hermes, e metti lì anche gli ID:
{
"gemini_key": "AIza...",
"telegram_token": "12345:ABC...",
"n8n_key": "eyJhbG...",
"telegram_chat_id": "123456789",
"n8n_base": "https://TUO-N8N",
"n8n_table_id": "ID_TABELLA",
"n8n_workflow_id": "ID_DEL_WORKFLOW_IMPORTATO"
}
Poi in scripts/fetch_latest_digest.py sostituisci https://n8n.as89.it con il tuo dominio n8n e l'ID workflow con il tuo.
4. Gli script audio
Copia scripts/ nel file system di Hermes. Serve ffmpeg sul sistema (apt install ffmpeg).
Prova del motore TTS:
cd <file-system-hermes>/hn-oss-digest
printf 'Test della voce. Funziona!\n' > /tmp/t.md
python3 narrate_to_audio.py /tmp/t.md /tmp/t.mp3
5. Il cron Hermes (il vocalino)
Nella chat con Hermes, chiedi di creare un cron job con queste istruzioni (regolane i dettagli):
Ogni giorno alle 20:20 ora italiana:
- esegui
fetch_latest_digest.py; se l'executionId è già innarrated-ids.txt, fermati- leggi
latest-digest.jsone scrivinarration.md: TUTTI i progetti, uno per uno, stile podcast (mai elenco meccanico), ~1 minuto per progetto, chiudendo OGNI progetto con "il nome da cercare è: "- esegui
narrate_to_audio.py→digest-audio.mp3- consegna in chat il file come messaggio vocale
⚠️ Trappola del fuso: lo scheduler di Hermes interpreta gli orari naturali come UTC. Per le 20:20 italiane il cron è 20 18 * * *. Verifica SEMPRE il campo next_run_at dopo la creazione.
I cinque pitfall reali (ci hanno costo ore — a te costeranno minuti)
1. Il Code node di n8n (task runner) è una sandbox povera
- ❌ niente
fetch, nienteURL, nienteAbortSignal, nienteprocess - ❌
require('https')→ "Module disallowed" - ✅
this.helpers.httpRequest({url, timeout})— l'unico modo di chiamare HTTP da un Code node - Parse degli URL a regex (vedi
readmeUrlFornel workflow), non connew URL()
2. Il codice del Code node è CACHE per workflow ID
Modificare jsCode via API (o UI!) non aggiorna l'esecuzione: il task runner tiene il codice della prima compilazione per quel workflow ID. Il trigger nuovo parte, ma esegue il codice vecchio — silenziosamente.
Fix: a ogni cambio di logica crea un workflow NUOVO (ID nuovo) e ritira il vecchio. Per debuggare il sandbox: workflow TMP monouso con jsCode di sonda (typeof fetch, lista globals) e leggi il runData dell'esecuzione.
3. I fail silenziosi sono il nemico
Il primo fetch README ingoiava gli errori con catch {} e sembrava funzionare: 0 download su 50 senza un messaggio. Regola: ogni operazione fallibile registra il proprio errore per item (readmeErr = 'HTTP ' + r.status), e leggi le esecuzioni con ?includeData=true.
4. Fusi orari: tre livelli di trappola
- n8n Schedule Trigger usa il timezone delle settings del workflow (mettilo su Europe/Rome)
- Hermes cron interpreta gli orari naturali come UTC
- I cron one-shot di test si impostano SOLO dopo aver guardato
date: un "test alle 10:35" salvato alle 11:15 non scatta mai
5. Il nodo DataTable nativo di n8n può essere rotto
Con i task runner abilitati, il nodo DataTable crasha (Cannot read properties of undefined (reading 'execute')) a prescindere dai parametri. Il workflow usa invece due HTTP Request che chiamano la REST API di n8n stessa (GET/POST /data-tables/<id>/rows) con l'header X-N8N-API-KEY presa dal nodo Credenziali. Funziona da sempre e dà anche lo storico (append-only).
Nota: l'API pubblica delle data tables supporta solo GET e POST righe (corpo {"data":[{...}]}) — niente update/delete per riga: ecco perché il checkpoint è append-only (leggi il MAX, non l'ultima).
Bonus — quota TTS Gemini: il free tier è ~10 richieste/giorno per modello TTS (il 429 dura fino a ~02:00 ora italiana). narrate_to_audio.py fa cascata su 4 modelli TTS (MODELS) e cache dei segmenti: il retry costa zero.
File
n8n/workflow-digest-oss.json workflow n8n completo (chiavi rimosse — reinseriscile)
scripts/fetch_latest_digest.py prende l'ultimo digest riuscito dall'API n8n → latest-digest.json
scripts/narrate_to_audio.py narrazione → MP3 (segmentazione, cache, cascata TTS, anti-divagazione)
Personalizzazioni rapide
| Vuoi... | Tocca... |
|---|---|
| descrizioni più lunghe/corte | nodo Seleziona OSS → prompt ("4 a 6 frasi", "400 caratteri") |
| voce più lenta/rapida | STYLE in narrate_to_audio.py (es. aggiungi "speak slowly") |
| altra voce | voiceName nel TTS (Kore, Charon, Orus, Sulafat, Puck…) — fatti un provino! |
| più/meno progetti | filtri nel Code Seleziona OSS (soglia punti, keyword OSS) |
| orari | nodo Ogni 12 ore in n8n; cron del job Hermes |
Licenza e note
- Codice e manuale: MIT — prendi, adatta, condividi.
- Le API gratuite cambiano limiti spesso: i numeri di questo README erano veri a ottobre 2026.
- Se il progetto ti è utile, il ringraziamento giusto è un contributo a Hermes Agent o a n8n — su cui tutto questo è costruito.