From c4d809f5c6db6399be23dad663cdfac76777e8bd Mon Sep 17 00:00:00 2001 From: vados-dev Date: Wed, 1 Jul 2026 00:20:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 01 00:20:03. --- .../20260701/20260701002001-Binary.backup | Bin 0 -> 366246 bytes MikroNUC/20260701/20260701002001-Config.rsc | 4986 +++++++++++++++++ .../20260701/20260701002001-Diff-Config.rsc | 6 + 3 files changed, 4992 insertions(+) create mode 100644 MikroNUC/20260701/20260701002001-Binary.backup create mode 100644 MikroNUC/20260701/20260701002001-Config.rsc create mode 100644 MikroNUC/history/20260701/20260701002001-Diff-Config.rsc diff --git a/MikroNUC/20260701/20260701002001-Binary.backup b/MikroNUC/20260701/20260701002001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..f23e3c997433273627658aa087e2685ec4a495a8 GIT binary patch literal 366246 zcmeEv2UrtJ7xrutAfZ>WVn7g31Dl?xC>>FnG_P-aA;Xy*KQ= zckErs|D4T+qUiPh&%NJw|NnCz-91@mX3w0NIp>{oW_Jo#&Re&5vI#Or2x<7fSqhC- zrB25YLKs4bfeZ(=3T;w_HmgFr6Lcg8Lx_PfeoR1~bcN10OQWn!7Vk%1%k_G_9Z5$a zg#1t>hl?43#sG2`Z=S^?E%6mAfKaRPIVklc>_| z2o}oeP^bwcb1+md+9q8HNyzhS;Wt$2_7|kYB4ji~hr%!P50luJ)S51(ij+NCTYHO^cJ+x&# z8e&jifh1f&2ydx~K_M#tBD0%IkHJLc4P@2IH~#g?iDxJ+dP@|G3DOeE!QFMfP4ZtEf^-=j; z1Uds7h7cNo5E_XP8if!Vix4V82q_T4k`cmG2%%mGx;4Vo2%!vwkQpi`+8WxtFG8q4 zLf8O=P(F-(PyzV}x&+a0O%P((&=F!d$`N8vk`Q8;+Jf40fO{#RiBjQbI^-LOP;a=S zfuveE=@4SbGZCT}DGP&PvJQXHW^2+5z0Y`&bK7{RzJv=3#AN1h)_C% z;Lc$3#}I@V`9l$6m?%-jFtSC+6*{x4gS%IFRD4KmXh2YicXM~I#PDG6;Rv}}x_br2 zh6jg*LN(^oy1sF$pvMj?_ma0;u>w2g&bSiba7L58EyJNKMF#w_IMwq(%ma4B0MB2zDICEc(8YcxwbMqQzh>q&s50+ zz9|MCSy7A~-Unw_6a(X>q7=HZlosC<0DM9LmcWOFyH}g&xcI1m$dD>olhs;Xx+F~j z&6A{2XJ)h{#m*z^2g2i%G=L#CNkd%)Bn_E`ByENv#H-{314U(M7f@m8$(ka+AWuii+ay{`NMwO>1GgDIqh71Jv6`au^ z!&Mofxan>)tK>12N@OXS8N!-!nLw_~1L;J4+y`KlPcloBq)U<%8o+F+GPzEjDNBal z4M%8|Ybc83q$7m&MhIzJ>PyhxXEmfVwlE{co4vh`Rc=8J44XhL#(C|hY8s3-& z8h#F$<_-n!695$fiZVwC9)SUZv_}X;Ojr^#V1bSh9U9aMAqLf&9Hchz7dx;dV7QPY zoM4P1C!k#6M1d1@8n8*V5OSqhnKQt$fO(;&s*@@@l4+VEO|8i(`bRjzs&j<>5=S(- zN}KY}@I$H259QbRQOJ_j|4fTftMfzsC4NjbiZr!OF{gTqA+ir4dqgOZ|I0o^tIiRv z29B(=ByzRZpE&zyGQaClCW!W7h|j=?X=;5{i_9D)T`3HRhrGI1h~D8uL(poCkRSHV*}cC9N`# zaW*T-NC%YzszXky1-Qyd)eKE(g8(>_wg5mYC<+FJz^g`+4O1)xvpB?t$CCoEM97t{ zO4lhg=?Yzi!81-jjVx#+*+%{93L-+F7~sDFkz+`KR2f+UX1YR^lq^+im@;*`Qk9gc zk$@ItCQGzTS+XQONg;2>%t%#8v` zL;Nvak?uNGVseg93xp7;ZSWklLI|}+2>By~1t5fhaRI#279rFQ zA;OaogAj&;!C)iAaN#3Fry@iVkm^^65W`Faf+x&FXN2f@CPH1vYcl*rMWUr%0P$1c z2Mr;ZT*%W%r3{k{n1rekqTlr(CNm6wm~ptkWP^Ir#^Amr>2vtFk2?d02_`4NC^q!9q0t17PEdm2$2L=W? z!>JB%br??d5Mr3uM~Gg#HbM;`wLd~9AoWQ|c#6o6C`^#u5rh~mkHQ^WgpR?L30>5X zrzs?F2B%nfAbGT}LWfkLgR9U%Rp`JfbTnbuBiD3s?c6lXwNFNdlkA- z7229~+4Rm-WGZwy847J5awNx-9vM&`-AEcnmXQVp(UZIn=&vhDf70vqQj-3x*X!jZ z4K5QINq^Pr^{FHcK*d;Qu@yq`SR`iz6%^iX3PMAv2#uv8R75qXBX=so#!(SAk&4hb z3^7c`V~GCP1fjQpzTRPo!FdnV2Mp0qnGsl=fqjHKpWyTvLk#mTm|=GCmAsZ?h>@>{ zQzr^ya7rkKZnTqv=yQi+Xcq<13mp*JLosA?r6M}W*^O%Wdn2mh?*P@NHb=;VYDm`@ z($Ns|geyW6FRFo4FiDhQ;Jzt@6ZAyRaG*w#1&)Frqv14$iWp|&ftmm(7KRuteV~rM zP!AkK3`aI*;C2!u7(%V=eE^e;TC&^UE>p_?tW0eQZ(q7X$09wTf4^|4|32Z;{xcoc zHe01pq$;#pqi}ov?LC+F?-MTdKO)$S4lYgIl|3jVECRwf1 zW~eI^gY@r^Emp;n(ZRO<`^0PVpXt8#8M5B2%#2jEL~hg$^1nmEl&TU2#PUBYVMma# zT%Da>RmQTvL&jC{7!f@C_sQ7oKhujH)aj`?tYnEso-NTRSOo4){o6$h0Q=u3V$=VO zi0h|irs`A%(X#$=v_{0(|9!IlW`<$@56v*FND$1@rb$u>FIk3KqwA5XmZcb@H+kfA z7$%ZNx}2aRIZR@KLp>D9ae>)$Lpae9ay5rF1im_5P8?p3^qPQk%n(X21q%Gh6vAUc zxQ}3FPxxc=JqW+GD+G#MQGGZyf)h-Kh){!>R-sWzQt`ZBdOZ7=9#8qpk7t7$UL%ob z$$g1vm(hRLofH;ayQ4^s(KTpvsaqk!uR-`qI#LMtI)e%Vsc^7E?84w)BLWA6hs_@D zRJhq-8qYWcb9qgM7Fx;RA2b}`e1eC-0S+oFWLQZ7xhYkDGzOLpvSg*IlT;NhKyX>X z0aq630)+JoLQ+D`gRtTh*!6s`T5e79lDdvL{k4|QfqF%!u6B9EJ-Gv8 zBE+_htsDM)%`1n+%c3V85{~Te3Sl)9i9Ah}UL!ZaKjCYwiJQZ5vI?G}GDdo<8%z|DqJ%j%>qy>!pO%}VoO#~U?hYuZk`V{hl;plpI0 z!pA=D7i0r5LZZ)tZ2Yt95wal$It1!qg;lw$K{oNzZ6;+oejXB5G;9%X%%$1wg56rL zyJxxQ$n;Zh9d0}yYv86&_l8eewt3>*^5nuXyDqz10yp$XRfh(=SU8bqWS_xOIVB##54?!k+?WvtEzj?5w}qnI!>d?gZz zaZnnAz;J~atcfv3K@1pTI1;W}@aEgruG7fT@A8d}*ZQ>QGQJ#rWP=bg&h#@hYNaZb zP=IjUB!?GVZ=y&}Ed-Gg7}c;MqE3ZYD#tuX6#yNX4k{4hq=fmXRDp)UZ%~2`XKB-Bg&X-Lxe%gT}_fQ z6fm{>_VIa4)E?8yG%5ZCXz$yZqglJ8^jAfc`!Gw zRw0=hb%w$>QmvJ#vz-ju9wTBv#_?*2B00?vqVJs_)C1q*`)>al0*;LdqAO?D@Iie!Wuz<+8YO%Lk>#OV2yC<33xS9cKW2C_r7-_JdMjGsb zk*>ND0%XJ?%Y#UjkzS2KdRS43z&a5NSHO}H9T<=hgzgO?GKiirFl+b;jbY3N0kvw` zAcFL5HcCY{jC|U6*(fz+gUGz$X_OyjqgKyGA&hNBHtLVE(Q3#>tH?(CQ8tqrvYAw5 zGvOE{`CZ*it7n7ZW~i3wk8+s(D2ExeDuHs0v0r0ngDzpDA(4@WrZ>`%2YPKogy^h6 zX(}Tfm>As4NOQYY(nq3$dqJ0yDKu#SE1(N{K;a}-iz63yKo^`2-4_`mRTIhkaOp$R z0FIuKfDV#MfH9J{Yh)Vu^Byv61v=>}Ndv&@3;h-$B9ucWM=Blg6UiBa&|E5_p&-A6 zI8zku7akWIW{!fQ{9@H=9kWx&U;+X)GdvB#n`n>$7ZNB9!h2}QmSi)8=y`65V_eI5jc5TYNYAT$ha5{r=Gp{!0JHein|5W=rv zcmZ|^FTi@vrBW~o#SSf`q|ASwn#(Mu4d|{|JRu)9H>Ecj+UA5GjY`FR*cLd$l$t;@ zb)Y1~v8`|m_+joqgIcj|aclTVbs!$Y>=c5#I9qY}xESZ+#L?N=iU&VA94=qX7kB>4 zC2rM-T|tyTMb^f> z-xRr}wNIPmoRR$EvR0q#EC|H?iMlng!7x3IMx|h;xQ!__p@9SZHo>VFrpFt=4QpC0 zn&W~)jXRH-7jcR&`!b`^q2{X-Uq>fal_NC?;c^PzCt}Us{;V@^6DTX+I48XB{Q1$n z(`L(7jkzN`5w&Y#JsW=WS}go^okEuXf=y$(Z`n%0>vX?AKX=HDomW5X<nNY^TPUsBh820BFH&KRT0&eL!=NN-q>{8o(SE_IBvp_?qhp4r%N$U2f?sxawvSwu zq>?E#Iv<%jtuAQLid12#YN;fZNjxAZ;1@LNp12UusQK$U>5@Rh67N5hGbo@`S2Qvx zRY`TFiow!te^|Ors8bBosf&>V0|W5fuZZ>W%fGUY23lKxRoiR$B6%MIxLTwKh`3<=n!c7|Q$=3eZVii_ zKdjhU=5;}r*SF8KPD7H@yEfvq+ZDC@Vdu0v8N0G=cYVR$4Y{;5*tR2QS!V5Xr|0hs zyY1QQ&2;XEaC*;?r9Cz_AG~Yc^0pEETb+#Vk($+gZad+)*;n1}r=4)DJEqss^5YvE z<<1AYc+ckSdR6x6tVLkyiY)=0%a*0jKMXv#V|2&Xo9aCb_nMz^nN7R)etoT15#~{5 zOP{s%>%J*(LDAkdo)jvEumv()@{R2o`ZL=@y&l6)JRPw^`C`HCdiC~BS?_l`0dEMV zsS&LRt z`I|irJ09JdHnXqp!Bp=RcAM^G%$;{UqHx-TQR6ahudu1VbHXyc?A%?mo@W>An$?Bz z^4yp^ac{=RKDAzQWux+jRC)e%dcf#ctWA%z+s>KQ@pjWryZswIV|f*Xw91Je-+1cH z{5DI4&tII+=~?Ug%7@m+(^m#Ni+sZ`-wJ8fWSb!FWb+u)_dXBzbqG(DxYj;LTh*s! zv*!6JOP;K8a(r}g_?~T|)Z61yN9g6-vOPNByX(0xxOMf>jj-HJ8{O$9_)}Ji#jcQD zG252CeliuaUv;SPz*Jt3(dI1=JUTaNQrdN}ouD8pZ0DZ15Totwko8e0;CGECLk4Eg3#{o zQ5AizVX9gMSos=Ji$TGJapEK5V&ekh+5~X;0uIi>g96wrb`41D7!5C=9l{H!Ye}SK z`L@KgX(HuK->ost8XbF~tZL~j{sTxWj6+O<**KS+0~?SQ&gF=Wli*Ij1opT%+kohr z7-eJBzF{0@d_){GHZFi^5D*h2#0&~x|EWSg_TE++y?BRA{j4Z9aeta;zrOZ~BVNAw zc>ckyu_cpMyy_X?lySu#eeyKl)Irpx|N8b5dQY?2zuu>QpLXou>5i8tKHvAc z|CBq9Zyh6#F0p-8>+uS=7i>2>iK?ja_CA-UG`!Paxgs-olJ?L_{qt_-BRU1$TDiWt z&FeN_E~E(e$gW-wJ%1&nynmywMGxDXxwK!<<5_rUd|tD{{cjv*PH}2UeK=2iRb|Q9 zv!+&uh>2l=x$_Ur?0>&BGJZ|qw55A!e`L=*-{$TKyPHk&r@aWuIN3TY<#a^&3is}R zSQUH?UAiK5!ZB%VVCMJ_(?50ZzJ9IbUc-jl@)%Pe`h6Od7p9tLKv)G>0bYO&!0jt$ z^mN=5!}JskRhiW5hUD*79=svEezL?{xSZNx=d9oG>+qq3HuAQ{% zuut0-7nZzJ7kYoeTe}b5v3EpRW9yY1BL8xL&CuYK6J%~msz zLF;jH%4{D_=9rVyJD%yl9w5X9u2-gc*IlP%q@7MLdb915FXp(vYoZ*x)PLAgQQVx7 z-9pp49`GDIuynwvQ440oJ~&YLRG8c3^NWPeB8}}u+D)Hm}K66WF{a z8*RYmG9_Os#|0cIUx0IzJfTD?;^Q0{hbxmQae)A^`CmX@KSJgcNoS`i>Fi{nIKKdm zs;@d-Vu>RB;$&)_PAiwBDy&enUzj>rBA4fYzmtZ-(`7zQ!Rr~OW)inacc((5Qz=z4 ziB7=`$kZjPH7Z>Wll5zuT|Tod5m<75m{%3E(*d)mdesT5!W%Gg4t-bfxU-qb*%jVg$adYvBmxcMC+jTp} zc{)TIo(mUp;C|#*=V% zS5@eSn_&j9523v8LU$0itQy;^4COVSKE%v6$!qiTcO@nP>C8P7FWX%Irjrs-b=s9~ zIp;o3>6Ap3yd2QNl%2_%#~c%_7`E~K;+e%4ANiiT(z)!UZj^STDDbjLzYmnmtzQ?d zyRiD*h3gJS<7@|v2t7UJ(-xnmb}Ne3dggz8IH&U?;hYtBZ=`M*;ylaml6!Q{IR5z_ zv?uw4R!pa`Hrc+pSagu4^?#=En7eGw)7dMM?Wc7(FxI{~V_`|~S5qh9o*8#J3!aG{ zUX;IXHo)-<3db4EbD{e(1R^QR5<6o-o^xz)p>(|fx&XK$x;(NCS^F_v;>lEY;q_Vqs{S74f2T6~J)|!=hO$3Mka~ZWNLK^IWtP3%T{YreknRV+*#iS zVA*i?m5i#ei~+E$rOOXt*|*W@h6Wy3Bo8c-2UbwPH!y4$z_1;70cC*!hEZw3yQ|tb zh)55pg2Q4W*lsw}pwsI7P*QA9+yl~BIJo>=UdF*o4(+zLx#Ig#b5?w$yx~Qj=IuuRlWc77)9ZFD=WY$R#+>e6 zXNl^%8Q1Gb%%qP?;*Z7;jR{%yG5jass}gL{vyLA%)4RXhfBe~wqT<_wg`t`6zr$uV zgsK-WUFCKH^IN{Qux zOJPnx7q|6mrw4RhoN=@CRQAzEXVJ~nMBiH~E2~S(cexIXxn#O%seY31)bjG%8{ST1 zg$y2XFOYFTNAZ0>{Nl7n#h#^Mqqkdy*3UlaxUl}Vh3tJZy>wKbo?EB!qDNM`Ev=>; z$a?5Gd4clUt<|prx*VCBI7pjucWB)+?11gQJ6R5$v(!-!qXt^H?m~5&Yrj+3(XQ=L z>b{Llny@B2-xS9ew>g{TIcqx0bn)S*;xj9*%$aWCN#T31^PlwK^#DZ{fWi za6&x&lIUgo_tDJ&e1Wzw`UydU7{S-?!TnQZ{Y%Q{_u&3Lxc_T|yApEoWqc7&Ad>U> zO1?qrWA1{LOD;u<;bN%fk?*V;UWbAxoXm} z6_Cpmhmbe{yMAnoAr$>5CLN&IkC6d^yJ8}qSs9|#CZsRS62(2KRdf7D%yDUCX+`P`l9*ax*&src0+?@4lS&OFQKHOHHdRmKF#AtQ-Z-^E`$^uWG( zkv8ZxXK&o|E6dAuQL2L} zqk2xbzVD#rja!`6$0oFB-)Bw2PmhV}xG} z?^ri)yx@NO@z$>!_pcx6TzvRxP~%g(0($r_3l4rW?LmiOvtt@|h+o?3t4`*-E=Iw) z-!af9sBXl@7an1qKG9qzlr|1+8#8eI^__jTd`WV;b-2S|7aQ66N7C)*dnEWg?s~wB zPV*3NwQjoXRm7C~X05Ga-#&ZpTVPX(RV$%sT+Xh}%bT`68J~aa;*8z1UahApkA4SD zzw;|r!K#+9x|M>VuBEwFC*NcJ1Ewh~wfke|9Q|-|nrU9W%Rx?aW>XS6Om=)3+tPE` zM6YKJn`6THSzqE}b`PF$ASG)1DA(xuV-lp}UaOog78l;xx7_0E^QDj4B{VT*rX}?8 zu2a?n@7m7YEDM*3@>A7unlry?plHc8~hf@#%{mQA!Y0iQACw-l?)F~L<;YDB4{YPLx@I%8 zQR`Dz&pq`k{j{U?u_5QxdCOQgwo%@{oicqB|MK3XwZqyj^P1ggjJ~U9%B{80ug%Jk zPid!NM`HsfJ$@5*p-di-kUP2)Wzse2tUtDub@tK?UNmD%(LqbI8*hgTbe!{Ed)BVC zxH&UoZ~gOM?x;3}g%0$cU$Ek=EI4D|Xg!O&ebv~0n=%Slx2ki}-|OO+Lq$8fO>=u2 zd%xrUhK1~)vcd)z4vc9T)T_ag+SmL0)p{xYc%*3QSHe+xMqlA51)F2U>c92>f5cS% z*8jit|8M>OTmS!8^nc}aP9YR?WnwALQOZRU37f0nuw`5}ha-~6WKvwjW{U+pu2jeq z%S2+4SiqCQVh1@-Amhq;GO3(HOy_FCB^A>-CeBvM1#&K~lu5WU37;(yv87U(fQKu% ze4NXX!>Wc~!}l}Er@~_P+H>!*nV>UW|ELt{Hpsx zK7a2*n{z8dG*w>${m0+13gZ%!GA<7DFb=%u1q&D82cP`;pJ3q!uk2o1a%snitrnjx z@?6in*tt{tn%k{zG4Jv912c+GO?vm@*eQ$ck}mB-2h3*8DH|iRbbe}<@%H3kAOAr! zAGQuiYq9F=LdQ;mKC`GnWws8fn(M0zm&{Aq6m%l#Y9l?h{({@BekIO7vf{T@1{*aH}Iy>g-(sSYSpB=nYjoy9H#P#q-yKX-vFx3A+cS*&yMz7J z4pwP_xNM1BAmE9VA~w!rb44;CSES^M6%rnogR{9@u9&OfO4uTq4Cipf0)ja@lg6us}79oGKQG&*$?6Qkjy&hhq6$zKkp3;YvAQBIWUgoFBjQ62|>Y@4Q_5 z)s=O%QM6y2PNGq&6shuR0bau*yJp}}tV}?3vr3QR-?z%n@~^M5V^y&AmzLSxg81$= zynsTQr5cvmjjirS{KaK<9(W^z53zm)W7PyouDIjixWSd^U774p#QNt;?*4Tg|5=cz z{oP}Zrt%xQCJR?hADTd0!6+Izz`UpV5&_Zc4aex7Q^^$~TyIxuH$4A16x zCw|@bbksGsPnxTqk||~HIuCxDUT53zW>*JyIj8%a(qsSdMYgfSLlfR?&8&Yv?{56+ zb{`Tn9Vm4^IQQ=K#Kp@@w|`cfov9n&EL{6qF3cIf`)O$Ryjy8;5z;ls8(PSAG@teP zOAeYU(Vc8u;4gc9LkJ^zlZ3nOb&qV!ffU4zM`jZ5jYPN$|? zTi?I_W$)95MK3G8YTvoZe!ToH*OJq4$t65*U;pF<(xvlGtT5~1S=Fm%LA*1LA-hhW zJff-UiPKt4Z&)xyT$*JvvHLBr32D-9j6dcsEnS-_ddcfG>+YNpLmRih-|X}OR{A7n z+ws`bts|29v2Q2&jG9Dk>=QJrE&HAICd(aLZVU6=wxb2s{bE0->}av?Y)<>A<3(?h z%<3F!8QRwh>%YI_gS7iYaOdgn`)qI0-rszcSGwF>AJ{yDmixc}*=nFCG^SohM5wfW1gBMWz(nqtzn_;GD7N>NFCABJo1v#0wvxKwNH zGKt?QbL95rV85dbd5A>||AFuO=W^aoJ<@SZ|0ZQcowV|g0$q2Hd2f{_K6zrNWlcWE zj2tsW_2y^G?|u(|mNNDncjx+rLx$K+ z8EQF~KgPNwqWJi#KFaf58rPcfHh;%#+WL!SsqNb@dO6-})yp#3)Bf&Xo=2XZCQOw3 zyVoD-w_woKmSeV9l{*QumiQE>ylXq_TG6n;?pks9uDvDCJpA`8Y_2%4Gh|THJ%d*D zY2u@w+thzaneW@Ox|T1>P9AJ~_qxNi*DVL+Aoi|jUCTGcbu0CovW|V#)WySW<>W`$ z%k1;rR`sSRiZ&K^54se;aeB7PRR6Fe7mIt3{kTeT-fHHWtOsX4cRVO~)yc9+U48k7 zn8m80&pqDAn39N@iMz&4JG9lTEc9K%$R5k)DZV=IKA!s|I~A3h#JWYh<%ID1j#QkP zv-DMNfA013x#rhmI~0#;d?WglQ;U|pft z206>lZP>8WuX*tEx}4Iwu@4`0db76V(Cqgea)(?gDN7x>sNMDzh4a3SYhk*rRJQQx zsEqEZp=)H{M=BMXyw4&Yssz zo`1Pun)p?Hq-7o7dE-Oc%e^;zjGMk@-`Gjpg7fC?z=A@7kD6Syx)9-(2b$^M(HGtlN#*MMKs`F_ug|Za?gW<>a?+n@5)Ct??O1o8H4=%^$m? zKeAmc-qf|unQ~>?)fN55A8&bf08RKH^KrX7vr>*s*@RDejDl6pgPMPyqS>z?Ao zmub&W($|R7j+qqlJ0^$B;-IrO!+ zQ{Bn@qUfja!b#+7M)N%R=+sj7!CNc){u4J^|QxeWt=(Olw(4EZn zM-LYIq%ZY6bNWm}%)DOTV*TMpqL1#8_vZ1E100>N?t64(;_~c2y5>gZ#5k2%+xV?( zB1&3C_3SWZ!QeSL?IXtvN}i-Ww;P?qy4XnDBzNHF7119q-RKuOtj&$+o9()P?0Y9* zntk(~7a2Rw>+1;=Z^f_X=lJxQxOj+5*v@`$5-t7O^i7~XKd|h!6my!DAGr1W&^dzw zmW9nsb1_}f(Ga3)tY@5;Q}U0!P6#S@K7Fj)V^D)ItF$PTzWSdzV>Gc z6G1v&fW0vIC+SXiVqyxzT95k%QsZtFACR*Ak@kZbnh;hU8D5HF8-0r+oXh6n!p^@` z#-D+vt+6p3n{9r)W#n7D;{?xPu3-hHqs~8AOtW3#x?#$(_rr49MmxQGX2-kW;N3`Q z;#G_5w&;er-It6dEzjYW>!!x|#677~|8=hh3zDB48ggj+hVGQJ%HHx;!M-DhTS-jZ z7n|t2+FcKS&0SbaYqNlUZdV_tQxQ5%X_$LG?bpPfPi*mo!7Bwvv&tP$nf=9^3S*{wPY!8Xd*MBNQS*TEp!NZpx_My>u)BBeIrMv*=0D!$ zU9WNLx)1rZs&BlLdrz}po-=K(;>z@#;BZ{gdGWgz=ZfB|XJv-aHcg!JO#>6JEO`!} zT9UQvPTPb1R*rn(TW~U+K2KmuS&`?0t={sX;dO<65lZ>{aoZrKF!#9g~&QxgZOKYK56EPH0RqK)=zx5o7M zQ{(P<2n4IKKf16l*bF(dpZoSo%NsX}z51+P`@Wv&ZXc^Hr(Y=BjbGh=hlRg*;Owvc z`+N{PPu||kj+is8F0XL3l6$QYuPpr@DZfVwR^eUvUmq!BeMI3=lB9!4cE|iF*A}L!!e3n|G;Bxu6QYvJN6k?@7z)`Td zGO>hf2yNAbq$>O%wrqX~myh#tk%%YchKkulDC-Blf~(&;Fkl-W) zcBD026!Ue>@UCN)y7g(&nc|77=_LBTKZMRXGZ{A&*uv(2byx?PgzrOXsttXvAsoS~* z@7VL8)#&i!XRgOfCtbTd$TId;V2{^#{nq8A=(p`CMV=lz3Y4eoQYH`Im{H&P)Q*+Q z@=q+RgU3!kUC+Jvz!Swm@mT%k5saaD=Bm}*ERVH!aqF*G$~d&%{rb}Lv%PPI-*-$+ z3l_1xWSgXEx{u;LO{Mk2)bs>+scbPn^zR$@M&jz=) zo_Bt`@9t6D;+H4t_i40iVHX@NzmO1rvxKrI!%Xix=D=9Ho%@DPIn{pT7faIzuj>yx zY%xRQ-|E%g;M(zbc9ez(C$8h&w0%CJ^NY8B=cC@<9cSggy|XO6xl7@xn0_waY?F&8 z5Bqkw5m48z>|w;aE1#r-8yTBk72WEe%SvqM-l%c?BAx%dElww=_I{t7lX?5_`>XT& z96eX-*x6&pMtm`$yi5IKjT1GmfWAcdenvue__Qe(+l+nn%<4ck&nBp@(rt>};rXXJKb^(0pL_XMtF+B;?{)Rb+k9lq{&7V1(;I{bDgEOtt;ts3sFXB%2c<{7+^Rb7c_D+5%mfdH}W>29ei2_PJR<>?Yc5Cy7 z_cC^u6Sq7vukDO|5%Q$#$77T84?gI6b<}phfsaP)URxsTY0amftMA$Q;PWo+M{eoR z?Zcu0cdwUBm{ua$eSblhAlrqV8eMqe{AtXd@uP9Y-c7q3hwmNYpQ2TLe7(f_fR)bem=iUB>5}3!ErSoV|~Gr|z@QF&|Z{cdJh`>n80EzO`jQ(Z0UfcKnpW z@byfq?i)A0@eEmO-EO`8k(6}F$|D~-&rF;XeP-WTPVZNv4kb^Ut@mg+_e4?SL8&=M zElj$MzFTK^u;2PCJ)}r+P=&o}0W>IIG21S<8D~+LOJm`9B|f%RX~o?ur!$F7)l6 z7#02|{$|t`YRB*}pgbpj}dvwmU)!BbwCtbn#M(*^TGHk&lMY3<~e$_sIQ;8&$ff zLm+2;X}DLPgEO9NNnzO}P2F4k#_izw6T`3XJCg2L(k}U9urz(fj49LZ*4L*D?=72E zFtUD!dZo^1#0if#=J$Sg{*1iMwkwC8EvScgQwKI$)rvl6q2rzl9l>Ey2OJhocmej6 zbXfE~i`N=Ca&5pj^Vwf=HvE~xq9MBhUXKV=Th^#P|I}M?M%{VXS4E0heyHyj-Lt(4 z=HQ;HHQgx7g2onYY;v&lTxL)Nty>g^&~vEPLT z8(J?qzi8p;K6h>@(x26Ca#Yq}T5euwNc&FhyzM@GuJ!UzU5C#%?l=p(4t_tp;f{jX z4cwXRdmm@SjpIDrBJ~@5eDCJ2cgA*T)N#$Q*0I*Z-Uxc6jA76D<6S|k;`4UfwU74H z>HVpNn$vl|N7wpw*FQcy%JhDDLWd~NPVR-KWmg-nI&;bU!|htshXZ5IMly@3^a(RW zKl5Jv9yY&+%@3S@|LeoX;L7-ofq!G*-x&Be2L88WV8WHb$GJSQK*>|G zd^U$IljBOMNG9Tnxk|Q_BjL(%1+I`u1X8(DAYrp5Tp2Er$~lJD{%gWr;L4~GZ^0Ka zZG{2BTrSQQhyq1{fjEzFZ`6p3`JT{8H$Oc&}I_urvfe#g zzWegbMeQ0-DPkX6JfLNqP44-|S-Va*_MY2z#@*7g0apC-*Ov~o7?%6tmgs)3fw7ey4gOJv#apsM&EAs zg1stV5B=7MA8x<6wdG<9#T>^AMavZVJGQp7@|!nC&?RobZNZ}*o(XH>L$5puycm4k zvYTbI$4P=SzI%HF)haN3_;FS3i>Ao#uBN4pmtRA{Ft2gW%jAJBcQgGq4(|2bZRmz~ zdF>0rwuzqJsrB^9N`}{?%*3F@k5`;)y}jGD!;?}2QIGt4iWbYeJ5D~F?GTz<=U1i* z3zMeBPRLE^_KLQbe#qkE+pd@2nf_l*6`I+n*P3S^`(;OmsCiDK4lmy^(tl!`jj`Nr z?*57~K3n(lYu^s>DBk_S?_G;+w{LeoaM$%o+ccYvpVUL`uo;STbvk~nvwY{e14*a5 ztZL%)w2{gCUg<%J4d|_l(Zkq!tJTWq)6Q*$HEI$NCG)o8G6 z^<$Hvk>*p6yn6KJ)4mt|nG16+L_WCY?8FHZv9Dac^{A}=t~tpzS1+u67C4#F{e_iX zs@~hzr*`hT>&4H7l<-BK^%C+Q46_MK&}>z^jz6;~^d49Gv#G-GA@h64RQXDQaXt0_ z`jDwyP|4@<fty+E@^-bhA)xy5-Qw}~x9{%16YGqVSnlG#nu`0wFXKA=qpXjV3pOA4_ib4jaM42Jn6ep#X$XATsQk z%po_F=95<;d2J5wX|{w@D`NY1y}mVh^@siX+GB;P}rbDG!^*c@^>ljRsU ztgb`;hK;MS`s5GTz!r8|C3aYCgb*Viwy!q#An!DWg1q454JTrgcORgLJ)Vg@zvT+0 zBr{dVgB`26aI%3Dv9onk*k+SFnpL5Ry{~N{SuHq;5p3#Q{Sd()ST+2C*hyOefAy;2 zub2McZcRG$&R=Oz2JCE3wAtU=n)YOCvJ~kuO-=@Uq3Pezu+WdR>1i1(nK~^kGhL-q zDYVAzl8rlX!?h=hq#2(>2mnJ&j@h}eLg4u8iG zVnoBn?pE$zK@s60QSm(@1A@GXvSAxdb6KiNk*@2JCXvbUKimH`xMEQYcBQiTjZpXdx&agMMKN{lcJGLBjMlYOO9xlBNhX zB*Y*g1rlaMLI=DL&h_jN9osG}Haa236YqoL6+GHk=P^~K)j^}HGBVWaROqX~SQC;N zxHdtN91DaBDahQ#WwN=8%OQd-%L-j@4qMeiC83<`RGCDU>Q0s;u2MlG4T{W;ybqgr zi%}%U9F-&cBE&GK6cJsGLM=k{LcLy3D?;?O#Na4rVCF@Lftf&6=|zNb_(8^XhK!6p zgjgUKGC}2pP9!qg5lv`NsZbn@cVJwmOfv4t?q7xRFp`GxGk~O_bqYv&7>1BPisVpw zB8C}eix?Qy7BP^uEn?W%z{wWw5kj+vKR^qb^wF|7VKiIhYLX?9tF>WdetKp8dOs+H zQmqg^QHU8)2tR26isaCHqCzTq@x~q526-TSoCl)iBg`ZpVNf=uI$IQAiwY%XU_B9? z8HEs04Jy>77VvG_6Vcbwkbxa@@&{CbQk8$AdlisRH$gqkqfvgxXf$_mG}`?p8r=+x zso=t_C&J(vL{%y9Ao@=p1S|uOv}!yO&#zu}f&Y zal%+!Rz=R9-4ZKu(g^`U4hmG-j33703j}!EiiY+tjv_>j#>G_6WCxF(p=bR#tJ!y1MU{;ML&KQ$`^KR|R_OE~EodBJmt@ zSx0uNOr9apCCijaM%{0;#Gw0)RyLfl(2<;q0Sn3rhl;DD#XYy*y55|A*MWPf3jV-BM*L76d0on_ib%ZgLT zXiVg4F2>=@_VA$sV%+(m$nUdLs%NKlhza1-lo^bHT&-jG@l8vSu*k`o(Gt}qnS^Uy zgWfjGY0Qb?K!Q@Tt`dnvrc5$|KOZtHT)}G7z+#!eyKG=7K?KCa$knc6$L7`lS;M?e zq4QN`=vYcss!pMSGQn^G$b$o}j@8WwVP61@YWThyb*4_0oI%Zc3~195LkyQ_;LHJ)GhmTDgJOmm%m*C`F~<-CSzw4^P6*xvL*AB=91ndt z12N%bg&}YANDfn9PB(XPX`hH9IW?37EI>+|NRFGnoX*4$YN#(~FrYXaLvc(ff&m9A zceRdGr>k^o4J)8S7^g!Rh#TZ1k;^q&qpfdAR>_8(z`_CIFm|~TgiuS;WYZ7=_Vmyi zdYFm6(p^GG)M1{NrKbQ|@n6R;-ZPQ@d4a~55BSe3Zf`O-jL9P^Aa%M@mDHm8&CtwrSqo-=B|@$?0g9Q#_dYdq?^eluP_jan!VFbwm>&(>y6vEtRB__rlY)I!S7(Azcfm zyCG%uyTENN;Fc(*+TUjVD$~9-GtI1t=}<|Ef*Gh$$&(C%Zd6^+z98l_wcIx#k`*2! zCDIW*+I(|!#P`fGTtJ+SRsJAO`n*De35V}Z*U%sA;uWciB#k7E8I&y1F$2{)Xm-~h za}yuN3RLR|UaUx747V;;;p71qAzuv9^VKbTvEkaBLA3FMYav7%6POHwT{UnNWDu8- z=%e4YvLs#343x-HGBcQ3MY^1suCDSGYMq)H5-Cxo`am66)$35%bq(2BhVBrOCQ+r5 zz11lovW7nbq?3RVG#Z9)XEzu^7!@IyjWeu3C0WBFV=KDRN?4o0>Os07{H-nt(2#z$A-C_!HeA2{;oWI|CFwBcBTT zpU|*t{sDf$)cgfrX~3(=&v>QN2>4L$`ggL68O8e-*);)n&3?)*^ ztACnZ>tAQr640QDD@t`)Fr{Zn@B{%LmYf1O=xVAtlS>^l6@>^lBB zyEeeC?N8ZtGP1k=@3U&vG()XlXV=!~|NJS(&PI+QVhRrlQNp3~3#TaLRE9v|v{0tD}Al}3RXk`l@pwa8~d0_PcEc_q#-YzzhEK3i|nd#l_ zDQ)krq+QuEW`$c(Ia}m3Gnpjom+Y)r7OSe8JI$(cv8rcR#hTe*W{`|#W<*XzBw3v9 zYD1PEV+`v4}R&uC7utNW#^oYNI*xFhB3*>IE`IRM7U@iY=G&w~eG?7+*+G&`I!B|^ac6Q=?01HPlb^hg=Va+E)BHEn z!6=^&aqaQ9TCJCe(>ce{rOhw zUvIbmM}dJqu=o7h)~8QiJb3i!=Cg;L?Wdj1;Si7$vM+4>n||ZJ@$+ic|9Tq#mf!fo zxBee#_UmcuLPw!p8rSH2cnt|GGb+KkqmGy`TR(|GnS(&pM&M;5WYX z%m0)vbJNcSjbjH@BxOE=r*=R_5M|OokS6f=57<-uS7_D!gKxI}3_Jh8|5wGtc2%E^ zkN?3FJi}k?CjT_=DBp*>0FrO(=MkH+jsS_)``EVqVypH3Kl%lVD0%-^uD3`!4dHJ7 zRqUaD74l&CtF6}if9228u8rK)v~QyBVWL7;6f6-8r>x>C9bh<@u~>phS$>r;1O7YT zXtn+!_NE9^`9`bti(qa@>2I}Ke}p|G!dqaw!qU)cwf>WDwOZ@gFSc5(zw%zI_4j{` zzyG`MwOW7qpYZqJd#}~{eFzVPz`VyHF!=nn_gbybAz;wgd#%<)%KZL&tyW+9{p;_w zT2EnOM5xSrt=4m~mH+$iwOaqBl=%u9(@M(a-r>ej2ItguT)B+zlI_5MHmZ~vEc)M~x|OT)jEexvor?*pwc z=3vd9wVJ?o^&Xl?S}pUtSFU|<<=XlvA6WYrT)pO{S^qrW&+zrt#s^oVK=OLBTMXRq zN9ia*@$d;;2JXp&w7>%sRpZG)xg6n**xP;>r`4mpKr7zU?Stn>N%nA5;^7BZ)(`R` zi3dkwoil0HUwCP1)|!Jf8=BLjq(}^4{wmreU%qE|{9a^UEt+B8|H6)jjd!2LBU5j0 z`7o)}@JHjfb&}T;!Owyi20x2OoqG{_jTWiwwU;&Es4<|9jiM+@I2|UV_{^-9Jj^nw z1iz-}7ibg6UH~yTxEH`|*Vz zA~p1oW(PT%-?ROjzPWCHT%S{r!0$8A_>N*B$X|rWC$eGm^=os;6y=COki7AR5z*G- zMl9!;lln6va!^k{vam<5v?=h4GV&oS%R+EoX&OQDIvGr>M7runFJG=t;-XB-aO8N@~Dot_MMBSns?DcvP=yH9S8z&?Vh}~yPQ_*X}!)xH-Ys+ZZ#KR#^ zQk$xxLE@tIDleQN7W6&0%laHMK{d`O;b)d|HVl}f=IMZl z%nJx-VHBE7JD0N+mZd_Y=$pfpuNqpt+<@jZNGzZX8b~~rY`idtaHAi@sm9Ulg;aZu z@gdAq!!v`W!dE56#+!><6#=JWNwHrRg9bLTJ%Ekg(BfSa@LTzKGD@oCD#rK06$6Hx zhuPX1Ph*%_*3&~7jZq|1oAcNl%D3`46+DlaQJC1A!OSyelZQnC`M~`wS9v~KK1iy; zk(o|Lc|7c&C8e1bqy9phT}LnI@rz;vL9-nB%OHs!?gmqBK{wMfDf(gkGAYa?F3Z!r z7>2K&#${>BV$ea*M_sUmgD{2`XLiJb9-#Vwg$TPfW){F8djvA7ywG;lo#w-JNmxRj zx^8K|{EI)QO6)KCO6-cS#SZ_Azd>5;7ys+uAT1Uf0(f};X0rnP5$WCZEWy0L2*_1yEP zSUI<*WtES)8Ax;Qp{Sh3sE&;_z{q0IUSaIImvK>9z)c&4Um0CG+N-n<*wPQPq1V(} z*wo|cm|teov1#83Up`B!X@Nm}fY#Cjv+#0#lw_v;cshP%`liR4GK~cdMZM?^6%|}0 zC7_(7gJXtqp-mySAEN%Ek+ycL;?=7SQ||&$*ZW|2f+o=v8jp+W)fdh@}R^@4C<+7=fyLbo{y3BkN<`;&SxQJH8lGAp!h`!eul zJ8YK97t!Co$X+ zeP1#I3fmavhouyQ0%92;phH2R-?&+zaecUAX|kaZ@sw+GFv`nBS&%6wS|kzSm62g8 z3n+b2U7#36mA<4fp1vVj_(#8@vhX`93wtXaS=qL;w34V2UpD^d|Hto@SmS zgn@)&cbaA6uQfIZS$X;^*JKej=qk0l{tws?AUkuuQte@O@+=wWCyA+wX|hpUkj)nq z+~aSwZ3WTYL_J=Cdc1y3+J2T`ndBa%VUoY{dUMPgP>B^YI;yHkxxTzyLv&L&&P;WbmL|`_HWnfoW3*_}(v(N}Y1Wn54Tc1C@=&ybdFk7$aF8g^WVCbz z)9*ZtWVx=-5d%Fb@(E5-^wI2+2fUbwyeIuAS-Ej=Fo=gY_j|q7o440idc9kNIKHtK zudJ@btE*R$Y2UQ{-&#Wz z9@@{8q%zBA);gCSMvOar!q?hv!qC9Y%&Ek+4R1qc z)+tv>FvH8U!h)kpn_oQkqCQm&;G<;lw!Ak*2=V{Vd4r@+#QV0K*Cc)CiI~kXYj?uw zH3>u8gO0bf!}!uUO{=4PT6G4+=YCHWAOH$k8c>F9AECCN+rzOO?<>EPy z8=3nWNe-`u2#;%GmbQ&H*EUVWJY!1p++~c|uc9AayIPb3XhWajh~QrF{k`lObdw~j z`f#2p9AKP4mR!n#Hd}2QPMMd6x7C3LZI@Gattk}hUh@E8y;CgoEKhxR9L<5h`(7X? zkXNg3qOS2Ca20Ggty_xgaN`6s*KrT1j-j2YZ(7GrmCWT>WjETST5Pl@`6wNnZM27Z zR&|QxC)2b@I+LUrrwFsj%8m9YCXG8Ts${r<4QpqRPqS*H%`>&pPP3DElnyuAGCLdX zE`D{hr10BH)A z78|>Cq@eJWNyUS6uN0cjJ#)=b8EH@6Fb`U}2yAh2@dG|U z_FuoRrjNg%rjG~tX$E_M8EihiUYe)z;5a@^O5gbLA4mV3j2}*-AB;fa+&PMWtGO+%G&Z6F?&^d+`)Olq{vH&BrM7FgAw6~ zS8Rr9kqoN5I5Y4kV7Gaa4x#N}8!aMhXv#@ENKk2WI5e>tr-LHjeP9N0F|>f3rpIYl zyCrz=WO*mfrtxTfmye1)NXuh@VDbXX-gIJO^K5gcgXiWXjm`DtVOk!OhESbNl0LLU)Fo zp6P)d=Ms5mS!(0JPzF0ddSIp{Fmio5IV|E~(m~_bWjXrb%5s`jNpTPllI7F%Al0sE ziz(f+L8J`;wGHm1j~)=#fhIfSI9ndaS$vojZ&mx6E-hs|PCDZ3VE+Vf1Dx4bP1y<= z?Vw1WvrR%Oo0p!_KoT-iI*ML#@PT=dz_(}e(a;Qqt!ZZ2!xZj7X(|O7)ZSN-%n+0f znHa2sz%$vCEsrTgx4SEAOJ-Q4cwi3l0zD^%IZcaXl$0ged2aPS{^X~|PDp9ug2;9P zPfLD4L|rD6xWJ*nNUOrsM1rVmo*%&yl$HcYg!w#;Pm)zz!W_k=*-w(p4AYZzm<&P7 zX;$TCNR^RqZhE^>24qR$*MSOjqSfE%=J@Qx#?W zgS?oILxwv^lhZ1nq=W9{X!8B4zuH^9vARa~R*kDd_K>tn1{G{xY1aS3L~$_=OfC%{ zl(}`o-%ZVaqStqB{rI9nCK{)cazBaQc-v&B#VUy~V3y>3gHXdK#OtLCpFbQEAv_~a zv!uAFprs=olo91!A!QSew^`)}cBN?RsN0TDLEmkEuDt4GbusLP9_PcPPZU8eW%ghDK0I zEWfNO%jOP}?Mx@Wj`H{qf8le4ULryjx=9ds`M%ab=%xJuedOA^*4xv7f+oWTCztz=qK2@C;sHz+bpPSQcr0SzuOJ#stYj*&KOwFpyEBvK^$5}iHHhUL1{ z#<^|A20c;>*W=_=@DcIbA7f#P;B&TR*eb=vE^sMnu#1G2c{T~x5nG782i7p*TRtyg zmuSxbX04;psNTjvYRBrpT!?umE2cmmesD~E&cSvM;qdO!!dqc`n#Ci`Mr~tf%{EG- zf*?M~k5kuV{O&KjC3>u=3jBWB38M9)OIVJt(K;94z_-vxpFX0cf&6Q-vGd*?7vdy)+jVHGV6r#LmL?pl4pyAGlU(xW*Tg_Y@N;~j(yRO*H0C=2ITuCqE> z)$*x00fQ z>%Cb*NyVO!Z1d$b4V-#yb?)UzKtI`N`X zya9Ad&xufXr1~yRYMx^`>8n!kqEmc>=hml5v&|nrDn`KxhBy-i5jKe^(0UXEK0zBl zTi5|UvtY8n%;QWYu=*V7VO^5h?9?R6Y@Siiu;7O-$e?X#22JJFzF7&UQp3h}^Knug zVJg$ooECX@Xy6DkXZf`7XSbx-V|M;oL($IhbDU1HJ5U+p^YP>n4UQ~O9-L%q;w8be zNzIP5%`G4*CjeWp!R^xd8!k9ZOC(d`2uTQu+F0S3pwlc15$qBSL0Z~+OD4(UeMALx zJ>NNZLvIoRNyt$)JQ^qDizN|C+FL>9FS6kP$IeK2oq)Km{dqfGv|60Gz>mNTq-z$D z+AILZyfJh>s=w0fjY~6*U-wsfEHb_G3#4Kg(|$Il<%}AYRi+-U4F<7>#OC><>dvO< z9Wx7JhpW7bN1bsphK=-gsBU0etFM;AgBW zO8=o3YpY1uX$>Th7sU*GdF>-u#=?*WETe77JIan%gjA|c!rpg?U6yX=oMr|6!{F~u}`~) zE!G2Vy4Gd#x=hPTN!pFH?HI%hNv<#7=eIq~lkz$&616UXx|gP{k_aLr*nN1D%7L=Y z{Q#w~cnmPz$?&Qt29(Q#xeUf(OcUGS!@P^_9?84NPD;ec7bClD(u2j$V+nlfQh`6* zJ|yAXmbCJ;$du%yvd1ek(t=-FmlR11t`^uE=S<>GOhjo%Yp%-(ub;y+j$V9Bvxk_w z)|n7;lBfPr411IYws0DQG99@3JOa=;AVL%47m=E1pGLM$mIsM#m`(@~ReG6?#gYIJ zB$(6aGi27>x44 zF;NKR?|U`pWtHN{va_EMDb}nORyU%PEDFGEXj~m44a>Bqb7uwkg7yVI_|b;qXGWQpPvc>Nu=C2%3%5n}g+uyDj1P?qDhF)7R;Q zJ=iDlsK3_3VT4YCnd!G>iiCceHPO9n`}zrvl4OZVkk6O4$xGyb&hk^UeSK`Iv`B{C zZkK+Hk{E2*-OHX+N{!QWG&1`*HzAtGelp5Wmza1+-z>)Q$f?V^s}!ym;FuCW&13T6 z;ecp*I4xqhdXWO)dRq@9kP}r-H3u^ZMBVW0|Mlg&)?GV6T7UD4|JzIG?&32Pm-89@R`vW zvB%4)y{_0#yd57am$rxkN0IJuiC&jA_9CC--~ToBoc=k_bLy)|z613?`)~e;g1r8> z{y(Q6FQoT@BQ;3-iv-br1DZ72e+IYepZ~SXxmEpo(641=5;C|hhI=_KEpJh7#+7>e za<0@La;lSc^B~VEQ{~ga5i`*!&62CmH=!o`OL{@vFrFpx@XTuHaNh!4eh;HRc~bct zgE~|ZNkaBplpN<}1x_C(2WgfJO-k9jLCJC`5m8!d0M`~A+}sgk@d9V7a)~P}-UMbP zeN7piccGaT(Y(;buRlI#0!JK0p(7Fed1+nIEw1u)(R$^P_E)YEm>=u;%z;dj)U zxu%<*dL`xpjwcuhLf|JQbN@oyHT2{pDT;K66Z4jcX;-i~3lGk_qN69(5l+RhR5S}P z+$4u^f5WJwW^p67CW)W$!cto5t=MYjfqjviuuL`@CWAWFmTry1{5k}}+3?ztopU`8fG9KR8GS3Ds~7`-ht#P)LQ6 zagnTGaCJV=9RvG3KRg^cS`gQRDbL3H`KZLC`(#{txb(X$f0$NByHckndY?<+$W}he zizjr@Pm4JCA0&u9+{Om7c))i!y6}&5a&p6QdVX>goc;EzK8i;p95His<4M|0C+Wdi zH!lvU-UnB%UDNv?!Jk858-5(_!^IEP$`Q9HTOJ=!hsreC~35P$dWvC!6O&dYI)!QkvgA$csV3>4kom z1ZDU^M0-#M!V=7hzwziaJ}ad|?QaP?!;EBUKCPrf4$BuDnND-Fjm?=nVHcbrWJU8ZUdKCVNwn`FcLX@+fVO$c+hc+ZSF|GnErq=)lMHE7Yimo;oOLSU{@ zvc<_BXanhi$#UM8&Z2t!DPYSuo2ZJodZyERb^mMv1vql(*_oQ6+jg zbV@RY-r3FD4bJI_NYr_4#K4Hvop^nh*MIfwUM!1b!ZlrP3NBZ@gi#C zS(OlHp!(xHn-ob2%PN?Kj--V1J;5oGn#d`W;?uOOOcYNmtS9-j>K5t2lJ_$o4e^Lo zk86gG)mf_=tuk?TW(Jh3%x&Jq`xt(l-;K9yzP+5i?Yvd%upO_HpCrX7K8wt&C0KUf zsm-sVA2l|*x7Qsmcj;6RB`em0E20xLB?O)YSCTh8|Cqb@EG`g+yL5?x)@rgV&*lL=$vE|$8<{0`YAe(W{Giv8I^p;Am6j~&B!nCnxw9$91 zN0_n4vtdTjUN%flXdUjGwtOYMu3qLDC*G4{@(U#!rt3W7Vt3@sd2}%?J$vz(PNusE z>RI%Ikk3WFP`YAYeg?=#(hf!;#l*}2JO{6*>99I72{;KZ<5+nRgL{UMrhXwan#URLjcHyJH z{7=+e`>QkNTF|_2to^r6?tYGh<2Rq&p;Ir&zYb&VGyg>H2Z5anrrPic47LCFKfa`) zmbn&iK)47+*X^{H7f@!#M*IKCahE0MAhN-Q4_~L%lONdCN?c4l1`2(sMXp;L3pwGR zO|#$1_f2~jH!7mIZ<#tsUn8*)J)+nvc>o&V4%Kma&GO+v`uZ&-=U0)-oL=K9gi*{p zeBe6a5}bs`J|!tL127I`FuZjJ{#m-B9NS36pbIKfKWHg5mxF3PPWf;bjT86DTB|97$NpJUh%Lgkr4tjTQQu-+cfQvof{Mba(>^RF$DJxC* zmQqbbo70NYH2@sw5tDq~_(x)v%pqAY0o(o=M|Pk@UM-=*%G&x`Z@sr>R#&^dTU{LY zB1aZR3I)r$w?yU)k`&4gpAv)X&olZsDI~ch&B_hzjNIs9rhkwYo7qsKqDdoPCNeIk z6w)~eE338vxVdKGEevvs_qZ6y9-9jH~6~?-lgz?9X%W1@8m{ zy8uz%HM5&$Fk)KCkKGex|1lYExEQ)O9x3mnS^6NZ5?Hl%t9XplGz!!X5*gHAf?vdF zAztFrc)lG{t-ass>v64qnjexYNT7Z|$0UixaYLziuUzr%lKfus+=C8j-{-vgIH{ga z_qQDLj)`2mCZt!aVS#pwUZe*EZdcKW5&z``ss30iVPmd3gpRJ0k zsgL}@v?!8He2tErT_?P(2JWC>V8J}NYEMSP8H_e&cT)H~Z3EJFe9pVA=#Jd7ai?f0FnEv@d6H@^O*)Z10je zdQIHYn)7g>rS3E}FnK0}hNxJq`j^%Y9wwM$A1HbO%hKjyl2yBv z-x$hLCOf}^XE-FG3F7wv@Lp>!i{`h$wZuItzOB6RWRV-fc7`B}>a*Af(LHk zB8NV(VMpdUyORtdpNk8oOjgjMEYxX$UP483(EF091wOlEp;0ogrl-jhZ^7R_5 z6937EdF6;S``pgOnqW%~GiX+L&D6cn<~BO_A{msVFqB&gnh3J6R%a`m&7sgxfV~q!Q!rq7{0@_k+y`Y%9W}hc#5e-JD$U;o z8_Jxy+iM4~$*}!r{>VjiT$K}MNFpDUZ3-iwVXVt`>7)rxj^zhPV&v^e44=~@cT8|t zPnaYu4EfBSo3IteiySSO6*q+bg|G@zOeZI@#fla$V01l=N_JE_jp~aZ!j2ru;(SHX zq7_MUY)Y)QIJxO=GjT`7f(f8m=7?Ld0oONvZvC>FVr^gO))Su$n0};AlK=_|coZ8v z48{GguGPlCRBf(J^A#zuI!K?bA zSz)e~Qa{VUJTW9o$hm??eH!#z%B=pOR zvva1jn1g4Y2c|L)swQh$_}VvNd2d34m@|LPEfRtx)f?+Iaer9b<8U(Xpi^E90&|yp z4C4pxVQaQ>%StXyTwYdb4H?BQT{`899PPGqFA_!1N*k>!dNidSMO8W@*bZBa%z_>R z)@r;vCpUwjhR45MdT%_jV(YQXoZJi&Qw}VFy@rWW&zvF5I1{|VNCTEw<}`ssIzu{S zR;$}cPBlEpK+#o5J{OkL&5AeTnuHCef$loWrsIhBoV>^48MpQo6z2D0pBOv=s^b&JHcNNDHpuOO-*K()l^P5_78n4yDBWT0-%igR~f*#sx5J?E-ZX z-%wVS(FQWnto@g1xuNViEsGt{M^Y2x&L6bR8SYJyH)_k}P36vkYlgksocoxR7wQJEzHy`m?aNWVA5%P~Ptf{SgN=eJ zf&18P4g-I-dtCR3Tdh;W`mHT}#ODN{*znA#oh=(rXz9+z6S5QsZ`qY)8aCL1-nvFf z73LXY70rQN1e2sM7BrW~OVf5EHqj?}Nq6Z)?xYLH9|s9QYBwp02EEY65IgrGY^C52 z#sX?65sS@Hip(>9e>ez2Md#G+BAl7p>1rbvbWZvH2Xyn)1$}SfW`IHEb?w(}=sEf% zE`|_E>ve4SLxSEy3Jn^@EY%lI%5-&63kXW{8faQYFVzmMoL)n^s-m*Y$~=S$?B3!! zgHhaK-F3rsMKSY?!Atj3Uy)}ztC0@y%|hTE`l*zS%5cR-rC${qmobiQpV{?U6=%Xe9o1Epd#1p z{9Y9U0r||MW`TW11t7kv(we==0H7W~Ws8jM*bKYPY zv_pR^vnt*Nqj{tnsYe$HX z8zgYOwx~;yCMn1H5_x7`!Ij0jSe`K)w{G9L z%Z9$W(0`J=CfHT|RB+sV?(YV)1Au`97_b%)mMsQ3Z1Pe705dq9*6*1rKL>F)R`1@p zd+YY<-J4_=WebIl&O7mfz%epf93=xi1f%X9)q?42akryruc#g@ejYCfk<&gh>B)&W z1SjhEy6;JKTT*)2H>bxAaT=%T-jC1oegD`YA0M+xH~Icl8W9?BW;Z#hhuU38IGkps6(k%4Mhn|R94xF1c6L(4 zvy}~#ta_GEE@lPM?qZ)72`(P78-2@`w!~+2%ez-%)sehEC~rV(u7r?`9g!C>3gyR6 z0TwYGwU#m1Wh}HAXWxU%hE`6>HQW}!yC#w)w@rX@5C#hYhVJ>@@IO-B=C?N=h(&ZzRInA`C7Xen5$_W_{`2&6U`6 z+X4e}h|+A3ZKWRDUkESM}sw1T2eE<4NCCCqcV5bcp&-}xv*s077a*e6C|L8TgLC-Pi= zj0SvU$x=d_1%WLZ)=m1(AfO#Ehps&q23ZV(SwL$2ET=r8wH`@^DKVW#RWnpicaDBN!Usw(@Kd*)8hQ>_h^U@s&ix@T_RW=iC6EL*cay-aDoAxppx))YNf<5II?i6NT)+vNPguR*z8NBi)#C=xWjkU4~wJU%J&ga590Vd-{*7wYDld) zR1wC>+4nR+`gw7NXf{gBhD=;$GTk4g1GD{9vIGt#DOZ^kxZF=-jsY3sjctH1eIR>_ zGZ*Jg)Zj*l8#TP!bdX-KgIinjVLu|@Eh&2)FQan*EGau0FgyS@&@m?)>`#$JCoVfg zIR^2=!?=oF^e}xWe5ij@FA5xTT#LVSh1vF!+}MEnqb9IYj@kR$Sc!v zQJ=KfyLZ8jAT`#4IPMIIgOg#T3uvv7Mq;=od!|jGsLHR(lPeNPLvP2g>#qz5$^dIS z9M^7@K6yV^sxuw_Ovh3$ZlTgwcv1 z?U+@$%#7f-jnwrw8?_{0W2U(UXuN@DD*@?q+M8|2gGIGl>HQTefztYgi>r4~t4>e5 zl^0r3Cnkx|e`Op%142>Ir)NW`0a+*(6U{enVpIiRxSj1%B^a?V5P2q_tplT6YSr;yEB(^bx2!Vi1t?Qzr>EMO)YiqwO1gE#`K83qCjs<$H08@{xmHwU`@D7Xmp(B~DALT%LC)lUA8!YhcfFpS+ z@`9Fm`x1LA9uXbPV(JVi0X86@J|sBTLKEJDxOwlKuJx)of@Q9NjVkB+Tjw(JixOD{ z8d;g7kXmZog(u#zU}t`6Om;vG_-m+i=1E=@5q(;*ExQ^dw@I@q$%YA%ozq2g_5h^2 z6OF{6v|B$Q$+XS@XyT(F9m_$HB%Pz-uv0=cF|W)==FZxkJGXjwLLXA?{?g!X%t0~g z=*{*tGCJ}U+=F^ib-IR?_y2s?d$1u7NYNrEE#+Q#a}<{b$+R&Tb1;g_Bg4blpK|Im z>t~Eo+-t*^b|kzv%1<-OG3f2r%+|9{Ikos%StVmdwU10*;5_oW)QmpPeTqCxPm)X< zcP76eM8&TEAiuZ0sr2i(LHE-4=PGG``K3d*xoq|3+jB)Ps}awIcjr2u*L!oVSa4Xj zLCrI>TIiDsgO<`_=Ux;a zlOKz&?Su?u{`o8p-}z5Vs^QO)zhcy2^r#W_`hG*wHJfsS^O{zQP_px4>oKpqID_zm z`YhjXG)2pt*=n z!meSCg)Q&LB`z6Z%4{lNFS`$lc(6eCwHLW*6MjAK?8dX-)}APzxAH%_rxYrhBRhPnd-#3GWyJ z`?GKRz1Y%==)ykTK*NUOqa+?CMdu(*M#KKX7uc}PpwhGvmhr}O5G;tZKb&d62+Wkr zBueeDx*884#qGR%Ee^}7gPhNOS50luL>D@pVobjO3AJTII#rRkitB+hvuT@iFj$t1^6(esFk5+3W4pDUy1g9zS0y`)fwD zz5DEA&iww{E7(brk{D{n_+0&3G&TOP1XoQFk+#!xRHa#muBu^0J6befnTYPoqV1VQ zvwkkwNVC?)pLf#T8W4tPX@70Rha`t&s*qGdnq(>v6>^JogAzrph;;TfA-g7+e<^&9 z>u4jrugVegvgSu!j#!GoL)3qXogcCira#4%7=7d=H49$^a_kLEnRa?FX8Lp4o{%Pc zN#uOzP#-IHO29F*4Z^3*>p@M5@JDXyYn+5xIny}z;^_JrjC6W|l?WJj}8utMD2bg3cs_%~KD21sOi%iw9&C zFB9ykR2Qm?dJ1o&zCB4VXkh>^q4e3tt$md@COdJ3%)7HDXSG48vceji+s#0gnTLi2 z572&_yU2_1!eIN9u0`OVi*QEwJX@!_Noe#l^fhawiOl2Nj4`D8(tnUov!QBbO$^7F zY_Q=4gFJ68f`ZWO8EhIK!dMiOfw+`y-P{?R3B;<+-?{#t&`D!`1rVs@0kIYY?LP`U zG2-Y@Pa*P~ZF8*Z3Zy?Z0dRb;h!?s7AI-(SEC_SXmslT~74r?$$@7GGE|Tf4gdL>N zFB;+WoOVZwN4yuOmE>d0l3HNao-lFta-PR@e@1FLxGPKaHy@DZI1a z4%ISv$@EhTwQ6fcjrU4%2!zN+i2fxz(LJ46x`)!L_1TrN|@JYcA;8Ca%j z^;KA?9EpNj6j}fTu*JzI7<~c*1@o!M;x`J|8$*VhzLjYD8pokPeI%o7wuEwMH8iwi|=EmWW;@Bs9;D){LZe~Dy$^1p6%x{3V#hIm@$Nf zHva#fG)-ey7Z~@t%&*Y<)BH5tBXBD=gqLYnez&)+7GtH*^YLVbA_Cx*`kq6-TaG_zq=R(#lkQ8%!2;A0t( z08sL#K3@?h;7A<5d}TWI0ye7iI(-$hqV&we4|yEQu-*wlOdFWHx`xjYsKP< zenM1)tBKGMYFfD=MLQffe3@=~b1G|RB6q0eK=e3;rKWQ*Gc;hHSLOL?1974qii$uQ zi*{kD9@FO##$`P|nvwG)kALPfMHKlyyHv5sNsMyZHPO8c)?49adx;fUgV*rUT0E&z z&Emo~<0iJF%Osl}xIHE&!cEkP{M{ z!AqQPCrVW?T&OyC`FWA05}U>WMyg&3i#D!4wL??^PjhB@70T52j1EhB4=)0o0uMIV zoz34iD?KoFt$CV)D$mMfz{998papKb8?ItBnh5uwL8W`e!WbX^iSUn7<2~dQR(OR# z9eHyZ$x|mSo6HRKTxDH0GDmSyB1)wD^3`5@Ve!lFefa$^-t6sZa$--V{}bR%R*@J6 zVJumn$nOCLUx@bhX!p;QD5hP!o=2@j4?Mjpxf&?;p{9ASqZ}`u{9wNDA}hTgKV@l_ zF-lqD{rC0ws=D$-E41{AEU59Q#FikCcP_4(y0583cGaMlJp%>3Zz3{|c&lophd?6} zv6n_7iNr%ewaLw!yiRe&3%NBop2zoXU~qv-c6Fi6qyEZ7KU(+_g6K;chJ+X`Xsi;I zS|lAp4ub@XIU#}5?AzfBR%ftoPbAL4x}BA|Dr*g=@bzMtwXt0aw_g`#Ykj{nUfqsv zr+y=kMXX&%$LVpAGrjNU`N#zoy4(H+Bff5emMKQ~30<8066OwkW6jF8c|t;x-r`&R z?Jz$$@Y1$My`>vVHyubPE>9ft>dGYwgr!PK!_;l;}lg}P*MVB0|>8xOl zSylCzAfU1B+*?`oBXzxICSaq7ctoZ?Y7qH3r(6o0J&5Bu5bg9tgi9b6#JDUez z&zp{w`g9D3^XKt~SMg@O;wPW@6^XKX#;huM0@rvvQ)XVUNdX z*9(VH<`O?z(5ig?Vx)9RSXwnV5HYCFhC|3^|H&tx)SryUW-Y8$mgbhZZO_hWC}hJz z_>y|1_Cf6Exjh{6-%mc_zvD6gElX{YXLtRarP6bgTka4TBB2N_jMC>OGIe@(;DJiBfd7YNktnMqPle;!TL>Ihc0<{**yp4 z=vYYWRoA@3%Pd50{spAk71iK(o_}#?Y8Nya{>{h@(9KRv6PO2dhEk&c9~OnK{tX1o z6cCCZryGg~{OuX;vh)Q$VA4|A6wnqP)`?3C2Gpem|K!LEB7mt&`m*GOGXs{o=J;Ep zVOkzvM0#jgrqd2GBs-w%T#6$oFC;i^{jDeteK$fkOKnYrMv%>;GrBRaPG)4Lv|7WA zOrNh2V0hMFCjw9J;JYBS51xpf68FEkg9|||g6uGeb)`Fp8I-iN)t5|? z#rvZ)J9Jb9@k9fmR|%QrAMxo=d}t!^sZFN$j^vgfXlu5LB&Lu^G;Hz%LI;E6I&Sk? z77>)1*`&&X|A%YpWrUdIpKG z`Z~Qq$6UIt!k|$hJZHu(ki;Ne5()m&!Ad$H6N(7+Q~ScFKSEHQ`s#BfVw=WH3(Bm> zW)f7)IRd;uZg2iJNo^*u6?4zD)uR_JG2yJvc(-`X`(;hg1ZUL2QnrU&Rq;>=LCnsUjE(kdz9k;yYXvBgRK zj*>Dd{pqRoWy8aL4H2*b8) zk3q{A1SjX{N5rhGZV(o^KRhMe(NO&Iv>wk^pvSHTs$uz}zW>>iNy7;rO5RWED@t6Ba6@OU_zTDgEF5>x@ zvpEo;l7=>;$2lZRI+Xj8p6u$al%gbSr*=&dtk%^mrgOc)ih#$OBoIT+ysW)-8=HnJ zY-f@{gCJ*=+%{nuN-$Os|Mi@`&2z}s^ZcMP^&G>rKu+dG6CKFy@{x%+?<%{NeebjA zsZyUb)rGh+qa-dXgEWTeK{_znP&`Vjvqe)deoBXY3I?!f!;v`i6m2AOwX%!W7v7mS zAM?x=;r;Y8l%Eg1d*7T=CZwskTbLCZY&8%&bkEj8Bz&O#QBQp1K%07@jFE5b0SA9$ zfNUC+q(XAewR>hE`Yav|>7ItKZ3o~pS{)H{LsOW$DS?yeywsE`GhKL)LIu}pJyg`a0fjDEr_~N1+ zFQMg}>0lCc%Q_e>&{PmnN*!nXA+GZL2*0G)f%9UskTOvy^!oL95fB_r{- z@_oLb+iAA)Mkc`NOB&+j3{iH6q6SYd$GPVZp2?8TLWD=oU_4I;$4T`LkeoANR>Agf zAU94RbDty<>yzo>+u_B9#Hvh_7Q})OOg+s8cZ?L_$lig-a^Xh#V>yEgc;vuH*)AWY zMOjHN4v$?o$d zrXuYfFcz+1%C3#V(chB#W4sV+i}h8k)+xNE&_ZEa-< z^Oi66ln6@sP+*|zHtf+iWzD0yL)mFc3$)V=BTd{MQO-`(B^E=9gvGp+Xj>R&0E^p* zvC)7wVvlnLhoekHVqKG|ZrRl7HU2Bt?|^2&u56vC)QxDk<9^pIZPGhreX#2AJ;#SAz^ zQAB+~lDe15&h=^kXwIw$)2_TnKzZ0eA$g^BsR&jw76Jug8uvMN75*P`@ z0#0Bpdp-dXYlg z5OQG`s9Je=a}AQ8hRv)ZpG#h(hq(Ntw_(cZ{^!Y{>bF-m%$Tza_uH!*W|U^f{r1|1 zDbrDsRsHr2Lpv}wV`--kaoUEDd~6s#1tSMQ+Q^t(6u|+JSy)s_7fubvt8N!rfcmsa=5ns9VAT0-RF~ojl&Q)sKIZ{eCw%)nd_M2QJd+D5#dlA``{PJW0L_|kW zN9owP5qHnV`}yep*^4Y4YU`GDn3j)WqPRdb9ZxZ(~XG|C#Kn9C&^BdQ9kj3T-)1pXhFoisb{OcEL$)9>9Idv_k$Mi4>fHL|vM*GS*&km?bo0U9T{qhj097YJufS1SB}ij(3eKbhZ?DV^91|AlDe@4$t9Yb`L3~2*^lo%|E8hHV?%ls{ zaos@i^$K?$?A>vb(^O%l*IDy=xbx5#ZFNRzX#VbMq_OPY{%k=p)zR$yGh3gg;56)f zXfbjL?nryVmQK_rT8XeH^EMN*J(UO~-Ym*9=+L@40V>9I=iY)g)TWtR!~rsyTn}Wp z&>lY8yMG4^jrp!Gnve3rUyt3g*`PF(h#ZnW8Wzf6{SDr`Fdu#lJO@j<-!klN&RC^r zsOgWIWMft1Q^=xcL%=X@gKNhkjlW?^twnmN+QValqC>`K3IieQKcR!R6H?O)Xu|Cv z4SI(>G_S(MPF_Vo7R9zBobe#lU+C3x zXjjCEAI@#(wcocmGryPZ73S(*bZxKw-QT#ru=u?Xm)18v>fihRZ@%2yd-bE={o>0v zU+Fn|krdPY(tKf}@Afv~fc$Rn0sUF+@t+m`v&w(g_|FagbCdtv;y-p7qfhkR)jRzB zF8|@ySMT$$E&8*@57+qNnml9x)*f;ZyXZ-?HyE&+T<0d&xyf~Ia-CaTPwiz z51gQYItv6?Q}TiB^1f+%L+8usvz zAh$;A7ZY;gI$bBYg=5NZ=dq9LTTv;T6nN$4fNQI?83vjK2Oh1D0$m5hrc-L`ePq_g zO|-`S1)53J?$Pm>teO~}A=qHug&RwBBgZfu(m_O^ysHXY4av6m5bB1B#N-GG^fSH! z+4_K1oe$`OnrCt?n=@k(LlLr?XmFGclC0zuxX6z`!u`c``~sJd)UqninRV3L&#`NU z%0`u6-?=5R5FU2fC6?Ik|oi9yTop0mp#KU2clwdeo2+~O~R`HKG z3KY?BSw{1Pv^Q_=+>2n422paftwZ5eQM+(f_ppcu$pPdzY=11aLR>k4Pubf)nI0xM zE9bmbs)W$x?(+;nyrSZwOZm8X1a-Me8FDmJ-c_#*M{t5rbOj;%EP7*@WnGy`Fdw{q z*Dw-|iVGT}#S$v`_k4#j%Yn4OQ2uX}?J%+r% zN6{#EewYx}n>0z>AmrWV)0KM6xW1|~%Q|DW=cSSq)v#vC>AfNuP#SwtTIa|e4{kkb zY8Gt&?n-t)pDm(Aar-w+ob$XlINn}(U4gj2LU)PRxS@V9scQ^4wv(nI#0w8 z=S?sR&v|2F1%;L)U*t9dj4Q+Fc(Eo!PlgQ;+XeV2KQ+^dJ2SB@KZhR#1B7<3u;7yQq{Gmj}ZfdCQ-&8@SME>j-+D9%Q(Vl@`Hmi zsrt6D!O=83=CiA7tG936A?$cr+Lh3}iB45(tl0lr8C#G1DcWlPLbyckuY($`e-zqt{@+e^8I+kOp5D70tt^RJksiZ zktPSh^SUKQDMFUsc}fwtAxQvq1i#`moXbh6e0=!)AfISTnUSy0){j zibo^9iiHs0Q4Y-h znfWXp<|U4&;OYi)=sTvT>6N{jVn)O`3F7VH{RGBlzStnOoX%xtSXm3tykIioTF(+( zk;z3ohaKF1MOp$x7>OY=IMlemL1EJ|VG973_k*1Ld{9~t z+So#q#BR?D44fxvmJ@CU8`Uck5_Fjbz$yV2>4Vc^B!uvyu%SMp5V#S%oOh%YDomB< zqh-31=@Pe!Sryy7jgl?5ijm@)Ppiqa>QBq0XwreKz7m{nbvOyQ`e9LE8wRYB63ki( zktof$b$!rgm|Y)ACbJQVyr_Q_{V3!HoN@EGoavHVluTfJS*x~4nyK_?l`p^6c6NYF zob6fZ3~Mxj%850E2kp@#OLBa`_HlA5q#cN;qhvV6u}yOHIN0Q|AA*UDZzoAseTDS9 z`-Dcnd$tY1xwf}Yw^ukUI7qbJIx4g~!G;2*2oZ4^^f$>z>EJ9PHQaBvb5CFSN;y7r z$l@s)N?{X84-S%oY*IYkK~NVX*^cr$nW@sMG^GVsW55LbdJVUS#z*#Z@+UmoO|plP zi|Oy&i_*g^FUa;*H)(|<^EQRsr~)ByiNP$HxTJHHNm(V{jW_s6IBa0v5~y`vtn6SV zsX0_t zQndWCo|K}`OS3-6vlHCgR26YnB1EM>9^Sx0blfZ7EVc5HPj^y$!> zEkZ>(9bcOK=)xkTHXzcdCv=)KZ(Jeu*qQ6*S}?UKtIWD*v(9o&itcJg|9Ly0)>@r{W1 z%wYks_>m%ZNf|GipJ)HWhsOxj2pM?Ts=&#jIMuuO9&hw+%&ez&X>evZ{#e%1<{RWB zG_jiIbe^OP=+NcFoQ}t5UEJuE!=ohW4=9}Q`_^F1AE(+B>7>%b1_GHk6PF*9UA)d+ z^aKUX>yalozLu4X>@k#>NP**aXE8gMur`OV#a2g&GWj^prtxTW_IFh?=F4~o%20I{Vn<~D2c(zq_VfV~kWFy-b>$L)I4Lvq?)To^&)QE&#t z;?zTCzt)n~V!r%RM?mA6S8kTF8fSt904vC~9%)IE6@z=XcK^W<;(oj4VFrUaP7frd zfq1QQ1E|8uGR9MzJriM*-A6y`xJQd@nDb_gKVQS3*fnhue=JpQW=C-mhj^-61GD&v zq`)mbvvNbu|6L$%-53L5`)QsZmu8e6C!RUUZgn6`y_KIAeo30l8FCiJX^9AWQywMz zadv3>=6YMcUtcu3O9?ke7cw4A^^9e3&c;JkIVmqU1h(mBLys30qP^8Mwc5#UdL1E? zj(_V?$#0P)_$)toc=bdR9AISvlRw0!v(qtR8@Zcxs=QFpP*D`I{d{<~q-dv0OL0CA zX}FrvJAaj{-6bF&UlNC~S)&85UtfL+Zt~AfGjVvuuHu-Nkg1P8ERhna$HqjOrf5Av zuB}yl$KYU&C$ti*5@@oPROI!OnL#LUk%Oao{qH129uMr1!b&*fw z@F5FyG%)l7AGJWX2 zkUbvMZTo%6*iP&yJ$A6#hcEM8Z9QAy8+X4>^0H~pp-R-qEAl4F95AAAPaVY%X=J}0 zr#QRcmbl;vz(=_;bWHjjKG$wFhuPS*$y$-5WV^JK(xjt}`5u?+8JmjGEY+#xrYUgFE*VK)Evi62xJ{KD5 z_Qnfx7!RM2l*|{!G&2@E2>3`3H;}c$Jp{@&_?&?n)!Y!gh{v}XgJnZ702$KiNOb#j zi9ivz^*y!Vdci6FR>6Tbvp$D76|KR@$z(rl&XVF>C=hj2Ln{QSNk+SM4C)} z#l8u(h{2d31)7#IDLo_cgO*9^vq%Y-z-Fq($7c7ex8yIs)Y^GeK+r>ii*dHfLGPYT zSEj@5;NGUSlEQAhwdrc44f@lp!GdnK>})h*)H~s&F%KRC2#L^T-&NlCS{3H0qnNF2 zu1mj7`?Pb<#RqO{05N|8Co4WmAe#5gHX;_2vML&^(!RdqZ7^MBI)O+#b(%G1I7$qv zs&IS~rz70wGP`GK)Ozu;6YN1gL#z&nx#|f%b7;fWK4%C*fK4RhQ8WSR8F&NeUH5J= z*_=%HXvkO@ycvZ;@)p#v)A?-ZGoq~=ns&tb;-CwJv8!}3a^`aEoa zIGN@d78YM!k0kqYk({K-DJcSC{$xd=!)8cgp>P>t7`@Um}Z>f9o>l#J1{>^5=7jG({?whcw`T=6A&%ANZjVf4M(FNZTR(UZMBZ5 z`@=?K_S=dd$~Q+xU;FfxPE_FPE#3vqc86fgxjnH4tlgalHI|sU%~*gryT@C?q9LaO zjEwZ{fzfs>vKSgBRyG%@k;_i$99o@InkWpI(AkJoda`T^j@N9MJ&_FIW7|jrky1d1 zd6X9+1n*9qBoc%EXJ~YmWI_sUYGQ^^XUWo}l3%mGGMqOxA6%IgvWii+xIV*efF$>D z5UV&dU$Aud6h11VaTZKDy2VF1%uE!%^R$ql%u)Nw|?VB?we%a43Y^z zAx{woOlyoxEFGo(G>;KX+(pxvZvt~glyx=0bhmhC$QbX(K1%E#}w{;1p}N1LCITCMm0 z)_3oJ{*BfjzyEvhwOZp=>)YS@Nvrk#KR*6O>#)_j`g12Z3vjwE#{1yOCpg1~ z)~{Sq(q?k?Xd)ttSzlf*^0G_B)-9&X)p)WTZP;3z*WijOJFc6ZIExRH;y2SFNQqz( zypyE?SIS3d=!)hJ(8n8BuDmVnwr#f_g)Z0@VLNvvHi$c0f32n5_)E%-auKU=;y1vF z|HCi+ofMq-)gS)t6rA{1eNOyGgI0|be?gi^TCJ+^3a zEPqa6q1%0vtvsvZG)oF!I{d}E4AXb_{|>?Q*94|-HcN+Jt~CV-@lU@Yb1}%XYWtCi zPLk?~l12K@pPFc;2i~WD%SZxdwOUmC_=kQ0yu7wdD=kt$E~810ccdrDbkgVFPt9Rm zC8zNj|Ni)gVF&k%ba&G6AZu*O*XzAq=%&E7V)@)6sY~%x9;qiA~{UU zsyM^p6Bg)TPkp%39p;1Mr0AwOuBRTRMZb)zcm<@!Yf@`s*PuVEls1Tq<0P3R#eQD- zWcI&5`A>=T|D%7zWcKd~nZ4IcX20mS0~-6|Z+asd<1Q4&?IR{doxFU#GyJSvbE?5%p698J1umY&<0mZ83qYJdlKR`oGayPL8L z2Y6mr+=XD`vO|d{lg2jjoIs8zIGKGZ7+D=@hEd;9oATqb?ds%~<}Zgds}X#9VOzwn4dJ)`&&^2oaXwt$-05sTWy#>P z+<*6f{!hem|JSel&m`G@Us&!NKFj@btpR9&^=II}fBU^K{Pu$@*WCSuqJKg&^npPc z)&O){wQe3}ut}J_&`=qZPE%53W!&0=p1hoB;djl>n2vmC zzrMY=9*t%g{$KXq1U{~-IvBt5Ze`yfArP)d6KCXTMl++$9$QXiOR<$$OC&i?Vmb2D z%#-xunfE5|y^*Y}q@@r(TJ{2^Yz4wr*0K{)pn)b3Xqy6se@WUB3Wc)oQ26Zccg{Wc zzWd(1(c&bu-}n0uL7sW&wo8wr=yH6f8aaP>{XW`=Ojf_A&(lo`lqQZmo;rLsc&YGPk{Pgq+y z?~$J&>+JdM&yaP#Sg_7bpXZF}-1Cf33&1Oa>e(~ z8Gavsx!E|2@Uys#nNM{=Rp7yl|`{Tzp zX4oaC(r=cH_?b^Q$4nas4}3t*EG}nGsCRkPIsDKo8ddWu#~m|g2f|5uz?U&_-17YJ zD4*1%jWTJ`bzmHM;qeajT^}0i(BN?R_pyE}Jefv-t4{5Q! zzL>6_MAR}EKz#$??ptaLp(E?nx2*PimQ}!%j+Br~34s)hVSSu1jlp4_>uNBM%XybR z5BSh+d#O}%5N`M$#3O~C7E-3-1JxI))}vI%sW(E)VD2iMF+6+2f-vt{ah;<);D5^G zM`U&N_{cD*YbDb&UCSs#Hde?%)_b$(*br!3Gke95h3!Mt1a1!Y%cQG(c^5QGr_>JG=_gE9=Dox9f|6R|lL2 zdP(0r9@F5&9N0vIrMeJ-UFA6pV2hs%CQ+~FLhA15$b1cB&) zA1t+np&1kogHy^wfCZ!|D?w71WKg>Pq!Z_|FeLN?g>WTRU`gWIkB&z2WMPZR5Cfce zz&DYB3M!N1mlf&*{SaoPCO9_a|16W;AwGif%0=$wi7P_PMSK+Bpupu2^93UU4p7;S zZxuzJ62W=uSxCTi{9+jOBX%6k-@KI&=r768p_^S)6}L#TLx)#1;tZ#yTp}Cio;qY)C=^+&vu06Cxnt zXy6H+0@4>=!!4~e!8UHlD_qg>Bppp#60PDc)Sx$^H3HsI0F8WwvR`~i^g|)iNP#nk zk_*5fvOFi)J#-U3xFo4SVlf^)+`_lfx!Ppd=m+{1X_Uo!-F{_A(*z_Fjue1p>twS2 zs3LqqDj?J$kVWPhoMn;(OOu8`JBcY;^(c8NWl4ONB*bB70ZDEJ>nc_>!4p-(^Q?J{ zo2x^X1Z7;s3rXsUIhrWp)Ym#jj>03Q4`^r+p`}=?B_@ahRv!0~s8Q!ur{xe^Gtq!* zty(;1g%5Gpao_MwnSMX`&|3_Yk@sN8+pp3fnB}b)aegMa`djKX@*{^TF36ypH9{={ zna0|7@)KYY1ycvymZxN1wxur62tK&z;efc{C4B1qF~LXU0q_Y%B~HhL1~pc*@xB=1 zHA`bA**mo6bpSMvo5h+hvxD`f5_qT%azJN-6~`FE&^ae@^?{1rk{2681QY~TiJ10D zxEc-`iXtjcEK|Sa*e2eJ3^5D6DZG~fN0H5HN}2db9MBwXAP(eEps56;n+eiAhPXTV zYI!~@F9?dwxIujZrxM7$gd`-pOk8G4a&XR6%O$g%h@^vU-1Dt+8G_$&Q7xN8INz-6 z)a+75NH5LWhED`nBme@Rtv-LT6es~3lP$6EWoM>cZd^YvI>@kK`Ezi{7|Dsm?=ll0 zRo$_DI2aJVS!0QtG~zNyM|ygyl&U95t>RCaVii`c8mMe|VYFCC;tz#5$`{Y-h!`%8 zM?N3bp47QwxuPU!Vp>>&ittWmF}M-T8Xk-d@g$N$LVQ-%-mpV{xD3!mouY=9_FxDl zMiE8?N(jTE8@%GPB=^=?fym|HLBbFQZWUw3jQ)ivh4k3_>$j+&o zuJkg;JNS_{No^52TalwvD}@;~0?#d2$fBDxctp`C3U*IqNC4O&1Fq}1J9$!l7hgxN z0aH)~>tX5}ae@vaih52lv}U43#uHsqA5Wwo?UqLX@G#Ec7eqwSODGsK6i)Q?Ae<=F z00b5_dIup!uZ!eHg#aRXh$_OuZ` z!){Mx+JQG%PF+U1$U5KuD{W+)9TXj5N3P|x;J_JJF z!|)*z4mL1AcWj3!PnuvNVi{tma7jaG3q(C#S%5c z+dmI>w(ECXc@FUbK7q&=pLmu+`%#Eir8*oW6Ei+00;eR2FYJq#N?3rK7_uM>3rACw zTBQ3~5(1H_j)L{^Vy;a(&1Lwu!H-F##9C9*;t~Oei0=K@U(^$ytGU`#c-azaL`Z^a(x^Qh5jHf z5JelxNR{*poN*NDz+G}>1ZLRMvyc)kIuf{kO!N;yW&szay@P;L2BSc)97Kv*C2o0w zA?nfw!$w5Z0^MVV)6|Q^#BifkSW~bsgU+I^}WSwZc~v$L=+R8a(Kh z2W&{XD4vSlAK|qd>em%m@do{zK@w1TNx0fdzwou zq=My)fJLVn6aE~9KfA+Q4dRn%fmfHqpEUgGg+I5$pWEQi9q=auf3^g@leVVp2N<}B6T>N_ zcP9aax*n$NM`=8$%RCU6SdZfU6Sf66H3nt}d`SH|4zdCZ>xc-F^|()sz~!T2uX)aJ z^*CBx#gs8ExYE4@Xwjz>5UIq`qep4;3Y*70>!U}Hs>HXlB)-OV!7d2Ngc}5gD@h~5 z+0wgF2B5(~YDBoOBwEpuq*)L}I=f5nPB^YLYuOM<6R86b)k#9~k4Q(6=4A9%VD`k1 zOSZP6kp(jhuwUCCIEA@Vwlq~$4hPXFJZ*8rsnImV+ZD|s=!}}w6clBR$X9sPXmH^T z2G|V>_YK+k5`)kd639c|1!-bSxCHVI$%r56hN3oYpZQ|5CGl*b4fYjMmv4ID7-1D1VE!@WqovSlB`ZKiEJFLWY( zU=Y-+jqq{~zognIenIn85fxV~Ug5iW$ByppI%5l%8rb>AeX;MY${UP5i=f2tY%^#8zT+LC}-vU*Uubu9I6K%Z)DzxnNjAa`7XCeG&`^_~^-N zfxEEtD1nd=Y`5_yDJ~JtOxa%oNr6$}8N{J}_#VcOa1I)s!*LiSf89Oel05S=zP z6KbTDEQ5fkA&QuYuU?5i6(D#;%kvx;FBI1zqI3|5RM506e8KRz?>M~d*m&wTxSGkA zLxZHwK6hqjII;l+-4r2xg{LLG5mC1CZ9JFF@^(VN3VP2joq{-036aEV6m(KjkKabEA|9OGPN@{S^*d~kK{w{9LSH%;{8xN!yl+9V!P_5qq``ws@M5o|JjyA*RWz+C{?k_mp^Ic#8(&Yvj*f9cGi}u6G z#mFW(SG}xAYtT4oDZ5j}3?*|rB|-Kk^E=_P-ebMV4t3Nh*(suY0%j&V99U_3`Bea= zq4zN~yI`G#<4Cs@qHae0opM?tDuk5{^)BqtOeH2mdZkuuQaL^`f<06$h_bnKMKLAs zR0+&$ zk<2OMkU}hA0D+YQBVMeU&Ws#!0w5vX6fgj2e>cy1t*6?)kXAa#v|~qVRI%ec16U`e zl$J3SD_>{gm9WmJZAkbg@kwGArRUx)mOPR~ZZat<_KHANr~+ygwF!iP_#7-2Eh6wf zjIIk%1eprG#|$oba}m9l&T3g70`~Ox(pgRK1AG*%qNPrq_VNTerb5nGoR&D)luAz=Wk*!MQCxBJz$3!Bb9!6==&Dtkmeh}5B?erV1Ha@& z%q*S|Nrpf892x!iOv$PHhSGa5F?U)VY#*o?R$1rLV;g>sS=GM%eZi$)MqGNM&ZRGi zZ3ZGBhj@UG3S<_Ix(q1r-mO)1Kv<1+*U_v+u0s&()?gMW@m}ZJLYbJe1P)0&b>Z z$#`lA@-=!j*Mu{^e9oAM!(5O=xaOKAj_b-jk#9qUggQ>ymRS)?A87la6n#Giwt z9FX-2?CPrF8WqzwT@NeeGN1;h1$b&@(-lcf3v_dUWts}#e9nd8#2FKN)irH)d|4X!S zt-8F#5!CdcmCg)LcJj1JI-GK<5X)nfRow!pZIr}MgKvTFf?4c_1JX32t_)9h@iZT? zxwxlGo-(I#1)dVTA4lBUC0c=-xx$l;Eu6%WfW`GxlYBg!$SfDfOXWm z?aA3L?s!>PL1^hM#|X$R^oTDD6ppL0z3e8YX)$1+crSZ87{Fy z!+&*(tLi%QRtbhEu?~Ia9$$b%>eD6;8C$~-UX_GdS_D8uEBMj8AN6vXfc;j2*o38AM7=d0Atq(BMcsgwx|!do`1 z%7xkm#;x7}2*ZsB5-Dh18Nhn$LBYD_gI9+o^9l9~fNLHJ_bEkO&|{|zUC?`!#p?Q# zFB-7d0K$>E%9Qd!zg=$`>nPvEdmLcMUr3j_a68)%8oypEy?z4+@tJS#L0| zp15%Z1?z?uyoy>TPC=z z)Sojep;fA%E?!s=K#gYJ=&nro)VBdlt-cNYqD-hKH`xVKsc$7R61l%dLy=5V4@X+T zSY#`~!i8bG($UD(rO^=YS><44BML=MZ7c$>Xe5Fgt#Tk?%sjPG1n5j7QOswRg9#ea z=;Dct3AZwiz@9)rQKvK}Xs7{IicP1~sULCVRoEL6GI8gdqo6_Nic+28 z`GuFd&n^XUL5h;^N6Mwh3}b5O%7>aPP*q&nty=?t^m0lzJYp9kn=t_YxaxtN3@X41aFsa@up+~Bj0Q{vOk;uhZ8I%yAH zR^er%W{VSLU_(-F49n(S+%t=gUDA_B;|01LuFVZgL(LhWkp|ql3nk?)B(4(RhuY3t z9fqJS8aF|4cp8F;D6GdWFY-xkAtnQ2YjD^+%u^JSNoPH7MYaS7BUT)RLy6tekn1Te zZfZ?e4cE88MFSR~wjT;sdVIK_#%@w70U)N25>0|iB${mFt~pclz@!EST!bW7m~ue| zGm-U6j0HQ4BqnSjl9B|2I~@B=+jNccgrntkh(TuU?Tq6VO;JR9DjtlI&!+ChxEabe zA+T}poMGE0XfHt?idg4o3#TivJzh2Z;vCh7^J|bxJ7&BXEU`(BSaM7_ZCg5u6&UAKWa@;#+RjX!63<4rsU^8jPfhG-Zn+vAPQ?_HLab~UZ$~M6I8B7+a zn7?IvzELiZ*yY7h=wA*{;xZZY!gi1sNhk8%nQUjKBdd9Vi}HdwxW!mqP_VGDkkK~J z@PfH?$((P8nk^LALP5D6ue#=pbzF^Yd}x#>_8be?b3u59X-u$vj9a}jNI#K;8symW zd7LW0=Gb@&_;<%93-8ZbVhYsj~ohINU12#?LolFeD z?aaP)EJzh&)O@F6_*PL8*;c}*$poLzB*XASP0lPu`_cOVsZ-!b(aHy#B~X|>t6&MMQaGw2bhfZXl!~MGyD(ND$^#!CCPVVBi?nUi&ucclsFN2{c0NM$mGHeAm>mt96x}F6LQ{kb1Ldi zgvZRAZk{Ja6k-d$K|FrtMRf6e9qGic~k@FmTZyf~rGU zOO8MlnKvO!E6}Atzb!d_jvp@IO-4y5kjNyIx&=l=5=->*bWW)LQZsMv86dL!}|$#PO}1rkq0_?op6O z*FZNEW#u@qK{4L^5jihXf>KE_HOS?Qb_-S&f#swK?R6?u*o*^}af#J)X85$@&&lPJ z=zd1CFeZ}qiwKa&fmPAyWu8)o$iadl1LK#!rIbTozPO$nE>bNXt5rP||134Y%4Mo4A$9UndUO%zq`Ksn1=TJC6;X`}w(Bc3&xg%CgM&VasiufBQH4`YOEaym@kwn| zt7$$nX%C{E8fn3I_+_-=NH)@C!AO92Y5nm;xFi(B5eq;hqgW&?P!Prn(FANDVfOf< z#}iuP8SzTA86pxW4G~$^Y`B8!6=O;R9eVsWYMFZ4sr?j9fq4w$YH8u4raR-f6)w(o z&=7IV0$ae~M9mRsG2ZJU90g*rP#py$P)ta%EXkkROtgN$+7*k zX&2qas^7b4%Io?9bQACn-FzG9-Ajz_i4RTAtyjT`HY%cnBUA`8gAm|pqo))=hSpG! zJOHN!R2aOFTLc55fKUsDLqPthM{&XH7(S#H%M3M$L9}6+v7%*b;p2F4Do{3ixaD)( zG)r)%6e!<3UM*Wi%ZIQQOlk$!=fa^=8+=G|9O1rc@B$q%^{L!w0}aQifpG%+7^JZt zfpFp@d>8KtT&OsEdFMc)T}`)AMNQCjJyYcT*OpAucNnF>K|^;J)V1TyISc7v?8`I@ z(+aoU7+uwXkq^jh$U#?dylX{E6rK8&jSm7PAlOw(tAKk+1g@JT z7+co8eo7VK2uCAyQCvK+|5eXbs4 z;og+zwV@!7LI0H~a^k)Xvy>sDphQse+8W|a8_g|@O=`Tz?8#)(7#3LmXnY7C9(buB zjvUN@(Qw3}Ff4qBr?3PBM7pNuk;MSniEJ8vH5} zr^zIELLgmEs!$;-B;35RLTKIU9L!2nL%7I98RR-3wG3bFaY~EA0tr{?(_oV9M8-+g zqz$t5V6}w4$Dra<>U-3&RE zhC8W-y$C+mdCRGJ+iVwSCA+3Bk5%mU&=G^oT4ne-_# zB)A(gJO#Z90a-VR5RL-6#|Xa5av|a`BXaX@Mt~hF>cj_($wkx6bBva#GYlv5P=26$K}_paQj`1z8r# zQXCPYP05i-&O%Fx`06{o5k!?7t2c=)NYVwiMURkVfpO7qja0=*FjNE%h4t$Ca)ULM z;mLIS5bCaYrB#OFxo|fCjNWj&douK#z&o`@}2u;AL63~r^PAuE< z#r@VLK4-dMRJf3h0j*|3UJ?KV1Qv(b0t|MArfZ`A>kN0oaA!>)cZpy*wM`a|yyZGJp4y=d7LA(F*(@AG53DxY*n|;8{7gs& z+W25=1)$!-Z@Nj_J-q2A+&x4^b5Oujx<`etTn2Rt1|@lMco1Rz2mA;f0ih#e3yw{8 zo6@ZL(aA{Nqu~DpbImjTHgK%sg-DhS+w17H6>%jQxfM*i%rcYC5|RNi?mD>qJUAdx zGG{E?Eb+1dN<3Qv4k9yj=c;~q2cnnEOIAP3Fil*#3hq@8lfbfrs3@Gsz{ zf-f{00NFt}0>TR7B%($yPh>OrKaJhh_uDKWieWeULG{YV=(-xv5}$WW}@}+Bu}SBQV1kS3sJO?NpaydAkc+jm=LnO zQ6UwCvve`RzA?6L>4J`j>2?;E#1IPp2)0d*PR0#zg&YKsDQ0@7h&=KnyNn2l0eC1t z1VJqVmIyTUuT=51x&;BPK^{>_;o`6 z$MYQ+u4sY%hoYT=W5Q802mxyb8Jie^S_#V&i$T0eIQwfsOcREe>Vah-b>dJ13W>HU zS`^jY2C+JII|+j7LeNse;WTxCBsKTuA{-b@567 zB@(b1jv*=y!|Cu;Z> zDb2a71^~@WW2q@Y2nhMQhWfylMC~+g2t7`EJE&9(4FFsx96W_EZ#7i#D3fz#6eBF9 ziPw)H=V^XarF_PiIrJdX@SrldVS-dGd^O>ZJT-5+8EGd_j9n|Aw3B+ksHWEn2T9qP zg*a09Brqy2paoSRNY7HCa7;|l0*P45j!|k?1wD*5jm$^P9daUY;U7U=Q_fa<3jt07 z1lq$4PZ03HUFSmNr(+kyvVx$9snV#ONjn|)ImNaB@e4r)?^A3AqZiYwR*ELW@s_99ZeWH87F*!+Fx-Zm=oGuzQ?tq?bg7}AR_q*x znb|ZH!J|}1PO%d)muUbxzv>WI*Ay0bQVT&n#l{KUfogJA6TbkV+~(9X?5& zzSXhKG;HhgBO-zJ>Z#;l6@*Je@YV3O09{K99~@P-Ps#L6yqZ!v4R|toAOMdhL2zIo z&gFj7KGPqqO%Kw=B|-p>)~3r=aj>dPZ=RIJ8|@3@EQKFFYS-5_ zK~slqjA%Q+aqr+29-mtka;t4%M@c-0AtGqP*+Ys>%taFx^@;L&9Q?-MWRB|?l@-VtN&#A|9q0IOjG;n(DJ1XPoVLGmFm8)B^Y729#j z;jm(3PuuELn}kF)<0740)W$+Ty}(LRXJwE^5UtlY0bET-YF{f2@L~@I~+*~!;hj&h))8B zXd~Cr183EM-6jj)08kAS=@Lnf|8NW$hjs6B=d z6X6}@DZ@@k6w4uo(4q}pB?47L1*kv8YzqA7d}QJ}4pG+r2C^%(j#RYKN;=%}BoB^R zNv(EBWO2CFQrA2}vyq#KPi(rxK%%j$3izSjF}hO%V-?_tq!fTrbj$EjAv{Qfl3^%E zLo6y5*2__h2CEnqKqLXKp_Ua46F}&A5_rLswYDN~@1rB3+$kchlqlO6iUldsgaTJL z_eq#Yt^HDf7N=Hq_tuX>&;k>CMHiU(liSTW-yNy{{Y|{tb7w&eq<}JAC zJ6KO48T7~iB^AjXE%eJEpqmII0xzZ3Jn8=Fcx^N$&>f8h8eQFS(Th@%GqzQ!i2t7oCY4^PEp3J z%UUrVxVM(D0W!`$<@g>;VKJ}E8fwF8_Hso z!4#%7k9jcmAmRe%0P-JlCyK-FG2ZvUepJDDQGk*X5n~C3*8xb0qI4834vMXzC@oUP zMuUt|MTGikZNk;13A}yr|p#hVgFJMf~f*A#q^asEa6Ee_f1vey461P(IfFSw` zy!rZwb$369p0q9FjekDZf9S%#+F8Fu5yev;9=8J-l&J_2vF*Z`2opS+kxXNz}|G;fH;9sapgq{(>1gM4z%O}f@>=h;yjHY z+XwN|Mwz=N9_+#cSR#RhS<-g}R610Qo^#-qs(Q=?CM9EvRW#A+0Wvs)Y3EB0GIt7I zglS`P9*XvXky5OY4YxDqvgBxk6v{|*lo`hWmD{VCk(?QePR*4Fr1EFX)Lf{mPdorE zsVOS1C^RwM=P0Ck=v1vvk8)pJdc)*zaDhoHIkb!s-w!!?=!8x~l9Ik4vSI{p@I*5I zY16bdEGjR`)`Ytcj}pjJM&JtsTviHOG(<|lA(mOiSfzq8;%6lSE0>*=rUe9{m?Vg3 zrjhj~UZIv+S60>3NZkmZ0@mRT2`og18JWI>xV9pYqG1SoMIqp zCC85uPW2o=5Pd=|8bu}xqR)3Uhp&om4tpehvzjCLbKDq|+#5GmiAif)jaUK0*rn?c z!gtkC9mD|Cd|_#2TYp<_wmnTI$o^$sr2@SG8j+Jy0UMyx}q1=JQN?chRh1!7T*5c3IiL@m^HB;{AI z?hVEZV-IgAPuf%JL)VkT+CV|I`7V8%E9xgPX&>IUZ5y8$7@y$d6MbV7eA~8dM<(q_ z*Z{MNxce=(nZ%wLJrXwy>y-zF`>Cq(uG0ArWNPK!n2ZHb2JsS2Lp z@*)g08S#X8D0rIm!yy_oR3?$s8&IIvOX{$RLDQ$BpCI8CM~tv1%FqBSs?BDJQ>}tK z?Jb*z4a})lB>>}j7|L}6!_!W(YCKCE5T&^8Xs?n@3HNlK&e8bBgp&e#Sj7;#wV>nx zK5>yvC}2F6x3}!nO8XtpH%r3B6EjOJADI*24T(jJZ!SVRd=4Apu2b_(S9t75@j`nB z1sP6Hg+GhE`WefuEI?eFOq@^I-PEjb2*6p?R3MKEK9;(bl2S^ChwP*fBA7}9+Yo94 zLzZ$SitM>&^x$ZJdU)XaiS(Y4k%{y~-}p5IXqtr{dq76%>SBf~ht zdnnS&AXg}kVYO&pR_T-oJ1`+NW5t;lwt{bX#}M)5Q59mcu-mOp-035d6Iy0@B@U6! zEM!_J87fbPi5otWZRpj*!w9yhmq3D)A%z)y;-G|`0oo(~lFE^YU?Ztoo4(mB%BZ#s zPYjHWjg0Xz0Xkw&5@>u!YfcAn`XXQZP%RoU6L7%eLpFV4{+#0PqC-A9eHe_1D%$?MOq8EhO0xSFdS7c5w!AjoKf)lJwPUynPzVYR|{^D5OFjj z6`q!Mn7{*#mBzP+>X>dng4z>o!#r6F9!#rV#YEjra;=qWmoXqgTGF12sRcbR89q)j zm+qLRz@mJCr$!ymPYd0ol^+bb&-RS;jrA)*kHJJ^H9N=yyKD$il!OrYn9DUbkSHJQ zZN%v=c9Ldk+1%h22TraK1A%phO$1;y(oYcb z|Iwx%deq*b^B?KU7VXIWWcJfJ) zKY;3Wg>*J~g%~mESRHIQ#Y%}or6M>F;`=%ZbTpd0vMG_DfzVsj5jhz- z8b6pox#dZtMaZj+t_DG1xI(?5kVs`n(7uaifdjfOV#NC8>t$R<6j#Hao6 z@!`->;0O*7)u7UdB46sTV!&~Sf)8W*Q%uQ}Xspf!c~MC`HZH0(zp%#hgavd&F0V%2 zlPUk=bv$u5xE6t@!A}wS3L!|kmjYKK#Zc&x^$!1fBtMVnh%w za`HRY6+G@#OyvYva2JkvNWG)yHD|3-yf)UFsh1?%<|Exe0 zhtRi3mvBl~#C63*PLf3yat^9#2YyKPNH~h+?i{6OD)Ds4aSwSSVjD&PX|3Y#3{NOk z1-uU{7Q(CSWYUg{&M1H`5dm3SXbjTVf+dCFp=l$l9z;@)k(;!I1?|a=U{X~|GG8S4 zcxdM>NW>)ytGyyjHZT3Yftxn;Sh<7i$E*1km)lL#kDF06bK{M*a5MypG2M|G*nN~b zM|bP}_u_TLx(O3UBOKPma+o#I^n6Hy3H&E+o>+yVj+(McP_ zNtU*jLuj%>>_KL^Qe!HHlLBu=I3n6=2q@Sp%2bh(_J5-D8wV-nML0HDB-tTNQh54t z4`0uTBSOFc6G)QH86G84lNKIv4sMQ-@EefH z=yvj?`YyhXTm#9vZ+51|*(;tLa3L}MW_Dsr8}WVM(zKg}$6YWLf>jhrBz%Vt3>jAW zJ!^;Z&txax*u>6bVoU`H5~ov7qnp?X<}=2QF}CUPH~q{iGq&lUAO4xu#7=B_#X80+ zjBRecg|SUPKGwu$8QZcRZvvTNY|H6-J0@d%*IGHR=qW8Yd`nZ_J7+*bf72Z^t7W4I z)}VYf;1!Lkd6nainX>~R+U1LbZh62iIwdIlq#Xr=f$$Vu5h6|?DnESm+}2@rL<}Mo zwA+C&J8txNR^V!kwsVB1!Mev3dVmiC69g}YK(sq3my~#dF$9peTjWSX<`-=-Ro4e( zUozZQ$T;01b>dSPW{!s0E{6%t0VSv?fnhLudU<=hoq$wy02Of2MMrp;C!-5q4>2qP zNh}e*!im|aHH8ZG&0uZP1qZ7KOnb?tz>Ih;@!d24S(YcUT@kPnhKHEAb_j5Z6MVdl zL!R$mY7Ue?qHK1~R!OZFNNVjHN?)}A=M@iFp6}`O`mPWE7U{LO@N=ZsKP7ryr_<}1 z;rc+hkw-wfM=x#J;(A56l;k?2B>i_p4r^z1?-ZjBKI3dF^EhBd~w2x05&{wjSAtZzeH9t zC6NOR(!`FzIf=mCBVNEI1L6hvlOTbtyU1@Nb0kt1>{Q?Y{Px>9y;IfYyVk&+4iZE< zI#UVJ7NrmZfVVrYy6ixt#KNsxa6RxlcEY`mX4zQm?aF2=aFyEe-mXw~P_d?P8a*U{ zD2IJ`_M)tJ&=q@T1VTjAe#^x+Qi(9AOYIQeqOQ@UThaYRG#yFSWq6{U6lA?g$SaT{ z_yj#e7>VAaFnRQPgLcV04jBha=J8C~^M$oVB@|`BIp~p^&vS5HnGy!5x$QMwxTD4> zdqM&Qra!!~Q04{)Hbr$Q;VlTR8-YuOf?8LZesOMpZPtWb(xhxr`TezuVFTVk3YOuT zI14LZ4$e%>nf8DfM$p^=XLi)JY>}c+mK`?zA;UXHS#ha#de|KrjR55Vr+5q>Ym>jq zvP=DD*{bL+6hS7Ki40h~#}nCHzN53NyJ!23OrgDR&))ultM=_5y!x60L&GDZ*N%-( z9K7z(^*7vTOczV$%PH*aVJ_HsDw#ubInm0KgLo64xPZg1Rq78k$vn@42nzV1f*H6| zH0bOz{XMQ>7w5E*NkE5|J-FXej_EKj6dilsbbZKMXnT-|qPJ4&f+^3L$5pvxj%PuJ z%I4sOm}m}2gdc$icxMCvNc=C$@?dhp-7;8F2KFi7tgJ_>^@S%8{|P2>=g5jSL521D-ZYMR3q9OHUAXC&GtP zspKHv#*>4sP{~s+i8`cjG$DN&Pr?)$j2Q7y!IJj+q~8}3_!z}GL_2QTz%WXthqt*| zx0*aygA(vD%QxL#P`(9=DS7kUsX~CO2j4T*vSHbZ)1a3fI|sz%Da6n8?yQ8QCSqWz zh-1)G!8QSf6c5Gv3P}WBiel=*AFaDYLf)&o9D0lzE?xv$j}zRqeRq*?04AY^uDi=| zc6FX$>QxvcjZm_>`mSS-G6JW8VIay&CMgD%g`6KCf`rx$AL46EW85mBq6jU`M~=xI z8l?fixZ?Q})Kd@@VsWqS_mxVN&sjCG*Y?Lu@Hfc_q@XkfyhL6Mj?Q;diW1Y~soU;| zbTc?Q-@Vj(;21SXYfCB%61^B&9?ooz=Z9`-#i*Ka@N|1wJ`%MSBoR2x?64o>w5&t>zSBIbF|l8d#} z5W%H{^bv>>8=U=hq6GU~y%?dz7c9<(JaY#u+tlQUViQEsO7)anS~zf^7F@O@97wk% z0Mj*A2`x)y5Q~DlN&0c_sMmDs;{ITOGy*}Op6_Clx;zD)qW5Ocv4K0=CO?wiohVu* zH_@u`=WxuKWY=+wpO}K|7A6pyY*c%k@};gVi%$at#JRwrk}$#}9FQKqqH)WU*!2-mU=Qv%G7E|=dPo|M4H-zU;_*CWtVk9GAc zFIPa}U3VPfb?bGS*JYs zKyzK`;EC#xik`sK^_UyEw79}`B7OkuA|d13t!le;O}FbNR6BB7ag=>BV0xer_6DjT zxxU2{>Gz2Essu4f%sRY8mk>Ua6oaFoXsjwE!j|PtR9dR_E8tKHcctDTMTl$zg$Sf_ ztdnp{n__7DfY$<;q?#CVWC@f|usD1Y5N^>(_ZBC-umL$XExDrjG+tLgYyvV&#;N(e zvKa*e9V3^<=V%pW>!p(FtGa&)K2e=m>L5~4Z(uzwr#zz}xk?}dm@yu!0iXk0csZEL z<7$Yz8b}C^2njqCLr}r*A+TuDkhpTaW*#Qn`R@M)!fn{;Q%t%`vqc5*r;>ELe~+Zw z5X`G3-W9@4$5U|B{5KNsO2K>z#M=<6zaR1yst|b1+DI$1?B1~EtU2zNV|e=wyHo~- zH;tPx_9SiEKxl{y(t4>vsNdy!?kk}WepS-UcN#I-s5FCt)k(AZZD{#v zEa%|F!b-FiO}P;tQayT=bPkD0r>2X^fN;R?iDUy`qO3gv-{l}ozBu23v8)ErKom=W znvi6`bpz~JiQoyaDuTHIdkM{OX?^H_0_Lui%Pa}bh#t0j%x%b!{$Ik~m4f<7#@!%r zF}j}!OhMZeqU{fj4J3F<1ZmVeU^NO?`fV%EWwTk~hi!$5p{CMlAY#a~*r-it2$+Uy zC0w7oAd;7BKy0uyZUQvUWyU~zF`^B@AmFH=qHMLfzq+W+AiOItw{TY;A2qW<285j{ z1Hz1j*|cfT>ycouxZ%p1A!75D6amKc2(Tbxb0bxSh_4MB+UbTo(}lxBUO;nDs_Ht& z7oi(|%v|j4JG5`=z~K1Az_7SQuQyTX>dJO?C7M^+$k@Qp$i#qbB$wTh$z^*oxt^X( z&yG`U^6I^^xgDz>&ecPMYDT&2>Zfz{P~Y{c_k4EM{g3BV?Ng|nmz8rJ-K+1qL)PwG z{k+FJW#xR&s(aqACf(VS@9gO5>D->iZ`->&PN6jgg`RBB_U!hK?YT65+mY{DJxup2 zsN}k{JF-37ySnme@pVV%j@1KdzXDVaqNMUWI=6SG>1$7CZq=|GRIuAM&9ld|*=#PG z&2{E;xxCTYncuz3<+=QBe78ND&u6pQ?cE*SGd-O>T|N13GhgcH+@8&E&t|ioP_}dT z>PMj9z02ht`Q5wxQ)oj$d6!$_xo#Lr{#3@Ipu5Z8y~{1~eD)Lu(GwC)bKW3u5HqiD7-Sgtu+Gu#sTBeVmc3$tm(){pQiUW1Z8W-cCq z0|jiZNlKXubt$6+1jNef^5(G{ue=#f3ViChLhihZ;Qca zZwvPf-^jK5m1?^hZrZ4nx;rzz;bvxU71>;1uD$c7KNn1joAx|fJ3&2=UX!@l0{MOL z9ISfDUB~g$CCjBAgBN9I7OoKJT{J!J%~jH#b*tIiIYh^u9XkzY#Nn+j`$W05`!=jJLdB0QePO7XW@Y0eF2pfSZj4*$T+^ zhc*NshB~(@#w_JL_KbOx&sh~PlWN{H!aC86m%g{pKwK)Xx#iwOqrmB7rVFs5 zA*YLTE-Xxfmpr(8!Ji8+AQgim&COy=xs1HW1=cTE&O~#7#^M#=1TLCfXl0n@vY8#3 z9hrQ4zLU=yzPVs5%4a#*PhRyi?OLT-6GPFVNoXz9nS3w1n|1-K^x;qm5z$^M(Qhgk zOhFA)j-eyN`#jH@wc%UZay$gT&I}*!n<(&#;oVX* zG0&@_EC?o@nX!tZ2f3@kZNtZIigSi#_j+y-9(ZmMBj8QXll^9)Ujwge=?$8f9d~8; zsN?z&XR&~ngB|Q2<(-`!{0eOVslJMB-f9_#_VH^E4({a@t6~-1#nvT9R087kA;W|3 z6rFN!tyC5G^gv?*9D{2#-!r%swm2h?{exg{p9{Ko%LFb86)h_vW010#x^FUN4<>O*A*R zgU12OOA+W)t)h2lLIF4t+fd)3ePS$d;?|zE?Bj4p7o<*1;BiOxOeHm+p-D|LQD>d> z-m+!dzOJ8Kwr(%#C!b62MZ5StBtB1z7oX=TIUO_ya`d$ui0K|Kgg=y%GyJe{BtgaH zzu?qxjS4SWkb9<5o$(UQE$#RVa0~WVd-1V&nXVYeg}RZ;W{0>}cKlw~s7Ropyo3W) zw&}yQ?1e-#4`gP0RE!BkLZOu|LVlx)Q!;zKImh*(UKMmM;Qpw4DG9VApUHJ^&*ZY% zs2CFMHHZ2XLn@VG>vx32v)-@}>mi^QaScO_iwU(yr7q1YVD544&_ck_#NF!DK?Y*i z63v)n6DN)ehEO^yPR;hcKod;QI-sG>SfR=qgHSa}B@m~pY6b*cZ@W>gQWrQd#juMr zvd`w`mX+ypA;npEZ?c(eCYR1;J32!uG~l4`ILoVhhLGh8v+Y)87T~gUvnaD$Pzg+s z0>5+xxk^AL8jpxG3lgZRmOMXZ7~R3_AZoxjg?SKz79r*w@DG7|4J`usK4cg`lG6|e z28s;K3F6`cy#g};*o){b!4cg+LVGY&eZ|FsBhS|1(xL%I@a~u?>M*T&ri)AhzBDBB zJfC%)TD5n0OXA2-|Q-rwitEx@I9`f^@fZ;!`T1 z5nV{=qon846>&Kw$*^2_T)3U9-zapP9Lrw5gT>V29h|}%Nn%Y zGwY>A`E*S-<`-tuHJcJy=z{TO$$k`w@6#Q{|IoznX#d6`Jgw!z8R4_R$kST^dHBI~ zc-#tY%Y(9cSbL@QgZhAF)b7JF1wjyDrWv^ht=rHtxQfQ&*KFG?V*o0KFLS}hKpes) z_Di)eZ z!;h=!)O^h2z|RY$T#B?`>@{DSIK)eOkI zYMQPW*N7^-+QBbXF=hZU(--59fQ--E&2~b*d==lM_ z#n4ZfDFhsWkHSupx*}yuL-w#tl}Ng~MgWSkts^33$_DZLt4wmsixsz&N={ndMV~I} z9P`ief@8j4=a}`$P|lBQ2fUm&Q6MI|;CQ@WP1Ti(7U?GF_)Pa?^4WAhnuX*9P3%;u zQBVkMV2pXDTbXr_GV!^d{%vB6T}|KDFviB{`#Q$h5PgT*Q}lf!W9%S(->f}vh92no zgKHSOo(<(UF!pxFHnJ0}nVo1_$Jl1J3H~%Qw)y(WqK#%=F!hj(H^@ z(Xp!;y1B1O8~qtB++pCEf}UU=;U}8_t-U$e2=Ojt49VrsfMys&a(NSd-?^5tKWiGw zp9?s*fw4`1>99;|HZ!)l2~n|`u`M0r`}^`;-G#-u?t|0U?6`Vx_?G@1hxQvgt~!vt zdPmQ`1O1hm19^Y$_?WrlShi!RI^Ng24zw-TGhh&y_avLk<<_c9OHGXBU@Y0E!&o-L zXkE`>jIC{g;Wjm~zZiRMGV$Yy!j_D0JnQ~PzI^ZTXQj_9E_~*>FMagcA9~?4*Kc|8 z%ieO+AO8A9pZd-_Cf{`Rwf}nVomuXzXn|Q@{*@YbpKU%l{+YVN^JfBvI?eDKXTKmP;&`r+*_`||al*mV7UuYTK0 zANWMY`qyneum8oB{5h>}*#E^Zz3NZS`#U;Od;9f^N_ux)6= z32tBWe)k9O?7Z;W_vX7(?|pTtwY&53zdW3svp)GRFM9Z)1H+GB@WPotGY+h|{U>kS zv-zGEv#l?@@!h|9*Rh`;{lL9GjIb{~7_H+OxicJPgVm%RT+g#qK9;d6ib^cUUz z*H3%J_uu;B-1T?d`=+>sS+5pmjlMA5<-TI~`1nlME!&UJT)yw}J=4Pr?U!HPZx3C+ zcl5f2`QydDzL}ZDQN!rOg}DHQ!ql{J>`Kj<2&=PMy;**RUi*T_AD{o){YP5QJnNR^ zZ0e0I-*{fzg*W}=ea7x@J@m-ie)_k!z2@!@bo|RNdp@%3kF7HwKev788SB@5?V`Uw z_Kdr}^qhbA)vq3X@#a0Ze*65t-1C+ny!_WUeP!(ZFZ{z@J-HLFI&JQcGJpP)k5^u^ zckLtJ{>zsveBsAmx0^rs@$5b9w09kP=u_`pU;X}v2RpuW(~JK63+czcvi8j9zjxiH zxBg@PZ9lC3_U)q!=H!m+ADw^AXZOtCluobx;xBjp$LqhI`uNt*ExS~DF?NrjIgnOs83P`G)CQtuEi5WK9q8^1_ygp)wwbXk2EIhL`nT_Hdi0)` zU3_xt4eLjy?|bZz{~`IDgPB)9;`Ds}V_z*^@U6Cr7^#? zY5yPo{e}7aU;MsHzjx^73)VdJ1|xI#%P;&p_VC3U@BHZCOCIh8mS()+{@dSj;QfU+ zcfJ43fBo;z+`Indquz-#*1r1wFI@h!*L?KXe?8x4eK7yfXGeE!c*g_(^sP_*=kMM+ zJN&XwZJmAFmw)uTqksB^XWhTE^y&BfNwMXD=U0!v$p547ee?DoeB!^par+CtGqSL~ zxb1&^{6Od2=eG9z)mdvt@^88Ko##~EJo@b`$1lnK)txi@fA~=C;2978#l7!*bI)s@ z`-!*xX!4&vy5qYGAGrO;4ZkFJAof{bzpl|NG*1fAFQVzO(1j z$L>DyrJHshU4Q@lLx1ywi$4FkO|4y*+_$go$Iq(W`+e^vV@Gla?_T$;ec#-8!uaq5 z9gj>6_WUlf<5Pco_gzO{aPW?wJ$!k7<2!EsPI2O<-*`8Fvw2$|W9)ABosL|8?Vn~J z`swRFG> z{Cm%hw=OijYuA~X?pJ>D_PH-#dh74@{{8-cyK~_;&c|E-{Q5_-&v^XLUj4_l=e>cy z=~=@M^p9P+xBZ{1mtNBUqt4I1^8RnW^}ZLi{H%NQ+;6UV)h|B%zdrFB9K@qL`kwKg z?%8)-@^2kqe^KhEkKJT`ZuYvLzjgkmv-u-CZ~OR<9{t-bV>=%E@tvt({^CQ`m;d_C zm)`c1zE__=eec7@p1(N#7Xw$G{K+l(9rt{G_|Vi1$NcS2f8N>u-2Tn?in`#$&RRXsBuueji!Ui6Z?|Mg!!`PFMTkB=LpAAH_jd)K||o&Whu(`1Z2=Ysru zcYN|QyPrLtTYLGI>%Lj}uNQvtZ6AN*@XueEdinhEw|(%UU;N`2OP{>=UU%awp1=Ku zS2sV<{=Ekt_||{CXXjr$`tHB_#No3)fBD0oKX7@|3m*KTegD4P*onJlx7_f^fBxZb zvVC`YfAjqR+`IV?Ub6GPx88N%n|r?A)iZR-OTKaAD{lJypU*t+r;q!uN`0d;2TJ90 z79V{0?!SG}V>g_<<-aZ-`{Pf|-S?}9Uzh*;FW&O*x!b;Y{qZ|rcGu(Ydf)Aze!kWA z+S6ZiW?R?YSB~ENnaQn9_qFF<@v283cwmV)w_}sS?FTD7>mwxYKUt~vmZv4n! z?%c?}v*9CO;e|V&|Bw9jsWVzW_P`_ipLfxr;_Z(;qwU%aC-KK-6`lV9W?>)n)e9{%#zHofJMBMT3I?Dl{7{)g6WJN@>D2Bz=4 z_+^`3^}THiFFEZeZ@KBL=l<1$A6?(qyZO4O|GLw9Xl&$9KmGVz&sjfs z?jxW3YG>`^@A~9X`)Q?X9=zmo>#N&4UUk#jGjIRQhp)Z*rYnBE zFkreTw$!x`<62MSj%j}#w7*8Rzc$i-S6%yNWc<)J^a1ifwcivT|60}lx^Vj&yJqKZ zO&i6fC((o*#59hBUnd(!`z5x0JKFv^Y$y+rn6Nki)+SIf7Zw)4tuiB}-iK+`;NK5p zg_HMy!g=zZb-3~yt`h}rXN-y5otLqSQA}67+0=q%Pdmq33nDAt-tC31J%yg`LjU%{ z-o1rAR{^j{bRx0<5MdEFGKNC|`UBKE4HxfZ(?)i3)0yl9JB^*(bmHV@b}>7-x#?_n za`T!E?Bu3<|D%aLot?~Wc6^sxV4mJkWps}J~aZ0taxu5V^HUmoqz=IFqx_l1H)aqY+@Q-1`+>^c?4hK&;#1W>=EJNOsKW*q|R40ZyCuH!492(mWt zP?Qi~0q91x`eT?OOy&$RBbfB*q^d)a^I^6mj@}Iq2mmRP7x2#xdJc>75okax4m*>c z!{EO}Z6Zg4g2b6#N@ZY9e?xu2P+m`Ez@T^w)rE!rG_?;4b&|e=*z>3zpy@qS{yfIm zE9p6i$FEa)AfVS$Ur%F<{TDrdI%Di(R2O^%pP=s-F~)AEc7Qw=Y3>&@#vY?G&t#0f z0C**1>{*Pl9x8tcW9%;a{%pqB-_du@82dQQaSLPY-SCn)Pm;M!xV2ov`ohun*e?Z^CR+y#ldtffq z&R)Pv`rZ$1QW@Z=KcY6TVvN0xzVBm%f4#Ml`_`ODF*o5H`B;otKZd;3}Jxojv8TzU;-TP7epw9wvu*eDiF&l?;)QrMo& zb+^Sn?vjt;npx8>6}D&PlNr~seR$N7&1Qk~pTBo}?5e4Wv4i6i1N~DYV}tt!hsQxi zZ=}kXYNJ>;;nqy3wZ4B~&%u3qu`9G<=NP4mWnXC?H!9V#nJGFIsB>=r)qTVJMyC4u zhX#kI28Q|u59keaYYnW&2B6Xfp-K=7cW7j+f3@`>{%*K$Xh5&GOY84^0%IL?PQPkw zWO!m=xPR*4SZK7{wA!1hj_1$1CU_3FY#80Qe`I_@?`gYUwq*FmwBaEXPa7Q>pV&7x zFh1442P&LRAa9cm_GvXw6W3={%v`<$Iyy&H)}d0@#7?b=%|>b7wEdcky`P~P0Pw{2 zufynRv8dJ!Lunp8xaYv&-YASf7E1h$j=njFO4LEssa4rDXwO)-8 z@Wz3u1A{|@2;WU|xpru^;)mIVOCzuw`cqXyXiGB;s7An9OpXp65FV%o&P9Sf2lb8& z8_GY-*p}1UA$5Lx#VlFI=NTAz?8KIH74XZ>Y#QI~rt%lI&l_%g*_v*b3x7+w9S}uT zz93XtmU$_G!_Z0vH_P9=xaG1+hlI}CgrkfZ3f_O z660)QH?wI-(3LLo1UIUF8qz^f;`6Or)f0KsVtSEFsNGUd=eh+9Eic^xrA6>i8k2Wc z9n1DJ-kd7W6U`DPz=?1EeRj*XTp-2=9_-{ChqHP<%E5S_%0YPC#vWHeWeS+k&W#Opm|jB2&HX!z!= z0|`{Cu2ZTNeQVzG7n@7wj8QB5>7r|vOxw4NvIoPfSat#bHbY#IRW!XqGgr$7-?`WX zhBWyQ;~N1Y7J!Hs!!8y0Zb}lSmY06l;a24NiZG1oEQx1SM)a!%oQsKYuGn?|o&uL* zges|bz{<6yi@uhkq5z>__Nqwo2Fr5Y%DiW-wAOn$1B2)y`ENVhm z$mD#L;1*O_uVGTbI4!YJ6{%~6b=8$BrE(UaU>0}+G)_s1x-zCZSJDDBTCBV@Dx>5r zXc8LhG)7X@K9*ZCma1w(FkT=4{XKZvZ;b*8cvG^5kkHL`!!u&tU}w+?ayfI7Brbl)KA|C^_c z>BZ?(Mp>W^l%yzP;Cs4UA7r_4iHmp;8Q75xAIc*qui(qkr(^2UI^x zRo^=@eAOWOGgJe*^}3`}Xc1nA$%gJPNAHW!fOu!I5g{WW&VJ zX#XJE*Q#K^iPkgl*@Mw2W~KPhI?(Wz<5FPyL0uZ3s=OYDdm+8Wp{RFj{Yv$6dUJj| z9FuF$2$X7fi)F_STG=3ehgsn}x1yDa|AH4=7@A~9HnIJ&tZ-|ff6p4O>CerYjyr3j z2!qAan4yBvsq@0MalCMRjpT&_KO`$WWKO5SA5%hYv{vPwQ?9`U6(ZG=SF3{57P-!y zqESYURxCR;xOCcajagHwv0h`g6+3B-aZyY0h7Qe@V(?>inMB4W& z)q-jqN(nqJ#34HlAflJ;*uG2gi}9K!h7n6RBI< z^nS{&kafT`ej9Oxe3%p{Y+G`Sy#QRE7w(U$vtYV2wQ?KW{9?LQ*YeCX-u~9covLYb z&#AdZlZaTHezN>IZq$6IV)#~(j>5xPFyFKx2k$;76X7J-y-*-Q3(^<|9Za?C*${IO za6#NAgQX5;@sx)Zk+KT`B4JTMZKcv;JD^Dc@F(&-j%u+&4DCiJZhIEYbxCc|RcB`h z9w4U)qH#}dATck&&C7;hI_7bRCoswdymbTVviN&grvUMgSW_ETLab|+H9k^AVrPrv zsz%wGc1=iGq9}GFJTlzHuxx%4nJF)iH&d>OC8w=%TCUcoCYw*w9-lg;t&bDW)pcS) zwH_qB8N66F4IqvG!`{0EM{;F}VFh$IyBqx=ha3($+U+|iVY zErEXoNtk9Z*CM#Yfv6$Ey}`ffn(rTkWXw`Ed4#@j!U$fhvjvqpw4_#25jRxHjbTtp z9pDAkm6iF06t>12oWR+$Z46>z?9AKEE+c4#GN*m!%+(+%;30=IBMph7*L|n2DBc*c zGsraD+jNICGJz;eb3#FQc5j1B&spOz@`o6rs04*)?K$`LDr<;AlV*DsH< zBr)<*7As^#gP{|KkSh-OU!eq~ZYWsEyfs%FHC;T78TN;wk$nfx2=gnaEpS4pWE0Pc z)m*0TQsKLb3^`Fjjn{f}4{zizS%U!hGjXSSxiY`9gC#cV?T644lmHelF0oXZ$Gi7S zJGC8AK(dV!1;NZsVT=sq72-4II(Q)BBX-INj_ohMleuS%#XqoF%U&aXOL8CR!x1?L zbJidb1G@N8@`{dW`(44b4FYk5tfRd4zz-v8^W4Rx6elqzkDVYIn=aWgt%2jw)OjF= zjQAJ&`_YjZP=)p@JlF{rRX?V~QFI*p_WvY#0t|u#<=3wlB5R8HO)uYD70eSOm>J3* zZ=)eM8Jo%+3WNZt8KlC=!cJ|+AOJ%A-vDL?q^{Zd5`5Y0`8GoU2_nuvoIDGIHBngM z4Ah;uaTv^n12dp=80Thg5Di^b4HQFrLhma;{r9q0>M^K;ZA2E-zbo#qEXVVXG1>_H zp)gTb@WI7l{;H0B`cS|;bS9vF$BY8!1>wOq_I-=7LZiSLnt|gU6W=4D1135%gu1kE zdQ4v%QVUWU#{5zuVdNt5r9&6C-^44LRc^b<13Z&qADL` zAYAY+r)@Sio+Hb{6~}{@wPFCYY@ZQqZ|BnciGpgoa_ebhuhm`O>~I|UZOfs{&~UjmtvTSx?BPqk@y z=cHnVI|@CL-7A0E>5*L=y07V=`;mZdAmeQA0>)V+DwGJ2uHakJ!MGJO#3Z&!hrUOm zff*6{V&p@1&r#qUnh_-eOi45d{BeH(?3~&Pwfl5d0dj@Vi>d(F%BT`>E{V7-q5KIK z;3UhqD^t`m?u4iyVUPsK@FuxML;j{tG4AIpU%WJ2AU6C_?v>wV)c6SI<(i0t1Bh@< z+;y`ieVg)NGOBsvMDm94#wo@41)*9@H9}OM!w_yXpH#cmW>Xjvs_!LFb-HwahaXNi zP-Bv^61#%e(@C-ZPY6?y!LLT;ejc%2uA6cqsS~+4=3duf?lIT8D;>v}E5`z=w8w1% zPedR(IGaoYF(){p(qM)#+gGRO(gez)Du4zVt(kArw7$%yht%~)L+V9Sj^%sovOF~A zP7t{?N`QzCM|iIV*^vikR2oaBAA5cmG7NU>~`nUJL64gq5flDw~s|HVynjhMy7RtZtC#z?>c zkhk2UoE_=Bi4x&P8=y^K2!n6Zd;%y9@e4GGaG~8GUJKgHWMyu9lHrJN#*<&Yhd3hP~2m}53eq3RDyYZyE{-C!0u)Eo4 zb@!SZ)lMzZ05HIUj>ZkVooryUwoz+UJM{)K7NTo#0Ct0&q+p4!a*}AGxGXoqA9S!74yARVKyf5O7B!ZhM}+TwOSAXyj6R&S8I0?E3KMHUxV=P z@JUPr8sRn3fLZN*M(O?Xa%K4*)VwJM-E6d&A-M)DUOV>3!7RTN0LlS1-w-vqv9dU- zL<6cl`Rz}OY{6#zpkMq9s=puv2ZwX2nvN|H-r zqY=B8+)U-P5_7ruNj0TAss4}nq(bPCrxgB^UG@lJY2OT)q!dusG2u9}fWKs$!LEeh zKKYn@%4YFdT7qJY&IX~vU7jF9vEm8Bf39WAA(kr!KwNz7q9OPK9u#mgNE+}skrrhW zm}ZdJU^Ka*b9uEmwi|@ggj6{|34_46Q!mn3W)Ojlse+7pswB)H(?DZ|zFAC!6_ix+ z#9|q0oKrf5Sq7O-qG5UvITQ%(gwhjZ=ZHc0A(MK|NU%Ysr}1dqTz@H$tr9al3jcbp ztUFmKsnkv!Yd0YlI9u!*68USE?;X-0dI2G?1Zl3A+rHPYdNvDN-Hrms>+gls?)Yt| z?{xYGQO3r))QLl6!PXkT&T1 zV@}(syI+D(+SDe#=R)Flh))M>Bb1^+iiRn-Wa-H$MD27le!X>z7;A#&R zCQ)=07~J8AQjb zi5|PTc&+)g^PsW2x4X8txmjy5^p+J=ER>dP$m+)^sFR1=uQJqwUyVHcp_bA3t~@d2 zh)qjjEabp0j(|6H2>4Gg@k?2;QUU{`G1$qvLm%0RV~|cA3l*bKxgys=L0G`~(h#H4qL8c=Sefe$1C1PUmBT~= ze^1;2EfTNJFBteGOhYpAA{Pwt2b<7vF%n=6at0vBW(c`UsE3O!Z~+*)fR0?#g1g)z z;6st;W1KId2GS%R>|Z#d)P|%rVMN|Pa$FZ=YZ4v7J8ta^LFzx4;ytCYL|(wa7{YNt zG2K9ENW;+V(~y_}C7vGO+E~Q!BgfA|5FA4pYa7HL;(o+ zn3fe=DZnQ<8rlZ=AukS&5*UeI*4IKfXcT_-RpTrWa7l@fg8yEwcHwH_1BFMrYWo(@ z5}?v)*WyXLr%Lv(w>z!I?pAkWEp{S;T6&-2NxKII!{VS&yUF?xPue|jhY*GDv}(=m z>UypFq~3YZ-G)A;4^;O-t;NoiYO|^EX`%;+O)46i=I$&_$XWAQ(oade@`DKF*sIW- zaMke7uB;ao|#=wnNS#$+TXzdtKlx05YJ) zH)396j++_BhpSkdJflw>FgBLtegCVIc;8ES`zJd=ge(c}U9s9q_k9|I7ap>=WS{kg& z4;*f?W{u&fHPPrC)XMvQFeJUPK+Z8>iFYjp4M<2Afve66-!ZFlPHF*-nZx2S7|kz<9_46K2Aq}%}8 zR+pAM-)7E9wi8+P6N#RZ=L$L`!8tG)yG8zQZ!$p) zeiMQin3X}OtN-&ZD*(T7forfS1fyI-V;fq6t%z%hF&V3~3$vB%#NgsKOZFARvjeAR zdZw)Y3VU#IntxkI^S{lt$cZrv%CGnXfE#5P1x{}a`WCjacp8|V=^n#g6AA`PR5=Gw zte7F8WrD#DSUXqiYH)@08NtUeYx#?1k|)6m?N4Ac;u& z3h3YF4hZlYS0@0l>Zm&2K253-VpZ22VNM75ujHF7{)7Eu0FS?JU;vrw*p`+W?v8WboI0U7rt#2^M_8B9EK z(y)PLPe6v%3$JGiR1y5c+)Fjp3v?3A?-@%60mUE)GU#*rX5>RAtC1fS>Ioh)uP2c6>g@~_NlC&AlvkIZ8hA}NJx#g+03*k&_qV=`O@gSd|VB#^}Z zfEDEM#f17K0P|PKNbSDdjY$F*qTE`pSa%7%W@45}2BzWmq*oyee~>25Hy5GM@O>>w-Z$9Zq{41&Bn*w_3b(gShS!XW~T*=NXl>jO`aYPjA}uC z`!9Hb-&cbMUWb&EI8t`pt!t0FqRuptuZ(ikiAh`>p6;~lE#j2H=gILB!9 z!c5qQGV2{M2jB5OcU;#boBr6dQAL+x=6DZaM5TyXimABn1}$;^iA6yi;4;MNi-)Dt zHl1R+aL0kyb53;J^+%j{#48_a0c&cEJ-iL_s7>zrBTzk>E>_bXmH^t2B3am!lrUIL z@`(bS_jBPGyT}j-;lH66eWvKl0z`C!0AKN;4wG3ZS1Rzd0#6x8vZ>2Bbd~-oQRv`O zX9K9omxT)zC6R*s?c76qGI4El8D@aD0BB1POI!O8BITK{29>!qKM#0$=UTh{AX8AK z11ANQNzwp7ZLr!)pg4$Z-{P?w{up96{BbPJ@z4zVOZM-BQE`t3eL#mfJ^}G-R4`D3 znAgQI{tX?*|6Ae+XR9VKev|z(YvbZN{Xuj@;hPAjJ~+Sobe7c2@EC3`MQBG5>s zl2ln~H9*{O9MbVHW`SQ}|4wqiuTC>8%He?ZzJkh)SY&#A>O}yEbNhZ6I+$DV753-F zG3A>&Oz~n&fqn#{2;0_y(;t+WT`N~=sG7?5`VM)&&3$0WIz}QJ2kh#KcltHpPphp0<`v!*!9TF5*S5a#scwzwi&R4O#0NWfy5VZeZuBBJf%`o9u_2y=) zzE$nix~)#TnYebix*s@wGotcbIo-DjLNS6Yd7Bwv%<(2-Lo{N6KafoV}hEno|0N!4`$Lm}+|h_y}#ks08Q zn!>4QnJ)7r2D5Mi<~sv3u)#&1-BI9U%N)|c#2}Io{}FP)yVN|S?CroG(u{1(rZ9Wx z*i==MvVW`UBVLlBwZ=Yap&XLC%pQqQkNJ7RTq7SXjxg#3p5KRN;P_*RHNk>OBW|aB zmQUVVtf4TU;5y$clDi@<`Mw{h>k&e%X{J)POxGH_jEDkYD-H?@M38$vPK5SBi-w< zoqVkkGqi*V?xW2xJn{p(h?Ml-`7;5{Dv~2@{(sKIc1&W1D@H&?N`Sr-QE3m0s%YUs z%7VQm3n+06NE-$P>JPSpPzaXQ| zmI2Wqc_$e6p-^fUFCiwzbmOh7PR z=a+2LqRkSS=5ZUNjzk)B@Z?Yi71%I z+xJg_VW3+arI2v_K`!B%#Bi%ZYd^V!`IxS4?6zZ_+$(+GJTi|}vuUucK&P~uPikxJ zdZ(5yED6@6La;DE4$9fF5XA|W(DiE^*J2`cDGtH91Zun`ka7%S+Or1DvO`H{IWp)NAAqhm_8-)ku&oT%U`RuD5lh>lcD_VdH{yZ7Z6- zjoo&_r6$1;Ab)P!kjut`kgBNngBtse8&PmL%#t|~m_9L4g@ZWdB7i#@aK%PUilmA| zFu%!oVSGK2NF_N8OcRt{b1a!a;kz(1e2k3z(t@$HfLyxmshmH14$Qss{4PYzCYZx@ z-?W&Z!Nh^`D%=LHE~?T6_C&xck#x4(jRj+6X#x6V zl7xohmSFIRKlhhd`Won7#jE=;3fbxg@Cc1o=l%*W4@1DTKc5y$CEE-KX!1~;Aw}>@ zEizA#3{Mr4D~XjEdL%si*oz#V3RtR#FA*>q?Lx3e+V#Z=cMqxosZ6x@&ekS)VC=Q( zou@oSpD@XW#;^7RbKmsLdkV)UD}O6nS>%)x6(x0>h=WzXCfQTgdQ9SC0w%)?fTJyl zC^-(ddJ*MaNs4$E;un&A@p#SZ{4P$BzOAE3A{HU;&IXS*fa#ieZ~~T-@sc?jJywuzQJCi&b8`cTP-gp995X2Gup|nAyfg*9Y^(_< zBWPERJ@*!OHGs^b!s|fzQ?aUzooXFJ#XwaxaLJW@(`5-QUxhyQTH6rQM%-gUX$a&l z*rjXgpwez?ae7QO5LZH9t#?t2>#5Op5MDN# z=7%6#p6;0ycbL38`Y%r+Y?FoiS{{o$ADhQ;+Q-8be;Ae7N_lnxz!H{Te^HxY(OB|Q zqp~o&sQ5(1qrEsud`CwTuX492FOY?Bl z@ewNq%2SJClTuES3H!{^C^6O304;B_lHABFd*`89e9qVmk{;;}m8MZbB#G=vK^r)V8!=3!hyga8I+7O3mHGHY!U8AofX2n49uPdHaCq?iFS!D1-qp5V zuuPJt|CAed!mmaIgiu^r5%7-AfC=t-%rJDq$n;R!z70=G@uQ0qn%@9IlTP08cLbqH zbRx*j1Aj>4EF2IBR|IxWCId)TP8w>mcRZ1Y?|sz)1))2+RwsuP)RVI0WD-~|`Q$`5 zsrl>ReR`!bzhK~3?9`JXM?%fRez6($XY7`*L?d8txGGD?S4vC*-U|QW<+>g{nmsm$ zE~^mBcOtDTPZf+^!^awtl1EN%O8**rAwLi~T?wHts6|G{(ZKgeX^5*F?-MCu-W{he z+TsH!P9@?SB&W=FCKKg=v+!M3m=z+a{FEfwy(k3QJ@Geza1`Aa-F-heG6S31@Rvdb z;S&X8hy>If1^x?1W`%`MNyL|$D}RY7;BV*QACR>aT|naYS8wEnSo~(f1LPHFICQK5 zaJ;W=tjTC(A`~8fuLi5R9y{bits5I_UC7{`ssOpYV-{ANrJ}qzv0?DMgG5qFh#mlC zuR^>99@LMQJF#7n16FBQ6~NVSl5g%**B{h(Yu)WywY6K{-NH#I6%(SzLb;4e%WSRG z@YGr?sL$J(j-E7H4{Iz+AfFWQtYWb)Rpv1s;`ejvbvyN)T4PTg>RqL}VqeWIr+FVa zg}4zmFQkeC$!)&PT#_|At%lTKQ7lb~9JvGrge%5JHl zG%tgFJ%So9n`&BhfV$LfVdX$JsJ_U$A8q#!6tcoABLX ziRwY;hBig%^p7b%kadzuQqQ7JX*Fwl28{0@@&GBzqTG!Z%u-}J9u@hW_?+Q9)~MT9 zoMy#dKb2aR5ao=6gdRM~4d+cjNz|^j-~g1On|vqepzd89N`h5ZhDmq~2tjbQ-2!1V z2%MI@J4s3n0`xWcScIJBPZdgP2qq=@inR~qlt0g$Nie*04cTDkXs-G0+PyQYanRodRND_;79mua5@f{hGOOM=f-|^%jeU(2*p0j_OpR@3*(NpM6@RhhwJkwp= zFUqsI-pT>id*T)rG$$rFD04m(@pz9-U57{l2J$FyO3)l5NQ%6{|#=o55F3eJcr{b zAFsmuiYZ(iGry<9%)h}glXoZ(v)Zhawvq}Qt=jj^Lm$yC@`C{E8KMBTC`jX)5-Ry4 zwujHwCCs%?NcbacxiS*2V$Lm1A^uH% z)WNR?uk=<~-_ZgLA1=}>@sEq6)c19Ico7pzG%s*!Q|EMxXuwUhQb(4kpSss$LfBWF z%t!QnA+@`xd^-B_-9Ti>AQMiM#T!h7w=L<=EQR*dE9F%RMtUnTE~T0frDZkM zHsmH)W{cTCtv6U_8Pj{#qdLIr@tQ!rDOOL|hOy$3R9-5;HUN|H2AcF@T@)XH?~A#O zDSVDO8DO)a{I^o&(;@?#c!@s-{HJISqQXt7-7wGXqwx?UQ=hp86AvOLSZOGKy@po-kj_Aous`STT_)g)2uhX))3t! z;u?I}@UD%&V@7Z|F`zDqcLH;HJ%Oc(G6_6|%sLXAv0Ub;!LB%@Byj}G$Cs<8Kpqy( zVd^k;;?Q(*R*j7SzXhUTYpHW>rHrXzSp*O)pjqIU+;d|?b(^scVMSmm4M^oAh(CWB zD>e68+Xmn3IX1SJL+r7ioK0X7FE}``B>`m$JmX_4@x6*=-egH3?*cUCYy@!EpL})L zDUVD%x&OiU^R98(lMVrtR6q^wELmE{CG8dicti4GXm3iuj<`j zqSw?RH);3OIQA^mI2?vI*Q@IfYTb=i{V_B3eAjlukr`P78q6@3U?%j2qbM*(_Dpyj zx_;m5tEumjH=1y{EN&6c)YJoIArdoymc;e=KaLKN&=b3s@H*RtnS){3r zHK9U>Z8=jjCr(?iCTD6%&HZ;nZXTKuyuMLyH>;iX2enogV-403aHeE2H5s40<=)~d znllaH&nfGETR=WLdH}-S+ODs)YP&n_ZhLp9*P;2c}n`dtFXG)VdemJ*8e#riV@qS0saJRkwptiBMjU4Tn76lRwib3CC8DzVr zZ8HXRyRp@+H@lCktuv1p^6QHB;(fXeiP`GA&Akq^g@VDE2B)I$<@u%M9i#+1wVg)m zX?MH6Q$IT#Z!6=vRGD9amR@T#8CBV-?Zn9@&onSSGzx{`nVM)Q1DfJV=;oN=%gt7; zx(y4z*=p>ZdF55R_g6nIZ3=@W_zPxxTeSM1R^2!U4OLezNkh5q#<|F7Uzs-9U2oIv zrA=qeug;G?e%PrTTU#GJwrXpS%a3c%o0!yHV^4CX^;Pq#4MW%pv2rTY!so}D zYZ0&9V5MC3ElvF!Rpb;}D1QwCCh8VA)kYT#E z84`C9Kj6F=`**B1&^WDaa(%&vI(@;_!o3M*JUvnP?E=b>HYt^ugkQoiup=6jFlx~K z3MrK!rB+a~%#gB&B`|;q@0Y>IbYOzp7!67xjY@3%LcfGIs4j~xlX}%1dk*;XcP&R% zDg5@Ye=vnR-&AC1GjL41XH?KEY0~cfmew5UH)IXL48?z>2+$wJx|WX$b3MnKWA?$* z+){}xASr>>6!mQ1bC1a}4N0jK(C1?(z_fSd-4q>}R@d;Om)UIL&bQ^<*cf9|E0`Ih zUf|i8wZ%VeRbF9}1p~iqWu;pyFh4&YnlI*tNFT6U{7Zk|a9Xz#bM+ohr`3-U`Qq0V zcXZmT-+zsnl?ezE3M?@67r7w7IlTFxIk$7%ezaX;QJlD^x8ifDkAf%KO{u6(bJf(} zyXR(Zk{E-sf2^_jbt5*8Mx&!SNz69@^|-zxtt=C(nRw0gW!hKb=rm3hQ>Q><0twr& zX@2RNi-cGxUgejE`q-{!y;79NIF;|2f{4I)s_Ew=RxADki)GbqaEqjb;nfUH6=n6ro?aU>wV7*JZuHdB}kiEAixR!G21{72pzeN82ZMC-4q zL{jU{A@tWuB?yt)3!%X^AQ&=70AVdJgiKjlSbb-1bdq#N2$4A141e)8BBhShua8@H zC9K?>oQaA3wI8=6MiHThIU|CIyRWY?1n)2S{(sGoDvYOA()+WFAwsu#78Fs=7d%a>)$=WXjb(@e(l)Vs2yyMX|VRdTK(kXEzs&-6Rag_-mqQFV5;g} z&t~>2;;8-s3zVbHxu*|9@t)4+RSrclCszh|aeY!zr%yWM7Ui;Ap;3wr9~&d;kv5C2 z+8$AAlMW4|S+eVsP1g(uGw|0(=AnsyJfXeN2Uh_6*V@>1!+{f}1E|aH@)b>>5O6H! z-Y?51$nBXH5GhmItxGnDuQg(l01$(u;RkCd;0>R%n7J-6b0D;B2fLQtm%GF%NJbpF zn~Nh!cPId2xMFyP+3v=d1}q2P*$_cQ<^1P7ivj#=B|J@k)p&tSG+xRbL3^*Kzj)n1@DCQer`SWOi)_;2;^=O3VerrzD;cgaSbd^b_ z$A&*^j_pKR)qg40;e6>X5|V{gJp;<&`T-uKRC3cR%iuNzU-{uzxal}s48*1^Isv}a zUdqk@ns9JJ6#1Li@@&3dL5o>&;rjj9)KEB6@>o@KqdpvPaH_o0+yZkv=wj0Dsds)i zcc|@{EC~(Xm8OFkOxnGlD%ZP}Qi*z&Z#!Q9{`6jFb7dNC*sKzU7K`Zp>2>h{QmMl~ zq5p5jj-mD4dS_#;TWxLOrRk*IQ!URkqtS;+n6!K9O=qT7Hbg?I#tkJDDU5Gwy|KGl z-|BAE@yeaw?Z`9tGFsUIAv!&x!7Z^3Xli{@N--Hr5urJvgt2d>NUX0(U+~WYVrzMR`!o_%KzOenkbG$wc7`9+G&(uE@ zwt=tj#i{1II;vT^%&wpiU-<|G?Np&0DCp3)X&}Q_k;vy1l=-N29wYS}`iBn1BiRg& zXGuMRs;UoZj}jAty1S-@DV&h}wtdeGf*H~>f%?pl#vu(tN`o1)=Z(2e_ zfh~v3Pc9^##~W+-zO%Ejh7AsBWCAA7$U=?!#yS>iRL9YP%+Hpwq&bcT)QcR8=S1;r zS*02HN3JRQW*&8E_DAu#OKhsTd?$I|3F~_+pf-%dbVFf1tb|7Ap;MKtQ%AJN?8E00 z+*R&9odTk{Y?5$Am;6#<7L1<9rNH6*r1wl@(v?8UYLHW?aJoN*4jSa#G!PeB4rZT* z+zpKV<9gwj9y}88q<+0^aKZIskS4!G2*4f1moA-2w&0pgzI!;8KGF5)AoRIta!1Iq> z2tdrw8;s}%+V;vwtjX?)XrijYY{6g8l zuOr$6M*Oe*Y1sh49tL)ZHWRraMkfnugs>ay530LcwVfJwp?qVfzFTi(y*I859nbMq zuiU0l* zXCEgDKPa$6h9Eb~qCAGaOG{vBPQ#ECjn#6wQdBL%xS!|gRSI|B5K(t=vg}(<{^Ati z2RaJyKL`o{3o9tV1IPo)6a8WAES?yUyCd>5mU-btPS52TwyHz(bKfKFg_QI5(D9PN zQ+YdjMoYkep@2~mb780n%+)Ly$5x)`;cWY*!_HMOc)>4o#0;3=j9!HVIIzd4ZPKED z>SWibz*6vS#52bRiTYl0f9j$@zd4RwDA9lTW%p4+9TI>)IitEu=E)lR;q$Q*MLuFm zEES@$tdJXTE2UgtijJ3DpQo%BDLy~F%Fb5!jWPK0@Oc!02{a@D1=NLjdkGYtIuc4# zp0r+{E<%ZCM=jSGg|rk8Uy=H*#}6-#iPJhv{Bt~kP29e~#u^(9*%6CLb{=&)WJtr% z>{HSU`~#Yfmk>9SRsdrq$O)n9f8){#tdi$p-g@W|rn0iKVBi;Q3Ooh!( z!_2wK5GNjzFwKEa33B5Y$~)I*%rT2KqKhXSw}X*$naBa3$PD^4GRU;j8f!&9n1u(d zN*pEdXJsRLb@#QgH*FWl-p6?wGAqy#l0phRtPh1O#4u3X+FeR4n zf_bRAqD0>wuomx~36=ouC72`X|Fc}wzl>2Vg&Hr8VD#SZ%5Jx|yV+>1*ShUmr&HhE zYIis5+qG`9*4n9~w+SlGE_o}S8j&|{;tgn`wYR%lYqh(z-RjzQ4eyhnf~x$J&S~F( z8koqY)?IHj9@f#11j?)KrLSUO>p9Q2>D;Whf^MF#LQUW@x|@w{?oI^tR8~WhH3*X} z>nF)tQXH8579&XT8w|ca=B}6Ttr|}YpybK%(ey#4r@)9N+*&v!OgA2G6*G9?Fam_j>e3&NAAml~Hl z!I;8!y}90KwY!a0eXG99lWJ7AF`5onh^rCpk{*_-mpU)}zOu*4dZ}LMyyT`*@=QCG z_A@`dMooD~!NKI{iCI;~89a(+o^Q+WCDf+_@FPmJLvYn+A3Rhm{~DBvhw=LOr_UGU(8 zQ(GynrhmfX-;l*0)}E$HCl(QheTx-WL%%cmOyvR;Sm0{{nM`4>gx%KaHnt0(yB0)T-ua3 zI$sXZAB!VV4(5AoFlSiKb00Pue|0_-6|wU?(ltfpsIRk8Cl}9ACU{@uB{oM6q)!+5 zRx$rHM~@QtU5z~_Iu`g6P?wq^iy-rjR&BdhZP%DLvPgf&M@5ycKI{xPqsPsS`P%1^ zv(xZfJ#%sSd39&-$3c52q^;KzV@WH_Mp*ts3TxfCcAoJg9cD3!PU-{q+*KkK6vve5=x{> zyehdPECyHr4ZV)pV#I{jMO^(K~iG<8{FPMUKsBGOF3XaKFJI7 z%XCqQrE|wmI%5OcFjwqCTuL7!$)Na>)vKv^$MsTDeN zagD|YI*rDEJjHBiSieG#8v5=yf^=_i4GE?*uE_Ik`_>q)FyY_ariOcTXA~MZ6W8pe zuOUx!;YQ-A$+j!Z8gewR@$1TYOhitTW4ZPrVK&0o3YDYs4V+Cz>fQfmOhwM@;*G0v zCI|0}lNRhlqnk=e=ofW!#kq2m8qlx{4|kOFbAT@qYvSB%Z~>Zg<~2A?w}B01^k=D~ zo7vG!o#q^`CtlbCp22MK;BsQ6yfU9mp`zJOt@gwqpk2W>yZONMu!FayxT5Pp5NI>$B|vk(UoreVHda<_|{#!I`izIoftRwH0hEa*D|@ zo-{cztyF@j^Grxi<+KoPiIvk#A|-7an0ymbA74m><-JL<(jZ?}P8Cgp!X*~VlA9bq z=Aj^vbrJQ1z^bg7{yn0dFq@SEXQrK4WHn9|c6r^jXqdpwgo0LJO6DYB0-BMP*?1Qv z;Wd$!yj0XJfz>p~VTPt4e-*dLr7c}i{tEAMmM+AmA}`HoHeTiYQEtZ6TVK-cLKX3^ zd-O$Qx&9af!>8|YuG>8x%J!>uy-27C!}W*D$Ag2920Ksc4<7EVY%Pb*{L=PP^XcHx zv&DG~zN^2&k;6^sI}=mPUgOAG=T@!hE?<}csan%XHOkw&wU0aN+l{@AE=IMEQBV=2 z)S=6o-%Qs`shm`;;uKGUQg33?lW)taoz`BvQ`_jWjO=)K&s9vS4PR1=DHklI9agns z$trrqXF@A>8kI(^7&}p*Tirsb{m0r%uGD39L<(1SHzAcym%c)!ldy@pg=V8iM@Msm zXee_NC_x5=U+mvy>Kct-?4^tN$o&6EOOxX%DXpGj%vX{%aA>%=zT$^EeZ^nn*-Yae zh03B$-Te|=AyPYG=qzokj|Pr4fOn9{G-TG*JPBM#t#Lrf{@6q3$`JJ)5LuFh0~5m7 z&7kY0+4BQ4at@)0avyDKc|az4;k4 z@XiPW%-|GqYls6E8SyWEox~SdI>~w>OaD8QdDdBKuz`t!%U!T4@T;UR?M z!{0yY%4Z_SXX$&&LFrq*-J4@SimCJ~?8(Is%iq);mOm8cirA|-DR=!6#4?r#V>B=$ z5{&~73>1%XlOa|salFVUrsm`eN2ZlI^stZAi3iIGO(jgI`Q#DfGx5CG%j5!SNk^qz zPAU*{fK}aDY$(S*8Ek&HUeeMq5fc{#;c zCb)j0ht!KocHkUR53Fci?(CeoZ({AGtoRixT48t#kjD$>0-<(MbZ}T%St)}JzdXb; zaG2OVu1SZ}?gT)bKmTd~%!wUCpbK;D&{ZNh8RwFF73n%&a?E+r{&7qOZy%ZvP2C#M z5H{l7 zAf`GEqYU6j`^U54KrtOxZdemQAMNL5qR%cWf@s=@6ITLYH@@M+9nvAjLb<%8@^%@t zNP!!3|CHc|ZNgPi3}##h37Y}L`d@{=e%)IUgC3$>-sinOjo1-}e_kA`z?I|LzxdU!e)Sg5ig>+HAb%*Z3L6)AwTu5^v5v+p zoRLU>%!3?>f8x0SsN;CxGR*>C^zn3XJmP)cLpeKxB-nn6Jz)y!uQ(?zU&NE6907429HP@d z&PAt7$Y~2xy=x+yBb*d(H=4EGC$+WK#~Rh|b$>*?BiajIsB!re?QgoW(EiGb36oV! zVRoav+y$fMZf*B5OhJ~Fn$U>?7^O4oHxQp(r#BamB|d320>@wF0tftR@LC+7g@QOY zVGZ7wr{%K)8bvg4WpAbZvFj?LhaB$35q(aF=>HK%^mv{E*G+N49nl`o3K9DI(UBQY zV$*%cqc#al+wn<2E$SR*Ts9R0*X;b1a}XOjNzEnzCxLD8coPVB)+N=U-%9x&1Vg#E zaFS=04)pk?AU_M!C8U5K&x6?{DFB&!uO~(M1S}$I&!2yXl7Nrs`+vQXCxu^68XUfj^>iYcy~jto53x1;!trG_fI%)yLHi%MQp(N@91Z08nkcMm{Y>vF=--P1-*c>R4pyjS z_bLi87{?0m{Y!~nkp6=gbT-=kg>0-I$6%S0ih zBuiq{zMG9j} zkw>BPwg~BCTBzZxnxlXgb~Y@l@p)(iC&rvK8T%Z#rt1RH{q|;O8$zIUZ7=My zumz6S-|=mxKpZ&(-=%88(FCLWJy9EC8eqzzt~qkL0(;otun9?-rONvTiq-6WOenec z1rWSP!A=j5C3x(@TlJ~T|F#GBomkWhE@Ji@>{?-X3Rv+0*(UP-a@t*{B#ra&E zy3XsU>mTwklB{t-U3Y18aKU>lwg-;JsK%QkekFqaczXe zAA3H=@&eoYpGYoLx$k9HHmdf94KIh{=~gU@9(T#cZksg03@gU1;?jb#v@jQ0Bg6JW zkr$~Xo{#Os^Sfk&?weyb;@T-cVBLU>SX?Qu2$hd$Cs{tkzGum1XPX>3u1oq5-UBb; zNeK7%q0tA|Z{?4Fgn+xS9bCVP_l< zVw9Sm?GI&ZJqmn)8L}1TV>_9G7-U)y6A=vNE~$&P-GJEY!8C+NWdQNBUf|e$`ZEFi z@%Ui)wioiiEHDgk93kPrAGYBeUIx@#$Ztbo;HpvtlCRtTIgG7WO3YPUFO^w;8FK> z9?4RU0z9YwAU8VQL}8>zLOXpQ7cPpJU9M&MV=tN=x%9BeHt-!w|H^@ zH#MLcH^Yb$Cn5tHpc;Rwxn29XyS`nmV|MGy(3Cu#CSPjw_uu*ROd|@v^7M*h)Ipe= ztaskqI=vc<64nrhd-20(0S=!(Nohm>77t|weTbu{>CzX1d#51*b0vRHJ4hdN zY}b`Tj%iPoGwX=ksWrKa^D^PJkjFj24 zz+|pD&5<%BUKr%1V%?PR&V$KwQq9(Zr*AcxAthI$pT%~J5Mz#;j8_?+&4W_uMqxLwz5zOi;Aj3_?6Mnv@DEb zk~Z6X^Q*gk!G% zCVzm|1)w$qRm>Z4ap70_TpHJ2wR^EC!ene4^H10{@3nt)BSmoU@{og9uQ7@Yzd(^u zXt?Up2;yf!Auneg(L1e+i|}qE0ONpWkN~$_8BG%OuVS_LRgql`#Yv z>(BhB;Wr6mKtP*M?#1XPw0T*c=w#LPhkndT!77ZhR)a?hRMY|&!IKIuaDga7(i^D+<=a=F> zON1jOQv&X|+ zSP=6=BlOVmK^j7vLtadnpi$Gmt>`+TfjfN%rJ8ShxUh@2tqdh{qU z0@+p59rqm%#c;=SfGDSDFk&SPf@DfeOZ<`zE}O#mFj%GUv0GSeW8rhR3zD>fbc#0u z5AB}84<+Rx#H-glMzw-%fEq>eG-5#Fm(PqZ8yAzsOa5#2`vL8n5j99;_Gc{D48xf) zu)0Ijnz6$Oe-B;L!-r;w&rDVX^#M=@|A0!c1^5L2h^*1RYxct#3?4fZ`QZ$}s#sl^ za|Eh~E@XSbvv8G<3SI?tKcL|NcTdUPjmzgiQ14@dRD_GC^0XWHW6$>Y_YE=}DD7ca z255w$?%`_pz2SRL({H=%h%uPO6hFY!V`qwxzfSsOwvIcPuJ6L>oZ+lzZSdj9JQV{p3>(~&UyY+r$`GaivX)>+W7@Af$ z3@rGCsPUTeWh4GCQ_~Kk3)BRdA*WBq3~^**uHeK#skfK20nN3we6c$QpGKZAg2ky1jjR1!vSn1UqYa(0rWUDyChEk|kA` z>K#WkOz(wS08#x69fgrO9CaNpqyc!I0kOtM$btLg2%nJ8K}W0E$a1+-!v9IRY~cTi zO=XRbN49xN+v>=ht@=*2^^`oUJtgl0Rg6(?MxYY=!v=jQ5=Rcl_=`^&{LOHvnu%eB z|0T*W$_QHm3MF+RKHwPt!H@VK{O1JH!k|E@0VxDv@@FiV1~S3fj_Mw`^P-|qtE}b zFhi#MefDAzD^Sle*}s=&$aEM@$K!o}d4^0!7JIoeL#8ROxQZ>Z=aqXiWO{7#e?Y0> z_yrDP9^M4_MhtNtYleO)8ZBa(1sF7VzPr#bd4B=w*u1p`ENzd1k~NH22a7nf(c!W( z%0-wHJSzpDMaOm)+1w77OZ$P@XOCbuL3qd;f{ywDE4{>)?r^ze1;-#ew2yvt&3O;ugDNrfRF7XVQ#0z274j(`0*KJ{*$3El0P4Lz5 z5m!ekHX1$}?ixPKnfas8_kgvRGlP$~&@)Ie!F-FWPxydfm#rCwE?nCgq-e7%H?<03 z2cKoS6yyLQ6e*h|B+ireNG$y{7Z0U$=?DF;lUS^M-?c%un3jduwkY>#gfK0Yvb#i9 zL^aQ;@-Y)sp_6zrIYMg=;QcExC=^Z4^CP_N3yX&Gf{-Fue-sV!XR!^QE!o+d;uAGrQux1JM4Inn611T2UizCycE@3}zUg{em0dTn%7U~BkWVe294dT@ z`wn&Ob>G`}`Wg@{a#Tu#J_k>W!$BJ6v1K4Uu=c3NsR{US>rJBhnm6Q zThn#=fJ;U25zH(CYk<0L12x_HVPhcyW!Q|eQ{tl;86ic-3rExfq!-jO`O(xW4$aZX z@%m{%5g&DZlJrG<;*~Ue7&|Gu_wXr(5gn!BPJVP-gw@0z7W)CEo_0mC+@a}?VLwIS zvgAiIarm6xYe4PO`-~&HB|j=D5Fber#3x-UGt{DgNP_^viy$s1fFV`5*bf}L>FeOC z2$W=DK8aq06pxAqnP1F2evu?D>{C3>EO2@NX(9xlB0n)T@KXe2ctD;00D!g_4l|2g z-1q&60yWQWRuOGSTK*9ErvkBaoZ zvX8D3F44#g%pr|vpo0nd$*YfTh(nv{ZZsp8pZLfU0gsxZ|11WHPn!0Lj|RioC)0_( zZ4F$(CvU1Rb*TjGe7#tF&omzuuIw=SdZ{#P#6R&;34Qt3izSvO`-vATNu>YmESY9* zT3T4_2mW}p24~xOPX%!73nHkQM-G+KKa3zyou0^Qk8k#g$+Q@zW?n=~{q?hTouLDV z{Zf{@PcK>W->K$`krSa>q1Z;d9a8!U*`Q!pjT)2`oW!$WEQT@%*@@CYaTHK+MHFnH zWH^fK!c?+wi>jC&nHk}$7||i5Z-=!{?$a5SI_U9?ckK#ygVX53k`DKT@HF6k z2}{x@|2Oj^c08NDpaEfFN&~P|dvQ!q@l1RVeR)RyXzZE+F@u1Bf#_U7O)Gj9;y!}R zWgUQ52>8qN(YXtaQ86CUp&2-kwYmAYg$Z$IW$)p51Z-1CdVVw@9X|-bsjCy10FBIV zW{<^%sk0%5v!o8rPP~_pgw!0mGz{Hi)??fE%pjN{Ez`!|ZHN7W3q_0KL*no6Q#dv; z29rx8$|sHNcce)9Ok6Mgl9&@;!|7KH zr$9Vo;)7)Ke>}la?1|!R@m;)23(Q{6iH6VT*o~0g<%Wmg>ipeSb#1NQ*?A5#Qt5BqL6oMba1SWdVdJlYG zdJl-%8jDIq=)^i)ocu5A$p3%9$-n4WkpISrdfcF$499Ndl<>CQ^1-Dn49!0Jhz0%u z>GU87{6J9*p3Sj+i}5wy^SBQz!UsJsED6K154P<622*MTTzYO z{fXGX4LHT5)98f#VCZ?8 zK;{Kz^$Pp*;?@Y?7Ca95)58}%Q; zB=Pb3L7)Grz&20qX`vP?dCK8P5>R#0%IM%~rBY6%)ypB|05>z|nJj}393jHT@0^K% zP88nZ{-mjp)|yC%A9C1%Ur8R#k;6EukF>C(#N~pSIu`!$s8k9F-C}R~i;F|jss@rs zQLt7x0ud4mjgt-1sfSK#0m$*T$GTx1dOEuI`kvTOQor10@Bix zRzMBhOw2w+w801+B`csdq;56c5a1qa8tf+phww(MA%a=PO_NpAEH)O;xNgB6wi5X| zMGaTLkq^Jjk%J8=YzBN3f3!iwoQHI54fHZMn=AnXHNmrL?165e1WuPDY9|<(xJC{G z!OhPl_<3GEVr{TI?P%~X!NB<&g+{1VrX(#m?@msqG0WyR) zeX>^8U0toWs?@t?4NR|3huE3kVO^~X=Y#0-3VN%7?D%G@vA4UizPHBGQ*NwvJ5QS^ zGv11aJ2V{^V%>|{YODG-APWauOK`Y?1%Woy5CWH%78gM7)C@pmyh;uLk~@n5Bv&ju z1>xwQYypA~iWU-U_wCFY2w&Fh9L_Tgk(53;40RQ*Qr84}01NSYaun@{_1$duEoBbh z3PabJn{$TdXqLV}KXecYp{;k4ZHef1`fY)*0AgJ(t*$Pd(13nx9w`m*L50I8GpN)u zw|6>Co|QE_PZd}%EiNy?nBGcG3hV0!B()rqn0yRkF1)RYxsc3A5~qlV1%WERiB&|P zy%`b%v)6aL7s;r3alnU5M@L7=fSCVnZiN0zIVmK*E5&cu=>=wR9J`wvm3y#SDhht0 zJ-ioW8}Yd1Ux1GM1TXE6h9%P-IUa^+NCNAowrwS3gcu)N64}h{xKn&{ai+k~F$LGJ zasE<}7r_ubiGv>3cdAW-^GxsoJgs^h?JKQY5e=q-Gb9;H?U@h3uULOPmJ?A}f+PsX87h$^3E`*RebskcQwGSUTsd3nurjRyIl2!g8;$^$KT`zb zJ;XD~FQ?xQZIf`$oZ2LuC-?brlh-Scjf(TtA1w64XptE>Y`4(k3<)fpcB-O_5^0-(PDj9jh;u)|M$f}Y+ESciO z7s{1;B6=z?-X~50&;U!FJ_#f6v1C49k0a~CdmU#F5m~SVhS8d}=Qw#&;WjelBbM4k9Lnljyh&u=v!lW2N3en716kiu zl{S~?9zIcU+y%d;ki0t%9Q>LibJ!R(^IPwlK|fUc`MI{?KTm!N#ox`ezi)<-)n~C| zC5R%KTP>Fp6Nir|o|z54jX$Co8n=@6daK^-bT{kUH6RA$UE8gX_7BNVXWxG|>VNTUMEhU#o&7IHUjGX#48M35e&JaD7cYkH7f1g7 zK7P%AkX<19V&nugBnxFbJu2qj*LD3qTqdh;h2_!DLD^C{_puK=l$0V+$oqM&8N*~j z4PpO2PfXkl{K)^zn46P{iVg9~52DZJfMJCeRr$fZB{!El%^!Zd#M0-ZNR;zltMQSP4|EvqZ_4& zLv~Z`HRfP%^I?=2c0*KqXzz7A@L3KaULvk?Q&a&h0potft>N#Uq_vzZ{2pg8CkitK zK0;0=N~O>|q-3E?Dx?IOWS1UNmmHY^1ojabIVKB*J2U62Sh%BHZ?UTU$5>GqVU_|L z>$nt9^nAjsx&zZX0CO=iq>%~b ztpUN4EY0;QIb z(UNnJr{>PL5<5;E5D(Z3cx#wGHfLw1bhP1v0Tow0tKEuAN&F7y2`sm;`@K+8be$zsL4hy&qc!# zki_fj)%6Fp?nWJz2iL=)e?Y-~4}@W%%$OJC_%uo#*G8H>wNq_(YAr#nW!pbc+P+*V zm&-7a%gwDi>WNM*x0fy_srF<6a<2fDy~<;m#-#PNcyN6G-qq9h+p?S_!XjY&-#Z~- zygsPoySh8KxC@2K$!T9)#{aR7&}|ArC$?6Qx;8v$g5`u05|h}}9lxN&H6!X-$7D#& za2zljGsxC~hHRGUi5Cz|#SbW6pxRD2a?N8=cSigHFQUNdjge(!!IrH98WGnI!?ej6 zJo0Je9ME9a8pt(LNT*_&p+xkJCpMQLs`29ll{(9I=qc4ITw2AZ7aBSRfN2nbXHE?O zq7x5dTr&vZA1@}+)sI(MR>}u#aBkEw>+uJ)&+mE)pB5ewi(V-bQrrm=pJW+)N4?kajzF7Oj zUtioCt(F^$quqUHWx;;1*Q+fzDn~%Lcw0ixpthx&pOMYz(4T)Y{>c5L=^m|aY(1|& zFWaB5?7#Tv-sk?K`g3UO4K}V;qtQt;_$nXV44;FVwMm`V6ss@v_F_)z-O3d-jK|Jc zLY1}Z`oq0umxV)N2MSxLin0>2)@!U5EM2#`*V;-{QNs+&D%T;q%tmc(Z%b`nox_w! z$m3&jC^b`-nBHS$Gz$<>c6re_SYMm}O?S8+j643;?$V0&{O}`xc<+l}?=c4B^N;Y)j+btrlhii&(U z>I79tnm*$Z8jq5TIAWCQVqG0a`zyoVB93!otqVb=+BLi<62-wxM*#8bym)u)IpF=; zwHzEa)NE{TS3C6vUXX~ovr647!{hKdicYt%`0j3P{iO1u4d~#;##$1=I9NfIuvZxHtQd&P?TG= z-B^E^+;w&5#E`^J07(${XyTnZ+{r%1>%Ly!sczM}+x0bd4C*b?lo>^)H2|duwEeA} zYJD4%#dT{tYqgE;PObBx!Kff#74@$5)nkABVd|3YHZ}o%&)w?A2J)ez>b$a`*V;gq zO7#wOtJF!)O5OXbpdK3deW-FByAfA+q6ZbDFM}2p7YvKSS1mCd6$|5SyM={y;E>q5 zN_ie{-`FtH0HLn1ZjHWyowRDr?WdhaW|@}qmMK{+y;AD25aZQ1v8T><)-hQY;D7di*%)5y-yUe!?VYIoowq0L8slF;gfOrBT#O?TI2B}~yNnEviC>n^o zello12l%+8lB#MVmLaOSGQ?y7P$jD+d0SpVaz4PQRAv`v@di_z@p=V|V$6+$W8Wnw zQX~(t`WKfw?(5`^$2ZvBB3~3CbwJ`$VajcWkX1(-+@>xOc}+-SY57CF9H6~I)Eh*K zwL`W$qh}Fs*~(&>I5Q7wdJWM4zJqwYY<|2VKGjGCNlp`Dk}&Q{s3s)GmiJDO7KlG} z3O|}`G{Ht1q<9(!%4%mM2@dY^q7u;olt64IISYz!)9|KUSKdx)UDc-Pm;H6QAYJyI$KL-ar!4MxNaA}2IGazKxbxXDrpBG>$EmaO}uW1ff!x?#;jJ|<FOT)@GI7@rPVC`)@{my{C*+`P-;uIWp|#a<@rF1fWZ8M}~u!24w!1|Sy+ zFi#6Ri8%XG1~dp4_GDkNj2FLC1i_aEDUH3(R-?YVMSy!G?ON+`t@To%2DwrAf@BxK zpNky;u>+WSIF^yeS6={o4Vt%kk*5HQO9)Va2H!{kNWV!j!ijaqsMtL*J;l+Chv4q2QvlE4z}%^X`p z0P>UwIK8q8rUUTx<((6^U*_%ru5idgZ2604!j=gPAOLEo8*?6gF%D71bQ1mj*SJB@ zUr)HA#q?L7q);`u@jVlw0<-QG6jFSJ4WLk%m@52of)omcTZLav+$j_$iiN_&yM;pG zy+WaIyHJ?8QYc*e)&JYxw@25JoORZfWJ%Yr8P9t%k6d{s;~8-4J|x+)GEPQ*jIGSb z_E`4VlaMui`f7D!?kiaXECZ9U*OUGLS$Z5a5tab`m(*feqv^$syzr zatIt2mi>o)64(UT1F`n}zIt?Z-@dXe&#-%Tk$t-RR(iep``s%B%*4NiZ87s4g z7%P`b>{|JWBDVM|gDp_3MTIE#h+V(!aitdu7U5wtSt3TTJx_T!5x0NRMsl*`MlB2X z+OJibu5aTy+5KYq>2p7c_tWhvb{nDCc%GdjlXWkDOl@^_bzETrS|)fO!4f$pI!`NV zMLY~*I0jfV6j3dhQUxKD;l*QSE zC${TzrLz)P1_PxUt28XfRMR!gKeg$)-sbKz{ZI}>p_Fx>9b*zzF8{-IEjVwt>P$gj z#DVP_V_~m zL!skNykc7ARc|;gGu6Jfp9Fr~Nit;tQ#&(-Z<*O=ij;oSSDA$)7;b?`MM^6oN64*+* z4wnpPR$EC`J+fROVu>9pdA1>=deOTxs_!Lah64e&Z@M-O^LAltqSlB?#H-vOF%S!$ zd988SLq5c2f7#deY=HJu)c%ZN@}*6=)_f;fa{cR?hAB96zvg=W(BM`F6YKUWjJT@{ zYrdlydmH&L09y}fOWRi}Vqt@KM%2Vs7F_&%^}GPUTLbJy-K@k3g=-q@D;B}hb;7q0 z?3J$T2w-f91Nfn{B4fOfF#+Gva$^TB+t;jS6xc1tjuYv?=SED<6y}V1zN`bc&i+Th z{`vdjuPBoWmcH@@hyb0^SHuHnt7c-k)BK+;)ersYDpRW;mP>=kSdS!6VJ*svM1 z$Z9MN>6w6)T0p$zC}Jr;9Gb!@A~U#HmFbD;$r*Z>4ul#FoNT9OHF3fj)IFtRU4RMJ zZAq|{gj2YBt#NRMo&;A!y#(A(XxRE)@Za)!k{8CjlwoMV+tGKxGyU@j$0HbkM(LP9 z%TMJBV}p25k3U0qictV>J-k4$zgG+s<*5P2%2RrP7;O1*Z`AM*V-zJkQ{d2c@6NKZ zJ)Cf72_9a%9n?@B9zdhFr!iX}CXNUgvE#}r_dL8K?sTIVfk-b;LL^IeLME(Dj#H*$ogH&;kz`|RW7#G+R^cIB ztvG9yN5q9yZnBm)TTLrZ%ZHX9SB(XNB<8-(#EJ)+G|$XPlP&;K0Hd>tSbp!+#B^CA<`lG>c0h3>L*eeFrTZk!#7j zfBW%2^qW8Ne=`B(n1Y6ay_;HM3@{icVJNu0+6q>Ecz2Ggn8D!HQwt@dD(-rcO?El* zqEwOLof$X82vB?ky>R+UBHNEUZj2j)N!Bw1wmt|PE0PsHC6@|j5o#DP9CIBdA2hi+ z-b~ZPIm?WZ2yeOe>#Zs?Ymh3;%+-Vmk>TbdxxoY`cI)9JMH({&1f&_XKt{-k0{aft z4%KJqLF%)uPr7@0I;n?Zh(rEjlbaCg*xgQOO{!s&Zc^8*QLqp3eyIohv0^|z9{tlG;Q z{)=&~Gsb9m?^W)+$obO33ilHJl$-3YoCrv*btb@hiX(Qs;ZZc*#Pi{TZ-+NMKS@jx zy~%a)2Kekl$mDIw=wkxwHz^cofTwGRpsYG1&SY$ffHBoP`P!i0AQ~d~_ivKi)ev7M zQ~)U((fX%^3zX4M?kF7Mj~CLqAvbtI*Nr{9Q|*Ru%5E)pJN8T+b>CrxY@O<#EE4mc z(^Rj^G)SL&ixyOqyk+QP=)_TOuoo2^N0rAzo)|=I=y7gf3&Ln{hLTe$s}NfaEB0Km zx8FNBYnsMaFVQ|!dHiG$Cl#D)e#^|`N0qKt1Q0w_KohlEV`c(2Zfc#@k1asJ56qGb z+XspHz9r)PY+q?*kQNF`Q?nyyseQ-!v+}fvu<+0p#kX`L;VaG(zT(Uy*?VLrA_g{- zu*prcI{Nh7@l)seW_8T4>@COG(oB~58nU=s*G6VBd;Z)>dAcKCp;TX#ZgY=RMV&Rn z4U6B9su&YO)9FHvdL*ThF0>cbT=NYbOTHpo=dBY(y!BzlTUq=o{yHBW$++;tk_L^$ zTBjS*UNXGkYcR1;$}t|cNL2p$-EZl<&lGqRs3e6xytNVDh!G04CA!BA`a5h8sb(P! zXi%2|ObM|sxTK`Rn%)Sqhu;Og%t$&Sn2k8~fX1`*sJOZA01M~Ih}ih$zHDsjAB6H1 zi;i7n*P;4Hll>?LE10D!Md_$)P^zaG5iOAs9O42$P8#j`j;LS^Ap~B zMu8%IubgrQ1_S`!0HFQ0;dm|fg0`4!J})Pm5}Rh0h!_{Xwq{$cXuR7B56?{1Co3wh z8}qZ|6?yIqMdG4Zb2EQh0p55fY-~IgI~5{2Jc(Q<*1ric#$2~c0Sb(5d6)lfMGX8o z#lU8-C>DmVEEwd(iNzNexdTQPra0V=*Q_M4;OG$^snjfi^CZv8tmZ|##K|VBJU#C30e_s)a;JUK^O+>VeTj?e(if0&m1>JsbTBW+Plv${vxmM_@!#XLN=s z6;tvY4t0EZ{w{^yQs)+F}3Vch& zs7!o?$rohuU7^eV!XO*wO6Qbj}#>((^ zmB5Hp2dH~|Fzr^rVuK>Pef6@u(+ckb{w&sQ!a*Y(2H|3kV9|4eAWV2vmE{;LIiiZ* z@)Y=V5htqq3T5t!0T)uO=6-Q}uM+s)TICe36cpB|N2T3L}3X}7ovdno{vHc2Pqs<4p0&ywm@clwQ=CHJ}{k2MvEb#~jBpgnetlEBp z^m1m+?~6-}!1~5>7uUw^(A;+rm+9QV+En#u@etC#@8#^6HHuZeg1!*adWnC$dB zcOlbgcAU{)lnvIT8bq<$Ov;cwKq-UG7~ef$23yUTKY9SKzvnkSJw1&p!KU?4_qG2Y z=@etL8VzVjex>hnyu{vVXrFJyhtItW&&@x1`qXomaPIojrDLb(XXj379rx&}?Rj?7 z&X_S8qKzGqV_pSSuq}l1E*O*_B9$Jz_-f!)-mH>ig&0>Fw0SzX4~ySo*j1{7-`YTR z0Ns1<-s#?S%uNfG4KhYt-N>yEx${Ls?(a$AM-^Ex=2jwZw=6fYAUV)2sGhpZD0`Mg z<&jfsD{oXQSjHC*QmVb`BeSYdfqh0R6|FxulyEAQzO=M?e{teWbx!t~sMRNxmH1=ApsvU`& zdRuheis=fftm8~4N@*zen*gs|=%g_l!{f*?YMjEGUez+Ur{tYVrAbUbJ!vSa1g~7P z$L8lw6%=&P7`ieA?a_(UvFB_J&8u1NQI{B4Vk^Xg>ti@(KThm` zfYu)8(jrVxUk~6w+f21eDq-wEXS`0MeLJ5#d-~M5e;{{<#q^3aHQA#inVi%a8{lw+s_HsG%Ra_sZpvCon*(uY|h4G)rqSj6|s;-Sg{Or;^{s0O5? zx-Kt2`fJ#tQT=d3xgU9`G8=|{(4bP;2Zyc!4bV)|(u(vXd-{iC^uz%o}95 zG+h}z^&U?fUggas2>Sp@x8)MZ^ld(Tfzn}~-F9y7oNy&8;nMlhG)NL7ik=yb<00v; z)~nJ6@a4P>Af{OuQv61dkzje1H!V8CFVx+|^Ve9BQL<>gv9gm*Fy^n4JRr_CcR>7f z(fswF)Vhw4rY5ghx=^hG^OP1w5^=a$ePIT>_~g;!l@^belOQxnyRzciq_~n-m0`MR zM!{;#dvsOCoaVWH_o~=$t_mi!WQ80%;q4$=OTDPk^3d*LD=Tc-30E#@%nc@qJYpod zvvge?6R>WpbUr9+fkC>Mf6vbu$Hp)>fHKpAWC$T5JS?b^a_SjHuCGO!sMV)ooS3>o zN6r(qdQF=w3Ob_#492gwn|n#Y{_N}1r(k)_glvrc@NmCn;V3iL4dvA!YK0MxV=!ug zXYhem^SbbA*h3`-T1|89wJQ!fJX1zLFW&<#I%28@qHM)-uDx@W8W2m`Jr_=&J$B~o z@r4E4$)G3tIQJaYex6Sm4Q%szV7xa{w#QIYpm6cnBP2;qYaeKxM zA|Nx)2&aE~QI&}+ZdwMUxP|!3fC=|{DFPvs)^3#2g%ql$7m`9>-Q90iDznUG))}w$ zx4iwxMP>7>M)k7Y=6D%hH+7x-ZH`6G0<7pc6uHj1bf}K$cz){8 z`Ke>whBJ9|ksm!BKhs?Hgx~Tz=V#B(%^o|F^(u@ODi75CtG^rmI>{%bu007!JVNzN zy`T1)ZzNHbHwHW3DT)@H<1tYXZO)D zTY(k%xP#dZpzjd;ifNO!k(H7L_|f>zfv_K$XnG%^^POC-BJpNt#+H3Y#~ z+;@}`u-`;J+-kx)3d(2XdOvwUh2KM28QbGgA}(G`Rfo6Bna+&FIalQH#(WO^s_-9` z&*UzdNiJTgBk#`VlSj*LnO=rNRup)F4_BiFCSGJ00x@bU@7_Fo9D7vzQTfznu!RhG)_(dCFZ1e~7=R$G9 z6zk+8`vd^q+Tdgx(YP`YJt{%))*w>6!a910;o^o~m9la1c!m!x${hoVDCbCF>alck zRd?d1vWwMix`)hRw|JA*FpSC8K;ae_yC56cnqu>sN7I^QbAdKNbcCgIf@SU2WzJ`c zWX@+tgic!&t_05GsoTz@o*l=P)3{S$A?QX9ubkq^Y7i|~=2L^utO8r))!%|mtq6=; z$7d?Dt&Z!@$Z7zE%79dL3`sq5fuAC+PxldU!79FRik-RcYSb0XDV85p#;KJx0w2cQ)YPhmQiAQRZhldR4)rXbaw8Yx$cCWNhah# z*oGc3mT@Yro4!W$$K1g1*8Sx}OSI)a+T%F?TGXkr9Rt*y6=7ctr zA1uvC**aW2iIW@oNvtPrZj#FB) zFa;ut5;%4txR}m*5~xY3H;szPC&7f&1C2mv@_9cEewL^5BPW$nFc$p3&sUX7ZdKDe zco+c6rF(5kj4VCl9S}=mfV`MB6&1EuD#VYQ8uJLUU#A%L5zEm0XlDRix588sN#LT! z89LDtyQV<_t{GGf>oQfIK?OANXATnliPdcN2B%RH7DQ>ZlKrW^`zfX+b4hFP_Ece2 zTD=^stl2)aKFeB!EAZ;^$&Iane^)Ey-R88;0Xau+06# z`}XC6l{)Y6zI~=f@5pPB9UFW2O4nY4S@Pv>xCWDC>|p3h8p^s639xm^>1>hY^rLb& zMdhy0qhzS`rgu7OYSZ}~)1R$5CXpgfd{^Gr{nYl6l0WU;fVhm{$Ks)^O&EBc4QRJy8L=c9~C zn7x|eIhB2e={AE}qU!=##j5lW3|uGR`~o@bC}f!0AskUPl)~6YoUR=r=eKqEjnr31 zUzeYrxY-T>6KSF$=2_j;i8R3g!o?$W3U|Ft=oIkQ3WL-sJFY40)fN~F#>;jb_)~s+ zqEYRvxuHSQL-@UQaz0l?&W}iPM%Idy=gyeMU*w2KI8F?s0H-!$95}IEkGC>#b~ETE z;_%4anTp+NVMCS9pGZ#!ITAOv+>JKRug#q?Hri!UcuzFv48jc2M9+fT$jiWA5&hMt z4;?r#BcAGj|UC^J5_Q--8fl{<%^_55wdk+JzqquM$`z5SR0t3`3F8oK zF6d#0E&*V`JOnpU^iV|HM~ERw4YJ1*H;Xc(i-}}+F~;05<_;Di+*xezWQ>&=NfVzT?(Th3}V|u-rE^t z@05AtjYB%fdxWvl5H6v>wG=NrQ)0uomI8NEjFuTI4{evXRqR0UPCQVF?Ip&_JK<7y zV34uVp(o(`>Dt(c>}C`uT8Da|_-pIy>vzcF@xFuj=-Md4x3Oz`uaL+%EV?hRv2SVHs82koNi?Ip*16n zmDho5G!wOAcj3o9c&K`UAlTguf!V)13&IK-3N*@E#x{(G@f&|fjW>J`qlu6=(LV#* z7%RVk&Y4CXi4;gxKRO~tAV$@KB&vF$%oxLLU?(`DmMWm!sf0?s>r~ z91kBmec{yJH}Bg1@VOHU+;7eEIJVoo@^B|^zxiIq#-5mIdqLCoDw+7+1cjNOKePAE z+ZlUo`+TFaer^nZ;XMW z#>T5|Sc!sef(x$bxc562T_4tS9ULS+y!R~Ptl0}E8W&FNeWy`yxm+#-8`U(2mK$F` z3*d6MRJhB0!RLMwN)>Yaxa_h?*-hYrBS!bB?;#_6oALd4i7}jg7?K1Ix)GfT#vR7A zMS~mY!a(CQ*kI{N19u28b;rXX@W%awyqy*1Ft|Yu^-bk4v_TGy zP35p{gB&LEIgB#Kx?wxATVf2u-vbi=ct61A8@qwi?cx$2H9+pdPw=!IhL#k^;u;1) z#@Jz*0g!$|e&Z^DH_Pw8UteE;LVm9k{CwjJjJ?Fz@6hwNiATT4Sm|?&l|Ii{`5O51 z8uUWjaD;D-rkZAC*{f|F-PthlD5`;x4)1Ih+mA$UtIe&aXHUT$?8e#PJ|kpfrhbmUL({kwa<{Il;H{PN`A zyldB|p8xi*{@K6!`L{g(1HV4?$oKv9h5D7}QKWD!W-YD$ge>wP%9S(GUipn7^ac41 z9vqY3;K2#`J7(jqT zZe{E}7$w->uCM>y`Y5|bgWwJtz<0=roTw9n&*2(`BmAEpuT>s(x>3aa#9OPtj<3yI zm6K~BkE~}aD{ho@ZLjj|iP_4d5nq|9)E;{bRCaSYlo=Zvu3ep)n3@So$Lv~0pba1@(LNSI0doc8^8bh^W3{6hfj$Ew1{bKdlMJOy9 zlamwrH%Shn29-x=Pn=QHXX`OZZ4&~4uX`aHzlRz*{J!-j>)#*!JB-$6@n0LSjTPg6 zs0aT?H2x2cPZY6o5rxhkJu@dU+z=Rk4YJWVtShEgG&*+OI`qfVu$YUo2s$`EF`*H( zhep1a)*1Vyb(Ur5Z9V8RS+TxZbj=+*F0oa>&|AwTMOHmWaD^Ra)}TMGb^%Pg!px(r z{uQ#8!(@>e^@dF(4Pbhho|viOUlD$ul{3Q8W7V^#=PMP3nDJUgP|}tY)9}Ah_GtL% ze7u-owU5?~$a?l^*hp`~s975~?3f!blL>^F?~I0RHF2h4McMZ@OcwQYX})nAV=tB1 zy{zTN%T*CxwucasgWZq*|*X*YZB6ni33NqP7&4$(kxFmf@o(IY%t_Hya&_ubmH{F=3 zLEbTb>U@o`o9d|TX5qwuuOBMWme()$*QBFv3YR-xMx){v$3;T%Ns|!FX2> zF5e-?@{K0hn;DK@&-`za&*k)1$Er9AVyMB@`u;<5!HoBZ*nDFfV?Vo%-Noh`&{kv1 zkTEt2J7wq{Zj)OwjO`kcVIx`C#;^<@RQ^FR9W|{u5f|n7UIB``l)bwevg?lO^ zx5ZU7&>*8%Jll^|IP?llT;Hk!fQDwWRK@nS3PVk0V(f)DeiC^qZg)10U)4E5?I2Sv zwv@8m4DF<$sD@*E+*a8_BM&38YV7dvl4D<@8<}7o6Qq>~j#8i?2~}#eHjRjMu4qUa zE{q~o8faCVrNzM44WLSSLARwq&?2W3*$I!G#kLB)1)<{PS~XsaVV;BX@wdl#?_u+e zzb-NMHzmftUSeYusndnr@q)JND{{SGroaN`C?}`69#+6yD;A&l4-= z3GU8|RpF1xEbx9zs9#5KXN;8oIHfas8)MMGj_zTMc3kiqO>8YWp|EY0IS@W6RJi3n zpu(~1VuW7`Lh2tK4_Y}N}CJDYFZExG3n zjIj}Rjdl@sF;>Ej=XVIsDYG{M7tqkNF~&x(t(RGD1`1lLHeJ8v`kFMb`(#FoQ{5^tsKbo)xE*VT z&F3sotE+Q&F0F{Z6!Wxm@;Qhq>KyWQi@Qyq>mJX?u5iBYK=8^%X zX|fDw3^c1eB;OCq_f`4+4rKRj^2eh{KE&9!%irIO$X7@YEB^kS@%?F#?FcRyja$th z#*ZJt<45)4iwn;! zoSUDi)x`Ia^9%TfCaYGfP2zig;mLEj8W`V;eDdI-si_I;V12r2O*##3O;0Qyw5AvN zl+!pcSzm1OD!iE0s)-%7c|y{dT3=V2Uf?WaWqS`WMwqxuf6@5!;=m2^2HPTUu=438(J`16WDIOJR03o#gJH^} zr#vH^i~tlo#5@Tf(VY$|!pJ}BdO8^SIGxDx{G{soS<%+%L_J}+i5!=0t85`tsW2-- zjFtdJiF9#!oa9^NaVw^?Z+cpU(9pKyFba^L${%xUJF>$i*KdniuAztZL1H2zMreCobj1v0G@04-aSL_zXw z`2_&k3P4WJHVO0rU+ZCzh|E-5(UB^Gq-$4qq#2d#9y2PARuHQ)1ZAlV%>@!Ft-|4V zmA7y;O^fSJH50D3UA<62S=4@O)s1+&Ye%|RPqUeID7GB`BSaq#RTo zfsHh(v#7}$0+}1aq(aMpU|nI_<_$lL|rU-V!eZ_!4qR<|2QD9s&)8=s-+t=3v&Z>! zr#n~Pe)*|p=bKk*%|n;ZAO42Oi{urkR$wV44L{2RP-4O84d+u8X@YQql~7&krNhB> zVQAy_3I!^H!m4d+%@H~=48tl0Ch3GzL4y^;Fbv?FQ=F$#F<^5f5{EBTC@L2wVMd0c z5)qc_ut4`A!m?^AP(w6T?Sx)jjpCKC8rtdP4y`~mRfWwVWD7AcRAW;(0-G5iK^!ht z?I=~eVJgeVXXY1>R}X+6CouX}O)P@(x!Yc9raFY8f+E{*>r8Sr?KayX37D#o`?3|0 zjfF<7>l-uK8w~0 zSz&yf#)HR8#4c+X1^xq6_- z)U1Oy{_w^KtjxD_T_qXNN?@`pljKW{+%VGzIeMY!To2^x4G_4?Qw0>{kY_sCG#Wjc z?QkXZ^k5}>hoZcesGCV?5}OpC84&Ub1t+B4An8VDEaHQ%9uUnTjY>9YHy@c<8RjFY zxe6VUW^}pPwIg~^jiYcbUX+T)*Gz37BB)dpn{;(bEKDN1<#shn3ELD>b$uu37_ui7 zZ?{%j)iC0V?p0MhVVT0^u?H0a)kG9@0$m)YjM#S<7t?tQHHdkXI9h%ryVFiHUb3T9 zAi)F_cbbs7byXp|$DO94?+db6yf28|viAs)1V-E_8adOk4t1CV)_r?6m3nK1Y*a-@2{oR^6j+_&T*nftvLuLd>Gu}G zkdTpB3Gak-lbR|GdhOmEBYU-P3RQ>*fviZi>OOP#b8Uov>IOZ`b&-|37{`$`c8J#aGLr@z1wg+hNq-_>? z;s`VXp;v5)Q^{T5b-bMN2{jPm!o`BBDf+t`#@Gy?KZ1*!J>3lw0?Aj6tviU?D|#^p z2s+r(3N$5vdLW&9IA`LyZOwkbor=h@p_wN3gFtg5_;WQ0jS67qQn@&eo@yaD>R>&f zQT=3HRBI{BbKF%UQz~Dm4Q$8Zu?7ZeWQ1coOB#hn0Iac9s_s`;xUBBHav#$7{Ox{k1`(H@z{X}4l7{< z9KAR|qYk~5;lmyf+}8%d6y>2eVE1H{u@{iyyYTpKJYK}(U3k13kMF_bpWy-fZjdT@ zFYAy}@2-LqR|bY!Ze*x9DPncCN`q6?PP-68NE(sd_#@iUi8A9b_55nXa%_-^>IdKu ze|VF*!6(oUeI}FlTNr8R~&wmW{av3#K6CKAp*@WwR;@?Nu?R ziW!S+om<>(iowb>1;ApU7@`_+M;lL|TFoR{jIFralm|3b;g|y3>DnBfL^%enW;doD zRFI@LL*qnL%(V!tVw#ECU{Yxysvu1>sfIjq!zELlhklijc58)42`-k@Ng-hA*h_H| zbsf5rP-U%Z+Jv=)?fXVjqdr3H$|SNw_zA@2+nMHp2E8D%t4R>1^B($!LdAMiX9NPE zaGxh`PCbWAAd&N~UerK#pzyX<8lh9Pmvwnpnra&wkj8O;MaN`Qb%L&+)-@ZK9n*eZ zW{o#oP*0RtsZnBMcT{ONPCANswIXyKw%RLMjJ1&vHQv8|4g+S@irVPtXcK+7U=^e zsxb|l7Ja`!oIizcTt&ph24;!~QeSCQh$+wyAr=7oVcA4XZ|eIbebWhYWlJH3RXWIn zqqlUHuJmv}m;u0i93~V481!1*7VlX(Vf^TZe%RqHSF27y+&ZvlS_bh(JPy1S9#!p@ zsU@Z5j*40gBD!5ZBKwxjC=Nl!&|T7^K&!lgSPE^?45`Dxp9q(FyxsxZi0s&o#yv>Q zh{_mO<>E537YG~aI^EcHGP)o%JB_&##1@@dmVK%fjv^tVTd@;b9a}?Wv+f$kSJ{|O z-QN&oh4R;e(V(ZxhB0vtv^(9nf7@}bS&%vq8OfZmm4yZMe&0z*`OPTAykv*zQY%{z^sME^fk!tG71x7cJZ@v6 zRS?+{#PM#0=AIEBden~bzTNd-i7B@lC;t9w*c z3^BQ_Gt333Gop;VR5N8TF^Ou6B1sjO?u}uTQJE!IJ1O0nrG)AJmV$@XXXldL)oW@D z1aT+M@ieCUZf8o>L;O_7qA4?&LDD`jR?i9!g)2KtFC}v@tqP8#FuxefN4Em#9P}W9 zNfDBjAwMETR_fx;MnYL;1%M$plelFrIzT>|SllhB+K0Z%L>fx)o;agI5K_>DPS;CZ z3qIY#rO2>nLnlBLligp!gdxyoWJOa#s>S9jp{||OH7Nx*>4eQKe$-IgROYrPQC7Sb zH>_4O-DRcq(`q&h97JJZXf=Z%iQ!Dyf>Q;Bq#pc+rdk&(Z;e9ee}bNg5?w&1f5ONR z?Md}BsHk)}5o?|Y-5J{#swO}yj)Z4roKYb&(0jBr-}rXMUK(I`GD8R%CzG%7s9R+) zJ$q?@y`eCAW~x@JRqr?BdgK@21>@E~#Wpw&Ff&$KW~}rgW2Nt441N@z`A+fPei@Pm z7G@(J)?d%#occCA^B2SqS}+5$z+;Wo&G}YKk3(u{JG(omrLJ@ft#_ z5f6j$HM`@rHK-3k@|1q{a-eOO(FaD9$^KktbgRKnp4Q-VA z-e#y8vwsPJjy8}0;DIQ&ScZ@3>&-?PRyToI^nh@a|v&iH=H_K4pBr z-}wFjW9&iM_7582FzZ+O|BUhdVdMMfjPH*ac=!e5`=iG9FB;#!Y<&L;&gIKGe9Q=k z4XX5T`&W%`xJ|)M75;wRXyb1fB0Suzs5bs4qdUIn@3)L{Kg$@*9qI44 zjr?Ip3p&8x=M4DIGe$Ilzuz(7zhJ=st^xl=X@`ry-!tI9WWfKv0smzK{tpaz_%BfP z`9lN#j|})fHsCLefX?aIhO~jlas(FLWLbNU8HEED!mme3g%4qsvkT$mU}#$=d^f^( zVP&%|Qv^2QN8zNQH|kzQ&8R4q8yV-{hun4}i7uTS>%fyk`A&r2r{Jkp@cdA|i^4Od zW4nK}G9XIVNvMd&kmN@)MNoJZ;Xj7($5as%jwNLI1HIwX2*=sRw5kOEW`yrY_;e;8 zIDaYEd&3VS{Fi$3nML@=5Pmq5&k2P8T5tGCgny-7LpUdPArxxV{ zz3`_HK8^6FGw|<6xI^#{nuPQLgmZ+ye+aHP>X}p@MC==TOZgDO(Q)MuX5fDY;SVGH zLz(aoBm7_V=JRt1|6YWDI71j80b$%CX)hb~3y8W+Mtvlc>Z6GImolo9iTXuEospFG z(M;4YBkFr)s*mEr2DpqOBKZ|WeZP$QWx~%yChf-n@JEGdKaQwBmQf$0w2e&KPXJ(8 z0%U9ZGNK^h05hM+r1~VHzE3tJo9b5)^#d~Mlkm8w8}bV9yolUBfUSsu`nVzj;@e+G zbQ{sH2qJ)n1c@p3HxRp|V@HmYqo-D;gFge9%LIe_EOf!YNp+Gf`4w5V!ZGFkTY&gM z3Gthvc--Hji~lSD-fIGEbN#TJWYF^40Qw;Xl&Qk2y;W#rtMECq3a^SP>@%zId9w-x zFd}s|nM(YQS&3IOmH0w$B_^_!_+7ISUl5hRg>)JZUoOV*veY~hIejgBjA|bw1P=_xA;7?6};W%=9JKGt50H{AxP?rMC{b&iF&K4qqv# z1K3U1fdGbEagwhCI8WE%D-b#D=evo`g>0Xvx z5aeIzz(q>z;gN=vF?Ly0g5H5RuWY{69btl#hvH>LTW8H}` zS~i*yYE^4{uC4V3z(E-HZf!$(5b+fr#T<&Qt7~M$qLTrc!pI=MM#gedEKa(9OP7!3 Qt_USv+@eKRkg9h78=_p*&;S4c literal 0 HcmV?d00001 diff --git a/MikroNUC/20260701/20260701002001-Config.rsc b/MikroNUC/20260701/20260701002001-Config.rsc new file mode 100644 index 0000000..7a56a8d --- /dev/null +++ b/MikroNUC/20260701/20260701002001-Config.rsc @@ -0,0 +1,4986 @@ +# 2026-07-01 00:20:02 by RouterOS 7.23.1 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-LAN +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/interface veth +add address=172.20.0.2/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ + gateway=172.20.0.1 gateway6="" name=veth-awg2wg +/interface wireguard +add disabled=yes listen-port=12430 mtu=1360 name=wg-awg2wg +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n#\ + \n#root.mnuc.backuper\ + \n\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n#:local Script AM-GlobalConfig;\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ + \n:global MkDir;\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +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=":\ + local image \"https://ros.vados.ru/amneziawg/img/awg-proxy-amd64-7.20-Dock\ + er.tar.gz\";\ + \n:global " +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=ether1 +add bridge=Bridge-LAN interface=*104 +add bridge=Bridge-LAN interface=*103 +add bridge=Bridge-LAN interface=5G-MikroGig-1 +add bridge=*107 interface=*108 +add bridge=*109 interface=*10A +add bridge=*10C interface=*10D +add bridge=*10E interface=*10F +add bridge=*110 interface=*111 +add bridge=*112 interface=*113 +add bridge=*114 interface=*115 +add bridge=*116 interface=*117 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0,::/0 endpoint-address=172.20.0.2 endpoint-port=\ + 51820 interface=wg-awg2wg name=awg2wg persistent-keepalive=45s \ + public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/ip address +add address=10.30.30.10/24 interface=ether1 network=10.30.30.0 +add address=172.20.0.1/30 interface=veth-awg2wg network=172.20.0.0 +add address=10.9.0.3 interface=wg-awg2wg network=10.9.0.3 +/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 +add comment=Monitor-AWG2WG distance=1 dst-address=9.9.9.9/32 gateway=\ + wg-awg2wg +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +add comment=Monitor-AWG2WG down-script=\ + "/ip route disable [find comment=\"Route-AWG2WG\"]" host=9.9.9.9 \ + interval=10s packet-count=5 thr-avg=0ms thr-loss-count=4 timeout=5s type=\ + icmp up-script="/ip route enable [find comment=\"Route-AWG2WG\"]" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260701/20260701002001-Diff-Config.rsc b/MikroNUC/history/20260701/20260701002001-Diff-Config.rsc new file mode 100644 index 0000000..5be8e3c --- /dev/null +++ b/MikroNUC/history/20260701/20260701002001-Diff-Config.rsc @@ -0,0 +1,6 @@ +4764,4768d4763 +< add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ +< local image \"https://ros.vados.ru/amneziawg/img/awg-proxy-amd64-7.20-Dock\ +< er.tar.gz\";\ +< \n:global " -- 2.52.0