Pull to refresh

Телеграм терминал для Laurent

Reading time9 min
Views1.9K
Ранее на Хабре я размещал несколько статей о многофункциональных сетевых контроллерах управления и мониторинга Laurent, в том числе наиболее продвинутого модуля компании KernelChip — контроллера Laurent-5G. Мы обзорно изучали этот контроллер (это делали также и другие авторы), устанавливали его в корпус, создавая портотип готового PDU, имели опыт управления функциями модулей Laurent через скрипты Микротик Роутер ОС.

C того времени много воды утекло, но надежные контроллеры от KernelChip и сейчас работают в наших сетях, выполняя свои функции как часы. Имея «большой запас» аппаратных возможностей (см. статью «Многофункциональный сетевой контроллер управления и мониторинга Laurent-5G»), за время с момента разработки, Laurent-5G и его предшественники «обросли» многими программными возможностями, их прошивки стали поддерживать не только работу в локальной сети, но и мониторинг и управление в «облачном» режиме.

В последнее время я много занимался написанием скриптов для РоутерОС компании Микротик, и, в частности, работал над парсерами логов роутера с уведомлением работы в чатботы Телеграм. При этом мы «научили» чатботы выполнять скрипты, функции и прямые конструкции команд РоутерОС (https://habr.com/ru/post/650563).

На основе опыта этих работ и изучения документации последних версий прошивок Laurent, получивших возможность не только исполнять http GET, но и возвращать многочисленные данные настроек и статуса в формате JSON, мне пришла в голову простая мысль – а почему бы не написать своеобразный бот-терминал для Телеграм, который бы позволял передавать команды модулям Laurent и информировать пользователя в чате о результатах их выполнения ? Это особенно было бы удобно для управления контроллерами Laurent с мобильных устройств, где размеры экрана не удобны для работы с WEB-интерфейсом модулей и облачного сервиса.

Сказано – сделано! Такой терминал (я назвал его Laurent Telegram Terminal, LTT) был достаточно быстро написан на основе структуры этого бота и успешно работает, позволяя выполнять (внимание!) все $KE-команды модулей Laurent (см. описание $KE команд на сайте производителя), а также считывать статус состояния и настроек в JSON-формате с последующим парсингом в многомерный ассоциативный массив РОС (используется скриптовый JSON-парсер для Микротик). Результаты работы команд возвращаются в виде текстовых сообщений в Телеграм чатбот пользователя.

В своей работе LTT использует возможности команды РоутерОС /tool fetch, организуя обращения к модулям Laurent через HTTP GET для выполнения $KE-команд:

 http://<IP адрес_модуля>/cmd.cgi[?psw=<Пароль_Модуля>]&cmd=<Ke_Команда>


и запроса статусов:

 http://<IP адрес модуля>/<json_sensor.cgi>[?psw=<Пароль модуля>]
 http://<IP адрес модуля>/<json_set.cgi>[?psw=<Пароль модуля>]


Для обращения к API Telegram также используются /tool fetch HTTP POST c URL

https:// api.telegram.org/<bot$TToken>/getUpdates\?chat_id=<ChatId>

позволяющая считывать команды из бота, и

https:// api.telegram.org/<bot$TToken>/sendmessage\?chat_id=<ChatId>

для печати сообщений от Laurent в чат.

Весьма компактный код терминала LTTsystem с комментариями:

# LTTsystem by Sertik 28/11/2022
# ----------------------------------------------------------------------------------------------
# http://адрес_модуля/cmd.cgi?psw=<Пароль_Модуля>&cmd=<Ke_Команда>
# http://<IP адрес модуля>/<имя json файла>[?psw=<Пароль модуля>]
#----------------------------------------------------------------------------------------------

#LTT settings:
:local Emoji "%F0%9F%A7%9A"
:local Esys "$Emoji$[/system identity get name]%0A"
:local TToken "XXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
:local TChatId "YYYYYYYYY"
:local Ladr 192.168.0.1
:local Sport 80
:local PSW "Laurent"

# allowed $KE commands:
:local arrayCom [:toarray {"REL"="set on/off/switch rele";
                                      "RDR"="rele status";
                                      "INF"="information Laurent-5G board";
                                      "GST"="modem status";
                                      "SMS"="send SMS";
                                      "PSW"="read module password";
                                      "SEC"="security policy";
                                      "RID"="read status OUT line";
                                      "SENSOR"="get sensors status Laurent";
                                      "SETTINGS"="get sensors status Laurent";
                                       "WR"="set on/off/switch OUT line"}]

# sub-function of replacing a space with a comma in a line
:local SpaceComChar do={:local string; :set $string $1;
:local StrTele ""; :local code "";
:for i from=0 to=([:len $string]-1) do={:local keys [:pick $string $i (1+$i)];; if ($keys=" ") do={:set $code ","} else={:set $code $keys}; :set $StrTele ("$StrTele"."$code")}
:return $StrTele;}

#main body script
/system script run JParseFunctions
:global Toffset
:if ([:typeof $Toffset] != "num") do={:set Toffset 0}
do {
:global JSONIn [/tool fetch url="https://api.telegram.org/bot$TToken/getUpdates\?chat_id=$TChatId&offset=$Toffset" as-value output=user]; :set JSONIn ($JSONIn->"data")
} on-error={:set $JSONIn []}
:global fJParse
:global fJParsePrintVar
:global Jdebug false
:global JParseOut [$fJParse]
:local Results ($JParseOut->"result")

# -----------------
:if ([:len $Results]>0) do={
:local TXT
  :foreach k,v in=$Results do={
    :if (any ($v->"message"->"text")) do={
     :local cmd ($v->"message"->"text")
     :local cmdR 
:if ([:len [:find $cmd " "]]=0) do={:set cmdR $cmd} else={:set cmdR [:pick $cmd 0 [:find $cmd " "]]}
      :local prefix "/"
      :local cmdrun 0

# executing special commands "list" (output commands to chat)
 :if ($cmdrun=0) do={
  :if (($cmd="list") or ($cmd=$prefix."list")) do={
  :local cmdlist; :foreach t,n in=$arrayCom do={:set $cmdlist ("$cmdlist"."/$t "."- "."$n"."\n")}
   /tool fetch url="https://api.telegram.org/bot$TToken/sendmessage\?chat_id=$TChatId"  \
    http-method=post  http-data="text=$Emoji $[/system identity get name] Laurent terminal commands:%0A$cmdlist" keep-result=no; set cmdrun 1
 }
}

# executing special commands for read JSON data
 :if ($cmdrun=0) do={
    :local fetS 
  :if (($cmdR="SENSOR") or ($cmdR=$prefix."SENSOR")) do={:set fetS "sensor"}
  :if (($cmdR="SETTINGS") or ($cmdR=$prefix."SETTINGS")) do={:set fetS "set"}
         :if ([:len $fetS]!=0) do={
                          :local StrFetchLaurent; :local Lanswer; 
                          :set StrFetchLaurent ("http://"."$Ladr".":"."$Sport"."/json_"."$fetS".".cgi\?psw=$PSW")
                         :do {
                          :set Lanswer [/tool fetch url=$StrFetchLaurent as-value output=user]
                          } on-error={:set TXT "ERROR call to Laurent 5G module"; log error $TXT; :set $JSONIn []}
                          :global JSONLoads;
            :if ($cmd!=$cmdR) do={
                         :local WLanswer [$JSONLoads ($Lanswer->"data")]
                         :local cmdX [:pick $cmd ([:find $cmd " "]+1) [:len $cmd]]; :set $cmdR $cmdX; :set Lanswer [:tostr ($WLanswer->$cmdX)];} else={:set Lanswer ($Lanswer->"data")}
   /tool fetch url="https://api.telegram.org/bot$TToken/sendmessage\?chat_id=$TChatId"  \
    http-method=post  http-data="text=$Emoji $[/system identity get name] Laurent status [$cmdR]:%0A $Lanswer" keep-result=no; set cmdrun 1
 }
}


# executing commands from a list arrayCom
   :if ($cmdrun=0) do={   
        :foreach key,val in=$arrayCom do={
              :if (($cmdR=$key) or ($cmdR=$prefix.$key)) do={
                      :set cmd [$SpaceComChar $cmd]; 
                         if ($cmdR=$prefix.$key) do={:set cmd [:pick $cmd ([:find $cmd $prefix]+1) [:len $cmd]]}
                          :local StrFetchLaurent; :local Lanswer; 
                          :set StrFetchLaurent ("http://"."$Ladr".":"."$Sport"."/cmd.cgi\?psw=$PSW&cmd=$cmd")
                         :do {
                          :set Lanswer [/tool fetch url=$StrFetchLaurent as-value output=user]
                          } on-error={:set TXT "ERROR call to Laurent 5G module"; log error $TXT;}
                                  :set Lanswer ($Lanswer->"data")                     
                                  :set cmdrun 1;
                                 :set TXT ("Executing Laurent command in progress <- $cmd -> %0A Module answer $Lanswer")
                                /tool fetch url="https://api.telegram.org/bot$TToken/sendmessage\?chat_id=$TChatId" \
                                 http-method=post  http-data="text=$Esys $TXT" keep-result=no;
              }
       }
 }

    :set $Toffset ($v->"update_id" + 1)}
  }  
} else={ 
  :set $Toffset 0;
}


LTTsystem это простой скрипт для polling-бота, меня его работа вполне устраивает. Желающие могут создать webhook-бот самостоятельно, например по этому примеру.

Как создать своего бота в Телеграм и чат с ним я описывать не буду, для этого существует множество инструкций в Интернет, например одна из первых здесь. Кто хочет — сделает это по имеющимся описаниям без особого труда.

Далее, для работы Laurent Телеграм Терминала с созданным ботом необходимо, конечно, иметь роутер Микротик, либо создать его на ПК X86 или сделать «облачный виртуальный» аналог роутера — (Cloud Hosted Router).

На роутере нужно поместить вышеприведенный код в репозиторий скриптов, настроить в скрипте пользовательские параметры, такие, как API KEY ID бота (бот Токен) и ID чата пользователя, Emoji своего роутера (для наглядности отображения сообщений в чате), а также параметры модуля Laurent в своей локальной сети (IP-адрес, порт, пароль доступа для защищенного режима).

:local Emoji "%F0%9F%A7%9A"
:local Esys "$Emoji$[/system identity get name]%0A"
:local TToken "XXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
:local TChatId "YYYYYYYYY"
:local Ladr 192.168.0.1
:local Sport 80
:local PSW "Laurent"

Не забудьте также разместить в репозитории JSON-парсер JParseFunctions, использующийся в работе LTTsystem (ссылка на его загрузку приведена выше).

Чтобы вызывать LTTsystem с нужной переодичностью, необходимо добавить задание в Планировщик роутера (/system Scheduler), указав старт задания при загрузке и частоту повторения (оптимально 10-20 секунд, можно чаще (зависит от загруженности и производительности роутера и скорости Интернет-соединения).

В коде главного скрипта Лоран-Терминала LTTsystem настраивается список разрешенных к исполнению из чата $KE-команд Laurent (массив arrayCom, см. код). Название команды в списке должно точно соответствовать её $KE-имени без параметров; команды, отсутствующие в списке, исполняться из чата не будут. Таким образом, мы организуем защиту от выполнения «опасных» команд или тех команд, выполнение которых не хотим доверять Телеграмм-боту. Пользователь может редактировать этот массив команд по своему усмотрению. Обратите внимание, что параметры команд должны передаваться в чате после имени команды через пробел, а не через запятую, как в $KE-терминале WEB-интерфейса Laurent. Разделять параметры пробелами в Телеграмм чате удобнее и нагляднее, нежели разделение их запятыми.

Вот несколько иллюстраций работы простого Laurent Telegram Терминала:

image
Ответ бота на команду INF (модуль возвращает имя, версию прошивки и серийный номер платы).


Ответы бота на команды REL 3 0 (Выключить реле №3) и RDR ALL (запросить статус всех четырех реле) при успешном завершении.


На команду включить реле 5 (не существующее реле) модуль возвращает в чатбот ошибку (#ERR)


Иллюстрация ответа бота на запрос статуса модуля раздела SETTINGS. Ответ возвращается в виде многомерного ассоциативного массива.

Можно запросить только определенную группу параметров разделов SETTINGS и SENSORS. Например:


Ответ бота при запросе части статуса модуля раздела SENSOR – только параметры adc (АЦП) в виде ключевого массива, преобразованного в строку.

Как видно из иллюстраций, скрипт бота LTTsystem развернут на маломощном роутере MapLite и даже на нем бот работает быстро и не загружает процессор.

Следует иметь ввиду, что приведенная выше версия скрипта LTTsystem, не поддерживает работу с групповыми чатами Телеграм и лишена многих сервисных возможностей существующей расширенной версии, код которой, возможно будет приведен позже на русскоязычном форуме Микротик в разделе «готовые скрипты».

В расширенной версии, сделанной «под ключ» можно настроить все нужные параметры в скрипте LTTstart и запустить его, скрипт выполнит все необходимые настройки и сам добавит задание в Планировщик автоматически (в отключенном состоянии). После проверки параметров, по готовности, пользователю останется только его активировать.

Также в «продакшен» версии присутствует функция установки списка команд бота для быстрого вызова из чата Телеграм (по /), куда добавлены следующие сервисные команды: /list (печатающая в чат список разрешенных $KE-команд), команда /chatid (позволяющая быстро узнать ID чата), команды /pause и /restore, соответственно приостанавливающая и возобнавляющая возможность управления модулем Laurent через Телеграм Терминал. Также имеются внутренние команды /ip и /password, позволяющие устанавливать IP-адрес и порт контроллера, с которым мы работаем (т.к. в локальной сети контроллер может быть не один – поддержка мультимодульного управления) и установка пароля доступа к модулю для защищенного режима.


Иллюстрация списка сервисных команд расширенной версии LTT

В «проде» также сделана «надстройка» над исполнением в Телеграм $KE-команд в виде ещё одного вышележащего уровня – списка смысловых команд пользователя. То есть в полной версии можно включать, например, третье реле не командой REL 3 1 в чате, а командой «RouterON», более понятной пользователю и подставляющей при своём исполнении, соответствующую задаче, $KE-команду.


Список доступных команд расширенной версии LTT выдается по сервисной команде /list.

В дальнейших планах «прикрутить» к моему Laurent-терминалу продвинутый движок Телеграм-парсинга бота от Brook, позволяющего работать не только в «терминальном» режиме, подавая в чат текстовые команды, но и обеспечивающего функционирование в режиме «графического бота», создавать свои кнопки, клавиатурные наборы под конктретные пользовательские меню, что делает работу с чатом Телеграм более удобной и наглядной.

«Программное обеспечение» нашего бота «крутится» на стороне пользователя (на его роутере Микротик), но, в принципе, если нужно, можно сделать бот публичным сервисом.

Просьба в комментариях сильно «не пинать», т.к. в статье приведена простая первая версия Laurent Телеграм Терминала, преимущественно как пример возможности создания подобных ботов на основе скриптов РоутерОС.

Зато теперь обладатели Микротик могут управлять сетевыми модулями Laurent от KernelChip, с мобильных устройств, находящихся где угодно, в том числе удалённо, из чатботов Телеграм без VPN и «облаков»!
По аналогии можно сделать такое же управление через роутеры Микротик из Телеграм любыми аппаратными сетевыми ресурсами, работающими с HTTP GET или POST запросами.
Tags:
Hubs:
+3
Comments3

Articles