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