From ebad4d758a2bc90db048447cac6c471e26429b11 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Tue, 21 Jul 2026 17:00:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 21 17:00:03. --- .../20260721/20260721170001-Binary.backup | Bin 0 -> 390591 bytes MikroNUC/20260721/20260721170001-Config.rsc | 5543 +++++++++++++++++ .../20260721/20260721170001-Diff-Config.rsc | 3 + 3 files changed, 5546 insertions(+) create mode 100644 MikroNUC/20260721/20260721170001-Binary.backup create mode 100644 MikroNUC/20260721/20260721170001-Config.rsc create mode 100644 MikroNUC/history/20260721/20260721170001-Diff-Config.rsc diff --git a/MikroNUC/20260721/20260721170001-Binary.backup b/MikroNUC/20260721/20260721170001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..3255aa54bdb5aa5ae0d8dc2380d87590da24ed37 GIT binary patch literal 390591 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

uz-XbVdk~IN3V>;!lVa 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 z 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 literal 0 HcmV?d00001 diff --git a/MikroNUC/20260721/20260721170001-Config.rsc b/MikroNUC/20260721/20260721170001-Config.rsc new file mode 100644 index 0000000..cbb8bba --- /dev/null +++ b/MikroNUC/20260721/20260721170001-Config.rsc @@ -0,0 +1,5543 @@ +# 2026-07-21 17:00:02 by RouterOS 7.23.2 +# 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 +add name=bridge1 +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/interface wireguard +add comment="Interface for Master Connection" disabled=yes listen-port=22022 \ + mtu=1360 name=MASTER +add listen-port=21641 mtu=1420 name=wg-VDSina +/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=*136 +add bridge=Bridge-LAN interface=ether1 +/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 +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 comment=\ + "Peer for MikroMaster connection" endpoint-address=10.30.30.81 \ + endpoint-port=33033 interface=MASTER name=MASTER persistent-keepalive=10s \ + public-key="zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.0.2 interface=MASTER network=10.10.0.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-autodetect=no time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=no 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/20260721/20260721170001-Diff-Config.rsc b/MikroNUC/history/20260721/20260721170001-Diff-Config.rsc new file mode 100644 index 0000000..442d41c --- /dev/null +++ b/MikroNUC/history/20260721/20260721170001-Diff-Config.rsc @@ -0,0 +1,3 @@ +5497a5498,5499 +> /system gps +> set set-system-time=yes -- 2.52.0