From 6114438349131abac0f13eeb0f47e941314f560f Mon Sep 17 00:00:00 2001 From: vados-dev Date: Wed, 15 Jul 2026 20:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 15 20:40:03. --- .../20260715/20260715204001-Binary.backup | Bin 0 -> 388625 bytes MikroNUC/20260715/20260715204001-Config.rsc | 5524 +++++++++++++++++ .../20260715/20260715204001-Diff-Config.rsc | 3 + 3 files changed, 5527 insertions(+) create mode 100644 MikroNUC/20260715/20260715204001-Binary.backup create mode 100644 MikroNUC/20260715/20260715204001-Config.rsc create mode 100644 MikroNUC/history/20260715/20260715204001-Diff-Config.rsc diff --git a/MikroNUC/20260715/20260715204001-Binary.backup b/MikroNUC/20260715/20260715204001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..fe565d4ca1a6f1f471aff7f3f63286e9e2412469 GIT binary patch literal 388625 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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 +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=*136 +add bridge=Bridge-LAN interface=ether1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ + WiFi-CAPsMAN-7DB23C23C976 enabled=yes interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 comment=\ + "Peer for MikroMaster connection" endpoint-address=10.30.30.81 \ + endpoint-port=33033 interface=MASTER name=MASTER persistent-keepalive=10s \ + public-key="zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.0.2 interface=MASTER network=10.10.0.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=no ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260715/20260715204001-Diff-Config.rsc b/MikroNUC/history/20260715/20260715204001-Diff-Config.rsc new file mode 100644 index 0000000..7595fff --- /dev/null +++ b/MikroNUC/history/20260715/20260715204001-Diff-Config.rsc @@ -0,0 +1,3 @@ +5477,5478d5476 +< /system gps +< set set-system-time=yes -- 2.52.0