Compare commits

..

No commits in common. "main" and "change-138" have entirely different histories.

130 changed files with 317 additions and 804 deletions

View file

@ -61,7 +61,7 @@ Import a certificate by CommonName
Running the function `$CertificateAvailable` with that name as parameter Running the function `$CertificateAvailable` with that name as parameter
makes sure the certificate is available in the device's store: makes sure the certificate is available in the device's store:
$CertificateAvailable "ISRG Root X2" "fetch"; $CertificateAvailable "ISRG Root X2";
If the certificate is actually available already nothing happens, and there If the certificate is actually available already nothing happens, and there
is no output. Otherwise the certificate is downloaded and imported. is no output. Otherwise the certificate is downloaded and imported.

View file

@ -35,7 +35,6 @@ Add yourself to the list,
[donate with PayPal ↗️](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)! [donate with PayPal ↗️](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)!
* Abdul Mannan Abbasi * Abdul Mannan Abbasi
* Alex Maier
* Andrea Ruffini Perico * Andrea Ruffini Perico
* Andrew Cox * Andrew Cox
* Christoph Boss (@Kampfwurst) * Christoph Boss (@Kampfwurst)

View file

@ -17,39 +17,33 @@ Initial commands
Run the complete base installation: Run the complete base installation:
{ {
:local BaseUrl "https://rsc.eworm.de/main/"; :local BaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/";
:local CertCommonName "ISRG Root X2";
:local CertFileName "ISRG-Root-X2.pem"; :local CertFileName "ISRG-Root-X2.pem";
:local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470"; :local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";
:local CertSettings [ /certificate/settings/get ]; :put "Importing certificate...";
:if (!((($CertSettings->"builtin-trust-anchors") = "trusted" || \ /tool/fetch ($BaseUrl . "certs/" . $CertFileName) dst-path=$CertFileName as-value;
($CertSettings->"builtin-trust-store") ~ "fetch" || \ :delay 1s;
($CertSettings->"builtin-trust-store") = "all") && \ /certificate/import file-name=$CertFileName passphrase="";
[[ :parse (":return [ :len [ /certificate/builtin/find where common-name=\"" . $CertCommonName . "\" ] ]") ]] > 0)) do={ :if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] != 1) do={
:put "Importing certificate..."; :error "Something is wrong with your certificates!";
/tool/fetch ($BaseUrl . "certs/" . $CertFileName) dst-path=$CertFileName as-value;
:delay 1s;
/certificate/import file-name=$CertFileName passphrase="";
:if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] != 1) do={
:error "Something is wrong with your certificates!";
};
:delay 1s;
}; };
:delay 1s;
:put "Renaming global-config-overlay, if exists..."; :put "Renaming global-config-overlay, if exists...";
/system/script/set name=("global-config-overlay-" . [ /system/clock/get date ] . "-" . [ /system/clock/get time ]) [ find where name="global-config-overlay" ]; /system/script/set name=("global-config-overlay-" . [ /system/clock/get date ] . "-" . [ /system/clock/get time ]) [ find where name="global-config-overlay" ];
:foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={
:put "Installing $Script..."; :put "Installing $Script...";
/system/script/remove [ find where name=$Script ]; /system/script/remove [ find where name=$Script ];
/system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ($BaseUrl . $Script . ".rsc") output=user as-value ]->"data"); /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ($BaseUrl . $Script . ".rsc") output=user as-value]->"data");
}; };
:put "Loading configuration and functions..."; :put "Loading configuration and functions...";
/system/script { run global-config; run global-functions; }; /system/script { run global-config; run global-functions; };
:if ([ :len [ /certificate/find where fingerprint=$CertFingerprint ] ] > 0) do={ :put "Scheduling to load configuration and functions...";
:put "Renaming certificate by its common-name..."; /system/scheduler/remove [ find where name="global-scripts" ];
:global CertificateNameByCN; /system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
$CertificateNameByCN $CertFingerprint; :put "Renaming certificate by its common-name...";
}; :global CertificateNameByCN;
$CertificateNameByCN $CertFingerprint;
}; };
Then continue setup with Then continue setup with

View file

@ -2,45 +2,38 @@
# template scripts -> final scripts # template scripts -> final scripts
# markdown files -> html files # markdown files -> html files
ALL_RSC := $(wildcard *.rsc */*.rsc) CAPSMAN = $(wildcard *.capsman.rsc)
GEN_RSC := $(wildcard *.capsman.rsc *.local.rsc *.wifi.rsc) LOCAL = $(wildcard *.local.rsc)
WIFI = $(wildcard *.wifi.rsc)
MARKDOWN := $(wildcard *.md doc/*.md doc/mod/*.md) MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
HTML := $(MARKDOWN:.md=.html) HTML = $(MARKDOWN:.md=.html)
DATE ?= $(shell date --rfc-email) all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML) checksums.json
VERSION ?= $(shell git symbolic-ref --short HEAD 2>/dev/null)/$(shell git rev-list --count HEAD 2>/dev/null)/$(shell git rev-parse --short=8 HEAD 2>/dev/null)
export DATE VERSION
.PHONY: all checksums commitinfo docs rsc clean %.html: %.md Makefile
markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@
all: checksums docs rsc %.capsman.rsc: %.template.rsc Makefile
sed -e '/\/interface\/wifi\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.capsman|' \
-e '/^# NOT \/caps-man\/ #$$/,/^# NOT \/caps-man\/ #$$/d' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@
checksums: checksums.json %.local.rsc: %.template.rsc Makefile
sed -e '/\/caps-man\//d' -e '/\/interface\/wifi\//d' -e 's|%TEMPL%|.local|' \
-e '/^# NOT \/interface\/wireless\/ #$$/,/^# NOT \/interface\/wireless\/ #$$/d' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@
checksums.json: contrib/checksums.sh $(ALL_RSC) %.wifi.rsc: %.template.rsc Makefile
contrib/checksums.sh > $@ sed -e '/\/caps-man\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.wifi|' \
-e '/^# NOT \/interface\/wifi\/ #$$/,/^# NOT \/interface\/wifi\/ #$$/d' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@
commitinfo: global-functions.rsc checksums.json: contrib/checksums.sh *.rsc */*.rsc
contrib/commitinfo.sh $< > $<~ contrib/checksums.sh
mv $<~ $<
docs: $(HTML)
%.html: %.md general/style.css contrib/html.sh contrib/html.sh.d/head.html contrib/html.sh.d/foot.html
contrib/html.sh $< > $@
rsc: $(GEN_RSC)
%.capsman.rsc: %.template.rsc contrib/template-capsman.sh
contrib/template-capsman.sh $< > $@
%.local.rsc: %.template.rsc contrib/template-local.sh
contrib/template-local.sh $< > $@
%.wifi.rsc: %.template.rsc contrib/template-wifi.sh
contrib/template-wifi.sh $< > $@
clean: clean:
rm -f $(HTML) checksums.json rm -f $(HTML) checksums.json
make -C contrib/ clean

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 207 B

After

Width:  |  Height:  |  Size: 207 B

Before After
Before After

View file

@ -55,18 +55,15 @@ Initial setup
### Get me ready! ### Get me ready!
If you know how things work just copy and paste the If you know how things work just copy and paste the
[initial commands](INITIAL-COMMANDS.md). These also support fixing an [initial commands](INITIAL-COMMANDS.md). Remember to edit and rerun
existing but broken installation. Remember to edit and rerun
`global-config-overlay`! `global-config-overlay`!
First time users should take the long way below.
> 💡️ **Hint**: First time users should take
> [the long way in detail](#the-long-way-in-detail) below.
### Live presentation ### Live presentation
Want to see it in action? I've had a presentation [Repository based Want to see it in action? I've had a presentation [Repository based
RouterOS script distribution ↗️](https://www.youtube.com/watch?v=B9neG3oAhcY) RouterOS script distribution ↗️](https://www.youtube.com/watch?v=B9neG3oAhcY)
including demonstration recorded live at [MUM Europe including demonstation recorded live at [MUM Europe
2019 ↗️](https://mum.mikrotik.com/2019/EU/) in Vienna. 2019 ↗️](https://mum.mikrotik.com/2019/EU/) in Vienna.
> ⚠️ **Warning**: Some details changed. So see the presentation, then follow > ⚠️ **Warning**: Some details changed. So see the presentation, then follow
@ -75,22 +72,11 @@ including demonstration recorded live at [MUM Europe
### The long way in detail ### The long way in detail
The update script does server certificate verification, so first step is to The update script does server certificate verification, so first step is to
download the certificates. download the certificates. If you intend to download the scripts from a
> 💡️ **Hint**: RouterOS 7.19 comes with a builtin certificate store. You
> can skip the steps regarding certificate download and import and jump
> to [installation of scripts](#installation-of-scripts) if you set the
> trust for these builtin trust anchors:
> `/certificate/settings/set builtin-trust-anchors=trusted;`
> With RouterOS 7.21 the functionality was changed. Set this at minimum,
> but make sure not to drop other targets:
> `/certificate/settings/set builtin-trust-store=fetch;`
If you intend to download the scripts from a
different location (for example from github.com) install the corresponding different location (for example from github.com) install the corresponding
certificate chain. certificate chain.
/tool/fetch "https://rsc.eworm.de/main/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem"; /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem";
![screenshot: download certs](README.d/01-download-certs.avif) ![screenshot: download certs](README.d/01-download-certs.avif)
@ -120,24 +106,22 @@ is shown.
Always make sure there are no certificates installed you do not know or want! Always make sure there are no certificates installed you do not know or want!
#### Installation of scripts
All following commands will verify the server certificate. For validity the All following commands will verify the server certificate. For validity the
certificate's lifetime is checked with local time, so make sure the device's certificate's lifetime is checked with local time, so make sure the device's
date and time is set correctly! date and time is set correctly!
Now let's download the main scripts and add them in configuration on the fly. Now let's download the main scripts and add them in configuration on the fly.
:foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://rsc.eworm.de/main/" . $Script . ".rsc") output=user as-value ]->"data"); }; :foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script owner=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); };
![screenshot: import scripts](README.d/04-import-scripts.avif) ![screenshot: import scripts](README.d/04-import-scripts.avif)
And finally run configuration and functions. This will also add the And finally load configuration and functions and add the scheduler.
scheduler for loading at system startup automatically.
/system/script { run global-config; run global-functions; }; /system/script { run global-config; run global-functions; };
/system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
![screenshot: run scripts](README.d/05-run-scripts.avif) ![screenshot: run and schedule scripts](README.d/05-run-and-schedule-scripts.avif)
> 💡️ **Hint**: You see complaints regarding syntax errors? Most likely the > 💡️ **Hint**: You see complaints regarding syntax errors? Most likely the
> RouterOS on your device is too old. Check for updates! > RouterOS on your device is too old. Check for updates!
@ -225,7 +209,7 @@ cleanup add a scheduler entry.
$ScriptInstallUpdate dhcp-to-dns,lease-script; $ScriptInstallUpdate dhcp-to-dns,lease-script;
/ip/dhcp-server/set lease-script=lease-script [ find ]; /ip/dhcp-server/set lease-script=lease-script [ find ];
/system/scheduler/add name="dhcp-to-dns" interval=5m start-time=startup on-event="/system/script/run dhcp-to-dns;"; /system/scheduler/add name="dhcp-to-dns" interval=5m on-event="/system/script/run dhcp-to-dns;";
![screenshot: setup lease script](README.d/12-setup-lease-script.avif) ![screenshot: setup lease script](README.d/12-setup-lease-script.avif)
@ -407,15 +391,14 @@ immediately remove the link in question.
Upstream Upstream
-------- --------
[rsc.eworm.de](https://rsc.eworm.de/) [![upstream](README.d/upstream.png)](https://rsc.eworm.de/)
[![upstream](general/qr-code.png)](https://rsc.eworm.de/) URL:
[GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts)
### Code hosting Mirror:
[eworm.de](https://git.eworm.de/cgit/routeros-scripts/about/)
* [git.eworm.de](https://git.eworm.de/cgit/routeros-scripts/about/) [GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts)
* [GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts)
* [GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts)
--- ---
[⬆️ Go back to top](#top) [⬆️ Go back to top](#top)

View file

@ -22,7 +22,7 @@
:foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /caps-man/access-list/get $AccList mac-address ]; :local Mac [ /caps-man/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/caps-man/access-list/print without-paging where mac-address=$Mac; /caps-man/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={

View file

@ -22,7 +22,7 @@
:foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; :local Mac [ /interface/wireless/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/interface/wireless/access-list/print without-paging where mac-address=$Mac; /interface/wireless/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={

View file

@ -27,9 +27,9 @@
:local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; :local Mac [ /interface/wifi/access-list/get $AccList mac-address ];
:local Mac [ /interface/wireless/access-list/get $AccList mac-address ]; :local Mac [ /interface/wireless/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/caps-man/access-list/print without-paging where mac-address=$Mac; /caps-man/access-list/print where mac-address=$Mac;
/interface/wifi/access-list/print without-paging where mac-address=$Mac; /interface/wifi/access-list/print where mac-address=$Mac;
/interface/wireless/access-list/print without-paging where mac-address=$Mac; /interface/wireless/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={

View file

@ -22,7 +22,7 @@
:foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={ :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /interface/wifi/access-list/get $AccList mac-address ]; :local Mac [ /interface/wifi/access-list/get $AccList mac-address ];
:if ($Seen->$Mac = 1) do={ :if ($Seen->$Mac = 1) do={
/interface/wifi/access-list/print without-paging where mac-address=$Mac; /interface/wifi/access-list/print where mac-address=$Mac;
:local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ]; :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
:if ([ :typeof $Remove ] = "num") do={ :if ([ :typeof $Remove ] = "num") do={

View file

@ -27,7 +27,6 @@
:global CleanName; :global CleanName;
:global DeviceInfo; :global DeviceInfo;
:global FileExists;
:global FormatLine; :global FormatLine;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
@ -125,19 +124,17 @@
attach=$Attach; remove-attach=true }); attach=$Attach; remove-attach=true });
# wait for the mail to be sent # wait for the mail to be sent
:do { :local I 0;
:retry { :while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={
:if ([ $FileExists ($FilePath . ".conf") ".conf file" ] = true || \ :if ($I >= 120) do={
[ $FileExists ($FilePath . ".backup") "backup" ] = true || \ $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed.");
[ $FileExists ($FilePath . ".rsc") "script" ] = true) do={ :set PackagesUpdateBackupFailure true;
:error "Files are still available."; :set ExitOK true;
} :error false;
} delay=1s max=120; }
} on-error={ :delay 1s;
$LogPrint warning $ScriptName ("Files are still available, sending e-mail failed."); :set I ($I + 1);
:set PackagesUpdateBackupFailure true;
} }
# do not remove the files here, as the mail is still queued!
} do={ } do={
:global ExitError; $ExitError $ExitOK [ :jobname ] $Err; :global ExitError; $ExitError $ExitOK [ :jobname ] $Err;
} }

View file

@ -20,7 +20,6 @@
:global CleanFilePath; :global CleanFilePath;
:global DownloadPackage; :global DownloadPackage;
:global FileGet;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
:global RmFile; :global RmFile;
@ -43,7 +42,7 @@
:error false; :error false;
} }
:if ([ $FileGet $PackagePath ] = false) do={ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={
$LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!"); $PackagePath . ") failed!");
@ -54,7 +53,7 @@
"). Please place your packages!"); "). Please place your packages!");
} }
:foreach Package in=[ /file/find where type="package" \ :foreach Package in=[ /file/find where type=package \
package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ]; :local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={ :if ($File->"package-architecture" = "mips") do={
@ -67,7 +66,7 @@
} }
} }
:if ([ :len [ /file/find where type="package" name~("^" . $PackagePath) ] ] = 0) do={ :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={
$LogPrint info $ScriptName ("No packages available, downloading default set."); $LogPrint info $ScriptName ("No packages available, downloading default set.");
:foreach Arch in={ "arm"; "mipsbe" } do={ :foreach Arch in={ "arm"; "mipsbe" } do={
:foreach Package in={ "routeros"; "wireless" } do={ :foreach Package in={ "routeros"; "wireless" } do={

View file

@ -21,7 +21,6 @@
:global CleanFilePath; :global CleanFilePath;
:global DownloadPackage; :global DownloadPackage;
:global FileGet;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
:global RmFile; :global RmFile;
@ -45,7 +44,7 @@
:error false; :error false;
} }
:if ([ $FileGet $PackagePath ] = false) do={ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={
$LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!"); $PackagePath . ") failed!");
@ -56,7 +55,7 @@
"). Please place your packages!"); "). Please place your packages!");
} }
:foreach Package in=[ /file/find where type="package" \ :foreach Package in=[ /file/find where type=package \
package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ]; :local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={ :if ($File->"package-architecture" = "mips") do={
@ -69,7 +68,7 @@
} }
} }
:if ([ :len [ /file/find where type="package" name~("^" . $PackagePath) ] ] = 0) do={ :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={
$LogPrint info $ScriptName ("No packages available, downloading default set."); $LogPrint info $ScriptName ("No packages available, downloading default set.");
# NOT /interface/wifi/ # # NOT /interface/wifi/ #
:foreach Arch in={ "arm"; "mipsbe" } do={ :foreach Arch in={ "arm"; "mipsbe" } do={

View file

@ -20,7 +20,6 @@
:global CleanFilePath; :global CleanFilePath;
:global DownloadPackage; :global DownloadPackage;
:global FileGet;
:global LogPrint; :global LogPrint;
:global MkDir; :global MkDir;
:global RmFile; :global RmFile;
@ -43,7 +42,7 @@
:error false; :error false;
} }
:if ([ $FileGet $PackagePath ] = false) do={ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={ :if ([ $MkDir $PackagePath ] = false) do={
$LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!"); $PackagePath . ") failed!");
@ -54,7 +53,7 @@
"). Please place your packages!"); "). Please place your packages!");
} }
:foreach Package in=[ /file/find where type="package" \ :foreach Package in=[ /file/find where type=package \
package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={ package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ]; :local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={ :if ($File->"package-architecture" = "mips") do={
@ -67,7 +66,7 @@
} }
} }
:if ([ :len [ /file/find where type="package" name~("^" . $PackagePath) ] ] = 0) do={ :if ([ :len [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={
$LogPrint info $ScriptName ("No packages available, downloading default set."); $LogPrint info $ScriptName ("No packages available, downloading default set.");
:foreach Arch in={ "arm"; "arm64" } do={ :foreach Arch in={ "arm"; "arm64" } do={
:local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" }; :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" };

View file

@ -21,7 +21,7 @@
:global CertWarnTime; :global CertWarnTime;
:global Identity; :global Identity;
:global CertificateAvailable; :global CertificateAvailable
:global EscapeForRegEx; :global EscapeForRegEx;
:global IfThenElse; :global IfThenElse;
:global LogPrint; :global LogPrint;
@ -189,7 +189,7 @@
fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ]; fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ];
:local CertNewVal [ /certificate/get $CertNew ]; :local CertNewVal [ /certificate/get $CertNew ];
:if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") "fetch" ] = false) do={ :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={
$LogPrint warning $ScriptName ("The certificate chain is not available!"); $LogPrint warning $ScriptName ("The certificate chain is not available!");
} }
@ -231,7 +231,7 @@
:local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ]; :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ];
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "lock-with-ink-pen,warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \ subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \
message=("A certificate on " . $Identity . " " . $State . ".\n\n" . [ $FormatInfo $Cert ]) }); message=("A certificate on " . $Identity . " " . $State . ".\n\n" . [ $FormatInfo $Cert ]) });
$LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' " . $State . \ $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' " . $State . \
", it is invalid after " . ($CertVal->"invalid-after") . "."); ", it is invalid after " . ($CertVal->"invalid-after") . ".");

View file

@ -11,8 +11,7 @@
:global CheckHealthPlugins; :global CheckHealthPlugins;
:set ($CheckHealthPlugins->[ :jobname ]) do={ :set ($CheckHealthPlugins->[ :jobname ]) do={
:local FuncName [ :tostr $0 ]; :local FuncName [ :tostr $0 ];
:local ScriptName [ :tostr $1 ];
:global CheckHealthLast; :global CheckHealthLast;
:global Identity; :global Identity;
@ -33,13 +32,13 @@
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={ :if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
:if ($CheckHealthLast->$Name = "ok" && \ :if ($CheckHealthLast->$Name = "ok" && \
$Value != "ok") do={ $Value != "ok") do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$FuncName; \
subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \ subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \
message=("The device '" . $Name . "' on " . $Identity . " failed!") }); message=("The device '" . $Name . "' on " . $Identity . " failed!") });
} }
:if ($CheckHealthLast->$Name != "ok" && \ :if ($CheckHealthLast->$Name != "ok" && \
$Value = "ok") do={ $Value = "ok") do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$FuncName; \
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
message=("The device '" . $Name . "' on " . $Identity . " recovered!") }); message=("The device '" . $Name . "' on " . $Identity . " recovered!") });
} }

View file

@ -11,8 +11,7 @@
:global CheckHealthPlugins; :global CheckHealthPlugins;
:set ($CheckHealthPlugins->[ :jobname ]) do={ :set ($CheckHealthPlugins->[ :jobname ]) do={
:local FuncName [ :tostr $0 ]; :local FuncName [ :tostr $0 ];
:local ScriptName [ :tostr $1 ];
:global CheckHealthLast; :global CheckHealthLast;
:global CheckHealthTemperature; :global CheckHealthTemperature;
@ -55,7 +54,7 @@
} }
:if ($Value > $CheckHealthTemperature->$Name && \ :if ($Value > $CheckHealthTemperature->$Name && \
$CheckHealthTemperatureNotified->$Name != true) do={ $CheckHealthTemperatureNotified->$Name != true) do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$FuncName; \
subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \ subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \
message=("The " . $Name . " on " . $Identity . " is above threshold: " . \ message=("The " . $Name . " on " . $Identity . " is above threshold: " . \
$Value . "\C2\B0" . "C") }); $Value . "\C2\B0" . "C") });
@ -63,7 +62,7 @@
} }
:if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \ :if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \
$CheckHealthTemperatureNotified->$Name = true) do={ $CheckHealthTemperatureNotified->$Name = true) do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$FuncName; \
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \ message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \
$Value . "\C2\B0" . "C") }); $Value . "\C2\B0" . "C") });

View file

@ -11,8 +11,7 @@
:global CheckHealthPlugins; :global CheckHealthPlugins;
:set ($CheckHealthPlugins->[ :jobname ]) do={ :set ($CheckHealthPlugins->[ :jobname ]) do={
:local FuncName [ :tostr $0 ]; :local FuncName [ :tostr $0 ];
:local ScriptName [ :tostr $1 ];
:global CheckHealthLast; :global CheckHealthLast;
:global CheckHealthVoltageLow; :global CheckHealthVoltageLow;
@ -40,7 +39,7 @@
:if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \ :if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \
$NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={ $NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$FuncName; \
subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \ subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \
$NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \ $NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \
message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \ message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \
@ -48,12 +47,12 @@
[ $FormatLine "new value" ($Value . " V") 12 ]) }); [ $FormatLine "new value" ($Value . " V") 12 ]) });
} else={ } else={
:if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={ :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$FuncName; \
subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \ subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \
message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") }); message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") });
} }
:if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={ :if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$FuncName; \
subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \ subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \
message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") }); message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") });
} }

View file

@ -92,16 +92,16 @@
:onerror Err { :onerror Err {
/system/script/run $Plugin; /system/script/run $Plugin;
} do={ } do={
$LogPrint error $ScriptName ("Plugin '" . $PluginVal->"name" . "' failed to run: " . $Err); $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run: " . $Err);
} }
} else={ } else={
$LogPrint error $ScriptName ("Plugin '" . $PluginVal->"name" . "' failed syntax validation, skipping."); $LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping.");
} }
} }
:foreach PluginName,Discard in=$CheckHealthPlugins do={ :foreach PluginName,Discard in=$CheckHealthPlugins do={
($CheckHealthPlugins->$PluginName) \ ($CheckHealthPlugins->$PluginName) \
("\$CheckHealthPlugins->\"" . $PluginName . "\"") $ScriptName; ("\$CheckHealthPlugins->\"" . $PluginName . "\"");
} }
:set CheckHealthPlugins; :set CheckHealthPlugins;

View file

@ -42,7 +42,7 @@
$LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!"); $LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!");
:if ($SentCertificateNotification != "expired") do={ :if ($SentCertificateNotification != "expired") do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "scroll,cross-mark" ] . "License expired!"); \ subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \
message=("Your license expired on " . ($License->"deadline-at") . \ message=("Your license expired on " . ($License->"deadline-at") . \
", can no longer update RouterOS on " . $Identity . "...") }); ", can no longer update RouterOS on " . $Identity . "...") });
:set SentCertificateNotification "expired"; :set SentCertificateNotification "expired";
@ -55,7 +55,7 @@
$LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!"); $LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!");
:if ($SentCertificateNotification != "warning") do={ :if ($SentCertificateNotification != "warning") do={
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "scroll,warning-sign" ] . "License about to expire!"); \ subject=([ $SymbolForNotification "warning-sign" ] . "License about to expire!"); \
message=("Your license failed to renew and is about to expire on " . \ message=("Your license failed to renew and is about to expire on " . \
($License->"deadline-at") . " on " . $Identity . "...") }); ($License->"deadline-at") . " on " . $Identity . "...") });
:set SentCertificateNotification "warning"; :set SentCertificateNotification "warning";
@ -68,7 +68,7 @@
[ :totime ($License->"deadline-at") ] - 4w > [ :timestamp ]) do={ [ :totime ($License->"deadline-at") ] - 4w > [ :timestamp ]) do={
$LogPrint info $ScriptName ("Your license was successfully renewed."); $LogPrint info $ScriptName ("Your license was successfully renewed.");
$SendNotification2 ({ origin=$ScriptName; \ $SendNotification2 ({ origin=$ScriptName; \
subject=([ $SymbolForNotification "scroll,white-heavy-check-mark" ] . "License renewed"); \ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "License renewed"); \
message=("Your license was successfully renewed on " . $Identity . \ message=("Your license was successfully renewed on " . $Identity . \
". It is now valid until " . ($License->"deadline-at") . ".") }); ". It is now valid until " . ($License->"deadline-at") . ".") });
:set SentCertificateNotification; :set SentCertificateNotification;

View file

@ -28,7 +28,6 @@
:global EscapeForRegEx; :global EscapeForRegEx;
:global FetchUserAgentStr; :global FetchUserAgentStr;
:global LogPrint; :global LogPrint;
:global RebootForUpdate;
:global ScriptFromTerminal; :global ScriptFromTerminal;
:global ScriptLock; :global ScriptLock;
:global SendNotification2; :global SendNotification2;
@ -39,11 +38,14 @@
:local DoUpdate do={ :local DoUpdate do={
:local ScriptName [ :tostr $1 ]; :local ScriptName [ :tostr $1 ];
:global LogPrint;
:if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={ :if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={
/system/script/run packages-update; /system/script/run packages-update;
} else={ } else={
/system/package/update/install without-paging; /system/package/update/install without-paging;
} }
$LogPrint info $ScriptName ("Waiting for system to reboot.");
} }
:if ([ $ScriptLock $ScriptName ] = false) do={ :if ([ $ScriptLock $ScriptName ] = false) do={
@ -60,14 +62,9 @@
$WaitFullyConnected; $WaitFullyConnected;
:if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={ :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={
:if ([ :typeof $RebootForUpdate ] = "nothing") do={ $LogPrint info $ScriptName ("A reboot for update is already scheduled.");
$LogPrint info $ScriptName ("Found a stale scheduler for reboot, removing."); :set ExitOK true;
/system/scheduler/remove "_RebootForUpdate"; :error false;
} else={
$LogPrint info $ScriptName ("A reboot for update is already scheduled.");
:set ExitOK true;
:error false;
}
} }
$LogPrint debug $ScriptName ("Checking for updates..."); $LogPrint debug $ScriptName ("Checking for updates...");

View file

@ -1,17 +0,0 @@
# Makefile
HTML := $(shell grep -xl '<!-- static html //-->' *.html)
.PHONY: all docs clean
all: docs
badges.html: badges.md
markdown $< > $@
docs: static-html.sh $(HTML) badges.html
./static-html.sh $(HTML)
clean:
rm -f badges.html
git checkout HEAD -- $(HTML)

View file

@ -1,6 +0,0 @@
[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.15-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)

View file

@ -6,4 +6,4 @@ set -e
md5sum $(find -name '*.rsc' | sort) | \ md5sum $(find -name '*.rsc' | sort) | \
sed -e "s| \./||" -e 's|.rsc$||' | \ sed -e "s| \./||" -e 's|.rsc$||' | \
jq --raw-input --null-input '[ inputs | split (" ") | { (.[1]): (.[0]) }] | add' jq --raw-input --null-input '[ inputs | split (" ") | { (.[1]): (.[0]) }] | add' > 'checksums.json'

View file

@ -1,6 +0,0 @@
#!/bin/sh
sed \
-e "/^:global CommitId/c :global CommitId \"${COMMITID:-unknown}\";" \
-e "/^:global CommitInfo/c :global CommitInfo \"${COMMITINFO:-unknown}\";" \
< "${1}"

View file

@ -1,23 +0,0 @@
#!/bin/sh
set -e
RELTO="$(dirname "${1}")"
sed \
-e "s|__TITLE__|$(head -n1 "${1}")|" \
-e "s|__GENERAL__|$(realpath --relative-to="${RELTO}" general/)|" \
-e "s|__ROOT__|$(realpath --relative-to="${RELTO}" ./)|" \
< "${0}.d/head.html"
markdown -f toc,idanchor "${1}" | sed \
-e 's/href="\([-_\./[:alnum:]]*\)\.md\(#[-[:alnum:]]*\)\?"/href="\1.html\2"/g' \
-e '/<h[1234] /s| id="\(.*\)">| id="\L\1">|' \
-e '/<h[1234] /s|-2[1789cd]-||g' -e '/<h[1234] /s|--26-amp-3b-||g' \
-e '/^<pre>/s|pre|pre class="code" onclick="CopyToClipboard(this)"|g' \
-e '/The above link may be broken on code hosting sites/s|blockquote|blockquote style="display: none;"|'
sed \
-e "s|__DATE__|${DATE:-$(date --rfc-email)}|" \
-e "s|__VERSION__|${VERSION:-unknown}|" \
< "${0}.d/foot.html"

View file

@ -1,5 +0,0 @@
<p class="foot">RouterOS Scripts documentation generated on <i>__DATE__</i> for <i>__VERSION__</i><br />
Copyright &copy; 2013-2025 Christian Hesse &lt;mail@eworm.de&gt;</p>
</body></html>

View file

@ -1,16 +0,0 @@
<!DOCTYPE html><html lang="en">
<head><meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>RouterOS Scripts :: __TITLE__</title>
<link rel="stylesheet" type="text/css" href="__GENERAL__/style.css">
<link rel="icon" type="image/png" href="__ROOT__/logo.png">
<script type="text/javascript" src="__GENERAL__/clipboard.js"></script>
</head><body>
<table><tr>
<td><img src="__GENERAL__/eworm-meadow.avif" alt="eworm on meadow" /></td>
<td><img src="__GENERAL__/qr-code.png" alt="QR code: rsc.eworm.de" /></td>
<td class="head"><span class="top">RouterOS Scripts</span><br />
<span class="bottom">a collection of scripts for MikroTik RouterOS</span></td>
</tr></table>
<hr />

View file

@ -0,0 +1,5 @@
body {
font-family: fira-sans, sans-serif;
font-size: 10pt;
background-color: transparent;
}

View file

@ -1,30 +1,14 @@
<!DOCTYPE html><html lang="en"> <!DOCTYPE html>
<!-- static html //--> <html lang="en">
<head><meta http-equiv="content-type" content="text/html; charset=UTF-8"> <head>
<title>RouterOS Scripts :: Logo Color Changer</title> <meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="../general/style.css"> <title>RouterOS-Scripts Logo Color Changer</title>
<link rel="icon" type="image/png" href="../logo.png"> <link rel="stylesheet" type="text/css" href="logo-color.d/style.css">
<script src="logo-color.d/script.js"></script> <script src="logo-color.d/script.js"></script>
</head><body> </head>
<body>
<table><tr> <h1>RouterOS-Scripts Logo Color Changer</h1>
<td><img src="../general/eworm-meadow.avif" alt="eworm on meadow" /></td>
<td><img src="../general/qr-code.png" alt="QR code: rsc.eworm.de" /></td>
<td class="head"><span class="top">RouterOS Scripts</span><br />
<span class="bottom">a collection of scripts for MikroTik RouterOS</span></td>
</tr></table>
<hr />
<h1>Logo Color Changer</h1>
<!-- badges here //-->
<p><a href="../README.md">⬅️ Go back to main README</a></p>
<blockquote style="/* display */"><p>💡️ <strong>Hint</strong>: This site or links
on it may be broken on code hosting sites. Use
<a href="https://rsc.eworm.de/main/contrib/logo-color.html">Logo Color Changer</a>
instead.</p></blockquote>
<p>You want the logo for your own notifications? But you joined the <p>You want the logo for your own notifications? But you joined the
<a href="https://t.me/routeros_scripts">Telegram Group</a> and want <a href="https://t.me/routeros_scripts">Telegram Group</a> and want
@ -40,23 +24,17 @@ something that differentiates? Color it!</p>
<p>Then right-click, click "<i>Take Screenshot</i>" and finally select the <p>Then right-click, click "<i>Take Screenshot</i>" and finally select the
logo and download it.</p> logo and download it.</p>
<p><img src="logo-color.d/browser-01.avif" alt="Screenshot Browser 01"></p> <p><img src="logo-color.d/browser-01.avif" width=533 height=482 alt="Screenshot Browser 01">
<p><img src="logo-color.d/browser-02.avif" alt="Screenshot Browser 02"></p> <img src="logo-color.d/browser-02.avif" width=533 height=482 alt="Screenshot Browser 02">
<p><img src="logo-color.d/browser-03.avif" alt="Screenshot Browser 03"></p> <img src="logo-color.d/browser-03.avif" width=533 height=482 alt="Screenshot Browser 03"></p>
<p>(This example is with <p>(This example is with
<a href="https://www.mozilla.org/de/firefox/new/">Firefox</a>. The workflow <a href="https://www.mozilla.org/de/firefox/new/">Firefox</a>. The workflow
for other browsers may differ.)</p> for other browsers may differ.)</p>
<p>See how to <p>See how to
<a href="../doc/mod/notification-telegram.md#set-a-profile-photo">Set <a href="../../about/doc/mod/notification-telegram.md#set-a-profile-photo">Set
a profile photo</a> for your Telegram bot.</p> a profile photo</a> for your Telegram bot.</p>
<hr /> </body>
</html>
<p><a href="../README.md">⬅️ Go back to main README</a><br/>
<a href="#top">⬆️ Go back to top</a></p>
<p class="foot">Copyright &copy; 2013-2025 Christian Hesse &lt;mail@eworm.de&gt;</p>
</body></html>

View file

@ -0,0 +1,36 @@
body {
font-family: fira-sans, sans-serif;
font-size: 10pt;
background-color: transparent;
}
div.notification {
position: relative;
float: right;
width: 600px;
border: 3px outset #6c5d53;
/* border-radius: 5px; */
padding: 10px;
background-color: #e6e6e6;
}
div.content {
padding-left: 60px;
}
img.logo {
float: left;
border-radius: 50%;
}
p.heading {
margin: 0px;
font-weight: bold;
text-decoration: underline;
}
p.hint {
display: none;
}
pre {
font-family: fira-mono, monospace;
white-space: pre-wrap;
}
span.link {
color: #863600;
}

View file

@ -1,57 +1,35 @@
<!DOCTYPE html><html lang="en"> <!DOCTYPE html>
<!-- static html //--> <html lang="en">
<head><meta http-equiv="content-type" content="text/html; charset=UTF-8"> <head>
<title>RouterOS Scripts :: Notification Generator</title> <meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="../general/style.css"> <title>RouterOS-Scripts Notification Generator</title>
<link rel="icon" type="image/png" href="../logo.png"> <link rel="stylesheet" type="text/css" href="notification.d/style.css">
<script src="notification.d/script.js"></script> <script src="notification.d/script.js"></script>
</head><body> </head>
<body>
<table><tr> <h1>RouterOS-Scripts Notification Generator</h1>
<td><img src="../general/eworm-meadow.avif" alt="eworm on meadow" /></td>
<td><img src="../general/qr-code.png" alt="QR code: rsc.eworm.de" /></td>
<td class="head"><span class="top">RouterOS Scripts</span><br />
<span class="bottom">a collection of scripts for MikroTik RouterOS</span></td>
</tr></table>
<hr />
<h1>Notification Generator</h1>
<!-- badges here //-->
<p><a href="../README.md">⬅️ Go back to main README</a></p>
<blockquote style="/* display */"><p>💡️ <strong>Hint</strong>: This site or links
on it may be broken on code hosting sites. Use
<a href="https://rsc.eworm.de/main/contrib/notification.html">Notification Generator</a>
instead.</p></blockquote>
<div class="notification"> <div class="notification">
<img src="../logo.svg" alt="logo" class="logo" width=48 height=48> <img src="../logo.svg" alt="logo" class="logo" width=48 height=48>
<div class="content"> <div class="content">
<p id="heading" class="heading">[<span id="hostname">MikroTik</span>] <span id="subject"> Subject</span></p> <p id="heading" class="heading">[<span id="hostname">MikroTik</span>] <span id="subject"> Subject</span></p>
<pre id="message">Message</pre> <pre id="message">Message</pre>
<p id="link" class="hint">🔗 <span id="link-text" class="link">https://rsc.eworm.de/</span></p> <p id="link" class="hint">🔗 <span id="link-text" class="link">https://eworm.de/</span></p>
<p id="queued" class="hint">⏰ This message was queued since <i><span id="queued-since">2025-10-29 16:06:18</span></i> and may be obsolete.</p> <p id="queued" class="hint">⏰ This message was queued since <span id="queued-since">oct/18/2022 18:30:48</span> and may be obsolete.</p>
<p id="cut" class="hint">✂️ The message was too long and has been truncated, cut off <i><span id="cut-percent">13</span>%</i>!</p> <p id="cut" class="hint">✂️ The message was too long and has been truncated, cut off <span id="cut-percent">13</span>%!</p>
</div> </div>
</div> </div>
<p>Hostname: <input type="text" value="MikroTik" onchange="update(this, 'hostname')"></p> <p>Hostname: <input type="text" value="MikroTik" onchange="update(this, 'hostname')"></p>
<p>Subject: <input type="text" size=50 value=" Subject" onchange="update(this, 'subject')"></p> <p>Subject: <input type="text" size=50 value=" Subject" onchange="update(this, 'subject')"></p>
<p>Message: <textarea id="w3review" name="w3review" rows="4" cols="50" onchange="update(this, 'message')">Message</textarea></p> <p>Message: <textarea id="w3review" name="w3review" rows="4" cols="50" onchange="update(this, 'message')">Message</textarea></p>
<p><input type="checkbox" onclick="visible(this, 'link')"> Show link: <input type="text" value="https://rsc.eworm.de/" onchange="update(this, 'link-text')"></p> <p><input type="checkbox" onclick="visible(this, 'link')"> Show link: <input type="text" value="https://eworm.de/" onchange="update(this, 'link-text')"></p>
<p><input type="checkbox" onclick="visible(this, 'queued')"> Queued since <input type="text" value="2025-10-29 16:06:18" onchange="update(this, 'queued-since')"></p> <p><input type="checkbox" onclick="visible(this, 'queued')"> Queued since <input type="text" value="oct/18/2022 18:30:48" onchange="update(this, 'queued-since')"></p>
<p><input type="checkbox" onclick="visible(this, 'cut')"> Cut-off with <input type="number" min=1 max=99 value=13 onchange="update(this, 'cut-percent')"> percent</p> <p><input type="checkbox" onclick="visible(this, 'cut')"> Cut-off with <input type="number" min=1 max=99 value=13 onchange="update(this, 'cut-percent')"> percent</p>
<p>Then right-click, click "<i>Take Screenshot</i>" and finally select the <p>Then right-click, click "<i>Take Screenshot</i>" and finally select the
notification and download it.</p> notification and download it.</p>
<hr /> </body>
</html>
<p><a href="../README.md">⬅️ Go back to main README</a><br/>
<a href="#top">⬆️ Go back to top</a></p>
<p class="foot">Copyright &copy; 2013-2025 Christian Hesse &lt;mail@eworm.de&gt;</p>
</body></html>

View file

@ -1,10 +0,0 @@
#!/bin/sh
set -e
sed -i \
-e '/href=/s|\.md|\.html|' \
-e '/blockquote/s|/\* display \*/|display: none;|' \
-e '/<!-- badges here \/\/-->/r badges.html' \
-e '/<!-- badges here \/\/-->/d' \
"${@}"

View file

@ -1,11 +0,0 @@
#!/bin/sh
set -e
sed \
-e '/\/interface\/wifi\//d' \
-e '/\/interface\/wireless\//d' \
-e 's|%TEMPL%|.capsman|' \
-e '/^# NOT \/caps-man\/ #$/,/^# NOT \/caps-man\/ #$/d' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< "${1}"

View file

@ -1,11 +0,0 @@
#!/bin/sh
set -e
sed \
-e '/\/caps-man\//d' \
-e '/\/interface\/wifi\//d' \
-e 's|%TEMPL%|.local|' \
-e '/^# NOT \/interface\/wireless\/ #$/,/^# NOT \/interface\/wireless\/ #$/d' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< "${1}"

View file

@ -1,11 +0,0 @@
#!/bin/sh
set -e
sed \
-e '/\/caps-man\//d' \
-e '/\/interface\/wireless\//d' \
-e 's|%TEMPL%|.wifi|' \
-e '/^# NOT \/interface\/wifi\/ #$/,/^# NOT \/interface\/wifi\/ #$/d' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< "${1}"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -19,10 +19,9 @@ Description
This script tries to download and renew certificates, then notifies about This script tries to download and renew certificates, then notifies about
certificates that are still about to expire. certificates that are still about to expire.
### Sample notifications ### Sample notification
![check-certificates notification warning](check-certificates.d/notification-01-warn.avif) ![check-certificates notification](check-certificates.d/notification.avif)
![check-certificates notification renew](check-certificates.d/notification-02-renew.avif)
Requirements and installation Requirements and installation
----------------------------- -----------------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -20,10 +20,9 @@ On *Cloud Hosted Router* (*CHR*) the licensing is perpetual: Buy once, use
forever - but it needs regular renewal. This script checks licensing state forever - but it needs regular renewal. This script checks licensing state
and sends a notification to warn before expiration. and sends a notification to warn before expiration.
### Sample notifications ### Sample notification
![check-perpetual-license notification warn](check-perpetual-license.d/notification-01-warn.avif) ![check-perpetual-license notification](check-perpetual-license.d/notification.avif)
![check-perpetual-license notification renew](check-perpetual-license.d/notification-02-renew.avif)
Requirements and installation Requirements and installation
----------------------------- -----------------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -31,14 +31,13 @@ automatically is supported.
> of view. At the same time it can be source of serve breakage. So test > of view. At the same time it can be source of serve breakage. So test
> versions in lab and read > versions in lab and read
> [changelog ↗️](https://mikrotik.com/download/changelogs/) and > [changelog ↗️](https://mikrotik.com/download/changelogs/) and
> [forum ↗️](https://forum.mikrotik.com/c/announcements/5) before deploying > [forum ↗️](https://forum.mikrotik.com/viewforum.php?f=21) before deploying
> to your production environment! Automatic updates should be handled > to your production environment! Automatic updates should be handled
> with care! > with care!
### Sample notifications ### Sample notification
![check-routeros-update notification found](check-routeros-update.d/notification-01-found.avif) ![check-routeros-update notification](check-routeros-update.d/notification.avif)
![check-routeros-update notification neighbor](check-routeros-update.d/notification-02-neighbor.avif)
Requirements and installation Requirements and installation
----------------------------- -----------------------------
@ -88,8 +87,6 @@ Be notified when run from scheduler or run it manually:
If an update is found you can install it right away. If an update is found you can install it right away.
![Terminal](check-routeros-update.d/terminal.avif)
Installing script [packages-update](packages-update.md) gives extra options. Installing script [packages-update](packages-update.md) gives extra options.
Tips & Tricks Tips & Tricks

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

View file

@ -33,9 +33,6 @@ certificate is checked.
> ⚠️ **Warning**: The script does not limit the size of a list, but keep in > ⚠️ **Warning**: The script does not limit the size of a list, but keep in
> mind that huge lists can exhaust your device's resources (RAM and CPU), > mind that huge lists can exhaust your device's resources (RAM and CPU),
> and may take a long time to process. > and may take a long time to process.
> Even crashes for the complete scripting (and CLI) subsystem are possible.
> This should be logged accordingly with warnings when global functions are
> reloaded from scheduler.
Requirements and installation Requirements and installation
----------------------------- -----------------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -35,10 +35,9 @@ The script works around the limitations, for example it does:
It is intended to be run periodically from scheduler, then collects new It is intended to be run periodically from scheduler, then collects new
log messages and forwards them via notification. log messages and forwards them via notification.
### Sample notifications ### Sample notification
![log-forward notification info](log-forward.d/notification-01-info.avif) ![log-forward notification](log-forward.d/notification.avif)
![log-forward notification warn](log-forward.d/notification-02-warn.avif)
Requirements and installation Requirements and installation
----------------------------- -----------------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -33,7 +33,7 @@ Call the function `$InspectVar` with a variable as parameter:
$InspectVar $ModeButton; $InspectVar $ModeButton;
![InspectVar](inspectvar.d/01-inspectvar.avif) ![InspectVar](inspectvar.d/inspectvar.avif)
--- ---
[⬅️ Go back to main README](../../README.md) [⬅️ Go back to main README](../../README.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -43,7 +43,7 @@ It expects an IP address in CIDR notation as argument.
$IPCalc 192.168.88.1/24; $IPCalc 192.168.88.1/24;
![IPCalc](ipcalc.d/01-ipcalc.avif) ![IPCalc](ipcalc.d/ipcalc.avif)
### IPCalcReturn ### IPCalcReturn
@ -53,7 +53,7 @@ the information in a named array.
:put ([ $IPCalcReturn 192.168.88.1/24 ]->"broadcast"); :put ([ $IPCalcReturn 192.168.88.1/24 ]->"broadcast");
![IPCalcReturn](ipcalc.d/02-ipcalcreturn.avif) ![IPCalcReturn](ipcalc.d/ipcalcreturn.avif)
--- ---
[⬅️ Go back to main README](../../README.md) [⬅️ Go back to main README](../../README.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before After
Before After

View file

@ -49,7 +49,7 @@ your server in device's certificate store.
The example below is for `matrix.org`, which uses a trust chain from *Google The example below is for `matrix.org`, which uses a trust chain from *Google
Trust Services*. Run this to import the required certificate: Trust Services*. Run this to import the required certificate:
$CertificateAvailable "GTS Root R4" "fetch"; $CertificateAvailable "GTS Root R4";
Replace the CA certificate name with what ever is needed for your server. Replace the CA certificate name with what ever is needed for your server.
You may want to find the You may want to find the

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more