From 7f6e35b2dff0721cd0fc7f2caaae910d4040d718 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 01:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 01:40:03. --- .../20260628/20260628014001-Binary.backup | Bin 0 -> 356598 bytes MikroNUC/20260628/20260628014001-Config.rsc | 4797 +++++++++++++++++ .../20260628/20260628014001-Diff-Config.rsc | 10 + 3 files changed, 4807 insertions(+) create mode 100644 MikroNUC/20260628/20260628014001-Binary.backup create mode 100644 MikroNUC/20260628/20260628014001-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628014001-Diff-Config.rsc diff --git a/MikroNUC/20260628/20260628014001-Binary.backup b/MikroNUC/20260628/20260628014001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..368dba563cec71090ba606872801b2d0d3c72212 GIT binary patch literal 356598 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

lS-AIXjpfdAxTCZ9?;ie2&#?r1Us6 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%0AY~m!8iV4wU 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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 +/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/20260628014001-Diff-Config.rsc b/MikroNUC/history/20260628/20260628014001-Diff-Config.rsc new file mode 100644 index 0000000..f6a6dec --- /dev/null +++ b/MikroNUC/history/20260628/20260628014001-Diff-Config.rsc @@ -0,0 +1,10 @@ +31,34d30 +< add address=172.18.0.2/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ +< gateway=172.18.0.1 gateway6="" name=veth-awg-proxy +< /interface wireguard +< add listen-port=12429 mtu=1420 name=wg-awg-proxy +4696d4691 +< add address=172.18.0.1/30 interface=veth-awg-proxy network=172.18.0.0 +4705,4706d4699 +< /ip firewall nat +< add action=masquerade chain=srcnat src-address=172.18.0.0/30 -- 2.52.0