Compare commits

..

47 commits

Author SHA1 Message Date
Christian Hesse
ce39b79f69 capsman-download-packages: fix parameter for $RmFile
The function can not handle ids, we have to pass a name instead.
2025-03-13 11:50:38 +01:00
Christian Hesse
20bf609c44 check-routeros-update: fix condition for license check
Turns out that `next-renewal-at` is moved forward when renewal failed,
so it never matches the criteria. Just start complaining three weeks
before deadline.
2025-03-13 10:51:39 +01:00
Christian Hesse
b63e0fcb2f netwatch-notify: check matching address type only 2025-03-12 11:26:22 +01:00
Christian Hesse
1555426687 netwatch-notify: increase the timeout even more
This interacts with the number of addresses in the address-list. Having
a lot of addresses there (for exemple from script 'fw-addr-lists' 😜)
makes the 'find' take longer. We have to make sure that 'find' succeeds
before the address times out.

As this does not hurt... Let's just bump to 10 seconds to be safe.
2025-03-12 11:18:18 +01:00
Christian Hesse
97b99316b2 netwatch-notify: increase timeout...
... as a timeout of one second expires immediately. 🤨
2025-03-12 10:31:11 +01:00
Christian Hesse
788400c458 fw-addr-lists: raw.githubusercontent.com requires 'USERTrust RSA Certification Authority' now 2025-03-11 15:51:25 +01:00
Christian Hesse
eb59dd21ca check-routeros-update: check perpetual license...
... as these have to be renewed and can expire.
2025-03-11 15:51:25 +01:00
Christian Hesse
79a4b369cb Merge branch 'fw-addr-lists' into next 2025-03-11 15:51:25 +01:00
Christian Hesse
f0e6cbcfe1 fw-addr-lists: get branch from calculated checksum
The addresses were spread very uneven before.

Let's calculate a checksum, and take the first two characters of that.
The addresses are now spread evenly on 256 branches (0x00 to 0xff).
2025-03-11 15:51:10 +01:00
Christian Hesse
d71ea804b0 fw-addr-lists: two characters for branch
Using one character for IPv4 is ok (1 to 9), but IPv6 global unicase
(2000::/3) results in just two different characters (2 and 3).

So let's use first two characters...
2025-03-11 14:20:50 +01:00
Christian Hesse
e148df9e57 fw-addr-lists: put addresses into "branches"...
... effectively adding another layer and some complexity, but:
The addresses are sorted inside the array, and sorting less addresses in
a branch saves a lot of processing power. So this is a lot faster now...
2025-03-11 14:20:44 +01:00
Christian Hesse
2f55bfaf00 fw-addr-lists: strip cidr for host addresses
This makes sure the addresses match later when we read them from
address-list for renew.
2025-03-11 14:19:10 +01:00
Christian Hesse
ea6de35699 fw-addr-lists: do not clean up
Cleanup is important on renew (so the script does not attempt to re-add),
but we do not care here.
2025-03-11 14:13:55 +01:00
Christian Hesse
fb343c99e3 fw-addr-lists: put timeout into variable 2025-03-06 22:59:31 +01:00
Christian Hesse
7be26a0712 DEBUG: add info on $LogPrintVerbose 2025-03-06 15:59:44 +01:00
Christian Hesse
6d718ec987 fw-addr-lists: use $LogPrintVerbose ...
... to reduce debug output and speed up execution.
2025-03-06 15:59:44 +01:00
Christian Hesse
e341e1c30c global-functions: introduce $LogPrintVerbose ...
... which is a declared function, but has no code, intentionally. It can be
called as a no-op by default.

If you want this output set the function to be the same as $LogPrint:

    :set LogPrintVerbose $LogPrint;
2025-03-06 15:59:11 +01:00
Christian Hesse
b43b1b3955 Merge branch 'checksums' into next 2025-03-06 10:43:15 +01:00
Christian Hesse
1b46a5fd9b global-functions: $ScriptInstallUpdate: checksum only for same source
So ignore if script is fetched from different base or with different
suffix.
2025-03-06 10:43:13 +01:00
Christian Hesse
b13360e4b8 global-functions: $ScriptInstallUpdate: simplify check
This one should suffice...
2025-03-06 10:42:52 +01:00
Christian Hesse
c9de6d8579 global-functions: $ScriptInstallUpdate: put checksum into variable 2025-03-06 10:42:27 +01:00
Christian Hesse
10374afc18 global-functions: $ScriptInstallUpdate: support checksums for CRLF scripts 2025-03-06 10:42:00 +01:00
Christian Hesse
0c1d96f89d global-functions: $ScriptInstallUpdate: get and compare checksums
The file 'checksums.json' is generated when deploying to my web
server... This should speed up the update a lot as it reduces downloads
to a minimum. 🎉😁
2025-03-06 10:41:28 +01:00
Christian Hesse
3ccaafd1b3 global-functions: $ScriptInstallUpdate: move code into block 2025-03-05 01:15:22 +01:00
Christian Hesse
469f783a92 ipv6-update: check for availability of both variables 2025-03-03 09:12:43 +01:00
Christian Hesse
33c02e0609 ipv6-update: ignore if prefix is no longer valid 2025-03-03 09:10:54 +01:00
Christian Hesse
6331505dbe Merge branch 'quote-file-name' into next
This is required with RouterOS 7.18 now...

Well, probably the change was introduced with one of the
beta versions...
2025-02-27 10:52:52 +01:00
Christian Hesse
0c4fb42616 mod/notification-telegram: $GetTelegramChatId: give thead id...
... if message was sent to group's topic.
2025-02-27 10:52:52 +01:00
Christian Hesse
f5189b8bd7 INITIAL-COMMANDS: quote the certificate file name 2025-02-27 10:52:52 +01:00
Christian Hesse
e2fe653035 mod/notification-telegram: $GetTelegramChatId: use last message 2025-02-27 10:52:52 +01:00
Christian Hesse
b11be59b08 README: quote the certificate file name 2025-02-27 10:52:52 +01:00
Christian Hesse
24de060904 Merge branch 'check-certificates' into next 2025-02-27 10:52:19 +01:00
Christian Hesse
14195c51ca check-certificates: try PKCS#12 before PEM...
... as that is more likely to have a private key.

Is that true? 🤨
2025-02-26 18:25:58 +01:00
Christian Hesse
e833dfcf25 check-certificates: simplify return from function...
... and also break earch on success.
2025-02-26 18:05:32 +01:00
Christian Hesse
512c54bd59 check-certificates: ... and even more 2025-02-26 18:05:32 +01:00
Christian Hesse
3d40b4419d check-certificates: add more debug output 2025-02-26 18:03:45 +01:00
Christian Hesse
a6d4e7e82c check-certificates: drop dot from type...
... and add it in file name.
2025-02-26 18:03:17 +01:00
Christian Hesse
f6c2225f68 check-certificates: catch and ignore import error
Hmm... 🤨 When was that runtime error introduced? I *think* it
worked before.
2025-02-26 13:57:51 +01:00
Christian Hesse
53b13b295a mod/notification-telegram: introduce $GetTelegramChatId 2025-02-25 22:37:30 +01:00
Christian Hesse
4eafcaa3ac telegram-chat: say hello when awaiting commands 2025-02-25 22:16:48 +01:00
Christian Hesse
c33eb41c9c global-functions: $DeviceInfo: add license level, re-order 2025-02-25 17:55:26 +01:00
Christian Hesse
78f9687558 Merge branch 'telegram-topics' into next 2025-02-25 17:55:26 +01:00
Christian Hesse
e5de9de391 notify on support for Telegram group topics 2025-02-25 17:55:26 +01:00
Christian Hesse
7928c5f054 telegram-chat: support reply in group's topic 2025-02-25 17:55:26 +01:00
Christian Hesse
757fa60e6f telegram-chat: make $IsReply a boolean...
... and check for correct data type.

We need this for a group with topic feature enabled, as that variable is
set there, but is is an array.
2025-02-25 17:55:26 +01:00
Christian Hesse
a22b62f588 mod/notification-telegram: support sending to group's topic...
... when a group has enabled the "Topics" feature.
2025-02-25 17:55:12 +01:00
Christian Hesse
cad104879c mod/notification-telegram: simplify the queue...
... and pass http-data as a complete sting.
2025-02-24 15:18:53 +01:00
20 changed files with 302 additions and 98 deletions

View file

@ -44,6 +44,20 @@ instead.
Disable or remote that setting to restore regular logging. Disable or remote that setting to restore regular logging.
## Verbose output
Specific scripts can generate huge amount of output. These do use a function
`$LogPrintVerbose`, which is declared, but has no code, intentionally.
If you *really* want that output set the function to be the same as
`$LogPrint`:
:set LogPrintVerbose $LogPrint;
To revert that change just run:
:set LogPrintVerbose;
--- ---
[⬅️ Go back to main README](README.md) [⬅️ Go back to main README](README.md)
[⬆️ Go back to top](#top) [⬆️ Go back to top](#top)

View file

@ -19,7 +19,7 @@ Run the complete base installation:
{ {
/tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value; /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value;
:delay 1s; :delay 1s;
/certificate/import file-name=isrg-root-x2.pem passphrase=""; /certificate/import file-name="isrg-root-x2.pem" passphrase="";
:if ([ :len [ /certificate/find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={ :if ([ :len [ /certificate/find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={
:error "Something is wrong with your certificates!"; :error "Something is wrong with your certificates!";
}; };

View file

@ -87,7 +87,7 @@ file to your MikroTik device.
Then we import the certificate. Then we import the certificate.
/certificate/import file-name=isrg-root-x2.pem passphrase=""; /certificate/import file-name="isrg-root-x2.pem" passphrase="";
Do not worry that the command is not shown - that happens because it contains Do not worry that the command is not shown - that happens because it contains
a sensitive property, the passphrase. a sensitive property, the passphrase.

View file

@ -62,7 +62,7 @@
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={ ($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true; :set Updated true;
$RmFile $Package; $RmFile ($File->"name");
} }
} }

View file

@ -64,7 +64,7 @@
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={ ($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true; :set Updated true;
$RmFile $Package; $RmFile ($File->"name");
} }
} }

View file

@ -62,7 +62,7 @@
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \ :if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={ ($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true; :set Updated true;
$RmFile $Package; $RmFile ($File->"name");
} }
} }

View file

@ -15,7 +15,7 @@ DOMAINS_DUAL = \
git.eworm.de/ISRG-Root-X2 \ git.eworm.de/ISRG-Root-X2 \
lists.blocklist.de/Certum-Trusted-Network-CA \ lists.blocklist.de/Certum-Trusted-Network-CA \
matrix.org/GTS-Root-R4 \ matrix.org/GTS-Root-R4 \
raw.githubusercontent.com/DigiCert-Global-Root-G2 \ raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \
rsc.eworm.de/ISRG-Root-X2 \ rsc.eworm.de/ISRG-Root-X2 \
upgrade.mikrotik.com/ISRG-Root-X1 upgrade.mikrotik.com/ISRG-Root-X1
DOMAINS_IPV4 = \ DOMAINS_IPV4 = \

View file

@ -0,0 +1,41 @@
# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
# Label: "USERTrust RSA Certification Authority"
# Serial: 2645093764781058787591871645665788717
# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5
# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e
# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----

View file

@ -48,21 +48,26 @@
:global UrlEncode; :global UrlEncode;
:global WaitForFile; :global WaitForFile;
:local Return false; :foreach Type in={ "p12"; "pem" } do={
:local CertFileName ([ $UrlEncode $FetchName ] . "." . $Type);
$LogPrint debug $ScriptName ("Trying type '" . $Type . "' for '" . $CertName . \
"' (file '" . $CertFileName . "')...");
:foreach Type in={ ".pem"; ".p12" } do={
:local CertFileName ([ $UrlEncode $FetchName ] . $Type);
:do { :do {
/tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \ /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \
($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value; ($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value;
$WaitForFile $CertFileName; $WaitForFile $CertFileName;
:local DecryptionFailed true; :local DecryptionFailed true;
:foreach PassPhrase in=$CertRenewPass do={ :foreach I,PassPhrase in=$CertRenewPass do={
:do {
$LogPrint debug $ScriptName ("Trying " . $I . ". passphrase... ");
:local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ]; :local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ];
:if ($Result->"decryption-failures" = 0) do={ :if ($Result->"decryption-failures" = 0) do={
$LogPrint debug $ScriptName ("Success!");
:set DecryptionFailed false; :set DecryptionFailed false;
} }
} on-error={ }
} }
$RmFile $CertFileName; $RmFile $CertFileName;
@ -77,13 +82,13 @@
$CertificateNameByCN [ /certificate/get $CertInChain common-name ]; $CertificateNameByCN [ /certificate/get $CertInChain common-name ];
} }
:set Return true; :return true;
} on-error={ } on-error={
$LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'."); $LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'.");
} }
} }
:return $Return; :return false;
} }
:local FormatInfo do={ :local FormatInfo do={

View file

@ -66,6 +66,27 @@
:error "A reboot for update is already scheduled."; :error "A reboot for update is already scheduled.";
} }
:local License [ /system/license/get ];
:if ([ :typeof ($License->"deadline-at") ] = "str") do={
:if ([ :len ($License->"next-renewal-at") ] = 0 && ($License->"limited-upgrades") = true) do={
$LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!");
$SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \
message=("Your license expired on " . ($License->"deadline-at") . \
", can no longer update RouterOS on " . $Identity . "...") });
:set ExitOK true;
:error false;
}
:if ([ :totime ($License->"deadline-at") ] - 3w < [ :timestamp ]) do={
$LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!");
$SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "warning-sign" ] . "License about to expire!"); \
message=("Your license failed to renew and is about to expire on " . \
($License->"deadline-at") . " on " . $Identity . "...") });
}
}
$LogPrint debug $ScriptName ("Checking for updates..."); $LogPrint debug $ScriptName ("Checking for updates...");
/system/package/update/check-for-updates without-paging as-value; /system/package/update/check-for-updates without-paging as-value;
:local Update [ /system/package/update/get ]; :local Update [ /system/package/update/get ];

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -38,14 +38,21 @@ create your own bot:
![create new bot](notification-telegram.d/newbot.avif) ![create new bot](notification-telegram.d/newbot.avif)
Now open a chat with your bot and start it by clicking the `START` button. Set that token from *BotFather* (use your own!) to `TelegramTokenId`, for
now just temporarily:
Open just another chat with [GetIDs Bot](https://t.me/getidsbot), again start :set TelegramTokenId "5214364459:AAHLwf1o7ybbKDo6pY24Kd2bZ5rjCakDXTc";
with the `START` button. It will send you some information, including the
`id`, just below `You`. Now open a chat with your bot and start it by clicking the `START` button,
then send your first message. Any text will do. On your device run
`$GetTelegramChatId` to retrieve the chat id:
$GetTelegramChatId;
![get chat id](notification-telegram.d/getchatid.avif)
Finally edit `global-config-overlay`, add `TelegramTokenId` with the token Finally edit `global-config-overlay`, add `TelegramTokenId` with the token
from *BotFather* and `TelegramChatId` with your id from *GetIDs Bot*. Then from *BotFather* and `TelegramChatId` with your retrieved chat id. Then
reload the configuration. reload the configuration.
> **Info**: Copy relevant configuration from > **Info**: Copy relevant configuration from
@ -54,9 +61,13 @@ reload the configuration.
### Notifications to a group ### Notifications to a group
Sending notifications to a group is possible as well. Add your bot and the Sending notifications to a group is possible as well. Add your bot to a group
*GetIDs Bot* to a group, then use the group's id (which starts with a dash) and make it an admin (required for read access!) and send a message and run
for `TelegramChatId`. Then remove *GetIDs Bot* from group. `$GetTelegramChatId` again. Then use that chat id (which starts with a dash)
for `TelegramChatId`.
Groups can enable the `Topics` feature. Use `TelegramThreadId` to send to a
specific topic in a group.
Usage and invocation Usage and invocation
-------------------- --------------------

View file

@ -24,6 +24,7 @@
:global HumanReadableNum; :global HumanReadableNum;
:global LogPrint; :global LogPrint;
:global LogPrintOnce; :global LogPrintOnce;
:global LogPrintVerbose;
:global ScriptLock; :global ScriptLock;
:global WaitFullyConnected; :global WaitFullyConnected;
@ -36,6 +37,11 @@
} }
} }
:local GetBranch do={
:global EitherOr;
:return [ :pick [ :convert transform=md5 to=hex [ :pick $1 0 [ $EitherOr [ :find $1 "/" ] [ :len $1 ] ] ] ] 0 2 ];
}
:if ([ $ScriptLock $ScriptName ] = false) do={ :if ([ $ScriptLock $ScriptName ] = false) do={
:set ExitOK true; :set ExitOK true;
:error false; :error false;
@ -99,17 +105,24 @@
:set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr")); :set Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr"));
} }
:do { :do {
:local Branch [ $GetBranch $Address ];
:if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={ :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$") do={
:set ($IPv4Addresses->$Address) $TimeOut; :if ($Address ~ "/32\$") do={
:set Address [ :pick $Address 0 ([ :len $Address ] - 3) ];
}
:set ($IPv4Addresses->$Branch->$Address) $TimeOut;
:error true; :error true;
} }
:if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={ :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={
:set ($IPv6Addresses->$Address) $TimeOut; :if ($Address ~ "/128\$") do={
:set Address [ :pick $Address 0 ([ :len $Address ] - 4) ];
}
:set ($IPv6Addresses->$Branch->$Address) $TimeOut;
:error true; :error true;
} }
:if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={ :if ($Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={
:set ($IPv4Addresses->$Address) $TimeOut; :set ($IPv4Addresses->$Branch->$Address) $TimeOut;
:set ($IPv6Addresses->$Address) $TimeOut; :set ($IPv6Addresses->$Branch->$Address) $TimeOut;
:error true; :error true;
} }
} on-error={ } } on-error={ }
@ -119,15 +132,17 @@
:foreach Entry in=[ /ip/firewall/address-list/find where \ :foreach Entry in=[ /ip/firewall/address-list/find where \
list=$FwListName comment=$ListComment ] do={ list=$FwListName comment=$ListComment ] do={
:local Address [ /ip/firewall/address-list/get $Entry address ]; :local Address [ /ip/firewall/address-list/get $Entry address ];
:if ([ :typeof ($IPv4Addresses->$Address) ] = "time") do={ :local Branch [ $GetBranch $Address ];
$LogPrint debug $ScriptName ("Renewing IPv4 address in list '" . $FwListName . \ :local TimeOut ($IPv4Addresses->$Branch->$Address);
"' with " . ($IPv4Addresses->$Address) . ": " . $Address); :if ([ :typeof $TimeOut ] = "time") do={
/ip/firewall/address-list/set $Entry timeout=($IPv4Addresses->$Address); $LogPrintVerbose debug $ScriptName ("Renewing IPv4 address in list '" . $FwListName . \
:set ($IPv4Addresses->$Address); "' with " . $TimeOut . ": " . $Address);
/ip/firewall/address-list/set $Entry timeout=$TimeOut;
:set ($IPv4Addresses->$Branch->$Address);
:set CntRenew ($CntRenew + 1); :set CntRenew ($CntRenew + 1);
} else={ } else={
:if ($Failure = false) do={ :if ($Failure = false) do={
$LogPrint debug $ScriptName ("Removing IPv4 address from list '" . $FwListName . \ $LogPrintVerbose debug $ScriptName ("Removing IPv4 address from list '" . $FwListName . \
"': " . $Address); "': " . $Address);
/ip/firewall/address-list/remove $Entry; /ip/firewall/address-list/remove $Entry;
:set CntRemove ($CntRemove + 1); :set CntRemove ($CntRemove + 1);
@ -138,15 +153,17 @@
:foreach Entry in=[ /ipv6/firewall/address-list/find where \ :foreach Entry in=[ /ipv6/firewall/address-list/find where \
list=$FwListName comment=$ListComment ] do={ list=$FwListName comment=$ListComment ] do={
:local Address [ /ipv6/firewall/address-list/get $Entry address ]; :local Address [ /ipv6/firewall/address-list/get $Entry address ];
:if ([ :typeof ($IPv6Addresses->$Address) ] = "time") do={ :local Branch [ $GetBranch $Address ];
$LogPrint debug $ScriptName ("Renewing IPv6 address in list '" . $FwListName . \ :local TimeOut ($IPv6Addresses->$Branch->$Address);
"' with " . ($IPv6Addresses->$Address) . ": " . $Address); :if ([ :typeof $TimeOut ] = "time") do={
/ipv6/firewall/address-list/set $Entry timeout=($IPv6Addresses->$Address); $LogPrintVerbose debug $ScriptName ("Renewing IPv6 address in list '" . $FwListName . \
:set ($IPv6Addresses->$Address); "' with " . $TimeOut . ": " . $Address);
/ipv6/firewall/address-list/set $Entry timeout=$TimeOut;
:set ($IPv6Addresses->$Branch->$Address);
:set CntRenew ($CntRenew + 1); :set CntRenew ($CntRenew + 1);
} else={ } else={
:if ($Failure = false) do={ :if ($Failure = false) do={
$LogPrint debug $ScriptName ("Removing IPv6 address from list '" . $FwListName . \ $LogPrintVerbose debug $ScriptName ("Removing IPv6 address from list '" . $FwListName . \
"': " . $Address); "': " . $Address);
/ipv6/firewall/address-list/remove $Entry; /ipv6/firewall/address-list/remove $Entry;
:set CntRemove ($CntRemove + 1); :set CntRemove ($CntRemove + 1);
@ -154,33 +171,37 @@
} }
} }
:foreach Address,Timeout in=$IPv4Addresses do={ :foreach BranchName,Branch in=$IPv4Addresses do={
$LogPrint debug $ScriptName ("Adding IPv4 address to list '" . $FwListName . \ $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName);
:foreach Address,Timeout in=$Branch do={
$LogPrintVerbose debug $ScriptName ("Adding IPv4 address to list '" . $FwListName . \
"' with " . $Timeout . ": " . $Address); "' with " . $Timeout . ": " . $Address);
:do { :do {
/ip/firewall/address-list/add list=$FwListName comment=$ListComment \ /ip/firewall/address-list/add list=$FwListName comment=$ListComment \
address=$Address timeout=$Timeout; address=$Address timeout=$Timeout;
:set ($IPv4Addresses->$Address);
:set CntAdd ($CntAdd + 1); :set CntAdd ($CntAdd + 1);
} on-error={ } on-error={
$LogPrint warning $ScriptName ("Failed to add IPv4 address to list '" . $FwListName . \ $LogPrint warning $ScriptName ("Failed to add IPv4 address to list '" . $FwListName . \
"': " . $Address); "': " . $Address);
} }
} }
}
:foreach Address,Timeout in=$IPv6Addresses do={ :foreach BranchName,Branch in=$IPv6Addresses do={
$LogPrint debug $ScriptName ("Adding IPv6 address to list '" . $FwListName . \ $LogPrintVerbose debug $ScriptName ("Handling branch: " . $BranchName);
:foreach Address,Timeout in=$Branch do={
$LogPrintVerbose debug $ScriptName ("Adding IPv6 address to list '" . $FwListName . \
"' with " . $Timeout . ": " . $Address); "' with " . $Timeout . ": " . $Address);
:do { :do {
/ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \ /ipv6/firewall/address-list/add list=$FwListName comment=$ListComment \
address=$Address timeout=$Timeout; address=$Address timeout=$Timeout;
:set ($IPv6Addresses->$Address);
:set CntAdd ($CntAdd + 1); :set CntAdd ($CntAdd + 1);
} on-error={ } on-error={
$LogPrint warning $ScriptName ("Failed to add IPv6 address to list '" . $FwListName . \ $LogPrint warning $ScriptName ("Failed to add IPv6 address to list '" . $FwListName . \
"': " . $Address); "': " . $Address);
} }
} }
}
$LogPrint info $ScriptName ("list: " . $FwListName . \ $LogPrint info $ScriptName ("list: " . $FwListName . \
" (" . [ $HumanReadableNum ($CntAdd + $CntRenew) 1000 ] . ")" . \ " (" . [ $HumanReadableNum ($CntAdd + $CntRenew) 1000 ] . ")" . \

View file

@ -33,6 +33,8 @@
:global TelegramChatId ""; :global TelegramChatId "";
#:global TelegramTokenId "123456:ABCDEF-GHI"; #:global TelegramTokenId "123456:ABCDEF-GHI";
#:global TelegramChatId "12345678"; #:global TelegramChatId "12345678";
# Use this to send notifications to a specific topic in group.
:global TelegramThreadId "";
# Using telegram-chat you have to define trusted chat ids (not group ids!) # Using telegram-chat you have to define trusted chat ids (not group ids!)
# or user names. Groups allow to chat with devices simultaneously. # or user names. Groups allow to chat with devices simultaneously.
#:global TelegramChatIdsTrusted { #:global TelegramChatIdsTrusted {
@ -101,7 +103,7 @@
# cert="ISRG Root X2" }; # cert="ISRG Root X2" };
{ url="https://raw.githubusercontent.com/stamparm/ipsum/refs/heads/master/levels/4.txt"; { url="https://raw.githubusercontent.com/stamparm/ipsum/refs/heads/master/levels/4.txt";
# # higher level (decrease the numerical value) for more addresses, and vice versa # # higher level (decrease the numerical value) for more addresses, and vice versa
cert="DigiCert Global Root G2" }; cert="USERTrust RSA Certification Authority" };
{ url="https://www.dshield.org/block.txt"; cidr="/24"; { url="https://www.dshield.org/block.txt"; cidr="/24";
cert="ISRG Root X1" }; cert="ISRG Root X1" };
{ url="https://lists.blocklist.de/lists/strongips.txt"; { url="https://lists.blocklist.de/lists/strongips.txt";

View file

@ -15,7 +15,7 @@
# Git commit id & info, expected configuration version # Git commit id & info, expected configuration version
:global CommitId "unknown"; :global CommitId "unknown";
:global CommitInfo "unknown"; :global CommitInfo "unknown";
:global ExpectedConfigVersion 133; :global ExpectedConfigVersion 135;
# global variables not to be changed by user # global variables not to be changed by user
:global GlobalFunctionsReady false; :global GlobalFunctionsReady false;
@ -55,6 +55,7 @@
:global IsTimeSync; :global IsTimeSync;
:global LogPrint; :global LogPrint;
:global LogPrintOnce; :global LogPrintOnce;
:global LogPrintVerbose;
:global MAX; :global MAX;
:global MIN; :global MIN;
:global MkDir; :global MkDir;
@ -309,16 +310,19 @@
([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \ ([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \
[ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \ [ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \
([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \ ([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \
[ $FormatLine "Board name" ($Resource->"board-name") ] . "\n" . \ "Hardware:\n" . \
[ $FormatLine "Architecture" ($Resource->"architecture-name") ] . "\n" . \ [ $FormatLine " Board" ($Resource->"board-name") ] . "\n" . \
[ $FormatLine " Arch" ($Resource->"architecture-name") ] . "\n" . \
[ $IfThenElse ($RouterBoard->"routerboard" = true) \ [ $IfThenElse ($RouterBoard->"routerboard" = true) \
([ $FormatLine " Model" ($RouterBoard->"model") ] . \ ([ $FormatLine " Model" ($RouterBoard->"model") ] . \
[ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \ [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \
(" " . $RouterBoard->"revision") ] . "\n" . \ (" " . $RouterBoard->"revision") ] . "\n" . \
[ $FormatLine "Serial number" ($RouterBoard->"serial-number") ] . "\n") ] . \ [ $FormatLine " Serial" ($RouterBoard->"serial-number") ] . "\n") ] . \
[ $IfThenElse ([ :len ($License->"level") ] > 0) \ [ $IfThenElse ([ :len ($License->"nlevel") ] > 0) \
([ $FormatLine "License" ($License->"level") ] . "\n") ] . \ ([ $FormatLine " License" ("level " . ($License->"nlevel")) ] . "\n") ] . \
"RouterOS:\n" . \ "RouterOS:\n" . \
[ $IfThenElse ([ :len ($License->"level") ] > 0) \
([ $FormatLine " License" ("level " . ($License->"level")) ] . "\n") ] . \
[ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \ [ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \
[ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \ [ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \
[ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \ [ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \
@ -845,6 +849,9 @@
:return true; :return true;
} }
# The function $LogPrintVerbose is declared, but has no code, intentionally.
# https://rsc.eworm.de/DEBUG.md#verbose-output
# get max value # get max value
:set MAX do={ :set MAX do={
:if ($1 > $2) do={ :return $1; } :if ($1 > $2) do={ :return $1; }
@ -1147,6 +1154,14 @@
:local ReloadGlobalConfig false; :local ReloadGlobalConfig false;
:local DeviceMode [ /system/device-mode/get ]; :local DeviceMode [ /system/device-mode/get ];
:local CheckSums ({});
:do {
:local Url ($ScriptUpdatesBaseUrl . "checksums.json" . $ScriptUpdatesUrlSuffix);
$LogPrint debug $0 ("Fetching checksums from url: " . $Url);
:set CheckSums [ :deserialize from=json ([ /tool/fetch check-certificate=yes-without-crl \
http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ]->"data") ];
} on-error={ }
:foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\r?\n" ] do={ :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\r?\n" ] do={
:local ScriptVal [ /system/script/get $Script ]; :local ScriptVal [ /system/script/get $Script ];
:local ScriptInfo [ $ParseKeyValueStore ($ScriptVal->"comment") ]; :local ScriptInfo [ $ParseKeyValueStore ($ScriptVal->"comment") ];
@ -1160,7 +1175,19 @@
} }
} }
:if (!($ScriptInfo->"ignore" = true)) do={ :do {
:if ($ScriptInfo->"ignore" = true) do={
$LogPrint debug $0 ("Ignoring script '" . $ScriptVal->"name" . "', as requested.");
:error true;
}
:local CheckSum ($CheckSums->($ScriptVal->"name"));
:if ([ :len ($ScriptInfo->"base-url") ] = 0 && [ :len ($ScriptInfo->"url-suffix") ] = 0 && \
[ :convert transform=md5 to=hex [ :tolf ($ScriptVal->"source") ] ] = $CheckSum) do={
$LogPrint debug $0 ("Checksum for script '" . $ScriptVal->"name" . "' matches, ignoring.");
:error true;
}
:do { :do {
:local BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ]; :local BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ];
:local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ]; :local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ];
@ -1179,10 +1206,9 @@
} else={ } else={
$LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!"); $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!");
} }
} :error false;
} }
:do {
:if ([ :len $SourceNew ] = 0) do={ :if ([ :len $SourceNew ] = 0) do={
$LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'."); $LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'.");
:error false; :error false;

View file

@ -20,7 +20,9 @@
:global ScriptLock; :global ScriptLock;
:local NaAddress $"na-address"; :local NaAddress $"na-address";
:local NaValid $"na-valid";
:local PdPrefix $"pd-prefix"; :local PdPrefix $"pd-prefix";
:local PdValid $"pd-valid";
:if ([ $ScriptLock $ScriptName ] = false) do={ :if ([ $ScriptLock $ScriptName ] = false) do={
:set ExitOK true; :set ExitOK true;
@ -33,12 +35,18 @@
:error false; :error false;
} }
:if ([ :typeof $PdPrefix ] = "nothing") do={ :if ([ :typeof $PdPrefix ] = "nothing" || [ :typeof $PdValid ] = "nothing") do={
$LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client."); $LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client.");
:set ExitOK true; :set ExitOK true;
:error false; :error false;
} }
:if ($PdValid != 1) do={
$LogPrint info $ScriptName ("The prefix " . $PdPrefix . " is no longer valid. Ignoring.");
:set ExitOK true;
:error false;
}
:local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ]; :local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ];
:if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={ :if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={
/ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool) dynamic=yes; /ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool) dynamic=yes;

View file

@ -10,6 +10,7 @@
# https://rsc.eworm.de/doc/mod/notification-telegram.md # https://rsc.eworm.de/doc/mod/notification-telegram.md
:global FlushTelegramQueue; :global FlushTelegramQueue;
:global GetTelegramChatId;
:global NotificationFunctions; :global NotificationFunctions;
:global PurgeTelegramQueue; :global PurgeTelegramQueue;
:global SendTelegram; :global SendTelegram;
@ -22,7 +23,6 @@
:global IsFullyConnected; :global IsFullyConnected;
:global LogPrint; :global LogPrint;
:global UrlEncode;
:if ([ $IsFullyConnected ] = false) do={ :if ([ $IsFullyConnected ] = false) do={
$LogPrint debug $0 ("System is not fully connected, not flushing."); $LogPrint debug $0 ("System is not fully connected, not flushing.");
@ -41,9 +41,7 @@
:do { :do {
:local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \
("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \ ("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
http-data=("chat_id=" . ($Message->"chatid") . "&disable_notification=" . ($Message->"silent") . \ http-data=($Message->"http-data") as-value ]->"data");
"&reply_to_message_id=" . ($Message->"replyto") . "&disable_web_page_preview=true" . \
"&parse_mode=MarkdownV2&text=" . [ $UrlEncode ($Message->"text") ]) as-value ]->"data");
:set ($TelegramQueue->$Id); :set ($TelegramQueue->$Id);
:set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1;
} on-error={ } on-error={
@ -61,6 +59,45 @@
:global ExitError; $ExitError false $0; :global ExitError; $ExitError false $0;
} } } }
# get the chat id
:set GetTelegramChatId do={ :do {
:global TelegramTokenId;
:global CertificateAvailable;
:global LogPrint;
:if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={
$LogPrint warning $0 ("Downloading required certificate failed.");
:return false;
}
:local Data;
:do {
: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");
} on-error={
$LogPrint warning $0 ("Fetching data failed!");
: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"));
}
} on-error={
:global ExitError; $ExitError false $0;
} }
# send notification via telegram - expects one array argument # send notification via telegram - expects one array argument
:set ($NotificationFunctions->"telegram") do={ :set ($NotificationFunctions->"telegram") do={
:local Notification $1; :local Notification $1;
@ -71,6 +108,8 @@
:global TelegramChatIdOverride; :global TelegramChatIdOverride;
:global TelegramMessageIDs; :global TelegramMessageIDs;
:global TelegramQueue; :global TelegramQueue;
:global TelegramThreadId;
:global TelegramThreadIdOverride;
:global TelegramTokenId; :global TelegramTokenId;
:global TelegramTokenIdOverride; :global TelegramTokenIdOverride;
@ -111,6 +150,8 @@
:local ChatId [ $EitherOr ($Notification->"chatid") \ :local ChatId [ $EitherOr ($Notification->"chatid") \
[ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ]; [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ];
:local ThreadId [ $EitherOr ($Notification->"threadid") \
[ $EitherOr ($TelegramThreadIdOverride->($Notification->"origin")) $TelegramThreadId ] ];
:local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ]; :local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ];
:if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={ :if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
@ -145,6 +186,9 @@
(($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%_!") "plain" "_" ]); (($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");
:do { :do {
:if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={ :if ([ $CertificateAvailable "Go Daddy Root Certificate Authority - G2" ] = false) do={
$LogPrint warning $0 ("Downloading required certificate failed."); $LogPrint warning $0 ("Downloading required certificate failed.");
@ -152,9 +196,7 @@
} }
:local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \
("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \ ("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \ http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) as-value ]->"data");
"&reply_to_message_id=" . ($Notification->"replyto") . "&disable_web_page_preview=true" . \
"&parse_mode=MarkdownV2&text=" . [ $UrlEncode $Text ]) as-value ]->"data");
:set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1; :set ($TelegramMessageIDs->[ :tostr ([ :deserialize from=json value=$Data ]->"result"->"message_id") ]) 1;
} on-error={ } on-error={
$LogPrint info $0 ("Failed sending Telegram notification! Queuing..."); $LogPrint info $0 ("Failed sending Telegram notification! Queuing...");
@ -165,8 +207,8 @@
:set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \ :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \
[ $EscapeMD ("This message was queued since _" . [ /system/clock/get date ] . \ [ $EscapeMD ("This message was queued since _" . [ /system/clock/get date ] . \
" " . [ /system/clock/get time ] . "_ and may be obsolete.") "plain" "_" ]); " " . [ /system/clock/get time ] . "_ and may be obsolete.") "plain" "_" ]);
:set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId; :set ($TelegramQueue->[ :len $TelegramQueue ]) { tokenid=$TokenId;
text=$Text; silent=($Notification->"silent"); replyto=($Notification->"replyto") }; http-data=($HTTPData . "&text=" . [ $UrlEncode $Text ]) };
:if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] = 0) do={ :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] = 0) do={
/system/scheduler/add name="_FlushTelegramQueue" interval=1m start-time=startup \ /system/scheduler/add name="_FlushTelegramQueue" interval=1m start-time=startup \
on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;"); on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;");

View file

@ -61,16 +61,20 @@
:global GetRandom20CharAlNum; :global GetRandom20CharAlNum;
:local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]); :local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]);
/ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s; :if ([ :typeof [ :toip $Expected ] ] = "ip") do={
/ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s;
:delay 20ms; :delay 20ms;
:if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={
:return true; :return true;
} }
/ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s; }
:if ([ :typeof [ :toip6 $Expected ] ] = "ip6") do={
/ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=10s;
:delay 20ms; :delay 20ms;
:if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={ :if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={
:return true; :return true;
} }
}
:return false; :return false;
} }

View file

@ -58,6 +58,8 @@
131="Enhanced certificate download to fallback to mkcert.org, so all (commonly trusted) root certificates are available now."; 131="Enhanced certificate download to fallback to mkcert.org, so all (commonly trusted) root certificates are available now.";
132="Split off plugins from 'check-health', so the script works on all devices to monitor CPU and RAM. The supported plugins for sensors in hardware are installed automatically."; 132="Split off plugins from 'check-health', so the script works on all devices to monitor CPU and RAM. The supported plugins for sensors in hardware are installed automatically.";
133="Updated the default configuration for 'fw-addr-lists', deprecated lists were removed, a collective list was added."; 133="Updated the default configuration for 'fw-addr-lists', deprecated lists were removed, a collective list was added.";
134="Enhanced 'mod/notification-telegram' and 'telegram-chat' to support topics in groups.";
135="Introduced helper function '\$GetTelegramChatId' for 'mod/notification-telegram' which helps retrieve information.";
}; };
# Migration steps to be applied on script updates # Migration steps to be applied on script updates

View file

@ -97,13 +97,14 @@
:foreach Update in=($JSON->"result") do={ :foreach Update in=($JSON->"result") do={
:set UpdateID ($Update->"update_id"); :set UpdateID ($Update->"update_id");
:local Message ($Update->"message"); :local Message ($Update->"message");
:local IsReply [ :len ($Message->"reply_to_message") ]; :local IsReply ([ :typeof ($Message->"reply_to_message") ] = "string");
:local IsMyReply ($TelegramMessageIDs->[ :tostr ($Message->"reply_to_message"->"message_id") ]); :local IsMyReply ($TelegramMessageIDs->[ :tostr ($Message->"reply_to_message"->"message_id") ]);
:if (($IsMyReply = 1 || $TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={ :if (($IsMyReply = 1 || $TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={
:local Trusted false; :local Trusted false;
:local Chat ($Message->"chat"); :local Chat ($Message->"chat");
:local From ($Message->"from"); :local From ($Message->"from");
:local Command ($Message->"text"); :local Command ($Message->"text");
:local ThreadId [ $IfThenElse ($Message->"is_topic_message") ($Message->"message_thread_id") "" ];
:foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={ :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={
:if ($From->"id" = $IdsTrusted || $From->"username" = $IdsTrusted) do={ :if ($From->"id" = $IdsTrusted || $From->"username" = $IdsTrusted) do={
@ -115,9 +116,11 @@
:local Done false; :local Done false;
:if ($Command = "?") do={ :if ($Command = "?") do={
$LogPrint info $ScriptName ("Sending notice for update " . $UpdateID . "."); $LogPrint info $ScriptName ("Sending notice for update " . $UpdateID . ".");
$SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \
replyto=($Message->"message_id"); threadid=$ThreadId; \
subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
message=("Online" . [ $IfThenElse $TelegramChatActive " (and active!)" ] . ", awaiting your commands!") }); message=([ $IfThenElse ([ :len ($From->"first_name") ] > 0) ("Hello " . ($From->"first_name") . "!\n\n") ] . \
"Online" . [ $IfThenElse $TelegramChatActive " (and active!)" ] . ", awaiting your commands!") });
:set Done true; :set Done true;
} }
:if ($Done = false && [ :pick $Command 0 1 ] = "!") do={ :if ($Done = false && [ :pick $Command 0 1 ] = "!") do={
@ -130,7 +133,8 @@
" from update " . $UpdateID . "!"); " from update " . $UpdateID . "!");
:set Done true; :set Done true;
} }
:if ($Done = false && ($IsMyReply = 1 || ($IsReply = 0 && $TelegramChatActive = true)) && [ :len $Command ] > 0) do={ :if ($Done = false && ($IsMyReply = 1 || ($IsReply = false && \
$TelegramChatActive = true)) && [ :len $Command ] > 0) do={
:if ([ $ValidateSyntax $Command ] = true) do={ :if ([ $ValidateSyntax $Command ] = true) do={
:local State ""; :local State "";
:local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]); :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]);
@ -149,7 +153,8 @@
:set State ([ $SymbolForNotification "cross-mark" ] . "The command failed with an error!\n\n"); :set State ([ $SymbolForNotification "cross-mark" ] . "The command failed with an error!\n\n");
} }
:local Content ([ /file/read chunk-size=32768 file=$File as-value ]->"data"); :local Content ([ /file/read chunk-size=32768 file=$File as-value ]->"data");
$SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; \
replyto=($Message->"message_id"); threadid=$ThreadId; \
subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \
$State . [ $IfThenElse ([ :len $Content ] > 0) \ $State . [ $IfThenElse ([ :len $Content ] > 0) \
@ -158,7 +163,8 @@
$RmDir "tmpfs/telegram-chat"; $RmDir "tmpfs/telegram-chat";
} else={ } else={
$LogPrint info $ScriptName ("The command from update " . $UpdateID . " failed syntax validation!"); $LogPrint info $ScriptName ("The command from update " . $UpdateID . " failed syntax validation!");
$SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \
replyto=($Message->"message_id"); threadid=$ThreadId; \
subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \ message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Command . "\n\n" . \
[ $SymbolForNotification "cross-mark" ] . "The command failed syntax validation!") }); [ $SymbolForNotification "cross-mark" ] . "The command failed syntax validation!") });
@ -170,7 +176,8 @@
" (ID " . $From->"id" . ") in update " . $UpdateID . "!"); " (ID " . $From->"id" . ") in update " . $UpdateID . "!");
:if ($Command ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={ :if ($Command ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={
$LogPrint warning $ScriptName $MessageText; $LogPrint warning $ScriptName $MessageText;
$SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; \
replyto=($Message->"message_id"); threadid=$ThreadId; \
subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
message=("You are not trusted.") }); message=("You are not trusted.") });
} else={ } else={