From 85e5ab5647e37bcb3a2d8db7ffd1f9ac76c75587 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 04:00:04 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 04:00:04. --- .../20260628/20260628040001-Binary.backup | Bin 0 -> 369904 bytes MikroNUC/20260628/20260628040001-Config.rsc | 4917 +++++++++++++++++ .../20260628/20260628040001-Diff-Config.rsc | 17 + 3 files changed, 4934 insertions(+) create mode 100644 MikroNUC/20260628/20260628040001-Binary.backup create mode 100644 MikroNUC/20260628/20260628040001-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628040001-Diff-Config.rsc diff --git a/MikroNUC/20260628/20260628040001-Binary.backup b/MikroNUC/20260628/20260628040001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..c29f99de13655567f05b94f23c4e0c2507ba6c16 GIT binary patch literal 369904 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 E0eAu*$^ZZW literal 0 HcmV?d00001 diff --git a/MikroNUC/20260628/20260628040001-Config.rsc b/MikroNUC/20260628/20260628040001-Config.rsc new file mode 100644 index 0000000..6cb7540 --- /dev/null +++ b/MikroNUC/20260628/20260628040001-Config.rsc @@ -0,0 +1,4917 @@ +# 2026-06-28 04:00:03 by RouterOS 7.23.1 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no name=ether2 +/interface veth +add address=172.28.232.14/30 container-mac-address=22:A6:30:44:4A:A0 dhcp=no \ + gateway=172.28.232.13 gateway6="" mac-address=22:A6:30:44:4A:9F name=\ + veth-awg-MikroNuc +/interface wireguard +add listen-port=14587 mtu=1420 name=wg-awg-MikroNuc +add listen-port=22022 mtu=1360 name=wg-wg-Master +/caps-man datapath +add bridge=Bridge-LAN client-to-client-forwarding=yes local-forwarding=yes \ + name=xs-DataPath +add bridge=Bridge-LAN client-to-client-forwarding=yes local-forwarding=yes \ + name=DataPath +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/container +add comment=awg-MikroNuc envlists=awg-MikroNuc-env file=\ + sata1/pull/awg-proxy-amd64.tar.gz hostname=awg-MikroNuc interface=\ + veth-awg-MikroNuc layer-dir=/sata1 name=awg-proxy-amd64 root-dir=\ + /sata1/awg-MikroNuc shm-size=4096.0KiB start-on-boot=yes +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\r\ + \n#\r\ + \n#root.mnuc.backuper\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add comment=awg-MikroNuc dont-require-permissions=no name=\ + awg-MikroNuc-uninstall owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-MikroNu\ + c-env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-Mikr\ + oNuc-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n /ip/route/remove [find where comment=awg-MikroNuc-tunnel]\ + \n /ip/route/remove [find where comment=awg-MikroNuc-dns]\ + \n /container/stop [find where interface=veth-awg-MikroNuc]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-MikroNuc]\ + \n /container/envs/remove [find where list=\"awg-MikroNuc-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-MikroNuc\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.28.232.12/30\"]\ + \n /ip/address/remove [find where address=\"172.28.232.13/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-MikroNuc\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-Mikro\ + Nuc\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-MikroNuc\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-MikroNuc\"]\ + \n :do { /file/remove [find where name~\"awg-MikroNuc.+tar\"] } on-error=\ + {}\ + \n :do { /file/remove [find where name=\"sata1/awg-MikroNuc\"] } on-error\ + ={}\ + \n :do { /file/remove [find where name=\"sata1/ru_ranges_timeout.rsc\"] }\ + \_on-error={}\ + \n :do { /file/remove [find where name~\"sata1/pull\"] } on-error={}\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-MikroNuc-uninstall]\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=Bridge-LAN registry-mirrors="https://re\ + g.vados.ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1 memory-high=200.0MiB memory-max=4096.0MiB registry-url=\ + https://registry-1.docker.io tmpdir=/sata1/pull username=amsternl +/container envs +add key=AWG_CLIENT_PUB list=awg-MikroNuc-env value=\ + "NAonhRSuFIrFpNKTs5X32ItcvFuO1kiJUigQZb95YlE=" +add key=AWG_H1 list=awg-MikroNuc-env value=472437748-472438637 +add key=AWG_H2 list=awg-MikroNuc-env value=707808381-707809250 +add key=AWG_H3 list=awg-MikroNuc-env value=1609078552-1609079495 +add key=AWG_H4 list=awg-MikroNuc-env value=1012329484-1012329741 +add key=AWG_I1 list=awg-MikroNuc-env value=\ + "" +add key=AWG_I2 list=awg-MikroNuc-env value="" +add key=AWG_I3 list=awg-MikroNuc-env value="" +add key=AWG_I4 list=awg-MikroNuc-env value="" +add key=AWG_I5 list=awg-MikroNuc-env value="" +add key=AWG_JC list=awg-MikroNuc-env value=9 +add key=AWG_JMAX list=awg-MikroNuc-env value=920 +add key=AWG_JMIN list=awg-MikroNuc-env value=105 +add key=AWG_LISTEN list=awg-MikroNuc-env value=:51820 +add key=AWG_PREV_DNS list=awg-MikroNuc-env value=10.9.0.1 +add key=AWG_PREV_TMPDIR list=awg-MikroNuc-env value="" +add key=AWG_REMOTE list=awg-MikroNuc-env value=109.107.177.79:55055 +add key=AWG_S1 list=awg-MikroNuc-env value=90 +add key=AWG_S2 list=awg-MikroNuc-env value=136 +add key=AWG_S3 list=awg-MikroNuc-env value=45 +add key=AWG_S4 list=awg-MikroNuc-env value=27 +add key=AWG_SERVER_PUB list=awg-MikroNuc-env value=\ + "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/interface bridge port +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=*C4 +add bridge=Bridge-LAN interface=*C2 +add bridge=Bridge-LAN interface=*C5 +add bridge=Bridge-LAN interface=*C1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=Internet internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=lo list=LAN +add interface=ether2 list=Internet +add interface=*C6 list=WAN +add interface=ether2 list=WAN +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 endpoint-address=172.28.232.14 endpoint-port=\ + 51820 interface=wg-awg-MikroNuc name=peer8 persistent-keepalive=10s \ + public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +add allowed-address=0.0.0.0/0 client-address=10.1.0.1/24 \ + client-allowed-address=10.1.0.2/32 client-dns=10.1.0.1 client-endpoint=\ + 10.30.30.10 client-keepalive=15s endpoint-address=10.30.30.10 \ + endpoint-port=22022 interface=wg-wg-Master name=MasterPeer \ + persistent-keepalive=15s public-key=\ + "64Y25EUZQQt8EIeAJYD8qWt+te8qvGEepnswdBgmzlY=" responder=yes +/ip address +add address=10.30.30.10/24 interface=ether2 network=10.30.30.0 +add address=172.28.232.13/30 interface=veth-awg-MikroNuc network=\ + 172.28.232.12 +add address=10.9.0.3 interface=wg-awg-MikroNuc network=10.9.0.3 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=Bridge-LAN \ + lease-script=lease-script lease-time=10m name=nucDHCP server-address=\ + 10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.9.0.1 verify-doh-cert=yes +/ip firewall nat +add action=masquerade chain=srcnat src-address=172.18.0.0/30 +add action=masquerade chain=srcnat src-address=172.28.232.12/30 +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=bfd disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +add comment=awg-MikroNuc-dns distance=1 dst-address=10.9.0.1/32 gateway=\ + wg-awg-MikroNuc +/ip service +set ftp disabled=yes +set www disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both password-authentication=no \ + publickey-authentication-options=verify-required strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260628/20260628040001-Diff-Config.rsc b/MikroNUC/history/20260628/20260628040001-Diff-Config.rsc new file mode 100644 index 0000000..930e2c4 --- /dev/null +++ b/MikroNUC/history/20260628/20260628040001-Diff-Config.rsc @@ -0,0 +1,17 @@ +33c33 +< add listen-port=22022 mtu=1360 name=wg-wg-Master +--- +> add listen-port=23934 mtu=1360 name=wg-wg-Master +4805,4810c4805,4809 +< add allowed-address=0.0.0.0/0 client-address=10.1.0.1/24 \ +< client-allowed-address=10.1.0.2/32 client-dns=10.1.0.1 client-endpoint=\ +< 10.30.30.10 client-keepalive=15s endpoint-address=10.30.30.10 \ +< endpoint-port=22022 interface=wg-wg-Master name=MasterPeer \ +< persistent-keepalive=15s public-key=\ +< "64Y25EUZQQt8EIeAJYD8qWt+te8qvGEepnswdBgmzlY=" responder=yes +--- +> add allowed-address=0.0.0.0/0 client-address=10.1.0.2/32 \ +> client-allowed-address=0.0.0.0/32 client-dns=10.30.30.10 \ +> client-keepalive=15s endpoint-address=10.30.30.10 endpoint-port=22022 \ +> interface=wg-wg-Master name=MasterPeer persistent-keepalive=15s \ +> public-key="64Y25EUZQQt8EIeAJYD8qWt+te8qvGEepnswdBgmzlY=" responder=yes -- 2.52.0