🦊 Digest OSS Hacker News — vocalino e testo, ogni 12 ore

Un sistema completamente gratuito e riproducibile che:

  1. raccoglie tutti i progetti open source apparsi su Hacker News nelle ultime 12 ore
  2. legge il README vero di ogni progetto prima di descriverlo (niente descrizioni dal titolo!)
  3. scrive un digest testuale in italiano e lo invia su un canale/bot Telegram
  4. 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

  1. In n8n: Import from File → n8n/workflow-digest-oss.json
  2. Apri il nodo Credenziali (Set) e compila:
    • gemini_key — la tua chiave Google AI Studio
    • telegram_token — token del bot
    • telegram_chat_id — il tuo chat ID
    • n8n_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)
  3. Apri i nodi Leggi checkpoint e Aggiorna checkpoint e sostituisci l'ID IL_TUO_TABLE_ID con il TABLE_ID del punto 1
  4. 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:

  1. esegui fetch_latest_digest.py; se l'executionId è già in narrated-ids.txt, fermati
  2. leggi latest-digest.json e scrivi narration.md: TUTTI i progetti, uno per uno, stile podcast (mai elenco meccanico), ~1 minuto per progetto, chiudendo OGNI progetto con "il nome da cercare è: "
  3. esegui narrate_to_audio.py → digest-audio.mp3
  4. 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, niente URL, niente AbortSignal, niente process
  • ❌ 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 readmeUrlFor nel workflow), non con new 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.
S
Description
Vocalino e testo: tutti i progetti open source da Hacker News, raccontati in italiano. n8n + Gemini + Hermes Agent, tutto gratuito e riproducibile.
Readme
41 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 100%