Perché far parlare MikroTik con Telegram?

Perché la mail di avviso la leggi la mattina dopo, il messaggio sul telefono lo leggi subito. Un router che ti scrive “la linea principale è caduta” o “c’è un DHCP pirata sulla LAN” mentre succede vale più di dieci grafici guardati il giorno dopo.

In questo howto costruiamo una sola cosa: uno script telegram che manda un messaggio. Una volta che c’è, lo usano tutti gli altri howto del Dojo che devono avvisare qualcuno, dal Netwatch al watchdog PoE: una riga e il router ti scrive.

Schema: script telegram del MikroTik che chiama api.telegram.org, il bot che inoltra il messaggio al telefono
Lo script del router chiama l’API di Telegram in HTTPS; il bot consegna il messaggio sul telefono.

Passo 1: come creo il bot?

I messaggi li manda un bot, un account Telegram automatico. Si crea parlando con un altro bot, quello ufficiale di Telegram:

  1. su Telegram cerca @BotFather e scrivigli /newbot;
  2. dai al bot un nome (per esempio “Router Ufficio”) e uno username che finisca con bot (per esempio routerufficio_bot);
  3. BotFather ti risponde con il token, una stringa come 123456789:AAH…. Copiala.

Un consiglio nato sul campo: se sbagli a scrivere il nome o lo username, non correggere il messaggio, mandane uno nuovo. BotFather legge solo i messaggi nuovi e, se ne modifichi uno, si perde a metà della procedura e risponde con un errore generico. Nel dubbio, /cancel e si riparte con /newbot.

⚠️ Attenzione: il token è la password del bot. Chi lo ha può mandare messaggi a nome suo. Se finisce dove non deve, BotFather ne genera uno nuovo e il vecchio smette di funzionare.

Passo 2: a chi deve scrivere il bot?

Il bot scrive a una chat, identificata da un numero, il chat_id. Per scoprirlo:

  1. apri la chat con il tuo bot, premi Avvia e scrivigli un messaggio qualunque, per esempio “ciao” (un bot non può scrivere per primo a qualcuno che non gli ha mai scritto);
  2. dal browser apri https://api.telegram.org/botTOKEN/getUpdates, mettendo il tuo token al posto di TOKEN;
  3. nella risposta cerca "chat":{"id":: il numero che segue è il tuo chat_id.

Vuoi gli avvisi in un gruppo, per esempio quello dei tecnici? Aggiungi il bot al gruppo, scrivi un messaggio nel gruppo e ripeti il punto 2: il chat_id di un gruppo è un numero negativo, per esempio -1001234567890.

Passo 3: il router raggiunge Telegram?

Il router deve risolvere api.telegram.org e uscire su internet in HTTPS. Una prova veloce:

/ping api.telegram.org count=3

Se risponde, sei pronto. Se non risolve il nome, controlla il DNS del router (/ip dns print).

Passo 4: come creo lo script telegram?

Uno script che contiene il “motore” dell’invio. Metti il tuo token e il tuo chat_id al posto di quelli di esempio:

/system script add name=telegram policy=read,write,test source={
:local token "123456789:AAH-esempio-del-token"
:local chat "987654321"
:local testo ([/system identity get name] . ": " . $1)
/tool fetch url=("https://api.telegram.org/bot" . $token . "/sendMessage") http-method=post \
    http-header-field="Content-Type: application/json" check-certificate=yes \
    http-data=[:serialize to=json value={"chat_id"=$chat; "text"=$testo} options=json.no-string-conversion] \
    output=none
}

Cosa fa, riga per riga:

  • $1 è il testo che gli passi; davanti ci mette il nome del router (/system identity), così quando hai venti router sai subito chi ti sta scrivendo;
  • /tool fetch chiama l’API di Telegram in HTTPS; check-certificate=yes verifica che dall’altra parte ci sia davvero Telegram, con i certificati già presenti in RouterOS;
  • :serialize to=json costruisce il messaggio in JSON e sistema da solo virgolette, barre e accenti, che scritti a mano romperebbero tutto. L’opzione json.no-string-conversion serve a non far diventare il chat_id un numero con i decimali (senza, "987654321" diventa 987654321.000000).

Il /tool fetch e tutti i suoi parametri sono nel manuale MikroTik: Fetch.

Passo 5: come mando il primo messaggio?

Lo script lo trasformi in una funzione con :parse e poi la chiami. Dal terminale, tutto dentro le graffe (una variabile :local vive solo dentro il suo blocco):

{
:local telegram [:parse [/system script get telegram source]]
$telegram "ciao, sono il router"
}

Sul telefono arriva un messaggio come Router Ufficio: ciao, sono il router. Il testo puoi anche comporlo al volo:

$telegram ("RouterOS " . [/system resource get version] . ", uptime " . [/system resource get uptime])

Le stesse due righe vanno in qualunque script che deve avvisarti: uno scheduler, un Netwatch, un alert del DHCP. Come funzionano :parse e le funzioni lo trovi nel manuale MikroTik: Scripting.

⚠️ Attenzione: perché non una funzione globale? Sembrerebbe più comodo definire $telegram una volta sola con :global. L’ho provato: da terminale funziona, ma negli script di Netwatch la funzione globale risulta vuota e il messaggio non parte, senza nessun errore nel log. Con :parse ogni script si carica la sua copia al momento e funziona ovunque, anche subito dopo un riavvio.

Passo 6: come mi faccio avvisare quando il router si riavvia?

Un riavvio che non hai chiesto tu è sempre una notizia: corrente andata via, un crash, qualcuno che ha staccato la spina. Uno scheduler all’avvio te lo dice:

/system scheduler add name=telegram-avvio start-time=startup on-event={
    :delay 30s
    :local telegram [:parse [/system script get telegram source]]
    $telegram ("riavviato, RouterOS " . [/system resource get version])
}

Il :delay 30s lascia al router il tempo di avere internet. In laboratorio, dopo un riavvio, il messaggio è arrivato in meno di un minuto:

CHR: riavviato, RouterOS 7.24.5 (stable)

Passo 7: come capisco se qualcosa non va?

Ogni invio lascia una riga nel log:

/log print where topics~"fetch"
  • Download from api.telegram.org FINISHED: il messaggio è partito;
  • FAILED: ERROR parsing http: 401 …: il token è sbagliato o è stato revocato;
  • FAILED: Status 400, Bad Request: il chat_id è sbagliato, oppure dalla chat non hai mai scritto al bot;
  • nessuna riga: lo script non è partito. Controlla che lo script si chiami proprio telegram e, da terminale, di aver messo le righe dentro le graffe.

Cosa controllo prima di metterlo in produzione?

Il token sta scritto nello script, e si vede in due posti dove non te lo aspetti. L’ho verificato: /system script export lo riporta per intero e, quando aggiungi o modifichi lo script, anche il log di sistema registra tutto il sorgente, token compreso. Quindi:

  • gli export e i log che mandi in giro, per assistenza o in un forum, vanno ripuliti dal token; se mandi i log a un syslog esterno, ricordati che anche lì c’è il token;
  • l’accesso al router va protetto: segui l’Hardening di base di un router MikroTik;
  • un bot per cliente, non uno per tutti: se un token esce, ne rifai uno solo.

Adesso il router sa scrivere: negli altri howto del Dojo, quando serve un avviso, bastano le due righe del passo 5.

Provato in laboratorio su PNETLab con CHR RouterOS 7.24.5 (stable) e un bot vero: creazione con BotFather, chat_id da getUpdates, messaggio ricevuto sul telefono (con accenti), JSON con virgolette e barre, messaggio all’avvio dopo un riavvio, errori 400 e 401 nel log, token visibile nell’export.

Domande frequenti

Come manda un messaggio Telegram un router MikroTik?

Con /tool fetch verso l’API dei bot di Telegram, metodo sendMessage, passando in JSON il chat_id e il testo. Serve un bot creato con @BotFather e il suo token.

Come trovo il chat_id?

Scrivi un messaggio al bot, poi apri https://api.telegram.org/bot<token>/getUpdates: il numero dopo "chat":{"id": è il chat_id. Per un gruppo è un numero negativo.

Perché il messaggio non arriva?

Guarda /log print where topics~"fetch": 401 vuol dire token sbagliato, 400 chat_id sbagliato o bot mai contattato. Se non c’è nessuna riga, lo script non è partito.

Perché la funzione globale $telegram non funziona in Netwatch?

Perché gli script di Netwatch non vedono le funzioni globali create da un’altra sessione: la variabile risulta vuota e il messaggio non parte. Caricando lo script con :local telegram [:parse [/system script get telegram source]] funziona in qualunque contesto.

Posso mandare messaggi con accenti e virgolette?

Sì, se il JSON lo costruisci con :serialize to=json: RouterOS fa da solo l’escape di virgolette e barre e lascia passare gli accenti.