Differenze tra le versioni di "GOLEM Telegram Bot"

Da GolemWiki.
Jump to navigation Jump to search
m (Aggiornamenti sul codice github e il VPS)
(→‎Cosa fa il nostro bot: Appuntate alcune delle nuove funzionalità -- da rivedere)
Riga 75: Riga 75:
 
Il nostro bot è raggiungibile col nome @GOLEMpoliBot, naturalmente riceve messaggi da chiunque, ma prende in considerazione solo quelli provenienti dagli utenti che noi abilitiamo (per ragioni che saranno ovvie presto).
 
Il nostro bot è raggiungibile col nome @GOLEMpoliBot, naturalmente riceve messaggi da chiunque, ma prende in considerazione solo quelli provenienti dagli utenti che noi abilitiamo (per ragioni che saranno ovvie presto).
  
Il messaggio viene sempre inoltrato al nostro canale su Telegram, indipendentemente dal contenuto.
+
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.
Se il messaggio contiene un URL, allora si suppone che il link sia adatto per la mailing list, e allora viene analizzato, e se punta ad una pagina HTML con un campo ''title'' valido, viene inoltrato anche sulla nostra mailing list tradizionale.
+
Il caso di invio programmato compaiono di seguito un calendario ed una lista di orari che consentono la pianificazione.
  
'''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 per snellire il processo di condivisione.
+
Il bot accetta anche alcuni comandi, come <code>/help</code> per avere rapide informazioni sul funzionamento e <code>/list</code> per prendere visione dei messaggi pianificati.
Così, per condividere un link interessante, basterà inviarlo al bot e lui penserà a inoltrarlo ai canali di comunicazione giusti, senza necessità di usare comandi strani.
+
 
 +
'''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 10<sup>100</sup>)
 
Tempo stimato per la condivisione: 3 secondi. Immediatezza d'uso: 1 Googol (circa 10<sup>100</sup>)
  
Infine, un altro script indipendente osserva i cambiamenti sul feed RSS del nostro blog Wordpress, e quando ci sono dei nuovi articoli li inoltra sulla mailing list e sul canale. Di nuovo, penserà a tutto lui.
+
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:
 +
 
 +
[[File:Telegram-Bot-StateMachine.jpg | 600px | center]]
 +
 
 +
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 <code>Memcached</code>, 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 <code>''codice_utente''-''nome_variabile''</code>.
 +
 
 +
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 bacchettati con un messaggio di errore), da gruppi o dal canale stesso.
 +
Dopodiché viene recuperato lo stato in cui il bot si trova (relativo all'utente che ha sollevato la sollecitazione) e il percorso prosegue seguendo la macchina a stati.
  
 
[[Category:Officina]]
 
[[Category:Officina]]
 
[[Category:Howto]]
 
[[Category:Howto]]

Versione delle 10:26, 25 set 2017

Il bot Telegram del GOLEM è in fase di testing.

Il codice sorgente può essere consultato su github.

Al momento tutto sta correttamente funzionando sul nostro VPS.

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].

Cosa fa il nostro bot

Il nostro bot è raggiungibile col nome @GOLEMpoliBot, naturalmente riceve messaggi da chiunque, ma prende in considerazione solo quelli provenienti dagli utenti che noi abilitiamo (per ragioni che saranno ovvie presto).

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 bacchettati con un messaggio di errore), da gruppi o dal canale stesso. Dopodiché viene recuperato lo stato in cui il bot si trova (relativo all'utente che ha sollevato la sollecitazione) e il percorso prosegue seguendo la macchina a stati.