aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Christian Hesse <mail@eworm.de>2023-01-30 16:08:00 +0100
committerGravatar Christian Hesse <mail@eworm.de>2023-01-31 17:26:51 +0100
commit819c7294c62c84fbc59bbf16a7d9ce97b4957e57 (patch)
tree6103eedc20429f2fa21b5c98a795ee6de3646536
parentf666d2f8fffd7cefc4aa590533f47e7c7d14abd7 (diff)
introduce telegram-chatchange-90
Druvis from Mikrotik produced a video "MikroTik Telegram bot - Chat with your Router?". He shows his script to chat with a Router via Telegram bot to send it commands: https://youtu.be/KLX6j3sLRIE This script is kind of limited and has several issues... 🥴 Let's make it robust, usable, multi-device capable and just fun! 😁 (Sadly Mikrotik has a policy to not allow links in Youtube comments. Thus my comment with several hints was removed immediately. If anybody is in contact with Druvis... Please tell him about this script!)
-rw-r--r--README.md1
-rw-r--r--doc/mod/notification-telegram.md1
-rw-r--r--doc/telegram-chat.d/01-chat-specific.avifbin0 -> 31869 bytes
-rw-r--r--doc/telegram-chat.d/02-chat-all.avifbin0 -> 48099 bytes
-rw-r--r--doc/telegram-chat.md89
-rw-r--r--global-config8
-rw-r--r--global-config.changes1
-rw-r--r--global-functions3
-rw-r--r--telegram-chat120
9 files changed, 222 insertions, 1 deletions
diff --git a/README.md b/README.md
index a5a2894..42f897b 100644
--- a/README.md
+++ b/README.md
@@ -202,6 +202,7 @@ Available scripts
* [Forward received SMS](doc/sms-forward.md)
* [Import SSH keys](doc/ssh-keys-import.md)
* [Play Super Mario theme](doc/super-mario-theme.md)
+* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md)
* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md)
* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md)
* [Update tunnelbroker configuration](doc/update-tunnelbroker.md)
diff --git a/doc/mod/notification-telegram.md b/doc/mod/notification-telegram.md
index b55739a..5e6c1a0 100644
--- a/doc/mod/notification-telegram.md
+++ b/doc/mod/notification-telegram.md
@@ -66,6 +66,7 @@ methods:
See also
--------
+* [Chat with your router and send commands via Telegram bot](../telegram-chat.md)
* [Send notifications via e-mail](notification-email.md)
* [Send notifications via Matrix](notification-matrix.md)
diff --git a/doc/telegram-chat.d/01-chat-specific.avif b/doc/telegram-chat.d/01-chat-specific.avif
new file mode 100644
index 0000000..387dc3a
--- /dev/null
+++ b/doc/telegram-chat.d/01-chat-specific.avif
Binary files differ
diff --git a/doc/telegram-chat.d/02-chat-all.avif b/doc/telegram-chat.d/02-chat-all.avif
new file mode 100644
index 0000000..32fc181
--- /dev/null
+++ b/doc/telegram-chat.d/02-chat-all.avif
Binary files differ
diff --git a/doc/telegram-chat.md b/doc/telegram-chat.md
new file mode 100644
index 0000000..b245efa
--- /dev/null
+++ b/doc/telegram-chat.md
@@ -0,0 +1,89 @@
+Chat with your router and send commands via Telegram bot
+========================================================
+
+[⬅️ Go back to main README](../README.md)
+
+> ℹ️ **Info**: This script can not be used on its own but requires the base
+> installation. See [main README](../README.md) for details.
+
+Description
+-----------
+
+This script makes your device poll a Telegram bot for new messages. With
+these messages you can send commands to your device and make it run them.
+The resulting output is send back to you.
+
+Requirements and installation
+-----------------------------
+
+Just install the script and the module for notifications via Telegram:
+
+ $ScriptInstallUpdate telegram-chat,mod/notification-telegram;
+
+Then create a schedule that runs the script periodically:
+
+ /system/scheduler/add start-time=startup interval=30s name=telegram-chat on-event="/system/script/run telegram-chat;";
+
+> ⚠️ **Warning**: Make sure to keep the interval in sync when installing
+> on several devices. Differing polling intervals will result in missed
+> messages.
+
+Configuration
+-------------
+
+Make sure to configure
+[notifications via telegram](mod/notification-telegram.md) first. The
+additional configuration goes to `global-config-overlay`, these are the
+parameters:
+
+* `TelegramChatIdsTrusted`: an array with trusted chat ids or user names
+* `TelegramChatGroups`: define the groups a device should belong to
+
+Usage and invocation
+--------------------
+
+This script is capable of chatting with multiple devices. By default a
+device is passive and not acting on messages. To activate it send a message
+containing `! identity` (exclamation mark, optional space and system's
+identity). To query all dynamic ip addresses form a device named "*MikroTik*"
+send `! MikroTik`, followed by `/ip/address/print where dynamic;`.
+
+![chat to specific device](telegram-chat.d/01-chat-specific.avif)
+
+Devices can be grouped to chat with them simultaneously. The default group
+"*all*" can be activated by sending `! @all`, which will make all devices
+act on your commands.
+
+![chat to all devices](telegram-chat.d/02-chat-all.avif)
+
+Send a single exclamation mark or non-existent identity to make all
+devices passive again.
+
+Known limitations
+-----------------
+
+### Do not use numeric ids!
+
+Numeric ids are valid within a session only. Usually you can use something
+like this to print all ip addresses and remove the first one:
+
+ /ip/address/print;
+ /ip/address/remove 0;
+
+This will fail when sent in separate messages. Instead you should use basic
+scripting capabilities. Try to print what you want to act on...
+
+ /ip/address/print where interface=eth;
+
+... verify and finally remove it.
+
+ /ip/address/remove [ find where interface=eth ];
+
+See also
+--------
+
+* [Send notifications via Telegram](mod/notification-telegram.md)
+
+---
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/global-config b/global-config
index dcd7a09..4b5338a 100644
--- a/global-config
+++ b/global-config
@@ -35,6 +35,14 @@
:global TelegramChatId "";
#:global TelegramTokenId "123456:ABCDEF-GHI";
#:global TelegramChatId "12345678";
+# Using telegram-chat you have to define trusted chat ids (not group ids!)
+# or user names. Groups allow to chat with devices simultaneously.
+#:global TelegramChatIdsTrusted {
+# "12345678";
+# "example_user";
+#};
+:global TelegramChatGroups "(all)";
+#:global TelegramChatGroups "(all|home|office)";
# This is whether or not to send Telegram messages with fixed-width font.
:global TelegramFixedWidthFont true;
diff --git a/global-config.changes b/global-config.changes
index 2ae335e..94da56c 100644
--- a/global-config.changes
+++ b/global-config.changes
@@ -98,6 +98,7 @@
87="Added support for extra text (or emojis \F0\9F\9A\80) in notification tags.";
88="Added support for monitoring CPU load and available free RAM in 'check-health'.";
89="Made the warning time for 'check-certificates' configurable.";
+ 90="Chat with your router! Introduced 'telegram-chat' to chat via Telegram bot and send commands to your router.";
};
# Migration steps to be applied on script updates
diff --git a/global-functions b/global-functions
index 165ac9a..7fe3873 100644
--- a/global-functions
+++ b/global-functions
@@ -12,7 +12,7 @@
:local 0 "global-functions";
# expected configuration version
-:global ExpectedConfigVersion 89;
+:global ExpectedConfigVersion 90;
# global variables not to be changed by user
:global GlobalFunctionsReady false;
@@ -1101,6 +1101,7 @@
"pushpin"="\F0\9F\93\8C";
"scissors"="\E2\9C\82";
"sparkles"="\E2\9C\A8";
+ "speech-balloon"="\F0\9F\92\AC";
"up-arrow"="\E2\AC\86";
"warning-sign"="\E2\9A\A0";
"white-heavy-check-mark"="\E2\9C\85"
diff --git a/telegram-chat b/telegram-chat
new file mode 100644
index 0000000..9c8261e
--- /dev/null
+++ b/telegram-chat
@@ -0,0 +1,120 @@
+#!rsc by RouterOS
+# RouterOS script: telegram-chat
+# Copyright (c) 2023 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# use Telegram to chat with your Router and send commands
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/telegram-chat.md
+
+:local 0 "telegram-chat";
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:global Identity;
+:global TelegramChatActive;
+:global TelegramChatGroups;
+:global TelegramChatId;
+:global TelegramChatIdsTrusted;
+:global TelegramChatOffset;
+:global TelegramTokenId;
+
+:global CertificateAvailable;
+:global EscapeForRegEx;
+:global GetRandom20CharAlNum;
+:global IfThenElse;
+:global LogPrintExit2;
+:global MkDir;
+:global ScriptLock;
+:global SendTelegram2;
+:global SymbolForNotification;
+:global ValidateSyntax;
+:global WaitForFile;
+:global WaitFullyConnected;
+
+$ScriptLock $0;
+
+$WaitFullyConnected;
+
+:if ([ :typeof $TelegramChatOffset ] != "num") do={
+ :set TelegramChatOffset 0;
+}
+
+:if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
+ $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
+}
+
+:local JsonGetKey do={
+ :local Array [ :toarray $1 ];
+ :local Key [ :tostr $2 ];
+
+ :for I from=0 to=([ :len $Array ] - 1) do={
+ :if (($Array->$I) = $Key) do={
+ :if ($Array->($I + 1) = ":") do={
+ :return ($Array->($I + 2));
+ }
+ :return [ :pick ($Array->($I + 1)) 1 [ :len ($Array->($I + 1)) ] ];
+ }
+ }
+
+ :return false;
+}
+
+:local Data;
+:do {
+ :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
+ ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \
+ $TelegramChatOffset . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data");
+ :set Data [ :pick $Data ([ :find $Data "[" ] + 1) ([ :len $Data ] - 2) ];
+} on-error={
+ $LogPrintExit2 info $0 ("Failed getting updates from Telegram.") true;
+}
+
+:foreach Update in=[ :toarray $Data ] do={
+ :local UpdateID [ $JsonGetKey $Update "update_id" ];
+ :if ($UpdateID >= $TelegramChatOffset) do={
+ :set TelegramChatOffset ($UpdateID + 1);
+ :local Trusted false;
+ :local Message [ $JsonGetKey $Update "message" ];
+ :local From [ $JsonGetKey $Message "from" ];
+ :local FromID [ $JsonGetKey $From "id" ];
+ :local FromUserName [ $JsonGetKey $From "username" ];
+ :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={
+ :if ($FromID = $IdsTrusted || $FromUserName = $IdsTrusted) do={
+ :set Trusted true;
+ }
+ }
+
+ :if ($Trusted = true) do={
+ :local Text [ $JsonGetKey $Message "text" ];
+ :if ([ :pick $Text 0 1 ] = "!") do={
+ :if ($Text ~ ("^! *(" . [ $EscapeForRegEx $Identity ] . "|@" . $TelegramChatGroups . ")\$")) do={
+ :set TelegramChatActive true;
+ } else={
+ :set TelegramChatActive false;
+ }
+ $LogPrintExit2 info $0 ("Now " . [ $IfThenElse $TelegramChatActive "active" "passive" ] . "!") false;
+ } else={
+ :if ($TelegramChatActive = true && [ :len $Text ] > 0) do={
+ :if ([ $ValidateSyntax $Text ] = true) do={
+ :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]);
+ $MkDir "tmpfs/telegram-chat";
+ $LogPrintExit2 info $0 ("Running command: " . $Text) false;
+ :exec script=($Text . "; :execute script=\":put\" file=" . $File . ".done") file=$File;
+ :if ([ $WaitForFile ($File . ".done.txt") 200 ] = false) do={
+ $LogPrintExit2 warning $0 ("Command did not finish, possibly still running.") false;
+ }
+ :local Content [ /file/get ($File . ".txt") content ];
+ $SendTelegram2 ({ origin=$0; silent=false; \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=("Command:\n" . $Text . "\n\nOutput:\n" . $Content) });
+ /file/remove "tmpfs/telegram-chat";
+ } else={
+ $LogPrintExit2 warning $0 ("The command failed syntax validation: " . $Text) false;
+ }
+ }
+ }
+ } else={
+ $LogPrintExit2 warning $0 ("Received a message from untrusted contact '" . $FromUserName . "' (ID " . $FromID . ")!") false;
+ }
+ }
+}