From 5991dcec809c296ca3edfaf4bfbd64394f3a54d4 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 03:00:04 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 03:00:04. --- .../20260628/20260628030002-Binary.backup | Bin 0 -> 365478 bytes MikroNUC/20260628/20260628030002-Config.rsc | 4944 +++++++++++++++++ .../20260628/20260628030002-Diff-Config.rsc | 98 + 3 files changed, 5042 insertions(+) create mode 100644 MikroNUC/20260628/20260628030002-Binary.backup create mode 100644 MikroNUC/20260628/20260628030002-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628030002-Diff-Config.rsc diff --git a/MikroNUC/20260628/20260628030002-Binary.backup b/MikroNUC/20260628/20260628030002-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..8a509ab1e5b87f0fb2d1c7d2aa190e6080cf3fa2 GIT binary patch literal 365478 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 x2fS3r})HfxN%Y{J)E0`S%)W% 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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="#\ + \r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\r\ + \n#\r\ + \n#root.mnuc.backuper\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \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\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \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 comment=awg-MikroNuc dont-require-permissions=no name=\ + awg-MikroNuc-uninstall owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n :put \"Uninstalling AmneziaWG Standalone...\"\ + \n :log info \"Uninstalling AmneziaWG Standalone...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/system/script/get [find where name=awg-MikroNuc-p\ + rev-dns] comment] } on-error={}\ + \n :if ([:len \$prevDNS] > 0) do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n } else={ :put \"No previous DNS to restore.\" }\ + \n :do { /system/script/remove [find where name=awg-MikroNuc-prev-dns] } \ + on-error={}\ + \n /ip/route/remove [find where comment=awg-MikroNuc-tunnel]\ + \n /ip/route/remove [find where comment=awg-MikroNuc-dns]\ + \n :do { /ip/route/remove [find where comment~\"awg-MikroNuc\"] } on-erro\ + r={}\ + \n :do { /ip/dns/static/remove [find where comment~\"awg-MikroNuc\"] } on\ + -error={}\ + \n :do { /ip/dns/forwarders/remove [find where comment~\"awg-MikroNuc\"] \ + } on-error={}\ + \n :do { /ip/firewall/mangle/remove [find where comment~\"awg-MikroNuc\"]\ + \_} on-error={}\ + \n :do { /routing/rule/remove [find where comment~\"awg-MikroNuc\"] } on-\ + error={}\ + \n :do { /routing/table/remove [find where comment~\"awg-MikroNuc\"] } on\ + -error={}\ + \n :do { /ip/firewall/address-list/remove [find where list=RU and comment\ + =RIPE-RU] } on-error={}\ + \n :do { /ip/firewall/address-list/remove [find where comment~\"awg-Mikro\ + Nuc-private\"] } on-error={}\ + \n :do { /ip/firewall/address-list/remove [find where list=\"awg-MikroNuc\ + -vpn-server\"] } on-error={}\ + \n :do { /ip/firewall/address-list/remove [find where comment~\"awg-Mikro\ + Nuc-excl-ip\"] } on-error={}\ + \n :do { /system/scheduler/remove [find where comment~\"awg-MikroNuc\"] }\ + \_on-error={}\ + \n :do { /tool/netwatch/remove [find where comment~\"awg-MikroNuc\"] } on\ + -error={}\ + \n :do { /system/script/remove [find where comment~\"awg-MikroNuc\" and n\ + ame!=\"awg-MikroNuc-uninstall\"] } on-error={}\ + \n :foreach rule in=[/ip/firewall/filter/find where comment~\"disabled-by\ + -awg-MikroNuc\"] do={\ + \n :local c [/ip/firewall/filter/get \$rule comment]\ + \n :local origComment [:pick \$c ([:find \$c \"|\"] + 1) [:len \$c]]\ + \n /ip/firewall/filter/set \$rule disabled=no comment=\$origComment\ + \n }\ + \n :foreach rule in=[/ip/firewall/filter/find where comment~\"ft-modified\ + -by-awg-MikroNuc\"] do={\ + \n :local c [/ip/firewall/filter/get \$rule comment]\ + \n :local origComment [:pick \$c ([:find \$c \"|\"] + 1) [:len \$c]]\ + \n /ip/firewall/filter/set \$rule out-interface-list=\"\" comment=\$ori\ + gComment\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-MikroNuc\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-Mikro\ + Nuc\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-MikroNuc\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-MikroNuc\"]\ + \n :do { /file/remove [find where name=\"disk1/ru_ranges_timeout.rsc\"] }\ + \_on-error={}\ + \n :do { /file/remove [find where name=\"usb1/ru_ranges_timeout.rsc\"] } \ + on-error={}\ + \n :put \"Uninstall AmneziaWG Proxy Standalone complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy Standalone complete!\"\ + \n /system/script/remove [find where name=awg-MikroNuc-uninstall]\ + \n" +add comment=10.9.0.1 dont-require-permissions=no name=awg-MikroNuc-prev-dns \ + owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\ + "" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!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=Bridge-LAN registry-mirrors="https://re\ + g.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 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=4096.0MiB memory-max=4096.0MiB registry-url=\ + https://registry-1.docker.io tmpdir=/tmp username=amsternl +/container envs +add key=AWG_CLIENT_PUB list=awg-proxy-env value=\ + "NAonhRSuFIrFpNKTs5X32ItcvFuO1kiJUigQZb95YlE=" +add key=AWG_H1 list=awg-proxy-env value=472437748-472438637 +add key=AWG_H2 list=awg-proxy-env value=707808381-707809250 +add key=AWG_H3 list=awg-proxy-env value=1609078552-1609079495 +add key=AWG_H4 list=awg-proxy-env value=1012329484-1012329741 +add key=AWG_I1 list=awg-proxy-env value=\ + "" +add key=AWG_I2 list=awg-proxy-env value="" +add key=AWG_I3 list=awg-proxy-env value="" +add key=AWG_I4 list=awg-proxy-env value="" +add key=AWG_I5 list=awg-proxy-env value="" +add key=AWG_JC list=awg-proxy-env value=9 +add key=AWG_JMAX list=awg-proxy-env value=920 +add key=AWG_JMIN list=awg-proxy-env value=105 +add key=AWG_LISTEN list=awg-proxy-env value=:55055 +add key=AWG_REMOTE list=awg-proxy-env value=109.107.177.79:55055 +add key=AWG_S1 list=awg-proxy-env value=90 +add key=AWG_S2 list=awg-proxy-env value=136 +add key=AWG_S3 list=awg-proxy-env value=45 +add key=AWG_S4 list=awg-proxy-env value=27 +add key=AWG_SERVER_PUB list=awg-proxy-env value=\ + "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/interface bridge port +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=*C4 +add bridge=Bridge-LAN interface=*C2 +add bridge=Bridge-LAN interface=*C5 +add bridge=Bridge-LAN interface=*C1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=Internet internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=lo list=LAN +add interface=ether2 list=Internet +add interface=docker-awg-veth list=WAN +add interface=ether2 list=WAN +/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=10.30.30.10 endpoint-port=\ + 51820 interface=wg-awg-MikroNuc name=peer3 persistent-keepalive=10s \ + public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/ip address +add address=10.30.30.10/24 interface=ether2 network=10.30.30.0 +add address=10.9.0.3/24 interface=docker-awg-veth network=10.9.0.0 +add address=172.18.0.1/30 interface=veth-awg-proxy network=172.18.0.0 +add address=10.9.0.3 interface=wg-awg-MikroNuc network=10.9.0.3 +/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=Bridge-LAN \ + 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.81 verify-doh-cert=yes +/ip firewall nat +add action=masquerade chain=srcnat src-address=172.18.0.0/30 +/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=bfd disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +add disabled=no distance=1 dst-address=0.0.0.0/0 gateway=docker-awg-veth \ + routing-table=r_to_vpn scope=30 target-scope=10 +add comment=awg-MikroNuc-dns distance=1 dst-address=10.9.0.1/32 gateway=\ + wg-awg-MikroNuc +/ip service +set ftp disabled=yes +set www 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 password-authentication=no \ + publickey-authentication-options=verify-required strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN 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 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/20260628/20260628030002-Diff-Config.rsc b/MikroNUC/history/20260628/20260628030002-Diff-Config.rsc new file mode 100644 index 0000000..673755e --- /dev/null +++ b/MikroNUC/history/20260628/20260628030002-Diff-Config.rsc @@ -0,0 +1,98 @@ +35c35 +< add listen-port=14587 mtu=1420 name=wg-awg-MikroNuc +--- +> add listen-port=12429 mtu=1360 name=wg-awg-proxy +4669,4740d4668 +< add comment=awg-MikroNuc dont-require-permissions=no name=\ +< awg-MikroNuc-uninstall owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ +< \n :put \"Uninstalling AmneziaWG Standalone...\"\ +< \n :log info \"Uninstalling AmneziaWG Standalone...\"\ +< \n :local prevDNS \"\"\ +< \n :do { :set prevDNS [/system/script/get [find where name=awg-MikroNuc-p\ +< rev-dns] comment] } on-error={}\ +< \n :if ([:len \$prevDNS] > 0) do={\ +< \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ +< \n :put (\"DNS restored to: \$prevDNS\")\ +< \n } else={ :put \"No previous DNS to restore.\" }\ +< \n :do { /system/script/remove [find where name=awg-MikroNuc-prev-dns] } \ +< on-error={}\ +< \n /ip/route/remove [find where comment=awg-MikroNuc-tunnel]\ +< \n /ip/route/remove [find where comment=awg-MikroNuc-dns]\ +< \n :do { /ip/route/remove [find where comment~\"awg-MikroNuc\"] } on-erro\ +< r={}\ +< \n :do { /ip/dns/static/remove [find where comment~\"awg-MikroNuc\"] } on\ +< -error={}\ +< \n :do { /ip/dns/forwarders/remove [find where comment~\"awg-MikroNuc\"] \ +< } on-error={}\ +< \n :do { /ip/firewall/mangle/remove [find where comment~\"awg-MikroNuc\"]\ +< \_} on-error={}\ +< \n :do { /routing/rule/remove [find where comment~\"awg-MikroNuc\"] } on-\ +< error={}\ +< \n :do { /routing/table/remove [find where comment~\"awg-MikroNuc\"] } on\ +< -error={}\ +< \n :do { /ip/firewall/address-list/remove [find where list=RU and comment\ +< =RIPE-RU] } on-error={}\ +< \n :do { /ip/firewall/address-list/remove [find where comment~\"awg-Mikro\ +< Nuc-private\"] } on-error={}\ +< \n :do { /ip/firewall/address-list/remove [find where list=\"awg-MikroNuc\ +< -vpn-server\"] } on-error={}\ +< \n :do { /ip/firewall/address-list/remove [find where comment~\"awg-Mikro\ +< Nuc-excl-ip\"] } on-error={}\ +< \n :do { /system/scheduler/remove [find where comment~\"awg-MikroNuc\"] }\ +< \_on-error={}\ +< \n :do { /tool/netwatch/remove [find where comment~\"awg-MikroNuc\"] } on\ +< -error={}\ +< \n :do { /system/script/remove [find where comment~\"awg-MikroNuc\" and n\ +< ame!=\"awg-MikroNuc-uninstall\"] } on-error={}\ +< \n :foreach rule in=[/ip/firewall/filter/find where comment~\"disabled-by\ +< -awg-MikroNuc\"] do={\ +< \n :local c [/ip/firewall/filter/get \$rule comment]\ +< \n :local origComment [:pick \$c ([:find \$c \"|\"] + 1) [:len \$c]]\ +< \n /ip/firewall/filter/set \$rule disabled=no comment=\$origComment\ +< \n }\ +< \n :foreach rule in=[/ip/firewall/filter/find where comment~\"ft-modified\ +< -by-awg-MikroNuc\"] do={\ +< \n :local c [/ip/firewall/filter/get \$rule comment]\ +< \n :local origComment [:pick \$c ([:find \$c \"|\"] + 1) [:len \$c]]\ +< \n /ip/firewall/filter/set \$rule out-interface-list=\"\" comment=\$ori\ +< gComment\ +< \n }\ +< \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-MikroNuc\"]\ +< \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-Mikro\ +< Nuc\"]\ +< \n /ip/address/remove [find where interface=\"wg-awg-MikroNuc\"]\ +< \n /interface/wireguard/remove [find where name=\"wg-awg-MikroNuc\"]\ +< \n :do { /file/remove [find where name=\"disk1/ru_ranges_timeout.rsc\"] }\ +< \_on-error={}\ +< \n :do { /file/remove [find where name=\"usb1/ru_ranges_timeout.rsc\"] } \ +< on-error={}\ +< \n :put \"Uninstall AmneziaWG Proxy Standalone complete!\"\ +< \n :log info \"Uninstall AmneziaWG Proxy Standalone complete!\"\ +< \n /system/script/remove [find where name=awg-MikroNuc-uninstall]\ +< \n" +< add comment=10.9.0.1 dont-require-permissions=no name=awg-MikroNuc-prev-dns \ +< owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\ +< "" +4806c4734 +< add key=AWG_LISTEN list=awg-proxy-env value=:55055 +--- +> add key=AWG_LISTEN list=awg-proxy-env value=:13055 +4834,4836c4762,4764 +< add allowed-address=0.0.0.0/0 endpoint-address=10.30.30.10 endpoint-port=\ +< 51820 interface=wg-awg-MikroNuc name=peer3 persistent-keepalive=10s \ +< public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +--- +> add allowed-address=0.0.0.0/0 endpoint-address=172.18.0.2 endpoint-port=51820 \ +> interface=wg-awg-proxy name=peer1 persistent-keepalive=25s public-key=\ +> "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +4841c4769 +< add address=10.9.0.3 interface=wg-awg-MikroNuc network=10.9.0.3 +--- +> add address=10.10.10.2 interface=wg-awg-proxy network=10.10.10.0 +4851a4780 +> add action=masquerade chain=srcnat out-interface=wg-awg-proxy +4863,4864d4791 +< add comment=awg-MikroNuc-dns distance=1 dst-address=10.9.0.1/32 gateway=\ +< wg-awg-MikroNuc -- 2.52.0