From 669e67666c89b54f43585b0e26fa2483fba9ab40 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 16 Aug 2026 07:00:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Aug 16 07:00:03. --- .../20260816/20260816070001-Binary.backup | Bin 0 -> 390919 bytes MikroNUC/20260816/20260816070001-Config.rsc | 5531 +++++++++++++++++ .../20260816/20260816070001-Diff-Config.rsc | 7 + 3 files changed, 5538 insertions(+) create mode 100644 MikroNUC/20260816/20260816070001-Binary.backup create mode 100644 MikroNUC/20260816/20260816070001-Config.rsc create mode 100644 MikroNUC/history/20260816/20260816070001-Diff-Config.rsc diff --git a/MikroNUC/20260816/20260816070001-Binary.backup b/MikroNUC/20260816/20260816070001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..6ea250f1e73db0fe52d18092d9078441ff915576 GIT binary patch literal 390919 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

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