--- /dev/null
+1,5c1,4
+< #!rsc by Vados
+< # RouterOS script: mod/AM-TG-Notifications
+< # Script comment: Script send Notifications in Telegram
+< # Copyright (c) 2007-2026 Vados <vados@vados.ru>
+< #
+---
+> #!rsc by RouterOS
+> # RouterOS script: mod/notification-telegram
+> # Copyright (c) 2013-2026 Christian Hesse <mail@eworm.de>
+> # https://rsc.eworm.de/COPYING.md
+9,14c8,10
+< :local ExitOK false;
+< :onerror Err {
+< :global GlobalConfReady; :global GlobalFuncReady;
+< :retry { :if ($GlobalConfReady != true || $GlobalFuncReady != true) \
+< do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50;
+< :local ScriptName [ :jobname ];
+---
+> #
+> # send notifications via Telegram
+> # https://rsc.eworm.de/doc/mod/notification-telegram.md
+26a23
+>
+30,34c27,32
+<
+< #:if ([ $IsFullyConnected ] = false) do={
+< # $LogPrint debug $0 ("System is not fully connected, not flushing.");
+< # :return false;
+< #}
+---
+>
+> :if ([ $IsFullyConnected ] = false) do={
+> $LogPrint debug $0 ("System is not fully connected, not flushing.");
+> :return false;
+> }
+>
+41a40
+>
+44a44
+>
+58a59
+>
+63,64c64,67
+< } do={:global ExitOnError; $ExitOnError $0 $Err}
+< }
+---
+> } do={
+> :global ExitOnError; $ExitOnError $0 $Err;
+> } }
+>
+68c71,72
+< # :global CertificateAvailable;
+---
+>
+> :global CertificateAvailable;
+70,73c74,79
+< # :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={
+< # $LogPrint warning $0 ("Downloading required certificate failed.");
+< # :return false;
+< # }
+---
+>
+> :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={
+> $LogPrint warning $0 ("Downloading required certificate failed.");
+> :return false;
+> }
+>
+82a89
+>
+84a92
+>
+88a97
+>
+94,95c103,106
+< } do={:global ExitOnError; $ExitOnError $0 $Err}
+< }
+---
+> } do={
+> :global ExitOnError; $ExitOnError $0 $Err;
+> } }
+>
+98a110
+>
+108a121
+>
+116a130
+>
+120a135
+>
+122a138
+>
+133c149,153
+< :if ($Mode = "body") do={:return ("```\n" . $Text . "\n```")}
+---
+>
+> :if ($Mode = "body") do={
+> :return ("```\n" . $Text . "\n```");
+> }
+>
+135a156
+>
+142,143c163,171
+< :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={:return false}
+< :if ([ :typeof $TelegramMessageIDs ] = "nothing") do={:set TelegramMessageIDs ({})}
+---
+>
+> :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
+> :return false;
+> }
+>
+> :if ([ :typeof $TelegramMessageIDs ] = "nothing") do={
+> :set TelegramMessageIDs ({});
+> }
+>
+154c182,184
+< } else={:set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ])}
+---
+> } else={
+> :set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]);
+> }
+164a195
+>
+179c210,213
+< :if ([ :typeof $TelegramQueue ] = "nothing") do={:set TelegramQueue ({})}
+---
+>
+> :if ([ :typeof $TelegramQueue ] = "nothing") do={
+> :set TelegramQueue ({});
+> }
+190a225
+>
+193a229
+>
+196a233
+>
+199a237
+>
+201,202c239,242
+< } do={:global ExitOnError; $ExitOnError $0 $Err}
+< }
+---
+> } do={
+> :global ExitOnError; $ExitOnError $0 $Err;
+> } }
+>
+205a246
+>
+206a248
+>
--- /dev/null
+#!rsc by RouterOS
+# RouterOS script: mod/notification-telegram
+# Copyright (c) 2013-2026 Christian Hesse <mail@eworm.de>
+# https://rsc.eworm.de/COPYING.md
+#
+# requires RouterOS, version=7.19
+# requires device-mode, fetch, scheduler
+#
+# send notifications via Telegram
+# https://rsc.eworm.de/doc/mod/notification-telegram.md
+
+:global FlushTelegramQueue;
+:global GetTelegramChatId;
+:global NotificationFunctions;
+:global PurgeTelegramQueue;
+:global SendTelegram;
+:global SendTelegram2;
+
+# flush telegram queue
+:set FlushTelegramQueue do={ :onerror Err {
+ :global TelegramQueue;
+ :global TelegramMessageIDs;
+
+ :global CertificateAvailable;
+ :global IsFullyConnected;
+ :global LogPrint;
+
+ :if ([ $IsFullyConnected ] = false) do={
+ $LogPrint debug $0 ("System is not fully connected, not flushing.");
+ :return false;
+ }
+
+ :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={
+ $LogPrint warning $0 ("Downloading required certificate failed.");
+ :return false;
+ }
+
+ :local AllDone true;
+ :local QueueLen [ :len $TelegramQueue ];
+
+ :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] > 0 && $QueueLen = 0) do={
+ $LogPrint warning $0 ("Flushing Telegram messages from scheduler, but queue is empty.");
+ }
+
+ :foreach Id,Message in=$TelegramQueue do={
+ :if ([ :typeof $Message ] = "array" ) do={
+ :onerror Err {
+ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \
+ ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
+ http-data=($Message->"http-data") as-value ]->"data");
+ :set ($TelegramQueue->$Id);
+ :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1;
+ } do={
+ $LogPrint debug $0 ("Sending queued Telegram message failed: " . $Err);
+ :set AllDone false;
+ }
+ }
+ }
+
+ :if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={
+ /system/scheduler/remove [ find where name="_FlushTelegramQueue" ];
+ :set TelegramQueue;
+ }
+} do={
+ :global ExitOnError; $ExitOnError $0 $Err;
+} }
+
+# get the chat id
+:set GetTelegramChatId do={ :onerror Err {
+ :global TelegramTokenId;
+
+ :global CertificateAvailable;
+ :global LogPrint;
+
+ :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={
+ $LogPrint warning $0 ("Downloading required certificate failed.");
+ :return false;
+ }
+
+ :local Data;
+ :onerror Err {
+ :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
+ ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=0" . \
+ "&allowed_updates=%5B%22message%22%5D") as-value ]->"data");
+ } do={
+ $LogPrint warning $0 ("Fetching data failed: " . $Err);
+ :return false;
+ }
+
+ :local JSON [ :deserialize from=json value=$Data ];
+ :local Count [ :len ($JSON->"result") ];
+
+ :if ($Count = 0) do={
+ $LogPrint info $0 ("No message received.");
+ :return false;
+ }
+
+ :local Message ($JSON->"result"->($Count - 1)->"message");
+ $LogPrint info $0 ("The chat id is: " . ($Message->"chat"->"id"));
+ :if (($Message->"is_topic_message") = true) do={
+ $LogPrint info $0 ("The thread id is: " . ($Message->"message_thread_id"));
+ }
+} do={
+ :global ExitOnError; $ExitOnError $0 $Err;
+} }
+
+# send notification via telegram - expects one array argument
+:set ($NotificationFunctions->"telegram") do={
+ :local Notification $1;
+
+ :global Identity;
+ :global IdentityExtra;
+ :global TelegramChatId;
+ :global TelegramChatIdOverride;
+ :global TelegramMessageIDs;
+ :global TelegramQueue;
+ :global TelegramThreadId;
+ :global TelegramThreadIdOverride;
+ :global TelegramTokenId;
+ :global TelegramTokenIdOverride;
+
+ :global CertificateAvailable;
+ :global CharacterReplace;
+ :global EitherOr;
+ :global IfThenElse;
+ :global LogPrint;
+ :global ProtocolStrip;
+ :global SymbolForNotification;
+ :global UrlEncode;
+
+ :local EscapeMD do={
+ :local Text [ :tostr $1 ];
+ :local Mode [ :tostr $2 ];
+ :local Excl [ :tostr $3 ];
+
+ :global CharacterReplace;
+ :global IfThenElse;
+
+ :local Chars {
+ "body"={ "\\"; "`" };
+ "plain"={ "_"; "*"; "["; "]"; "("; ")"; "~"; "`"; ">";
+ "#"; "+"; "-"; "="; "|"; "{"; "}"; "."; "!" };
+ }
+ :foreach Char in=($Chars->$Mode) do={
+ :if ([ :typeof [ :find $Excl $Char ] ] = "nil") do={
+ :set Text [ $CharacterReplace $Text $Char ("\\" . $Char) ];
+ }
+ }
+
+ :if ($Mode = "body") do={
+ :return ("```\n" . $Text . "\n```");
+ }
+
+ :return $Text;
+ }
+
+ :local ChatId [ $EitherOr ($Notification->"chatid") \
+ [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ];
+ :local ThreadId [ $EitherOr ($Notification->"threadid") \
+ [ $EitherOr ($TelegramThreadIdOverride->($Notification->"origin")) \
+ [ $IfThenElse ([ :len ($TelegramChatIdOverride->($Notification->"origin")) ] = 0) $TelegramThreadId ] ] ];
+ :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ];
+
+ :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
+ :return false;
+ }
+
+ :if ([ :typeof $TelegramMessageIDs ] = "nothing") do={
+ :set TelegramMessageIDs ({});
+ }
+
+ :local Truncated false;
+ :local Text ("*__" . [ $EscapeMD ("[" . $IdentityExtra . $Identity . "] " . \
+ ($Notification->"subject")) "plain" ] . "__*\n\n");
+ :local LenSubject [ :len $Text ];
+ :local LenMessage [ :len ($Notification->"message") ];
+ :local LenLink ([ :len ($Notification->"link") ] * 2);
+ :local LenSum ($LenSubject + $LenMessage + $LenLink);
+ :if ($LenSum > 3968) do={
+ :set Text ($Text . [ $EscapeMD ([ :pick ($Notification->"message") 0 (3840 - $LenSubject - $LenLink) ] . "...") "body" ]);
+ :set Truncated true;
+ } else={
+ :set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]);
+ }
+ :if ($LenLink > 0) do={
+ :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . \
+ "[" . [ $EscapeMD [ $ProtocolStrip ($Notification->"link") ] "plain" ] . "]" . \
+ "(" . [ $EscapeMD ($Notification->"link") "plain" ] . ")");
+ }
+ :if ($Truncated = true) do={
+ :set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \
+ [ $EscapeMD ("The message was too long and has been truncated, cut off _" . \
+ (($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%_!") "plain" "_" ]);
+ }
+
+ :local HTTPData ("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \
+ "&reply_to_message_id=" . ($Notification->"replyto") . "&message_thread_id=" . $ThreadId . \
+ "&disable_web_page_preview=true&parse_mode=MarkdownV2");
+ :onerror Err {
+ :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" "fetch" ] = false) do={
+ $LogPrint warning $0 ("Downloading required certificate failed.");
+ :error false;
+ }
+ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \
+ ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
+ http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) as-value ]->"data");
+ :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1;
+ } do={
+ $LogPrint info $0 ("Failed sending Telegram notification: " . $Err . " - Queuing...");
+
+ :if ([ :typeof $TelegramQueue ] = "nothing") do={
+ :set TelegramQueue ({});
+ }
+ :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \
+ [ $EscapeMD ("This message was queued since _" . [ /system/clock/get date ] . \
+ " " . [ /system/clock/get time ] . "_ and may be obsolete.") "plain" "_" ]);
+ :set ($TelegramQueue->[ :len $TelegramQueue ]) { tokenid=$TokenId;
+ http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) };
+ :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] = 0) do={
+ /system/scheduler/add name="_FlushTelegramQueue" interval=1m start-time=startup \
+ on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;");
+ }
+ }
+}
+
+# purge the Telegram queue
+:set PurgeTelegramQueue do={
+ :global TelegramQueue;
+
+ /system/scheduler/remove [ find where name="_FlushTelegramQueue" ];
+ :set TelegramQueue;
+}
+
+# send notification via telegram - expects at least two string arguments
+:set SendTelegram do={ :onerror Err {
+ :global SendTelegram2;
+
+ $SendTelegram2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 });
+} do={
+ :global ExitOnError; $ExitOnError $0 $Err;
+} }
+
+# send notification via telegram - expects one array argument
+:set SendTelegram2 do={
+ :local Notification $1;
+
+ :global NotificationFunctions;
+
+ ($NotificationFunctions->"telegram") ("\$NotificationFunctions->\"telegram\"") $Notification;
+}