From 6a05231a2dad98f8f09d9cf669fc1989f5ae1383 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 17 Aug 2026 00:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Aug 17 00:40:03. --- .../20260817/20260817004001-Binary.backup | Bin 0 -> 434796 bytes MikroNUC/20260817/20260817004001-Config.rsc | 5539 +++++++++++++++++ .../20260817/20260817004001-Diff-Config.rsc | 9 + 3 files changed, 5548 insertions(+) create mode 100644 MikroNUC/20260817/20260817004001-Binary.backup create mode 100644 MikroNUC/20260817/20260817004001-Config.rsc create mode 100644 MikroNUC/history/20260817/20260817004001-Diff-Config.rsc diff --git a/MikroNUC/20260817/20260817004001-Binary.backup b/MikroNUC/20260817/20260817004001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..d405546d1d95a0f24a89e47f49da7e24b6c3b866 GIT binary patch literal 434796 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%0Akr7e6^|*G=i0iVn>uK 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 literal 0 HcmV?d00001 diff --git a/MikroNUC/20260817/20260817004001-Config.rsc b/MikroNUC/20260817/20260817004001-Config.rsc new file mode 100644 index 0000000..2316fb8 --- /dev/null +++ b/MikroNUC/20260817/20260817004001-Config.rsc @@ -0,0 +1,5539 @@ +# 2026-08-17 00:40:01 by RouterOS 7.23.3 +# 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 +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/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 +/disk +add file-path=/sata1/swap file-size=2046.9MiB slot=file-sata1-swap swap=yes \ + type=file +add slot=ram tmpfs-max-size=100000000 type=tmpfs +add slot=tmpfs tmpfs-max-size=715827882 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/caps-man datapath +add bridge=Bridge-LAN client-to-client-forwarding=yes interface-list=LAN \ + local-forwarding=yes name=xs-DataPath +add bridge=Bridge-LAN client-to-client-forwarding=yes interface-list=LAN \ + local-forwarding=yes name=DataPath +/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 +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.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="#\ + \n\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\";\ + \n\r\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\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\";\ + \n\r\ + \n\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n\ + \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\");\ + \n\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n\r\ + \n\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n\ + \n:global MkDir;\ + \n\r\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\r\ + \n\r\ + \n\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/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 disk=*7 lan-bridge=Bridge-LAN registry-mirrors="https://reg.vados.ru/:http\ + s://reg.vados.ru/,https://registry-1.docker.io:https://registry-1.docker.i\ + o,https://ghcr.io:https://ghcr.io" show-in-webfig=no +/caps-man aaa +set interim-update=20m20s mac-caching=16m40s +/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 +# bad package path +set ca-certificate=WiFi-CAPsMAN-CA-6D4D5AB6A085 certificate=\ + WiFi-CAPsMAN-6D4D5AB6A085 enabled=yes package-path=\ + /tmpfs/capsman-packages require-peer-certificate=yes upgrade-policy=\ + require-same-version +/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 builtin-trust-store=untrusted +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=LAN +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=auto certificate=auto interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/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=*3 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.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/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=none disabled=no distance=1 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 reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +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-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 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 +/special-login +add port=serial0 +/system clock +set time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/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 package update +set check-certificate=yes-without-crl ip-version=ipv4 +/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 +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260817/20260817004001-Diff-Config.rsc b/MikroNUC/history/20260817/20260817004001-Diff-Config.rsc new file mode 100644 index 0000000..889b890 --- /dev/null +++ b/MikroNUC/history/20260817/20260817004001-Diff-Config.rsc @@ -0,0 +1,9 @@ +5393d5392 +< # bad package path +5411c5410 +< set builtin-trust-store=untrusted +--- +> set builtin-trust-store=all +5492,5493d5490 +< /system gps +< set set-system-time=yes -- 2.52.0