From 92f9b31b2314db61774590ad67df4fbeee78ecd1 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 29 Jun 2026 13:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 29 13:40:03. --- .../20260629/20260629134001-Binary.backup | Bin 0 -> 374714 bytes MikroNUC/20260629/20260629134001-Config.rsc | 5012 +++++++++++++++++ .../20260629/20260629134001-Diff-Config.rsc | 9 + 3 files changed, 5021 insertions(+) create mode 100644 MikroNUC/20260629/20260629134001-Binary.backup create mode 100644 MikroNUC/20260629/20260629134001-Config.rsc create mode 100644 MikroNUC/history/20260629/20260629134001-Diff-Config.rsc diff --git a/MikroNUC/20260629/20260629134001-Binary.backup b/MikroNUC/20260629/20260629134001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..ec33a44795e66446f220807be28d97a629f3701f GIT binary patch literal 374714 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%0D5d~-BzN%AX6^{ipYE@4G>g;cPU`Bu~m@g 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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/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.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 \"C\D0\BE\D0\B7\D0\B4\D0\B0\D0\B5\D0\BC \D0\B4\D0\B8\D1\81\D0\BA \ + \D0\B2 \D0\BE\D0\BF\D0\B5\D1\80\D0\B0\D1\82\D0\B8\D0\B2\D0\BA\D0\B5 \D0\B4\ + \D0\BB\D1\8F \D0\BA\D0\BE\D0\BD\D1\82\D0\B5\D0\B9\D0\BD\D0\B5\D1\80\D0\BE\ + \D0\B2\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth \D0\B8 \D1\81\D0\B5\D1\82\D1\8C:\"\ + \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=/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=/awg-proxy-go/config-client.json dst=/app/conf\ + ig.json list=awgcfg-client\ + \n\ + \n:put \"Download image, and save him to sata1/tmp/awgproxy0033-amd64.tar.\ + gz\"\ + \n/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-0034\ + -amd64.tar.gz\" dst-path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\" interface=a\ + wg-proxy-go-client hostname=awg-proxy-go-client root-dir=ram/awg-proxy-cli\ + ent logging=yes start-on-boot=yes workdir=/ name=\"awg-proxy-go-client\" m\ + ountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n /container/start awg-proxy-go-client\ + \n\ + \n:put \"\D0\94\D0\BE\D0\B1\D0\B0\D0\B2\D0\BB\D1\8F\D0\B5\D0\BC \D0\BF\D0\ + \BE\D0\BB\D1\8C\D0\B7\D0\BE\D0\B2\D0\B0\D1\82\D0\B5\D0\BB\D1\8F \D1\81 \D0\ + \BF\D1\80\D0\B0\D0\B2\D0\B0\D0\BC\D0\B8 \D0\BD\D0\B0 \D1\87\D1\82\D0\B5\D0\ + \BD\D0\B8\D0\B5 \D0\B8 \D0\B7\D0\B0\D0\BF\D0\B8\D1\81\D1\8C, \D0\BE\D0\B3\ + \D1\80\D0\B0\D0\BD\D0\B8\D1\87\D0\B8\D0\B2\D0\B0\D0\B5\D0\BC \D0\B4\D0\BE\ + \D1\81\D1\82\D1\83\D0\BF \D0\BF\D0\BE ip \D0\BA\D0\BE\D0\BD\D1\82\D0\B5\D0\ + \B9\D0\BD\D0\B5\D1\80\D0\B0:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"\D0\94\D0\BE\D0\B1\D0\B0\D0\B2\D0\BB\D1\8F\D0\B5\D0\BC \D0\BF\D1\ + \80\D0\BE\D0\B1\D1\80\D0\BE\D1\81 \D0\BF\D0\BE\D1\80\D1\82\D0\B0 \D0\B2\D0\ + \B5\D0\B1-\D0\BC\D0\BE\D1\80\D0\B4\D1\8B \D0\BA \D0\BA\D0\BE\D0\BD\D1\82\ + \D0\B5\D0\B9\D0\BD\D0\B5\D1\80\D1\83\"\ + \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 \"\D0\9D\D0\B0 \D1\80\D0\BE\D1\83\D1\82\D0\B5\D1\80\D0\B5 \D0\BF\D0\ + \BE\D0\B4\D0\BD\D1\8F\D1\82\D1\8C \D0\BD\D0\B0\D1\82\D0\B8\D0\B2\D0\BD\D1\ + \8B\D0\B9 WireGuard-\D1\81\D0\B5\D1\80\D0\B2\D0\B5\D1\80 wg-awg, \D0\BD\D0\ + \B0\D1\81\D1\82\D1\80\D0\BE\D0\B8\D1\82\D1\8C \D0\B5\D0\BC\D1\83 \D0\B0\D0\ + \B4\D1\80\D0\B5\D1\81.\"\ + \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 \"\D0\9F\D1\80\D0\BE\D0\B2\D0\B5\D1\80\D0\B8\D1\82\D1\8C \D0\B4\D0\ + \BE\D1\81\D1\82\D1\83\D0\BF \D0\BA \D1\80\D0\BE\D1\83\D1\82\D0\B5\D1\80\D1\ + \83 \D0\BF\D0\BE api: \D0\B2\D0\BA\D0\BB\D1\8E\D1\87\D0\B8\D1\82\D1\8C /ip\ + /service www-ssl (HTTPS, \D0\BF\D0\BE\D1\80\D1\82 443) \D0\B8\D0\BB\D0\B8 \ + www (HTTP, \D0\BF\D0\BE\D1\80\D1\82 80).\"\ + \n:put \"\D0\B7\D0\B0\D0\B9\D1\82\D0\B8 \D0\B2 \D0\B2\D0\B5\D0\B1 \D0\BF\ + \D0\B0\D0\BD\D0\B5\D0\BB\D1\8C \D1\83\D0\BF\D1\80\D0\B0\D0\B2\D0\BB\D0\B5\ + \D0\BD\D0\B8\D1\8F \D0\B8 \D0\BD\D0\B0\D1\81\D1\82\D1\80\D0\BE\D0\B8\D1\82\ + \D1\8C \D0\BF\D1\80\D0\BE\D0\BA\D1\81\D0\B8 \D0\BF\D0\BE http://your-route\ + -address:8088/\"\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +/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="ssh,ftp,read,write,policy,!local,!telnet,!reboot,!tes\ + t,!winbox,!password,!web,!sniff,!sensitive,!api,!romon,!rest-api" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1 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_ENDPOINT list=awgui-env value=10.30.30.10:443 +add key=SECRET_KEY list=awgui-env value=RanDomSecret32SymboLs2325272931q +add key=WEB_PASS list=awgui-env value=zoomzoomrulezz +add key=WEB_USER list=awgui-env value=vados +/container mounts +add dst=/config list=awg-conf src=/sata1/amnezia/amneziawg +add dst=/app/config.json list=awgcfg-client src=\ + /awg-proxy-go/config-client.json +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=ether1 +add bridge=Bridge-LAN interface=2G-MikroGig-1 +add bridge=Bridge-LAN interface=2G-MikroMaster-1 +add bridge=Bridge-LAN interface=5G-MikroGig-1 +add bridge=Bridge-LAN interface=5G-MikroMaster-1 +add bridge=br-awg interface=veth-awg +add bridge=docker interface=awg-proxy-go-client +/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 +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 endpoint-address=172.19.77.106 endpoint-port=\ + 51820 interface=*F3 name=peer19 persistent-keepalive=10s public-key=\ + "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/ip address +add address=10.30.30.10/24 interface=ether1 network=10.30.30.0 +add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +add address=192.168.100.1/24 interface=docker network=192.168.100.0 +add address=172.17.0.1/24 interface=wg-awg-client network=172.17.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 nat +add action=masquerade chain=srcnat src-address=172.19.77.104/30 +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=8089 in-interface=ether1 protocol=\ + tcp to-addresses=192.168.100.3 to-ports=8088 +/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 telnet disabled=yes +set www disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +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-plain=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 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260629/20260629134001-Diff-Config.rsc b/MikroNUC/history/20260629/20260629134001-Diff-Config.rsc new file mode 100644 index 0000000..a7239ca --- /dev/null +++ b/MikroNUC/history/20260629/20260629134001-Diff-Config.rsc @@ -0,0 +1,9 @@ +49,51c49,52 +< interface=awg-proxy-go-client layer-dir=/sata1 logging=yes mountlists=\ +< awgcfg-client name=awg-proxy-go-client root-dir=/ram/awg-proxy-client \ +< start-on-boot=yes workdir=/ +--- +> interface=awg-proxy-go-client layer-dir=/sata1 logging=yes memory-high=\ +> 1800.0KiB memory-max=2048.0KiB mountlists=awgcfg-client name=\ +> awg-proxy-go-client root-dir=/ram/awg-proxy-client start-on-boot=yes \ +> workdir=/ -- 2.52.0