From 96edb66bbd22a9d4465c525396526a242f2285f8 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sat, 25 Jul 2026 11:20:04 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 25 11:20:03. --- .../20260725/20260725112002-Binary.backup | Bin 0 -> 390919 bytes MikroNUC/20260725/20260725112002-Config.rsc | 5531 +++++++++++++++++ .../20260725/20260725112002-Diff-Config.rsc | 23 + 3 files changed, 5554 insertions(+) create mode 100644 MikroNUC/20260725/20260725112002-Binary.backup create mode 100644 MikroNUC/20260725/20260725112002-Config.rsc create mode 100644 MikroNUC/history/20260725/20260725112002-Diff-Config.rsc diff --git a/MikroNUC/20260725/20260725112002-Binary.backup b/MikroNUC/20260725/20260725112002-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..32e975cb3262430dfb946283157b75728f5ea00b GIT binary patch literal 390919 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�El`?2Ua?pmVLeYDBVO| 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
Tswa8 zAT22WMw6ZB0C+Djxh0SOccfOOvyH_c^PVN|#sk{J`4(<g%Qk#tK(M>o_5YT0fi z>yOQpki<@pA}Jaz`4QWO0;lv3nl~<5VC5>ShPE2FtnQGsjGLLVo+wMH@kz4ud z_1Xq*0^gvchq;q_X7UKl7j4#)R;Q^?BTGAXllJJ(K8%qLe#F;4>OufFWy4`_8~Sn5t=9ld z<3Ye@XD7w88MI#GuZ;%*Q~0a6Z&tzJQS6UOQj$@L{YLjIDb{7uS^eQ8EB0q{As$|)V zw05YImsr1OgvJ96E}_xpY!kiW5(Yg=SGCF0(Cp+39$dAFvIRl~57h19JY8CC5d5H9 zEwnnmB#_yOvF-?c;z7naX@7TGa1;%?&GE!1D8uYF18q+>LsSRB1Gi-ny{YJM_ZU9W zMt>rez%F5&zbevE+;f)D?N=7|RY5&5%5f=>s5_dYj^kLDg&D*}{6>Gl$EI!Uqf_fc z-~y%0`Y8TVv#lpjL@A{kqwpUL!mGT3C8_Ga42_n_?2GAC1e&;&n7)b>->gt7gQO_ZZWV2jI8;nwalc#BR6Eg$z9|$3 zt1GL-s`5>7oD8E0k@yghdNz+Tw~o^NGKagzQL;YBBNBRCoj{23FwXMg6;T)j9fbqy z!H3acz~`jul?|IJxP|0zddFns*iW#PpjZi(#?Sci7>EN6Do}N>K1U62&C3KARH(0d zrNEO8G~Ej3`OZ{38e5H^J4v+Bk4X3PM^rx=iA4&@=oKLV5G~baagSmA15TO?G!@ek z#arrqIe%O8oqq5)ms{rb`pI{QPW7^Z$T*M!T+Z*+CXY1Yq__9&pH_#7mV_D?*SItUeZ|Nn;|cF3Z7NGv&l<=e26dijPRwogyqEU8diDA9kk%rvB;q?eMt@Q2m$5N3bU0Z3Z>bid0MHg+@ z4=uW%IANJv@1@zh~9Z9z9PY7bmCtOf@5f27PlBY$D6NPlYqbwaK{U&QK$J@rIL@OErHfHP2pO=C(;B>YX z5q{~+oJ)`==OqUo&f?-2hmnb$MEzbe0WsCnq`(0J{|1f=ZrrGQ-fg{q**GbwEK`9}G-RMoB>cR7< z7saP20HM7pA*-Yi&zbV*dIedn9uNA)v1VK2OBttL3U<${0Kz>T7s@A zpblP@;}xj-#$&diok#)V_Y2Z<1Y4as(l#Cu~k7;9kc<)J@)^~ z`0lN6-B<3k2j5S}=4`G|8${|Ct{R>s;|tB(DY&*=!M;ZNdX}ZxeN{8x`Rmq4a0N}W9+k-9lXl1`TDf6r&F-AeC;BUs`9$s!aD4=T z4()9Gcbrl5??^JHmrnE-Y&wzG0TPR*JwuAo-!9cqMFNpNY*kLq+D9-Je=?~>3tnQS3W9AAy=EpF}@%Cy29QC>((- z5dD06oE=5_?Q+Mi9pb#>{*2s;Idur|ly=qCj{u+?OPhpT5|SngQfZLN(AdNn{lxBe zko?v!I-BBay3~u;pnU(f_fRLW@Q)HTDZf!2f0z(gEmfDyRb6|AE{uil=9|A4DA9`0 z=A7rAat)*e0dWs$#l>k~WoiKC;okdjwTytNckTAVfm$03{HUpzK}35WM~R7R>BXi8 zdLNRP<;n>y!K+E~gBLtk1;X9KD3A$<4Gh?hi^%en2?iz4kOswN4&D}s`SofE4p#lr`Zpfb zHoRaOSqZk0l{fD0>ecqo?aub(j;m@DJ>X+iK7Xo3itI-bS@kDqP+ZLFoCundWCX(d1r z+r+h%ygr1l1H}*89C&))+dyINhq{2e`j%PL(Ck=^7;}dZbYx)24Fp#X9wd={%2c z&`>k-WFC&B{*Wx-rc-sKDGr8UoTNIaOhOsf4ZC<2TBDUA!8K((K9bC=6qUULD=94$ z6qQ4!XxvwE&H~ZZHk3BPSqpOC8BNn|QjyOw8F8OBHt)et8@(bIgk9_}R|sm3T^b}S z(A~N*Qn}*Ln&k}@y%|NlB3IavrfgUhW=CTb-Vx#FS6wp>L*qf9jeBQ{S_}!-#ZWa? zX*O9Mq1n=8fTBN;nuv28Qf0c3N zUW3=+i)^r-pg1G{%DX=twcC8`8nFC?;+(cvsJ=|xY&OeHvAVK&N5qhMzW%0%f@Avm zl6@|7sl=3B&JtQozj!TIHkTp5^&Ua@b6l;7d%a&{`;((pN(G}q7$@}f6&}8ie_rCB zef(3$KlAwKr}*bJ{(0bUHSi~x!=rEGp9cQv;-8=3pU?5n7x<@%e{R{)#ac7|0|4hd zF`7c-K@BK$?J(m%YUjaR`a#H~_axsxNMhXRXx$Ec#Qb^#T|tdqLWE|0Q%EPcq?GrX zkHgH(qjgP8ozp@q-4#Uhm|8*9gV(QL%jOkzPkYv{U%xg5a&=33OzT2J2$69Z1mJ4c zC`q=)1JVKHaEKY5@dFUminCyf*Z_Ls;Ze+H8?~@{Pg@!m~s z)sJ}Z=lxf&yiJeI$EqH@B!2Q#CXzYy58#Y_cQ0syKxN+m6e?niw_#tsdf&-M5J*0b zlAZCvXHn1Ch0K56l71ZNy06xFERnI3$L#XJ$5HZeoSZncS9)&{CnvPc^uFq_P2>WG zq+SQYZ!7$z%|`wU-OGz)xKi_>dZ3o>-MQ-uHY?QN=f5d<-`nUdvmY>lPdl}isxRGL zZm9;rq4CCl5gd$Y6jq!UF=vyWczi<>Cb>>;g?3_K0%d^S=T=%@O=ip0;%3buurW zfn{oB5exH88u2$lLf(t>Jk99haVI0nMFE$LmSs_AVXlfaRf9C4Q@8dCr$U2<;?IRj z4-XZI0fXJT5dBa!G`|T`&a3xTyVX*QLV!bxo_+Hb@<=sCf>(ycfm@~>Qnf*MUMLLLvI1V=XVNs z0W%uL8$-i*79G4Ag-6k=5m&#a@i=?m-< zZe>go*QUR^-jGmNp126w{}Q_1fSQoX)GymF!D&YeLGh z8gl)$>_c3KZnYDs*Hg-?uAGL0ZhKgDoZDz3WzTNc zO{v|<9X3?jt@zcDGKnZIdNQX**np-myvDw_eLT58ZKkRne*GS;8m-SH0&cYNAcnyp zI?6)R`xn^nrYa;Z2Tu#Mg@qbKVP2@>ELC}tK?LZK8!EL^2C|dwN0Y12O?s|wTP|xb zJ!z?R-{cI{+V?d>c59vcxYYNgTU#-md+o3m^FUCFN5b&K`bQn-I-$_DLOCm{@S~ygsPo`^P`@N`!d$PjTo_X2EMo}?;M%EcH}zPE>*pT*KX1F1AVRG zHAEg}Wg2BRuiL zRJG0*_iZF<=|1YdOZ+n~j-zZVvqW5qEy!rLQUo=q*V-~o*G3vr75|BM?2?#gavY?4#^DOClsxEdnHd@2JNbV0#jZvmZrrAo9Z>G+vf3!Nn)gj=3F7NWL+&D zXaN@}xg=g06@E`okI{WdrwWMC# zY&@jG-3>n2zCH}&fh(n-fBlcCs@gyNzgSBDHIdT4ZAgiB1V*l~KbcGUY zmHk#_KS~V*oIBhTvhPt`9FGs0y>z&E5Jl(FDO$O9fr=VfGF9J1y~cc;ML6TD zj>A(N=0XwSaTfIzt*gqp-Ug?Hrs*J8jR*W_-pY6u4?34-GN7PsC@#^4QtEtlLg@jR z@w#^^P|1Zq&86QaMBG1Dxi?hlUsmO6W%Tpcau?{ce^xb_)?%9Vo!xf*(|SKHOg&pS z75i~?x{(~F8%b|4?nfKjuIWlIY{(5RDp=_|#cAo;P7!7W$`q=gwY0k2UhS-uo8V9& zUFpP^Fs;xvOcOWUTuCuB(KiIFHYA`j5M)R*74_r7=E3Gp#ol}Y&7y>4Z*d$U`y_&> zRpXKBrCApBiorR${V+X4({UcH;?GCnV8Eq^^+h6Vkrc-$%7Lm^@T*5*77n8#%5r+C z-Gmw3VDyX!QN~4V>*IVJ4hH8c$F*mnxk3hMi1X^@`}-KM(^R#NYAA#8del5>s-WEu zRJ|V^hT}njv|ju$kGJ@>rKYMaVMr{7qScTWTdh_4FIZ5{>-w@snCZ5c<)*4FtA;5$ zTu(>Hj4jo(Lxp-q-n8$Lp%#-SzJU5BWkk% zLu>(c8_v?KzmO;K;o(9aC3%d}z6)78OcNg@5CGQE8FrNb^LSv!!$I^WK8Ob> zOGuaUl6f8fm@X-&5!c^@!_fdBgh$Ngq%aUl_9UbVhUX1N1-ulm`~$_$JUdKRWuX03 ziq&1ASK@0cH&w8CF8+WOWW+hx(m8zMBQv+JpEvbc(oWV%~h9_x8vTf8do=)7*{{Iiiq`R zEYj5ue5Yz)X*Dsf{Ql*ovJVt38;M>E*Gd&!w&4V5_EE*sLsIk7Q>JD`ius&aHu0gu z!V%R#Uciq@TBwGW$8o`leJ+vMT_+>KtLo(*M|=fbd_U=MF` zh#Tn79ia-j#YH2$x8AEb%i`N2W+z-WGstAqy9CTxWmC`%-STjnm*h`cIC8xBu~z6% zFC2(=t*avwOn0`ut~O(l(D0gcv|JapaE(%#lgZR^=QyW6rW3KohbCWqYlkDW$c{Fl z#)&7`K!d27c;HSC$8DcQXQhmKopJ9fyP9t1s9_Pd0|!e~W_rU?tC)cpKjvhm(f4Z?B4C(7VMN^OidQMXkd_0pv87LTTjb``GI^|hfB3~*Qj zxAUT<-i4%90)3e6yz5a2`Jz=5DB3ej<~6t0)SLLio1kK#9c0^EIJ(L zaH%1Hdnj^6B^PuulX$-(SZKgfVZu3KDa|mn!|^mqqAVQjrcPal3SxGi9;R6@;wH|o z{KK}yY$hJ;L|(NCrp8CdVUk3!UThtT%HZdmlB=?FH42N~vAmD-YpA7N0WbJ9&3TxB z<0PM^*$Ia;Ugrg_XM0WYn%Bt0Z4^;9e2oLZ04S%p1dACNBR*7{Yv0{^-rU@Hw)I>M z2?~xU(I!?$J>V~QIBtSa%bn)(aY%jqx>rnwG_xXWv0zI30BEG+WEfrIr%~?&%BWaM!W9 zpy2H6tm$l?(FJpjesoF~&(29=izMadYLrEX@f!o%&gQlX9-dh7yvmVbfeG8kwAHJV z^n)7Skib@6q`;8sk0T;(sWRA9+sK)URPQ+Iov1;2#LG8KGn92N!g!EV0NRSv04Eu! zYk_w!*u)*qMSNmQ#f0M`9fn2R(@gd)p{Kn-oi=Me{>YMx`sH!lF`#u;)$1zZ{dz&~ zvkGaUok#qy7jUh7FA`Yr++ncS3!3to)II7)+bJ~hTH^9^>hx4SdGYMg?#9-$9sDsj zXL${^HA!&`CEe#MWW1kj>&-Zc;mgCS+9^W10#cTD^YQKuF4=oon`i`;rv;8at5WgP z&T*Q-bbCFy6+xAIS-jH<>s zziyu#y%@o!EXLTVp{)qeWDX`q3X&;9bQ)zj&f9W|U#|M`;bD~V;vH}xnAcDmqN~-> zxKJ-^F#k4khqJ-xD!9|^G}{6H|K(s`sW~Nu6G_%zr-P+t+wXuRhvDIB07^l~(Xhi?({XN;etR&>(0A#@vDvQKJ@J)o8QnuPp;K)^0q{hoOEc zWx&u^OVQh=KW>*%4wBPM#-PGpFUgd^+xw;0`f1TIkWwEmr~(2_oSy1w>N#|9kAgnB z2HTL^YB;DvhPSh?=Oso^y~a~REno6m=v7WE7enY#Ivim$4kqITtCt?CgS0r-%csfx z-C1@fYLklyOtj%uOncaok|7#c7&3rg|E;wg#q#-jZd|0=I~k91Qsg{IgpRWC91tR! zMTNh%(>#xHAFIi@rHw(}7Nq-=(R6{fHc$pJdQ)JtDJAaY4M4paWjEpG$*QAmKC?B& zwHx9@@c3t2KTXHXmleJ z;`BbAkQq|FXJ2~{CZd4~+x5e7USKmXRIn$h)Rb^0%mH=@TsLi^^F0Tu>vZ4TOE$<) zE%i{O>OI+T6d&n53khVGGoA|kmS{-GU_{i)RKy-Al(8W+#tjN)@0{lktqLW~LT6oMbtI z7ptQ#94O$$B6Ac@AWz7MEG?fF^aDt=DyjxbB~GoI=E!{Or%BO>l3sQ`D!S*9zOK)b z8{^x%`PSOq7eM#rLDO^V8a&BHMHx6D1z#9=h?k9?zQv5JA+tP?GY1ofF68E9KvEE9 z!3YGBk8Tzh9#_aAvsjVWAtllJI^#;qHm7#z;FL8sS5#c6B#QbtQwq+H-i!uuFD{VQ zLPf1`eJ&22CWs+vIQ+2%cqT_GeX2LwVBw@=I49W05Q}vm>BRf$1GQp9s5Ec+}X#$>@B%C}5j8F~Yq8N~eEvZ)`Ltp{% zjvJD2iYSo-S`roP$Th!KUcIDy9{AF1-ZJE#gqQZc&{s8Q&~5H604g8%dQqNpeN2cX zN}+)rN){Tw;m>^M-0*G1N=tsc$W3oR9Ky!NFQ?)37kG$%3bB{M{)=^2lLWZTlIA>{t}3s#6OjL}pQ*i^qU` zLaD`JT?iBz;=hpbM-(3xMc6yWo_%f|iTk7tabuk1EWQRj1}b2pYikqA^FsXYjTa*8HW6SpmKxzj z7s`;(ftY3V=waGFXAi`GWzE4!ZJ7k8yrzASZ@n1xDfVbz{7PewF4eNASt|;d-V&*& z6QWE8MXFxce4J364sOQI+LQI&@2@J{Iz~IGVRDWb>uH>h^TD}3Ic+*(swYZicMdNi zGMA2v?JQDvyt~=)kSgaz86L_rPS~s0UoI9n-6qE(py~%1f?(?z}`85h42{xpu=NXSG;niE2ii&V`xN|4nvjiI+S)$YivcB-U{IJ|o59K%f1H_t^UY zRm`a;af0}C)Qf3n;2=bHc$$!q71)VbBA{Db3)3%dC60bdug!ps!=iUN~lFh|O+|+59#n`&V%&<*xVvbHoenVbF zBN#%vI8_4MNcbd9;)3tD?yKV{gHw^A8UuOFp}r&l31+}?jd=5Mr-*2WoSB^a#BZuLwW$`$Hj@w;7>iMI8fR%jr*@=*#k4M5F^gm9 z)@zfEO+ZkJGZ7m&@L_8OSf5ir{GqdZ_`@I4?jaeP8;nmK9_3iMPU>UK zj<94Iod}yHw)kzvvR1G6O7Ff#{1fh49u*4+tkQ)@af0o2irR8sNmFkH%PzC7q;r90 zAd340m!I1M68-2fPNKdVgfJq?bmGeKD5LEs2p;!$+={fujzEPJMa~Drr)6z6D?Z`y zGmJ?et0K`2#W<3h0hpB3IX%EAE|IX1q7xr+u0nN|W+x$CQ%KAVc5nht&31=aIvw<* zOoxP6enFP9T$l!c_(24LP=!bmexs{`R+Ikch`ULe(4wF4EPZNPkep$7b`C7?_$Y_q zwSR{9_05RGWhxrQS=&n~nxWSRjp-PSdh&d0^Rk&ZW4z_*%xMlmfi#b!StnJc*Fd4O zV;Hb5pVr7E;auE|#W&^FUEDCusoc)_g%l#mA9ma1^H$aO%HzGgh2a zpRoN;=Q}7}F^2y~s$G&}{Hz(mKzvAojUg~A5$C)ZBAYnaKejndlwRs4mO<-Okp>!Z z-;ym#=I)eSor#?U<~k2M*CPVEfSze*RBF*XZcyeNkCCe=fl-CcBU}Q=7gtKc(nP)z zup|n$sVGHdYC0XA>K%`;nS%J7(2nR~Qw8h-MJ(R7HIF6#N78&Iu~PBe^2Wtz5$GnV zhcM+OUNN4a8D&YMzM~^014xV$O6D{eO0%kbLkJij@(FIZGPYp5P(CG_Bw2VyS@32{ zhmOthjVE+$&KN0Kc*ZBS^zNW-$)~KGUb#pIr%EM#Sq*)EweDb1?;g=97`=PscEbt; zd#bz9a0JVWyt2AzmRF~gq~hm%A$f($qUzr@EtuaW84d9dL4qCUVT9Y3INeRvo#3tk z0Nor=t5?hjg!ze%)(}gQex`Rs5hu4FGOF1EKVaaiZf5yLp&~8A2y+ z;^hsy;?zo&P;Mo6>E~PCuwUzju!vn%rvNwZO_4-59OmIT9`q@sMsZr+IrN3u0h-WJ zs{I=d!gDzQ($$ksmAR&HRn?pn)Ejm#9auB76RO-1Gm)eEssTcmK=vK`}GrD$kefoQSJv4 z_Y0*Q{R@%h#MhO2xmu*0_ZU#tE1Qr`NT;jXWUP8Wxsex&K2Ce;hF`^sboV<@lyMj* z<04Y5Iw=$rCn#M^kviJMM-&v{IURtpTN}7jluSKNLY-4PyX)Jky(&Apc0-q)c*Gc! z=**}YuDfA3+DOSF%$2LofBE}MNE&A&4ks?gMcNPx7uPYe;Y;>89mddh3LcuUppI2| z=#eyHvQ51cYR(xLd)o~Ak8%oz(21nwIDZzMZRKo77#UzvU7dCxhw$LGa=G$Yj>90l znb8%nPSdUY88U))(}!Z{R9rL>%b29EcMvy5khyALGFD87JBTXlOk6W2$Wa?DVOFdW z2Rl0%m@3+A7qCt)cr2YTvZi+6KbD{)NKF%l=m+aIRCw=K_Tx;z;mXdQv(;%f359ER zxSU+s$D%krQ>B=*I!VK3>n)DL-br{AEpj_Lpe{aI*MC>B*fa0)^AU6Okvdr)w1Fbfu^-s@czJd3L3i|?6Y zV8;fq{#3=V@E!7^;W$?qU9w`o6d1(ZekLWrFO!TXCqW?&dtOH+`cU?EWG_J|X6K)h|RF?)zZ2a|~PG>s=s)=R9`M z+$0Z;_Nv(x(;)eKeAB2w>W@I*4N!^e97WiQl&nXOFbUsfner_4RIvupBZ~zJl`yG# z4P-ncY*y9l)6&FU8j`C0Q*@WNj?#46N~S8!wG6IUN#|`(WhvZh=~^D%Z1g7Lmz%B# zkaF&-LB6*;CU;6ut^$roY6Vy(x2D==r$VR2w6;pj8?ha1(RiOC$+_Q~Egh+ZD~PacESIn)0nN zOt3g_n(~G)yP1UFX}jB0l_iM^*uJ>iJwAk+s=_wZf;xzL z;W&?Q4nP9VU+v^3^c#oDJa(l!8finBp|n#ta3bawcAIg5IY9rTb|N2kuZX@||IsAl zl@TgNM1?2huLD>~vUChDZt&JnnHQyEV<^t3VM6z`w$SQDj)LAZ#l}%3p-hsyC3WpY zpsFZ@9(RUfi^!a}c#-K}EV?H`+HS714u^~MLOu;$!>!iitX|p8GFjwcHV@s;KOh-J+Rpq;u`bIr6N5H99mItC=lN85SAl_WrKr}&F zaf5S7(+!;i4zlEyz?~KGB+oR=t|4DK9H=a!gI#n0iwj6ZeRo&DltZQLxfHilO;Rp| zl+GzO*+kbz$i^X+o$se4+$DJt)28A)lI;UXCD$my?Tqb~1lmwSnRrer&kUtCAS^xp`9X)oDZ#!~D@ zf;@6UXDUnSSde%zEE{qz=6?`Hi9UwgNI6CRpFH~ zXH?IM6Du#Blw$?hR7_DsGSkv}i>xq9?Ha3QYGQ8aVohvuxcEi^3m&kk%a@4MHVo1W zlU_vJ!!cE^ry@LU1FL$9R?}*UU!j~PCB9XC4CFQmS*X4ubppO7yV;LO7SX6J_*b+s zsHHb;tcs9!wi@XLN@LfdN2KqXqdG(ZWcgxgbze5#iIk{dUQzhZ$VLwc7Ej(!e9a#h zW2owtGl{kDUPV)HW7utOODzyJRJUb2S`u((IN-@MJldt&kv-z@CDy#DO5ylSGWa{hw^94kMO9%6(c z>$u{ySSjCDcxaNV@Cz+3;lR-mUrL4|(&QcvafG39iL~873Vl*j`!0q|Uygo4!yAcM z5>Ha00W0dv=7LkLES(+=qA-DQ8jl3QbRI@(-2go2B&#E{_<*S7b(crgVv57l9XU&5 z-H%FjR3S-ai%NRwGak7p2_M%g*tX$~3A(Ll!+b3L}HMm~J_kL_eunuJKP# zSveV``z8kO*22gEJt?jxWCc&0KN_Ut{)cH^M12nNRLqjvm*xbz5w%G9%{}y^j;SM+ zrQ;&XIASM;7x^=!$Y^>d{h9ab594fjhP*aiIA5x}nOPACkSt~@sG~wZYun0bDPzN< zIw?{F>w(>dFdG=URi;Vg;YZsqwjVd1t$%N~@o;Nvx3Rmn^U;0hUN61am1hK0O+#ky zA95O&&NryN!1WLI0%sh3BBqtQz)me0X~dD;o3T?f7n4>_&Z#o2Uoki%>f^Gu^mRP4 z1(NxoBR)Ef!{0;EQ3kz2RRU|yiL&aTgde~}X~toC%DzGo<|jn>`lyNsS?qS36L&5| zdP0{hucjffnOUcK#ZXnvXKsA7*l=I>!LVD@7Z5=`)XEH>DlE}pAbXNu+Bo72Hqol% zgU_O#&T4C_VEy^?t>@}FqfW$=Mw;4l&&ZT%l5JC4OqPJs8;F<@ObC5vfi4T{iEpZf z!ymzi^NZmh{VGr2Y2jnQc_6EEbtI@uu%JP3r|gX9&RRUTY3nngQ0?Y{OV$(NbH51V zg${-zg4ABJmh_!5G>c5I$1OR~85g&-w?J!nJ!10lXhtcKRqoP^!Pn0LGj?Wp#NmvD+|jYr*ZbCQVGT-L%xYs&!d`65=K2cb=LZ z`NH&Z(YbQRv?LaFR#kmF&5H)xCiCir7e0Hqwf6k6QS_8d^n9GyI2D=|z#nkdvS>u#fiVu}?avt*gmR99xv_xg=T%SS6o8RG@ zjEbBeDxf^~)_4(hl_}J~3`Q%|^->~@BO&`Pjt6SFu{e&iJ~<5w6p;d3xx}U;t0Jch z|M;+X6u3kK!ZlOSh(5#Z~qokI(jujSNT-qeO6} zli!u0;GJ|BnIOPfmL^ByAhU1hn-VA&11FiskqB^KZQ0xy+KWPy(xQuYVu;!Orx0in zjh3`MG-*gPSCpEQO`wCe%+;lNt8O{Xz-nDVf>X?eGGcWtGAb|LW#UME zD-DTLx}4Wll{qOMnHL;1)3$L)^AiV(_3oUMXX>kl7q~~Ah{}!$K~AsqcT)w%R6+FN z%1Zc}gX|?$*%_;_B}yn;@L0M9+)T2Wr*EwJ*$|}0+(4@J`=oS^8`k@i zrR$P+<1@!J8g|sOFPda}1&Uxo{#4DAUJ-W)2Vg~vnM|MyxY}*xrwhH_ODLV>VsF_a zlPgjW3CpWGQx%dFvX$^iWa|(hi&fI8T$1)rWAK}R)cV5DP39syVo7qOpZ4(GnmC*S z21*c3b{ytXNKJ3#qbx<)?3?n!g6;(YV@P;wN4{2ATVi;e+@Z}eQGV;X7~OqUGvE2^ z)<-DT{aJdzXRlOkJwsvoU!M8dtp$<$kkYc7MaNyJ6hhqNA`(Ta);Gg=@OOV)}h+4%64AbpT2lNAZ4JNt+CzvdOXK;GDYH=|MP>K#%L5Qvl;Y(F3 zFZd+Js0oT#5`H;|IjuKC3X|Q?+hPlDj6RN%3zvc_;>}eLGX-TWRil16Va3M>Puw{K zc)`e@y)P z?+L$t&E?l$_1_2MmVN^3zW(Oitt{{1Qj$-?eoFTw7;8|(hVpyDqDz$oxl(gg=@Z-w zDjrg96-1PzfgHwJbcP$-rAALK+#RX>alHtN}O}h~^NnIQAkShPOU4 zDLj*x$ZDa)HNa3N_BqZ;*l-U&z$F9x0P!Rgkj>83=i-i(-i4nE37~%R6D7~g>-yQe z4W~nc=s;&Z;J&1U5D>nYX6CX3E{R23w+KC`FYe=B$7m3qcW<{^LtLfyrhD6~4$5na z)99W8k{b5@?740JBI7+XK_L<9<2a)}>Vc2y{vy&_%r&}lD|&HGuA}I>rV19tKz6%C zU4gpb$Bv9N5_d#)dEECll793C6$ks#o8}-d*xQm8hO=-EdTU&$Hm)l(%784lM^T15 zYQjOz8pyi-coLJ&wFfr&b*QX%`+lc?w&e>gshP)k~jEouMZco-(28=EKayYp>F#uDmKIXM5^LS@ABgv4;n`0$7%0`es(7R zBu@H|qd`1$LnzE9REZ3tJy$`i-C0_`eP`wFz2@rT+QUbW*PlH7aO1lleZ2W>Yx{Sf z@9e(#hAD>C)x&!sD1oEcSZm~{J)mcm$eJ%1Tx3Rv|o*dvi1hE;N8i7|`8tIn8b&5-g*# zGct`NN7o;^@+c!D4Zt9h*Fus3YoX=`R1jO;_=sG)nBuO16~kx^wRNI*Xp98_xT@t7 z-ZL0wwfHD0*7|*^=WM!oloZb+#G7;ml5H&sFQo??+o#J?q9n>y{qrwM!)$Dy-nr;G zB*qwJZN+3Ere_ErhecTRyEcDn+8~l2#O_n-LMf{y1od7iV@1kRgDXl{jrnO(wrbGn zGWIeys*tkKEAUd}vSmY|xNZ4Dc`K0XRpZa4QrA$n`BFN%KOMWoLG;+og#cS=QgAf* zTgHSdwo4QDW6zh3`&jCKMSap`FIBB<4I6WZrn;}%tWjKn=ZpE@*{e<9+AQ!hWJH!%nP^Y6$HwF5YROH{G_URq!u%NP!sfpQn4AD- zIx-;$SoVx+{2~P%t2`bW9ZL_`NG``COICWAmhESsxkZ2IU4q-Joqnk5;-~0` zuC>gcDFjG!0Z=tSxUWF;_)5;Ls?pH1zzJa`Y&C}S82Q_}F@j$;duWDAI%8cEC*&Ni^(R3I#e!SNB!T$TJ zpfLVpb#Je^|7}ZiOK*eb+MT=pq?jImoRS>x{QkkZ~>lc}fGxYyYKHUyiH*5{ptFRDe>je)n8>s5glt|Kn`5>t0`ZuHX9 z3Qwr`b@)ZB;}_%6q7F?jx-QfrNm@S2zGsCV?7^-z1ts*&zv#F}DOSbIq$%s=1w$fz zrpCYtD2}M|RM@4mNm^^O{t6Px(5|*Sv=P zWt&*rAlgu$;yqnvJd-1NO(X-&l#ew9(bgBf45#|I8quzU62T*3f)2$9R_MEj7DpS3 zRGzfVeYQQl^C!T#QwRNuS@&YGXfXa&vhJOqBkN8@^L3f`igp2RlzQWotc{9cA(iNzHuV7U&W!4wyuT8Lyl%Pe(VMe$gwDp9T#FWw922 zc3!nxtro{&=kcP~RignWMxDiJeWD`>4bzMNzRIAIFYACeSf+OYi}Ml~WG{yMU;%-n zf`+rr=Kl4vHf?%WM{nU?JwNL3>pCIqS9L;Icy`o?l2bPm?DxL^ozIZ5`PU@_Y{tz1 zvl*MeB(Eal>+4@%lpFG*42Oqu%X4To%F;LI7)G5$=iRkWpT7EdV`q2$8Q-GU4OVa8 zZr#2e%wDsT=j)qWyX(4>cI#fV-CAk3S5}%U_iotbcOU8Q?p+(4?{03G8MRy2pU!tT z*S=>)@3gKx{!ZJxeS?=f`sMc0o$C+1q~Bh?{=9dV^~;@=YmfY)ne_5XXL)I5W%+J{ zzTLgEbc5au7FJp-cUyOt?zS8B?Ox~h^>O;4!Akp1>t1W+?(N&127kS`eDC^*`p_V% zjV!6oz2&>h4f(pV+`cyMHVp1QI8d!OtyZhuYPFX;?RF(6$b;a)CMvEjq8 z7cHK}S#&fGvp&TmoCBa|M-3y^_tl85L1~;s=leKNFmwEl)-r$5)iOpwAXnDaH~-rY zzVjJQ3j7!UD(juUA$sSXYQ6L87~1PBnwh!ve>=JZ_8#39o`*%)UMz;AMFVal9QN-l zH;XW99{rfBxnQo{)2KMMlTuM~O5Tor587*wHd|2Nht9#8hbl|cqS22t8PPr%q(`_y zpnD$WDnA}J^7zM5cX?BeJEutlXTc{E>xg5)KxqTkEG^t z9-c<(I3B`H8s`Uu>tL2HeP298UaA^rgKjV_arz|6fGYvH(L2ttFoEjlxO<^E_7_l^ zp~>tl!_;o-dt9)7b~%GtOUC>NG(n4|7uqW!?4s;Yk3|YB{&y=%cTht z3_j9xA&|7gE%Rz)UWTVNJc7UFrh2xvyQ+p^{-be3bzTi;A(`~>Fz)dPdRIdwVL`+6 zj>957&tC{pAIw>x!b6ev(m{9JA2B_pS<%G^0uyKq4U1Gm z9vRp>_!TeYU9wD7oHQ&7BP3xP(zsKe$%nhU+dJxBc`~0y2kOPfwLmX=4(Qb3^lf*&8DkG@R;dJ%foSOEcs8@AHI6Av;JIE6`hH21lgtUZm;d^$h$vI z(;@yZ8xNu%|M;q%?5sa}zP|hFqxJ7gH_yZ5aXQ?IdRbH~b#~5&2kFOoXQ^|$v(mY@ z)c(;`JBE+4{_OGg*2c44>AKx&F42GO*6Q-|lBVR3(~yh4veY*0gCczHXkhh8rR2yo zhe?f$a#f8qReQ6O+D1Pcgy&J_D58Gq%h*L%vB^SmkuygXO;cKW##AG`JUyO$OSj>h zbIXlJI9Q;Y>>Q`#L0{#^>Dek?FUEOS&bkJ(vx|s0Pg70@t#y2D8fQ%*C=AV?rXO)##zV z(J<{t-TXMs3cNRh%?0UCwwKXB_d3n?ox9C;t5sG*+~2G>CWF-PSH8dGsb{xiR_h+p zd$fjL=RBdsveuP(S>ayQk1Pa0!ii_>u)Wh5lN8Ph5E}}`!RHHD@&(f^l9Yy&`nBBhZ z4k5-Mw&93R&!U6II3~e_a<_8gQyZWqTZrvZvHr+`fWddSb@yJC4P@;@7+Jmz=m?i3 zZ3v>HyDqFrSj4B1`ZUg>r*M%Qd6b<-nPS&?fkd*%!KN=-tyc|JCy^okv=v)Z%+KJhvC^@x55Q>N@&FDR7 z*N4{0RRT*fPLgOq38<95tXPae9pV!Ei+b2hlekE;#@eS(JD)z?_fo|u-9$RP3N)AY zktu(!)SOAWmPk@9WqVn?i?Uwci+XVeX9(H z^B(OARJ=@X1%w(1BuY*Lg5TSSPQs$<&2(HiURhuuwmoR0q+(cYqXb~84k@?ArIvnG z%YCv*s$M6yB_j&%edo*!YTmSLK8m6&uj<4!zAo?=y;uQ2#q=5e63T>Z;iXJ_hoVrO zv!mv>5vZ9?`WN-l4PdmkDe?ovRq&556s$y0+t^7mSESsUDjqI*rBv>28iLAVYoAM| ze4T$!Oq}yC8)>PHoQA%OzR`0fX1L4}^Y6J5bFvu9Z&vk#DChrcs+jOWedpkMb&xH% zZ{DD=UA`R_$0{)WlQD)yoL0Jl(4KNk42qUER+^nw<1zV?5;+a-&zg=O=f&eVQvn-| zFstY(_xK_kbgPfq(^w2oQ}JK(_h+jU4;aV)>BTrk zlvCb@Hu|mloa%mw&+Htzo=l&OhT4ozvUE2-G36vwU5BJr)oEvbc21!zMWc16vD8wX z&T8xKYP&Q0C4rcoQ!P|&!G(>ZETWngd0a#}*Q4b6OXhNRU9jBAO=h`Z?PqwKC?3%7 zCh5L&tP`hriAf)&NfE|Llqu;ArCgjV<#6*-W_oEVxm_BU%28do2mx?dNct#v@FEY7 zqE*A9T%3*kO}U=fnz0C+2eV&b+H1Ico3D6Rd68z}QDg`-JUe;}r2K%3U(%feRY^cQAw1Uqt3d!2dc%VocW9x z(hm)6@JWCML2NyELFaH^>^xkh&^l*P)J=9)RW!(> zt_))+U@VqxkTOp$0-&H2N8gbfA`EZ;kvsFQQ==z^Xxb5DmI`CWDxg_Di|*%iE_3 z{_vElDMMOJp$~lY3d<77I&h7<1vMtpAuSsykYU9uOkQ4_Qm4;SP+vuFUogwHo66Yj zQr2g-q*#rIV}-4CC0vVKB(ciVan@tXEg1pvu;dwlyQ&<6>UOv%ew0P0kDu+RfZnw$ ztABr4Hdm2`?HanqRuyo`UZtFt00HCe=j)%mdi-oB*yoUOg*afTBLd13N}l|28+=)) zc6PU(uRUGA6ewU}cR1=pl@o%9=Uhl|PZAT1!*eA(N)KNaXhP-g=Jw-_=hFcPWnan3 za;Q(-z8k#RPjY(AD96S8a(_$+1ux2p?G*?Yr;`fX)7bFFpK`i!UcJ<%a>|Y8m`2_W^gDrXtMhBH1L^@7D~mubbno4&GVhQ^GP>|mf^sIv z0aL!Vl_;}IoidHNgIg|hE`37=Q##N8StXS02;xpKSNY277%QwZ^@ zo~nNNo_*Okd~m+2$jSFrefZ7GH*r_}u58TeLXsAz(|@-DjEYWNr`3I52=^kNld~t- zS*Y|~jr_f^n{M5sUz8TJvL%?W5cs5JuG?oeewBsEQIx+DQ>dBcy~^&dBcm^b;-`}{ z8;u8pszC`?SN70>*a|g$frAc~U_(t8Q=di)7WdYw*k|LU)5IZk#L-cFI}7t78~2KF z7CEUH%rVAB(ZMSr6B8n$uq!idyS2E~vRNXC#|k&!Lmq}ulHEodE4z23OLG4E{lKI+YPURx299NOh&70w1a6a#1 z&58VDzt?6Wkw5C>bX>qAD4KE;%<)*UA(@h1@*JZg09TlnFyyPY$q$;-?6LV7`uGY(! zPVTON?CROwRWDyFy}Po5>*RM=ba0IX@2a;=uI=R+-W9KZnMCWV?hG{7&$O=U&Ljcf zB-OgALz4}BlU(bH4!_dG-iquRFt2#31ymE4TRWB3S?Xz?0c2hlmwln*=4(Vm#8GON)+L1&M zMI6cf`?`_#K~b2{oT$yIh{kG>hFKP#s~Y#j^)KWwI@c$ax1E3`hiy%-y2U~*hs(;* zP`^zrS6`rlvEy!DiL7;yN3sIVPzbcmsb^ce>#Mjc0M+Ob6^(Pc>lGMbZ+4tfNqy4W zWk1_L!bDAYI~&FgavF?H>78|gir1OTz9rU=^8&Z2ciR=gHbgYAfOndvSlWWV0v@q> zP75*Nj_V5m4m=5>Y7*&|o3V7}3$#;AC0 zh*mj*Pzv=_L?SeN!)Tah=Z)j|=(yWywKiwJa5osjtaog8T(XD*oHP&~#YIG3Ndvwu zg=jcJh*T1@PvgBM*M~T5`)RSA=IW(X^TMk^ljA+y8NxD3*P+MbdQINpYFf-xr%|@= z^cp37w^!;zcMGrcrK#w`Ex9OIJdoHRGh1aR3R}MH79uJ;jFU7|VEbe|G+@$rEu}km z0L#aRhw&RXM#(*V&{ZATUg5!YLwEaOreo55# z=yX-llsq(!vw_`xK8lOu@j4U(BLG6y{OBI0$j^$D%&Vx=0X7 z&;Zl{07(qc0~1BOKzv9M0rTRd{NVa+XaGT?1MeIj8HOhj5~!nIk!I%=FJpP>&S<_$ z1B6V(%shee*)O>#XP1ovS2Q4|QrbtQ_y|oki=rLgx}^O`UCT$$wJQ3qC+oagEjG}i zaoq1W@=@3$NnKy~hP+RMw%hJx7u z{dDd5vyEp@SJlVq8EsT4f?aQfy4_$iw6jb*p&!XcDB?dL-a>)W3e~xN=c9*BivQA@ zQvhoxidN0F4p$@Hxi}$~?;%aR0n{)`L9^7caL|U+%z>Vpfnv-&sV;b&9IdJvSgG-V zi&1F;W*RLg*-}$;PRcZ<$x!Nd7BLk!ddNgZq@c9m61T^#0V*8OtqyFjN>p)yKSgQj;%bU^gBHm>)gY+P2 z7X8Wxs(oi=Wu?=;+tq0tI&pB` znry;*h{8g(Ta*Zqqoc(U`Y64v8Jl4{4F_FhGB8YbbjfK_&(^=Ut8h8)j?QzkZR|I? zrbp$YsE3P4^btT+9B1kH=$P&+GwHbkFr{>qM^&8nq(X&Mgh50c?6idVgM(P@-rQ}{ z|EBvjlgum|yLKk_n*+=EAc?LH2F|T;`1S4-|LVGMaH%@Tu5l4fMMuSU$JBv5x6tBk z-%j6tQ)=fpJwuFHmX6`brvl=hznThx?caV=HZ!E$d1+^7oY27ok@Y6c+;%lJCr=W; zR+@?qo5@vmvvaO|;sa-MZU(g7y}0-xCGBz((8{|FTX*{7s$qd3MwZ z&yE!5@4LB(8%s~==-999Kc!6h|4eccf4lnV*t=dwNK5>$eoLojuECKE*seGEnx9}( zAWc+o3Oa4u2ULcKdoztcO|uhDIiXBV+n66fqnjplX%gHUzLr1(@sQ3Ot9rm+8Wl}G z!Ljen-4`lockZ_AAoRPcBWJpIJoQDCWe#NPqWtpHu!zh<2Yl)o)!58KYLyveRC!OVy!S^_T89v!E5CP6RJ4Hae-$1bK5 zzKwo#y2!L;{W#;m5lLveG-PC8v6!cyfci4VD1+FxTRS_#MF3Hvg4N~a*7CBQ;o8{| zL@JYoTm1(K$a2&zn4Kdn{kD!zf*_o$3nz}(?Ch>R->o=ct>N?YA&prMe&is2s1Nto)Wrj zu7HOse}PHj*)A?b;oAcUh`ItC6Duk7r4tzFpdOC%uU;pG!6Ff=0P>@=3}J9_s?{}e zBTFj5Eu4d;-Z(k@o#8Qv| zfwmhE`)s1TGarRk9!2wD(zF`WSL=1^MCA2p^eCZwgdjMuB6=EEIB2DQ`3&k6uT3GG zn+#|sM_>}pDn}$oH%f}U8@$}mS#$fd+njOp5hci9C*x*Ts@UTGSYt=c!hF*|Z0^38 z#qL$NXVHt1>Z*9eZUoK2cTwTM!N=vUNt*SeQ~7gtJIxCe6>|INM}uqi@!g*4 zs(Z8F9meRU-MT&d-C_6!zja!(J8gVv&F*yYZ0XMI&XVe?<=eA6%c`q7E3+S>K}FrT z*$?qXMPkD2hv=a)#bEYBbXu7#FuQ?q-cpUV*$u#E_LOHg0Fk#lJi7r%>^|@8#%+1y z?t|Icm)lX4?a%TR!%Q&gH7bnFu5pIa#x`h1`^T->b;)%C-59fMcH1N^eWY`i2uUCw z&Z}Ywu9a6cA@D8~S!{n(Vi^kcpK3{EB1YOm_oNnO`lETZ#JX~);A>QKsAguAZK#tJ zf*tG#q zwR&Rrr*9(z-KHzAKqx4ReSj;z?In*n^jOAA^)xoO9_w9eIg($}kr$cQWWcaW%1_9E zDTbqqbYscurqEII2mt_>)tF8-ZV3>!S!k0(ciBMcKHKGmi>qzUsjz~m&f7ka6duq0V=z$K^EU-w1NJ1KMlSDmtrMHy)Urh}y`dJ5IXlz}4 zg`S9U$xp~Gqo3`#deiAV$`>ZLorv)bvyi-@y|8J^?@U}KZRX2klIG!QlDyQjqd&}~ z%!Kq@%ski}C-5D}Cjq}d=@_{5a?!>Q1HO+CjI-lXt|(QLSR#@P$|a@l<4x-!gLt$< z>Q-kdl|hjnOXG5sWl9e>sVX%&sT)?6k^mK8WA>8*Fj7{E{WqFCE!wr}O1Wz#5lD>l z&kIZ4q_C7MRH?F*i(=rG`1=1>rKJQfH?1x8|FQQb@Npbf;&AD(b@~0=vMn%Y7}du<*CP-+NWnUER|o$%+HJ--rDB&5XP2uJ>NO_v#(evhv_kTh4fJ zDXEWo(WQXY_?10KEm-7IIIkh#)E_0flr-4W3oj*;B=3F;rh$}$+k|pnwmxYEcc!N> zywoN9c58GDuDDIe$;O6+BF@@&ajmvdT&p?dN)~V6sD2i(=^XIt!~3Q z1;fdwaIR>V>CV;r)zhV^II!bZE=(Rzho4nCb} zy@D_uPm@}&R3Gniz*oXU?tHOq7L=<`NHI1U>AMQ@u6NZ?#B0H9{5r3`_p7%{**;Zq zbkopESBlv8n!=1~3g~A8=K+|+fdg-ZnZkMXy%>@Zs|g`CwaRnW(+T@K#J?K| zA$$4nMnXu6|86FPOz_`OJIjB!5JG17?>6aqJM_SxKhj9ZAu`dknvnYlX(1;`J2?q0 zwvkr&XeXrYoHc~Bt=RXyY9J@uU<_^NH^JiuLONDEv}90cBRMI4?hUrv zP`6!ZuL0US*KMz9)~P^a@SO^_w7d~ELW|R0i}UmI*y8;B{KWEGgt0X@64LtKRzkeS zY`ch!*>*7-Y>Z8G6)a5)e1t6}G`kv_y>XM&Y|H%oe8jd((6snH6KsBYvu}aMH+M>n zH)$0e$E;QK6_2?Mn!P}7wneMx?wI*+J2>X$EQt(V?aSrq)gDC3`D9{wk- z0M>R9f)&`C2|;vu4d4tRh%RsDzprT`(4^6YqzQ#_HPgGal2Co1Xq?q&%1 zYuOqMA_6fEyJN9f6CpfxTmvC77)$s0FqRe=tz~P3kfsJ0ZbJikQ|fh>cm6!1c62$~ zB@aIS<$F(D5?P<0|Mat8^2B8ydcno3I$r$Jx8CxXzkSgszWwv()!mw z`=>|Va^!g*_?Hh4y!6Y59%wyu|Eu5rl7}BC>;H1qt~dPpdgWPH-ZS>aFTLuo&i>S2 z&F^^5e|`AH&ukj_`mbMjPH*QwKeA(D^-0Cp_kQaq@7%e0|NDCSx4-Yzg)94aUj42c zyNmim-+IwwkB(1%=iC>}zD65wyzLin9BRAk#boOXZg|h{-hK2}PyfKZ&)om25B48@ z^mliBqB`@&zrXy!pQ#D$uF120dH##;{JZmB{^Pg3ICkin_r5u7V^+Bv(8`3uN`iB{NLYo_xu-r{tcu3gCFm{i>!I~!AC#w z&Q+Bke>mCurCVO~*I$Ty^D9m3pZC7z*0=pr&)a`m`Tc#<^Yo^{Lr)xk?PrFL-x7&5 zeepNf{Kp%#p&9D63zuZxrh+Xi$_eO?ZH+I{z z-}z5%zkS;G8#i3Go}-zkQd-N;lAppsLBG zZ{(LhzTw_)CcpU6i@Q@@U!!Mb_1!P}%B{bd{oVdwKTzHIgQI=1eB)RD^5{4BMjHP8 zzuxh=b2h&}+W)SmYhF4zbKntr_@aB--=6&W*t42GJN@NH2A==UgD-CS!-@L`fBd1x zfA;ymyR*F``jX@uFZkCVv_5gyOD{T=y=T=_?*4DS@E{$pRupZmS? z{<&Aa<^8YgTKylfJMPx*i9Ye~FB@%r(~n-?GW3SqAHRD0;fsHjEdJ;0d1rs*59{-P z_?zL6Os?9T-FMOV8=JQjQqGkPV}JP%n|mI7@!wqWqk~7zZG7||t?SO0ZT@@m*hMY3 ze>8dbW4nQ*Y4<#M+gr!qufC=4{criZf4g{i)u|`!lk1vZ{oog_{?FHb^tXR^Jg$GR z=h4qhU%UDp4}bN0pZMV)-ZnS+(obxid;6Dv_J^mx;tQ8NcunDx@BOR%mWQ8LIq@Rr zIY0XDZ9jS7zrTIk^S?heKajub|9pIWXYq4ecm3^#rm3E{-uuo?<+n`#`}OHdV}E=5 z?AT8qt>~TuWfjI_TP5haO-@-yRTi})&I(eZYzHIid+9M{10RQdi(tE%#UCB z*M}bOzVJJ*dG!mc&%H-^^CgoHkEE_2j{bAyic3d+w(~QueDJ$(yZ=R7{erwAKM(v42l2#U{KEJ4&%NW)f9?Ioi?;vro43%<%^mpF+m7F|QF;8DXMFr;Pkgo` zHTcb+-@g4fzy46=Wxu`sCC~Up{MF~=?tM%fdehopC$2yBi(@^5cYS{HVD|7)XW;zj zZv5xy*M3xZ?stE9!HL&j@Z!Ju+!K3t&Gx?h+^@dq?mPeGTMvD0e_J}OO@HvYcMLbb z`<*}h4W)#TXPw*gzQKn+ecfg0Sku)V2fkbW?-zXW?H_;RuYT501uC zC-0c+IQ;kz|MGX;@!RcBJ@1FZZGUn1HTS>mj{D!T>l=N$CN91E+c&)Yme2q7>~nwl z9p_cszg;c@Q~9igM;^QLvoHGQ;Zw){`=ZnfKT*8@zaD#i&p&+e*n5i4_~M}xx4-m` z@4WkOZu{i(^c}BT``Yz8`tH1b`pBnm+S+h`H1_gWJ@N2MCaTYP<{OgFerta7ML)RW zM<4qlxp~(OA9>d`E#&*FKk^ktz5RLrq`YDKx-B1j`0=slUT`pf+cz)Van>(CaZmn( z-$}mhc=A(s-V0+WeCX?Q@msr{*ZtKe-`jlC7nP6gZoS-m?8{$oee2^l&p-CD+y3## zA8Nj8?QM@Ha<^af($-h~=&Jd<*ZktGw`_R!-#+ruRY!++{^Hf9`8Ls~>;&Lr*u(E9`sZ(yR5a4fMY1mZtT$efq=u_ulffpI-9G z&b>RovFo9a14lhy%I9b2z~=K>&U^^|9jKr9-9!k1t%ERXC;?G~%0M93S z&YC875wd=o+|@+RB7~en2!S$d@j3A6?bD>KX$^cJ*RlbAts*De8aGXo4zdwG)=iU+ z#-33!O=2J$5FolIJ8Ig8ajoLx4r#v`+HZ8*Zwj>ESJOTXj33&DKHxlX+i&oXzsYUC z+24Lk-(2z5h?ZY`5)IfvNaHy8X3;pxFQM%l!S*+ii5^G>44VVX+5jx({QNvx42Vk_)Lp=Ep1P|UcYgH&R^T>5$i?K;8ym^)i7QofEx3Mo zKQFf?pl@%VY%TCutPkDjqc^oVZ$%vzd@oG<-frP3d`R>&LzNFq4drq7!b)pbiUkO zyF{P}tb9kaGwW2gxMkK{=x?S8o6)U&FE7$aK7BSJpE;ZHbD~G!>o%?jAUN5!3ai+9 z>ytO_&!J`de#zvzjlP3 zWkV>5zDujR}=6gdZG z%MY;kzyrPj+xZCtUxWNPY|2NV0k%10J%0{^{}OK#X&4mbwCp9k49w|Mye}Ba8+aKI zLEg&i!bX3Rw+|b2ivNa#(&p^|6&T{>&nAStl0Sz7^&7lAAkgc0U*{1*{+mBPpAhmf zUKi-b1N`>|gpk{KJAj@GeC`*)3Cn+9ObB^CP*+08C4`V&y!@qvkURMA%LpNV&wnd~ zkdN~@b`V0|11~wPx}28*60wE<22${HIFkq=S4iyxEd!i{ziot&-}CYSa^K^>uOx&# z29qF!Tm^f<$9Xj&{#JhkoQ^v&u(cdUO$=;J=|IH%>0Qlp>_bc-jm zmT5TfsJFYj8z}!d!|BwXY$i36&Ll>%Q>o-=axx94dJC_7g*1xhj8&yjYt=|%Xl7I{ z_B5&3CaqA`jqB+Ntz0Ql2+jj_&KlVppB$aa#z!WSli9>XJUK2m&@VNx3LAh*=lUu^ z0@s66sgcvI2T5Zm;}Z$F-nCMH=kR5$S)H|eQd5(e#N!0 z=xCE$SzeWzCa#g1XwwSEsR2=mp@DU71F$@y{mW}~UMQ&bLrd+`GehIa;ousKDS_cf9IWTK}D`4*5L*nJb}=$ufFYE-?4ny<>1-Ek0+jg3AG2H1ajxrW z?*fM!>qi(C6N(T5+vN`kk&t(I(6!N z{<@72Wz%Xf)c`+ZPAp31Tzi4qc}rE|;@P-42bq^N2-$L+mXsWL4S?@LrD!^)a>T5n zz1Td>DO#mc645d$m}?2}%vPg5Km&^gmuP$6Gtp_+H}zy#eB=GhT-ED}}~p6HyeGT#pQ?ia17iCHEbWa%t;=uf*?e|u#9;L!I>|!}<>$g%>>Fnw2FXz})Ahe=pIjT}MO4PO^kjA7! z3+;*`Xa`otd~vF>Gjk$i#fzgPYkP)^eDKKJuZrL`{k-aO znNl%PU@%ps6F5$RiZV0icJ7`Qz|msm#X%WQUR64wu}*y;b=${s%Z5_7+M;0Ehoe^K zX%1Tt15~y{9{1r+T;RcgSTe@mxbFkYj~ysVfR9oY!DTJB44|oa+LNUHaAd8O&y=me z8Le%uC;v2}!cXvz%`q;oG*6Evk^gVYX}N`5wNO|9#b8LenP54ORcj!Za@MPbyP~+#+%8?Px8jFll(Ug z>?C(Pb&qic7Y^;N^EKmN=QD8lKUEvrPtNbyp}Jb)V%)C$Y^#rK0KDl zj!iL*g4^ddX^_p}S&dRB3j*$I+oMFQ2%!6%KbZ ze~UwL-!(bq%GLbMIZ<%ojACRleJ*O{OQzwqvYP$hPYU0;m0h^NH)ye$pvmRP1~L{( z3b%Sf%$#OXr#MGVYmVX(2Ad@jLw7M(P79mDXkofh&_V_uf)pO4xd`ZE3doICD~fHF zs$lSp6iVS?sgY8$=;k2gRSHZ^G^~qM~q$R?|TM8F+K!&oZQ?2;K90BJ( zg0;M5M$!?&YD5$l9{`YM_0mTv6S|p%jB-H4NBs00HtnI50S%;)meN>T!p_7wtQP5ac5gNE6@F zRF5VSxMR5w%@gixCZMGSO3;pB1@l^=u;5~RJN`EUO9%`N6l;#!*70Bg$lR>(0s(5o zn}Cgh+f8VqhI3l$b)zHy)JQI1%BMEC1(5Yp^E4;4R$y-`+npjkforAv8c262G1=rn zIrgabz_P7ky{}jm(hNl7_W@PNhdBdbwe37y8@I1yJrj}DAOgvVX&NY zx-sV?4s2ZzwzRmHKY6!9|uN7yG1@DN$IuU-zaHZCcA zZ09JpF{)K6(Y#jDa~6%v)0~U3E$~RQ7W`-Po4J_svT!kFUnn|l4dZg9KBcpHjr4fs zn6@hHc&;?F4V8Kj_@*iO64d}T9BR#K+}$W=ne!0qd4~en2H~pVU>0FP;s12VG>_Um zIkjeZ$#I6+f_geMzgAKbH)P3;VNgUK;0o21v7X)lwz?ba!`bC+3}T@h@*`WjjG)e| zIqhrC+z=!MJmheu1|d=OI;+n~iZ_;RMpd@Ky-l`E9Sw-WHcluM#nG%e>Zr=LXufJY zW*N>TCjy1L3Q_oqh&o+%(W?d_sZ^zVS07Vi)DVxs0d=5zpce@Un+J|5FbcM|&?7Y* zRk@9iYD-5nryEgF`|>CHta78$p+sht;H;zW@CrHQ=4-&SSuj0{Li0sa>AcZ5jho$6 zM*xvhZ5}S3{pB$agFWsl#w~5Wi-93TD&RxLBqv8SgXV8NT9q+0TGnib;sp}W1m7Sd z=Ax5ucP3wi$aq>VuWGsca;t!sVSoS5{ruRWlPy$#yw?tp82k+jSCK3~(fX6mA5W&o~W6~Am72GqX4U&!EBX-IN zj_tV@*4#6?;vd+o)m|f>=W!qC_2KA8qxqsG2DI{{WOW$R_8P&o6)kau45Pd@X4;Tn z2t83PCBlD9rIiD^-;A`(ehFL@byBd4-mg?{ksMQ^TY^d4P|$?QS~+%o2oe!6a_#{RUoee{7ob# zR0TkY{~y3iRRHRq1Yg#5dYd7D1QF*Wjmu%M8pr@=pgN*e+lpf3$EY!<8z-Vgr(BZN zKrXcV^u7Yr-%xv{?t(hlM%04(CF1@nuN%e!vTyK1Vxm^zgO$U4OBnlfy?}Y>OhEmF z=2-d(5%gr%%vXV7f>0x6&C*L4-ow_xL}!OWOLR^%n7-Dg`Km=dek*_v*hiOcL?s-6 zThyu{Zh7VFST=nGpupV;z6q`n77W1nK&*SGEFaWBxZquu+H7pRjw}zN{MeaVF#uY& z&j_~b>(cuMBHOO4J3KX$$`0?zW~TNfCV{fA&CEVlm=n7ai5O-CJoLGRgjF?Un}!P)D@HmbdgOI4{VW7WwsPoh4TJ9e z0=j{Wv$+cxA1*;&EL6`SUBS0XrfL|pv_sL9f=0`xp*Tg&Q6Q>>O>GftM?^1xRODW| z8L1Uon4?`1kV}N#2jSkRd~og&aj%5*6E47&TE^XL4Bv;)Xc;6_#RoFHNp8_le^c8T z_w$r5UJx!28@^Qc%C8wUK7x5^6=x+NLaVszW=)y}8es|%**viU4Yu4H!Zk}2;}?W- zG1&-Fehx!eGkq|gjZaStLqhqzhl)!}_Z|FjT7w#sfR)%Pyq@-p^}kn`iVVI(RPLuE z)_dzFok;RTR*t!CVVJwXweC{KF6Me;0ae<8v;t3nG8u46nFd6+VA(d%&v+7W>g`TOxGG_7UVn--LF#x)ca|S*O-sSgsKRnvXb#Zjf%i4 zhGYX{;whcoYBv*-LMRVljLLIGiu+1od?4^sby-|&RZ%3YQGIX#=}c{1*(e&kSvuB>dfxr2=ts6oZn4b# zQM-_XD()cR3Th~xPGsTYDKW`xzWMqjr9pbevt{IWL8k<37=_l(6$5Zvn~rD3U=Kx8 zV1p~>+Z1Cq@$O1b#1FyHhi6i$#AGI$O6;FWq%*#imQAEvd%D@5Zz9kLuZafCa_>8& z-kbYl{ovEqu~rOvdMd>X$wRu>C23)W>? zbDcLs_47q_;SRu>@y*ve=@`@L8OebE4BX_Lzd7`nnhcefA|98-Mk995znRi$CFZj7 zld3)Jr23(IQX%wsrxgB|UG@lJX%-T^f{MzbB^}c_=7GOto58Mx;68alxslD{=AZ<{ z8tt-diMw1xgkr^gg#Wabtq!pqPyyoHYnKp$FW^A}Cl%v2%7Dj-v?%MtG*xj8M*SNa ztyha6T3sfs|xKtV=5WfG<;+knOjeY2PdE9g&r*c0$34v2#QfSuz5z83|UEZQJl@+%tSeAX_G8coe>*uB_WgA~LlT z$J$!B0A-7vKqB9oH;v=ea!x>yEJ2!M+PG=V#f?Im<}K=^9ZNUnW^7u>m}z~^$Q0?` z^wi`*n#-6+sj;)CcP9|E_Pqx)S!Qz!19e;Wh#0trv-|8eRQ3ah;p&9eiIL#Nx1TBh z#2QBJLj5Tk(0{sCJG9x=4(-%SCEWlv2dC;A-O4H3nlQ@t5g|SEz9iBHeIMeqjh1F3 z5d57M6w@f_24yLXcr9uLXqa?M7MzSk)Rs2m54Ua+WA)LzdW(mE9^eIgySoRLw5|1^ zLsd>2zBqcx(93V1>oK;t)BKto^j-jKSa=bYRCJi_+H!Zv2S|vJpQ>wB*NCE{z%U4g z&76=*dakUOwNlB&P)!??Aen@G4%E-$XMuEvLUPQqF|UjoZi4t557a>;p;;gjZ3@}(;Kmtmlz9g zU@J$!wP6VO*(QD|D^^Nipi>1q*;3g=c49$9SJn;3R5XTuoTosW5sTW@GO`=hO3Bm; zwwndhyX9U$gyIr$eUu&S?v8ouAR#P7m5-D)LjzAk1K6Mtffc^Q4ZtbjCTKUR$-ct}uybBl@gI%?# z4aqYiWop~n9JLkAqKaWUkiAS>pao@nuIeax%}^{_sEPrAZC^_BXJxN#N@E7r4 z*XL*F1oI(X)ZdEN^6oqE-O0{bTApSr)Y8p@s>J$iSS+VlE$0kPFHuzI0QH!s1uzE( z#e;5C;XE;Py9kR{t>Ajax@|?9b*P0Q!tG+!0s9Tb*5?eZRCALLgJAGfbBBK;0|3bR z&i*8TfIlstcdZoQ&zdh6ROKpO92_Mum#$Yo6v9EH@bsq|XMuowln5#K8|rEoTF8|W zkJM5y^FT|0N(Wtw8w)vEvOg=GNli_TW=DoxCnBg7?o-@Y$brGII4I<9YJG?s3psFy z5QRHZiRtn9a3Xs!nHkHDL!X`xRCX+pVrNQxdRpSsL=OWD9I35w;RJZBq(;H82Ubn3U6Si#J&>QEyDO&l?deAD8cysVG ztu2LI7B~xl45+cmNig zQivF?nL3|Voo4x4@h$7+Ll%>hFIDLjlx+#3C(N2WGO8+_OHy7@OD0~b(kV9mOb|!W zg-hR5rIRgjkPNbtHBwxx*DuA@LIJ1VHB=6FZV}0)hIi8e*D+JF7K8CKmDam)Nt8lR{TovP+N*(g% zFN0fz=odieZGHXSF%>`NDK}RGIb_>lPj|P99|AV&*|MQ$kZ0%P>)2DMgwWI=VyR2w zBhEm9jgnAGRV0*aR}>CbShnUckztNW4^K4+)FKAbs880ln`2Hkf<`qDbPpm!x-l`H zn1CyKRRy*^3}NB9iXTD&v&ezE_slIA9&ZUvA9Mm_4fqGtmy1Ntt+Jz1Dd{K93&eW^| zs%Y$^)B&UDIi;u>g$OL3p_KHa6ygBPS+$ah*YaY*38*ziz#gwg9T4{lNIzv3I&jSN zcE`Z|-i6r^6o@DSGIo0qgBVcDVB(Pzgbgga2r>+=cs&E4ir{b8y;PIEKpWBgp0RWg zPz=H*gFbgob4)7=IS_5#F)hzAlquK2Km{sY7*eTX>Bluk(irduetp2pIoZEEoa~3k z7VGlJzBfqs7vJ7Uxmt2`V0&R%+T7PGM~h8iXz?zN7H<0jD`qW9nImU}`8MrPEnmn@`;|DH8xuLpZ zT@!lExGa+wn1MXxSs8`N~K~ zotVVR5&En!gx=2OFy5hz&L}wnAI>pazAzK^q0D;6DN<#^yj3riG-Z!jH43Pr3ovuM z2QZ?N#4O2FJnRO|cm9b*K^)*R#L|n0rGqw|V!Ck0f!A}ovye3_oOi@4ADRc&RIM6# z8)VRevc=>H{;*o;VF{qUTMAVZ!C*E1ClYk-t_#Q5MGb)v{u`3fXMoQ1LPR$R@Z}!r zFqz)&SPY)V;3)%1Hgzu!U2JZND0FbCGXm7)$-;$bS@+Ox^j+ID!wm2i0Bs3e zqrX`Qk#fY8gUU4Z^Z;IVw5HQzH3elluvkzTKMerX2CF?Eii5~@9*^DdEr{LlEmxZ3 zp&7z2*)Id5;vBW+03D)y0^(bZVW0*vua#r`*L=m>FMSZRqVRo%tsUd4B z)58Rmjn19M6jiTy}dg|Ly; z=D6c}99-94wMvnS<`h*Wsv8A*!hg_W|338bWRTh&2uA?6`znWuFeK<497L^&;E4h3 zGf&0x0&H^-LC}6(T}v;&o1xFK>fAl4%@gBDd`@~#HJ4$mUEq~N5Qq2;J zpxO=?Z!wC*mUiWseQp?Le@k$^taX9fnjv33D3b?L{8mA;fUAW7Dy4{{mnmE*n7Jb~ z52i&9&BKj!ml7|P07D_|0Eo4^4UrjUO{+}dRLpB7=1C0x2nv|*6g8^=F7oV-0w3qK zGPN`eA_?&yAqrB7YR4&iTQtkGMmAG5c{OwZd=*H?2!~| znAN$Wf^&6Dv^c`36Ugcx*DT$vLaYgH{7cKjtatjG=|jfx%rd6+c{7 zTHM{j+rGY?Dt;g`Vau#qUtb@*=!-h}ipudQ>~*!^e;p!bgc2gSkLDp0I+2pz`@aI3 zWh6)1{NKxBI~vIrmw<|t0CT!SJ$qPGMGFs77VOPmK#F7FUDQ5QPtX?uZXwSyP+*a7 znMHNPe9Pcbo(L83g+?0(fYCAVmm2Hp+1b?>fzXl=@Eg`*6~0+HxjQe6+|6=E2HO_I zE-l%ykLt`R2MjOKf)@03ZWyTmcjC@M6606MsIz52H1OXEx_yMNR2atgc6aZpm3(_R zPu-*1JZw>oP|Y}X9~8NWDd-4w@@ypdrj9xr(eA3{udBD;2>BnYt4M1gH%Q8_t5Y*m zWAn%K=~S$^e_?38wD)l00L}E?FgR_^r6%Wxx(EAr^&UGoGCh|B8Tg#hWM(`*lub=d zWqf?g>N(vhX*rK=X(Dj616#62%($d^vD7%mh)_5i*qWJhurhMFX5c&hrQlW|SPIsi z2?rvI$j~BDM8YlJIy}CVHP62yc`E77_(WHZRp*Cc)t5LE!8#UbH3z606njb_3kbAhA)4>`EP z08v#=XChnBDp@o%H8GhSnn}AJTepe#U}qgKKy<7H#dM0);)Ct7avY_FELNnze^D)8 z1V?(^v5_+auO6>05i>Ch(Z{w4l^vUYmz2ASnPLPa{cQu?-97x0ZJTH_qHN=F8|6nR z{nxSQ*cPBfB~3O90fgh`eJ(l*8}*2?&7*K09MoTq4xuhG6XjHC`XcJSfjJ6%1QMXZ2pOW4~b7f-m3!knr1;mpp?G zaGtKc>AuvXUKO)krZmb{!$-PseO6AoHiwa}`vmF2#s%pbmo$ANlWCtzjgKKf{#+|S zE}J}rR7JfX)R@&v4h4t9S~6FxBAN3~RN^3(xCr2m23)b>k|IyVA(&r(yfD6=NTmE6 z24=XaU2`nyL*XqjGklDUd|$8H*Na?w!H_wB_8gdd>G>9jnyp~gSBS^ywE`dx_98oF z=>mIFz$#J6jHi|DaP?3Ii)RS52+06w;Y!}b7L+x5)q%cV=#xnjArvf!^3b_M=>)&jPUZ$5nI3j%T+cFu=oJV6?)IW+??>!&rGq_>JXUvD~Sjfg0HpUAz;g&-Ke&l$Y&)o zAo*xmMA?RSg5qM3pOQJ`ChoZry(fZsekF~Xw_ zV7ewAoPgzIY|<)~0}|5h6z2JAG+KlRWd$?u#taHNEQtajFSSE2BSXT;2-=ln&z;X* z4Is0q@H!CwRIKXML_CS1VxX!VxMbC=R$>V)H$WdVsd0#DBknPwGz4-N>{6>dsGysg zn;ugR#O2dhhr6h`_2g(fzMg75)!aHBX=;@Gti&**%*`vxeQsS6)xsZu`60-bOM7Oy z9s2K%KD`)WYb3ivc`W5vRa=15-W{g+p~kvm-Cex^maz2U7qvbXjU_KtW4&EFC7&pF zv@0ix7lx6<7VZ`$#w%#zuvsc;ITJ3Ej#H(qSx3oXoKu=qT7`o^vEnht{81RG=1LbUr`_`k4g8K`W04FP7?NFMBqjry2F;Jdb z6r1$qBrjo~R;l==x)h+*o2*A}yexaCL$mmtu^A*io;y^KMhT5`fcmHk5+$amAC~dEYClUA2$_dRyKxl%=J09jK1^k@|ax-R@ zshfqvgghNQu(LlIfWNZeP?No@I%b&}hCRgr1))1{tria{7*5Lklks7>$0z%`3CuqX z-nR|JdU{p-aGiQ;$dOR(rDz1~4Oe9e`AUjOz+2&8yj<3x^IZ#Cxx^~C z@|{TQ%2Nen*YKCGC=o+hys7Zl*o(4NPO6*SYOW z<=p{>mGgj&H6ASdjA!~9we*S;C^)OjBc~^5Ua2fNMbl6sWrPnG!juSR4ASSsD0yd{ z?Y_ti{>@@ARTH0q3m;ilm=#h2nKc!E_o5K!CB<(A0%>GVWM@rlUb70c0KX*q4S$YR zg&0QJie;YAy?nOt7ZH)Mq9Qhyx1Orfp&R*9wICA(-`d%@oKiXVAW()H)K|oXgC`L& zeRDlp1xG-DhT=i+{5AE$QJ&-TmRYTr%X+>D(!s`&Aun>92ph=X%Yl8`+~5UJYt6_| z7P8$3DnK4|m)#e9hbS+&8yGzAz?Z-iA}v7K7Kly310eEpi?%EB@Z?%70Mh1~jt)+x_9a+EMLsFu@5N#@ z#d3`E*3IqYaN_#z;v@v?!YWoNObbQ+_cY#15J zrZZEi_-G=VIXsOJ)Goj_wo?{f`BGsAF26o91cQ-k$kA7Q@ER?#~Qfo^T6SjL|ttiGGi$sy_pgOTMOQ&ARjl4nkjc_DdS`u#K zRSP*kuLx*Ls!-CGo%3yaSgIDinRFu<;_!pjU}LQj zGuRq(F5H(X8Mr_GmQgm9fAdkfZ(VApdOxvDK+H#NXz{FoZoe?^RxyZ`A6=J( z9bI>DD?ib^dwAgrjSRu@1)h3n)N4~1qW)*0Xq@)sCBpZjNbLVEf@%utio7*QBu0d)Xd}?;tNJyT}WZ zeLyKOeFSmNIEHDXML8)&N&w3SW3gQ-eu(HNVTT{)L|Ke~lwecRRn_n=TsOv<7Z*VV zw5T2^;k!EA1{`Nyr?&$#!XWzneO>fx@I^GcP}~#OMyCS-`J%mv@$tiZl9SnK2$X3T zX(5dnuxBjvZfiY4OQnTb-HAPn(@rx1Ow=!!yAUK)M* zZXhz$AQKNc0Y{)O6mTRA%>rn@ZJ>J)4%b2Ha1BBHMWEZesSH7l&L^P>?pP8Ie87p= z{P9Ji2g~ZFO=t#mJ!E{AGLf8|h$qLProzHo4Y1ZubtHsc66+`M=F~LYy$`lXQTNu)l^5+H0 z2So-p@d$qm%2lYf-W5%7rQ8nIunzE{pQ9P!ls6tmc^m5p8B*LwHY5D(QMHPeFEZB> zd@|20kMz#>r*3dvswkcse)VsMg3{FZ= z`+b0eOf83zDYp6y20_r_+>y5F{tt*OkNg{(Ke4k5aJ#8vsS;q4xO#|#E>VnAJd?*!)Z zdIC#*Wqf!FS%^Gr#&R`B4R*yL#g8Lc-o1!k0`jnM9i|TBOqMmh&Z@By;HN+oY%OrE z4Rm7~T^20`3xq7NOYXU0ro7EqM`1-^DOE^&C5S(N=_)m6QsXM$>nI!BnIZPrPn}I* z5-&J|uq6Ry3OwUui}1a2WnO$w3Sjd4@wYXhWPE5r2*bBE- z%|5d{bugnH+!tRskV((krTznh_F;^;$eR(E#(S0T{(QX#4!OocPLBD{!m`6*xOO-` zJeJ6gq>=}iS?uKn-L7a(zDTVdj3wA%8|8{)X_dkbd%-T5bH<#UCeVMQ375;_7I9fk zjY$jPn*p>WuD_SnQbB5ISxX9+?DBdps~36W8vBt&Y9ARADsgljRhdXkxQRKJHL!4K6bi#-H8CX(sGVnpjWWZRwW&mW92S00YHDKHD=+&9Y&aCz zBTS^=7tD6OXmuNWg~aA=AklmXGi3%4Eiwh6Cp)-!t%{ zt=PM-l&m-5rOpziSOU@E1WWiEGSFGm^wvw9ysVtO!VIW337~?rER%{)<9XWVa!B$> zc&a|P#^Ln%)ac|W0AZ_Oq&&4l0~&mTLSQGd)({UD6f!Ii$_~Zt!hOJbG4_8~Z4lz% zw}J< zv3GZaS!+=P7dvW2Z0bbV_=SE6tzJtk5>Mb&wrc1QZZDhHWtGCS{`7+(9p^|gv}WmA zA*aUBEUB@Ods?VD(nVek!3?=S0tDzwuC8S-iRN@8%515(aZ4q(fJy|crl?Ucjnaa$ zKy4)wvFNd?ZefZ**(R^y-ZR-O>A1j~8yll*Y6UZ6)C;^|YyB3MT9wsovS8p> zTUqKhZ)vwKl(iGlGSUa^7Jr4`H!Rhyhq>Y2U`y3^5qag;6&+#PtB-ADW@Q3`gaQlf zszu%maHO9en~qK_r1y_USi~#t6)*XxhDQt(?FLj-OS!d%-@Dh%a^+zR%Kom#=1+~- zIGPY0&0=D{3aH2R9YJN8SWVw+rY{S9C5}#YNinq@G$xST4x8qeu62NdDVl0p`T3{4hhR0U)lY)Vo5A`_&_wcPf8^e z#nWC04Xz4-qd5WyYk47LdZmTccg>BCpUwy&5+_^3U;G)70!Qkfk6U&nEZv+ei;4Y{ zAGeB&B0>+dOau{kUw_6ByuaZ4|7V6&Vmw17z5mS^B6OR}K@sVE!IK58+6Pyxr$Ftf zICk)|{_;hPW@SI*jSKon;^>|#wT8y>gEt%+1+8wYU@cMehV5duS6T16JhN93bM^ri zR46n*VR*=nE5dAEy`f0vILZjpwK2BArp`=+P+H{4|f|8-O z)1^$B%AS&D7k9v~z1ndNe;lMa+XPnt{NGTuRI-b@6AU)*byr`}1PTF>vCu2;Be%<1 zK%`7Tw=StYt2>?-*ApBqr3B2KNIcBcC%p3@9+rh47^2{!A0Fn_$w$#NDzdIBF zF!`Dd@Bo@g$A~(OZao;z`rF7_}}3I zKlCF2e;ohEi~-A4ThC+cD~h=pSgt{8q4nR4O%3Xxx!;iPWVo9J7+uvQ9%RGs(y9et z3fe!4bvR$Tm4vThRhNNsxPE{K0hQdgfqrnCf{*&4YPjh*TMWdeT66+@Nt}_L0W{&@ zgedaz)_OKytI=XsT)1B5ni>jcN*>oMTA70b4o;QT)1zRH2VFE4a`K(u+6h|FHI}dk z?@EKg3>pi$o22XASR_J?yjjqVx!v1lGJ6KL!3|qJf??Pkx_jHOcmQeV;lI%TIj&=9 zax$428Op{}qj+iBSjfqi=QX1V50lVX$jLXIHMMF(Bm`=#k;1mZ_}Yi3Cif&qvm;5o za_4tD-kIBsR(3#$mX2t!PHY33S}&G<&5K3v1(9_r9Cu+6{$p!V(Z>AUJrA#z~%gT~DfYh^W9@i-z$(ps$r6e7w zD*KS;sG>mt{F0W(eCSAi)25+W)($150rlCTOdY3|O{uj*nK3Hp>Skf+|usq*qI=6xru@ADQdz&Ny$Ev$#7F!DUitdyZ^p5~Z+c+|&T z)xD>aK(sEKBwW!we#tirM$g?+;Bda!dnPi4l|ag>Doaq|V1E)FRF%`Fsn}W#rzchg z36E!iE*>ux?lI(|Yeg82gA1Iu1Kr)dOJ0F_L|9cMk{CW&pH>k5JWki6rhL^Qq=Pm` zZDx7GiD(-+scdeLcl6JMp`ZxD0F-~NOi7U^SzZ)_226|Wxe9@CeVLY}&_b*5zkVkn z;~W<82fjnt^Kf&C%e-UDpr#GYvaX~QYpk=CA2wHn9X2oKEILj^oHB8Kt}K!#{HS5h zmmpX(KW{MlJAngeOoyt4+JdNAvCegVQZ*?G)QhAchSHmy0$;PJt0 znEa|uahQRS;7hyr6lmF0;loFju`M6(y~HLU76t`fws3&BHFS)1fCSYA9V{6jDt$e@ z-70>}(;P73cl}S<0Kgswc8E3&i{uZHBZRFP9*a+oCMFWxh4So)R@+wtW&lK<9?n?S0No|iwMOyS@!8oe&rNk zYZwLis-OU{uz~`NLC#d36cJ-*@g$1e9g&~0%nQ%ab0waQEMC@bH4P=*8*tt(>xMtj zEN>^A(GoDAN?_#2To|ekb3+!4V=GUJvAq2X!_Gl4c)<^?!VH+;j2?t!L9oZDZ3;#I zz{wt>0t>*m>dqV+ROI&__a`q3^jpWV3nlvNpX@$*P=^HI_sdjwc{#<(rhTlcJC2E% z;!1^REGy*3^-`&l>4}a_B@-hzHxiK!^~M%FIrq?q`o%y;mR>_TNozZ!jt{D?F(#7u+bRs4XB;h>Bd6LT?! zsj%5;SaWVN#BqltOmpBb0(pc~>0QZW%rT2K5*B;eZ3koZGLZxR9L<`ej;d^vT4Swv z54zw1t5Ofmq>h7Kv9$z-ff|mg+{Txpq}vWPsD%r~pJ}C%IiJ-G(^x1&##RUe#9sw# z0ny>lDiEe5Zc<0or?Dj;Q~=x~SibQ7nZT&y8So#pJ+bHZi$p zDm9$QrW2V=a&k1C-IE+oWTz9Ui6nZPpz^HA7%=3BylxX`Lldc)$;m`2olQ)}hsG0l zp9B?T<)7!Awh3xr(w{_jcxq~268%V^yzE}O0fT@?4YNR_UB&>qd2WE3z-46jOpSAQ zBB&>`8Xj4Lkm6o_KUwpO16}7af&||#$?|v#&>GfivCik12lLd;s4-x$PuLe2)XE9Z z_AtWp0A~S=0XIC$LDM=qTQcXRfYOR%nkD4m^QK{dz8(^aRVs{g7t|zh^0OI8x)v`$ zb@<^QZs}|c_~WP4Ss%oUAa~urYf$a$-!whPj3Rnl5kzV z+n%h^m*!Zj%Xe#>K@T?HWRCP6?6YmVGGA~eXZG|i=(na19MMy`>A7PQdj|J2vKKHV z2XJUdJLf!vCy_5Tnlo0F!gifIJe5jkr&7t$MSjt}Nyzq8u zkEQjJz0i5dJyOYK?NrdueCZyAn8{1Z4s*<_Y~9FE_Fz1j$&Mt3X3(XK7nK9;Y-FH) zJ;oe@WU_cS!;-WJ$H0&tSh#UkOz``#AH3)`Y5I*leNvmk%a0e7A+XIihPAA577tfy zT8^QBqd1EfOpj;5gAYz^skodf3yW{^itkGt4wUvSA`W{UD=vqAXY!fM1xT>K*9104 z1Sa4WEQ}OAs{o^u2!pqAruxg-xXE#PE zICpGnCY{)qNK8Xq{^VpPkvb5UMY z1Hu4LA`r5)GK~3`HsFoUmjm?2;z;xcb156lGM4kS51X+32Mk+f#IEO&9#YgBbsHPC zfAMr_g1bcyXRV?`s(F!{7ISrT^oW4pRn^d)1%WRXEm6&85o9){661+@I>Ee=MXEqP zDk^n#eWtv}IWRralepERN@RctN(d@%u{Zhk2=eKf=|n1U>i7(EHPZ(-l~ zbYXs8%k*eH2kaZt>E80-!hzBG{=q5e_H4J6Y&fGZbrBM1NlW_$tn0U<=* zt-$QUys+4{mY(N|4iv82mo9Y1g@UPt0d^uBHQ5TC( zRT2}zQ*sI^9ds8c>jf#bL}ym6(YP{9qw(Q(=ElwX6?#0+?(t>@2=%xY^`byngajx8?1~lx#!yV=PI>6_P zHF4T&umYO1>@`?Qw}B1S=+6R2H?yN@U&=W?oOodmcm}h@gUgA5?tvbE3YC!k4Aq{f z3TRibO*cPJtsgXx%oo5%pU2BGG2eCP_F+Af%bD{pd*SIY_}Ewx!NsZKdV)VjujZaU zUs&cGwhvNakvar!V+H^Xw6>Kct6#ZjdY;s~+fdo;fGa+v(ONDUrtz5_?0@9|6={_M% z^om4S-kT>@s>+j@YaB;;l|0cVSc_;{ET|_-0uu5wd{vOdzn9X_uXH7e<$f_v|T$#4hQ#v(0g-R!36SWS_M(5|}qeZ9eiHtPgG8n_ zv##bz;B1<&T2z^>8t7bUquv7|ODc9zgD`eG(DhQwnU?10$0_EE+kuAIWz(WTQw~Au z1NPS#s*?FBfeIzutV7m*NDec#$rR48ugp@X0Y97riV%LL1z!qXNZ|FK)gkpGX#Q_f zhcG%{M-}2iKljLpu>36uBDGq80d;`+p@TLpL~x`gF==Y4clE?A-#NQX zC6%Ih%mw3U7>daPMX-b?s)d&dFezTxX6g#_NOGiz|wiF zC$jW+H`cSxl7kI25RZqb+7^0|!35n~ABl$$jt_5L?8>L1j?dDi(m`qF%|b59z9duW z)$Ga256i2=4$G^Axgz!|PRdC$0H;OUE}(MUoabO^nRAv_F2fBITtCs{)NmpNOFvExu%az-XXl#x zCe~iSieIv#g;Ps}ap8sQ0%7Q)gu!8KV4xdp_`O4{1`d6@$2IA2+ARWzJ*U4K0CQr; z5a{f>cIYw@Y;<$UZ9uw?mmE>UnO$(n;QHelL{rZfX#}1NqS?k&LUPGg>g2HugHa)P zJ-A3fBFJz6m*fp(6>}AnJ;7EXqX8o7#U8}wbYotp1!@&_YT-y>>di2am6Oj%7^J>h zK&mslAixxUD<$gK5H~=vsZmfWRl5k@ocgSQOo%S9U{)=qQZx;!n3$~XsAE=?ingi)@dBzgK)tP(D0oC9?v_^?<<#;v(I>aZgW zf36&>z?Gx*52sF@I-h4nY$HVZl)x%%T;SCt{*T2vs2&TLAWr__zOzZBQv0#8B!`i0Ze^tWaZ~=ByKPTs}$r8>}p}zg|V3$ttEWTa)h3g3)p|F?j%{;Fa|> zp$!C#(wX%eh)*Rw7j?(tp0ptX$Mdk6TV%Fc zDoLV;H{6vYdNd5tKgbc?ou|NcO`LG^G{>_-*ygM=uUS+n&{^G}1;x?|x~W(+Pxa$9 zE}N2pYwi4`a}XO@OwA?$$A@k1czp=B+$9Y|zk%*u5DaBk?_!=+VW7t^1^HPREa3_G z?mU=Hk_3=-@AW)U-Uo|_+C8VgLw>+V^!>%^dQy0sFNY;F{Jtfj_~x?NIBdeB%$~}74T{z5y-O&$_XQBkCUOA_6w=n$ zNJv|s$9onGyY|(lW9@$IMlCxp)FCTTq`K+9}_JmQ_Pw_C4tZ_kI zC#f@UT1O-EdVwnOf-+Px^AM{maMxSWBd|+d26ZGN$W7v*cxZ&gYYh`)d4cWybsiTg z-S@I98(Dk9hF6E;g{@dEdTdcfCez9^m|?lN)!Emp_Vq@cd_^r7w#bVV5zngy-}5cX z2%XicC5LOLOpA2`GGgaI_kd9Oh<5ztZS1>C*^?Pp=JirZnS<~icnPoAaDQ*>bHM!P zsl~Nvw0#R>X(LMK&YivL&Yd_^ulX4RR8iD$6sJg)9EC^>`YczZO|#8WG}P8v%E;L8 zw30?#;q%`TQ9Ao~sr|cPqV-xW>4wxa=>0%aVY<3t7n7N3emyGn5bNnxV?DiIA0Zc= zTa+|}aCT2W&>?F1JjBR?nL)G%-EgQitK}(#Ja-hEI_QQ0Dn?vQi`uX=x&bjtHKSmb zz1DIR_yEmjE6m5XI0aFaZGxDHU@%*hq*&V#h^=mIgYc*fAfBGH^uio{x&VH6d@%fi zVe@b+=)lf#&S8R(iqT5=oL~X@zDoP%3RIwL~ zJX5Z^Cp|O|wjrQlrAo>KZR=E;mp3i%$z*0aur`KM$wG@+T>ax(NmaIWvtb5Kzb(oh z2--(2TeopzIBdWg@p~|r2P>Nf*Qr#&q69o`^pdb@k0{%Oz6Ctx7;dvJ>)aXOQMb7s z$&!u&Jf|M08=bC!R3u4gV$Q^ca~x)uo6nn7!|AG&=!q`pcr;(q^fDBfqYhJdizi3n zrUo?Qn(a_Uca$QvP>tU{J)Ss}9UhM-F}rm$H07O6jb}9a``x!QjVOG3rP zY}chjj%iP&Gi#pao@!+(jA98@YIs~6sOUR8>uofme}Jjn*4wkQyIWV^@ufOUTE{|q>EF%M~1 z|BiY}rBwv-`U;(}r_;S~bPo%3mCPItQ3Kw#2wa&*MR&MqHdd5Ls!+vjM4<_EeSE?$ z*T0rOKyJYa8=PcFo)R(KP{ryM>1wY-wc_8NPuc zqtMV|PzT~?LA=2x5}vSH5)JrR#kU&k>B88HVmC%Z$@*4LeZzJrYRtk$71UgbQn*rz zK!{!dwHE#cVt>@04`)dYnYJ@$QGNwDTX+&mpPgZ1$rIcE-}c@FxRT^N4@{shps%?v za)zwNGzT*h9^QL6x`9VFIk>tT(~V;Q^vv{-T(DkM;h`G!s;aB%0gchn=8BS+yDQl` zHY{5fm$XSsvSeBcaitx~4lO0?2vM{shGc~(hgVu}Y=rlSSnaN4d&Bbd#`ot@S;qs= zm>$b(v%2F|A&);Z|NQ?y|NJxaPtKvd!WDrgIY#hML*1!NF34~vgJUH@WbC-freQZ} zxQIf(+FYbwuyH9I^5L%0n7Nv6>ZssY@V900R(xBju3aD%lg3F9uD2r7a@Ip9as!)NRX|GJkRwQr+LzNlD~4c7ZYlwz%jCB` z%1|=tsObAEFYz{0TFmFv_DKk(u90#P;!;LH5s-MZy&k)7I1OW9Wc)&#CWV3uyEL$j zy9rcoKpw5tCL5#sT(Dde-LkIPWtCz&G+0m5os>j{>9jDXuXuR3-IW$(`)Ct->;{+( z(Vjz*SO3=GXe5`GDA+iYFi(743Y1x^N*H9IloTg8tD-Ut---iYPS@_aSUzXOfnf-) zQm6&`7Hy%CCna(bnsG0hVQ@}r1?QsY1f8EVu8u3wic7DjDIOGtgR?%HtAz4Cnh!VAUpgT|nqqitI2ab)2Qukj`6 zYYp|UkkgKnL*xX_82dNIjHzY^YPcCwT@IAi4GFG`Tit;D3_1sE!}KrHR5v8(_mBfb zPNnSMOiBtw?w>TV!gXRBV|8Kb`t|)YxZyf6*g@07^07Zux^^karqpSwxtlm~zAy9y z#OenqC{E0FxbFI~6XE0;gf#_`1|D=03NhY*jkcsCYqffX{u{MggZ^hal>_gDwz&^% zz2?g+^9xfeH;rp^H;v~Z6&1@3Nmk+?HteA!90eWIn?gDL4GF3mQnVtzWEw6R(Iqe_ z>4^whIAjcVZ1D$DwL5pH5+jI-C`MLs zgmT6~B?-qU%_u63lkXnNHJ%?u9$P>grPOv9RjhWx3mBu8h1(ajT8^Pj2&+V}q7$4k z-nQ)vm5s=3@gTG&inj$I6x51%>T!;`?F$tv+6{FKjLY&WxYmr|i~~*FF3JBUEO{}9 zI}Qv`WfcC$sTqLo2mT!$`UO>P5GFA}o1maLsED4NK;yPu-x1N&4Q?yqrA+{I5QiHY zIwk=j4kI^lDp6<=)JZ@w?^H&{`JWs+qKHX?si>e{1Hpv8?`lBnH6)r}#=dX@QLl5% z+^M*}tZTiFoZFG8T)hrVc_X6h>LXNY?4l)-AnK#Y5?eCOC@Q*P4NA6HTBIAfuFub` z8N@Ee>=LBb)%nFMIX=F!GPSs7tj=AZn^_wY{4-?SSe~6)n;SA_=dRDK%>kaSuNdGi znpR6-2Zh2#Lnga64@%_hiDpb)bQ&{sUy*v6ycGbeGGgMzbK$87!>F7*%bAZrBI!E2 zq0^Alk%~-s+tX%&T57(C*FYog$X)Gtf~Ylb%k6pm=|o;euf`*nj&f==o)YaE4?oQO zQXKeD?bDXwkqdeaV<4k^%cV~|nAjC_#<7QGyM{4f^CdSui@^sz%k&(~1LUMg>m)hj z+^dfy>IcR2Fr8fnQET1JAZsJ=Y|JeN(?q;mv~@JY;8rTFca1b7nR)h|AM=SS3Q>*e zGHLBXc;BT+VZikLAfeU1c%Y$07{&m0e+)E?kE+CY(yaU$Xm(sLnfC`8MqM(sdXm-XxZ#Xp2=Vb(s5b#DQP&K{Y#A; zd;c<(N4M;wivsziB0)a+RE4Sz1lvv&!MzCaa)uaki3eJdYcB_7bTt4;GCGoFFT)tv z8E6eK;f!zXv^A*SoAv^6kr<$`nO{dcwHXKXCUshi`=J)Q~3WQE)1-vz^06+2wlPnd&{SDnW2#26qYPlc>$GX8$oDIM$}$m(LNhYk zPU1vmG?6~t@)Ha>Xmi_5NzKzo4O!CR(XuSR$U*YS%Rc#(a2S6Ig_OfCp({MyntESR zC28l|$P#^4^5}GB$H})*Re~e&C{#&%`L~fJQB!;rva&+@|34@zXX)e5d+Xl+Of(Fzy{BL_zjr5czj90Pp8G*#io z^l5rx7A9W0E2kd>x2o-eF1-8z1vuy*jrt zCvx!G8<&hhevoR=m|C3Gpid9aHXTyX*2o@|qEyhC)K60FtC%L8edvY|0pPaK5TyqA9~zFtl7FdnE^=B9=4nNJ zQ&BE&o<_M@u1hDAn#MGM^~OBe!8rzs@`Ihqh0P3-2a-Ul6B*o1X@s3>cXkajJR5;eXL-UP?09Bmm{DY4gJ?B!Oe?t+Q$J$PWo_Y9 z2+qs2=x`TpMkRkp+h*kA*5>6`R_H?9;k5L)6GCl@jb@N+8f!rm;ZWCFWP%#`xtSZ> zFU&lzVmxfj~XRW~Z zUxY6jS@z+MP>722g$}WEcQ0xtW~WY-i%=C~aPq?Bg$Dg}KS+@~Gttl9Hq4%H)9JfR zrx2b>_>kEA!#xg)y{a9yc!t)}BD2|allJW@UlFp`#l}M%b$(`LYI=HpZQ*5k8R+`* z^&3~_7x_KsRI<#T9$sJ!Cy|da+(6gESRbzB?iFt(Z;zVo+o5#NIdO@nz+0ujnJ)?A zqrzPx-YUV-_U$A&CkpxU00)7$N^uM+VtJ0?gQ66?Rgx1(QrU@dj-iJxdM}TpVgwPLBg$f6NaRM_=F%_{_v}E7? zp4=cdaLPvK*@?>Qz9U^`lia7RkDF1;Ng4)U`IhPO;CcFP=S;P7izRhg0sp8hYCZLR zK9q*tPPD&voaQ=0K0Ztu;^q58of*C&$#$-*YcX9R?7goCQh)t@T(aP;#724`^&VXG zMWno-Rmb_~;bq0eGFkDH!gCh~E+qxmqqv&b4z^wF7^m|_*nTJUMe^7x^fc&M({3VF zIqX)+A1dVq4ATkM!pYKi*)N~K+}?iq$`6zBtQl!krUwc1L7e2Zpo7$h3F>CSUgpGuLt4Sy6HQQ#*Pcz1;h;uXiX-Ne|5+$3>)I9OQe#Q8B# zbCM+L8$>PfsnNE$+S4K{R(6!5nq;VIFU*wD)kM9PyH>A@AP3zH-AiLReNYXFK7Qap z0@)?vb;>UpYENjKm?4S>c!_&sKtLN?tg+g*9x= z^L0)dPT(LP{g|Ok05mlNk9HnyGvSAa%G4T^QXS5wKt)ZQtm^o%4ODRGGIZ>WBBOG2 zQD6kB&X4S#uZ^GIYIR<}JUzR+cyZ?D8}qNnyOYg!XUV-fe{IE?bT7UaZl(W<8 zYd4o^&Ui*uw{5x}eBI03I@EF)xP^n`5(ln;5Wvt~2pkB#bWU* zS{ojZW3N%Ix@|KYcJ9zQbW8~W>pfXm(z{)Gx4WX+E1X;e5 zvPegJLxv(|zvcROvR?C)!-pd~J3Cp2n7`T8L;sO3S4cdQPQN|38JW?pI&R*mUj(*v z7JR*Q@E%QJsCuVgU`H{6S2n_S#q>hgCl?J_V!d1nTXPv9>&H=&f;p=SrI^Dj1&uPL zpubP(mr}e)h2T}?^mu+@YT2N6X7m7Yt-A8|mA9=(fH~(3S;ew+%!l(UUY_vfWN0cu zRtJ!UboE6bu`2fn?{V$A=1O38`jP@oBYn})z#j2Z=3SD*O1ZY7s??32C|j9#g07cx z;Oc8tF``U@QPhhh-u9zo%3fj_su)=jB1XGQ`q}N0G!UC9=IqplRe%O@bO|FH)d1)( zBq4tfswLgim3K!eNQ^W4g2Z@oNenmLN)^6QslDZc%RV`&{gad2twgb|n#dRM^W#Jp z_)6Aa8oR)c@Y_!4_;$P=_?%c!n8K>)qJpNpv$KLQR* z<}~rqTK%H*o(jc#&nAEkDAoRrXhZ>}?5@{S&DIfLrrEpPj!s+r>PB+>3V&Naox`A+20Pt~XrFo5~Uc{6IodO4pg1^kzck0JT_0{jg#POKL9 z9h;IUiMh#IEz@v3GI^mj6ssPYj5fC#t1~O}%WLbG=daH}7#Qd6!0KR`bsZ=>FB9_v zy@Xi3L3mug2PF&lAkF>`<730m-wIoA-U^-8n=Nxa_)p~X0qO|E*9k zd)N>YH56i^Qi;uN#~7^{b)y1Hvgd3&p0Q&_aO@**2hhlz5f;!Ea6Yqt_Pbn=OA)!j6$Eo>Jv z+D>93y|ro3NS4=nHJewiIl94%0Av+e$9Bc`gTkm-j#x2JwcDm|H;jAfH_BNU3ppWXqwujr5)$Rvmi--{*8w6^4_ zn1n?+l0-_u&-|t@5k?uHJIOR0lW+l_;dDyZ1+9?4!}msi5M3ukktBTopRSuBjxznS zk)ze59YQ4b&rHo+om-!sC*whX+zz%J9N)uaSQ;}%q&VJBQmfn~vrjHet**_jNNJtI zeoljZv|g*#P?4j{SLVr1v~Ri_`E;q&?q#@p1yc5e@MWq*>w~ItqW{j9%lETsx>N{D zhw(qQhr{@Cr;g{#*4$JPnv_eMefT;4hsp%qWhv-n*Gf^hihyP8oN!~qG;GJ~+;I%g zOdQ|ZHQJ6DcOt&W47YXAMmEdzWdxk51d&4vsJ0u2p1CXY4#^KhB$3Spfs)938>LA@wE~zH5Xr;DZl*lO% zX2SqI3uyp~&IBl4YZ%}^kt}7Cdys6Zbwb7!Bzl^Fs~-*Bxi3j7Ne^Vb(-uDV5Ko`_ zi>4XUH&x9^dV*H<+Uj!M%!lOnsyUQcruF- zU3+8Z_SoF(!OYl=aI&^E7A|hM6QlOk8_l^3OZ6QHE&)s08FX0s_A}#hvK@@P+Ih)) zec9WYoV{{;>UPb3V`AgZOBde=UY@@VSdZ{}tt>6AWdJ@PDmNtBpx@f0x9cR&FYWfS zP5Ro(G1?eUh4Bkjrl)4E-B@1d?ojxE!Vy*1Fp=hZkmmyG)|YRrT*+k7-3+H$`f`_+4JYM=JONz}rQeh#oA-)Vn^Aa_y)f3;nwcK?POm)^b=HC_i{lg4?d_L> z_QhMHTaLZ)+I-~Sz7|flw{MIrSeMXZf_rU_F4dB7^-=Md<5zC1BIQX``TEk#)b&jJ z^u$MQtj!?lX(W~R-RKR}DQWP4L#V2ynBvG<>c~3LNj4_h%`vLy?DRUEO0CY(I+09{ zZ90;O9~Q~iJHCt4uj`ge)ke;x>({5&=9g%JMCKjV@*ZpN#n6Vv3^kg)v9PQrWo0?k9c2&7)T`I#m)BQUX+|h>=q{}L z>mwJ*7aiS$cw%;1E#Ju07`bJQ4#e_kT87_6({R(51ax$Cw042I6N#5rGX0}pqjuEq z@SAq}k#J@1^89N$6?ILzx-@ew+v|GI$tua7fJl(*XfjT(Zf}eHb@$INOkJ5$ z*Fj%JI%y_}X>GzN0@#l)OwC`X%i`AO7N+NB*B9p2t}byYD6pchwLY*LbY9FYvYjOm zIQP6hH9JdssLVQ|A+&E5VyQB}hGMlm`Bv$De*)&A&7g%W{Zx!xx|1d79DNj4R6-af z#m`)(Iyx7sZaYJX%g71{{=6bFC2ynP}g zg_g_;vYIA*l5p)xnkG_>ty`QFE$~0Jk3O0Ono%Pg#=w3WD9>G(NeFUJ7b=MzU4H#-&C^>cUIm;SUiHv|*{D|EXEOGzQMreQiZ+aAjg`5VZ_KaE%{FjH;rtBlMKq+* zNF}-lz!EZa#A|amaW3)RRae=o0CHpH9%hMp#<|5;$i1K4u`nAKjR8o~%(Jd$p2hV( zX6zU^5-M&#!wp%Y888=i9HY5wSf=N}g7670qUeNLfP5X_s8pghoj?mWL&J;`H#U7^ z%h_!xkd+EN*L-5wmV5* z=3SF(P4Ad4ZY<-v-?>=>i$uoi+S1C@mAU&uBVC5M1}(aAlcZsk4`m{a81wF1Tjl)t zzBErL-sEpcDCCkpg@)WP2B0)+p)+d6(J-Xc!81E8)Zc~y?O9NXHsNSw#0O=N%1KF7 zDq;5%u%e`rX?kPd34U5juH_f%F6TZt{nCje%!?$M=O;TEKl|w%uwjVFURsu#>8YYhA}ATF|uz7jIP@62}vg0 z3mES1&P>N}A)%VK4c@n2hkP)WSC-beRX{ZOP$IU(7L^^6@@6L)`%Vhg$9tOi8rSEi zSEg3vtp(2-YnyJYT>{y%aS!}7SXIt15Q%l3NAkr3A||73{7j(84ez@l21Q|oB`z*9;BMMU9NNUsjw5cTEs#aJut=@KXoyXfSO*paiGv7U zR0$);iX1bMI#dN$U}8a=&KfxUI?m@nNc*D$Bt;!W^fY{+V>S2pT;el=+VC!+wtG02 zh?^+66N}^g^YBFXkuswDk|H{kBgu~YdEhH;>N-q1QfNeu6-2fZ8Hwq70emyyIM-h1 z6W`@ShXO6?&|zq{oQ5H9Smm+9N%5A=dzg!JAQ>(xU76!J1d->afP)j0*gAlpcYZkW z`u*%3@QR=;;+F3|5V!P@0|A(wPE~#9PA4W4(_ZrThsB1VAIuy@Q}Va`l0tpMP2ijG z3e1Ze)kN_zUIE5>PO`Uq493_Q_IA%X#(D-A>p9OD`v7C?EMq;#80))x_wL<3#`@W_ zjP>{Qu($hPEMtrRTVo3pTcJWEd&I8a@*HUw3O3+jGub3YFg;ItIN`T{(n4~w=|*-1 z_uB6oP1iSZo$L^we)^7|#6xtuirGRa7M`c(Y0J8oKc=>`vokC)0V@-{k6?-%6P>3O ztwc^3#BdC-s~MtfY^4f97{kk{9~&Z#su_lPc>Ej~T|77}{oQ9q5kgOfLG+=stn0*B z;3uZ*JJM#wu?z-EEmmn*j;SVU7=LOLb+ydhW%wZoM4<$_%Zf3nD!2aqbuBpWtn5rq zU&Mi}d*r}A92x9*NvzoKgb2uP$=l~3@lUBOc~w?14RIloQQ0sg{AwIJj=kk1)@CQf zmNOJOY{lF9uDt3!=ViLt*YuOXkK0M6EMV$%rtm{E`&1dTe?~I9*1p^@qV1JynTZ>R zj+?2Xlng5!9|NWv*sjHoo8Z1F0&GB!68lhw)Fu>xMt)qCd_uXtun0%nj(+r1+fIVL zlCI?lhSRGpwW@+HSBh9-2P4lmw5gu=ZbSCHNSWb4q#K;5O<;RF-)kc0h*HHf?vWUX z1()wP4h!T%Z1#V3-FwzcYbtVmM%(hGwsP(IRGbuQ{rsW9tqum(lT~cu zuCDI-mSXH<#m}GI;0f?n3C^N!mBb3UYZ|O8mZhcZ zgdd=^w>z%I6JtpniJx8Jfbj-kO8ltp#ui+*?^c>oVA_@$C&Gcxy=*y?nA67jeie9l z_MZm(=kJTZEw`M>LM1a}MaA_y@UBK9BNA*zxTTVxDhQ*X6`5_?YnH}2Jaud|on*&x zd?VQjFb{8I{~7Kd>cA;=PQIwJb0^SHuIA0CeAq5uKvEzx!*Z$KDjTj(_DU9qDzs_r zlD228Lc7M&kiq~g%>w)_q)0{j;ZPJ#5gFl9F(yVQ#v1gaI}jQf!O3>|ttM93K;2V1 zRt4zEI<`obl5m=?UK_dCpkGQ?M!l4{o6>OjyWl_eZju*dUdk{e@J@9Vc)EWc?sx{b*nX;4XbZ$gl>9mR7?%QgEAaxw{wXdd`p0`2>mOGF;)w0X#i-*)8KWrfnF5El zi!008@^Io4WXZRc(SGh`FmXv2sC^!pumTbOMkj&^oJ(F`zl-njs%F zxiMbP)5JN;%q9`XcFlL&RHoM;HJItM2|Y!Io6GbDJu$Icfs-U@G)f3aF=&a7kW&Q) zCu@`S2K|uv?C^u`LxoN%Pz-U%|9QYg2zl)8G^{4out_(mE7mAkhxn|pgZ*f^9Sm+l ziUZ$!Tbot{cV023mYujgcBk42W8VxLR~IeXfkw|Ei#M4qQ_Clo7j-0LG5F z<-mV2u62fthWB3S&WoHct!_JB;#_l+A>&FwX07D_=P9Pmc+aC~s);wkRo@KX@%SV# zMf4`-#e3kh3n7!YC9RJsS+_x%Vlcb zO-1#pOpWv%Z=(X!q+@IL7&>v36ZX7ICb=p-5uYj&J_Sc1?RoT21Y%PPgzNG0}M zzP4W!&Wfh-RV3PHjSpQ7;>5tY<_|YMG$U=bJb==}1Ttrf#fV6{kD$6-w^@qn=A?qyy_kHP?L2#*(kd;d$#y8E^fBiZhzp$ zS4=w2(7q1YKZ@-8Agr)0RT@hBM1xX0#R=Y0TK`B572AKn4HbB2CLGfE*%1lwRtX;9 zjjxdFDBnqNVi(5G9g!GsmH4zsOk4UnOJ2Q4mgF?8v1Fgbz~OoEY8fy7YMvJbHwhn` zd0 z>jY6r{^4f9+(c!3e5^j!;JILOSc^|y~>dOd*}K1 zgtr<|U`QVjLr!lmPk=WP(0bePcsKTf79VWBE(V((Ho+=9Vwn5dnzHR^xMPQx8sqgb zL&kMtXN9~X&o5IXE{fF`^Cx7&dk4bK4X0|SL`2(3BG-!5--H-#tUDtUa*Q2%mH(wO z2L3I{z~1875;-+h)yYyC14#~ZO)VWD59*$dZ{v!JpT6h<_) zp#Y{-OxbfhJUKQp(V(BqxQEfOJ|>pnK&X^A-5+pBA|)}=eD^yIf*?zjBntl51DZ>q zSwfY4JVm1DmVNij{U2gkAMcDb;GF)^Qnj*v0G1OK(75hFNis6-@Qin@jPYL0GoGk| z6@sGm8lr z>0Y%Qmra1n=m%Vev2x}kc4BO$6&ED#V9R-6Y&lM7n7-lc(&7VMfw&p8oguCtJJ@Ms`nyJuY&sDRNLUU5 z*)jbD`NhbZUl$h`!R{N+-Pj#ALw((WU#4>pb5q%)PyD3)o7FHEB56ku1 z$XL7ExeJj;v13Dh5g5!#RT9Z+jg%n^iBbk@Xy1io8e28=KMD!=U-O%on3%woU=vEz z{po*-bPCxlr2&cLuXde|_ppy^*5`Zi;q%n$&4tTLi#OMC?s|QFc4=X1eo@)D`*uvv zGn-~+8>6P$m=QVVm8nXWg>c>lLit%T>A{PyO1$GVtK?WA#+3#wCl&6;`QKdZO4Grw z?_oNC?Y+2mI*`iTv{2C?P2#FX9$w@wl!@Hm70Qn+vP9+@kz?8wH>p5#pj%Kqd6!Z4 zEQ^GZGiyt4RNI)wmkTM`Uip!kRmi|D(n?0Fk3BUU!{|y&2lp4p&s67hpV3-;Z=>D9IMx!0EGSIFVm`qiZyE9Cnp&81@|=Mb*{^7X0JYmlYcs_Mv3{mq@? zsg=jMvyDb+hd zo>0%gcLjGdvA#flxYBZU%%-8JY~hw9MbWuil2kYm(DKsi+Le{LRYgF0KHYOymV{I` zorLtP>4Ix7`+yj%&hSG|osjmusr9E5A^>+eVNBcqhM6<+@(kJ>lpW7Lw}*QrEK3*1 zv**;_6*G=a-@Q{+D5|ziSD!_s$eSt!^>jKk$|^SM+svl#mHy4K2AaN>;JMtre+H-;!87wA7Bi1n*37o z+}J6EbY}28SQpU#PX#P?&Y`a@{xScchPy^x7`kk}k?y zLe<&Zk5sKx+m*cULZm9boX)gK_Q?1JImhmT7wCc?d51zPJIIon_$OVbc}Ey7O_!UV zipSHA?>NmQ2)iT_Zp%d?-M9JiS1BLH*<)+-YuuHrNSBU}`i3MfQPjw&9S=!YwZ10y z0KS#q1BhW38!3LJY?EOBj?=8r5q@s&E+4=bNO+L-3Ke&-HfZ?1ACtcVI(X5zGhXgBquM$<#HgQcv{HWNp>q|q0c1oDWH#LCjX zIHts^t-|@Bs0D;{Ise|6*N%;0Yyf4Z2gwjZM0kl)B|$1kk^QYmqqX`3HYcX8&;fb0 zRyq3*k5^f`V=g$n2?=gfBks3X<;AJ=MDWkL1c%K6USiG63^hh zc5~nKQtY7;y>?Tdd!31cwv#ENo0soin~s=DL6ogn&b4>0QoVdi`_QXPE3?ZhbE~Vk zlTG@%6&I-JUShMbv)SYvS7g&C0oiPiWlGagaE~R*S+dBc%SO4Du!l3&l1aed+G7&% z(?WCaVdJb)k=7lYB*V4C{CcZO=yh7Ie~Em~t^2eKWnH>y(AV?F4+(+kcRwoEqqX4+ z!(){VIKCBx)Wxq&svao9(&aOkUz&WrM+Y9uW%BvY#c4fNq7>2C#*F)+(_LzEI~(>l z?l{fZO~mw~iG3Tn359}p??$-N2}IY+Aj{A!M6I|-XYf$H$2Zmh~ zpKijv)Z1W2s!&d&;A>W)HWLqSYjlZ|Oq`A9^pqLIx>F#Pm79c3;Ixr;d&WH?AR}kE z(?2z-%EXm7Et90Yg?KW_xO=@6flx|o_e$wp3zfqQX`x>`+ig_pXPL{aGhXW-czyac zx#w9*^?tME_{q?Y)6~7BAFJ1DP0VjV|?<)_-tpy z8k^a0W|rcYn_C|DTmIO>)XMzS>~z+vFj^=*P$ns1_RRgDAA$=eWb{qCJn7AXZq+PkOU(1jVm1l{3U_W~=9kbhm%LkS&?6aLs#T@fe%-$1v*|t7XmSIDetL#`Z)Hg781 z^<-JzEoV2)`hb=d5i;j{%5uiBw{E2e(avajEw%Jmtgqh=lgQLIhp76>7`d0^(R`_G zx0%-Ultif8jO9L<2l*9?u~+487)KORXl6^;fFU?NodCaRy^Qt!K>l1PPMBhzJUz%0 z;H@^C93vW+8$@SB61-Isp=@8r4cW@zaCHkk>Z7Tf7O7)WyK$i+gGc~cghS8-y(_+uhPkQL-sPaLubd{#=Zk~CL7lSMR)1&a#tek zr}$9boGuFoT~KDT4!SDFAT+O9gZ~nrOXpo8u1<0F2FGfbS%W~iKA<)7!cvbEt;5BW zIJuD@#0q(HgIqql7{dzLM0h?qhOd0>96!AmT~tbqSB1oCjLSnt7K~G+IS^5n!7@ve z^Wm(JK@LjAJTfMq1p`tcH3DJDce+{dvpkiToK!}MEclO~uM8tMt0@W(ErE3DUK{6< zr6BKsSW*MT#jL5SaMCb{9}gt+C}p=!(dxsep?PViCD^yZR1#_6yv7+iQ5w6VL7rSO zs1)lWSDrz6YU0maO7JIUv(3ojv&sLX7WXO{c$jz=T&hkLT$mv(2#GExx_mqoKLF!K;b%r$$f;wUgb?W7v z_Zh(}$P}(Y<>{8v=Cp!|3Am${n`@zRbhB}R_paSX^n))S%y%6U>hrIu`NRV+_eOkXJ zm@iM3JF7Y`w}Hr-RBUHk{h=|Hs_NmnYPF23en@bYEMIcdR3{1AW&&@Nwqra;bOYnz z)`l1C2v@W^pUrvh9XCo@D#t<+{IKolUeXaSV6c()o+Jy0z83+Ej-x?jMws+l7Bk7~~ z`|#wvRz}Xx333K#Ny_uf`sOck#KRpYhEaf18!--?n6Bs8nRI3|=p_8`$o#Tl+BOzc z>HLZCbdV!)ZOL70^TO`@vbNAJvch|!K4uVRh$adXZVi@(zcTu(PfT98(BQxFfaP8y z`?YrqF(5taTq3(KABgPs_YIKBn)@ebk9JnOct8Qzv5;_rWSM+gOiJy}ao@vAkv~cu)s%^dz z_{QpJcd``aq}x8Evzt=U%Ttp-|AzLRp<5o_g>reMEENhwPQ#eQo(uXhLzfajU{1nK z6#Ymd?xMtyrUu>P#dnG_t&53d&oIW^Fm^0VLL6(Ob(%5O!x*bG{@{&89&8di#Ta{l zvHo6M6wz}A8#PYi2S86p&h3s9Cl#EPdqhDE9Vc23qaX}o(*x|2jIoak*m&cR4q%^P ztmh~$p}@5ipL)569mlm4xSOJ{pRxX)4l!=-NT5ytwCz6jq> zZ=X9Mx*3J>)}bCK{_VSW?>;Ju$NN#@qqqAIevG|M7h*h|<_bL5+Ffr&K_^7+UdGr{ zA~)Xf+>6SiD!qEaNGu8_5Kl73?%uu2nj*7ajcvwQlP!!q%}+P7_o6i?80)_aT%(z& z6?+_iJc%D!PY?uqf*~;bJXR7`(ompL)G{`)X&8UwnbF?xJ&Yzo-$eiP9%HQkQ|O!t z)R9PmO!a*yxCCNUEoh>$7y21v7!AzC+8hy}8EDB*LSd$iKP1R*OvD4z)8hMx_I*_Q zKBj$tsE0A^uRkh~h`JGl^nf^yLo^Ztp<*wbTo^I#zTJ27eeZi2M*Kn^xRl7}d2(Uo zxw~(ld#E-Xb%w(r@P_?_JZhCfII;(X`hg%E-2=kNfgl{)1Hxz?LLXzS6SgAL=6r(Z z8G(PipJ59lXThE)`9(333D1ZxW`ljK&zdBmlh7!`Kz^4R`Y&6yJY-_wL;n z#rIuGzcBI*#y-#3Z`1E@GWHq9zR6h6Z!y;Mb;kPN2B*CZ+ru#&6Wb+m){H9VP7B9F z_RQReYJi*Y&Vo2O9l3VPsa&60gv-&r!QjpufI&-?737F#`dI!f&B{deRaP`yW$ok6 zv`i0tvf;McreAe!@FL{!iog%vShbeorl;p|R)2AO>FeGPxqtU7|NYS$1Aq91-T(3* zf5+HIe(`(1^vwVESN`7@{hKo<{`jeX`{eKcP$YL>RoUG6F;V12lq;m&fcORveoB0U2hWLb@ZhNUKF%2XmZI-{ND20D?%w^kcl+4e zG&G%}q3M(uEI^$-kVltl7=ZDAVz_2JZ*`)`@e^;?z+SPIV;fg@Lno@dVr;un(lI^b zl`B)m^O3XNFlsNn04jTD5c(NAcf59Id~|%YkAK$3>SIT+R=!usy^NiEY{9jn04L0i znIH-Seq!M)W9ObiB{jcD*mW8Op6Af%pwoJj2Nc>QqDVOQ77#V1a$PJ()Wpc>#ro8A zb#A)4Fl{W)&%ZWi%&$sh)j6`>ITV4gi}iXKLC=3|$vM>0~m$Fx@2leqv!UmLw774=VMV8K^KQdM-_q|BFXppyNvzJU6y6& zV+C~StXMxNy5?u+1hz^T`fyNEWYu#7SK48E4Z7p%43a5#i+PmQzePK;Fj!=y-f&n^u+ zQEV7BYww25y73k*v-9yzYuI5kXBt)pzSuBP)b*Z)k%t-kd=Gny*>1d5<>B>;KvMO}vJaC!RMxYTjE@=k>DnF209DrEUygX}vI$L}Wm zcgW{rc&lSp90f5vi-ZXm@yy!HbA?oVYR=rr*6>J=2e6 zIP3~dToEb*fQDwWS;Z2m3`0#tX4u;;KZ!gUcQza6+)29#~bZ&5giU z4WLSSLC2O!&?2iH;c9woqa{NhKqx;QRgHIJYym;}cplQ;53z-jf7Qd-zwTk|&wAK7 zid6YR$MS-f>q~NdTI9qVUZ`LehGC9O;5~l?GWjOd5EO1ZiSHzpSlnDM3x7d?!21Pm ze(igNF)}RUBuL-GjKKoi_Yh-b)yLmxqP=N_eE+0?K=`Ck?)v$H42NZ33ttaH>K_#k zTGirZ<9<-&$NLAl_Vk%v_OI^Vz58c(@7{fkv7hW=C)vWt6M}o*!x%fk-e$OI`wU|} zxK;a6&N=<;{lJBRpL2}$y?wWzIrsxM{ho)he)6zF8w|D2J%w0kD<^nk>hA*{AHfe< z)K#4$$1`_TQxsw-SRw#iM-Ak(RBgJx?fQx|u)?87^OM6eGpIw4^&B%+44a3{QLC!+ zL@qCnUXPu$bMg>)6%~Yh-QrHucT|t(W49gO4iv9|GoZaIF+xal<1OK|5-TN;!B4}S zDHfwXO-4k>=%Z!*^Nql~e?#TbKa^)kVEo6d7C!*0r0-|Rz-eZL^54>8vFmr3Zq z-G5?AL|$R6?;o9JY+gkEZN~Z^za|1-7J(lX?_UB91p?et;{A+xzYk+cFH`n3acOvquWQXO?|Db*U63F%hF0qX5=3mDjKZ2jX zp(ZEagFkoi<5RZJ6AW7e@#9FlZQ4QnMI`$Ie*O-^Xr1=+aIdO1aQxR{+v}>=7hhYwxw^K{ zsMYxQ^o><~!;)32)yD9>uzGn7*U#d6!x_6cIX*sGxmcfQR>rImr!q0Raj`P7;fz}& z7sl!vO{WS^S+#0nMlB~HZA{Lut4%MkwlK4O5*Q;)Jg&Y-{(QRk9(jWuk~i3Xym9ir z@<#vh_koh7r2hB96)ejVMZT^S-W+XrWdr@OL4WVR2%3|Q7a zhgEMv50N0oX*)>jcf@!v1P>nrDI98`421?LmeZ`j_Z>^cU{nw>*cTu(0K5psb`dq? zY2oB=PNs*NC(=iBZ-9)@;71)#1p|)Njx5hls;-|^ZLM}x2*YjHxX@XGg-#{IDiLCo z2FOdK%dz7m-y+Yc#B{z&4U143T4o$Z0pLma7+YJB8R9N?QVGa|nvBJ;osDe6l)!**)HcM`TsyV*cA5r<2A%rk79Mm#aXgbv%J>Dv~J4o-My5 zLACrZS)b*i3mr{$`N3d*APS9aXUX?4s<)ezdD+iHhl6x`XZwjAGyOvMtEPel?0Sw~oaMR;g^ z+q6PWJ4YSbrjM3>X?J(ZDF&QZ~yR7Tws5NjcLo4MU&?LZnHY2wIE8R1Vdl+m72A zH7^H6B-pSjNjp@7YAKXv9H|jBhY=3NN~Lvae4NIE$F#OsL8yjSXiZwwhUq6}tYX0` z(Rj7h4lB0;R}l!rhn6YTZgP0f50SZgpvR=(!5a_Wr^!st_v+@lN-$t0!KNCKC0}af zhM7Ld(F;ZAdLUPCB!SN{SwIPdJkyD$k@RS`!tKyg!bFbh z6Vh%FbfW?WSw^Nk(sGaK9ZU%(IIF?m7AR$p$DZLg@@*{TqIvJwZRxD zb5U&4Q8}S`N2cv|6iNx(6jF74D`;!V6%}vV+jcdKoDKJmES|7T;bQN&jKJJG3fh4x z4ns!lyBiznxP=LOG4wO-bLciemA8l`jW< zhHrnwE@c!A$h0?J?uf^B8gBQ(LjCcN#$J$o9J>Zik;y7?w^kRXu~Fdo!s^wfg*h|` zr{tAJ58TG_W+jZ=Z8LE)gEv( zaS}OZTS0|1C_>F>>+*~t%x3IN6O9KwomvTR`+%tpAZy__GIJN8f@Yx1=3z#8t}- zi6W?4k}#{NNt|f*Owf3Y?d@@9bs7kAgQVKNog~#}6l`gu5vEi@ z==gN>nP!f%C}2d!=vG$DGZnPB)8yhzUk%4}p*%%3axArf0M%+H(MDW}J56y8P8N8NQBveVajGfRt?=daK{MV_kS_}jEbfb%$*il$ zdbVDh9In?c4%aVU9KJZIK&;FyEUnFzfsp=|QTiH{&KL8MwGS0vSeh-(m_>9u==ce3 zm;m!gcE`IV>iAFO=7IOZh2Rri@P#u*MwM3c1k4hombD{Wo$T23)1v3_hCk-f9@aC` z!_GY_3gU;0xg{&E_po#4ZEGu956xuVoV;n>S}`Zx@#dAA-qiTiOYuT@>$QoIi!axD z*ttHljnxa^tM)Klh>6X+5QoD(?A+7j|GrVJMotUf1$Ha-^h)ImJyc^F{wnnSDU#sh z_!cM3vP~j8n<8bd%J?DFI}D-cMH8W>$nUH4O~qT%iNJqED4&lxIY=9a~|gZK@Pm&>d};ST?5JH74pBgZ9aq!1|L` z6fqU21>Xym#FR`=s$4FS>x)#8wqp*GB9~4A)02V3bIIDKRiQf?WWA*Am}7|Q75L6b z;48%yJD+k;$Yz~|BfF2oi8swKoibG7gN9 z)-j1X{+5iAv?^)GbFH*y+16931UOBR?Kcrw0VS0X(d~e1D6Z;JSuw=q*7z`Dqt1xZ zV992RU}6&47D%sq&=eG$M_G{9DHLM^q9SWBlLcNsC!L%|sj>7z;Iv;HZupKEO0@VfjuQm@rj;wC% zSbM3?nVB3!ZYHy>PaZ)&nOI!3C)!=5 z8wL)durRDNgCL3FS-1iZ0}@FE{D!)D9u^dZLfG+w!a#{GpgoFwHzeAV+SyQ1;e8=i zJP*3lmZ4-#fL5Gc&+0gR;>a=HeLV{!Z!-4zUiKK%l#pTCn>HMEstkMNKHtmUQySeE zuhnYRr}emk{sO!}Zv7>!*W;u$V?A4p^?aJKo=-AHo(ldlf1kVsO#>6`6CUQ#Z{Ucq z(D0x%RPA8Lr#t%YV1^yS4yZ-@2y_`cHzb>4hGDGsz+ppN)iL8;gjORb42E~jwg*j? zu!?OeBH|9bz91bt!H~b{X?;csc* zU(vq*q4xb%#%RS6&o66j{1r`w|5dI2zsBgoD|&uiEB7}T!@f*w4ZolRJin!- z|2ktt6L@}GOaBcm{qJb$zbQ5h#Aba=N3ivrwg5k=N8uz0cR;I8V5folU_>Us|1z7}z<7(Ib&lSTb5ROBbX;mrx2N6Dm@QDl@c&X{H7sD?h z{AY@ArV##RgkQ?Qxq|S2R1Ck0@Lw&4&m;U_7U5q*`2UXZ`3!t`-{`+kOuvZm34~wI zr2j0!ElU4Bosd36yxxR#`|5`-#WTO57qLu}veJK<5vxxdW zk?Tviy#W4`h)8}8QGZoL{Vd^UG?VwsNbt`}^Zq=d{<(xZ2rgO=Y!q92xtGFA9mu?i#ED*TpSg|G1{4C+<*x?TlJa6;H> zGL`sky%Jx`RN@=ON{nVJ@jH4YzQHSj+u0NzzNuG&5*!aYNgffutJmQhnL7L)qW(vL zqn|J9i+_p~e8RSa++^+B6YkD^A9|D_wy5*{YRlG3;R4wXqaH&U=bd?Kf%+H zWS~*`%eamc_FJse40qy+W5!8kWEeN@4)3_08zjSY3J_-Rv#(Nsabwk(Gh@1>Bo_qz z7b_J!C)Q%gLdqDs#VbMYTim=%YsoloD)$)3B|o$q;owEM0^>_~m<61J7Jm*FQ8={X zE~&B9d`2FxsciW{LQBqQ70nW~`sm9Ua!QFQfv6j)@|aR0jAn!~BDaHzw$;(jk#%i% zB_;xLR%?2$sq6;8K^PXdD8P6SIonPYJ1}hB*`>`$DjSf=H;d%w$e2#@$w|kzRV6Xq Q<)LJY+o;fXP1%J14-mG~(*OVf literal 0 HcmV?d00001 diff --git a/MikroNUC/20260725/20260725112002-Config.rsc b/MikroNUC/20260725/20260725112002-Config.rsc new file mode 100644 index 0000000..736b0d2 --- /dev/null +++ b/MikroNUC/20260725/20260725112002-Config.rsc @@ -0,0 +1,5531 @@ +# 2026-07-25 11:20:02 by RouterOS 7.23.2 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN +/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/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n\r\ + \n\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n\r\ + \n\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n\ + \n:global MkDir;\ + \n\r\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\r\ + \n\r\ + \n\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=LAN +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ + WiFi-CAPsMAN-7DB23C23C976 enabled=yes interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=no ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260725/20260725112002-Diff-Config.rsc b/MikroNUC/history/20260725/20260725112002-Diff-Config.rsc new file mode 100644 index 0000000..b90792f --- /dev/null +++ b/MikroNUC/history/20260725/20260725112002-Diff-Config.rsc @@ -0,0 +1,23 @@ +24a25 +> add name=bridge1 +26a28,31 +> /interface wireguard +> add comment="Interface for Master Connection" disabled=yes listen-port=22022 \ +> mtu=1360 name=MASTER +> add listen-port=21641 mtu=1420 name=wg-VDSina +5420c5425,5426 +< add bridge=Bridge-LAN interface=LAN +--- +> add bridge=Bridge-LAN interface=*136 +> add bridge=Bridge-LAN interface=ether1 +5434a5441,5445 +> /interface wireguard peers +> add allowed-address=0.0.0.0/0 client-allowed-address=::/0 comment=\ +> "Peer for MikroMaster connection" endpoint-address=10.30.30.81 \ +> endpoint-port=33033 interface=MASTER name=MASTER persistent-keepalive=10s \ +> public-key="zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +5436a5448 +> add address=10.10.0.2 interface=MASTER network=10.10.0.0 +5485a5498,5499 +> /system gps +> set set-system-time=yes -- 2.52.0