From cb78826e3b3c0ffe692d87c00bafcbe96230b9fa Mon Sep 17 00:00:00 2001 From: vados-dev Date: Tue, 21 Jul 2026 06:00:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 21 06:00:03. --- .../20260721/20260721060001-Binary.backup | Bin 0 -> 388665 bytes MikroNUC/20260721/20260721060001-Config.rsc | 5542 +++++++++++++++++ .../20260721/20260721060001-Diff-Config.rsc | 44 + 3 files changed, 5586 insertions(+) create mode 100644 MikroNUC/20260721/20260721060001-Binary.backup create mode 100644 MikroNUC/20260721/20260721060001-Config.rsc create mode 100644 MikroNUC/history/20260721/20260721060001-Diff-Config.rsc diff --git a/MikroNUC/20260721/20260721060001-Binary.backup b/MikroNUC/20260721/20260721060001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..a6e3c6a3ac1454f38053035f13b6e0c15c8ba34e GIT binary patch literal 388665 zcmeEv2UrtJ*Z*u1AfZTaf=HF3z-H4`l#VF9iVagCQi4fnih|g%A@<&T?_vSbYsZ2G zdqJ_Hf)!Cz@_%NNAS&ML`+x5HzW4h+^E}MXE>q5)Idjf$&Y9gU+Pq>1r$GZ~0|2B9 z&n%%-CYGe5000C4Kmp1hu}xm1O;mxzyP$^ zkDJRo*tQqq;kRFiXrA%3@TFr>Pnz006$I@?F~GT@e5dK#s%C z5ddzf%Je`0C{+RuptG-7gIIdPCKq0B91(-Y zM1?eWo`5cNr;F&^c5j~1?#(m)n>RoH>&=fpfV7Gk8Hy>qe_c5Eo`nb;{5Ha-?JE@v zl7#LdK4D~lxQs?ZF>BDW9695HRsf@r~M0jE=h1^~~AfUFsZ-@08liC7qy6Xxg7r3W!WVzJ1$ zK3!#WpZ?k0jOa9h*gq>SF4;HQGcZCB>)SnZXiYPf;X2BsdNeqy3`< z;gKj`639zQiB9$v24;s~!uYfRFQ~>l2dB%0QW2Lgv=vFDwxL{^Tqw2mlcc8$`Es!& zy|j%`LLK=hLaD{Wg_8PzRw9xAnnXU^*-2~3Ra}=Svj^HpBs6C~lSoQ-l6#zgv^bsn zy-YJulmrnFH17}#z#jx!_Nsgz z{JIiz5R{k)SB5HkuT(iW=CKE%7(^CobqPxl5dd&~jhBGfN07|V@Z_h8h3WD{Nd}xn zGKksUDHMtzRhNwlg~CUbe~;UAN_9+%53Npn4pYNK1UA8u3h~}Ww)@GmzY;A?CZl%X}4L~|!1Gcw;n6W+KA{0^swPWh@^o&R5Drh0RFav|Q^j_=>i!_}7^*xR0;(zx@0F>_!^$jG zUJC(${*?{{+8*V&K&zDALV>N2OCVVglOh1vk)%SdfYDwZ0<6=Zs$Ikls3JHGE-BT$_hC3t(1{qh1+~w#K`uG+tktIarr5k8LakdX~0@H$B^;+ zaU6|&?MW+*o6b!VN+Bdqy2FP8A9gKCX z$YLBEXh9Ol0FdE;>H$C@c2G@HefS?k2o4|?0VXhka}=1uoE=OEFo8k?VTKt1b_6|b z3Iq!fF%VNFNv(pUnI=q=NOMd6EfgW&QiS{xMI?b(mh$hRhwv>ugkPga$WNC1dq#}- zEj`3vqDNCIOq0lk%f1~kppqeA1aJlN|0zSHZz&?RLs5q;u0SI5##0Yz<_{93Axz_n zQ{fbXvqqYkDwL@m0BsedIuGZGIuGZGI)4Gr~KX&0AK6C_S>yP_<0>TkK3(7{COQn`}cJSa9WbS)=@hvxEbkCK6 zW~2(aGNG+hD3f!ga$9+_&{pZNuob7 z5dZ-7Hq_aO0RXrN8v}rt004CwRA7h&07yrrk_YDiKx_a&Is?&hW`=6RUv|}Kq&<&@Pq^aTrUdJR7x4H z8=+r80s#4;6TUvBc*8}c4O~AGtMrxLsSEg#1TN|WrICJzC~Yo0;x4SKV_Qvz=2z2! zPk(4VpaGz$j@kf__vzrOf*cVDprqjc*8_k&^ceuqzyM$Xpn)-j1_qeJ#1c|<8YWf% zP_(T9AX``f&%0AKRL#Q^tI{;2ZWw|b}90>qt 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
+iZCTbB8K5!boMam=gU$XC(#%i^h#T} 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νF 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%0DC7oMAKp7FXGP|Ua!ZchxJ7wY>}jUD9VAV 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 aC;8%}<2F?zvE1dMq>EcCl9{aR!v6~^DvewK literal 0 HcmV?d00001 diff --git a/MikroNUC/20260721/20260721060001-Config.rsc b/MikroNUC/20260721/20260721060001-Config.rsc new file mode 100644 index 0000000..0b54c7a --- /dev/null +++ b/MikroNUC/20260721/20260721060001-Config.rsc @@ -0,0 +1,5542 @@ +# 2026-07-21 06:00:01 by RouterOS 7.23.2 +# 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=bridge1 +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/interface wireguard +add comment="Interface for Master Connection" disabled=yes listen-port=22022 \ + mtu=1360 name=MASTER +add listen-port=21641 mtu=1420 name=wg-VDSina +/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 +/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/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n\r\ + \n\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n\r\ + \n\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n\ + \n:global MkDir;\ + \n\r\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\r\ + \n\r\ + \n\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=*136 +add bridge=Bridge-LAN interface=ether1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ + WiFi-CAPsMAN-7DB23C23C976 enabled=yes interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 comment=\ + "Peer for MikroMaster connection" endpoint-address=10.30.30.81 \ + endpoint-port=33033 interface=MASTER name=MASTER persistent-keepalive=10s \ + public-key="zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.0.2 interface=MASTER network=10.10.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 service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=no ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260721/20260721060001-Diff-Config.rsc b/MikroNUC/history/20260721/20260721060001-Diff-Config.rsc new file mode 100644 index 0000000..a07957e --- /dev/null +++ b/MikroNUC/history/20260721/20260721060001-Diff-Config.rsc @@ -0,0 +1,44 @@ +2733d2732 +< \n\r\ +2736,2739c2735 +< \n\r\ +< \n\r\ +< \n#\r\ +< \n\ +--- +> \n#\ +2742,2743d2737 +< \n\r\ +< \n\r\ +2746,2749c2740 +< \n\r\ +< \n\r\ +< \n#:local Script AM-GlobalConfig;\r\ +< \n\ +--- +> \n#:local Script AM-GlobalConfig;\ +2753,2756c2744 +< \n\r\ +< \n#/usb1/mikro_bak_rsa.pub\r\ +< \n\r\ +< \n\ +--- +> \n#/usb1/mikro_bak_rsa.pub\ +2759,2762c2747 +< \n\r\ +< \n\r\ +< \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ +< \n\ +--- +> \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ +2764d2748 +< \n\r\ +2767,2769c2751 +< \n:local localDir \"/\";\r\ +< \n\r\ +< \n\ +--- +> \n:local localDir \"/\";\ +2773,2774d2754 +< \n\r\ +< \n\r\ -- 2.52.0