From c327b6e7f81ac1a0d7d7f60aeae4f475e2423a81 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 02:00:04 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 02:00:04. --- .../20260628/20260628020002-Binary.backup | Bin 0 -> 356667 bytes MikroNUC/20260628/20260628020002-Config.rsc | 4798 +++++++++++++++++ .../20260628/20260628020002-Diff-Config.rsc | 2 + 3 files changed, 4800 insertions(+) create mode 100644 MikroNUC/20260628/20260628020002-Binary.backup create mode 100644 MikroNUC/20260628/20260628020002-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628020002-Diff-Config.rsc diff --git a/MikroNUC/20260628/20260628020002-Binary.backup b/MikroNUC/20260628/20260628020002-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..64b8467c3821af3b9444d2d595498d7a9b126e55 GIT binary patch literal 356667 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

x5W){%Jz2U3CupY!b)THZ2ok?FGx}r)XY29ahg4 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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" +/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 +/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 +/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.10.10.1 interface=wg-awg-proxy network=10.10.10.0 +/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 +/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/20260628020002-Diff-Config.rsc b/MikroNUC/history/20260628/20260628020002-Diff-Config.rsc new file mode 100644 index 0000000..d65e5f6 --- /dev/null +++ b/MikroNUC/history/20260628/20260628020002-Diff-Config.rsc @@ -0,0 +1,2 @@ +4697d4696 +< add address=10.10.10.1 interface=wg-awg-proxy network=10.10.10.0 -- 2.52.0