Remote activation of Mikrotik scripts from Telegram

Alexander Koryukin pushed me to this implementation GeXoGeN with his publicationRemote turning on the computer for free, without SMS and without clouds, using MikrotikΒ«.

And a comment in one of the VK groups by Kirill Kazakov:

Yeah, it's not secure at all. I would rather write a telegram bot that only accepts activation commands from my account.

I decided to write such a bot.

So, the first thing to do is to create a bot in telegram.

  • We find in the search an account named @botfather
  • Click on the Start button at the bottom of the screen
  • Then we write him the command / newbot

Then we answer 2 simple questions:

  • The first question is the name of the bot to be created. MyMikrotikROuter
  • The second question is the nickname of the bot being created (should end with bot) MikrotikROuter_bot

In response, we will receive the token of our bot, in my case it is:

Use this token to access the HTTP API: 265373548:AAFyGCqJCei9mvcxvXOWBfnjSt1p3sX1XH4

Remote activation of Mikrotik scripts from Telegram
Then, you need to find our bot in the search by name @MikrotikROuter_bot and press the Start button.

After that, you need to open the browser and enter the following line:

 https://api.telegram.org/botXXXXXXXXXXXXXXXXXX/getUpdates

Where XXXXXXXXXXXXXXXXXX is your bot's token.

A page similar to the following will open:

Remote activation of Mikrotik scripts from Telegram

We find the following text on it:

"chat":{"id":631290,

So, we have all the necessary information for writing scripts for Mikrotik, namely:

Bot token: 265373548:AAFyGCqJCei9mvcxvXOWBfnjSt1p3sX1XH4

Chat ID where he should write: 631290

To check, we can go through the browser:

https://api.telegram.org/bot265373548:AAFyGCqJCei9mvcxvXOWBfnjSt1p3sX1XH4/sendmessage?chat_id=631290&text=test

Should get the result:

Remote activation of Mikrotik scripts from Telegram

For our convenience, we will immediately add commands for the bot:

Finding an account with a name @botfather
Then we write him a command / setcommands

  • He will ask which bot

We write:
@MikrotikROuter_bot

Add commands:

  • helloworld< β€” Test message on chat 1
  • itsworking-Test Message on chat 2
  • wolmypc-wake up my PC

Now if you type "/" in the chat, you should get:

Remote activation of Mikrotik scripts from Telegram

Now let's move on to MikroTik.

RouterOS has a console utility for copying files via ftp or http / https, the utility is called fetch, which is what we will use.

Open terminal and enter:

/tool fetch url="https://api.telegram.org/bot265373548:AAFyGCqJCei9mvcxvXOWBfnjSt1p3sX1XH4/sendmessage?chat_id=631290&text=test " keep-result=no

Please note that MikroTik needs "Β» to escape the sign Β«?' in the URL.

Should get the result:

Remote activation of Mikrotik scripts from Telegram

Now let's move on to scripts:

Bonjour Monde

system script add name="helloworld" policy=read source={/tool fetch url="https://api.telegram.org/bot265373548:AAFyGCqJCei9mvcxvXOWBfnjSt1p3sX1XH4/sendmessage?chat_id=631290&text=Hello,world! " keep-result=no}

itsworking

system script add name="itsworking" policy=read source={/tool fetch url="https://api.telegram.org/bot265373548:AAFyGCqJCei9mvcxvXOWBfnjSt1p3sX1XH4/sendmessage?chat_id=631290&text=Test OK, it's Working " keep-result=no}

wolmypc

system script add name="wolmypc" policy=read source="/tool wol mac=XX:XX:XX:XX:XX:XX interface=ifnamer
    n/tool fetch url="https://api.telegram.org/boXXXXXXXXXXXXXXXXXXX?chat_id=631290&text=wol OK" keep-resul
    t=no"

Do not forget to specify the correct mac and interface name, as well as bot-token and chat_id.

Now I will explain a little what they do:

The "helloworld" script sends a message: "Hello, world!" to our chat with the bot.
The "itsworking" script sends a message: "Test OK, it's Working!" to our chat with the bot.
These scripts are for demonstration purposes.
I added the "wolmypc" script as one of the possible implementations.
Upon execution of the script, the bot will write β€œwol OK” to the chat.
In fact, you can run absolutely any script.

Create a task:

Telegram.src

/system scheduler
add interval=30s name=Telegram on-event=":tool fetch url=("https://api.telegr
    am.org/".$botID."/getUpdates") ;r
    n:global content [/file get [/file find name=getUpdates] contents] ;r
    n:global startLoc 0;r
    n:global endLoc 0;r
    nr
    n:if ( [/file get [/file find name=getUpdates] size] > 50 ) do={r
    nr
    n:set startLoc  [:find $content "update_id" $lastEnd ] ;r
    n:set startLoc ( $startLoc + 11 ) ;r
    n:local endLoc [:find $content "," $startLoc] ;r
    n:local messageId ([:pick $content $startLoc $endLoc] + (1));r
    n:put [$messageId] ;r
    n:#log info message="updateID $messageId" ;r
    nr
    n:set startLoc  [:find $content "text" $lastEnd ] ;r
    n:set startLoc ( $startLoc  + 7 ) ;r
    n:local endLoc [:find $content "," ($startLoc)] ;r
    n:set endLoc ( $endLoc - 1 ) ;r
    n:local message [:pick $content ($startLoc + 2) $endLoc] ;r
    n:put [$message] ;r
    n:#log info message="message $message ";r
    nr
    n:set startLoc  [:find $content "chat" $lastEnd ] ;r
    n:set startLoc ( $startLoc + 12 ) ;r
    n:local endLoc [:find $content "," $startLoc] ;r
    n:local chatId ([:pick $content $startLoc $endLoc]);r
    n:put [$chatId] ;r
    n:#log info message="chatID $chatId ";r
    nr
    n:if (($chatId = $myChatID) and (:put [/system script find name=$messa
    ge] != "")) do={r
    n:system script run $message} else={:tool fetch url=("https://api.teleg
    ram.org/".$botID."/sendmessage?chat_id=".$chatId."&text=I can't t
    alk with you. ") keep-result=no} ;r
    n:tool fetch url=("https://api.telegram.org/".$botID."/getUpdates?
    offset=$messageId") keep-result=no; r
    n} r
    n" policy=
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon 
    start-date=nov/02/2010 start-time=00:00:00
	
add name=Telegram-startup on-event=":delay 5r
    n:global botID "botXXXXXXXXXXXXXXXXXX" ;r
    n:global myChatID "631290" ;r
    n:global startLoc 0;r
    n:global endLoc 0;r
    n:tool fetch url=("https://api.telegram.org/".$botID."/getUpdates") 
    ;" policy=
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon 
    start-time=startup

Readable Viewit is not clear why, but from the working script it does not disclose global data, added the script when the system boots.
Telegram startup

:delay 5
:global botID "botXXXXXXXXXXXXXXXXXX" ;   token bot
:global myChatID "xxxxxx" ;                               chat_id
:global startLoc 0;
:global endLoc 0;
:tool fetch url=("https://api.telegram.org/".$botID."/getUpdates") ;

Telegram

:tool fetch url=("https://api.telegram.org/".$botID."/getUpdates") ;
:global content [/file get [/file find name=getUpdates] contents] ;
:global startLoc 0;
:global endLoc 0;

:if ( [/file get [/file find name=getUpdates] size] > 50 ) do={

:set startLoc  [:find $content "update_id" $lastEnd ] ;
:set startLoc ( $startLoc + 11 ) ;
:local endLoc [:find $content "," $startLoc] ;
:local messageId ([:pick $content $startLoc $endLoc] + (1));
:put [$messageId] ;
#:log info message="updateID $messageId" ;

:set startLoc  [:find $content "text" $lastEnd ] ;
:set startLoc ( $startLoc  + 7 ) ;
:local endLoc [:find $content "," ($startLoc)] ;
:set endLoc ( $endLoc - 1 ) ;
:local message [:pick $content ($startLoc + 2) $endLoc] ;
:put [$message] ;
#:log info message="message $message ";

:set startLoc  [:find $content "chat" $lastEnd ] ;
:set startLoc ( $startLoc + 12 ) ;
:local endLoc [:find $content "," $startLoc] ;
:local chatId ([:pick $content $startLoc $endLoc]);
:put [$chatId] ;
#:log info message="chatID $chatId ";

:if (($chatId = $myChatID) and (:put [/system script find name=$message] != "")) do={
:system script run $message} else={:tool fetch url=("https://api.telegram.org/".$botID."/sendmessage?chat_id=".$chatId."&text=I can't talk with you. ") keep-result=no} ;
:tool fetch url=("https://api.telegram.org/".$botID."/getUpdates?offset=$messageId") keep-result=no; 
} 

How it works

Download our "getUpdates" messages every 30 seconds, then parse to find out update_id (message number) and text (our teams) and chat_id . By default, getUpdates displays from 1 to 100 messages, for convenience, after reading the command, we delete the message. Telegram api says to read a message you need the message number + 1

/getUpdates?offset=update_id + 1

All tested on Mikrotik rb915 RouterOS 6.37.1
If you send many commands at once, they will all be executed in turn with an interval of 30 seconds.

PS Many thanks to Kirill Kazakov for the idea and my friend Alexander for help with the scripts.

references

habrahabr.ru/post/313794
1spla.ru/index.php/blog/telegram_bot_for_mikrotik
core.telegram.org/bots/api
wiki.mikrotik.com/wiki/Manual:Scripting

upd:

03:11:16

Improved scripts:

Added check for chat_id
Checking for a fool, if someone writes to our bot, he will answer him: "I can't talk with you. β€œ, will similarly answer us if it does not recognize the command.
After executing the command, the bot unsubscribes to the chat (see wolmypc script)

UPD

Found with 7Stuntman7 that a file with above ~14 messages is no longer processed by the find command (Mikrotik limitations). Therefore, in the future, I will change the script to lua, thanks 7Stuntman7 for this, I did not know about lua.

UPD 08.12.2016

in Telegram, apparently, they slightly changed the "exhaust" of getUpdate. now in the main script you need to correct the message offset from 2 to 1

changes

:local message [:pick $content ($startLoc + 2) $endLoc] ;

Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π½Π° :

:local message [:pick $content ($startLoc + 1) $endLoc] ;

Source: habr.com