From 1872926d1e292ff360f5c287e490bbb0f5395e51 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 03:20:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 03:20:03. --- .../20260628/20260628032001-Binary.backup | Bin 0 -> 357824 bytes MikroNUC/20260628/20260628032001-Config.rsc | 4823 +++++++++++++++++ .../20260628/20260628032001-Diff-Config.rsc | 141 + 3 files changed, 4964 insertions(+) create mode 100644 MikroNUC/20260628/20260628032001-Binary.backup create mode 100644 MikroNUC/20260628/20260628032001-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628032001-Diff-Config.rsc diff --git a/MikroNUC/20260628/20260628032001-Binary.backup b/MikroNUC/20260628/20260628032001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..b1b9af3b561cb65f596bd327e597ac53391f7d05 GIT binary patch literal 357824 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 sk__ZHhT9X8I+_jlv@hg2*h-pV$nA50+ke! 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%0AWduumq4BfYuk2w(AXr- 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

m@Atr~w0$DB!a9mn{IDadYN0-?!9 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 WuHRDmW4g;j$riU*q5XA|y8jEO=qs)O literal 0 HcmV?d00001 diff --git a/MikroNUC/20260628/20260628032001-Config.rsc b/MikroNUC/20260628/20260628032001-Config.rsc new file mode 100644 index 0000000..11c8a45 --- /dev/null +++ b/MikroNUC/20260628/20260628032001-Config.rsc @@ -0,0 +1,4823 @@ +# 2026-06-28 03:20:02 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 +/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 +/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 +/routing table +add disabled=no fib name=r_to_vpn +/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\"]" +/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 username=amsternl +/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 +/ip address +add address=10.30.30.10/24 interface=ether2 network=10.30.30.0 +/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 +/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 +/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/20260628032001-Diff-Config.rsc b/MikroNUC/history/20260628/20260628032001-Diff-Config.rsc new file mode 100644 index 0000000..b98218d --- /dev/null +++ b/MikroNUC/history/20260628/20260628032001-Diff-Config.rsc @@ -0,0 +1,141 @@ +26a27,35 +> /interface veth +> add address=172.18.20.2/30 container-mac-address=44:F2:90:E8:88:95 dhcp=no \ +> gateway=172.18.20.1 gateway6="" mac-address=44:F2:90:E8:88:94 name=\ +> docker-awg-veth +> add address=172.18.0.2/30 container-mac-address=14:8C:F8:B8:02:D1 dhcp=no \ +> gateway=172.18.0.1 gateway6="" mac-address=14:8C:F8:B8:02:D0 name=\ +> veth-awg-proxy +> /interface wireguard +> add listen-port=14587 mtu=1420 name=wg-awg-MikroNuc +54a64,68 +> /container +> add envlists=awg-proxy-env file=sata1/tmp/awg-proxy-amd64.tar.gz hostname=\ +> awg-proxy interface=veth-awg-proxy layer-dir=/sata1 logging=yes name=\ +> awg-proxy-amd64 root-dir=/sata1/awg-proxy shm-size=4096.0KiB \ +> start-on-boot=yes +4654a4669,4740 +> 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 \"Uninstalling AmneziaWG Standalone...\"\ +> \n :log info \"Uninstalling AmneziaWG Standalone...\"\ +> \n :local prevDNS \"\"\ +> \n :do { :set prevDNS [/system/script/get [find where name=awg-MikroNuc-p\ +> rev-dns] comment] } on-error={}\ +> \n :if ([:len \$prevDNS] > 0) do={\ +> \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ +> \n :put (\"DNS restored to: \$prevDNS\")\ +> \n } else={ :put \"No previous DNS to restore.\" }\ +> \n :do { /system/script/remove [find where name=awg-MikroNuc-prev-dns] } \ +> on-error={}\ +> \n /ip/route/remove [find where comment=awg-MikroNuc-tunnel]\ +> \n /ip/route/remove [find where comment=awg-MikroNuc-dns]\ +> \n :do { /ip/route/remove [find where comment~\"awg-MikroNuc\"] } on-erro\ +> r={}\ +> \n :do { /ip/dns/static/remove [find where comment~\"awg-MikroNuc\"] } on\ +> -error={}\ +> \n :do { /ip/dns/forwarders/remove [find where comment~\"awg-MikroNuc\"] \ +> } on-error={}\ +> \n :do { /ip/firewall/mangle/remove [find where comment~\"awg-MikroNuc\"]\ +> \_} on-error={}\ +> \n :do { /routing/rule/remove [find where comment~\"awg-MikroNuc\"] } on-\ +> error={}\ +> \n :do { /routing/table/remove [find where comment~\"awg-MikroNuc\"] } on\ +> -error={}\ +> \n :do { /ip/firewall/address-list/remove [find where list=RU and comment\ +> =RIPE-RU] } on-error={}\ +> \n :do { /ip/firewall/address-list/remove [find where comment~\"awg-Mikro\ +> Nuc-private\"] } on-error={}\ +> \n :do { /ip/firewall/address-list/remove [find where list=\"awg-MikroNuc\ +> -vpn-server\"] } on-error={}\ +> \n :do { /ip/firewall/address-list/remove [find where comment~\"awg-Mikro\ +> Nuc-excl-ip\"] } on-error={}\ +> \n :do { /system/scheduler/remove [find where comment~\"awg-MikroNuc\"] }\ +> \_on-error={}\ +> \n :do { /tool/netwatch/remove [find where comment~\"awg-MikroNuc\"] } on\ +> -error={}\ +> \n :do { /system/script/remove [find where comment~\"awg-MikroNuc\" and n\ +> ame!=\"awg-MikroNuc-uninstall\"] } on-error={}\ +> \n :foreach rule in=[/ip/firewall/filter/find where comment~\"disabled-by\ +> -awg-MikroNuc\"] do={\ +> \n :local c [/ip/firewall/filter/get \$rule comment]\ +> \n :local origComment [:pick \$c ([:find \$c \"|\"] + 1) [:len \$c]]\ +> \n /ip/firewall/filter/set \$rule disabled=no comment=\$origComment\ +> \n }\ +> \n :foreach rule in=[/ip/firewall/filter/find where comment~\"ft-modified\ +> -by-awg-MikroNuc\"] do={\ +> \n :local c [/ip/firewall/filter/get \$rule comment]\ +> \n :local origComment [:pick \$c ([:find \$c \"|\"] + 1) [:len \$c]]\ +> \n /ip/firewall/filter/set \$rule out-interface-list=\"\" comment=\$ori\ +> gComment\ +> \n }\ +> \n /ip/firewall/nat/remove [find where out-interface=\"wg-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=\"disk1/ru_ranges_timeout.rsc\"] }\ +> \_on-error={}\ +> \n :do { /file/remove [find where name=\"usb1/ru_ranges_timeout.rsc\"] } \ +> on-error={}\ +> \n :put \"Uninstall AmneziaWG Proxy Standalone complete!\"\ +> \n :log info \"Uninstall AmneziaWG Proxy Standalone complete!\"\ +> \n /system/script/remove [find where name=awg-MikroNuc-uninstall]\ +> \n" +> add comment=10.9.0.1 dont-require-permissions=no name=awg-MikroNuc-prev-dns \ +> owner=vados policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\ +> "" +4702,4703c4788,4813 +< set layer-dir=/sata1 memory-high=200.0MiB memory-max=4096.0MiB registry-url=\ +< https://registry-1.docker.io username=amsternl +--- +> set layer-dir=/sata1 memory-high=4096.0MiB memory-max=4096.0MiB registry-url=\ +> https://registry-1.docker.io tmpdir=/tmp username=amsternl +> /container envs +> add key=AWG_CLIENT_PUB list=awg-proxy-env value=\ +> "NAonhRSuFIrFpNKTs5X32ItcvFuO1kiJUigQZb95YlE=" +> add key=AWG_H1 list=awg-proxy-env value=472437748-472438637 +> add key=AWG_H2 list=awg-proxy-env value=707808381-707809250 +> add key=AWG_H3 list=awg-proxy-env value=1609078552-1609079495 +> add key=AWG_H4 list=awg-proxy-env value=1012329484-1012329741 +> add key=AWG_I1 list=awg-proxy-env value=\ +> "" +> add key=AWG_I2 list=awg-proxy-env value="" +> add key=AWG_I3 list=awg-proxy-env value="" +> add key=AWG_I4 list=awg-proxy-env value="" +> add key=AWG_I5 list=awg-proxy-env value="" +> add key=AWG_JC list=awg-proxy-env value=9 +> add key=AWG_JMAX list=awg-proxy-env value=920 +> add key=AWG_JMIN list=awg-proxy-env value=105 +> add key=AWG_LISTEN list=awg-proxy-env value=:55055 +> add key=AWG_REMOTE list=awg-proxy-env value=109.107.177.79:55055 +> add key=AWG_S1 list=awg-proxy-env value=90 +> add key=AWG_S2 list=awg-proxy-env value=136 +> add key=AWG_S3 list=awg-proxy-env value=45 +> add key=AWG_S4 list=awg-proxy-env value=27 +> add key=AWG_SERVER_PUB list=awg-proxy-env value=\ +> "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +4719c4829 +< add interface=*C6 list=WAN +--- +> add interface=docker-awg-veth list=WAN +4722a4833,4836 +> /interface wireguard peers +> add allowed-address=0.0.0.0/0 endpoint-address=10.30.30.10 endpoint-port=\ +> 51820 interface=wg-awg-MikroNuc name=peer3 persistent-keepalive=10s \ +> public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +4724a4839,4841 +> add address=10.9.0.3/24 interface=docker-awg-veth network=10.9.0.0 +> add address=172.18.0.1/30 interface=veth-awg-proxy network=172.18.0.0 +> add address=10.9.0.3 interface=wg-awg-MikroNuc network=10.9.0.3 +4732c4849 +< doh-max-server-connections=20 servers=10.9.0.1 verify-doh-cert=yes +--- +> doh-max-server-connections=20 servers=10.30.30.81 verify-doh-cert=yes +4743a4861,4864 +> add disabled=no distance=1 dst-address=0.0.0.0/0 gateway=docker-awg-veth \ +> routing-table=r_to_vpn scope=30 target-scope=10 +> add comment=awg-MikroNuc-dns distance=1 dst-address=10.9.0.1/32 gateway=\ +> wg-awg-MikroNuc -- 2.52.0