From 40fd0c5e844267c53d9c41e02a83a546636a5f3a Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 08:40:05 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 08:40:05. --- .../20260628/20260628084001-Binary.backup | Bin 0 -> 367733 bytes MikroNUC/20260628/20260628084001-Config.rsc | 4910 +++++++++++++++++ .../20260628/20260628084001-Diff-Config.rsc | 101 + 3 files changed, 5011 insertions(+) create mode 100644 MikroNUC/20260628/20260628084001-Binary.backup create mode 100644 MikroNUC/20260628/20260628084001-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628084001-Diff-Config.rsc diff --git a/MikroNUC/20260628/20260628084001-Binary.backup b/MikroNUC/20260628/20260628084001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..6f914d361b15691ed7344a1a12e4dfbcbc17177f GIT binary patch literal 367733 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 P^N`qKi\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\r\ + \n#\r\ + \n#root.mnuc.backuper\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +/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=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=*DF +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1 memory-high=200.0MiB memory-max=4096.0MiB registry-url=\ + https://registry-1.docker.io tmpdir=/sata1/tmp username=amsternl +/container envs +add key=HANDSHAKE_TIMEOUT list=amneziawg value=60 +add key=LOCAL_NET list=amneziawg value=10.9.0.0/24 +add key=PING_TARGET list=amneziawg value=8.8.8.8 +add key=AWG_ENDPOINT list=awgui-env value=10.30.30.10:443 +add key=SECRET_KEY list=awgui-env value=RanDomSecret32SymboLs2325272931q +add key=WEB_PASS list=awgui-env value=zoomzoomrulezz +add key=WEB_USER list=awgui-env value=vados +/container mounts +add dst=/config list=awg-conf src=/sata1/amnezia/amneziawg +/ip smb +set enabled=no +/interface bridge port +add bridge=*FC interface=*FB +add bridge=br-awg interface=veth-awg +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 endpoint-address=172.19.77.106 endpoint-port=\ + 51820 interface=wg-AWG-VDSina name=peer19 persistent-keepalive=10s \ + public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/ip address +add address=10.30.30.10/24 interface=ether1 network=10.30.30.0 +add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=109.107.177.79,8.8.4.4 \ + verify-doh-cert=yes +/ip firewall nat +add action=masquerade chain=srcnat src-address=172.19.77.104/30 +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set www disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both password-authentication=no \ + publickey-authentication-options=verify-required strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260628/20260628084001-Diff-Config.rsc b/MikroNUC/history/20260628/20260628084001-Diff-Config.rsc new file mode 100644 index 0000000..a8a530a --- /dev/null +++ b/MikroNUC/history/20260628/20260628084001-Diff-Config.rsc @@ -0,0 +1,101 @@ +23,24d22 +< /interface bridge +< add name=br-awg +26c24 +< set [ find default-name=ether1 ] disable-running-check=no +--- +> set [ find default-name=ether1 ] disable-running-check=no name=ether2 +31,32c29,30 +< add address=10.9.9.2/24 container-mac-address=7E:39:0C:32:E5:06 dhcp=no \ +< gateway=10.9.9.1 gateway6="" mac-address=7E:39:0C:32:E5:05 name=veth-awg +--- +> add address=172.18.20.5/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ +> gateway=172.18.20.6 gateway6="" name=veth-awg +40,44d37 +< /container +< add envlists=awgui-env file=sata1/tmp/awg2-webui.tar hostname=awg2-webui \ +< interface=veth-awg layer-dir=/sata1/docker/awg2-webu logging=yes name=\ +< awg2-webui root-dir=/sata1/awg2-webui shm-size=4096.0KiB start-on-boot=\ +< yes +4643,4696d4635 +< add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ +< put \"Download image, and save him to usb1/tmp\"\ +< \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ +< ath=sata1/tmp/awg2-webui.tar\ +< \n\ +< \n:put \"1. Create veth interface\"\ +< \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ +< \n\ +< \n:put \"2. Create bridge and add veth\"\ +< \n/interface bridge add name=br-awg\ +< \n/interface bridge port add bridge=br-awg interface=veth-awg\ +< \n\ +< \n:put \"3. Assign IP to bridge\"\ +< \n/ip address add address=10.9.9.1/24 interface=br-awg\ +< \n\ +< \n:put \"4. NAT: masquerade container traffic\"\ +< \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ +< ade\ +< \n\ +< \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ +< \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ +< at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ +< \n\ +< \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ +< \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ +< nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ +< \n\ +< \n:put \"Create environment list..\"\ +< \n/container envs add list=awgui-env key=WEB_USER value=vados\ +< \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ +< \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ +< mboLs2325272931q\ +< \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ +< 3\ +< \n\ +< \n:put \"Load and start container...\"\ +< \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ +< =awg2-webui \\\ +< \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ +< i-env start-on-boot=yes logging=yes shm-size=4M\ +< \n\ +< \n:put \"Start...\"\ +< \n/container start 0\ +< \n\ +< \n:put \"Verify...\"\ +< \n/container print\ +< \n\ +< \n:put \"Should show: status=running\"\ +< \n\ +< \n/log print where topics~\"container\"\ +< \n\ +< \n:put \"Should show: AWG started, nginx running\"\ +< \n" +4773,4776d4711 +< add key=AWG_ENDPOINT list=awgui-env value=10.30.30.10:443 +< add key=SECRET_KEY list=awgui-env value=RanDomSecret32SymboLs2325272931q +< add key=WEB_PASS list=awgui-env value=zoomzoomrulezz +< add key=WEB_USER list=awgui-env value=vados +4781,4783d4715 +< /interface bridge port +< add bridge=*FC interface=*FB +< add bridge=br-awg interface=veth-awg +4799,4800c4731 +< add address=10.30.30.10/24 interface=ether1 network=10.30.30.0 +< add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +--- +> add address=10.30.30.10/24 interface=ether2 network=10.30.30.0 +4802c4733 +< add disabled=yes interface=ether1 name=client1 +--- +> add disabled=yes interface=ether2 name=client1 +4813,4817d4743 +< add action=masquerade chain=srcnat src-address=10.9.9.0/24 +< add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ +< to-addresses=10.9.9.2 to-ports=55055 +< add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ +< tcp to-addresses=10.9.9.2 to-ports=443 +4858,4859d4783 +< /system gps +< set set-system-time=yes -- 2.52.0