From 22044af1fad401178fe7fb9fc0dd839e3f105b76 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 29 Jun 2026 13:20:04 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 29 13:20:04. --- .../20260629/20260629132001-Binary.backup | Bin 0 -> 374714 bytes MikroNUC/20260629/20260629132001-Config.rsc | 5013 +++++++++++++++++ .../20260629/20260629132001-Diff-Config.rsc | 22 + 3 files changed, 5035 insertions(+) create mode 100644 MikroNUC/20260629/20260629132001-Binary.backup create mode 100644 MikroNUC/20260629/20260629132001-Config.rsc create mode 100644 MikroNUC/history/20260629/20260629132001-Diff-Config.rsc diff --git a/MikroNUC/20260629/20260629132001-Binary.backup b/MikroNUC/20260629/20260629132001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..6df06777cd546a6589f7e343aa7e7e8b1d8ef0c8 GIT binary patch literal 374714 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
    Y8bBWO|WLN$*E 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�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�e5aO>jcPLhtV)|z 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 HlC}F^la13N literal 0 HcmV?d00001 diff --git a/MikroNUC/20260629/20260629132001-Config.rsc b/MikroNUC/20260629/20260629132001-Config.rsc new file mode 100644 index 0000000..f923bdc --- /dev/null +++ b/MikroNUC/20260629/20260629132001-Config.rsc @@ -0,0 +1,5013 @@ +# 2026-06-29 13:20:03 by RouterOS 7.23.1 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN +add name=br-awg +add name=docker port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/interface veth +add address=192.168.100.3/24 container-mac-address=5E:AB:F0:EA:47:2D dhcp=no \ + gateway=192.168.100.1 gateway6="" mac-address=5E:AB:F0:EA:47:2C name=\ + awg-proxy-go-client +add address=10.9.9.2/24 container-mac-address=70:35:5E:FC:C3:B8 dhcp=no \ + gateway=10.9.9.1 gateway6="" mac-address=70:35:5E:FC:C3:B7 name=veth-awg +/interface wireguard +add listen-port=52175 mtu=1420 name=wg-awg-client +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/container +# exited with signal 9 (Killed) +add envlists=awgui-env file=sata1/tmp/awg2-webui.tar hostname=awg2-webui \ + interface=veth-awg layer-dir=/sata1/docker/awg2-webu logging=yes name=\ + awg2-webui root-dir=/sata1/awg2-webui shm-size=4096.0KiB start-on-boot=\ + yes +add file=tmp/awgproxy-0.0.1-0034-amd64.tar.gz hostname=awg-proxy-go-client \ + interface=awg-proxy-go-client layer-dir=/sata1 logging=yes memory-high=\ + 1800.0KiB memory-max=2048.0KiB mountlists=awgcfg-client name=\ + awg-proxy-go-client root-dir=/ram/awg-proxy-client start-on-boot=yes \ + workdir=/ +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \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#\ + \n#root.mnuc.backuper\ + \n\ + \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#:local Script AM-GlobalConfig;\ + \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#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ + \n:global MkDir;\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \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 \"C\D0\BE\D0\B7\D0\B4\D0\B0\D0\B5\D0\BC \D0\B4\D0\B8\D1\81\D0\BA \ + \D0\B2 \D0\BE\D0\BF\D0\B5\D1\80\D0\B0\D1\82\D0\B8\D0\B2\D0\BA\D0\B5 \D0\B4\ + \D0\BB\D1\8F \D0\BA\D0\BE\D0\BD\D1\82\D0\B5\D0\B9\D0\BD\D0\B5\D1\80\D0\BE\ + \D0\B2\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth \D0\B8 \D1\81\D0\B5\D1\82\D1\8C:\"\ + \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=/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=/awg-proxy-go/config-client.json dst=/app/conf\ + ig.json list=awgcfg-client\ + \n\ + \n:put \"Download image, and save him to sata1/tmp/awgproxy0033-amd64.tar.\ + gz\"\ + \n/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-0034\ + -amd64.tar.gz\" dst-path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\" interface=a\ + wg-proxy-go-client hostname=awg-proxy-go-client root-dir=ram/awg-proxy-cli\ + ent logging=yes start-on-boot=yes workdir=/ name=\"awg-proxy-go-client\" m\ + ountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n /container/start awg-proxy-go-client\ + \n\ + \n:put \"\D0\94\D0\BE\D0\B1\D0\B0\D0\B2\D0\BB\D1\8F\D0\B5\D0\BC \D0\BF\D0\ + \BE\D0\BB\D1\8C\D0\B7\D0\BE\D0\B2\D0\B0\D1\82\D0\B5\D0\BB\D1\8F \D1\81 \D0\ + \BF\D1\80\D0\B0\D0\B2\D0\B0\D0\BC\D0\B8 \D0\BD\D0\B0 \D1\87\D1\82\D0\B5\D0\ + \BD\D0\B8\D0\B5 \D0\B8 \D0\B7\D0\B0\D0\BF\D0\B8\D1\81\D1\8C, \D0\BE\D0\B3\ + \D1\80\D0\B0\D0\BD\D0\B8\D1\87\D0\B8\D0\B2\D0\B0\D0\B5\D0\BC \D0\B4\D0\BE\ + \D1\81\D1\82\D1\83\D0\BF \D0\BF\D0\BE ip \D0\BA\D0\BE\D0\BD\D1\82\D0\B5\D0\ + \B9\D0\BD\D0\B5\D1\80\D0\B0:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"\D0\94\D0\BE\D0\B1\D0\B0\D0\B2\D0\BB\D1\8F\D0\B5\D0\BC \D0\BF\D1\ + \80\D0\BE\D0\B1\D1\80\D0\BE\D1\81 \D0\BF\D0\BE\D1\80\D1\82\D0\B0 \D0\B2\D0\ + \B5\D0\B1-\D0\BC\D0\BE\D1\80\D0\B4\D1\8B \D0\BA \D0\BA\D0\BE\D0\BD\D1\82\ + \D0\B5\D0\B9\D0\BD\D0\B5\D1\80\D1\83\"\ + \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 \"\D0\9D\D0\B0 \D1\80\D0\BE\D1\83\D1\82\D0\B5\D1\80\D0\B5 \D0\BF\D0\ + \BE\D0\B4\D0\BD\D1\8F\D1\82\D1\8C \D0\BD\D0\B0\D1\82\D0\B8\D0\B2\D0\BD\D1\ + \8B\D0\B9 WireGuard-\D1\81\D0\B5\D1\80\D0\B2\D0\B5\D1\80 wg-awg, \D0\BD\D0\ + \B0\D1\81\D1\82\D1\80\D0\BE\D0\B8\D1\82\D1\8C \D0\B5\D0\BC\D1\83 \D0\B0\D0\ + \B4\D1\80\D0\B5\D1\81.\"\ + \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 \"\D0\9F\D1\80\D0\BE\D0\B2\D0\B5\D1\80\D0\B8\D1\82\D1\8C \D0\B4\D0\ + \BE\D1\81\D1\82\D1\83\D0\BF \D0\BA \D1\80\D0\BE\D1\83\D1\82\D0\B5\D1\80\D1\ + \83 \D0\BF\D0\BE api: \D0\B2\D0\BA\D0\BB\D1\8E\D1\87\D0\B8\D1\82\D1\8C /ip\ + /service www-ssl (HTTPS, \D0\BF\D0\BE\D1\80\D1\82 443) \D0\B8\D0\BB\D0\B8 \ + www (HTTP, \D0\BF\D0\BE\D1\80\D1\82 80).\"\ + \n:put \"\D0\B7\D0\B0\D0\B9\D1\82\D0\B8 \D0\B2 \D0\B2\D0\B5\D0\B1 \D0\BF\ + \D0\B0\D0\BD\D0\B5\D0\BB\D1\8C \D1\83\D0\BF\D1\80\D0\B0\D0\B2\D0\BB\D0\B5\ + \D0\BD\D0\B8\D1\8F \D0\B8 \D0\BD\D0\B0\D1\81\D1\82\D1\80\D0\BE\D0\B8\D1\82\ + \D1\8C \D0\BF\D1\80\D0\BE\D0\BA\D1\81\D0\B8 \D0\BF\D0\BE http://your-route\ + -address:8088/\"\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="ssh,ftp,read,write,policy,!local,!telnet,!reboot,!tes\ + t,!winbox,!password,!web,!sniff,!sensitive,!api,!romon,!rest-api" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1 memory-high=200.0MiB memory-max=4096.0MiB registry-url=\ + https://registry-1.docker.io tmpdir=/sata1/tmp username=amsternl +/container envs +add key=AWG_ENDPOINT list=awgui-env value=10.30.30.10:443 +add key=SECRET_KEY list=awgui-env value=RanDomSecret32SymboLs2325272931q +add key=WEB_PASS list=awgui-env value=zoomzoomrulezz +add key=WEB_USER list=awgui-env value=vados +/container mounts +add dst=/config list=awg-conf src=/sata1/amnezia/amneziawg +add dst=/app/config.json list=awgcfg-client src=\ + /awg-proxy-go/config-client.json +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=ether1 +add bridge=Bridge-LAN interface=2G-MikroGig-1 +add bridge=Bridge-LAN interface=2G-MikroMaster-1 +add bridge=Bridge-LAN interface=5G-MikroGig-1 +add bridge=Bridge-LAN interface=5G-MikroMaster-1 +add bridge=br-awg interface=veth-awg +add bridge=docker interface=awg-proxy-go-client +/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 +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 endpoint-address=172.19.77.106 endpoint-port=\ + 51820 interface=*F3 name=peer19 persistent-keepalive=10s public-key=\ + "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/ip address +add address=10.30.30.10/24 interface=ether1 network=10.30.30.0 +add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +add address=192.168.100.1/24 interface=docker network=192.168.100.0 +add address=172.17.0.1/24 interface=wg-awg-client network=172.17.0.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 nat +add action=masquerade chain=srcnat src-address=172.19.77.104/30 +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=8089 in-interface=ether1 protocol=\ + tcp to-addresses=192.168.100.3 to-ports=8088 +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set www disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260629/20260629132001-Diff-Config.rsc b/MikroNUC/history/20260629/20260629132001-Diff-Config.rsc new file mode 100644 index 0000000..dcc9fc5 --- /dev/null +++ b/MikroNUC/history/20260629/20260629132001-Diff-Config.rsc @@ -0,0 +1,22 @@ +48,52d47 +< add file=tmp/awgproxy-0.0.1-0034-amd64.tar.gz hostname=awg-proxy-go-client \ +< interface=awg-proxy-go-client layer-dir=/sata1 logging=yes memory-high=\ +< 1800.0KiB memory-max=2048.0KiB mountlists=awgcfg-client name=\ +< awg-proxy-go-client root-dir=/ram/awg-proxy-client start-on-boot=yes \ +< workdir=/ +4729,4730c4724,4725 +< \n/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-0034\ +< -amd64.tar.gz\" dst-path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ +--- +> \n/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy0033-amd64.\ +> tar.gz\" dst-path=\"sata1/tmp/awgproxy0033-amd64.tar.gz\"\ +4733,4736c4728,4731 +< \n/container/add file=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\" interface=a\ +< wg-proxy-go-client hostname=awg-proxy-go-client root-dir=ram/awg-proxy-cli\ +< ent logging=yes start-on-boot=yes workdir=/ name=\"awg-proxy-go-client\" m\ +< ountlists=awgcfg-client\ +--- +> \n/container/add file=sata1/tmp/awgproxy0033-amd64.tar.gz interface=awg-pr\ +> oxy-go-client hostname=awg-proxy-go-client root-dir=ram/awg-proxy-client l\ +> ogging=yes start-on-boot=yes workdir=/ name=\"awg-proxy-go-client\" mountl\ +> ists=awgcfg-client\ -- 2.52.0