From 8c94fcb68221196cfc61ce81429e8219cbdb5f89 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Tue, 30 Jun 2026 22:00:04 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 30 22:00:04. --- MikroMaster/tmp/Diff-Config.rsc | 3 - .../20260630/20260630220002-Binary.backup | Bin 0 -> 363538 bytes MikroNUC/20260630/20260630220002-Config.rsc | 4937 +++++++++++++++++ .../20260630/20260630220002-Diff-Config.rsc | 32 + 4 files changed, 4969 insertions(+), 3 deletions(-) delete mode 100644 MikroMaster/tmp/Diff-Config.rsc create mode 100644 MikroNUC/20260630/20260630220002-Binary.backup create mode 100644 MikroNUC/20260630/20260630220002-Config.rsc create mode 100644 MikroNUC/history/20260630/20260630220002-Diff-Config.rsc diff --git a/MikroMaster/tmp/Diff-Config.rsc b/MikroMaster/tmp/Diff-Config.rsc deleted file mode 100644 index c78191b..0000000 --- a/MikroMaster/tmp/Diff-Config.rsc +++ /dev/null @@ -1,3 +0,0 @@ -3496a3497,3498 -> #error exporting "/ip/ipsec/key/psk" (timeout) -> #error exporting "/ip/ipsec/key/qkd" (timeout) diff --git a/MikroNUC/20260630/20260630220002-Binary.backup b/MikroNUC/20260630/20260630220002-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..d275e063f1f7af461127879dbde802ba8b3b5acd GIT binary patch literal 363538 zcmeFa2Uru!_cuP91W4#rP!R)yU2BPA`z7~|&zj4VU+y(w))wob0dRml`OqaZWM!YK%$uO!{jga&DE1&JesJZT7Jq8Ls( z!Z4z43}K9D3c?uH)(B&u=5R4bm?uT8p(CtOPz(n|Ax9V;%6UXZ49t~=7+5VDVjwpf zVwlyYA$n5`AsP*_%?-6Yq*jq%O)8^6Wf%ovbd47cStE@8rLwZJs!DT(K3$<@+at`= zQ6`W}#C##2B@~FIEFMoH!V%`_pv0RA`OWz9W^6?>zOtE!$Efisa6TZ0V@*RP2)&^q z4ABs>LD1C|=rt8#AE=0dKEmY_T>ODAXyX+#WN2e`^N7|O+vo^=R+okt)K?%04-mpz zDq>KG#=pqv>XAB-XuN?mZG6LDZ@gH^=L&EImnD_+6fB;SC#kXV{2Ck2|DSC9pz6jC z+KN>TGdc`GXumaden2q>oo_POEJL&^d76S19UMzXm|s6EhU1PZW+TuI*bs!!Foe)> zgwP0t&}f8EDMCnr5SES*ra}nyKrpNkra=g0BZMqa1<}{g=e-d^eG$U?A%u!xj>A+h zLNFwVachPU!;X#+!�HgOY|2!`vR!lM_740F9%DuUU|i58OT;G#!}A~AJXmgE9?)wDrT$U-xQYj@e zg-n?S8X43O=qC7pzVagJ*2Hsz4x|`#Fwudejxvae(7|3Ph66c`T?{EeZ4+aRNCCQ7 zo-WG{kf~LQEPbjbTd&e&>A;4(sjRGwB&&Q|Sy>rO((ed8Wcchs2t|^tK$9?3L5F-` zdjcs4c{<{~luB>E(CDb}*re336cV(7mRcv=|p?H`(f2JS!7DGBxwpQKm@f+uGi$q(qVMNC&J_&fnqpW2w^=DLOO(q z5e(`n1JD6Xcm+aqx&=Z?_yU>+G!_HPhD#3+9Gk=CLCI=`JXNn%rKKsfI`|F~&8P>r<^ z@}$^UGC;wBmZPdQX;lNsJX4XW(dL)_BOGDha)kX7M>M%gm+{Z=L;02;%CGUGkfm$> znI5Bl%MbOJ_%YWiGBtX|tZ#b^kz)usB0_=uAC4i~w;a)G;K(*tBG>4Gh^xOg=Z6tx zrpT12)G&o$)@XCo)i~NzK^ke8Cq^3PiIF}@(1ax_A*@;nM$9#sCv*~g(2PSLRHi2s zp^7A|}k88|B{dpeX{rfx=n3lBaJjTV7Bs&XK4yX<}sTSZaCsi|a zr5ytNM%n_vPM|0l6aqFGO*SBX1d9p8hU!THSR>>~S7qrH+AM{>%HSDSJ4P0?l02h+ z^#l>Y$_3;{H2^~lDUd2VSHR3tsM6A<8ZA?%$x^D)aU`p#S&BTSPN9c&oURK(m>eO{ z+n}>CdxVG~?1T{Jj1cHFP+*uFLRcMxl1Il7!aNYd>LY{!yaBk=2qCO7LKq8FFkw#- z5)*4dgqkCSKyAZ9wIxER6+$QoAuJdn42%oll{N^Wwg?dm*LZ|591I2&4oL$q&_#G%4C>Vn+{_7gY^Idz##9I z2+=oN5vqb7AAw+`5dXJ9h~E7XLSSGJvPWQG9KgUJSGd#$t`5PaErhY@15I08@X5jqNYCJa$So+gmIDO?huLh{qjgbp{M!%XN< z6FS6%jw1}4BrC?`S+ofqWkR<$q5Vu~UlZEfgl=F$+marFo;ivfg+4!9q3cD?qc`fFunrJAGxs2IyEvq2~c zi{T8Tg2LNHL1-`)q0v-?N~s2QF^VeT(vgA~ob43DFxo*u^x1_s*FzX8?oe}HO}n<3;wHKc0@>1YV~!W|)sAJxDqm?X*&@FJAJ z1x6xgC{V-60!P4?k#HGBMGT9vK#hY73qy<+{?Nt%Xa|lVhBF&8a617K45C(#K7dI^ zE!p*Nmnr3ctxW9+r&E?f&mtXef4^|4|32Z;{yPKKK2N1ps1-V$QMldz_K{2b_X(H! z-x02pEiae1!TpGXy zmP16S!9u6dsw8T>@RzD*|5Ejo|G0WKxZ$-Dd9FNwh;|wMXWd9)!Mz)b;TTeaKBQ{n8LvHpr5t)}*PbT!7%Rf)CtTqze$j=tKmdiU}eX1p6ewOJ|m= zXX8ZpPp8UEHTu$w4!urffpG7HVmRQlfQ=atUVzXYL?4OK0S+R=)(gY{i7}`UXdwJQ zy+}_ZVh|2RV8#>N7M^saN}CC)82lMII)&C4{TfBq3c@kckY)_|9DE+efCvOWjDeC9 z6i6S#u}5gJ!I$Tb5D1`p@B(i{cnbQ3(g-2?E?)*}T-8b72Bi|NPEUl`o_TA=MKmh= za`ak}UoEf3g=t+zo%&kKe}CQ5lUF)FvyC6 z6QRJ#ee&YuTGmJV6K8+CDJ+c2+IYwlX_K0Nj0Tc_*KM;o~5)vf;H7Ofw< zwm30=)Xq!Z*1!!tMwOw}#3qMAEaZpI`c6C$8m#K9Zy16YAcI!TP@j$VgX{MDdQ;dj zy2~Ru3RHd?w|tP_n~lv?Os9V^AuGuS#fFp~T>L?~mjR1*6Vw{x5ATZn^25V-FQ4kY`n2v<27QFfP zZnYaY2VT0q{%WswT*j9p5A6^_#+81yR-;s@2?YqB8|3tY`wbMssf8d?0<#)+gw(Fm zN)?z7sRCdi(?JD7oRlyh)hf_1`3*|YiQK+X9bg2lO>rZY1~`LZsxrV6OqwF_{8GH( zE72ChkPvpo6V!)C)hLRI3Uz=dws5h53q&nontOt~&lBX~34*Ee$PwKGiVUKIa+WSr z$^r=+%|J~$)VP0PjvX@CJfe)*c0?Go!qY4*N1@Y$WUGtRrAxF`rX`e=EZjp;45!Qr zoUGf3E0j%|8f=RdLR+m2?p8uw6ZLGiLY}mM>>MfVFQaDXNI~c6@^yMeChU`dk3V?_ z$1cP<-~--W}OcPOu;CL9ZmWYZl!#)<{ zCb^y(U4Xt<&+cSy<8B6MjgbahV5Gr*8fmZ#M%r{I1jvX(mIsk4BfS!X^sspafpsFn z{$R<70Srh8LidIc8AQ(*m@RyT&M;A4Tgk~hD1ghy52}b9vHRt5u&pOWU7qx{tKZ!jI_K(SQQ;UBD4n#DVai> z39tf&pbr#IVzoqaV+RbusW5zzAyPG-d=9r>Bn{x`DGBHxsRS4!dAmxcfq(BN!&ab^ zu8=eUtlltg5h6l4WOAg^13xjGQ3%bZA{q(}OinaMae+~Z36Yj4G&V3nqtP=vh7Tkl zP*X!~5Z*)s47iX$X%OB+gEpmGAVe?pN*M*aTMYORHpi8?BLw^2y8FQR2cS=ZC>wPU zLi9Nh7($4Cgo4lzcu4Gdg-Tf+!|lKxSs{d9#qeV6B3_JjpG~D;6p90yPsy13TwTB{ zr}gWmSU9c-w=}0W9^CqPAdO1J{n=JH!(O&{`e%B*26-=LUFl5uppr-G+$VT0IjpTwR&*x}sjcb?48 zg?qS5_X%?~&uV*L+0G0U$(owIjn(zob@rfqq~nH|O|AS}r{@pnmpyCwx%RveJcwvp zJv$7mq|vAp%pA8frzY2Pg70QH6~ij=dho!OR*U95?_k4Dqvk}Ph^gxEC>>ETdC zFN8dq5+2SGNF^MFQXmt`IJlgL^Mne%Lco^@#9R^H6Hpzg@HDU^5A=l^_NXN%CNd04 zl13^?TND=mRyRcR`jLaX)Z zXz(v+)ZK~Upizs~bkwJTh9$P)S1>4`RF^d{C{@X~N)>}0#6hrwm{6w}s8bIk2L=Y9 z{?K55nO5(gt;kHptF-JoDqc)og%?vM8MLfDz4?ul(vXNXb8>nd>Nw^=8{Ed6(Rgs{ z!S8J_%*+hol5h2_3+`mlv)0B6K``vvxGOxiaR(zrjK==4IM&%nVVBF*}48W0sU+3`4h?-*nJkTRi{Fy!K1u=Z?0@D?K!K z!86C>6FXxJ=22|iqF8)W;``9OD%5_)t&v_=hnNSU{*2?t>kim?{aEv!5x34M516gX z^{q9!&rFXAeV=V=J&%6r+^XKzVsk}do38cCTt6(^@yzeMzOZ+%DIEu;XLV`7X}dFa z*Mm-(x3hQV+3);3#QgLjZv)uJS=SDL*J6}EfbjB*AeA%X8&L!*e z=O6l?-8QmAs||G@MET9lzQm?oeZRKWt7ywui^Wgd1a{j{IInciDqji}L)bhSF8R*( z4E{6QL%kNyPdOF6P5ENpt-5vhOj;XwDjBa2rl|qV4X;&HbAoTj9~Gp3WV&!^YbQs` zPgldH$0e+ObA2W4ZGJv|p10eVwoL}^Vr@BlpyJK$`W=pJ&Ya#`e}8hLWeyu|XV0E< zEV^XMxDjKrZ!NQP-!X1UrR?k-i|%LU?VQn>@$&4b+lg;R$v(AOba}nR}YR95iF zSF8<>^4iRr(cxB;j=O>yJZ1S6hqug68ryL4jiT0zh0kA{%kN(6+VTgs$Fi1(xrzd! zF5L`o*?5Z}@kFzD^Y{J__O_2wOFZ4q(pL0p(X?4n#-hioT$~?X7`l6lNPTOJdRV1= zOP)`Ad{{~nUo{o zD{#I@&R2-UJf4&%7x9#0fq)~Ga|CiJhc8r$#9W?)$K%R`I9tFb5K&Ev77$U5{~%Bj zF5*fRIA0*fc^sZV%9gN&e1SwJ=8MH_nUE&|l=L%uosE#^4`v>q)HjfTgj7?l{#Hm8 z$@v9H)kE4HAXTLuTW1QXr~s)#Z3#$4mmsw3M^r_hZCI+B0IL8aYB4C7NKR68VnSkY zV(VZIU%M4x;uH?$XZ z?z^_#xSkVidjyZ~%&+^Y&!=tsc6j3zDbM%5?mOwW^IPYbBa7@`)q1qd>jm4(L82;c zxV6{CN%e2{RW8d3o1i62VqP#??@Ur||ecCV_{K6-p)NWt8L)BD~lk4ah;GG*~@ z+8=q-&$YgD+~G#!qA4#zvrn|j%{UbuwamNQA2!8bBNi`Hk2@+&2+0}yVd|%D-PW#_ z+^t`KOCe+OgTPM%3L{l>3<#?jE5?hlez;@Rik^;}V^}2xLne!Q{h*><$^+Mh*G_D2 zBwR|Zx8vzC*KdGTtXC4#ZNk0VS@-NdecIyq)ZBHn_y^=hI0<}cwQy&1dJ}WZ%*=v{ zRZ$c?*GOX+tp}ctdmF#w*kHGGy*|5HK!8T3%l>9Dga=inq73GdsbmsZE9t&T_}#RE z^-3fMW)U6&O|MUDj*D^OZ2oNSV79Rst(yBwWvKB$D1%88;5^*QoY8%7Yi#gWH3h>h z%+1pJTVohbH#Y}@QcY2+o>9M>m;?kZeVKj4Z<1$r*KGYKPP9+5LoA|Fz1@ zMs?OG8JVZDO5bex6o5JJ>yjeJF7_R=Sd=(xc-M%`F8h54_Al=@V#K^@3HSGxJP{T& z{`?}jlSpfS0XdzB)?HpIf}=`@o=C7Lx&jfe%COt@lIam9+{eCHQE1b)$dcrj�j*=` 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

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