Node-red_telegram_bot.pdf

  • December 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Node-red_telegram_bot.pdf as PDF for free.

More details

  • Words: 2,989
  • Pages: 15
a· ti

ni

fic

o

Tec

c

o

e L ie iceo Sc

n

Sebastiano Melita

Node-red & BotTelegram Utenze a portata di Bot

Marconi Institute Press – 2019

IS G. Marcon

.

v ch i

· II S " M

n i " C i vi t a

ec

a

o rc

Questa pagina è intenzionalmente bianca

i n dice 1 2 3 4 5 6

Node-red 1 Bot 2 Tempistica dei Flow 3 Flows del progetto 4 Flow di impostazione 6 Flow di comando 7

e l e nco delle figure 4.1 4.2 4.3 5.4 5.5 5.6 6.7

Flow del progetto. 4 Nodo Comandi e messaggi. 5 Impostazioni dei nodi. 5 Metodo post. 6 http request. 7 Configurazione del nodo http request. Nodo salva e comanda. 8

8

1

node-red



Il ’copyleft’ ha lo scopo di incoraggiare le persone a cooperare e ad aiutarsi reciprocamente, e a dare a tutti la stessa libertà. Richard Stallman

ode-red è una applicazione web basata su Node.js, Open-Source e di tipo Flow-Based. Node.js è un ambiente multipiattaforma per l’esecuzione di codice Javascript lato server, e in questo senso è un concorrente di linguaggi come PHP o Python. Node.js possiede però una peculiarità che lo potrebbe rendere ad essi preferibile: segue in maniera coerente e pulita il paradigma JavaScript Everywhere, che mira ad unificare la realizzazione del lato client e del lato server di una applicazione web sotto un unico linguaggio di programmazione. Gli elementi funzionali di base di node-red sono oggetti software detti nodi, che possono rappresentare dispositivi hardware, moduli software locali al sistema e API di accesso a servizi in rete. I nodi comunicano tramite messaggi rappresentati in javascript da stringhe o da oggetti che, lungo il cammino che va dal primo nodo fino all’ultimo, vengono modificati mediante l’aggiunta di nuovi campi, o la generazione ex-novo di nuove stringhe o di nuovi oggetti. node-red è ricco di librerie di nodi già pronte all’uso ma, in ogni caso, permette all’utente di creare nodi customizzati adatti alle proprie esigenze tramite i linguaggi di programmazione JavaScript e Python. È anche possibile esportare da un server ad un altro i nodi in maniera semplice ed immediata, trasferendoli sotto forma di testo formattato in JSON da copiare da una finestra di esportazione ad un’altra di importazione. Il formato di scambio è assimilabile ad un elenco di oggetti JSON. In un flow chart node-red è utile immaginare i nodi terminali come dei connettori sul canale di collegamento col mondo esterno. Una ricca libreria di nodi è in grado di fornire connettori ai più disparati livelli di astrazione:

N

• I nodi connettori possono fornire una gestione diretta del mezzo fisico (livello 1 OSI), cioè comunicare con i GPIO e i transceiver (RS232, RS485, ecc.), realizzando a livello superiore (livello 2 pila OSI) i protocolli di arbitraggio più disparati (Profibus, Modbus, ecc);

2

1 . node - r e d

• oppure possono utilizzare i driver e i servizi di comunicazione offerti dal sistema operativo (Raspian) per comunicare su interfacce ethernet o wifi utilizzando le usuali API di rete del livello di trasporto (TCP e UDP). • Ma veramente impressionante è la semplicità d’uso e realizzazione dei servizi di rete di livello applicativo (HTTP, MQTT, COAP, WebSocket, Telegram, ecc.) perché, con pochi nodi e seguendo il naturale flusso logico dei dati, è possibile realizzare, in modalità per lo più visuale, una catena completa di trasferimento dell’informazione tra servizi eterogenei. I nodi intermedi si occupano di leggere, interpretare e smistare i contenuti tra un servizio e l’altro, eventualmente rielaborandoli runtime anche in base agli input dell’utente. Il tutto attraverso una programmazione in buona parte di tipo visuale. Tipici messaggi di risposta, ad esempio, possono essere composti al volo, sotto forma di stringhe JSON nel caso di un web service, oppure sotto forma di un template HTML nel caso di una pagina web. Con lo stesso elementare procedimento si possono creare server MQTT, COAP e web socket che difficilmente, con gli usuali linguaggi di programmazione, trovano una forma unificata di realizzazione altrettanto semplice. Si pensi alla realizzazione niente affatto banale di un server MQTT che in node-red si riduce al solo trascinamento sull’area di lavoro dell’oggetto, scelto tra i tanti disponibili, avente quella funzione. 2

b ot

Riassumiamo brevemente alcune caratteristiche importanti dei BOT: • sono chat riservate alle macchine, cioè ad utenti non “umani”; essi realizzano lo smistamento della messaggistica da utenti umani verso applicazioni di terze parti, ad esempio altri servizi di rete oppure applicazioni che girano su dispositivi HW. • servono essenzialmente a trasformare messaggi in azioni, o meglio, in una catena di operazioni; alcune sono standard cioè impostabili all’interno del BOT stesso ed altre sono invece custom in quanto definite su servizi remoti di terze parti. • sono essenzialmente utenze (account) Telegram speciali, per attivare le quali non è necessario fornire all’infrastruttura un numero di telefono. • sono identificati da un loro id che, di fatto, è indipendente da quello della chat principale dell’utente. Gli utenti possono interagire con i BOT in due maniere: • Possono inviare messaggi e comandi ai bot aprendo una chat con loro o aggiungendoli a gruppi esistenti.

2. bot

3

• Possono inviare richieste inline direttamente dal campo di testo digitando @username del bot e una query. Messaggi, comandi e richieste inviati dagli utenti vengono smistati al software in esecuzione sui server dell’utente. Il server di Telegram si occupa di gestire in maniera trasparente all’utente tutte le funzionalità di crittografia e comunicazione con l’API Telegram. L’utente comunica con questo server tramite una interfaccia HTTPS che offre una versione semplificata delle API di Telegram, detta BotApi. Una libreria Telegram di node-red, in pratica, si occupa proprio di richiamare in maniera semplificata queste API di rete tramite webservices, permettendo di realizzare connessioni ai servizi sia in modalità client che in modalità server. La registrazione di un dispositivo presso il Bot avviene in modalità client di tipo subscriber: il dispositivo rimane in stato di ascolto delle notifiche da parte del server, che non è reale ma simulato mediante una tecnica detta long polling. Il long polling consente di utilizzare un dispositivo in modalità di servizio analoga a quella di un server pur rimanendo di fatto un client. Questo è fondamentale perché permette di iniziare dall’esterno connessioni verso server interni ad una LAN senza dover intervenire in nessun modo sui dispositivi di protezione perimetrale della rete locale (Firewall o NAT). L’accesso al servizio Telegram da parte del dispositivo è filtrato tramite la valutazione del token del Bot, che deve essere inserito come parametro di base del webservice. L’invio di un messaggio al Bot da parte dell’utente avviene invece solo a partire da una normale chat di Telegram ed è filtrato in base alla chat id del messaggio stesso. Il BOT può avere o meno una propria ACL (Access Control List), se questa esiste. La chat id del mittente deve essere presente nella ACL per poter ottenere il diritto di accesso al BOT. Per usare un Bot è necessario inserirlo tra i contatti. Si può fare in due modi. Il primo: cercarlo con l’opzione “Cerca”. Il secondo: aggiungerlo in automatico alla rubrica cliccando sul link apposito. 3

t empistica dei flow

Prima di cominciare la descrizione dei singoli nodi è opportuno spiegare la logica di funzionamento dei flow, cioè in che maniera vengono scambiati i messaggi da un nodo all’altro, focalizzandoci sul fattore tempo. Un nodo è assimilabile ad una funzione javascript con un messaggio per input, un messaggio per output e una serie di parametri prelevati dai campi di testo del form di configurazione del nodo.

4

3 . t e m p i s t ic a de i f l ow

Ma quando è eseguita questa funzione? Tutte le volte che il nodo viene triggerato, cioè quando su un suo ingresso arriva un nuovo messaggio. In pratica un nodo è assimilabile a una funzione javascript che viene chiamata all’arrivo di un messaggio. Viene cioè implementato una sorta di interrupt di alto livello che esegue una funzione alla notifica di un nuovo messaggio all’ingresso di un nodo. È chiaramente una gestione ad eventi che è molto ben supportata dal sottostante linguaggio node.js per il quale ogni azione sul computer (apertura di un file, inserimento di un carattere, ecc.) è di fatto nativamente un evento. Gli eventi non si possono gestire all’interno del codice in maniera sincrona (cioè prevedibile) a priori come in una normale chiamata di funzione (precisamente localizzata all’interno del codice e quindi dalla tempistica nota), ma, per forza di cose, in maniera asincrona. node-red unifica tutta la gamma dei possibili eventi possibili in un unico evento parametrizzato: l’arrivo di un messaggio. La particolarità di Node.js (e quindi di node-red) è quella di gestire chiamate di funzione asincrone in un ambiente di esecuzione rigorosamente single-threaded. La catena delle varie esecuzioni procede dal primo all’ultimo nodo ma può essere commutata da un flow all’altro, per mezzo di nodi switch, od oppure filtrata, cioè, propagata o meno, in base al valore di determinati campi del messaggio. 4

f lows del progetto

Figura 4.1. Flow del progetto.

Il progetto è composto da due flow che eseguono due operazioni diverse. • Il primo restituisce alla chat telegram, a seguito cella ricezione del comando /scenario1, la tastiera di configurazione dello scenario richiesto. • Il secondo interpreta i messaggi sulla chat del Bot alla ricerca di comandi di attivazione o disattivazione di un relè.

5

4. flows de l p ro g e t t o

Prima di esaminare i due flow separatamente spieghiamo la funzione del nodo che abbiamo chiamato “Comandi e messaggi”. Si tratta di un receiver telegram, cioè un ascoltatore che, realizza un server virtuale tramite long polling. Il nodo, cioè, invia periodicamente delle richieste di servizio con le quali si Figura 4.2. Nodo Comandi e messagaccerta se è disponibile un nuovo messaggio gi. per lui. In questo caso il server Telegram si comporta come una casella di posta che viene interrogata con la cadenza del secondo o anche meno. Il nodo possiede due uscite. La prima solo per i comandi e la seconda, più generale per tutti gli altri messaggi. Nel primo flow siamo interessati ai comandi, nel secondo ai messaggi. I messaggi e i comandi in arrivo contengono due tipi di informazioni: • Informazioni esplicite cioè inviate dall’utente quando preme pulsanti o scrive sulla chat; • altre di servizio normalmente non visibili all’utente come la chat_id del mittente. Il nodo accetterà i messaggi solo se la chat_id del mittente è inclusa nella ACL degli utenti autorizzati impostata sul nodo dall’utente che ha configurato il servizio. Se il mittente è autorizzato il nodo triggera, cioè invia, un messaggio con i campi che ha ottenuto dalla richiesta di servizio cioè il messaggio (o il comando) e la chat_id del mittente. Il messaggio è codificato in ascii come un’unica stringa di testo che contiene la rappresentazione JSON dell’oggetto messaggio, cioè, i campi content e Chat_id.

(a) Impostazione del filtro di comando.

(b) Impostazione del Bot.

Figura 4.3. Impostazioni dei nodi.

L’impostazione del Bot (vedi fig. 4.3b) imposta il token per il diritto di accesso al Bot e, più in basso, anche l’elenco delle chat_id delle chat telegram autorizzate ad inviare messaggi al Bot. I messaggi con chat_id non presente in elenco verranno scartati.

6

5

4 . f l ows de l p ro g e t t o

f low di impostazione

Il ruolo di questo flow è selezionare la tastiera adatta ad un particolare scenario di utilizzo domotico ad esempio lo scenario di illuminazione di un ambiente oppure il più prosaico scenario di servizio di apertura di un cancello. La tastiera adatta deve essere scelta con un comando che riporta il nome dello scenario ad esempio /soggiorno oppure /cancello. Come si può intuire immediatamente i comandi sono sempre preceduti dal prefisso /. Il flusso delle operazioni è: 1. ricevi il comando tenendo traccia della chat_id del mittente; 2. crea i parametri della richiesta http di tipo POST da inviare alla chat a cui si riferisce l’id precedente; 3. esegui la richiesta del metodo sendMessage sulla API pubblica rest http pubblicata sul server remoto di Telegram. Il metodo sendMessage contiene il riferimento al token del mittente cioè il token del Bot. Tra i parametri della richiesta vi è il campo reply_markup che definisce le etichette dei pulsanti della tastiera Figura 5.4. Metodo post. da inviare sotto forma di array di array di array di stringhe. L’etichetta è anche il valore del messaggio Telegram che verrà inviato in chat alla pressione del pulsante. L’array rappresenta una tabella che definisce anche il layout dei pulsanti, nel nostro caso due righe e due colonne. Il Bot invia il messaggio in chat presentandosi come un qualunque altro utente della chat tramite il proprio token. Il codice completo del nodo è: Codice nodo 1 2 3 4 5 6 7 8 9 10 11 12

msg . payload = { chat_id : msg . payload . chatId , text : ' Adesso puoi comandare lo scenario 1 ', reply_markup : JSON . stringify ({ keyboard : [ [ ' 1 _ON ' , '1 _OFF '] , [ ' 2 _ON ' , '2 _OFF '] ] }) }; msg . headers = { ' content - type ': ' application / json '}; return msg ;

5. flow di i m p o s ta z ion e

7

Una assegnazione è richiesta per tradurre il parametro chat_id che, pur avendo uguale significato, ha nome diverso nelle due API che andiamo ad usare, quella di ricezione che fa l’aggiornamento dei contenuti di una chat e quella di trasmissione che esegue il metodo sendMessage sul nodo che pubblica i contenuti sul Bot. Il metodo di classe JSON.stringify(obj) ha per argomento un riferimento ad un oggetto creato inline, cioè direttamente come argomento di una funzione, e restituisce la sua rappresentazione sotto forma di stringa. Viene usato per creare il valore del campo reply_markup che deve essere di tipo stringa. In genere le stringhe sono, assieme ai byte, i soli formati per i dati utilizzabili per le trasmissioni di tipo seriale che sono tipicamente usate sui canali di comunicazione. In un momento successivo, in ricezione, viene effettuata l’operazione contraria di parallelizzazione ricostruendo l’oggetto originale per mezzo della funzione JSON.stringify(obj). Dobbiamo adesso utilizzare il sender di Telegram. In libreria ce ne è già uno pronto all’uso che però al momento è inutilizzabile probabilmente perché sono state recentemente aggiornate le API Figura 5.5. http request. del metodo sendMessage. È l’occasione per crearne uno nostro a partire dal nodo generico http request che è, più in generale, in grado di interfacciarsi con qualunque api rest http. Il nodo può lavorare in modalità get o post a seconda del tipo di richiesta http. Nel nostro caso le API del metodo richiedono parametri di tipo post che devono essere forniti come messaggio in ingresso e verranno inseriti nel corpo del messaggio http in uscita. In un campo del form del metodo deve poi essere inserito l’URL https della richiesta. Consultando la documentazione delle API di Telegram ricaviamo che la struttura deve essere: https://api.telegram.org/bot:/sendMessage 6

f low di comando

Questo flow serve a gestire le richieste di apertura e chiusura del relè provenienti da una chat Telegram. Ogni nodo Telegram di base esegue una particolare richiesta di servizio richiamando una API di rete (webservices rest http) pubblicata sul server Telegram. La risposta all’arrivo di un messaggio può essere valutata con un nodo di debug che restituisce:

8

6 . f l ow di c om a n d o

Figura 5.6. Configurazione del nodo http request. msg.payload : Object {chatId: 256332767, messageId: 1158, type:"message", content: "1_ON"}

Il flusso delle operazioni è: 1. ricevi il messaggio corrispondente allo stato del pulsante; 2. mappa lo stato del pulsante sulla chat di Telegram con il valore dello stato richiesto da openhab; 3. esegui la richiesta del metodo sendMessage sulla API pubblica rest http pubblicata sul server remoto (in realtà localhost) di node-red. Il nodo successivo è un nodo funzione, cioè un nodo generico di node-red che si occupa di eseguire programmi javascript elaborando gli ingressi e generando un messaggio in uscita nel momento in cui il nodo viene triggerato. Il suo ruolo è di mappare il comando della chat sul comando di node-red, cioè tradurre i comandi dalla sintassi della chat nella particolare sintassi richiesta da node-red. Figura 6.7. Nodo salva e comanIn linea del tutto generale questa operazione da. deve essere fatta avendo cura di non inviare nulla in presenza di messaggi spuri cioè non previsti, in questo caso non riconosciuti da node-red, cosa che può sempre avvenire provenendo questi da chat pubbliche.

6. flow di c om a n d o

9

Per il comando dei relè un canonico costrutto if-else-if è sufficiente allo scopo, basta inserire tanti rami quanti sono i messaggi da riconoscere. Comando relè. 1 2 3 4 5 6 7 8 9

var m = msg . payload . content ; msg . headers = { ' content - type ': ' application / json '}; if ( m ==" 1 _ON ") { msg . payload =" ON "; return msg ; } else if ( m ==" 1 _OFF ") { msg . payload =" OFF "; return msg ; }

A questo punto inviamo i comandi opportunamente tradotti al nodo sender di Nodered che utilizza i valori come parametri di una propria richiesta http presso un web service in ascolto sull’indirizzo localhost. Come al solito, un sender node-red altro non è che un particolare nodo http che compone la richiesta per noi liberandoci dall’incombenza di studiarci le API di quel particolare servizio di rete (in questo caso di Openhab).

MarconiInstitutePress

This work is licensed under the Creative Commons Attribution 3.0. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/3.0/ or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.

MarconiInstitutePress

Typeset with LATEX 2ε