From e3d9e1b838a3ea32c2d38b39f29fe3260b96e2f6 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Fri, 10 Jul 2026 14:20:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 10 14:20:03. --- .../20260710/20260710142001-Binary.backup | Bin 0 -> 385044 bytes MikroNUC/20260710/20260710142001-Config.rsc | 5521 +++++++++++++++++ .../20260710/20260710142001-Diff-Config.rsc | 3 + 3 files changed, 5524 insertions(+) create mode 100644 MikroNUC/20260710/20260710142001-Binary.backup create mode 100644 MikroNUC/20260710/20260710142001-Config.rsc create mode 100644 MikroNUC/history/20260710/20260710142001-Diff-Config.rsc diff --git a/MikroNUC/20260710/20260710142001-Binary.backup b/MikroNUC/20260710/20260710142001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..e17c3bb77801763ae6495d42dcc35029b786b465 GIT binary patch literal 385044 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%0DZEmA; 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 K+5{_W_a6a(LcKQt literal 0 HcmV?d00001 diff --git a/MikroNUC/20260710/20260710142001-Config.rsc b/MikroNUC/20260710/20260710142001-Config.rsc new file mode 100644 index 0000000..70c4cdc --- /dev/null +++ b/MikroNUC/20260710/20260710142001-Config.rsc @@ -0,0 +1,5521 @@ +# 2026-07-10 14:20:02 by RouterOS 7.23.1 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN +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#/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#\ + \n#root.mnuc.backuper\ + \n\ + \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#:local Script AM-GlobalConfig;\ + \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#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ + \n:global MkDir;\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \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 require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/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 package-path=/tmpfs/capsman-packages 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 gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system 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/20260710/20260710142001-Diff-Config.rsc b/MikroNUC/history/20260710/20260710142001-Diff-Config.rsc new file mode 100644 index 0000000..9c68ef9 --- /dev/null +++ b/MikroNUC/history/20260710/20260710142001-Diff-Config.rsc @@ -0,0 +1,3 @@ +5476,5477d5475 +< /system gps +< set set-system-time=yes -- 2.52.0