Vai al contenuto
SIMONE PIZZI
Progetti ↗
Runtime TelegramBot Desktop Titan Edition

TelegramBot cambia tutto: ora è Open Source!

TelegramBot ha venduto zero copie: tre download dell'installer, e li ho fatti io. Invece di lasciarlo affondare l'ho aperto. Codice pubblico, licenza MIT, zero euro.

Data
Lettura
15 minuti
Sezioni
12
Copertina: TelegramBot cambia tutto: ora è Open Source!

Runtime TelegramBot Desktop Titan Edition esce dal mercato ed entra sotto licenza MIT. Codice pubblico, release pubbliche, zero euro. Ecco perché, e cosa è servito per arrivarci.


Il file si chiama latest-linux.yml. È un manifesto di poche righe che ogni installazione Linux di Titan interroga all’avvio per sapere se da qualche parte esiste una versione più nuova di sé stessa. In cinque settimane di distribuzione ufficiale è stato letto zero volte.

Dietro quel numero non c’è un bug. Non è mai esistita un’installazione Linux di Titan al mondo. Nello stesso periodo l’installer per Windows è stato scaricato tre volte. Ero io, tutte e tre.

Questi sono i numeri commerciali di Runtime TelegramBot Desktop Titan Edition: il programma migliore che abbia mai messo insieme, un anno di lavoro, oltre quaranta release, un rodaggio lungo mesi in cui ogni notte pubblicava sui canali di Runtime Radio mentre io dormivo. Zero acquisti.

Da oggi è software libero sotto licenza MIT, il codice sorgente è pubblico e le release si scaricano da GitHub senza pagare niente:

👉 github.com/Pitz72/Runtime-TelegramBot-Desktop-Titan-Edition


Un programma senza nessuno a cui parlare

Sul perché non abbia venduto voglio essere onesto, perché la risposta comoda sarebbe «il mercato non capisce» e non regge.

Titan è verticale fino all’osso. Serve a chi gestisce più bot Telegram, più canali e decine di sorgenti fra RSS, podcast e YouTube, e vuole farlo dal proprio computer invece che pagando un abbonamento mensile a un servizio cloud. Le persone che hanno esattamente questo problema esistono, ma non sono tante, e soprattutto non passano di qui per caso.

Perché arrivassero bisognava andarle a cercare. Fare marketing, insomma. E il marketing non è nelle mie corde: non mi diverte, non lo so fare, e ogni volta che ci ho provato ho prodotto una pagina prodotto scritta bene e letta da nessuno. Ho costruito la vetrina, ci ho messo il prezzo, ho scritto il manuale in otto lingue e poi sono rimasto lì ad aspettare che qualcuno bussasse.

La scelta, alla fine, era fra due modi di finire.

Il primo: lasciarlo dov’era. Repository privata, prezzo di 9,99 euro, un programma che funziona e che nessuno avrebbe mai visto, destinato a spegnersi il giorno in cui avrei smesso di aggiornare Electron. Un anno di lavoro chiuso in un cassetto con la chiave.

Il secondo: aprirlo. Regalarlo, se vogliamo dirla brutalmente. Non guadagnarci un centesimo, ma metterlo in un posto dove chiunque possa leggerlo, compilarlo, correggerlo, portarselo via.

Preferisco dargli dignità che vederlo affondare in silenzio. Un programma che nessuno usa è già morto: tanto vale che sia vivo e gratis.

E poi c’è una ragione meno nobile e più vera: sono orgoglioso di questo codice. L’ho scritto con l’aiuto massiccio di modelli linguistici, e lo dico apertamente perché è la verità e perché è scritto anche nel README del progetto. Ma le decisioni sono state tutte mie, una per una, e la caccia ai bug pure. Un lavoro fatto così merita di stare in un posto dove si può guardare.


La licenza che vietava esattamente questo

Aprire un progetto sembra una cosa che si fa con un clic. C’è un interruttore su GitHub che dice Change visibility, lo sposti, e il codice diventa pubblico.

Non funziona così, e me ne sono accorto il 12 agosto, quando ho fatto la cosa che avrei dovuto fare per prima: leggere il progetto come se fosse di un altro.

L’audit ha attraversato tutto — 58 file, circa 7.400 righe fra TypeScript e TSX, la documentazione, le configurazioni di build, i 119 commit della storia. Il verdetto sul codice è stato migliore di quanto temessi: nessuna criticità gravissima, quattro gravi con correzioni da poche righe l’una, sette medie, nove lievi. La storia git pulita, nessun token, nessuna chiave privata, nessun file di credenziali mai finito dentro per sbaglio.

Il vero blocco era altrove, ed era il file più corto del repository.

LICENSE.txt conteneva l’EULA commerciale. Al punto 2 vietava di «distribuire, condividere, sub-licenziare» e di «fare reverse engineering o estrarre il codice sorgente». Cioè vietava, parola per parola, l’unica cosa che stavo per fare.

Sostituirlo non era una formalità burocratica. Finché quel testo restava lì, chiunque avesse scaricato il sorgente si sarebbe trovato davanti a un progetto in contraddizione con sé stesso, senza sapere che cosa gli fosse permesso. La vecchia licenza è stata archiviata in docs/storico/ come documento d’epoca, perché la propria storia non si cancella. Al suo posto è arrivata la MIT, che sta in venti righe e permette tutto.


Guardarsi dentro prima di farsi guardare

Le quattro criticità gravi erano tutte roba che un utente solo, sulla propria macchina, non incontra mai. Diventano un problema nel momento esatto in cui il programma smette di essere tuo.

La più bella è anche la più ironica. Quando un errore di rete arriva da Telegram, il messaggio dell’eccezione può contenere l’URL della chiamata, e l’URL delle API di Telegram ha il token del bot dentro l’indirizzo. Quel messaggio finiva nel file di log, nella console dell’applicazione e nel log esportato dal pulsante Esporta.

Finché sei l’unico utente non succede niente. Ma aprire un progetto significa invitare le persone ad aprire segnalazioni, e la prima cosa che si chiede a chi segnala un problema è: allegami il log. Avrei costruito una trappola in cui il gesto collaborativo per eccellenza consegnava a chiunque il controllo completo del bot di chi lo aveva fatto. Ora i token vengono redatti prima di essere scritti, ovunque.

Le altre tre le riassumo.

Avviare l’applicazione due volte apriva due motori sullo stesso database, e la deduplica non bastava a fermarli: entrambi potevano superare il controllo sullo stesso contenuto e pubblicarlo due volte sul canale. Su Windows bastava un doppio clic sull’icona a programma già minimizzato. Adesso la seconda istanza non parte: riporta in primo piano la prima.

Poi c’era il fatto che un feed poteva iniettare HTML nei messaggi pubblicati. I template mettono il link dentro un attributo href delimitato da apici, e la vecchia funzione di escape codificava soltanto la e commerciale. Bastava un <link> con un apice dentro per uscire dall’attributo e scrivere quel che si voleva nel messaggio. Telegram non esegue script, ma accetta i collegamenti: il risultato pratico è che il gestore di un feed poteva far annunciare un titolo e aprire un altro sito. Phishing verso tutti gli iscritti al canale, servito dal mio programma.

Infine, la validazione contro le richieste verso la rete interna si aggirava con il DNS. Controllavo il nome scritto nell’URL, non l’indirizzo a cui quel nome risolveva davvero. Ora c’è la risoluzione prima del fetch, più gli IPv6 privati, gli IPv4 in forma decimale ed esadecimale, l’intervallo CGNAT e i domini .local. Quello che resta scoperto è dichiarato per iscritto in SECURITY.md, perché un modello di sicurezza onesto dice anche cosa non protegge.


Le bugie di cortesia

Poi c’è la parte che non è codice, ed è quella che mi ha messo più a disagio.

I due file «leggimi» che accompagnavano l’installer cominciavano così: «Grazie per aver acquistato Runtime TelegramBot Titan Edition. Hai appena sbloccato l’automazione multi-canale definitiva.» Testo da pacchetto commerciale, scritto per un cliente che non è mai arrivato.

Nella stessa pagina c’era una frase peggiore. Per rassicurare chi vedeva l’avviso di SmartScreen al primo avvio, il leggimi dichiarava che il file scaricato era «SICURO al 100% e privo di malware». È un’affermazione che chi la scrive non può garantire e che chi la legge non ha modo di verificare: vale esattamente zero, e la scrivono tutti. Adesso quel paragrafo spiega la cosa vera, cioè che l’avviso riguarda il certificato di firma e non il contenuto, e indica l’unica verifica che conta davvero, quella diventata possibile solo oggi: il sorgente è pubblico, leggilo e compilatelo da solo.

Anche i manuali avevano un problema di coerenza. Il colophon in fondo a ogni PDF riportava l’EULA: «Tutti i diritti riservati», riproduzione vietata. Otto documenti ufficiali che dicevano il contrario di quello che il progetto stava diventando. Sono stati ricompilati con la licenza MIT, e i vecchi sono spariti dalla circolazione insieme alla vecchia repository delle release.


Otto lingue, e sei che non so rileggere

Titan parlava otto lingue. Adesso ne parla due: italiano e inglese.

Sembra un passo indietro, e in parte lo è. Il motivo però regge, e preferisco dirlo che nasconderlo: francese, tedesco, spagnolo, portoghese, russo e cinese erano lingue in cui potevo produrre testo ma non verificarlo. Nel manuale c’erano tre sezioni nuove scritte direttamente in otto lingue, senza passare da nessuna revisione seria, perché una revisione seria in cinese non ero in grado di farla.

Sei lingue che nessuno nel progetto sa rileggere sono sei modi di pubblicare testo sbagliato senza accorgersene. Meglio due edizioni controllate riga per riga che otto prese sulla fiducia.

La revisione delle due rimaste ha tirato fuori un errore che stava lì da un mese: il capitolo 7 dell’edizione inglese diceva che l’esportazione globale produce un file JSON. Non è vero, produce un .rtb. L’errore era stato corretto sull’italiano a luglio e mai riportato sull’inglese. Nessuno se ne era accorto, perché nessuno l’aveva letto.


La riga scritta per non essere letta

Sulla schermata iniziale, in fondo, c’è sempre stata una riga che dichiara chi ha fatto cosa: sviluppato con l’ausilio di LLM · concezione e direzione di Simone Pizzi.

Era a nove pixel, in maiuscoletto monospace, in blu al 35 per cento di opacità su fondo quasi nero. Contrasto misurato: 2,0 a 1. Sotto qualunque soglia di leggibilità esistente, e non per cattiveria: l’avevo trattata come una firma discreta, di quelle che si mettono nell’angolo.

Aprendo il sorgente quella riga cambia peso. Diventa la dichiarazione di paternità del progetto, la cosa che chi arriva ha diritto di sapere prima di tutto il resto: questo codice l’hanno scritto in gran parte Gemini e Claude, e ogni decisione su cosa dovesse fare l’ho presa io. Nasconderla in un progetto aperto è il contrario di quello che serve.

Adesso è testo normale, in un colore chiaro, a 7,13 a 1. Si legge.

Nella stessa passata è caduta la scritta INIT_SEQ · TITAN_DESKTOP_RUNTIME che stava in fondo alla schermata. Non era uno stato del programma né una diagnostica: era un vezzo da finto terminale rimasto dai primi mockup. Un programma che si fa guardare da estranei ha meno bisogno di sembrare qualcosa.


Mezzo gigabyte di passato

Prima di rendere pubblico il repository ho fatto una cosa che di solito si sconsiglia: ho riscritto la storia git.

Il motivo era il peso. Il pacchetto che si scarica clonando stava a 120,7 MB, quasi tutto dovuto ai manuali PDF versionati nel tempo: sedici file da 32 MB l’uno, otto lingue in due edizioni, più i manuali della vecchia serie 1.7 e alcuni whitepaper commerciali. Roba che non serve più a nessuno e che ogni futuro clone si sarebbe portato dietro per sempre.

Riscrivere la storia rompe i cloni esistenti, e di solito è una buona ragione per non farlo. Qui però l’ho misurato prima di decidere: la repository di partenza era privata, zero stelle, zero fork, zero issue. L’unico clone esistente al mondo era il mio.

Il primo tentativo ha sbagliato bersaglio in un modo istruttivo. Avevo filtrato per percorso, cioè per la cartella in cui quei PDF stavano adesso. Ma quei file erano stati spostati lì da un’altra cartella mesi prima, e i dati originali sopravvivevano intatti sotto il vecchio indirizzo. Poi ne è saltato fuori un secondo: git ls-tree mette fra virgolette i percorsi che contengono caratteri non ASCII, quindi il mio controllo sull’estensione .pdf a fine nome falliva su tutti i file con un trattino lungo, il cirillico o gli ideogrammi. Otto manuali su otto, invisibili al filtro.

Il filtro definitivo lavora sull’identità del contenuto, che nessuno spostamento e nessun nome strano può cambiare. Ventotto oggetti, 532 MB di dati non compressi usciti dalla storia. Il pacchetto da scaricare è sceso a 50,5 MB.

Poi ho verificato che non fosse cambiato niente di ciò che si vede: 249 file, gli stessi identificatori uno per uno, 129 commit con autori, date e messaggi intatti, 40 tag tutti validi. E un secondo passaggio sui segreti, rifatto su tutti i commit della storia nuova: 1073 file di testo controllati contro i formati dei token Telegram, dei personal access token GitHub, delle chiavi AWS, delle chiavi private. Nessun riscontro.


Il traghetto costruito per nessuno

C’è un episodio che riassume tutto meglio di qualsiasi ragionamento.

Fino alla versione 2.1.7 le release uscivano su una repository separata, una specie di ponte pubblico che esisteva solo perché il codice era privato e l’aggiornamento automatico aveva bisogno di un posto da cui scaricare. Spostando il progetto, il piano prevedeva una migrazione ordinata: pubblicare la nuova versione anche sul vecchio ponte, lasciare una finestra di qualche settimana perché le installazioni esistenti si accorgessero del trasloco, e solo dopo spegnere tutto.

Un lavoro pensato bene. Richiedeva un token da rigenerare, un passo di pubblicazione fra due organizzazioni diverse e un’attesa prima di poter chiudere.

Poi ho guardato i numeri di quel ponte. Una release, tre download dell’eseguibile, quattordici letture del manifesto di aggiornamento in cinque settimane, e lo zero di cui sopra sul lato Linux.

Non c’era nessuno da traghettare. Stavo costruendo un’infrastruttura di migrazione per aggiornare il computer di casa mia, che può scaricare l’installer a mano come chiunque altro. Il ponte è stato smontato prima di essere attraversato, e la vecchia repository cancellata il giorno stesso, anche perché continuava a servire pubblicamente gli otto manuali col colophon che diceva «Tutti i diritti riservati».

Nel frattempo, però, ho fatto la verifica che contava davvero: ho aggiornato la mia installazione reale dalla 2.1.7 alla 2.1.8 scaricata dalla release pubblica. È l’unico passo che, se sbagliato, non si corregge più a posteriori. È andato al primo colpo, schermata delle novità compresa.


Dedurre, dichiarare

Sistemata la licenza, restava del software da migliorare, e due giorni di lavoro sono finiti lì. Le versioni 2.1.9, 2.2.0 e 2.2.1 non c’entrano niente con l’apertura del codice: sono le cose che vedi quando smetti di guardare un programma da autore e cominci a guardarlo da estraneo.

La più sostanziosa riguarda il pannello dei log in fondo alla Dashboard. Con il motore acceso quella colonna andava a singhiozzi, e la causa non era un’animazione fatta male: era la quantità. Il registro scarica le righe in blocco tre volte al secondo, e con più bot in scansione arrivano a raffica.

Adesso quel pannello è un diario di scansione. Il motore dichiara nove tipi di evento (scansione avviata, sorgente in lettura, contenuto trovato, pubblicato, giro concluso) e a schermo restano solo quelli, più tutto ciò che è andato storto. Su un ciclo reale: ventotto righe emesse, sedici nel diario. La console grezza di prima non è sparita, sta dietro un interruttore, e le due viste leggono lo stesso identico flusso.

Il pezzo interessante non è cosmetico. Prima il livello di una riga, cioè se fosse informativa, un avviso o un guasto, veniva indovinato cercando parole come «error» e «Fallito» dentro il testo. Il testo, però, contiene anche il titolo del contenuto, che arriva dal feed e non lo scrivo io. Su un canale reale ci sono due video intitolati «97. Checked Errors» e «91. Digital transformation by trial and error»: diventavano due errori rossi a ogni singolo giro di scansione. Su una giornata di log — 2.657 righe — ne comparivano undici marcate come guasto. Una sola lo era davvero.

Adesso il livello lo dichiara il programma, e le parole del titolo non contano più.

È lo stesso gesto della licenza, della riga di paternità, del modello di sicurezza scritto nero su bianco: smettere di lasciare che le cose si deducano, e dirle.


Che cosa succede adesso

Titan è funzionalmente completo e in produzione. Non c’è una roadmap, e non la invento adesso per far sembrare vivo un progetto che ha finito di crescere: continuerò a correggere quello che si rompe, e questo è tutto quello che prometto.

Le versioni compilate per Windows e Linux si scaricano dalla pagina delle release: installer e portable per Windows, AppImage, deb, rpm, pacman e tar.gz per Linux, ognuno in versione x64 e arm64. Per macOS non c’è un pacchetto ufficiale e non ci sarà: il codice gira benissimo, ma firmarlo costa un certificato Apple a pagamento che non ha senso mantenere per un progetto senza entrate. Chi usa un Mac può compilarlo dal sorgente, e le istruzioni ci sono.

Se qualcuno ha voglia di metterci le mani, CONTRIBUTING.md racconta come si compila, dove sono i tranelli e quali cose non vanno toccate per nessun motivo. Ci sono due lavori che segnalo volentieri perché sono autonomi e utili: il progetto non ha un solo test automatico, ed è l’unico impegno che mi ero preso e non ho mai mantenuto; e nel cassetto delle idee c’è una modalità headless da far girare su un VPS, analizzata a fondo e mai cominciata.

Titan resta gratuito. Se vi è utile e volete darmi una mano con le spese, o solo scrivermi, tutto sta sulla pagina dei contatti; per una donazione diretta c’è paypal.me/runtimeradio. Nessuno dei due link cambia niente su quello che potete scaricare.

Un anno fa questo programma era uno script Python di poche righe che pubblicava un canale solo e si rompeva ogni due giorni. Adesso è un’applicazione con un motore a produttore e consumatore, i token cifrati dal portachiavi del sistema, un database allo schema 12 e un manuale da 33 pagine. Non ha venduto una copia.

Però funziona, ogni notte, e adesso è di chiunque lo voglia. Mi sembra un finale migliore di quello che stava per avere.


Runtime TelegramBot Desktop Titan Edition è software libero sotto licenza MIT. Codice, release e documentazione su GitHub. Sviluppato da Simone Pizzi per Runtime Radio.

Ogni tanto scrivo una lettera

Nessuna cadenza fissa, nessun riassunto automatico: parte solo quando ho qualcosa da raccontare. In fondo a ognuna c'è il link per uscire.

↑ Su

Cerca nel sito

Condividi

L'indirizzo di questa pagina:

Scrivi a Simone

Segnalazioni, correzioni, o solo per dire che una cosa non funziona: quest'ultima è la più utile di tutte.