Как стать автором
Обновить

Удалённое включение скриптов Mikrotik из Telegram

Время на прочтение6 мин
Количество просмотров60K
На данную реализацию меня подтолкнул Александр Корюкин GeXoGeN своей публикацией "Удалённое включение компьютера бесплатно, без SMS и без облаков, с помощью Mikrotik".

И комментарий в одной из групп ВК Кирилла Казакова:
Да уж, совсем не секьюрно. Я бы лучше написал телеграм бота, который принимает только с моего аккаунта команды на включение.

Я решил написать такого бота.

Итак, первое, что нужно сделать – это создать бота в telegram.

  • Находим в поиске аккаунт с именем @botfather
  • Нажимаем на кнопку Start в нижней части экрана
  • После чего пишем ему команду /newbot

Потом отвечаем на 2 несложных вопроса:

  • Первый вопрос – имя создаваемого бота MyMikrotikROuter
  • Второй вопрос – ник создаваемого бота (должен оканчиваться на bot) MikrotikROuter_bot

В ответ получим токен нашего бота, в моём случае это:

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

image
Затем, нужно найти нашего бота в поиске по имени @MikrotikROuter_bot и нажать на кнопку Start.

После этого нужно открыть браузер и ввести следующую строку:

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

Где XXXXXXXXXXXXXXXXXX – токен вашего бота.

Откроется страница примерно следующего вида:

image

Находим на ней следующий текст:

«chat»:{«id»:631290,

Итак, у нас есть вся необходимая информация для написания скриптов для Mikrotik'а, а именно:

Токен бота: 265373548:AAFyGCqJCei9mvcxvXOWBfnjSt1p3sX1XH4

ID чата, куда он должен писать: 631290

Для проверки можем зайти через браузер:

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

Должны получить результат:

image

Для нашего удобства, сразу добавим команды для бота:

Находим аккаунт с именем @botfather
После чего пишем ему команду /setcommands

  • Он спросит какому боту

Пишем:
@MikrotikROuter_bot

Добавляем команды:

  • helloworld< — Test message on chat 1
  • itsworking — Test Message on chat 2
  • wolmypc — wake Up my PC

Теперь если набрать в чате "/", то должны получить:

image

Теперь переходим к MikroTik.

В RouterOS есть консольная утилита для копирования файлов через ftp или http/https, утилита называется fetch, именно ей мы и будем пользоваться.

Открываем terminal и вводим:

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

Обратите внимание в MikroTik необходим "\" для экранирования знака "?" в URL.

Должны получить результат:

image

Теперь переходим к сриптам:

helloworld
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=ifname\r\
    \n/tool fetch url=\"https://api.telegram.org/boXXXXXXXXXXXXXXXXXXX\\?chat_id=631290&text=wol OK\" keep-resul\
    t=no"

Не забываем указать правильный mac и имя интерфейс, а так же bot-token и chat_id.

Сейчас немного поясню что они делаю:

Скрипт «helloworld» отправляет сообщение: " Hello,world!" в наш чат с ботом.
Скрипт «itsworking» отправляет сообщение: " Test OK, it's Working !" в наш чат с ботом.
Данные скрипты для демонстрации работы.
Скрипт «wolmypc» я добавил, как одну из возможных реализации.
По выполнению скрипта бот напишет в чат «wol OK».
По сути можно запускать абсолютно любой скрипт.

Создаем задание:



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\
    \n\r\
    \n:if ( [/file get [/file find name=getUpdates] size] > 50 ) do={\r\
    \n\r\
    \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\
    \n\r\
    \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\
    \n\r\
    \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\
    \n\r\
    \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 5\r\
    \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



Читаемый вид
непонятно почему но из рабочего скрипта не оглашает глобальные данные, добавил скрипт при загрузке системы.
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; 
} 




Как это работает


Скачиваем наши сообщения «getUpdates» каждые 30 сек., затем парсим, чтобы узнать update_id (номер сообщения) и text (наши команды) и chat_id . По умолчанию getUpdates выводит от 1 до 100 сообщений, для удобства после прочтения команды, сообщение удаляем. в Telegram api сказано, чтобы прочесть сообщение необходимо номер сообщения + 1

/getUpdates?offset=update_id + 1

Все проверено на Mikrotik rb915 RouterOS 6.37.1
Если отправить сразу много команд, они все по очереди будут выполняться с интервалом 30 сек.

P.S. Огромное спасибо Кириллу Казакову за идею и моему другу Александру за помощь со скриптами.

Ссылки


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



Доработал скрипты:

Добавил проверку на chat_id
Проверка на дурака, если кто то напишет нашему боту, он ответит ему: " I can't talk with you. ", аналогично ответит нам, если не распознает команду.
По выполнению команды, бот отписывается в чат (см. Скрипт wolmypc)

UPD

Нашли с 7Stuntman7 что файл с выше ~ 14 сообщений, перестает обрабатываться командой find (ограничения Mikrotik). По этому в будущем скрипт переделаю на lua cпасибо 7Stuntman7 за это, про lua не знал.

UPD 08.12.2016

в Telegram видимо немного изменили «выхлоп» getUpdate. теперь в основном скрипте нужно поправить смещение сообщения с 2 на 1

изменения
:local message [:pick $content ($startLoc + 2) $endLoc] ;

заменить на :

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

Теги:
Хабы:
Всего голосов 40: ↑37 и ↓3+34
Комментарии35

Публикации

Истории

Работа

Ближайшие события