GOLEM Telegram Bot

Da GolemWiki.

Su questa pagina si trovano...

  • Guida all'uso: un breve manuale d'uso del BOT (per gli addetti ai lavori o chi vuol replicarlo)
  • Creare un BOT: un prontuario per creare un nuovo BOT
  • Documentazione: una rapida descrizione del codice del GOLEM BOT

Il BOT del GOLEM provvede ad indirizzare messaggi, foto e video sul canale dell'associazione, permettendo anche di pianificarne l'invio in momenti successivi. È in fase di testing, al momento sta correttamente funzionando sul nostro VPS. Il codice sorgente può essere consultato su github.

Guida d'uso

Attenzione! Il BOT sta ancora funzionando "alla vecchia maniera". È necessario richiedere l'abilitazione per utilizzare la nuova interfaccia

Il BOT è raggiungibile col nome @GOLEMpoliBot, naturalmente riceve messaggi da chiunque, ma prende in considerazione solo quelli provenienti dagli utenti da noi abilitati. I restanti utenti saranno indirizzati verso il canale.

La prima volta che viene contattato, il BOT si presenta illustrando funzionalità e comandi.

Scritto un messaggio, viene richiesto se si intende inviarlo immediatamente (OK) o successivamente (Programma). Nel primo caso si riceverà semplicemente la conferma del corretto invio. Nel secondo caso vengono mostrati, in due step, i menù per impostare data ed ora di invio. È possibile interrompere la procedura in qualsiasi momento, tornando nella situazione iniziale.

I messaggi pianificati da qualsiasi utente sono consultabili mediante il comando /list. Allo stato attuale è possibile pianificare anche foto e video, ma non compaiono "graficamente" nella lista.

Creare un BOT

Aprire una chat col bot BotFather e creare un nuovo bot seguendo le intuitive istruzioni interattive. Generare anche il token. Il token è un codice casuale, ma univoco una volta creato, che serve agli sviluppatori per accedere al bot.

È una cosa brutta che assomiglia a questo:

110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw

Contattare il bot tramite token

Costruire un URL tipo questo: https://api.telegram.org/botTOKEN/metodo

  • TOKEN è ovviamente il token che abbiamo generato
  • metodo è l'operazione che vogliamo effettuare

Ne esistono tante [1], noi ne vedremo alcune che ci servono.

getMe

Ottiene informazioni sul bot. Lo usiamo per vedere se i server di Telegram funzionano. Esempio di URL da consultare:

https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/getMe

Riceveremo in risposta un documento JSON che possiamo facilmente interpretare tramite Python o PHP per estrapolare le varie informazioni attraverso il nostro programma.

Ricevere gli aggiornamenti

Quando qualcuno scrive al bot, il messaggio viene inviato ai server di Telegram, che lo mantengono per un certo periodo (max 24 ore). Per leggerlo tramite il nostro bot (il nostro programma) esistono due modi:

  • query attiva via HTTP (getUpdates): a intervalli regolari, il nostro programma interroga i server di Telegram per sapere se c'è un nuovo messaggio. CONTRO: è grande spreco di risorse; PRO: utile per fare debugging;
  • query passiva via HTTP (WebHook): quando i server di Telegram ricevono un messaggio per il bot, saranno loro a contattare il nostro programma via HTTP; PRO: non spreca risorse CONTRO: necessità di un server web dedicato e debugging più ostico.

Alla fine, la soluzione con query passiva WebHook è decisamente quella più sensata.

Mentre si usa un WebHook non si può usare il getUpdates, e viceversa (ma va?).

WebHook

Per impostare un WebHook bisogna usare il metodo setWebhook, con una cosa del genere:

https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/setWebhook?url=

Se lasciamo url= vuoto in questo modo, cancelliamo il WebHook e Telegram non contatterà il nostro programma quando il nostro bot riceverà un messaggio. Utile per tornare nella modalità getUpdates e fare debugging.

Altrimenti, dobbiamo specificare l'URL a cui si trova il nostro programma, per esempio:

https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/setWebhook?url=https://golem.linux.it/directory/bot.php

Ovviamente non è quello, mica siamo così citrulli da scriverlo qui! :-) È meglio usare URL con nomi strani e caratteri casuali, così si evita che qualcuno possa inavvertitamente (o intenzionalmente) usare il nostro programma a sproposito. Siccome ci fa fatica pensare a dei caratteri casuali, facciamoci aiutare da questo comando:

dd if=/dev/urandom bs=1 count=12 | base64

Certificato SSL

Importante: tutte le query devono avvenire attraverso HTTPS (HTTP su SSL). Si può acquistare un certificato (sganciando parecchi euri), si può generare con Let's Encrypt oppure si può generare in locale e autofirmarlo (self-signed). La prima soluzione è la più immediata e costosa, la seconda e la terza sono meno immediate ma sono gratuita (free as in free beer, che non è poi così male).

Generiamo il nostro certificato [2]:

openssl req -newkey rsa:2048 -sha256 -nodes -keyout golem.linux.it.key -x509 -days 720 -out golem.linux.it.pem -subj "/C=IT/ST=Italy/L=Florence/O=GruppoLinuxEmpoli/CN=golem.linux.it"
  • -days 720 imposta la scadenza (nel caso specifico, tra due anni)
  • -keyout golem.linux.it.key specifica il nome del file su cui verrà scritta la nostra chiave privata (da custodire gelosamente)
  • -out golem.linux.it.pem specifica il nome del file su cui verrà scritta la nostra chiave pubblica (che manderemo a Telegram)

E impostiamo il nostro webserver per usarlo come certificato per l'HTTPS. Per esempio, ecco qui come farlo su Apache.

Mandiamo a Telegram la nostra chiave pubblica effettuando una richiesta HTTP di tipo POST invece che GET, con lo stesso URL di prima, e ovviamente allegando il nostro certificato pubblico. Per farlo possiamo usare curl con questo praticissimo comando:

curl -F "url=https://golem.linux.it/directory/bot.php" -F "certificate=@golem.linux.it.pem" https://api.telegram.org/bot110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw/setWebhook

Il comando dovrebbe restituire un messaggio di successo dai server di Telegram.

Come doppio controllo, controlliamo che tutto funzioni davvero provando il metodo getUpdates: deve dare errore perché abbiamo impostato il WebHook.

Programma

Scriviamo il nostro programma (per esempio in PHP) e mettiamolo all'URL appena indicato a Telegram. Mandiamo un messaggio al nostro bot, e notiamo che il nostro script viene contattato (possiamo controllare /var/log/apache2/access.log per vedere se davvero viene chiamato. Si può usare tail -f su quel file per controllarlo in tempo reale)

A questo punto il nostro programma non deve far altro che leggere il JSON che gli è stato inviato tramite HTTP POST e decidere cosa fare. Il JSON contiene il messaggio e tutti i metadati relativi ad esso (presenza di link, emoticon, allegati come foto, video, ecc...) e per il suo contenuto ci sono varie strutture documentate [3].

Un esempio semplice da studiare e facilmente replicabile è la versione 0.99 del GOLEM BOT, reperibile su GitHub. Il BOT inoltra sul canale qualsiasi messaggio, a patto che il mittente sia fra gli utenti autorizzati.

Documentazione

Con l'ultima versione di codice il bot si comporta come segue: se è ricevuto del testo puro oppure un'immagine, viene richiesto mediante tastiera inline se si vuole inoltrare il contenuto al nostro canale Telegram, se si vuole programmare l'invio per il futuro oppure se si vuol annullare l'operazione. Il caso di invio programmato compaiono di seguito un calendario ed una lista di orari che consentono la pianificazione.

Il bot accetta anche alcuni comandi, come /help per avere rapide informazioni sul funzionamento e /list per prendere visione dei messaggi pianificati.

Oss: a proposito di questo uso "strano" del bot, abbiamo scelto di scrivere direttamente a lui e non di fargli leggere i comandi all'interno del nostro gruppo sia per snellire il processo di condivisione (basta fare copia-incolla del link di interesse o buttar giù un messaggio e dare l'OK), sia per non intasare il gruppo stesso con inutili chiacchiere col bot.

Tempo stimato per la condivisione: 3 secondi. Immediatezza d'uso: 1 Googol (circa 10100)

Un secondo script viene lanciato ogni ora dal server, controlla se ci sono messaggi pianificati, eventualmente li invia e li rimuove dalla pila.


Più nel dettaglio

Attenzione: si tratta, allo stato attuale, di appunti sparsi in fase di scrittura

La realizzazione di quanto descritto in precedenza si scontra con la seguente difficoltà: ogni volta che l'utente compie un'azione col bot Ogni volta che un utente invia un messaggio al bot viene chiamato lo script. Ciò rende difficile scrivere un programma che compia azioni "sequenziali".

Si è cercato di identificare le possibili situazioni in cui il bot si dovrà trovare (ad esempio: in attesa di un messaggio oppure in attesa di una data) e le possibili operazioni che l'utente può compiere in ciascuno stato con le relative azioni che il bot prenderà (ad esempio: scrivere un comando, cliccare su un pulsante, eccetera). Nel caso del nostro bot ne viene fuori il grafico seguente:

Telegram-Bot-StateMachine.jpg

Le cosiddette callback sono particolari "messaggi" che si inviano al bot cliccando sui pulsanti delle tastiere inline (che noi abbiamo utilizzato ampiamente).

Il problema della perdita delle variabili che si ha fra un messaggio e l'altro è stato risolto mediante Memcached, un demone che permette di salvare o recuperare variabili in uno spazio di memoria condiviso. Si è stabilito che ogni variabile relativa ad un certo utente debba essere salvata nella forma codice_utente-nome_variabile.

Ogni volta che il bot viene sollecitato lo script verifica che tipo di sollecitazione è stata fatta (callback, messaggio...) e si procede all'interpretazione dei dati. Vengono scartate richieste provenienti dagli utenti non autorizzati (che sono indirizzati verso il canale), da gruppi o dal canale stesso. Dopodiché viene recuperato lo stato in cui il bot si trova (relativo all'utente che ha lanciato la sollecitazione) e il percorso prosegue seguendo la macchina a stati.