From 55e25dc30a1b952e0c5b6a4b76efaadb927a5eb5 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Fri, 10 Jul 2026 03:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 10 03:40:03. --- MikroMaster/tmp/Config.rsc | 0 .../20260710/20260710034001-Binary.backup | Bin 0 -> 385044 bytes MikroNUC/20260710/20260710034001-Config.rsc | 5519 +++++++++++++++++ .../20260710/20260710034001-Diff-Config.rsc | 3 + 4 files changed, 5522 insertions(+) create mode 100644 MikroMaster/tmp/Config.rsc create mode 100644 MikroNUC/20260710/20260710034001-Binary.backup create mode 100644 MikroNUC/20260710/20260710034001-Config.rsc create mode 100644 MikroNUC/history/20260710/20260710034001-Diff-Config.rsc diff --git a/MikroMaster/tmp/Config.rsc b/MikroMaster/tmp/Config.rsc new file mode 100644 index 0000000..e69de29 diff --git a/MikroNUC/20260710/20260710034001-Binary.backup b/MikroNUC/20260710/20260710034001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..da295b08ec05e25030bc3a440f95c70766784f48 GIT binary patch literal 385044 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

Fz`$HYn}Z4@fetGXR?SC`iY$UnEKAa5(Ig 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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#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n#\ + \n#root.mnuc.backuper\ + \n\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n#:local Script AM-GlobalConfig;\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ + \n:global MkDir;\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=*136 +add bridge=Bridge-LAN interface=ether1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 comment=\ + "Peer for MikroMaster connection" endpoint-address=10.30.30.81 \ + endpoint-port=33033 interface=MASTER name=MASTER persistent-keepalive=10s \ + public-key="zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.0.2 interface=MASTER network=10.10.0.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260710/20260710034001-Diff-Config.rsc b/MikroNUC/history/20260710/20260710034001-Diff-Config.rsc new file mode 100644 index 0000000..7f05d76 --- /dev/null +++ b/MikroNUC/history/20260710/20260710034001-Diff-Config.rsc @@ -0,0 +1,3 @@ +5475a5476,5477 +> /system gps +> set set-system-time=yes -- 2.52.0