Удаленный запуск скриптов через Телеграм

Здесь выкладываем скрипты
Правила форума
Уважаемые Пользователи форума, обратите внимание!
Ни при каких обстоятельствах, Администрация форума, не несёт ответственности за какой-либо, прямой или косвенный, ущерб причиненный в результате использования материалов, взятых на этом Сайте или на любом другом сайте, на который имеется гиперссылка с данного Сайта. Возникновение неисправностей, потерю программ или данных в Ваших устройствах, даже если Администрация будет явно поставлена в известность о возможности такого ущерба.
Просим Вас быть предельно осторожными и внимательными, в использовании материалов раздела. Учитывать не только Ваши пожелания, но и границы возможностей вашего оборудования.
Ответить
Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

Спасибо большое за Ваш труд ! Очень хороший и нужный скрипт.
Для автора - есть универсальный JSON-парсер от Чупакабры, который прекрасно парсит данные из JSON в многомерные массивы.

https://habr.com/ru/post/337978/
https://github.com/Winand/mikrotik-json-parser

Я использую его постоянно, для распарсивания ответов от различных сайтов или аппаратуры.

Вы могли бы использовать его для парсинга в Вашем скрипте.

А вообще мне очень нравится Ваш скрипт. Написан профессионально. Жму Вам руку и снимаю шляпу ! Так держать, будем ждать Ваших новых скриптов, полезных всем.


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

Вопрос такой: если ставлю исполнение Вашего скрипта-парсера в Планировщике скажем не раз в минуту, а раз в 30 секунд, он начинает выполнять скрипт не один а два или более раз подряд. Можно как то подправить этот косяк ?


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

И ещё вопрос: можете ли Вы доработать скрипт, чтобы он мог исполнять не только процедуры (запуская одноимённые скрипты-команды из реппозитория роутера), но и функции из окружения переменных с параметрами. То есть команды-функции пользователя типа [$Function var1, var2] ?


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
Аватара пользователя
drpioneer
Сообщения: 142
Зарегистрирован: 30 май 2013, 10:20

Приветствую!
Sertik писал(а): 30 сен 2021, 12:04 Для автора - есть универсальный JSON-парсер от Чупакабры, который прекрасно парсит данные из JSON в многомерные массивы.
Я использую его постоянно, для распарсивания ответов от различных сайтов или аппаратуры.
Вы могли бы использовать его для парсинга в Вашем скрипте.

.....

Вопрос такой: если ставлю исполнение Вашего скрипта-парсера в Планировщике скажем не раз в минуту, а раз в 30 секунд, он начинает выполнять скрипт не один а два или более раз подряд. Можно как то подправить этот косяк ?

.....

И ещё вопрос: можете ли Вы доработать скрипт, чтобы он мог исполнять не только процедуры (запуская одноимённые скрипты-команды из реппозитория роутера), но и функции из окружения переменных с параметрами. То есть команды-функции пользователя типа [$Function var1, var2] ?
1. Встречный вопрос по универсальному парсеру от Чупакабры: какие преимущества от его использования по сравнению с тем, что уже имеется в скрипте?

2. По меньшему периоду времени исполнения скрипта: специально для вас сделал такую возможность и устранил проблему с повторным исполнением скрипта.

Код: Выделить всё


# Script remote activation script via Telegram by drPioneer
# https://forummikrotik.ru/viewtopic.php?p=78085
# tested on ROS 6.48.3
# updated 2021/10/01

:global oldMsg;

:do {
    :local botID    "botXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
    :local myChatID "-XXXXXXXXX";
    
    # Telegram messenger response parsing function by Dimonw
    # https://habr.com/ru/post/482802/
    :local parse do={
        :local variaMod ("\"".$variable);
        :local startLoc ([:find $content $variaMod -1] + [:len $variaMod] + 2);
        :local commaLoc ([:find $content "," $startLoc]);
        :local brakeLoc ([:find $content "}" $startLoc]);
        :local endLoc $commaLoc;
        :local startSymbol [:pick $content $startLoc]; 
        :if ($brakeLoc != 0 and ($commaLoc = 0 or $brakeLoc < $commaLoc)) do={ :set endLoc $brakeLoc; };
        :if ($startSymbol = "{") do={ :set endLoc ($brakeLoc + 1); };
        :if ($quotas = true) do={
            :set startLoc ($startLoc + 1);
            :set endLoc   ($endLoc - 1);
        }
        :if ($endLoc < $startLoc) do={ :set endLoc ($startLoc + 1); };
        :local message [:pick $content $startLoc $endLoc];
        :return $message;
    }

    # UNIX time conversion function to normal view by Pepelxl
    # https://forummikrotik.ru/viewtopic.php?t=11636
    :local unixTime do={
        :local dateY  ($timeStamp / 31436000 + 1970);
        :local dateM (($dateY - 1969) / 4);
        :local dateD (($timeStamp / 86400 - $dateM) % 365);
        :local days   [:toarray (31,28,31,30,31,30,31,31,30,31,30,31)];
        :local months [:toarray ("jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec")];
        :if ($dateY % 4 = 0) do={ :set ($days->1) 29; }
        do {
            :for i from=0 to=11 do={ 
                :if (($days->$i) > $dateD) do={ 
                    :set $dateM ($i); 
                    :set $dateD ($dateD + 1); 
                    :break;
                } else={ :set $dateD ($dateD - ($days->$i)); } 
            }
        } on-error={}
        :local timeS ($timeStamp % 86400);
        :local timeH ($timeS / 3600);
        :local timeM ($timeS % 3600 / 60);
        :set  $timeS ($timeS - $timeH * 3600 - $timeM * 60);
        :local normalTime (($months->$dateM)."/");
        :if ($dateD < 10) do={ :set $normalTime ($normalTime."0".[:tostr $dateD]."/"); } else={ :set $normalTime ($normalTime.[:tostr $dateD]."/"); }
        :set $normalTime ($normalTime.[:tostr $dateY]." ");   
        :if ($timeH < 10) do={ :set $normalTime ($normalTime."0".[:tostr $timeH].":"); } else={ :set $normalTime ($normalTime.[:tostr $timeH].":"); }
        :if ($timeM < 10) do={ :set $normalTime ($normalTime."0".[:tostr $timeM].":"); } else={ :set $normalTime ($normalTime.[:tostr $timeM].":"); }
        :if ($timeS < 10) do={ :set $normalTime ($normalTime."0".[:tostr $timeS]); }     else={ :set $normalTime ($normalTime.[:tostr $timeS]); }
        :put ([:totime $normalTime]);
        :return $normalTime;
    }
    
    # Main body of the script
    :local nameID   [ /system identity get name; ];
    :local dateNow  [ /system clock    get date; ];
    :local timeNow  [ /system clock    get time; ];
    :local httpResponse [ /tool fetch url="https://api.telegram.org/$botID/getUpdates\?offset=-1&limit=1&allowed_updates=message&timeout=60" as-value output=user; ];
    :local content ($httpResponse->"data");
    :if ([:len $content] > 30) do={
        :local msgId    [$parse content=$content variable="update_id"];
        :local message  [$parse content=$content variable="text" quotas=true];
        :set   message ([:pick $message ([:find $message "/"] + 1) ([:len $message])]);
        :local chat     [$parse content=$content variable="chat"];
        :local chatId   [$parse content=$chat    variable="id"];
        :local userName [$parse content=$content variable="username"];
        :local dateTime [$parse content=$content variable="date"];
        :set   dateTime ($dateTime + [ /system clock get gmt-offset; ]);
        :local normTime [$unixTime timeStamp=$dateTime];
        :put ("Current time on router:\t $dateNow $timeNow");
        :put ("Last message from $userName: time=$normTime, id=$msgId, text=$message.");
        :if (($normTime ~ $dateNow) && ([:totime [:pick $normTime ([:find $normTime " "] + 1) ([:find $normTime " "] + 9)]] > ($timeNow - 00:00:30))) do={
            :put ("Right time to activation script.");
            :if ($message != $oldMsg) do={
                :if (($chatId = $myChatID) && ([/system script find name=$message] != "")) do={
                    :put ("Telegram user $userName activated script '$message'.");
                    :log warning ("Telegram user $userName activated script '$message'.");                 
                    /system script run $message;
                    :set oldMsg ($message);
                    /tool fetch keep-result=no url="https://api.telegram.org/$botID/sendmessage\?chat_id=$chatId&text=$nameID: '$message' command accepted.";
                } else={ :put ("Not all conditions activation script are met."); }
            } else={ :put ("Command has already been executed."); }
        } else={ :put ("Bad time to activation script."); :set oldMsg (""); }
    }
} on-error={ :put ("Script error: 1 - message could have been sent a long time ago. 2 - check whether 'botID' & 'myChatID' variables are specified correctly."); }


Проверяйте...

3. По доработке скрипта на предмет исполнения команд-функций: к моему стыду я не сильно знаком с этой темой, поэтому не до конца понимаю, что нужно сделать... Накидайте ссылок для погружения в этот вопрос... А там посмотрим...

Заранее спасибо.


Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

К этому скрипту парсер Чупакабры ничего не добавит. Я прислал его на будущее. Когда нужно парсить JSON не стоит уже изобретать велосипед и выискивать информацию командами :find, вырезая куски :peek и т.д... Если, конечно нужно найти в JSON ответе один - два параметра, можно обойтись и своими силами ... А когда JSON возвращает много нужных данных - парсер Чупакабры незаменимая вещь. Ни о чём не надо думать - подсовываешь парсеру JSON, получаешь отпарсенный ключевой массив данных.
устранил проблему с повторным исполнением скрипта
Спасибо, всем пригодиться. Ткните носом где эта проблема была в тексте скрипта (для повышения образованности).

По поводу функций. Функция отличается от процедуры тем, что может иметь аргументы и может возвращать результат своей работы. Смотрите. Ваш парсер отлично справляется с исполнением скриптов чьи имена совпадают с пересланными на исполнение. Например посылаем /reboot. Если в репозитории роутера есть скрипт с таким именем - парсер его найдет и выполнит. А как быть, если есть функция ? У которой может быть N-ное число параметров и которая может вернуть результат своей работы в точку из которой она вызвана ?
Общая запись вызова функции, заранее размещенной в репозитории роутера такая [$FuncName var1=val1, var2=var2 ...].
Параметры функций (var) могут быть именованными и не содержать имен, тогда имеет значение их порядок 1,2,3 и т.д...
Для использования функции она должна быть объявлена переменной глобал, а потом вызвана.

:global $FuncName; [$FuncName var1=val1, var2=var2 ...]

Функция может вернуть результат своей работы, с помощью команды :return, в виде числа, строки, массива и т.д... то есть данных любого типа.

:global $FuncName; local answer [$FuncName var1=val1, var2=var2 ...]

В переменной answer будет результат.

Да ещё и надо понимать, что функция может быть аргументом другой функции, а также в РоутерОс поддерживается рекурсивный вызов функций ...

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


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
Аватара пользователя
drpioneer
Сообщения: 142
Зарегистрирован: 30 май 2013, 10:20

Sertik писал(а): 04 окт 2021, 14:00
устранил проблему с повторным исполнением скрипта
Спасибо, всем пригодиться. Ткните носом где эта проблема была в тексте скрипта (для повышения образованности).
Очень кратко: введена глобальная переменная oldMsg, которая хранит в себе последнюю выполненную скриптом команду. Теперь при повторном запуске скрипта, перед исполнением команды производится проверка oldMsg и принятие решения об её исполнении или неисполнении. Как-то так...


Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

Большое спасибо. Теперь можно запускать скрипт чаще чем раз в минуту. Пробовал раз в 15 секунд - работает безупречно. ! Повторных исполнений не было.


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
aleksey.yakushev
Сообщения: 3
Зарегистрирован: 01 апр 2021, 15:02

Здравствуйте!
ув. drpioneer,  перестал работать скрипт ((

Всё началось с вечера 19 октября, хотя утром  отрабатывал нормально.
При запуске скрипта из терминала выводится 2022 г. (см. скрин)
Можно поправить?

Заранее спасибо!

Изображение


Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

Приветствую и подтверждаю !

Скрипт действительно перестал работать :ny_tik:. Может в Телеграмм что-то изменили ?
Очень нужный был и отлично работающий скрипт ... Поправьте, пожалуйста, ув. Dr.Pioneer.

P/S При исполнении из терминала мне также выдаёт:

Last message from "result":[{"update_id":203788784: time=oct/20/2022 10:22:52, id=203788784, text=example.
Bad time to activation script.


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
Sertik
Сообщения: 1598
Зарегистрирован: 15 сен 2017, 09:03

Пришлось самому копать и поправить Ваш скрипт. Не долго думая вставил туда подправленную локальную функцию FuncUnixTimeToFormat, которую когда то взял у pepelxl с его разрешения и переделал под себя. Теперь весь основной скрипт удаленного запуска скриптов через Telegram работает правильно вроде как. Проверяйте ...

Код: Выделить всё

# Script remote activation script via Telegram by drPioneer
# https://forummikrotik.ru/viewtopic.php?p=78085
# tested on ROS 6.48
# updated 2021/04/05

:do {
    :global botID;
    :global myChatID;
    
    # Telegram messenger response parsing function by Dimonw
    # https://habr.com/ru/post/482802/
    :local parse do={
        :local variaMod ("\"".$variable);
        :local startLoc ([:find $content $variaMod -1] + [:len $variaMod] + 2);
        :local commaLoc ([:find $content "," $startLoc]);
        :local brakeLoc ([:find $content "}" $startLoc]);
        :local endLoc $commaLoc;
        :local startSymbol [:pick $content $startLoc]; 
        :if ($brakeLoc != 0 and ($commaLoc = 0 or $brakeLoc < $commaLoc)) do={ :set endLoc $brakeLoc; };
        :if ($startSymbol = "{") do={ :set endLoc ($brakeLoc + 1); };
        :if ($quotas = true) do={
            :set startLoc ($startLoc + 1);
            :set endLoc   ($endLoc - 1);
        }
        :if ($endLoc < $startLoc) do={ :set endLoc ($startLoc + 1); };
        :local message [:pick $content $startLoc $endLoc];
        :return $message;
    }


# год не ограничен столетием
# usial [$FuncUnixTimeToFormat "timeStamp", "type"]

# type:

# "unspecified" - month/dd/yyyy <only> ((Mikrotik sheduller format)
# 1 - yyyy/mm/dd hh:mm:ss
# 2 - dd:mm:yyyy hh:mm:ss
# 3 - dd month yyy hh mm ss
# 4 - yyyy month dd hh mm ss
#5 - month/dd/yyyy-hh:mm:ss (Mikrotik sheduller format)

:local FuncUnixTimeToFormat do={
:local decodedLine ""
:local timeStamp $1

:local timeS ($timeStamp % 86400)
:local timeH ($timeS / 3600)
:local timeM ($timeS % 3600 / 60)
:set $timeS ($timeS - $timeH * 3600 - $timeM * 60)
:local dateD ($timeStamp / 86400)
:local dateM 2
:local dateY 1970
:local leap false
:while (($dateD / 365) > 0) do={
:set $dateD ($dateD - 365)
:set $dateY ($dateY + 1)
:set $dateM ($dateM + 1) 
:if ($dateM = 4) do={:set $dateM 0
:if (($dateY % 400 = 0) or ($dateY % 100 != 0)) do={:set $leap true
:set $dateD ($dateD - 1)}} else={:set $leap false}}
:local months [:toarray (0,31,28,31,30,31,30,31,31,30,31,30,31)]
:if (leap) do={:set $dateD ($dateD + 1); :set ($months->2) 29}
do {
:for i from=1 to=12 do={:if (($months->$i) >= $dateD) do={:set $dateM $i; :set $dateD ($dateD + 1); break;} else={:set $dateD ($dateD - ($months->$i))}}
} on-error={}
:local tmod
:if ([:len $2]!=0) do={:set $tmod $2} else={:set $tmod (:nothing)}
:local s "/"
:local nf true
:local mstr {"jan";"feb";"mar";"apr";"may";"jun";"jul";"aug";"sep";"oct";"nov";"dec"}
:local strY [:tostr $dateY]
:local strMn
:local strD
:local strH
:local strM
:local strS
:if ($nf) do={
:if ($dateM > 9) do={:set $strMn [:tostr $dateM]} else={:set $strMn ("0".[:tostr $dateM])}
:if ($dateD > 9) do={:set $strD [:tostr $dateD]} else={:set $strD ("0".[:tostr $dateD])}
:if ($timeH > 9) do={:set $strH [:tostr $timeH]} else={:set $strH ("0".[:tostr $timeH])}
:if ($timeM > 9) do={:set $strM [:tostr $timeM]} else={:set $strM ("0".[:tostr $timeM])}
:if ($timeS > 9) do={:set $strS [:tostr $timeS]} else={:set $strS ("0".[:tostr $timeS])}
} else={
:set strMn [:tostr $dateM]
:set strD [:tostr $dateD]
:set strH [:tostr $timeH]
:set strM [:tostr $timeM]
:set strS [:tostr $timeS]
}
do {
:if ([:len $tmod]=0) do={:local mt ($mstr->($dateM - 1)); :set $decodedLine ("$mt/"."$strD/"."$strY"); break;}
:if ($tmod = 1) do={:set $decodedLine "$strY$s$strMn$s$strD $strH:$strM:$strS"; break;}
:if ($tmod = 2) do={:set $decodedLine "$strD$s$strMn$s$strY $strH:$strM:$strS"; break;}
:if ($tmod = 3) do={:set $decodedLine ("$strD ".($mstr->($dateM - 1))." $strY $strH:$strM:$strS"); break;}
:if ($tmod = 4) do={:set $decodedLine ("$strY ".($mstr->($dateM - 1))." $strD $strH:$strM:$strS"); break;}
:if ($tmod = 5) do={:local m ($mstr->($dateM - 1)); :set $decodedLine ("$m/"."$strD/"."$strY"." $strH:$strM:$strS"); break;}

} on-error={}
:return $decodedLine;
}


    
    # Main body of the script
    :local nameID   [ /system identity get name; ];
    :local dateNow  [ /system clock    get date; ];
    :local timeNow  [ /system clock    get time; ];
    :local httpResponse [ /tool fetch url="https://api.telegram.org/$botID/getUpdates\?offset=-1&limit=1&allowed_updates=message&timeout=60" as-value output=user; ];
    :local content ($httpResponse->"data");
    :if ([:len $content] > 30) do={
        :local msgId    [$parse content=$content variable="update_id"];
        :local message  [$parse content=$content variable="text" quotas=true];
        :set   message ([:pick $message ([:find $message "/"] + 1) ([:len $message])]);
        :local chat     [$parse content=$content variable="chat"];
        :local chatId   [$parse content=$chat    variable="id"];
        :local userName [$parse content=$content variable="username"];
        :local dateTime [$parse content=$content variable="date"];
        :set   dateTime ($dateTime + [ /system clock get gmt-offset; ]);
        :local normTime [$FuncUnixTimeToFormat $dateTime 5];
:log warning $normTime
        :put ("Current time on router:\t $dateNow $timeNow");
        :put ("Last message from $userName: time=$normTime, id=$msgId, text=$message.");
        :if (($normTime ~ $dateNow) && ([:totime [:pick $normTime ([:find $normTime " "] + 1) ([:find $normTime " "] + 9)]] > ($timeNow - 00:01:00))) do={
            :put ("Right time to activation script.");
            :if (($chatId = $myChatID) && ([/system script find name=$message] != "")) do={
                :put ("Telegram user $userName activated script '$message'.");
                :log warning ("Telegram user $userName activated script '$message'.");                 
                /system script run $message;
                /tool fetch keep-result=no url="https://api.telegram.org/$botID/sendmessage\?chat_id=$chatId&text=$nameID: '$message' command accepted.";
            } else={ :put ("Not all conditions activation script are met."); }
        } else={ :put ("Bad time to activation script."); }
    }
} on-error={ :put ("Script error: 1 - message could have been sent a long time ago. 2 - check whether 'botID' & 'myChatID' variables are specified correctly."); }


фрагменты скриптов, готовые работы, статьи, полезные приемы, ссылки
viewtopic.php?f=14&t=13947
Ответить