# 🦊 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](https://aistudio.google.com) β†’ "Get API key" | gratis (1.500 req/giorno sui modelli Flash) | | Bot Telegram | [@BotFather](https://t.me/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](https://github.com/NousResearch/hermes-agent) sullo stesso VPS | open source | Per scoprire il tuo chat ID Telegram: scrivi un messaggio al tuo bot, poi ```bash curl "https://api.telegram.org/bot/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: ```bash 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"): ```bash SINCE=$(( $(date +%s) - 43200 )) curl -X POST https://TUO-N8N/api/v1/data-tables//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: ```json { "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: ```bash cd /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//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](https://github.com/NousResearch/hermes-agent) o a [n8n](https://github.com/n8n-io/n8n) β€” su cui tutto questo Γ¨ costruito.