From 65ce5b9f83eb817f8833a9f4506aeaae5693e87e Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 16 Aug 2026 20:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Aug 16 20:40:03. --- .../20260816/20260816204001-Binary.backup | Bin 0 -> 392167 bytes MikroNUC/20260816/20260816204001-Config.rsc | 5534 +++++++++++++++++ .../20260816/20260816204001-Diff-Config.rsc | 6 + 3 files changed, 5540 insertions(+) create mode 100644 MikroNUC/20260816/20260816204001-Binary.backup create mode 100644 MikroNUC/20260816/20260816204001-Config.rsc create mode 100644 MikroNUC/history/20260816/20260816204001-Diff-Config.rsc diff --git a/MikroNUC/20260816/20260816204001-Binary.backup b/MikroNUC/20260816/20260816204001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..c3e874a1450c4c59be0d602c42b459f3ff7109e8 GIT binary patch literal 392167 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 Bw>tm; literal 0 HcmV?d00001 diff --git a/MikroNUC/20260816/20260816204001-Config.rsc b/MikroNUC/20260816/20260816204001-Config.rsc new file mode 100644 index 0000000..bc7c689 --- /dev/null +++ b/MikroNUC/20260816/20260816204001-Config.rsc @@ -0,0 +1,5534 @@ +# 2026-08-16 20:40:00 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 slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/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 auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man 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 +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + 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 crl-store=system +/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 +/ip neighbor discovery-settings +set lldp-mac-phy-config=yes lldp-max-frame-size=yes lldp-med-net-policy-vlan=\ + 1 lldp-vlan-info=yes +/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=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ + WiFi-CAPsMAN-7DB23C23C976 enabled=yes 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 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/20260816/20260816204001-Diff-Config.rsc b/MikroNUC/history/20260816/20260816204001-Diff-Config.rsc new file mode 100644 index 0000000..ef54c89 --- /dev/null +++ b/MikroNUC/history/20260816/20260816204001-Diff-Config.rsc @@ -0,0 +1,6 @@ +5488c5488,5490 +< set time-zone-name=Europe/Moscow +--- +> set time-zone-name=Europe/Amsterdam +> /system gps +> set set-system-time=yes -- 2.52.0