From 7fd5d9eed96b311396a5baa53194b10f7714faa1 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 16 Aug 2026 08:40:02 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Aug 16 08:40:02. --- .../20260816/20260816084001-Binary.backup | Bin 0 -> 390919 bytes MikroNUC/20260816/20260816084001-Config.rsc | 5533 +++++++++++++++++ .../20260816/20260816084001-Diff-Config.rsc | 3 + 3 files changed, 5536 insertions(+) create mode 100644 MikroNUC/20260816/20260816084001-Binary.backup create mode 100644 MikroNUC/20260816/20260816084001-Config.rsc create mode 100644 MikroNUC/history/20260816/20260816084001-Diff-Config.rsc diff --git a/MikroNUC/20260816/20260816084001-Binary.backup b/MikroNUC/20260816/20260816084001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..76a442638709a6961551d6bb677c8ed77a88d563 GIT binary patch literal 390919 zcmeEvcU%)q*Y<1@Affjns8lHmY;V9<0q!I$0J{+&4ctjM0Pp|+3<3Zm1$q!^&;t2jAP<+e-jF zdO#8M=;0z+VNpuLCiLLpNhRQ6DxzDAjgA+DMTJ<#vqNGmIs6b=@x5A!FAUdKz?~!q zdJq*@4_*Tq3VKX62JD2Fk>c|BtoR7Qz_N_^?zp^!Fb*%SPmqz~lVbn?-Qx0y0cBGJ zfYVUo002E40Fl6*6bcYTcx?nA1UC#o2(q#OApmQ*kOYVxCL$US5K~)s5)=g&Ab8ko zudslCXy5<=QO5xS(8K|Pgcc6qMA?_gE_dMECgZ_Jnua zcaO@j&$MxXfPD`oF$@Z!0}BWk;myB|4;&UeUwHGP;=r39@#{CwJgLBa;n3nl zz|AOFFCBoHa8E2eG!8BV01&wG0N`_Y03-l_-^c+V5deG@3P2J7_$Dj>$pGM~vf{Bd z8&*q!XGsS@Xk6yOJ@dsInE(*d763qy;DdfOQUpK`4rV^Y)UO_lk2lkxUUbFe!J{tz@98RR$P;LMR z+)2<@v*UQN34KQH#{=MgP9!TTjL+imIQ$T3k)auawh8{A;nx?J#|ig~Y#>HtgM}R^ zwoxK632m@Ga3?`a*Vo1H2rL4CaEu9$fVP+$%89Y$M1=981i{fUg0SeQIOs##dwY9b z#Fy;o?d`P_mp=;akmz?F0N^5S3cMu*^x$DV=zDB10O%>BNqoMciG!P~lgFfBM_*S* z(|+$DC%R@BH<%kA#biT>mN=4+X+t~3)>-MXfkf1 z&m<2!cc%eWL!;vaQLIQF?4FR===hk?VzE=i_XmYX6_;Tb5XEJ9uQYKPR;G)~QV0N) zZ|y*!zKHq-YEo1W0iFe)1DgeXBm$jGERV&d4ps*P>$HjMC=RS6kr*2$;1LI_goTxQ zdW9Dv47dtEJ|co9svvBAf6H*NLM=x)#PIqX2h@>dvpC`LG4#P|Nx)hU)A7RlF`SEh zyOLBSD~c7ui-iy&g2NR=$8$pA=!QS%0c&UAPKp8mnF#=h13)-}p?M02=m07_4*)z~ z3IIObfR+YYECLb(7c-#Xh$J!vo;iZY4Hm?Pg@o{8 z&f*9MIqYosTPa>ceS^jl{$R)Vm0`#C9RoXlv-p@s4BRioLjXE+X#n6Q5C~Y50e~VF z8Z;@`1UvwE*r9R&5U}!MB~^g`C_#e;?I%!$3#g+&4VLua0s|Lt&_L6u4uBp;PMQD> z9JF%Si0F|1fg~Bpi;Rv<&iSXb2>HH6$S<`B#|?`M|L3#^^L=|Tzt$ceCp7w>(_`50 z+k^e3_DII^BBKSo&ENMJ5RW0C41@;ce>jG4-?s=iNQ>kXSlsA1TjA0oHvSJIN`e>3 z3X6a$1hpnMKH?jW9%vx@%1}@G%1}@G%D04Np(n}`dbKP>sB2J9@M7?R>l^yeWa`0F z=?M)f27Idn&p&7#%+KpUJsz|U_UCnA+kdQsfoh5SR;O?B#EOZ6CI^}huGlQVHCJq! zVOJ^uh~LD%0AeR-C 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

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