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