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

2S9wVyu0A}pzSd@h)lR)7e0jNn+=*~ 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�S#eX&WCqu%}P` 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 literal 0 HcmV?d00001 diff --git a/MikroNUC/20260721/20260721090001-Config.rsc b/MikroNUC/20260721/20260721090001-Config.rsc new file mode 100644 index 0000000..f43a1b9 --- /dev/null +++ b/MikroNUC/20260721/20260721090001-Config.rsc @@ -0,0 +1,5544 @@ +# 2026-07-21 09:00:02 by RouterOS 7.23.2 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN +add name=bridge1 +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/interface wireguard +add comment="Interface for Master Connection" disabled=yes listen-port=22022 \ + mtu=1360 name=MASTER +add listen-port=21641 mtu=1420 name=wg-VDSina +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n\r\ + \n\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n\r\ + \n\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n\ + \n:global MkDir;\ + \n\r\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\r\ + \n\r\ + \n\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=*136 +add bridge=Bridge-LAN interface=ether1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ + WiFi-CAPsMAN-7DB23C23C976 enabled=yes interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 comment=\ + "Peer for MikroMaster connection" endpoint-address=10.30.30.81 \ + endpoint-port=33033 interface=MASTER name=MASTER persistent-keepalive=10s \ + public-key="zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.0.2 interface=MASTER network=10.10.0.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=no ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260721/20260721090001-Diff-Config.rsc b/MikroNUC/history/20260721/20260721090001-Diff-Config.rsc new file mode 100644 index 0000000..bee40cc --- /dev/null +++ b/MikroNUC/history/20260721/20260721090001-Diff-Config.rsc @@ -0,0 +1,3 @@ +5497,5498d5496 +< /system gps +< set set-system-time=yes -- 2.52.0