From f2d658501a918c682a66488d93f2d194082f8042 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Fri, 18 Sep 2026 08:20:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Sep 18 08:20:03. --- .../20260918/20260918082001-Binary.backup | Bin 0 -> 444189 bytes MikroNUC/20260918/20260918082001-Config.rsc | 5535 +++++++++++++++++ .../20260918/20260918082001-Diff-Config.rsc | 3 + 3 files changed, 5538 insertions(+) create mode 100644 MikroNUC/20260918/20260918082001-Binary.backup create mode 100644 MikroNUC/20260918/20260918082001-Config.rsc create mode 100644 MikroNUC/history/20260918/20260918082001-Diff-Config.rsc diff --git a/MikroNUC/20260918/20260918082001-Binary.backup b/MikroNUC/20260918/20260918082001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..046987d1f9eac6313a8bca05e213b1b098e2fa1a GIT binary patch literal 444189 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

xX@6C{*=x0~d~I#3yRA#k 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

Z_t;$nAX?L4}emYzI&NGH=>1a%$# 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 ^d$kY;Wi9o9jI*0@<%!K6K*IH*Gh7rhls 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�w&aA@`&+~cwYQv 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 VUR5)RX|{l}4X~EyZK5g*{|9KnoVx%3 literal 0 HcmV?d00001 diff --git a/MikroNUC/20260918/20260918082001-Config.rsc b/MikroNUC/20260918/20260918082001-Config.rsc new file mode 100644 index 0000000..c0fe429 --- /dev/null +++ b/MikroNUC/20260918/20260918082001-Config.rsc @@ -0,0 +1,5535 @@ +# 2026-09-18 08:20:02 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 called-format=ssid 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 +set ca-certificate=auto certificate=auto enabled=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set builtin-trust-store=all crl-store=system crl-use=yes +/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/20260918/20260918082001-Diff-Config.rsc b/MikroNUC/history/20260918/20260918082001-Diff-Config.rsc new file mode 100644 index 0000000..b7cbb9d --- /dev/null +++ b/MikroNUC/history/20260918/20260918082001-Diff-Config.rsc @@ -0,0 +1,3 @@ +5488,5489d5487 +< /system gps +< set set-system-time=yes -- 2.52.0