From 4d7ab3d83864f5e3621b8ecb7bd4c7044c7deb40 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Tue, 30 Jun 2026 23:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 30 23:40:03. --- MikroMaster/tmp/Diff-Config.rsc | 4 - .../20260630/20260630234001-Binary.backup | Bin 0 -> 366019 bytes MikroNUC/20260630/20260630234001-Config.rsc | 4981 +++++++++++++++++ .../20260630/20260630234001-Diff-Config.rsc | 6 + 4 files changed, 4987 insertions(+), 4 deletions(-) delete mode 100644 MikroMaster/tmp/Diff-Config.rsc create mode 100644 MikroNUC/20260630/20260630234001-Binary.backup create mode 100644 MikroNUC/20260630/20260630234001-Config.rsc create mode 100644 MikroNUC/history/20260630/20260630234001-Diff-Config.rsc diff --git a/MikroMaster/tmp/Diff-Config.rsc b/MikroMaster/tmp/Diff-Config.rsc deleted file mode 100644 index 604a75f..0000000 --- a/MikroMaster/tmp/Diff-Config.rsc +++ /dev/null @@ -1,4 +0,0 @@ -2681c2681 -< doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ ---- -> doh-max-server-connections=20 servers=109.107.177.79,10.30.30.33,8.8.4.4 \ diff --git a/MikroNUC/20260630/20260630234001-Binary.backup b/MikroNUC/20260630/20260630234001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..6d0b55a12886da062d0442bb9fc29577bb285864 GIT binary patch literal 366019 zcmeEv2UrtJ7xrutAfZ>WVrYUTu<414(h;RevzJtelwcA-6a}$iL+rix-aA;Xy*KQ= zckErs|D4SRP|@rCpL@UW{{QDbx_h$B%$_+jbIv>G%GE%4i%v#7&1qZ92!D&gb)Qo2s1+!m^rFI7^+|(6I4Oh>-BmDs&GZNsKS+&CQ+r^ z5iC^Dp->Y@=3uCzMxCisr6>7jN|Q2tWy&N=vaZd^2g9{FisV?K3d{lW!85Q1sG_lR zwVUu5Gr2-3$xPK%r!C0Z$rV{DnL^u!EIf@YoPrSgLeh<@(ID+@AaR6HLkyvvWEv*i zC+TS>2&JJ&PBOwUf+q}Nj0g(C7?zd@W1tprGDVmxB~?vFSY!W44v0vOFgld~h>94P zGYv7YS~SE!wP}c9Qiq1<%`k*$G{iPFJmw*_lKfKbVH9{6MnM=|?M6eE2%~@2>-Ck7 zG*#%56&kiJ!d&fT0=Y!Y7xGy`fmq7o@gyP~VXk&cyt$CyoG)+ARy5};n~Qji8t($< zgCaSWG*pPtYbwGJ4IwK8U7!NJq9W`)6*15UIDLeZ5AX$bykde3b*!o$QCnjj?V&B} z(GY|B5+vaQLU=<(3<^>C=b7DHdJHBiZy>8zzR|B&UaaJE1-OFClFE4s7Ej5O)L40b zjg{yBH!DA+s`5j&VU^vC4qX%KZwZa>TYy31SL-!QH~IUl7tY$)E3m11KdjiO_T~h(;?qLgnGjr z4J6gVNrwSb@TU3aw=*}iR3&nTpm$TWAjK3Sv>Zv zO66>;#y;piU_Z15701X<`kHe?pEE}lm>CRg;55brp~qB2FT#n&b8QwCpiWPYjmr!T z*Mw$7wTsvCJ90VUI$2g|W;8oR)iyzu)V{M+%T$}NN+T=tfOQW@74%as% zCn`88GqPjbj`F1B2u?URA*XN0m~2fzRAN#atsiMXrAoQcsDR<2k-8hMG!)4J<0;iD zG+9Qg^KAmSFH4i8tCSL%LZ(at6A5+!j0GHEK-@?gj7uZZQc(;>k!U8;I2igTVHDj^ zBnNUD#}=djJ4y_0A_W*nd9p0SSC*<$r0aU9Gju9-x)zN3YrS3{Mn3Y5Uat=z>9>Rd zFdTajLSZB;P$dji&>LU?fFN^@;xc&19;L!POU z2YgcuJhHMFJG>9ht}F(|OJyl^V<|1ZDFFC{0xW?K3lHx$(Q)xn0g)lqvL>swx^zjJ z0-7gDqt47|Ns66E)(?coCusmfY?6k$3P>6<3rX4xLx@+!2L_7D&@Q0D(kmEH3cVQE z0?>jXhByxUDc_@&loXlfeJ&#GYMx|4*Ysy50Oci?KL5yloQD&y53Je(t?khQ? zL53?bLUGgGW>m{#DwW7mGBboVmj=>_`gjb$s-9$)CP|khDKvoDQe|?TI#ZSm zy&I0uYS&N{$w@~D>x~f7B7>pX21d+Av!du6+#TEH91Ia;4gMyNx*O+ zM>xS4MNUAu!ifSW=rmxHY9ZuGu`*|XWdZX-O;smVb|lj@MVeZZQ~Zx`gni8s_DdYm zlCxUZZSmmA!LsT1@eE{hiG4O zM5}=#>nw>}t@S6)KAOz$dX$MGO`=MLAq1mFlbNc}8fS>r6{L}dabl!loEYho1Wj0? zLc*#QV#HX3aY83iAI;eHL1nr^QLcm{rJ$-jP=1YhC_l~v&C{-Pq$QZIn` zDe!}a5KJ!QX{1tyNd`)}r+Nr6%jx9n*;mU+A zYRJEIg1r+E=4Ps?ovK=%8wJU^P0LFl>_a$ZGc@s?p)q=r+}8?`pJHHQJ*Z z?Ou(xCS5kYGZmQ%T~3BV+lL&<@uWuvlt(v`hLL5YK|%B+?*sblO41+odcBmSKk4;) zIZ1=dL`KqI^m=_NNdr(ZmRVwjP&^jN89@bww~K<%P%1)Ws0bBP4eH2)im*3Uif*71qilH0rpdk93p%~gpLG(fggmzO5*<7iJ4sv#*8vgE1HT)f*+LY!9c~TAO z8bLZ5LSAr1h~iB(a0(`gG7Q`|g>ZtN$QcgQNV33D@MAQbil~TTHV&xqaAIMI(b5O% z=nM70F~o3WV+L*~LV_XGs@?}M$*3i}{p~WP{LjkNmhkqaD|9T<1N!$1m-_D$F6}?l zVQsTj8bzu?t2GL@=ilCQY5zXqQvWl;be7pYb@-_MQ$@f3hd2N!_ zI&Fr!QZY#X4%uSWEEyea>%ULDCjXi4Yo8(O&C1M3RZHYX?I8al>D6T{`#WS@9gh*gvwxqA&HgjJ*g>71n!`$#Xyn-vjeu+Wl=Ks(P!-@pKENz-3mGF{fs5QDCscKn@F?y3n zPKRM4X{0L%I+DXA7C6*HksKG8JvW3C9U)h9SVQ2e)8)kB^+>M?IL8d31XG~EpG+Y< z7KHl*`@Rh^`&bOD0P z3J$okNEaZiUmzj?l}r$^AXvBpUOJO39UCXYe_B;q52G*5=+Ns-76{kQD3Sv{3s@-w z;ROiYLG+R69pE4`tc5`gkQjpsfd<0=(}(mlA_n151ZG^pZQ)8+sx)b!iou_esa0r< z(XS%%sURF94Qa-b_rd3342VGB!x$(zL4ov<99x8z7<_s45dr~Jbzb0&2v0$~P#Pmd z-{r+XO)47++@Ms#)#-{5+ckUb_|V2BpO0S4_pasEBrmCJ(Wx)BeD>EXK6$0fL+**~ z7!x73ZEW4}XRBX2ELs{p@t|;Ie^&^rnMmYms`MJU0saYJYfao7ilbF>1M!;b+_aJ8 zx)pVweIun##ko37#rB|+H) zH-wLU>@Ua$VuVDW1=;v#*CS*@40H(8!3wJiSA%Tgr`b%*a{M$Tta#W$UeU!_?SkD} zuf1!z`|z}rZyc^a8)M+6Pxpq8Tef-Z-15YNqMesKEP)$(q$)+Dj!FoESjcyc^^JHS zG+5bKUoiwRKnAUfp?;feht?bL<)*MxMAt`h)*PB|X#=l8U2(bmXA=~y+ z{~@{nC;QyRabbfSC#seMLyR3Ltcqg7(D0Q= zB*sB$3Zo?nUw{3Pl^7!ty+xPtm{sq96O;X!tA#~MynaDu1> z40Bg-_ql>RTtP6E9yy|UK#@UoP|nh(Nm(FaqZz14hZ@%}%(X!Vn@5x}$A$=lR=AoZ zWh%5fkZe_v+GL5Q(zFDTl7(v!isY17fRlAQafY%`RET(#I+t@YLQ>D`meZCuR&wK39Q3yd__Pa_R> z!AMtM2>~+VkmW(7%1E!mAU&+8L|~nWg)3mmhz<-$2txOU5E(?z7??HugvKytgMivs z*&u@SZ8l0}HjI4QciAX4WP`}O;c1i~WutzbjY1gP%52mhWuw)QjaHeB_M>bjHDoiX z%x1zdNbUra#JI_M;qT(5eK=F~)w4oejE#k%mM@8k*inLmue04H2TV z2BoQt^u-H7y^QpkxZq0qaCC4l=u$F;CJkT(bU{xjoWyEz(Nhx8K~f1YM)G!*Oap)3O@^&NCtV?F09bvY-y%eWa>(RJr2~EI(;&Qw1{rW6fzlwnhlXrUHbaP>=ayIm zt6L2C5LP7?)<*~y9QO2t{trN(0#P>V5QOM+Auxmx{Rjo2VQ`aJgbWX5bqcWodt`wS zeig$Du#0#B)^iS(f>9`TXaOZ<-m}zPW*Kcjcg3Rd`M9|$y~)rv$NgwjD(=I!z!|30 z1e&P>B_WP&gO5ZuMtio?gnI2R|5&dydm_{rgL`C`7f z^ItA63%4~jBeFE#nR)^fDwS$MS-#m#rf3V}ZHE&&+pYrx_m+cc~smtqlTq$MxiDb=8-b878?K*o< zKH7d`}f+cU?HatJ#Z+7Q+#;g>D0#vk)ShLpCP^J@9wa z|Nq|fV_=AnLdex$!R9HIGKrio#c>W_$YtZWKqis!xDqypE#YwFB86Cp<4T@VDwZil ze2x<5NQAgZBH_#V0)b!zLhKs5^hkI_AB0?)5+2SGNF^MFQXmt`IJlgL^Mne%Lco^@ z#9R^H8&Dmo@YJv(5A;PUtWisdi(?p+B#l&()+pL9Se2v-QfPF{@N}62icav$&d&Cc ztCCbQg+}KiQ>WDh4O*EhELAO)q%w&I1O@zpM%^730va`cZ6{q4XjtO?hYAJ-lhdPZt&=$8cWi%PuX6QRB3^Or7xa4a+>x4TP^sJ@vfgl)m9o!ji zTR7N$D;_ozcQL49=gJ&k3kg&+E?~3S2F(nrnai&B^FP5t0p?b{;+P4d7GO3AFTl(x z1sH~IwYX`gxwd4%nfV=-#mpOHkzIUf-okSGx^?E&x`#zlBb7WbM4b2DdoV%=TME_PNqI;xfb)VBtICj<*w|i;F9qShLI#O|L zy`$XuK$pg|I6GgKe>`ImShjp~0Oyir*|YZp&u$;xvGvA!55m3YWn5y@uD)AW>t%#_ zl-ZJ}ZT-4$%$r}lXSElFiXm*i43~UkdxrkZ_E4|I@Doo(Y*#*?f2&@-J(Jh@ol3wP zf@yN6)y8Y(*PP(%{znDrADJOs*2cl!{NvT&8PTz8USD5Ddy|txpYKuobGxR4cd@pf zJy7v_cf*cHwxrGItGhp?@p8M3w=?F8r<6F#DAU3-?dq^%!m5a{t4#6DOu!1KSA-qSAKmjteo`&ff#tpq{_cDZdBw z?*aYq9ne*Xh{xrLB)CG%b!sBseLYysN6NsoLMGJ_i#y=1!2^Vpt3Y;$x<2(*eAZ1I~LcTyE6Z6GlwoJ&A z0809iz0N?$^*b{UQ0gm4Ktif%7JnksGd-yBmqarw)h0Ztj0?a@as^Nk%uUHY$UKfd=w>s|p9y723L?Dui|z8xNTMdGu) zuli5E?fAwq^2lP_m$e=(cYDruvy-Tb8*S@zadN}k{gulzgC}YauFya0WM56f5uyT4BozHbHDLs%~lUf$>Kgm39;)s&APMoOAKwdvW<`YO~rzz>|WS}^| z0FA1zI$dIkBK+cHYMo9im!vAJP_$o|I#?o?=YYSHhQiZjK25>v8K!0uw@G)WLZeeD zRWgZA!3@aMC95?mT@I7=YnWX=qb(6wa(<9k9kbH`v!{C37+V52Xbph9S(utdaK!>p z!K%g;tCA~L80QyWL-2Co$z#zS-wjwKDU??fc#Rz~7{cS~|MG;m)SkOR0lC&f77o z9?bo;rfJ-QaZ*)#PZLSq;DMu>Ee)FFetkvrfzzxT@2kJ`VKp630v-=&FZ|M>G`S{*?)Rv6L z;q0!e(DgUM3}7EZdEbTZAZ}SbwpSI(Yd&p=nQfByre|+UO#;%HyC+<-x%N#bC7|lm z%iVI$ewf@Ti7I(9poJ+rlQoxF6s;Jx;oYJcB^Mt0p1j<-{Df|lc7rJJl1ab!l*}z( z7Op+N>h1Y!4oBi_2aE_kHTmOapQd)pi`RJNe|Rvv^F!h6<#(>9t{>t&)9<23bk11* zxgNB~`Gb~Eqp&vGzP?a=fTs0-s_~q&boP^3%aiS=cGy40zByw-Y48_QC*khtcR2H( ziXL2$ziKwX@iXPo>|=VfWarCn#UfVU(Wl~HsBZV*9`5Vc&;0h`ydL7aw=%cw%c{*i z-t4(=#L9PT?;np|vUhshc+-7({m;^EZq=_C;Jjr;_qS!W)&(&vBi}Ag*)Vmm;&9)> zTepIDbba@z{u?6F==^|KyGleFSBa-o#Z#l>u;5FA{%NURVj_!0qE!4dMrayeHblZe@3kwPvZa9T~; zwKA$q!HKx>FPmw=T;JRKbXX~01r5?55=~+U(p%c+s{nLNL_HMkmyoXNtI(;L@d{aT zx*8TWDYVQup8y|Mh(-gv$xLHbp&a-;LM6_r4=9aH60gbB>J)NjltP!S)};JWbb7e6 zz74>#;q1#9)nOR}U|CC-AHuS4qtguyJg`U}SR@aupnz{+*iL|9+wlU*d;<)l(t>wY zw{Z}W9#9R3#YC{(aHc`0)%l^M*j~6Nq_J>t`MJD|g;5>q{7`BH1QW4w4u{Rbg%v;!`9S*~%NKAK zNrr7ow;6gGr%Zl-Ue#f4ZE?3=@|^uxrxh*PZAH1KX{XzHKVbG~VK(n*r1tWwRnZ3$ z&vsrsY52!i%ahC8hj#Kh!SE_sk**n3AJ<&JG4J8&o4U+8rA=n_bBd?cJ0B!onc%(e zR^9EdcimYf3g;fp8ZuiPa(cn6m-eIDzt6qH%6qe-$KzVF*RoD+J32NnJHqo&u}FRS z`O!~BE|WX8I@>eqo{9I<@EnJB+uB_A{h&D`K3vi80#Ea1gZ~LOw&%$;JC<{|hFfAz zb+5BnbMj+p5^-tIg0bbE2ht--?3%y-{m zvl>FxinU!MCr}U(NX9-sWRzlf7FLXd7+kkM_D|Q6M{6 ze$7&t6VSzN-I{3uT^D8CC_9;b#QhAqk(%gxQ)Oj!aoJASfiV|N7cS9H6rNmGacljX zsjQH}Bkl$=&g&???}lHP`mn^SENt{PtI+z{Cma{l-@1UkcZRo)%F}b}G+Ox3O1HVy zye7?8UcI^MWk8q1QxXShGwuwndzu}v&36aOp>vix>Os^%>(*VUZgcE+C_CD< zJwn~Pp-B_gBwkfvb<(aW0@{G^hA7m`Q_QuEW9ZE#%uj2-v97mMnYko&~ux4 zZw4F}PrE33(f(a@GXP(pEsTCb&>%+e^?PvtR9XL$^7%cue-G~e+TgB&TznZ{#1n|* ze7=$|Qt-q=g^hIUujQUOz-$#>LjvBG^qUSz^V;1ypm0snqFJF0pTb*ew&hz~mZU zK;2J9udy#__~-eD+b^kCBDdyKS=Ei7r3u0Z7FL6032{be7~R3VN|0krGHAZnwvBR)o>!D09s5e;|-z|@*&mEsUe;rccw*Iki(dmmJ5|iDN)l0H8V@8e=tlnv5 za=iEGdyCW!Qy2A??z?bukjC;wlHG9284eK|-u05Ry{|9pef8$n)KM+%yD2V}cCg0Q(PnfMq#qFqpNwQ01o!*GYjyrSDSv2PIigu-@7E4`{%Q{U6CK#!)(r<{|RPu zhWBPIh^d^LVJi2{D_%c-Z?=JP%`MBYAlb0WA&mRY{4I!r7f@Q`1=v1v{$`SpSn{&_ z0zQAw0-LkTLp0T20{zF|unOZ6lQJ$2^Dqv)=LHKF;0K@l`JZ6n2e0g2U3_u-h%FYM zEb?4WKi{!K`-RL!+jg^TB=Yz#V{bj4jytv~-(t6z!p53Ppp*I#b4 z;KST~7gh-Zj@rpo(#9q2_E&E$YBItV6gFB}@-nrxecMwk@g8%=WbNxh!Kgy7?e_{t z#|&94`!I`hO*!7?#nH8lF;`QUe@w}nL&3$$E`P{JZyzLpr{oTR- zY6q*bKwP#&E)eiUN)a39vAH6dkSkJh#R>_J%fZ=PE?3M|a3yS!OonqfVu3;_7Roph znOLC|2)S%IPFSFtMou*g#OL$*0;x>N;X|=}E?>r#@NlJ^FOl;2Le7uhc?skGrFUMg z{_4uQ+9=vDPAAbQRf<&kR{>tbBD-ebP^?Nobh9dt;@`K*&hoFXvSU@U^_P~}-Gun= zRlI;gnxz_+*^T+ykNAtr>^yOIgAcKOC1YO+lw5Jg!Eu8tv2j(hKN0JnFS+~Iar|dN zqSB|ksh{7D`Ak`>?lQa0#hE#`=53}*5^L`X+jRc0AKzzq(7U;+>DNZo{o=r!u^~L0 z(>U?V)+eK`x_#7K@sdm~f7^NRlk_@Uhc~-2xXW4Hr<5N1hA*^@9UhwSdP`>gdwF-_ zSG9Yepy@!V^WM34r^hbdX1aYd+w4f)@Or_TS8`#_xLr>|yXW0Zi;Ix1KGx7ew!Qhx zPoHzp6p8LcqXK`~t96=Qb*!cyF0;-&D*QBZ`0h?Gqgy(ulL_5#dXG<&c4Pc8XGz(bOwkKoubFpdj~Lph{k>+V_OsF_ zGTV;Bo@^PB)Q^2D$!F9=Y9pVZnQhr`tv6b3-+W7$=e7;ax9%7FDP?<$y=QXTM;$AE zon%($V9U_HR#^Xi9q*^z8-hDe^Vn;9gZA#m%e=B>=K8<}!lx7N$~)OJvqfh(twaMe zCuu(>k6C-? zYnDpDYs|D?kDJ^X(U|Nq+XuYzqp&QXdLY@8z&bCpsB zhs|g6IWn%4BbVbciBzs&%LF0?pUW0YMO-nP&y!1~LM5MobTw(+N=Qd8%lma(4z7cr z0)#(bqPN>N^2hHW8$N6ZAG@XJt*ZFyO$IB#ShXaE4QnVT5Tc){eN=TCzWoT_%}Rrp zk%;z^$bcwTPymb14&?Ad__#0_7uq0%FQL6)4{70BZ_IsbQ~M&f z+?_l9s2?LQQ8tw}8f`gnz=)4bd*0Q@CNG9BI_B^E;?VH|hfjjdsi}_}9*fu)?pc&J zwfl0(uEWFnKKxQ@m)3oqxV+=RcAeMvO&emxeEF98s^1z}yf9{u>(vHB`P@aC39pWw zh)vnKdzs%K(+l(W_aEZRr+O3Blh?AZn7VkHt(f!> zdy##v+sfV)Me&A`?m-vhH%!ZRnc^RI_(DnVF&|bc&RNY^opt~8r;Y~%FFRQ_sjIJe zAG1g`^qJ>t8B-E5BXQ^0sRy^1m507f7};a#T*VjXUB_}CXQ!evlUTQCx111O-;s*b zvzNTg?a#e-F4z2OY=@GfM%SZHI<+`aYma;9w(b77C6Z*2WP$OkUQjZX?g0%h3&R2FP!^jYzxz^WwHfN zMrCw=%Br{J>@Jg)?BM-Mi-SRD3wG@&ZC`Ie@qrI(VjXS-Qb(1Zz5eDhDt@UdclNqw z^6c|@)5I_8!!7Ih&K(!hUfy{9hq!60_l}vkH8^k14(_SQL+kfkpWyjw)MC41121QI_6T=$GREr0rSyQi65 z%*S5PHQ4w5{gmDP!_!r^KDL3E_M@Q^pX>ym-is|Yma~>PjA*s^jz_AtmE*ic0SS!G zrUyH&V_j~cetofL%xC)3Gj7*s6%Sbx#aKM)nEkNlmXqGNZ5mmTx7ueQZCVe9)qm`Y z{=jyzcwN^zXY%E#SC;o1cdX@^0W{(L%t!5R&rCTyc_TjY5eim04{H8tqV}!z%)5%? zpQk=ML0>ISJ8D)ibVj^Jki&Hh)oq$JfVL#xmNRTstTL_53(Lb%5^0$0mdD&dXVI75 zPIV{ollOTYE302R($ATecwsZE*Om31Q^)GZZz~_^o5&c*yPVN}cWF3dfz!gfLANv4 z9XU|wlfK03^r_PgG4pzTOZ11_MISsO@6P2V2RJ%k+57PDgk{-(bj^**iE%2ow((or zM3l6W>eXTL{K2zx+DDEPls-;-W;Z&Ab-`WRBzNGa<FWs;Z^SR><@ofOuxN-&*p7a$6D|GP^i7~X+rRXd6my!HAGqb*(Ak3m zmWItpb1_}s(V^D)ItF$PTyiHFzV>Gc z6G1v&fIT<(C+SYNV`2)!T95q(QsZuwACR*Ak@kZbnh;hU8D5HF8-0r+oXh6n!p^@` z#-D+vEwM2jn{9fudE^_s<9M%Ou3-hHqt4x5M6+G)x_S-RF$OEzjbXYp2Bc#67N4|5dLB^OGMR9CC2m`tFo7%HHx;!M-DhTS-hj z7MbX~+Fc8O#a&QKYcrpIc4r@_lMy;iS(ryX?U%%!k8SaU!7BtuvML-{i?=lWw6o1| zLEN~WN3ODq3WJXHpw;i&I)6cvJNJrKm33(}v`z84cSA0{bG-QYOG?o3nGbe;>OMVV z{gtcDcYo*`a&~&zUw=b7gvscQ~f#yy$IyX}E~D@H!|EjW=*pDQq>EYEYnR&9RYap9Gj?kjfD^loq7l&%fJ29Kb)Ee)>o z{6fdqVsrDi(|0?%(4HLDy&3c_z*T48;Js<~moXRi$F=HqUj0Yn6OT6TZ@fnAwmtl? zUeGR0i&h*D*SdRK_E`KTY{%h&i?mHV_LTHL?)CW2li|CE)^^~9mFj2BTv9G-dD4F? z`ZDj!slbx!t@o^XxT3?v$*W}RthGJbEgdhOuydzuO5#BEr^bsN%b(gUZ=?Ovtr7j* zl(^fT0>LWmk1p)?>?*6ysIa=)5mJ_sprae<5ueVP!@B;pCVp<*@>%KCw?;L5iS4A{oU_@!?K9d>E8GH%)P zofpH}=*0k^g%$WL=#}64{(;ZJ(a6X54hy?8oZGH*O=qt5+H%EWi)XZO@}9Jw+^s2{ z9cc|0#(Y^lylc@Cw?0jJ9Cvv=aK`ji%2GTyvSL+a+k4Bu$UYsAN?KUd&_1nwiSE=!a=Y7oY)we#d<%Clxb!)fa z?Yr-{8XbP@^tE{D#H*JES;pQB?D6W3-`boM{nqVe$jftkf$~&c%B0~NGU_{@+`eLI z{_zEM@Yrdm>Uosxf2=ql9;3fBf-w}&T)C>7<nxCJ{nyO*}Nzum%?{YT5~Yt$FcUByEPDZ zbI)z_-8G6^^5S^?KJGggbivWG^9k`cN-4WD%=ErR`^VVr*gJIc$@U{ZTbkZ~Re#ta zi|HEwRxkGi*N(rvy(~O9aV_tL?XwY`pTF@t7xm`OSS$Z+on`6GT?$Xe^mA#-^_#b~-Vo_q*hr%v*=vU76SC z$k|#)&m28E;d zyLw)CKbNyh;`p*jx5;*g=AG>PWG2gg&ZV2J(l))h+tnv;)8V3hW2NSs!+D$L1|R=; zv7U3mE%C$qr(31P9a7y}$erYQ|4IAiV-7{_ne;#`yT_Qto=i;=1(bQNXx*ax=BD-U zWb7`-Z+d24-4XjddV1XIwhKDBpMUK9v1s?W(YRvI#$AoV_YCn*(W*YYT5P@FN^Rrh z_a%J&JwO1fml;rE?qB+iaLz4r{K_sdZSlc&zodp4YNytvVz)SM$0 zCS69~sk1BCZ{6jd<}Jo0F{cH*X_pvNN< z?A>*J#*Ur=fwlbJjY*w#_rl!CJ>Gx3blZg5;YQ*7D{D#$m%iiQJaE~R(bO zV(CbH2zJR!y*+IAk!3e%Hl0GV$2&ck+2V_=iC$OzpN+X`pSd@8`SShe`}R+a z3V$7cBWg3YEQ>mLMYElMT;IgJr&BJDYuj(ji`OG(L?*mwU*?m4`-nr>^Y$CL&E-?Q zpOjy`sJD9@_>}u9E#17q%vqDDWw#HU)_?Kt8ud((@NRqO#@UQ6ohJJ>&UL!=>hvGs zYv;b-J(7EC9}b@p6yC}2p~qu4s&rw8 zK+d|daPK|`ra#`C!m>%4vZv&=+ktb(hhN)!INh7HiavXRhv!C_Gc92QP^0rrJ- zSoA%E*BUu;O~5zv*)62 z;GU}0-6%_g#uRU8a-i&NW^uiH;&iJzbH3Q{RZZz-`v=FLU7C}iZa2hcfIKRH*+hA= z)Fn7!;{MXjwH9ZGU)>q(t-9Khzq%}1YBTQOlM|*hxw6tvlcqY{y1Sv{z`18k+E1(S zTqO5Nn^`!w)MS+XvxTp((}!)ln(;Bd{r1>RwPF{~ZCdPfeMTl4ys`1*&bM|2c)8m> zd})*ITl;#~+FPUeb7Py-&S`aInB|0`L622|fjbx4n#ZQ#1DASDNH6!3`Sz@z>*kp2 zxKcoW+`PAc?|WB94BC+TFq^m5Ie!?XbWmXQ5sR%0FVy`!bpQON1NMd(ycMx{%+~>3 z+L~(4iQysRY(6pDT$p~apB-b1wi9@?+v;61sHJI=Um)`|le8+1Z6aBT-}wgX zTQ5AfaKY$4w{I%apVn@2MAl$xZeD0e`%di|+r9r(>&3yk4xg^yb{2LW{BC%|?FF&x zxii>zKTMAs%XzR_>NohAcUgYyG51fAg>%+$2%J_|ee`Da^82C2^{@; zHis>f<4UPWCgO^@O16|E;mUCZu8>ItQn^wfVY4M%87`5^IfmE%Yr5 zPB2}t!wawv>D;8-O)9=!GWfSl7wQ@J-=SK5$I+io70Sio(SA^4bO zH_K*^k_4xH_w)#=Rbcwy!^+whOp)CkO-mbZzlMTg-eaAY$^%{QWcqCw-0PX!(DiTg z+82av6+O9K>&fF44DW}Ti9w4VEkE0OTequ+CZ-0W9{G0_EtYk6oOCGLAvCwnuS^vd zBu$MSpPSO{C2bG=pv8wbT`#>g{lA(jG_y~yHP=4&^Y#u=bDc&VTDE58*;I)16MY{%ODNvFE3 zY~u99-Q-=b^q|BB^wuTm6CJi+UZ!6;tnFzeD_#~5-Mvq@O#zSYChU>|~oO=hr+9oW$t<+{!Lh z-`LlucJA71CC`ME@P%IW67ug4vk6PkY*D+8JH0UUE?4@qslx9e^Lxlt`$~avJ@x;IpN}MMYa+GW(&f#!maw(f5#`y|?ScwbRQl5e<M!$6U+TQ0*4HZ9G_$nYii ziH7~^;W$n%X@CueFOu|PD%_9cI1xKSIuK_!gys-C+G9$DP!d8Y1tFA<5Q5#WGGQZX zgisFbbcztlN5qbz@R?UM93eCUAv6*p*lZAuCO3i~LvHpC8^OZ{@O}uP0EAE=GVGbm zAvcuflUE^mZ4U2gwuDnFV*7W!zBPIEhyD86V}=cWA^bt?0B_iHn%EN99CA66sh>;K5SDSm1cN#%K-f(IRCt{O#AE1amo{2rbRrQM zFaE#Xnsn%$ztW%#*x8(Dv%j}B?a9_;Dbi({oDBFv)4!u(p&x0}(=u2xby`|xx=N=~ zXpP$?8+YJ_Yflu(sfW-K4An!C9N4fO`q+2f%L-LsrU=18X<}Dw8%RiO^zH;y)o$!2 z2)UYOWJ*(2GNQ{n8aEKvDl}ONjju*c2uT3EyfO??;%JQ!YEA4jU4hXMu>m_B{*EEU zh=z^btvtMgBEmzW;(J5}1T`kghHW&>WvMDfx~@l>L{{sYM;V^fL#xn1ML~rbH!g=g zrM<}U1A9C-B5BwY+LfeXJ7{Bi*wWcZg9MB;z1pVi-Wc4EAG_6jd307peewT_?nTi&vickEWA(P4k zrfI6wRE6gIB&JnK3{Uz#iCI;WB#B0nr0}Wa1Y`>vsnaWy*vZvt5>+}&tB`19$thiy3qk1^^b1Sq7Y5A=5~jCNYjshQG)1T( zAqEL4kT4q(I^cb9u2+ZX*mhyD(FrkLcpn_E=_( z7)5f-Q3bLuMhtUGG11j1)M7+0)a&)MVnkm<42}v0W?qaKm$jInJ zhy`*X6I4OyL?WXd(S!z-3dO;A2gYT}B;%g!{?!-{BWV~v14tTLr+}n~VF>x7NDieZ zVwhpJh=EaU5d&GS{p{@r&r~#_k%(x zUlqb93Na%J;U^71ksNwYR7ge7Uwa_iAWwvk^+dFMgqh?c49cc_%@zgNqC$xoSWiS} zMj=F0g9>%21$>+KMD#T@WMIdf`~g*RY|$qpVzH#2AvDm)H; zKp~b;2wQ-&_#6@5qUy2c(Um1w5Ra{106*he4U-vAtr!RQ)Vy*a4* z4SL%!r!gmn0|`pWx=JJxnKH=;{(Q)+a0RPP1B+z>@3MiV1Q8GuBUihQ9h-ms&l=`+ z3Z1ViL&s98QgsRqlnI6lKpq@$b^O|l5cUPYsD|&WQD^E@=}E+um}g=L<##3XQW45e zB$|{hE#Q_Y<*UEV{8grXYi62R6VsuR6a_O-qmm~X1nvH{pnXBiX==G|KqM#$3obsK&{CE<(N-qUWny_F}`eIfH29`&UDVHYP9`1iNbBD99i# zA<;*_Yh_8goEa#QrDSF>wTg5(GhJQnE7UqQGbB=?O7(#{u)eNCRo69SXBoOfNSZ{I zO7>Q#fXEvD2#`(!M$l*&zMb7*2w_x&U^dRM0+nP9i;S)4Mk`@$2D2})!DEqtn?pK|`fD8fj!Re>t%pBOY_$j;Q|1`Unzb;)1VAt}e z>{|WP>{|aiyOzMN)lb<4`1y}^1^}qPsC2D>UF)B+YxhsHYya!)S_8W_KV{e9pJvzb z*V(lJc5Q#ku9K17b$_2#`${v^`gL|~jsDM{a_ns67$T-nK?-=fWD1SWSEd1H8gT$9 zA?|4vI+)~E23w4A2Dp;Wf5OiIK@Q?gEPz(F@BtdVUY`e6|NpS}cCnFUS$bH`Oz&<_ zX?u62-IXn4R=5?FvqerblS#6E$r{8_g&CYpbve!^%rnz z?BBq*@8G{*lLA-KOhhhLvKwIUdgWJJt*bvjO5!r<@ZRd%xHK=D4jz26^^~Ll)W8Jl zsVbgoF~(mQOM#0Ye6vM56QKDvsZifUL%%#ovPtJ?bUN-#j*9)xaB%Xw@8daHddoEb zjdU=|r$bzO{H<2&>aXpj$3_15#nwj+1@6J;`b*`wnsfn9cbuRav_aj)CuK>lfLqT6 z_pb!^Fcj%`7{K*A)Ls3Kaq4&2O!YfdU;X~qzlrbUNg#C-`*6&T`L;g^*o+^1IZA)F z)%w@lt^ZMA;1BFQzq=C8~>)?_^_rOVv(b3xIt6VFLslEmUooz!(9N$xAn7#%~(f(MC*NQ+kU>)djB8&97UA8|4Y|fB%OwE zH~$LuP`?6sF#MHP>;1p>%d~4FcQx&ssC$^G&=mzs1j8w-xJm~Y&Sfl?U{aP}CDy-x z`x~v+Kg8Y?VJhEfwSFGV4JrMtR_l+kheUV_Y*$zsTCLW9{H<1N9s9*rtM%93YqkEt zuk!bQ=e<_zul^JM{(JAWTE7qBfe@JYI0OctzwutH^*IC#`g*U`nn;=7f3MZ*OTT~f zy;kcfOpFMXd9T%aF1GT2@4Z&*zmPKj{(G&~A0ghR)oT6i_gbw#ly?4u_gbw#D|Yt3 z^IofU0O8bXwf^pVt=171m{zOxU%uCB-2x6;t=1pD*J^!7zQg+8mG(cJe6#fruk5UT zr}Z!X{x@2GM$olhC1r)3F`|EtTdnv1*?;@Lq@z~r{a+aV<@6h^KY1UhgaHTZ^sJ=> z_Nw>LMAB-R-??(_gDcn8NBO|oyx`t7FU|Vr`F@74uQoooA_bDyligzAem_b_35thL z;3jZS9;5{xn5Y_04$9>SZ^Xv-!#J%Tuy^ZplhG;FZ@EFPJ9d&`GOrN%xQ zzpazJo(O&x#2ENlJnGzw&}+0vWv{)g0Y{Aib!-SlQNrmk8O3L2wd7%zNhSC-^#&$I znpNKL`e+ScpqW}{bIWdgaOK*|^~toFX}$(unNN#B@`^yD;skp!sKZPUD+L$@2@{E- z;04Y5LR%@IYuZ|Tk;V+VJ2a`jLe_dZkT)SCo+tSwR+p^GeeQlGn*#S|!p|KYICceG(UCV)i2a_JyITUx?lk zFbkIjD_YzzZ_Hlw>Q%I1_87<-0AEmFwr>Z8B0yaTK2r3VLrOaswFO{<`+kGYuNiy} z(oxc1AW&(FyC&)ux6%7&> ztyg(j6~=NC@h;T>yKrGM$g?WRsQ5^WAdz%6h&T$ zEIc&y3mo$B*|R6l4D(47`M>$#%C(^Hxn0)hmg5JBr$J%?Wzaz4v1H?gL4+IqAWk)oW-p}LYm5(JrW&3Z zEET>gF*e>@+^PsT6-$c!vKTb5k?jF&^oADint5dl+UzcRL62V)BM6%1$X^CY^l&$rY74rVmPyeM>z7GkCUIGw=EX34^)xO^ zQx<~`0zK-2EgXa~v^cZ#74!hr2P{O`tueCz2H7K!QRRiUtL`)(u1mra^3-)pd*q-0 z8C7C`-dAE*d@XkPpZ_h=V!!xb{}ySn*aX0<`?s4_*q?z4y8<2d58iVtUop*~OnjCU z=H)uA4VD$K>w;!OLp@1~GR?DBCR&IvK&#+M16xONmL(%dZ`O@n!>H$;KgG(qH7%=r z%*{ZWdk;nBG)8r7tN}(AgZ2tz*S(C3$^vfMDE!Lk($QX}b-m<_$A*21P9PsjW+ zn~qKUM)>ksQcVjC;sdmn9+-uf>!Tzy?Z?ycE7LbU)|6>1XejDMZ>Xr?A}ImoBpn~M4oe#ysnCPfbAGX zNF+n^WY-}4F-u0~pvcF{Vy3K$G&@{3zoSEcQf_8LVX)un%1VcI06K}`j_CW68BnOk zC_gNv7!(l800A8e0{zC#0*&j#6-$#1g@~tIn}bnaCdz_LIng4C2(OF`Q&~Xii|PW! zD5~@&h4J(a$-+PWC6$HWQCZkq>B!2qou!pTlK8UmKl?v^k7VQD{%3!cWFw3s6tB}P z8-KO2LCDI}U%DoXs6kh$-SvOKh5*@_^EGM@vy*4ZI6p~DRZNqO+JbDppnx8KqirjQ z?k4K-3e@BEYtr_!1j{7%APtlJmDihN)__W^n9)&HP0ID<Ob(u$ zW=UZpdJQshEj>z$vU)aUNk?q9X`flaY>cbmy2hgCz0z*v@inbNLg5F=*=O-+n(S72 z0X-ZQbt|?xelZztJ^S<#4>f!lm^!!?WPy83K(c`L*9_A)5vC+EUw+9_em~Dgw49he zbtD2@iE#DJhNH+el61ygTZT7iZQw3kl{xls`Y1XN5X?v%{xca2z=m31ZCu)ls z??o}9Cd*}9AJqBnhq!+)GQ+ZB)g&tL_$;Bvwk02kbWGYkG?EVQbp0x%|4fdGxJ>$y zZGa-V_Tw@gK;*cK?)46C9PHm69Nf9NdeB>qZ};xR@y+4QTeogmGzXTHfJytN?f=#qs_@W$rX-bF zKC{-j^e|%F;S;__Ody)%y}S2%_tEpGoqY=EiW*+uKb}y8RwvGeo#|w#V9}-$?bK{H zn5P_)q~B=SQ|p16cTC&kQmw&(VI{G<@?m?Iv`%^^-ybKZ=9StPg%29coMK=sJxPTR zTT3L)5QZ};*X`0;N@Zq;SFj)tNRP-;YFmnHsOwA`Jeh*0whBc>3hc7perO>I=>j_+ zX+TUa(W1$x)nr=rvEeas**Sq6HLp7NqG4Rcy!(<*!+hDvSg-OyF*-0W*_bK4@N4sG z2HBv2*>P%|pTD97VXFQM300+it}X^7>v_46#pcBXzr6|6W#PqSC_n~+U4^2yICpu` z0zTs@CCNgnv!joF8$Yq*3`g;r%tLNsDSl53*}!o8mKBBuZe~s;u5EZ5GP6#(N`e_) zo)s1xN!tA4u@~v7VgMf{gSX|qDZ+;Tf6f~ueInks<-8{8J5R)Hj#;}CPOnKA+8%Vg zr5(nX&S_d5<?QnnP3Tc_&O~i)-4y$dECg{*GO`B zHAHw^6SK5!w7IrvBIX%Wn&&QK#C{e1=-Sny96%fT495cZitq1b*PxpuS=ER0OyL0I z1hV8(4z$^7+i=RfG`y`2G-$h=vTIGDQ1_Y#0PCG%p=WvOyW?mM{N48gIf1-deG_$! z_kgQl!)e`8T!$Mcn7NL7Ky?i5OnuWjcB*79$11zgCe>o2J;_Ju;B2Ek%(JRfB)>aN zi=;D2igAk2nylPtk7Cle8|^eZiAU*hqb;+u(eC0`H%neu z^ktaD_#N-(QzFBSh2&&pmJRIeZq6HfY)I zQwQ8jaP|Whw`@Azcg-#?E=s|f=78g%y^p^8-DWDe*YtRCW1(|T^$d`vU}>?jOGgR{ zPnlFaIQL4S>D)8d9F>vwo^Ajv^)`bGi6AQu-GH{i_1 z`5dDaUcvnFxhJ5oALshAnhw$my9L*l{kA zcb26#4hdzj1EdFLS^^{2r<21X9wr?$eqENM53VeyS(OwA@gP|~O%GD-nzoqIJsU*Y z08rcDPWtEpVI63)Gmf+6ah%16N%2;-uj$fK#^amkkJl`c4Nr%Z0w47#DZiZB8 zP-$D`I@j;~?jRpW1Rb?UzdP|D>Y}P0=Dq&y?#e2ROP1-XT&V?LF@CC|jDL_9({adf zCuwq8<&$*Kog7WRU-eget2b8H$lj`PRmd2UR>`1(%`46NUzjK^#(~MD;e#@_Zuq;Y z*-!NP&aEF`RLDf*bW-jo(Hn1@?6g=V5eCeXoNo|n_=I@9bm8-dVvUNG>9}W|0ElijIgb2}oUs?e?UD_+?Yyr+3sxSITS#Hng$O3%u6dG~*xcyd zSi)&e7<%K1I9l;Hj?eIh8JJLrMXa18gY+Oxh5_wgjiAF5zSyio-kSAaQ#MeVETgj76yxEwb#4ZS?#Uf;{BfC=>6YNAIm-V2ce!w zpVCUEC6&MsP5rU*V~TZXMtTIQ*n40N6Tap15_XC93}Dte z3XSS*45W6f4$Os^cd}v%-~_!_Nq0S~K zSrWQSvv)DB)T}9pzi8-Q9S9t=OlFTpr-h*StxR#$nhUGAT;rFsuXEFcEir-MyL$Fc zl43tElYW-#Rv5y-`E0j`NmixRnN>@dQ+YyRJK%2$a?nYI9G1DL2^1~D2I(E~VY{B5 zO|vK2AQ4v4(r}7Xqw22J7rg5rdLTWTgHc$Sem34g$UvoDSdOxAe&sr=lSN(|_J$BV zSGa?R7QEmo!A4M7)7*<{F)@{ty|Lv0cC7NqWVUiXV`1$k#o8o&PjIeMWI7K&IU($QAUdrNKjVEkYWiCglIn(G(u4U(W3dvM5KAXa|3-7 z#iLQQXny_I0{~tc3QBuv0)M)U#J6F?#HTKF8n{P$aQ@vhy+u9yp`#NoD#aT>r}Uf% zbw{f2(xm1&mXp3J1ur_qH+XJ+nl#(|@uOlCoM4DEQ4nF1hytxgLEsa#@zaGJ;4=#* z`^!AeR06Bdksi<`naxg3qRi$Q^$ZJs=z zXNLxkAaj;a3x9S?ialoMpEVTi96!hDB)bEZF+Lwp9?{^)0_DL;rY2qzJe$<)NZZ^3 zqH+ST1smKhoxkCN!?Z*KC617Ukf@CnjtM%=vJk;8!4RaSt+!;7EZ#>{K-cq~b2s!R z5s-u&Wy7O!LcUlMp`^VPWd0%>4sh&@gx3j(>)M~U(?zSrnG5^~%s{$k5vk1rV9Xms z=cD>7z23Mq%71`ED9Zu156bk>V}j%_fDt=LAA6F(%@?9tA?#Go1I9a zFpw^4cR5=V2n#z|YCv{jplz{7Te-~K!-r>xDcK$wREu6gRz#7{Kpy+Fd)Q(<(57o$ zCa=r1tdykPNZXDn&b+uRRO3X8`8 z)13^jdSXDiJebR19L6-U4L;1f*zS?Mi|nLCjC?V&%O*Wo>^zphr!E!v!|g*7&TUC6 zPm4@RPAYr6G9xYcrFBV>#NcXyy>ZSY?!-itcC_ZYjPUw7Jmcua$25D0xoe#XAt!n2 zAH}doXtuP5*oNtZ08yov*;p(I06~ID-JsY^ zrmX!8!Iog*T8_UZY;ehfUOEouZ3se*o{Wa{jx0O-36WyWYGHLFI?18{%!bC*A=0o+YdUvUfG=oY;DaA+c#kM%DxkPaMDf+2 z4*e(Yu(P`AZ3JJfp0&}4jX`Z#0=%`*1hIP;Td??uQ++q$tXOUe%q2`Upf4u$5PGsg z38*4fL@%Y&jH<@jou>>clKg_$1(luqXC2xEvu1#9F2sHa#bt3{tM#u4=0m7wFz(5N z&Eb$RN;9#3dV~XHShT9KeMJiw2S9SLzPsT0pzSdvh)lQ@7a@d~n+=*~ zUR0a5k)ASt;id!oM^CjIEP;5J|X+ESZo}Xk7PmtGjA(9DP>y;aO z8RT+w*(uqJ+MbbR4O>_OT)wn}^rOBD-D$jVxt+Q>~7}%7dWUNWD2&j=0+rj_(eZvOayCPS}He5|8?8 zJsd{pB$%0gTc$|pr&$x-%eJqd;3!F!m;^a|X`8%64(KdDHQU$6rb>%s*zI=d$0&)x zhTXmFIi=J%O-CcMk8=~EdF&^n{B((lhxE;29FLs3th-9#dI63p@zXpeA07^friara zhRcUQIhbYx{BT+Vq(pAM02%A*;JAn-{pEZb+yVpen1FRV*(!kzC@#)aF-p9Q#;PQh zc2+$AJli2_Nz#odSk_kC_%tX%0qJ*{P84-nFF60oe_JyoZ9P( z{lwexv2tmPC~y?%4wvY4Sz|BqIsSuRRnO_4_dKV*isU;`|I`2Gk15FOf9wBq3i3iq zA2?EjRKG|H?Khx_qWx!ZtNw*wy_{RsuLu2FMkXPH>teW<ztTI(T9UL(ejnXW+>U&oQV&6_F%l zzeUM$URL1rVRDdW$+xh}cB@3xaom2H7#YPp?BS6Tq$eUR)=4@Es)ca1(p!X17`t(j}O>8V#@ zF5q~Afgl8aQZn~1v|U3_PLiTXhd42BiI{c;i?i_Hyem3-QXSz`3`<3`0K?6ap}c-e zB)<@ARhV)*!J)ZDGl(w#^WMYn3SyrtG1FuO)+{+;mXE3_V0p za99!#&4LCN73H9toaV*2J4}{w(a`dPhxcE6+&!wsqia;P!{>Mw(V?maqx|p?cM*`) z05M2qpO?rb?u`f5XG0nXr-|s)xXKqCUKgAMm2kP{*cGh3+QwaqSP9dvjn$D4-_esJqAog6($Zv5um+u2z@IrRsr zO(6w4TI#IydTY1VR@QoVd$-o24RbBJar4HF-r9{DH)%}fMTs~W7~b;PJm~Z^)&QA- zp=pB@I4tt%1e2@S)sCMZ6-hiq*K0lP04ltWx-&pb&RIS+M==f(=OSA`^>91k%y?ze zVQCgXqSOz5UR?x+^MX=gkd3MAnvVfRY2wi+KSfhim#q98cv6}&9ZyG9oF)0R9G!J% zan6V;pG)tw(0VAsx?4vgKfAfIdgI2O)jPf0{9vVb=l0!OckZlG5FGUJYp8m2_3n+^ zH*c@qAZ#@EFT;oy;?Za^pftrQp!7?9d6bWnFY|+gbdXRD*RX%M2?B*wC>a;Y3Ik)s81J(%)ryq}LsOuA3TrH4zu%kqb5b+jvWYNGeK1deRwqr7-R2mQ2& zga1K-*u!mXAd3flZ=(zUNGB&Z9H-|eN5R=|zv`oSG{O-xS2v!d-E@*3oOSc!km`MK z<=Qp9^FfZ~cg@qhEYtl_!ZEMqI+XAIQLVBO z94wK91}5VAz_TlYE#I>wOHSu{*3}gc1BzY#DPas$Gvzwl&U2W#R!^H=Q92l`qUIV0 zt&qF|ah|LdXPt6wP^@Wwq3A}|tB%$R|0$@$?+;1~b+=OQZo4d}$?zd%Wb%3i@u52w z2RyQs&LE5WO%P!FyvlFq0E5{#G7g0F zz{DqUIs&QD?uYI5rGSozV#Mbz#|Kq{5OK13E~JN9UL>XYorAm>B%EI8cSul%A4IeV zWgslUocJ4$PUEvuI@JD_urtg^mgdt+I^?i?!I9}SH`~~p$ur(+0%eYz9wa9?`Ldl2 zM$=)k{Zyrak{gf6PA_5CO^DA>9s-&h0X-sBl8K%s<#Alxq~(uD>CyA2dJ%}dvBts3 zFg%O1VLpC985-es3?XzWoucxfnyfD`^DT}pMFr=A3Zzw#4GPDdEuYV6pIC|#Aauvs zbkJq0=HTNxM7v2gyq{*+#@2)|cZ>JTsPo^uZA5xFzf^-3&3jqHMk55~8YNqt?146r z9+)iWed#Q!$DdO6QGpv%#19y9#GU39(um5bl={%AFqLusUo`TNuZxvt>b3N(*XwSQ zm1&ipAPW8fRsu3jq?rOzZvy50`ZTGG8M7ZKYco6ZT!J_yRazSv+f|2VVcB%q8=RBi!enW4m^h%*cf+tKHdqx|5*Ls8*dpsLv z6zyfhAYzG=%>((CGFo^j$mDJH*AvSGT;BQADF&YVXV)6%mSkLhH(i=duGKM46; zA)wbxnI7Wk!)ZdY z2b=0MNu})8Wj62W0x$kI@xj7r&hM}KULY~T-$WD@G<#TZe?<&}>}MA~`m6s$&9%QW zW3C0w`^MUT>*Vg|NH~7;$sIcNf}HCx);{x3W^AGBW_#EQUJKv^UaS1paW8UYVWd#7 ztb0pj&LByl?C>cuxc)q&kCQ@@OVX^|z|P2x9%lLnX|b6NH7c4k@?|39ayl-%pO-le z)zz`44n2l^{&^2AzI%iu(3*cypPHUoxx1}FGUThl@@YdZ}Z5OU)IUUPfbnzzPHRC-_HI#_g?T$Ft7^{Zkc3xq<}h2Xsu5SRA*KiucMD-!94TCC@$RkoJAftB;fF>2!Y^V{r|A zoK%}d;kv6eRe$FyimHdBN{X3bqG}x&Bj^UN_MasuspdwO8K&pENs-2*$CUr2*4+MQ zNwEntSkPLSAy;_9s~NtZec@^Zz~7&yqami;tMTj!9E%Eqg8lUP5c%1vxSIONA54oP z$;8*_$k}zm%WB{b3I-O;gRAyrG@QX`V|FKn&ofSWWBE9#_{9vWAC`l7l03?bXUXBi z*It#aqZmnilVWE&s?y2GZ_d7ZmP|(Rz{lFwD2X%3cx21)o5OQaQQYu&wYy)W$w7dP zhbi2fPkfqqL{^tiQ_6s$qqXB&?E$7ukdoJF?e!;#KS29(1}7gEiO=>fnWNYA&gO6V zzqS#krBHn`9mg5;2Uu_!BKLB;d^8=6&bIO_BfY_^w_QGXy!(tUfJ!q2S7?LVC5fZ6 zjPBPF267L#VuTSjmVcI%N%go2Ie+K)LF%KHPDpswpN%nr-UIynIPt~$i!6P;`XKjN z;6*Wdm<^cy6-MtW3a>flg-`Q=zY@R-sV*+BtnW{PT1GA`?>(ydWzKoE>c1ne)T6wZ zSC`)3KYNj-7?KdK4(l@{*@~-V_bjVopQdUd30{Y8<%M)OO#CspcRT4JM?HBpMPxnC zL)w1E3Brb&6U3{5O$9~q)Sf-b^r<7QN4TEglj&iCIrf2~7qBdC9wu3}TltNlEM>Cu zD|m)O5}F`>4*>7A=CWvh3tUUwqvG4j8&4LwA#7&|vZy|beGuKlX93}o@T}+L57$&1 z&MCXGw4~Lzc8J#nd)0Lq_olX3xEFJl3s)dqeBAT6wqi>F7oWu9!zE^~EfCA8Q2tdr zh0E)eIwqYFL$YHm^PotZ)s*H?E;Tbe$IZ5bq4Q}??BU0~>Z^ zp0hj25E9C@B#`vOe%}~54z^~GgWdE_b-=Pg{aDMDA_}fB9zm&1n<`(g!7A~ee3(~` zNVCuFT&xMUI~h1X2o3vF(rb1#xXNeV-`rJ#vGOxr0IP4g~LNg+A0r9C5Qgp5Ru z45Y(6JwC)vQ-Zo+lxu359!_wnK_@G@jVYJ;SLL?jD;)@_903kC9~$0 zN=xUPReJ;t*JpLM!r2@O4F%XcAv6U8hR5$P`N(}x*3?nM8%&HN;H%R7U9h3dnY+Dq z0GkZkf98)|M8{P*VTL5~LD{A-@)^duY?n@&;N)0-fFwrVj>PaeJ#xnchxLR>!orZx z?70bBVZ6xEf?07x=wAq{AjNcYB3rCz@d8HI)2L)erPHXs_#y1bp)Af<6fIhjB*&)2 zYKxPb?lu#5R4kYPnq`i-6&rAUF6Z$$;rc>NE+UpnylQ!Si5J_zlZI z<|%gmk&MB$l8j@&7>x#5P06=zDWqLnqk*#KgOG|;^#JOj60n)Ta5)ZRf#Wz!Z0dBO zO*I84M3mP+qy>KC z01Aldt)v$<8?ea*vWry@0CRzy5~YIK$y>f@p%jt3NOCX4M2io1M87%OQlPL z?Fp3ZPL0(I%whGAVFIZ*(}Eq40^u3n(76b{U}6Z9_LXlio_PZ^4NF45yf`~&N{cyo z=6PT$^Pp<7mW8i<6PEWTG>AF#*W4l@NK(DAUK96+wLK0e^A0-Y#ULfMIqii~kc+bguES{lz_M$qL`HZ?6I4_OdQx{HS&u+B1 zEma_OZlt4UbkkjP6AK}rADUGtk$jI{*qQ3>jt8W9xxtU=%e&jHFG6DQBed zh6ahR2_HN>{$GWXbZy!m40hQdZ6-vZAIhQw68T1paUk0$uQ|!#&OKxRNSwh%Aq_lL z^D7tP%W{%YMmn=b&u^n7zgLGZK5z|%+dqpK9!voS8o5tN#r)kv#%0f*GL#=hD#DK!S z64y}Fn|hRXNcWb!TI=jWz|IqO^reS9Ru4tty?L(IBkGR!3HLTy-(noww0E!LIjM~|<@r0J{Y&;=LaqyO1S*BrwE$FRllvH7!Ay&~G z*hMf&`eH$IdAu}jH)0cgl9zOsPUKFyaQtzQ0Hk)4qG-?yZ49wm?|(oyPhHUW7H$R@R9@G9-G-i{PvT+-k+fdN zhCd|eEu_$(Va!r};iODg7qx((G_QfCRrFHr(8}pGq^l|_%dE^psKD+mt}__LE!JH( zOji^$<RPKlK%Prn4I90MEQ$d&8S;q+uTC4Om#Yb1%w9I5c_*`UMbD4ORxKCODaG z%^`)qX*wNNf}iZpkd+frWX{BO?+aeLUn`K(*c=!ea4*Zhhy2-9u#pJr%p$>8NM~#r zrg5EnTZ*dbPnVodQRN~Svy}yS^X0A0T{bHDv@d0xp45d&z{lrYDF`ZZ-Ole-F%Xc? zJZcu$XH)>+h-HrulC*hdTWd456T9dcvD+%;!>ls0ERjxZ#Cx)ra?RO$1npYA+<^7P+) zw)_0WXFvR{-~LPS{$Q9K93G{gACJaaKKb3EtfnWYug`wcTUlMZar4&gJ9pX8Hy8R( zlGg;gs-FsuyU+dIfOY^dkN^YL0>ZMzAcsv}3IJdRr_=g9Q|0F%?#Alf8+UKrUcGyh z?4oR;(9wA(UJy7&MvJ3lpod`8y`x$%T`lf*6zvt&gT>F|1tD_UMh>h;pps?W<-w;CI_>E6`r$ zA#NO{4GkF}wImR$$8&sBua4Nh?V@My_>wV1(la=M z@;=!;)uoh8rWiVd%WkGmpoNVUktP z63WG_AlhB*(;~seBX*;2+0vHyjBa`NN~}7P_Xp(-Xw8)nlCdN50!E?y*eSpwrlZy} z2D^-fHskDjaM{qxNx6pG0(jR%vgEc2P!7UiA;8c*Kbs^^aNE4HWt%4^*oA+ZWB^B9 zLJcJZfl#`9gfgd@w)7&vO4~%D@&*@Y;23*Io+_t)ken?sqSLG|-MhIGn{HcRKn_uw z&H6IWL#P3GoSa3b{e$Gpq7tzxRta(2G+SGvcR|yPRj(>$K_guV&7qCFKIXE5IsM&+ zGK=g{0)+*WrHEEg)X8OM*}8;zE*GLbvgyD-x&n71Ln}R$HE|sK`;wQt)JzTN3_->$uK3R^QdZu>gmo=QaznGvRhSU zA9K&#>3OQPp%UzcWJLGO?b}R=9FApc)~A;#%{OET*uk2j$ErMfQvuX`D2^sgM3i5` zI~Ye6P8I2NXbZ`&JOi73iF8qIzZG}*j{0G7^qcuU0_s5=pXd90&R-3wHHRv~I63>C z21q|I&JfK;iP?~e%S@*GqjX@lpGua%p(N!hlLD9fNz5@IL%gvK5T*}gZ*k`0yonmz z2yvr^cbg8<3wCg8OFryJ6f>>cRfKr^iUD@_Qijpqk~Ie9BexBx4b6l^%KXiyWZIBM-~I~I9mIxgyy7JK(D zxDlkrS`f#bA#rdrjC29571Bry*JRJM2^3ZNRe5qn0%_>&_;vl20YMpHZHMF9t!Vi3-q>YqIDsb#+oo%5u_cnDwmlN z{I-$0{$`_=1Z>PSw*ZYd&}=0jeNKC`4SBGrb}PNVVkJ;ozi@H&4rULW8^fJ_N^plW2-!(~Ihr zPNOAl8n*~whc=E4F1`N-T^yL0DO?@s7Ls@-22+>JRXMjgG`o<>GT zo`QQ&FRD)0u=4(&?|Kh5?pKP#(btf=;p$qSrEUYDBD$GK0Dhv`X@Y2(i17lf$T z^&jN-wl|f29XIG+`u$Mbq`t`!Ro%QmQaW>#z6 zd3P+~5^?6qJ+u9LlU2`z7R`YvwM_J zM?>5hkso_l@;o}ECh_oORY;2Jvl#Ke!EM9vTm_z`bLN{>FMH5ZTI}45;$!k-(Y2kB zfy_Uj#o;^uX-PHwS@Ktm8jKz_qF&!`NV;ZIZg5`HN)bwSUTi()l^16aevq8eu6DP| z3z)OrNKzz5C);r6UbOXCj?ml1Y94V9j^Z?<&eiT61W=nd)b3!`Mz-mNd>mzDI5$5qdtpSzW@4D5X&+4H`5T(Mi}f%(1ZL z{kX&>BTSi11?*+_K@krY=)U$MH*Lbthvc-Krlc$Gt$1F8AsT+?(= zdJ$dNryFS4P<)ic!=&gOq{(R5U-$wWwi#5KHo`LAcn*REarTEZ4H$u$a+yS_9adN4 z0i?K{cdx}^S#^-}x$ml}4VtLK7&^l(Gv;n`W#aaZ|9Hz?O?_2^uxeP16M6pBxhcNR zNrRC*vP`SIIJ>Gge`~v}ax*>#K5$irZ_*DA4=H=SojOHQ&(q`QOJ#q}h_-j1eaxBP ze`^IhNm3F+%^07nUyG*3AC};%DI(H#nvSY8>(Es-tY}Az<|`A?eOa_UvuM`OB^znh z+W7NMx?2On5H0Pmjrfq{kW3YlN=TDT1)@T3k#10;s1=dUz9wYX1oJP2&v6}Xr1w=h zVqVt#sLK&c5qOCDFR}AOR>JfrxDum}yrgE~i$IROfhp5Y@5M}iF545*WG{)F?;Pr5 z#ZC!0X0}22w0S+KNfG|YO?{1%Fe_&o=UyCLKZTr$uDfkmv$8WR}yT$YogA}*7D6otYy%m&bmowt`Z;cj$hA_BPQUBmp;te;3bqk+qku_^2TH*u8?_m*5s@cmR5uBYeulngjWm&YoSQL*RA2fJ@@Y0yt*nXR7?TY)ykL;$ z%|%cUnmvO}<3kvWVlohyvaOptgEN6xwfQ^O-xE4%tgiq9l{_HUf}s6JfhR^B9qK7W zezR?kRb7GfrzQZ7?-lVvSKy<$*p~%i&iNASL$hMOfjW7f5YI(2-IcI|6#7LYoSxI} zNb!jG;s7VQ-8&6SN(0a(rmi`2)k4L!|R;M5}u0eKe0ittH z!rS^>f)y0o8rhaYX`t(y+XEtZYaPiP`~zlIN8bwD!R_VFg6pSoRV9UY_S>Oa1}~X@ zYN1wbt*G%{DGq@U*$C0UWGA|(GfS69Xp82;Fr`}ibc)MmikJrswKW6FRIR=W3zZ{L zP>VtffB?2Q`2?d+V4z?=6i4_B+4^7-ud|X(}y-2A~Kad%}$I35nm?HCu(11lF_tTt?xq!2~mgu+YZ;-;<_k z?CJvJUYGe5dViXqhI<5V#fI=Q&C2igw$);+6nZ|MtWZP%yi(sYE4?0?N3+tgUvkwf zw5h@}3VJ0-PCXw_R*|^`uL91ZN~_qN3m{$7zMp0`%*u)nTQKSdb`E?j0}=pA-qhzS z;shLtLY;543R6lSg?KWAGVsQtl76jNT+vU6if}a% z8bVDgH>7BXa(R?M&njwH$~Zr?Av?4rYc1%=4-|Uu_^xv_nx5NMq41EY)NB z9KyJ)$44`Ap5*aQeWr*a-)EO9HaUq=PP-<$m%(}~ylgMAB5Uv(K3a<>RjOHB*k;_s zc66C!vjexs#6-A>I+4FyCHccIqBk5EV>D`kYiXa_8DbZX;zBZRS~}oj0VQ~e^X){b z3Wf_+=Po}lvQ%QzIKW8ND`C;b)u(odD&T3(EU!YD`kv8YN$=rBfK%YX=DM@_TV|yP z#;!F_Q&8nunGARs6$Z4xZFj>}j7AgT{xhg_uUHu4!#@%JQEI%0oWcsP5U3+>E+cvB zq-B$tfu5_Z%SPrXE=oj+bYH&OYcDK*`MnRn|HYfVJxxyRsq}veyvZsO!yt?$>l67s zz~Bqf-X87#nG(gci`Vn0mFR(|S0z^i#Xi(D?{$>p#giY*7hYtg_v5E5%`!$QOT7QS z9$!^go@j-ZUXcYg9+lV<1oF*zQ=E^?;#{X8GJph9=s-(bYoP0%vM2tT2VlV8Hzfp4r?*)~r|NYYz;tG^xQ2M1o- z)~L61W9jD7?WH?QE4`>zcxMN_YHTKVyU?a4I`>u=-S@S{dQDnZk=dFyK07=1J+T}& z^%WhpSnlE#C&!!?~1tTC&q9uovK zww-$`tDYp?F$?V-Xb!7RvzIkLK<@%J86TSV&Z1c{Ylg;OlwQu~MIo z;c)&u-ta2ktXKTx6Tc!+R?nDK1yA4_k7r7)8FRB%sw`(p-7<#mHY4ou80~uDFv?uw zM+;h&&tHs`P6 z9X+>)L;m~8C;WFj=D%gBE%NNHpR-hYZgR^Vf+%Yhv%0<$A5sLhoeoH|HzS;bU0k5g zH7bBs$phv+5ZHohcY`xFJDyy=dt7zCzzShuT}7UR6*|!Z4!g@7<{O~+JeDPb2Imuv zg9u1)OC~s;H_dwAW?;tg>;4Mv{0nnIiWXxNJJW{ZrS#=R-LQe&A_AHxC3Nys2tQZl zTf>@X9nu2&0ui=3bb&jAFJq$F5ai0ZzGv2)d6fM5qfY`w zl;h@L0n@|O1_oPUC7C6KOx&&>7%&ODw;oC@9ZCRKgCkPJkiQ=b5u#??a9cHgkj|(= z57I)EK+hn{0~dq{D*4&$fi${R9`28!Uj4pqqONI6!$RxAENM}2N z{dc=v7&`~B*CZvy6eh3JvYK_iSdQU+vfr0AcT-fi4kTE=sq4@s?=HLNpd1|wX}#*2 zcX*kF$j!fiRJ)=Y{Lb?)?o921Cd0oOxdFP_iD?4!fX+}#^#8-6(AB?zfSCe9@#Azu z@qoWQ!(EoXzz0lPDw_h@!oxaoX~BTHwBVl}c|im)bxB{Ayl`f~Qr8@ROEgT&(yg#i8#;=w_*{iO>kLd2~iM=GDoJ%#>DZn33u8H3AIJ z`s+mC=^cC*g!aJ`v2*-p6BUqw{%KOavX;cVhECfQ$g%TZqSD{2gmE%_>f zUe+LM`hLW#&PuSqkV4!tIgYurVOrw;H+OI$s6~(+2C=Sm=P-kkmbUtmNwRoV-q8TS6?pXRyr-1@%_=q^{bsm7nE1t-$mEm+JVmGN;|yC3z>N3(Bmdlhp-{ z1|k~|>LR-l6b>$@%toOI4ZxcP#OVg{s7T`B8LI{!ARkYH^!1X9l+5msNPH|Us4 zw^bN4Dun0E*aeapq)Q^fUpiPx2V_DKp?+##`1D5zs#9Nmu0(9pm}xOJiaAGs z7s&0+-y*5a1h!)CnYJ1gMAY+JkI1!+zbthHqOfzsy zK5lzjkMEEn1dU;HN=a^Pl!p)}1N=0{S}Tr$#`;RkzSYG!tgHx-x;=QzvG#T?7(j-u z1k{QYtyhJ!zvSFs;>dFdf8}l@k=izn!&ikoz@!{Hb{e>I1-RK4LFSS@kO(WL32C3C zFoMF*T!gdj<2`TLi(v(8CC6ZVc8S z5zGqxY-{&Suk>5HW-6SsJk5*alFsx19Z8%S%2rb@nNeCLMLaTjrYE*IsozmjCZ#_; zwZ3e4xQ~1>rO03cOg5ZVs`D`4$E0cH1PScmm3P)#^F_Eq`34(@oAqJh;;ShsBt=+j zW6B(}W`;M$_=y^oRc%G$Nk8WqbB5d}X!y?6M^qetewh0hG6BJ?CmhCZU8H3>D z9Q}xxmDLTxBKL=FJbqq@i(I85 zsU-ESUg|Ad_p88;bP7l0-rn%Ty}d5}EG)#M$x-~}ep1C>7K1PM_PUFB{^e{A1gNB; z&FFCs$&wD`zN9C+dMl+U$=a!1Qv|DZHH+z7Z?GcZu_g(`kTWl9Z{5bG;R@TCB+wwp z86~$(ScVde6~uo%XK(Wyvh_Sas7yV_FfEXidC^1%a=UzFBF?+Yu4Ui*EPATcCrx!B zuFNQj%gP{)VS11bj5ZXH(&}u{6pWwJA)kT)EZT4+&OAjMiCnGhqV5LZ#N5yn=59*hWO}bxDE@_g8&)(>LQHukh^wo^j6euI z?T8{kXiuOTvqZiiBr#gj1dDbJ+Q#DZg^7NW6nV5^A`$`k0x5vM#UKihdb!@(+hzg1 zb@8o>j`aiz%T!$gS?Z@5g|peqm3c6)mcp~pf|xKGpBvC6omULTYM=31<7Qb$RbNA6 zq&qr|jWumrf10%C4^8j&!GRw=sy^cBs7m2NroYS5`^>WAHSd-;$R3&jhE|!fwg{pW z&yN(F3*Z_7cuq4kyBD=`^P*OY!G&$yuC*cJMy1Zh(=l(5qDUalm@vM$Xva%vIcGYU z1l_U@MhkRSm$bH^`sak64F=0 zV6BW0cSEjJLQ=gnGT9R(jH0k@Je* z^|vQA^wu?BM$YDN`0kk9r05}!Idn5|A!fxq1spI|beR&c#Nx!)_S9y_BTHR}Wl5Y* z!JT`s8!ejl=1}g>LmjzmyEk^e7Lq|1x6`MV=1wS~p8k{ZkP|t1cT98rcfqu3KHI~k zcOYY#r*jdqJ;X)zZ3Z!%&CATow%w!6n-d}A8KjasaysE*dCo&c`gyYZe2J+@dk2h# zYnZZYqj2=Mr2ZH$#M)wg6{~d$uPJl_)-=tEMeW|rrjGhq_{kcOvd8ciz<(JMt2_ie3At%|4w zeQsw}Qk=vi15*jp^0HagBbU5#;RS^-Y7uTkBo;lS*Y`}ANCh#5p-wRa4p9_QUy!8k zrLuE<+CQ2z>%p`u?-5WQd>tNu4q10_8qDXT!$ZFC!es`6MK(w!Gj|-9pk5L4=T0Q@ zBf$3WM6+O+;CG5M0CPjSOv{e3IjcW0wlMN6SC>^>;Kan3*b8ixXM+Sr09Kt~aJ|A@@o0q*EAQ0{0Rxinh{;gi5&^Clz*afOq z9^PDoV_Gm*>S(UwqeS2lw?)E zeZ$ZWjLlfu=|h~h;Uga#hEKuB0gyH_CKp9;Kx7scRnmo1gYl}{MHXQHot}h|yyvtn=ME$_wtr~IfzF|HjE3`_uaL@6MVlbQBjz@job%7 zzC1{a#J#(FV{g+9^I()uCTATAL$tGScXjW+n}x%KvGj0t@9yT_T{o&D^s3O_^;ipc@+}m_xqQr@5cGyX>lVp@nd?45MHXT|JGggfGB4MF}8+-R1 zAfvcAOgecHXNO5=kQRfH-_)bMO-C|Zv`B`t-)?cH^gGS2rwT$7_b^MPI4yYCP*nU+VB)Ng*xpY(E& zmStWL`(Sdm_U^3OzRN+8k49ndj$X=fI!ewulenmG)lBCg9(Yh}-rT$Ih+N9zk6#o$ z-r2jq>3ZZgPtyz$VbmmJsmim%sn`77J9|DTlejn@CEh5!nJ*_vGC1n&$D>i6`-oWG z+w?|LSqj~Juy@zZwgf=cNzf~BlvWAS7@nMUIQ^l|Vp|XhSjyfknYJ&}SpN~Bwi6$w zbikLq^KN@Wa*Kj9>A>47a|6eOMS6-ngzqXI>0uC`&^x^wo!*K!f17*v?^|3qP<*|@ zodSm|}vydLg6^hH~pQ5u@RyBcXMySG1EP)v0+JO9kqrztoMJ0Dt%T!K5& zUa+MTwTV_D?8&^%gltbG0*N<^@(en(u1x<^2yztj!w`?{j4J9Inq>qM$GFX3u_b$wb9|O<9lJ2(*dz&*>DH>|}qbAu{ z)%X;$=-Ci3jN9Pau}I@@m{MzzUaI!+n4sv8@tMLv$ofy{pzVaz^a7f2J4l1xArH-~ zFtL+Y5s(El@ghrKuRh4xtl|c|Ug-sup4LL+A&6cSqlX#P9XDq@Nc9(bwH(?NapH$_ z+j;HxEzZpEWqXCWx))vBYk&9GuP-cq@580_jgR{GzW*C9_x4`>=y$&O^37Lzj$S0i zbiXuTnCQE`O*kOG+j~HNR(t$sh5xMbpEdq-ga6#*KezagUB>7WeRuT^KflX=xb@Zh z{A-K;tntG&ez+zN8GyBiT*NMV((DZe>?YT_$#rgWots?e78kkAMQ(GE+g#-KLn~VT zl=28DSyN;lY?d6q17~un83kzaXMG9kL}tsC%kzZIGwMU0O!py7q@?;1=I{e2D4@;) z0oIg!V7q+qc=uUS<|96brFW4c=xZH!A&gEE6UCFXTP369u!zUV9~)-#m@(<;6WQxJ zNw-}-nvO!U!|0kP?n+IkuMa?LCCLVIna(LIVAauRyjwpjGDs zx}fHnT+8OnSj13-tR@;9rGq3ZIR!5A+U+7^Fdz9Bu1RcvaLcoYg%n;z4o%IS$(&i>(k>PT*7a_D`mV3C_wnZm{VAUWQ@ThAlIXm77A6^!pD9a-g)$Us5P&Gmlqg#(5FYr+`%AFr3 zg!Lv(5;q8WxA}CX9y6}5s?4&^nC*F~BtS#o->NCuR~UX<23a>s*PkD8hV+rPV# z-Op!>Xi?n$4HM@)?~RVDA!zda`wcwI(n>k(d=w^OKOvI_9R$^FcIZ7RNyVj*FGcgH4BjdZ)wX{H4wl@xyr&%))ct zm{>uf<;WMgjR51yFgjkW$e}kRI*~xa;|hrFWiE#BIoOyAZu7lcKYEm}J$u*+KTKDm~`=POzt&7K5a7FFHwzGR-qcJWed; zzl@6kY?gy+T5ytn&s8X;U7Y+fA?4Jx)&1H(&TGh8N?)-*nElvlQ2WOtI}E78Y%Q;oc56YuvNk z7qj*3(?`KfG9I?_>_CFlgSXqb(KoHaJBqP39 z!OIyhZ1|Rpf;GEpP(lwp?4D&+9HK#psNBwk?vzkuIcl0h6FtlG%KY}j$lf0VRsNjC zNl#RhPv5`c$-A^c&QL6ws-R5_av|nZo1=!5Z7;T!!6jd@a*SFgoayPO4SeDoQTf~l zWniEIY!2xfqbxa<5E?m`#Bk`!F%yP^L+U}KReqW!MPI7PW~3ioyIPb3vwvnji-&oM z<0-hhfgJjd>1ldpucnw0F;0Sbdw4&Av6(M6NG+#xnHg5r!ZRNt)$^o54o)ii8ARW&yBDfJOS?v=|8?yeMp_k0=Cg1TW_uDTNAC<@sotu4KBz ztzuTiHgBV3%dKLhxaQMpGOha4GAWvLAgiwgr&}FP0;-Gj3fU zv>9gChmy%`L?SQhUqwF(xdCU~JT7Ot&vujSVdP@^JNKgW zFv|5qpuaed8EB4?dO@Ynhvvpk@70fik~oeF(A^k$1tQBKE~ zCO^8c2&oN-H0lYR=FFQVJMYUmnDulY8dxoZB=vHh7N!^y^oPyq#eR*78Gq(*?7OLF z{IQg4zcd+_@DXyA>q;SlRUxmB%Ok*<_1cqRwn=-@<#~+;v^vu)Zx`k?ZumkJHv9xc zYQEWT#lLiARm z0FuL+ULj&P96L|aEG>^_JaAMA7Gb;O@&=iNg>477B7&W~p59O*4t#tgB0h6iKrDWw zh+R^~i{|Iq|M1~4LN!7L9=0lQvM5gVF22Vby&E&@sa+bJ8IC`ewY2#LISEayra7G_ zDFZrmIWec>@mUu)dgbsaN%{i{C;YxOSo6oJHbpwA^ss?I=FPkfxZPRI&LynPA#AbLQKC#fj>j5R@vzCQ=hs;+=|eZT zZqiKmfzDDpLl8b~u;U@59@#45<1b8-CFXH*+B_o1Cf=I*I2Ukd7S*{zD}?+)2t`pR z`yR|u%5I9|n!Gfwi!NYqgb7T!xzlmG-t>^1wig#h(0CM_0kJssklC-b zWVM(tztj=XxaO6crL4x8U;)4ia;-;Nl4Ql;-mTq#aD=$uu6dZjV2;xRNogQntK0yp zaI%c?)Mn2_*kt$7&pPhWA{*ws8RO5_Fer9So5UYWm7Cd7T*M)s>ej$4ej+JwOV6y_ zkn?{Rh+8+tK-hko=f|ZPrN@b9PO@7a2vcw6=Y?OACUb_Ig>hOUg5H!z$$p$2n!dT- zmhaaWjqXyy&C!L7M^imx8Jx56P*qOK%MF2Ty4ldWL8 zBndvt4<24U(F6xrnZV=^vFYq|jMzr*W}PZ86f{&6MQlGGo-HZb>C#f1&qErnX7tWq zQ#{0v$Pd zjphN?1;vBRE~n%l#iiL#lFY|1oM4q{m-YAWA3RPM=8 z0+JKq(RVig;v=iO^?N+o1(7xx;t;-oYf@)~$xN!MC6Xub;)*ZOZM%fYY#uB1v%sS7 zZ$4dtC%Y!hl$y5)(1_}@^I!)^YA}*sJU5;TK&QvDl+eHquVD{%0`tC0 z(eWsW;kI3}37#w}9_$2$P^Z;O2pX=%P`3n*2697^Vg^|(DeuO-QVlLG!471P2X)(i zUoy56J4%lotoGr{d{Tg_oMc5Sj&Bq`Z0EoFH}e)XLg#f}IOhMc55BK6qW4cIggt_Y6% zXH_E8trfQGJ7`KftZlhLk?c=w>`0oZy|fL7#K0Q`T#)V@+!mxz_h);#NM_p0cvAY+ zj0J$^Juq0X&1}v-EF{qj>d}@gIMYv`hFV|Y`o#si@k7PEz3%YKaXwsJ?lx@efZV|Q z*<`K{oMgo4^dAp7s688)=7`#PnYjwcNWuyRZ=u2 zCP;y%WlT!XNc^B>lKL!C!X>bos`0VeJ?ky`%P+Nd9u*Mukl6_q|qydFm);Yn$uRZ__^Q z+;j1P+ZsU3pTNnAj}nOHJ+qC7#iXo?2CKBM?|2(bSD8*A(oUUbjTw#-gQ_YVpTy}1 z_qojOSsJxoeCz~!kk1gS17favg3lb zSJxxSzFZ_HX>v-6fS5m7QRr}+o%|#zM)BDa{0*5Yk`9*eporrSH!GHAg7eaXA0-E6 zZSsaVc_ph$qqKq!)rj$?et_D&Y%h}r*FD?ApN`n$jslq{eV-@s-eF9&xulOh*jLM8qX;LH_g|GR!t1S!>UU4e6wYQ$E$H{3))X0LlG;xv8 z5x<%|)*XyB23?+-*y=Y!a2s|64JwVyBWb)k;5j`yA>`fXS?|*pI=5F_%Ig=ImF%jk zy4S;t@-gA$KI7_|K>(&1=Ljk*Ke+OiF?HB#@rB_c1nl;~-~D(Ib|NROf7y%HXSyOAA2-1**>c?zH`zQiyg zyEy)Px2|TE`Xy5yr;~{nbfhV{u2EK2+WDogbz5j7xO$6sL9^W<*m7=9tO09x=Ru7nW^OYUV9xIGmau5ZsQ@D*eS2WE z9g8f6hKZHUMQY@-Q#yxM=aeQ2115AfB9)#jn}Xvt8)i==L-^P>(mfoh6x&LYtbHA=Fv2G^ym*?5_;xP0a^aW`(R`)Ge;ha2p`WJsiX;&de7q z-93emifEh#Q;u%279tTmtc~)m!*szQv`+H|t0(1X?(?nRxRLuN**Ak^0#L|PgaOkU zBNIzUsXxtQ1k-rBaou2viN?_fiu0Ie$60=wMT_KS;#YKBjZN9?IkIYYq{{vj7|RjpR*xYc_9Z~pP-=c88Z{lD|w`=5WK z^(XKD-g~XqxYhdhw|=+PdjB6Ef1`ESYF+)A6P$%PXtl2X8Lye7)iQtaTi32!^P?Ma z)})U^$0ed8t!0GS3C9+JhVFiRygbH1!XNL)$3HI0_%~L1-8K5R((6v92(q`QnxhMf zZd|!`T)7tLaL#}$>-m*yP6|=vo)tmD zY0YPp-ukG@+?mjBFeeC_T{IURN&(E%a(^Y96JW*;mf|dNE)e5=@Z=MmVMFUzt|)0U zxq37a5yh-8FBf^)C1UFq)8%SBS&lYrEzWCjMU@@b%}$)fhe`1p=@6tuun6AC(ts=F zBQ$hHa|h_-jVo8)mUi2=TaQ8)Y>Tj+yAm73ovpvoQf~YO)$b}|HTA;>I#ix3xyZe8SVEP*Z(>I%?!!OpFf`s^|-;lW&6yl z{KK$=`$alDOaLE$>pXZunHoJHrAPp(KgGd%c^e+|5#L^o13O*|)%;|We?UkXN6N19>Ochsi*xNN&Rxn+4Ylpl>SHF|{h$96 zvE2XlOaC)T_TLwl`-abQzgTMk8eshy`0ua07lz+{aOIl2zfkl~Xofy8D8m|nZmZVK z!wfbFlNTB)W73HV$fPtlQGiXA2s#77M`t*wv8{Y^R-}hVm01`pn$=$KW@ojxdW+9- zn~zAV_zl%9?tO6O8vmP({#sIJaQ}99@M=H@gp? zUp0@?*Jc}DgHzt3$SAE5pr*TeHbKgM<*nf7ZuuX@$BE$ujyC+R*%{N35AD~t7uTcF z41*jNo>^rneAgX+{(sqf6ZlAu>R`OI_saJLHixMljn|{KW;8Rpc4t?MwUWFmdv#dS zdTp=P+SW`Is%Ra#AsZJ!@?d9F>-B6n$&K?6sXJLZLqwQ^`rKg~HhQ5v~n$TWQHQDppa~ zH{_6|^EMJLt7}3cdEnZcgv|`$o(1i8*J(45v7}_47fNM?_|@e8^uDmRa>2ttL)O{z z+n*upe34+CTRz7b)4Atap%#F5hMxfEe09s}Ij0y&9N#?TnS7EGr^5DZao+I72{(*K z6)qE8Xb+BP0O=TZ3E6ZV*X;TIa805g@|ehDQmgliDK|;PAF~FB86E?QR1chKYA+c= zVH$YV0Hp1X8~9ytzb)3wCbFWTmo0QRtj2$j@QBRN0>^02Tg;9E(&UQw&l`Rpl5fM; z`(3A26=6qlwIhj$^oEB@-lSWDgYyj=AH{TE4PTGfY)EGs`=0i-hX&%uHg4D@r!ruc zjrf^QI>${L2M>He&MYoxPN;Wz)H(dnD;ibvD#sl+=LW+`dcc=4c*64h@F*YGq>VCZ z(RE-PdExO6_gxhRD=`1kPvD?FJ-fU8dgDew<2vmGfQyuW%XBV9waRTG^%70|;l7xzo7G+d>Qg5=~PRw2}lUR-ceT1Sa0apcbv`yD=DluAn+ zb7yH~cwfz*cU;R~;%UA=zxJG38V65>vNw>`P>RX26-5556j{vQyoFCnIfSD*;oD~% zALD8y+K~(Z>jhM9ijRLL<)GBIr^H9q3(z~g7x!ki!)+~#W@%bPb9ud&b?v(>pQkIn zF3acPB7h((s+O!0X-RwJA-;!q1{=ik z6GT8zz-9;|(Hl*16>VBbQU(&ekr5b4n?Mmr62kg8VH!gtJlEA=9#`-#eID?k+xAka zWR@2 zP}fSPXS$YAhHR{mgRJ*v&#@uUxMuc>Aq(4wstMd28jwj>1&yvDE4?yRX`|k40c_OC4 z$$7AehDvoI0>Khm-n8#jt)jMsdb_l+(>}H&Kn|DvkhsG+Nn3!K7tdgE{l*&!DgW|exA z5^L)kkvD+Cf$7-nuRMAKoCtD}h=_CuBMs?1T-_#$_r}al&YSjN*)x&piL3*JjI);O z`O}DD!06B&m`WTl%Vlx?DHU5V!xP&es2l63NSol37_}h@32^stBu|Kdgrk8ccnU~g zcn!C;(gfSMA+K;n$CGq4ZA-L@yHJDPgw_anM*%eQ70Q0`A<++oNFxQ#7)mYxgUIrn zVE52X_~4SH0*S?V^l%&BLFa0dVWS`DTclAI>vj8;Ax#sIOgK^imaS9C`lE{Q38{ck zhd>sYXK-T?3053q3`6If#MK8Xc1vDt3=vQeSS4cGr{HQhXef%P zJh4Lkl4F~AD>B3^^rrA$1{_5;t0`sTBXK};w1GH~LxH9ekZvYO_ZZ^t@sngDapZkQ!SUwaw3urwsFt5%4Ge75@h#ZsUIY)rPq!dIM`cDZr=yyzgqqUF!SA!8&b62B`81*HpG)i3JLL9U3()A`Qb7^7j=pnUfP2plo&-A5hx)H zi*E3W&$8TGX9XgQdjp+95>(xBov5+_3!!gboR;k%l&$(TdR)wh|I6{bvC*>{h8VYg zQQJUc>FR4eZV{s-7s8}@!sn^PxLGu<1xV~5ttmn)+b7Ezrwu?nME*}0-90W;_k53^<8`&y#`D{6|9G; zZ^Q{Yh$!kg&Cr_378y@;X?;AAezaR20l>pJL!TEBMK7jc%uqPdlY?-gPy-NH)aV_A z7`--<8x;bGLVd_Pum=S*2U+OV#NXm#|*5L?m0XJL3; zn(F)(7$6-ts~u-nr{_zb%}k!c6s zU^#UekW9?@mX{u5ZgEAtwK zr^|<1Wy22c#~M==pl4%=V){jtD8SPiQtOCeFM3~r*Ex}WZFliuI0q@MUAe7580<}EN%@b9# z=zAP?q%j(n8}3{U66z513ujYP219ypLMr##7Bwjnx{1M)LZnIdCikO5JWoCxJmI^< zBw|hkEF055x?sAlMY%+ywF7Y->NX^)V&ABM36O-*R^`iOyN;6qvR`@^TR)EDhepvq zZ@5O$H(iJ$Gm7DtvF8DT0E|#WVSz(%A9EewDLUl|-?hS56vys0gc>~LmIrM}x+tEC z-5=q#8y?UVSn&q^oIw&$c}ck1Y^EDvWybJ4u_5(-@I(<3iOKgJ!F!rZETn?vjDSV8 z3)MUx#4;q{+Kxo;ZE$5K+}C{!wm*fpit|LZY*;qForae?;Lj2Ga}@rh;7=?3xfA{z zgFk!2TMgoqXn|K(z@Iey>4iVH!=KyW&mHh51An#!y_2@4><1XQh!evpr1vHPgt{K4 z>_=%lsLMPMm{^bE{S&qYH#G)k2Yg8VIsvi*3+spolJ$g7jlkujVy}7LaP>G^UB#3! zEx6LX1ZdHx6cDMzv17+*^9q~CJ?mq~j;X}AvLwF7b-^wO$%GpOhAT-U!r9V$Q3jyF zL25*}uq0a1lB8J>MLN4n?@c(aHD}onNfW695Y+IdR$%tTk4v_;s*wdV z46t9@AUKV=QnoZzRSpNyC_HU(#HrCV#M@QPBIt~o)D#qDjmTGc)o5_x4F=c^3HJ@z z`4WTB781xq-UVr5OSlB`L|EIl*>ID+9@;8jiM^j4 z)6=0%kKqBHN*qCY3W-bz=8zA7GLGtdL3Id9`v!oa#J0pX?DX^wEgpeJa?rFVYBM*R zMWq)C`U@@TK~v^)4V1?S8f$UPIt?5&?SqzmT*JLcy|QH=$8Dw^JTG)2eP9sOtBvpq z4!@+@D1JfnR1pSsmCw#H*t>l*452%A@FtsdCb?ofS@-&V^ z!cF|baR@*|!o*f$b3xFP=wIQ43a*n|AuEk93b|ldLUQq=gnbeW2>9s9Yk|A4^C*Fk z5Nx;cCMhlv&P>@~0!e{U;Tgo?0r(!$DeA$fRyu@~M?>~kA{^!j^bnmkH4|#2l`MmR zs3D4&h_7CWKOG==Ma%OX7cUgoBBFEF=OeTAnby%ABi@*O;v&GL3azzTZLE}MopQVEg7v_d*vLLQi$91|8e3c_it zgh!1epUm*Yb~+|Mts4|rsBRHFt8ChEbVCD0Vmt17YKIzRA9dAfdsO)TwiCpHNO>_Z zC1FB+$T2ROGt*UL&YZ4_tkxJjy}fFLVj0Jq?gf4 zL@A|`=sNxCIloah+)BDwc8bR%0wBuwEHA|P$Y1SQcF`2fEl3iB&0(&Q5=0pv4be9$ zHgRQrt0KxKrZFu9B)*?9xijNAWz&a?8{!xsO*_C11Pl7~F%!L1_z0%0{5DSe61_=6 zWm*lo)|&Pq)`GXvh?MI|X;fA`o87=eE;Vu8VykaVh4#j`f*Ds9jB;^h4Xn_50Ims?y+>^>Ed2ed+;lS{o0f z7?jO9*HEqB0sGyUg+!;|MvgYa=VjCIeC{tg-1A*v0n+6L8Q3ubS&R0=$;HSfIaj@` zNNdnIXeql}#SA5LyCp&PCiA=Dvfksp$qseYDA_5Zd;(@BI~-hXdigZ~rJ?sRG`nD( zh2u!K6ryfM{he}JA}WNH4fQVU&`c$!LVBfEY)UykF@il*EQqqXY*jHO?@$TOxQwas zP0tT(@;wRl20=twP*mpeN<=}|~Aqs{VUIT|s!5xqC z6}$<%sgn6Sb_brQj@^*Yo&2AcKX1CDZh*uQ-vad(WQxE7sbr3(shcU%Q<2OmKbl&mhr6OzFtI9>iuJ|Vi>0&C-q*5(NdJXUxL1UnAd=P`-ao|Ffn&p9Bdz~7*<*5(&L+cj#<^d{e8iuUrJnhv(BY2ifslWAmiTv z$Nu?Ut4OgH+iqO+BjrGVbEjfHG0KeZ@WElj!upSY5aiz8?o4M#rUyp1Qew&$8gHq< zb6xG(&h~sZ-vt#9n=_u}n+3EVA+ztC<%<@*1Hlg<--5u)Ngex?*A9Lrzz4OtH_nY==U$>mXT-wuSzm0#U$x#yY2GP zV9D}TK3hE$d%#>6vS*zkyI8K3%%L$|b;SxpbTbQ2^pv#ZwD@kqH(Vdm6!Jv2qtKZv zcQ)k>Ta%Cm0-v~+rVI@A^{=;oD7c3&64E@aj>42vDa&ZVy2C} z*Pk~b`osiQ%WG9$bX?ag`sF2P_JFepMQff}fS>(Fxhzr-%L_t;M&i#yQVz&^1$K4S zaE*%To34kIav4yA(*iuTvgwK>wyBy|Gs@*9?!k3thPoOC!Vp{eksU{&!+eG(^E}NJ z#8YNwF2fVKQi7*S=B!aG`w-R(4_cv?sMe9;$qwie7K7tzP{nMvfd3`hxK>?W;s|Pb z&`M{9Cp&psB^^#VRfy#=%BpSw)HX`ur@^Cd?u<3OObsvx+EWm^?P@2ZV2&coq+ zaqM062gEpAHcwbHRvFR~;$^$2UR$@~<>T1n;0dErEyECuIYH(qFu;`@;*dO1SxO5~ zfRz$T|A79pkRB$pGT?qH`s!BED$;epH+f=siR^)Zks;;4E|*aF=x+$A);>CEvVr3lR^>wN0^?S1 z0EFSj1Bn#0t_)zk^`Kx~^TDellKBMt1;90rhWnHvF6gmShA!wm%3^i>$rlaSYXITM zTxCl6px>^zer2(`e&vhRu-Nd7iMs|JcgJ;1rRw^n=Z~E$hX;krhO9RjS5Mryf`WBJ z3tq)6J#cEV^cblb6Jp*%nvFH9k+6w5klWy)?f5(``LX4Tz&;&Gf?X%Y!Q=Af&YR+D zx7dxCup27C9`LP*iw!jG1h}NUY64?^0&-sIVgtdwC4~gz1&s^@zG(+nJqDXS0a@b_ z46Q$vp;uw75YW96kwng4ScP6~4Yf?b0Z}8HQdv%7mnBf#P;ikZs4WxRR_f22mC!2H zPZuvN2%ttYZ**5CeCpc(rdHpEeo-dWlbh^e6V4_pEU+vJr(Mr#BXXS2PmAjn+7jFlL_KC<1h*ktpV~#=!&)X>{>e#)MlL zM`2GOps3Rt6ExI-D#fN>$RZDjD8pq0gRt=0D{!;?lEzelZ;7N4p<0*6oMj2?B5Eg` zHZxFVle+|hvZAV>?z$D-Nm`Zbk*@_ zwcQ>)p5g7Wqk!gpry#dvXdPi!CNpK<;M6X6O>Xcx$0_j{XK5R6ZJn}*F0b&iQM1K~ zGO!^jH-=^NF7BB{$1dr~qwxY=4%g;}rJ?2w&`1Mr-G!2J7ZO(q@I!6qtqnua7LA*r zI6MtOL=@IzmzVgIwh&VRu{Ahs9_A?u$)vL$w<24DgAuEa!lA@&X~^}I7B{t~tA^`a z;GzKwP}>g$D?L8kPh&SJl>iXaM~NmuB@#`xao3!!d0_6D@?f{gPF+sCB}js zMiLXY5J^db!5xnMrfs@LdD7AHI>aEe_IB2Bi>4@|Jrxhe$Y)dcV!{k%n-JL8KX2H! z3EE4Lha%Sb*}~}xY>!tBzc^3z;rtrp(vBG~(5vH#ZW6*za@cW?3wOpb&xh;Tj-kKi z7)0WxebcQRg9E@YAP@Z#g3VAL)+`_HyLxmyGdwghI?gLN3gJ(}O{_fME539KzX`Zn zx-*@fnH=|yTh*#r5`%!q7T8Rhb)ZRu+vcL_@|5k^X`ETBys{0jeioAjD&}w5o^O=P zqjq^|4EmP?l(Bwqc;G(=>4sJ147ZfZmE@rgNGrV9fT{0Ki zp=JvOwop*6$E&V6Yn@PIn;0JBiG9Zd_FNF2VHy)`ALCZ94AM^|p$0j&Jl_Eb@lwr1 za67Ya9S>5) z7&YIi7`|1MM7EvqX)3`NGRZLfP?IxD(SGzkK^tWEB-5^6!Y5b_^ugX`jp zCXEH&YXo+zO=7-4^$_+$*#cm0ciqi7Am`T&#B9*xgTV21yZT4lzBxFq>*eAK(nbTMm;_H^v{+@UJylzt61 zYH%w8aMA-Jh7cr`hG@ZbJveVGLi{{lvSw#ZS1jI)a0H7rq=L{?p<46#ktC>pL!L(2 zKO)6Mgw zh(c`Pc$l{+5-L=Tn;mgoTg9>+be&cm+q8Ykl41m4P?72;90o4>!%%e?YsnF)A`2#j zX$86z=(i=u&+#J#yvZmD1rnKrQn$c}NMeazp3Vu?Uup(WSeo%-K4R2IC=D32mE~Al zwI|BC7=xr0qdqXP*AY}Dp!U&7wUXoK)kv`oeW)}9oH)Lf)0A__!#xV}=o;vTqO2ST zHYmoMKPu-%N>C~(rUtou(Qd)2BCwnkp?;@Qh0Qon8JAf-XO_=6{=8g1iSB1L3u7W# zzlZ>d99R{NUgjxfh#V{^GBAGmTS__f<%{dN;Ud-I@mkeGK`tfdPwjkc03`N^;YXj0CnkRhNY;uq~#Rj0h%*V{IQ!kvZjPwCWBG!5g!B276RBYDc9S$WmjuH zh|&OP$56EftEL5P6AKR{sFYXAOIgt1j3yBJDpcrY&>s425}xlSV8@R*gZ^3(G&DmK z65)MfLaIxCc~I?gP!ZLrV7tCj^L*IMGdSpzm}-hB6ID3Xv^3M|8lTcewU*{HQ}z(r zsgV|ZhhI(`j$|WU9*hKdm)0LogiAs}9I*gIGKxjQ0tI2L5KX`a5@t^y+B%aZ)5O}+q*7KhaUQu0&Fq8>!Pl^okon|9G%s`|Z4 zro65%KsN#J(9O4j-o4D|9{bSb+HqdaK8W<f`a7z;-n z3d6#8cnV8EK%{GW9$5?!Pe{2q0uus~42fS5@rM|1_`Xq`hdp~QFeG-PG=v-DD0Tt3 zzgdE&p}J;LaC9iZSkccrVa%F5VU#MCePzk17?uqo@(G?0mIjAhtii7$ahgnmCj`>v zqzVgq&cUoSHH3>yltHcoQp@nwKBu%KERb-OJ`EO( zdkiW*p}t2QYXz(6^Lk(6hIR_QNGw0O&G1uhXp}X1PC1%buU3f|FM8IAub)?;g(`|Gt z0z`e;@cgug?k-SC;Yv=YykOaL>5}aUBWA@qmYuG;&Kw{0SARp!;*}E$$}){WyeAL z8}-r%ErsAflaXo?X~VhQ0w@{H5HLa-G@S5gEOF$lpy!6%H6T8qj0}Wg`Ze5k(oo`9h$4ag z>HBk!1Bs%}C62i91;e#q>lS7P+XTne(Fuxg@O-lhiqIsSDgoVy=)|%uU)*n9;`62p zMuiL67|?1qKwxo*Ex=$`Xr?Cmzs_(c4R_A;ahC{|Q`;oc3W?3f3E#vWazW&x zO?bwW4U0;q97GaA1V+1QFIcW)5fC~ew&2)gw<*nU90gxSp zBOt6GP9kdb@@$A!d|qs zT@jMPILj9k z>>Fe2mM`ddm~LljSq!1zk6_#6*i_sASII#DnPR4Qn#dzhvdf5&7=VWYL=e;>V2MCe z|5}AGH7o%q>L69A0B(H?C3C?nJ5@Z`v>ZQ~rT;ffa6 ze<<21I3^r5gAlN0k+F#psFkohu^7aggtNaE#57@esUBDcQYQ{Ippa;rqD4{NZ4j$d zx04{KE(D#mAwjT!A=OTaRKGZ%#>hF*hsRADlq%Rff=d9!#g!DWR2Q!VP$B`F;TWRQ zFq{rg_13DenF9Vf!X1*sDht>$f|#uBKs~1T9}4rShf2)*fHsx~3yrQGdk|8gz$*bK zkc_mXW=WHg6ahra2?ld22sv(Cydf}{@W|_^Vb#zA-38+*g-YTYix>s3wsi2=oVf7> zkIgA1#WfbiNiDfM7?c#Jth823=7Lo;MW~crqScW1SIY+4s8 z43?S}gn*E*Yp4%=Nz_i`hS1}rw}VQx&;Y=7!ogDr^HxIzk1{z|#xTNCns@^Ua-QbL zRLW=>nXRnWs|)5v_p+#x3d7yc2{HRWuzw-DeYK%hO$@B{%5 z+;uKQemZtREGr0#m@19hnX=PypVMp$5Wf&)@IK8}FuJk8lOH$5j;wD z^fWsWbD064^Q#VXbxmP`C$$jN(`=m39oRMwvcQv)yTcb02&tsv)8UiU>02G!OvAP= zKPnPvuboN`RYABk1YZqb3(&Q+@S!nf`;<)I#H%T#(|{+V2LkYD5(EbZ;#}@G?Kl0g z+RPALTp|SESZ$_k6^E+I^yW!fOr8Z=GZPSPYU8H7z0tlf&Qkc{qjr5=6Et<$#)!59 z9QO{+HTerHOY^UDex%?#BJMGPQLjRU)Lc5fJcr6^=#lPuLGNRux4Qe4v`Brw1ClaU zEW75LT!>C|#aK3^E=ErsZsLO%6x?&t0TXO(z@4I~>WLuK7CtdKIL31Y+R+U*bcGQQ zK1SPIRANTt-3T_?P{@Lt%j5a`-`D{`<3fpp6Bjk#Nt1>P*D(sk7xi;Ij3M&|>EaczTG3H&1R{baoqeR}#9TCCQJ*NUC%|tEPUg6dQCU$9PJmZgM&hbI0Uf2% zVz_=EQn0p@0aJxCV?Jmn%eL)CkE zV#JK+FJa3RI@WZ6z@Ccf7HkL4Piw{%^iEN+k7*c4+~G({7=9FGLVOZ1L>sw|9yqH8 z>`t*Tgd9xYYRvBf=A)Gs3^c;b;+l=&X7$Ayu(Qzgm23$17jfiuY)-8DvS5SzJ_Olm zif!2HZ9{>lz1F~-K?@D{s)Z>ghZNr9YlIDye+1-R88UG#K@v9KLG3YwmetD79tbZafq_^H;`SSb)=$=R?^{)CwXwpN@}%3B8$VV zmb&H^3RlpDJj?tYG7^?tBB&7h1qFaWK3E@E+lng^T7GhDcuwIE` zG+4!`03r!+4YjObm;ge@lfVn6thE(^dmkMM?gdjrt zMDVxHnvcPuQ95cWZjBKL3+GK8u^}wXOoVTv-EGXuVw*nbzHqm@wP3+b-@$qc$)HCD zD5*&9XrW&Q0o_Cx5qK%J=5hB=$7`c8f$nH5(CF%pBiENF*3;;SV}#^G7BK8o17!!s zp7;ceNbHk_m~)`B1k_E*`7qC(5pw4ST93wGSI>Tf6(;nx;xzCOcZxD*UDk@}z`eDE z4Va-1wz(xX_;9O=(4pG+j9D~lo(bmw$l?6NPOeVA30GN&T=9-Z+)x&y45l!ndCY^c z2N4%A2ax}eJ5d~VkMX_-_M-~MivpCCh!{&KybeH06s4nRaY$?pMQM>THX3A%Dk9WR zYZI<6XDG-$Q`k6)MU;AU4^Uki5s+0Bn4V}1k}X2yd;k}k{_?E5&ZVu#I!kvraW9Cc z!K-`>h_3K27ilR)2o0F@d=X=67R@M_q(1=+E4U$XlDL(s2L#bq;LX=Ztb6tq z&uu{v2v{X_L1uM##U znJAD@;E?ROT>1U+WS&g!h5=5s>YQtov_K8qb&_mwAroO|FjwuRrc3e^O@}PY0uP&( z@RtfGn54v?W)Ogmq4khxRvB1{{T^H8)8 zjFe)HY`C2r88G#xlr;6QaH{9{ zf#?%z(HJsW5PiO*Ieb-gbJ!#4o7Eh_pX0`$L3Q7 z<_k+J+Y0=h5E7MW<>>xXyfHKg!Ja~o#5I2+8ADvFB9d6z?r}Kvcyv1!##7m83mr=e z&pgC&sdsRBhUbiM)FzC-Gh!V=EugkIWd|2}D-erfgqV+^BWj_pBPqXvb#E|U7<+g_ zdCHzvAG)3#)&>fy&3EbBTvb1bDf`Hd9Xt5s;KU@KnCu&$)mlt86$%x0q zL&4Le9}dx&p)!f2-hcwVUQ&lm44OV2{R9cGIAVl7QHBOsQEfKMoN5)^slRL*HZZ4J zl>m(AVJO!P3{N}Bs_`swK$POTqrFNtCEU|_I!hCnAF<*nhLh436(vcOttxk(n1Q)q zx=VPcIq-1d4b*Hg-SySR=N!q1@s@*n@S~U&IsWmj5Kao{VHHE{)`F4)_{2pvp@8vN zUVquCl@2(bZ5P-9&sX!hTd@OYIwjtC8hAibu6xnyp z*rBn3^vK}#lj(h{)h!Tm#8!N~arK9$hzgQONo){b-A06l8 z0(8WlB+&SjenuvWlR`HQlF8zr3rBni9@-N{KeDQrzE&a( z0qq5?&gy}{SAc>Xf<9%fKYi9@xiwm!I)NH&9_VOY=ROZ7VO-Y0&{zwF6Dm*RiFo6de zD~)dt)iK?E1hpsFhIzaeJeXF!iix_LAW=Tp+lbR$$PtZE z_ozl`dP-ZMsEAI(N(!w{v{f-(&~AkK6Rt$Q!wiL1j?Yj`$W?d~m-d{XcGaR-3mG=9 zw@H3Uls+L*6dh7brNHH2X#O@$?Ig|8vbn)44xC&e1_J90n+U*aq@N(<|D#Pk^r*oN zRDh*&_<=ZHtx6(wwtTEUC5{!KPTL`Mrx^V#D&nRR%LqWw!q%vK?dDS=e*o3%3h8X} z3Nd2Ru{zl9#A;Nn&?-xqij@+FN=0xU#P@X+=x8)~Wm6(Q1EII5BXTlwG=4CFa?9gJ zi;!0tT@8Z3aD{q9A(6_EpnVt30u89qpSRo+8Vzmekpi;PkxhD5iBJ3CY+O`neqoL0F$?I3TwaU1CsY2#>v-aB za4iB)gP$VuRYH(-F9ohfilNXW>mhX7P}G_ZZ6ZlWuJh2LyLvR3=*qDk0T866iLg8?sV^cPUlPqs7htOn| z*n`Y+wZ>EoCk5V$a747#5KypHl&K;m?f*pQHx5$Di*RhRNU}qkr113P9=@IvM}&X@ zCXghXH#|zDCO7gSEk#uAYfzz0wgL`g;P8PSd5pu_BEiGS9o!rv;WsFg(e36*^<8`& zy#|tX-|WnYvsXMh=t5%pt?cBsHsbrhrD-<{kGo(h1gj{LNcavP95$@-yVnorpTYBu=NE#u|&?_2M z^D4(3H|GXHw96NV-14AZbV^Y8aXShI1K}ySB1D`(RDSsAxveAWh!{jFXtx7lcHHRk ztiaV6ZRaRYgLRK7^Z*|OCJ0^(foOM7E-CQ@V+bH`x5$x(%rDwvs;&>nzGS$qka4<2 z>cpop%p47~T@DkP14>X)0wZAb^z!z0I{~Ta04m_3i;nOJPevEK9%5Jol2{^qg%h(; zYYG+Wo59+o3l3Een)b3wff?~y;=5@8vMf(xyCPsE3=c7J?GWHHC-`_9hdkfC)Ep>( zMA_`T?UGtAlGNHaoW5!i&MO|YJm1sl^_?I3Ez)am;pa%Ne@gVaL8sTV!}Wo1BaeV| zk6qTX&Gm|KDamz4$-#RPlr~6ggNIv!MW-wYbfsp2%M;vf+^S$~6|g7@1DUm4a}jQA zryM=$GRy~BQA!a{6_u7c zkSq4g2!x2J1D16#N+`;LbI_wTpXcDZG9?U9bL%%dqM&Qra!!~ zQ04{)Hbr$Q;VlTR8-+`Sf?8LZesTUlZO(*T(xhxr`2)3zVFTVk3YL+YI14LZ4$V%^ zoA#g>M$p_rXKu{3Y>}c+mK`zuVZ%F4S#ha#de|KrjR56Ar+6G6Ym>jqvP%PI*{bL+ z6hS7Ki40h~#}nCHzN53NyJzRFOrgDRU;n`1Rr?PNU46~L;gQj?YsV)h4_$Zo`WtRE zW{M?qc5dFf`FOcvJJnlU&#x^ko>;myt?XYovND7|g5E6T9hKX5Iet_dL$IgkFI7!v z77$fj^`i~z2p4QTmCT{JoM`3AA-suCT)<)1D)k4NWS-|C1O}jijKWtx<2GBv^_{f(OW5X!IbCC6RKP?$Fm?qWpnUCOf&~1 z!jHlOyfXp-B>tD>^0IasIu2R*$LNMZSZnzmi4=4aMDLIr1Oz&2pSD4 zK#KBu)l{Nl*v1?r;a-YfjT$0TLD@*f@KsVY5^kuVU#SXemfd5|8rI|hgkAcIpjTJb ziE~*G#)L90kN`?tF1TxS>KyxvR#2${S{}E&(@! zL)Zka3^;noM3=%9e9F2E<;d9D1b~O2Muvm10Z$vHA~@uhr6&lx6X8RtRC0*#;K`v@ zsN^Y^L>GIS!`s}fTTLFUK?(Sn z<(qCVDBps`l)MG*R3X6CgYTJY*|2QIY0%4#odaU>6yj%kZ&pH56EUz<#4+fpV4HwK ziicu-g(LzmMKN{ZkJeoxA@5aP2|dOP7cT;>#|duQzPm&?0FzKd*WKkfyE;!W^(u^! zMkrZbd)Ki?8G+NlFc9S>lN1BXLe38mK|5An6-F>Vb|QG}M}qsQeAjnV*MT=Dz~ z>M4i{vDk0>eWeoRb5>3C+y1x-{w5iL6qKfbm&l8uv4u`bQDS;Lb=w`0ZidDdx|e$o z9HRzlZAoQ8q8B3^FnlBKxm16WQX!%Tr1x>YESHrW0$Y#evBYJuz$!VcgnT0|8`o)L z9=j45#c)}WD`2J2Wy=af;kM;v`7MoHuA2C)$aNKFt1pqG`;DQC6ojw!SO_31i3*wo zf1Dwqv+c;l!@lR0`W>MBUuFqq*%6+MYQsw1!KohQxoo~u#60g?cCnTlBDj>0J_1o< zgR{R*lwhB07bCRzg2ma8XYQb7o0=R^Y=S6Ssh*Nc3kMF=g3FeK1L?K|V7kUCp=GHI zVo`86Nk7gV^_p&7+#d{(Mj!~(^IdFGm#3gp^xo__HgIR#^hF|6H}1g!URH-jml5x5!YJ^W9mF0G?EQ<@s-5=gu<>MEIEiAz@0pC(~)d?*U{T@ zZ5?+^O=VW|Bh@ZTH=2Wr{)@p%AAR;ol%7}X;J%$Fxd50dJsj5zt4R0RSMX3LB zz-R<$)1e6ggB6xhQdC5zW1PixG2kYXv)!NBQDe7BX zfJ%0Xe5q^8;?n>DaV{{ZB#iJV2c(CuXx#E7c6}5S*dsg{*vC92-t6HW`Bt6+Fl9vH zRwx|?nZgJPF zeQRI(#-lq5fMMcCVQMOK^ojuIIvx+4%jI{5CnfOl_lb1f^$7IDV_p5q%N0;~*Bys= z-8$mw$Q6bnpvGyTQlQ*DKBzP`l@ZvJP47w{y#g2;q1J8rwmafk)@ctu&|FtLc%nL_ zqQ@|GJ?2I(Ev|5#h#v&INXYnhtJ*GI)9tzm)sCE29A%#hm>%eZy@4u7u5a-~`aL4P zDnU#VvyLp&C4|o;#o%Zt8mkJ4uoZa|m6mG#DmawFU8#3S5hB|_Ap)r!>m=OrrWo2j z;I#lIsV0UTSpp>#EDoOpgj+Pyy~PPHY(S1pORgwBjn@?rn}7_HacX|AY({}V$H?XJ zIa)>8da0!Ps_tKcPgEzCI*3%%8(2>(DbHv~t`W!pW{k&b0O-ILUJ0i1xEkWF1`>iJ zLIMxP5LEDc2rQa3B(7YqnTN@Cq5Hpqa2s~|1e5OaY*9h{i6q_b-y`WZ1oK*nca?C{ z@e~|2|Bb}ES}>mm@iv6&?}vPaDg<7$HqweLyEm*kYmWPs7~TQHE|r1d&EO^sc{zhV zG@_7}JxQB35E|lwv|g$Z>UX7{`%378Uz0TRokmPHD$QVEb<(VU8(MxE%Q-l)uo`Ve zQ*OkERF7UIokL>Msp(=eARO>}BH6%~C~J?vcO?juFV1&hENcNY5XCZ}CL|ej-2gjQ zBX|O=iePTQUP3cmULX3OfVr#XGRuN9qKB;=a~m?G|CexgwV-~SaW@EDjP54_Q_wbr zX!}EB0|}lIK^pZASdGG!e%s1(*=$z$VOybMsHt=sh#2xLHfj?Z0;Zu_3D@T?h~$+T z5F0Fwn*fb-g)z`xjA%nJ2skRJC|hmruPtgb2=B_vE!@?|N6l`M0bys$fG}fmE^XQi zdL-D(Z@BVih}e7uMSw9q0xXEw+)Py=;%n2UcDfrE88y0TqeiRLvnGCnvwIyopC$z^wCa@n3tuBRu{v+FdQyt-dD zw`*~p3kni|B0NceHxYXvU0AYd+l9!$l9H2pZ7$kteo#z zbI%9Vq&s`^ogFPh3U+&Dc=kj#o6Tjjxz2npmp3{) z^LzKWJeS{#?{;SM`D`}3v%8~vwx_eFt0&)W=1U!&JG1$n*=)8G%69Hu`v?@g_qej?{P~!*9}9-pUzkmbocms_qau#&z{C$x+E^cgm$jIfr-KK>juZC#}4h2 z4LMyybN%I9`~L0|Gv)4^d%Q7YtnLbLl7?C}8tVQp#Mc zOBp2~AXZkFH;>+U<;`$X;FHf5a_5yKcbekm&Uw&vs4W)}LTjPU_KX z=Xutg4d(*ja%mh1bY7Kn!68Y5o92~+c`2Hf<01HUX81_oWPw)<@0Oa0d0rJ|K``m; ztW^{}$XyL?8$NbZoHs1H*K>>Tz;lZj0dIPq>^BSj8hBkxZ_vE#xGTfQ9M^|9i$%N~ z?9jj%@9ga0&(sEx>Z{o1t(I|kKfm_SP(QC&6|3kjwJtlN5)h{k86JG6=#+bFrK-Ru z$Mt)m2O1OL7!>p!IdW5tJr7Tzg4c_hDYxu&074UjuoY@_M`vZD(WMBy7I-E3Va-yS{NcfU(}yMo$4OM- znFx)Mtyp)gZ(@S#-s(6N_`h2#o44M&+D0Y@`^N_-r>_~jftndN>;b1TVHRD}@5oOq zRc4%nUcMvWmG8;#>d4))+J?bK85|iH8yy;%q^5J(Ob7m#%N9C2J0vC_bPSR7Rd8)Z zK9Gb@?+r-3UMOiiO(RmpqjXV2Mfp^7y|j_<%EprEYJ#Zbgn4YcF4$-xMUpd36g3iB za>ke=JUTrw`>ZS@ZqAJss=~nnS!80~sg+CIn|BrqP~ES2y>!+!(cIh)9tSKhMW9o) zir$?G1>i(%Lw$$$i?P6oTYJv3Prw~rkUBAe#~se zLqF;#pH26pUHonmpJ&92&kK~C4w?fw`kGC|bdMCmAIixYepon~pyKjhaB8?lg_kVI zJyWU9dWq(icKijn1^cVL_*lG5SBw)v-Ns~X3VjP6GsI@ zD4i9jX8T^C38v>9&`@WsP-Trls2Zgbh|@JS0|KtM-KbWn3mlkY*u`1dXLEDQ>U6n~ z;w-#3*-SQ*OJ}nkogoz(aL{+0mDN2%$nwRxcB?W6a9O@tl-Vt)1SUvsadClO zff)eoMf8^7h;AUEJs7IK;$p#(XX|ij(EuZOcgz%ZnASYgMJ54X8j^XQ&$&*m+Pmml zzFAM1ylQyfqT`l&tEP9{aJ#znE=U=KZ8)UUMRO)yvyd@Cx?4K&DHYI&E+q6((*DqZ z0EKU7cIU1*9VpNbLCFg1fEsh@kcuEQv{N6eZTQxL$q!quxgSjAv}d{trptwC+=eWf z_+Tc?S_7*ZNF7-Q+0y~rYQRr>@|j!@&|z4o4_A+fjXG%qNu29t4O;G*jnblgvL+kz zi*xCkO^GaY!T8eT01CwS>W<=nXkvJ@fAcV&)^g#D@VQ{*>8*e~{J;i0ZiTkxA=x~v zz0&$YeZVqm_hE&CAc!#2jNF6PZD<)>MPu=6wr!R%02RZRxnN@;4&f5}6q`NR8y{{(C)qt*`QW*7+O8t6^N;fW(6=cKoHYjNZ|NG8_~Am$JKOdzGjsr z)QE!?q*4;ot<+HxKva1YZtWdeSyl3V)Jfv1BiW)71?GKzQFW?n24r3}P1lQSL=|4^ z;FqfyGk}=si}6Q5MraFGN~1dHg~~6^Wv&26O=Hwwu8rOVD6Kj4`~cu$=qJn+0*=7P zU?)jkk+Q8Jdsv}LB;8#j07co>QIRrblX$*~G3J?WWzIdu#OHeYw}~-!HGN;l7#pYW z8yI85^c`wX)A!Acu|xEItM%7)Tt(Rax08s>yy{R<0i(oZSu{sZTjc3lk)4;t8W+DYl8MJ zQthpu^{dbr{5u|P3Ff@=2-iW2V?m3Hi;LLe;^N})>RW`dZCJFdOZR?mg(3kJ( zE-cM=ADX#l*VRKKw+!q$e8AXs)xqr5yL$E?9H`75%=_~v#?4*FvmL|LiN4+qpq99v z0fWEv{%bY<&|9x2cK!#rSKIi62iEwq<GwVS zrF%|1J$-g@@zc+K$s^DB;0vC%aodYt`sSPd@YgT=cy{Da}R9!^B?`= z18=(dx$pni5AA&Em#+W#mh10*)mvY3|HmuVzwYRH-7l`>&uV@BfiHaVm49-<-~7p9 z+aLYvLod2x{m!rc;*T%vNc{5yZNr;Ra{HS1xj%SE=f&5)C*PfV&#OwU-JMtb<&o^X z^@)FZ;X@A|9QnpYFPQx^XAg@3v4%|CeA zuW$PD`1@Y)hkJT*CtrES{2yih{3jo)yt;q=!{7eP7cYMP$6vFXKk%{a^Vk{hJpAA% z-?6d!{SOUweDS6i{`u$AkA8Xm+0T8?hAnUT$NXD=SpDtW#um+~UDrRd@aoU(TevBm zUjKz(?*5P0eJ%B|?VnkC#A!LSc>m3t7cTh9cmMjCumA8h$xHv?JsWr2p4fK$>)-jr zx;Ni9{mm=?{k9*Eyy0!Tdj9t{yFc1m`rr3{_S$>@;Ek{N&A;9?Kb*VdJ?~EMd(DB{ zpZ$)1-1_TherMe|&p4aNEHJCiBw6x95JZgOzdzhCoIiuH=LwnvX|zk2&;FTD7D?cIO5e)mg94qf+vdBam*-~860A0K$u`oA6f(gQo6|Bk~iTK~Hf z_wM@s2Os{?=l=Tc=56gS8G6Gd|MuN2k38?CPdzpL`i-MA_dfc^|B!svq0Fluc6vVd z(XSLQ`c~!IxmUdDeXq%E`j6aQFE(D^{>ZmqwtvfCeDAfJ_r31Uhp$N8@U))~&Hrrn zNf&(hcV`!W_ul>wk8Hen`kJSHXWfRD(zxH+bl?yF{^I<7FM97~-#dKsMe81Xy^*>5 zWf%V)d+4c~@BGNniy!I*mS(*EzT4k?@O_0hb-nLRfBo-I>)&|l5%1(#>tA)>=dbwL zt3UGVzh3CGK9GO#Gh=%;z5V`w`qn4^^LKBV8+qv`x6i%xOF#PEu|NI%)9>3|`qaDs zq}X!*bE_v_=>O68zIpo(KK@@{zy0~&8C~32-0{CYcCd5)v)gYVkX`8VJ5j`J#S z8vFK@6PM=x>dx5%KYXxu=&T3+;+}WBspr+t{`gyeH1$s(+4bGU_uqc>ofAV&%6$K^ zSO0d8b>I7&m!A6b180Bb|NFvsfAGa~zO(PLNAEuQ#hZ2?+j!r?gMagbOFsA6Ev;Rb z-n+l;$4{@_^L_8d<41Fc?%we9{omYt()iH*9S=_r_53ce>yv+b_g%-Hf9Q^%J#29RJO1;RrpXw4)O1uWG)({d@P{|E>Rc_wK)VWc%*){^q&=*}wGx7>B_n|i+1 z)iZqQi@$#3%WwMJpU*z$r{C~jnfiKV9+b*wEj{qi-GBSSM{hWF%YQv}{Et65fA6mz zdTsvizi`XD=5PDL^(XFp>0RG==X-De)N`%2*PQw4v)j7vzH;p5Pfu-cy0<;|@>f1` z|4W8zx83o&p=ZCjc=1!ed)fCs`UQ5h=f)5J&_t8;y~X8qUv$*11EVd@L~qrF>_ z&O=}N>XtV@e01@lkKX8HLi-*e6np8fD=ztUOz*gHRQ%zjeong=eu!urb2 zj#u8a{_NX7{h@2GzUi4ieEO#nS9gA`=Mx_Tje4V_O=dWmj=fnB)$Jp~2J9~^hZ#_GoF?Jzi49c8|&znJ4A7fkBp8
l;wsraaY>ef=HekSXPj0JgAI7zj#vRlC255hsYJYvC z{jR$9&B*wnZRi8!foi`gJpT2n{SD#vH+Rj=-b>!V0IJ2MXt@=WW21-*lZQa64m6B)Pnt zRg7Y~;?1QNEqlf}(OMAc_xwi_donwf+w|a(g|B}8 z6V2=tgoyAU~F$vH+kFj-VbsX#)LEOtxr6$1Ubx@Hpy#jQI6%JE{LS2O>9F<-PMh2C( z41ue{Hht_0$VQu5TKK47=@N{w7McnC+rozPWd5RQmS|#Z>vk|gU^v(qRD;Hfe#I%( z%H{#WXW0xa6#x%Dfa~%(EL#UXhvj<;eFytomj4jHUVpbGMllC>w8rIhwWD-%^W5Oz`uvtaQ;jN*wl=e zuz@kK%9;Q=TNrEF%2?Am0wtT!qCT6kEimMB8QZ$?eBfsn!k>%a4=ft2!&U@(t!h^* zvd?p=Px$v-q*DeS2Usa2rdw)O@wjLDP##vSMCIY%5*yB2jJ<6gYZf)PG+%n@Hu=4B z{C%S-B*l|Z8s0UV!coFz#qVq9<>=PExQT7sG7Of;$f5oV8Ov?@aqZ}((|-iS>^c?4 zh8+eN1W>=EJNOsKW&;B1EOruyuH!492(mWtP?Qi~0q91x`eT?OOy(>xBbfA=q^d)a z3t_e-j@|_i2mmRP7x2$6dJc>7VQ4@s4m+Ek!{EP2Z6Zg4g2b6#LS3zpy_>7{sP9>E9g0h$FEU&AfVS!Ur%C;{TDrdGGpwc zR2O^%AE)n^Fvf1Dc7QyWXzovCj6F(ap2iq^KJZG$*wYzfJyiZu#@Jo-{TYn0zoYM* zG4?T<<2J_FyWk~po+On)&O_gU6}%iI3}fsvt^MtQ3p9qy8Dqbt@_=&RqVKJYv4>z1 zjIkZC7BtQ)7-KJ{xwkRKYVM#I8DpQI?|^eZAvok2V{fDHz{(`tIvHc# z&@jpZ#(D_m-Hfq+rZM+0U|yokPR1CgG3`VvpQQ35jIqmsPyqIfF`!ZSi~&9q zoUdh!eGZ6&T)l6kG82rkUiv->3JLWEuHN_4_h0?8iLtYW^H-o-H-&#M$G;aa_O^4_ zv)OPSxb!;4woO7DWud+Oh*2z>o;Nghw6HUq>u!sE+$A5weXOQkD(uY4C$p|&`|zkE zo6Q2}zp#H|{Hp26@k0}ng9Fo}<3sz0MkYW;Z>GwZX`|RM>DElBwQ*o@-=Y0_v1e+< z&NE6C%f8Y)VN|MRGgEXbQ0M%CtNTXwk52au3=fS=4-WSY9n>4>)*9G|4M3%fLX{wB z?C|LLz*_4;pxQ{^@St9AkJjIX1jah(oO#vw=*Z;Y$iVcW@z7{@XtlRg9nYV0P4FCU z+cdWS!05!J-qTLKY{~GA8N)*;o-sB$F}Z(yaAJC3A5=J(K;9-B?9*zVA?}%|n7Moh zbabAotV5-)iQQTgTaD6!Y5O%7dp}Dx0N{!3Ux(3?Vo|LdhSEHCXy3u1{wRz<7E1h$ zj=nhv-_t?Wsa4rBWY1c*VK z3ixGbE{*T@Qu&M97Yw((Y|XUGg}`Vx2XZ-7^il;<#mV zDc98=8jA#0*?=tm`dUuJHkn(wNx7_An*q3+#5kMS&1?oTS*43S!Hue)hP(-sA$eQ(x z)9bB_@$)vp4sn6&%iLTty`o#-eUgD3go`pfZa}2?f?4J>5T^k#h1Ge-clgat&9#j( zgz<2rS}oIM63ta(&Mc^8>Us|uqgrh)8ooK_K&H{E>y&Cm-&(NzrRI`3Yt+hqy6BoE z)AlW+?7{FVmR-QV%@BZN6-}?u%+<2NcPSL}LVUx7<8LY34zVCCA)`L6EDjF^gRVvt#Z*X*+Cd1=pgs#UYp z%<(pJ&_%E^9xW;GmDxj><6vE|#%RG|=%5)Dc4iAk)xy_tz(grsP}qXyo@f|z9!S0V z72rIi>qwo2Fr5Y%DiW-wAOn$gAyfp_ENVhm$mD#L;1*O_uVGTbI4!YJ6{%~6b=B1> zrE(UaU>0}+G)_s1x-zCZSJDDBTCBW0Dx>5rXc8LhG)7X@K9*ZGma1w(FkJo7Q~eh^t_>P%%j zXhvslXk`DaW7{>gZUY>q2X$=2*#05Z|F_N zB>69!gd|s;DlyIxaG2_L#+b28ZyP^gma85w8m>8KK?ny(_Tq8wl})qS#%E0vf^)q# zUb2j;>5}~L=<=&trLu(CUEp+D*)&|68#9o`162aoELlY#hjF>bZPP4ynDGM&qzsQ& zYQ_0hK36k655u1$YQmi5&(~(aj10gRe0yA!1_hxIm9J(2;|V+4Re-b6%Mib`T&w@`4LDHsS>vSi8mxHll3=%;2njef`&r z4^B)@5A;p;p;8Q75xAIc*j+#{qkr&-1XMpqRqr1ixoQag8L9!@dfn7sG>Y?RHJ^R` z!2ap}zWxJ)(+5U{M?qD&TpQ#DIC~78Y?>S%8yG_SS``d9(MAS7doUWstP~&G02mvFzAEE1SgcFe`k=cC<3_ zU+`iJLzC>tCUzi}6>bmo?>WOY{rNf5apz1FVX#;lGgL4-eO|aeju%d>le|#ihh&9^ z&6za#V@jxv)~eid$~CyjLF6RzYE_WhA}P32G|I@)ie;w;w=+AgF=t9O)@#hRVkfOJ zE@~;>&;c9DGyPgMWadab4>7FOff-pxjH;35iXMQRdXdbCIce_%t@OswlqwshkzPSP z>a8G%ULX*BR>$c8^$X$NW0)Uk`iSR+Nc(=JT2PHcDS^j@IAq5GMD(&TPb?g%1eA)`T0J>jl(VUT9 zD~KM|qezjTz_!xcnpifLnVc73Irdn{1K2i--cQ>VvH_UJZzHad50L_eZA*^P55VPl z;r^&Pi>5nUE4RTFDW+R>EzeBjHEC_!shT$ToSIuSiHOB9AnV^fi@%3;3J?#8HMMCq#JX-p<0C~RcCI+rX_T!Q*M!_2iefjzBg0(^%jP$dnewuD zGv%6Ca@roJp{|+!HZ?n0MeinnZj&wokh50~Sf!F$ zi%w)-0$EICqB0X?QC9CxIJ_IXUpu?r*ErS_c6c2z4tv8Nc6fK}ajbW2ukCPzeWCCJ=>bPAEv6)P8!-8i$cT z#8^HhC_HP=!B0ulnGFZB8aSLAqp9*WsD1ep2U51(A*Fqip7mTjJiA9e`wht9ESMfbs5S6O@zcaKK8thF0N^RL zF+2j(*AZfk1DBM-?N~7$m`Ae=48h};4;cmEXQoH3&kx2bhRzMmFrs*Y1T-Nr$dZ5J zB;*hV{t?LRmSOg+vsM}UHd$U?JX>-7@+eCZBQIq^HAXZTI$;P&)qwvMN^#nYH!e<&K+ckqlbzjoRJCxl8i@tj!AW$G>!zOTrT6BX2WtvC1ZM*fmD2!KBm zcdC~w^D8@8Vx!)E2t7dwVDaJ-OO<)Nd%v_(+YtpM+c;4W%-j?PlR#b}K4Y$fs~$dL zr;Om({?a>{d&XG&1Dmz%HR5+9_klhfk#jI-4FWNsiytMg=$N+O5lq`45J$*5%4-k& zFrqe3@=Ho_wqNqt38Jy-k{#0;I37)%2V%&Gf1$r09hm`DXuraPop4e06FMA4$FURq zPm?FWAV^St{dys?rikD4^1W5TJTZcqq3rQC8gi4dsm!532!NVFDx56r)OHL4AjJO- zU}iv$mz}S`m(8AUGX#(z;{1cjvoKf_g%!>~-I*JQ!CW{n13HHRVde(W&{fqyF|;T2 zz5>*LH+!WXgF4toWI_G=;{M8VJntC8a=;%76Lke2TpZ@F>e#0b1w-_rl3Y?)CIPNj=JrX)#qBBFNOZ%qB^tB8`7`9sJ1J&o;LPc-Sy3Gr}4113zYq4XK#16w#^fwC|b$RZLIN*75hy+V=M* z3ctl6bE5E0frUCCrIP2DK<4BY62aJ0Z5rM=saWBTLXTwk%Aa<6WEY3-YdYwDB%m9} zIGekGaTc-(B?6=?_?C1qz{3nNiEYxM?~!O=Muff?`H%*46gY=wM2P@X5)A@>+#dit zr?x`vKAly7Tp{$LDgd@Jssx-%A}&iPf5HVg$ujQB6t#>yAu321BmpwKNp8`Qzo}D< z`}x`zFAW!n4S$q-<#!o1K7x6=n!M7YrgXcHL1;F~m`07^sr z0!<sWeJn61KsI5QjZZ=xoz2-)>Q%f`e46vZ1aRYBB8`!LE)LPX} zy@8B{=o;L(GBw{x)@;?b>g`Upu4)tw-Yj=+i+b{YRrI6UC0`AE>mamyP$eEDTtTf> z+qEuSO4W9m%{O14x-v*WQYt3rs0qmh@3T$x2d@og)jY+)H zJJpY2=<9o}7KF-f)gJBD+MUEot0vOdAWk}b5)*+&cuh25R(qdOdcV9}S-uA~Z;C-T z8!cu?t^teJj{R{k%P$3hazM>DL``n2ERHJCfND>E`_m#@uvtIo7e9mQZ;9%{9e_2H zn6EtP7}F_@WWaw0ZgSpwTYF6HYUQPp^CS$vk3pje}`L8x$-Cx}q2c!Kbs zYuR#$<%$6i7hk(*2)=*^1)L0$20TurMcD+V86-9sO>XF1UM-I82H`XzRSrxt91wuQa^u*XX zVi11Fq+T-;Y>??`JQ_FGUkYTa#0-zZzm_ZOP8LckwG+qMO$eIJ7Q2Q-{+i``hct*@ zK*%dWnk(kE@Aa#m&Eh+^qrma{dm*(ue%tALodNx*-PnCXdmaCPdW-W5i$Kt(K6=vW zGMif%Xxy?SViN^0+JXD*>!|Dp4kOkH-4Y|gi|-^;{-51o)K2S9F@fIDS?$oChvmmuaewTbV!kRcrcxdGbumkcx=RT_LWKOoT&ucC6deVI-9_~oI3c-CZ|Dq7*Y&Lu(*@3k z{_Jj}Swo!K-sm=4we3cgnc-Ug=y+rX(Xncx$8IiOYd-BfXzcFouI+7Z)>;g`Wd#)r zr6n7(`Y{UX#z`$q> zcCzl!M|R>Eq*KR2#j8(DhJKusBx?m$=6b_GBS&22FpHfM7mg^kA-hW$k@t@r z*9F;{L`U$BTRTHE_YbCcPiZWX7cek}a2!yK%U>GOFf{u#BxXQ~=SKtsy;7UJ-y287 zGCdMddn^V3zFCfUOnOelds42(;ZsUp*IWC2!F(te^`GOleEbf4ce?$+v}kuk1IM=w zQdtUNv7*6v*z-)srKrvU>TyJEFb4<4gX4|iJn@`x0E;&s;d)idAu;zO8ldJi9E>9X zXab2&OgD3r4})Ovl)1zIlmWnJd_f6O00KUyWyMws@ClBFwn2W(i-V&ChHRJhwGa*( zg`a=jI12<^QX-__zniOFxLWu?;gPP|z6G=dsC3%3c+&2vlKt!LPOGuI)!kT&ors{8 z-lurd?t#IuI4IO^vOdIQ&WpdC|MDc> z_Y&T|W_jQ5N%Aj-Zd8^_raN*xyfB-zd!V1l)>IA-zE|Q`IlaYP;65$e#5=NcwhQ-W z=asIn;KPd_`?qw*{_pT(pO0A_`a7VO2J7+zhnuWfV>oI}G&%>h^1dGoNpI}9>|QIP z<}htr&f82`gI<)|wv%>ea;>r(g(OpR#>Nh)=$y(hN*r$@$N}?B&3P&H$5}kYhB8M# zhx7Bhq%sJ=Ml;s2Y3R^*jinGVVl#Cnu$CrDPy7ehoi zp9QVK)A{e^UT01gASAXpz{vBhiq%{l48wzJ^yvBHT7Pr*qsEg@mOtJa-8=kv4euCV zt8G`?o%(u=4$$2#>Y8EXSRpk7YoHz}H^8>lr6td|nRAlugqD9ugX3Aq36!lYkCX|# zn1BP~`-wk^k$POb~|G3Kvz;9gO8f*%|DA&-~ zhL&I};#y)%#_H_CY$ZD}xVX)deZ}zX!0DNuDXYK29$cK}-`3Im?{Y12V$6c_EB*lB zMj1wd(;I`ng>5XJ2Bv4a$FSFgg2571&H)rFW=Lq6V6X$$&eggaTp@i%@G;CqQ2hfJ@|m&*oz3E0T9Wkb&(Y3GyZ zSSnOBG&PM_#;N#-Ga#^03Q8G7LAfbHI9OrX%weL!9FrctZV<>K2GXd1oNG77oNNS* zy1Y_eMTYdX+IDRRuDl@4UwjQkT~rE4B9guW`nR|P0{q7KE=}e(wMhD&0x+3vyO_G`PVty|lD3=K}v7vQ{M2j-E4;0cT}z~m%<6wSd&+6CpT zIg&23@zFOCTZaBuOaw|fwK2Cf5L>fnk>Ud@2crC`z_~Kgi-Y=WI#9O+sK-kopk3HN z9}S#{_Ixw2QAHCTKq_C**$hn2F2UkKcDVxzaRB;3Rx*(-pCp{9_H(jE4v5PF%AYC= zojPU~$`x?Gk6~7W0wp3K;q(0zpaI*i~da|#N zEjBC3z8qxy7vJ8}aO_47u)VM>*W=e0M~iRi(Bj|aXc4zBuwp-;lsR&im~YbzQ7_6; z;jIv3PP@e=~{t6kX-Iu#DN#H`1Tgw&e zE}_><%reQqG~Ax_Dx@pi_g{bzq$$P2*N9SZTO~?;7XlE1hVbaGIWO@0YS6&zked-l%8t8r?QvJsnI`g; zQI0w>iHjrj>pFyfpUYvqLlvD7BY_X+7_DBI3HwlHy#wapJO1a6>zZWKAA2^c=u*ra z?*WXc6fsLN71!OMCC)#wD2M}GhB$rkuyop{Q%o1`IPiMTiH^Jei1Utk2CGRvQK0jFE*xVQ83G~vHx#4K6rEXsh;9(z zD?Zd=G7IHO1)f&mDFaD1bs2}Q(my2%9bD>c05$olaG|0kQjouuduUH4u5B*E4Dc2J zZ3$v&Yac?SJoD9{GMDD(0Wa@dYquX{3aWJAq@XfM8UUybR(lB)2a)YNJa)q$L+pk> zj-@#snn8cb{zEV-?$MwR=rG48AbyPs25J!Vx;Vzap~LupQyk%J)da?GvVUf6TwJF= zh>j?H6XDbc=XamZlA0MF!_B1#?FeF>>mQM!2|iJt=~;A!I1za^4kKdw6g)^MdFJ@GI=!Ne=kcX@*5P z9I)P3P`MF{Os`M92mo7NMz%HU0t!Hzi^fH0SS-8hz>Kz03DV>YnZ|ZA%dM2?DZ$sfLaHT zKRZNLo8wOGaq!crap0C9akv4AZ*BS_dC+423G|UNNOlMG2*7sV;83AMg5v5bYE1-B z3}D~+I+hn;n}Y~~_TSC5^h&xJCLF8Y+-%jis-0T5)oC{q*A7?r1E+6BRGurR`!+!+ zMvx_MGXsn{-b9Q%SNRn*V9%BK9^o%TUYJ4wf8&D&&LF{u-r)BL@EaG9d??&s1H1Z+ zTcE>lTj3Tk6y*OsSsl)6)7F;iDU7)tBgDwqLqwGcqXEk(|d!i9q0 zdqyoVEsCfGY~d`ax-MWSq#XdU)(Igp1Kd$lI2A3^WuC-f7EZu?XJ7_4xX80R3Vdvt zLmHSEL=xgZLb7z1nunCV9r#0wEX_sM$l!RSdp)+3uQg(ZmJq>xv>Ap+eqa}olK$I&CZJhG za-_}w&zabcNz8D?2&hO2(03v#?O{?jnpVUY8f_c^M#sRvjLPi%;_OlhLQ6uxZ&-^f_~zo|?mIeix6c_FY+De! zwqnP=v~`R`Q+aJoUU0 z@~}l2p_*~(2`GvYQ_vBJ706Z(ts-**wF3k5Z-_KQ~O%y&+lwZHz+G{*G zdhRq^mBFLqwIlbVr?tnlv+&7kGw8Q=kJieo%l8(ZKiO#Z>mUPvd#m2ruC8@kjYcQI zx4hDKBG>Fmw&hX*j&{JR8)C+;DPyT|j1i%5US(_M_hDt!a+iVcOqPOMfpjTYcP1Q& zC?aboL=n+jyxoy}DQlkplq{;~&iF(xj#Y2zu&MD9Xw zL^Rac*{!ebwPTO1FT{JWv##0@9qX9*(SQbgu(LsrqqLC41{C-&YFYzuq<5kaIWzF; zk!^{XiCu_3HZ4?keCw3lP0SP{CFxJEl*{w{l5JYFSt8RsZln4LrT-jzj_DL7a!u7N z1Q3p!_r>TaY}89+T2eTPhwQcF!KNV*1@n0O;VCc-bc>@D60SeUC0vsjZdGXQCzmiE z)3uG=cC3?orSF?Z=CNuv4Yn2Nly>t;ZLMAJ)Y63|!J1SE76!;cIXf1jIKdLSevRW= zOoT4QAy}6{jkg3c56Ux_Y%dhiS>-Es?Au-__yQgP34ddGNg8y3^K|y6`v;PGjr`$| z(mA#o3DSk@b8*u3wvKfDo*-S=xFB8Iil%R4x1DgQNiYP+pPM%1vaukfD(d~9#=hf5 z6dVq-WKIO8PfS$dAWpdm;Eo1du@RFZsp1gKZ!%sOUr!`bNe%lmu`D1=g*!4bFVzV3sJKP=5XCNEhcC%aiF{kw}GpRs&s)p5wJ=mo$WSx zAFdvTpeBMqi;xU}+8^+N+(lVq!B|;ZfIgWdp`o}X7(C+7{Uw&Z2D(@A>OPD@wz>g4 zLZj8WzrxGI5HRh}r^QmqHp2m$JQQb05&Tk%%o8NTQ^n*;Vr7ON3C}+EB8R5}mg?b4 z1WZP|5bTk5eR0CwgK9u36YagTwFw>=d#!rsDNoTSOtPWztNp;-H$C&7!m-K9-^o@M zIpst}N!=#mVAZcl_LQ|Alen0G$?yW;XbU1rj>D~9M0r<|BHo4gg=AkmUb8yCi&LcU z>L`+kMaVlBBuOy?R4hK>ngOK~6B1uha_EF(({(;)YJrrkL7Eh?1x&GAL*E0751?Gy zdj{s_gx^VKinW$QVDeWI5l(}zS@00BmCSCGEhq9>4IO5llb~TGGL3hF;$o1YTn1|; zp8(#iVk-az*I&X$sL+GBcbppq`f5SVZy0;PzrDV*!J`dex+Wf+faPSoWR6CU6{K4f z=K03l+yEk!*}fIW3`#pJi2@)mO+hakYr@G0+EruEy~SM(AhW3OIuQO;tZHMYTE|c^ zP*n|Fa%JCiSwhQKp^v@RHpH|M_n1%`0=Wxz>6$vIw3}L-9#aj(mC#q~UDV=wYP6k1 zPgzg3xQ--ElZu~}80H*v^P1y6w=s#j${&FFA;^}eduGKQChv~^i<1c3WZ}M+$0EO4QcUJREg=#EOCP)S}p=l#^t_K65lmO!YKC%bTnu zH!{oKd1w}&Gd6>yN4i6$X_U}7r>KuXkT59?v5+oFB70KM29DxJ3{xp$fK8{4q=j;2 zK0cAKzzIB{adD^z1dk~k9z6eZuE3giwXGK{ljP|?;l`cts}TVq6jxRRyrVNDAQ1h@~Y=-?A zyX7m<2-q90$`bOG5|e#aLg))>kQF~ zGPtKIKyL4tg%xM1C@)TI7(DMFk(3gm2SC}Y5O0A8_2cDEY**xfRoYbra5bFdn>*F@ z2ld@rce_???bdg5EPJO4=*i(mkSE;VpS98m0-bYR$ZiLMXsp3F#n{P9hWX(>iAvIVOOIB2~ z$!gKdBACMzFP_|IHTIdhZ#Q^sJs60xTWToH%V1xRpvKFlnpPd4F16bj$>Y_HwQjr9 zXjQjr-Oke{LeP`|+sdLUknyF$4q_g3V+{tQ)KDXeCg5K$kSR4)5K7d{ieySHDU`|N zW|@>@w-~Z>5)0NsAcN(?Zj&-1F(bk3G|Wz0F26Fi8&5h z13$3KvkSB3(x}fKh2`1#*>Y<8*R*{UCEIXlx(D=_{}?L}{^;Py515e}dwFsG?t3~x z^6e`utQK1Y!PnJsw#?oaY#D{I5*Y0!e0Nx)deFI{O;I}iV~P)Courb~v#3*A&6=J8 z<2#5vK+3WxccTTf6q$}kMSdqfXE={F>NXaqS+UnorIsZ`IpZLq2aj^Yc@t0)wQDUn z0Hx?A-w8UXdl!e2V3n0&5*`CW5L|7yK-dfdrzP)Bl2U^JeN8?VA*cCMg_0VANlCt9 z?E^XG&ogHd3@=?dQ;0PKXJ}qC{796MyfSUD65{ZK)nH@Fu+D4^Iis4${tLVqf6FMF z!GHOv;>l7gjr4yihIx)aD<6ADCtBkcYMTnqImBmD} zUyY7wyOcfQYaI$DgVu-&Se$58HC&CBE&-~V1RA6cW`j((+Z#EWYrea7@62i(^mhT( z_Jfy22o(R_S(;RgDEne9?`+Cd&s=F0SZOoL-gr;f2A)8`r63o|IOdfI2M>uevK)Vf81m`Y zxY`NeLA3b`xoC5#kT4SwaglFS_c{$8+aH>{`pmQrXcV2|R(f(N;iAe*NhbSr(|(Y* z0H3F1)Y14ZlvM|6w)AOL%YD1a>r(zvFCO8$uLq4-q{MUai4jyliy=V?-{o){aGcGa zZwFL_LG=6Ya?x)h5vA)Larc>xxCR39uYFY8-hR4S-|aTXJ=X~b)NW%|=e+#HjD)M0b4yc*e}f-&@Txj=p?15E(MagcD`)1{2|J zOFA@5q5bqqc@+-VRpoHiApQx^EpOj6s8Re1n&3O9!a)K!5u5+x6C&cO>ZWg@Y0HO@ z#nhQaUnM}|>(z~&`tDA(z6~v3VQ&?c;|*&7?wCVbnMWllD-Y79EPt$&Zx4@IutaPL z^C;n8;`UZrWv~*zX${OEq|yCxw7)X!E&d2dJq;ey0FvF_Ujc0lj58BemJL$iOCE;*UYO3bk&> zq6w~4+`$dj0X|G}G#i}qR&|tjGDpZzVi~d-;rF~@j!bL7+$HeI5!bN+p)Zw$T8TS% zm3TZr$Mby1l=RDMUw--Jmy?he>eRA@J8wu{oB`_!vk(y*R+yM7{BnX63WZyRUryX9 z6efy=!o<6ULgBqap>VrUn7C3XV2{@dh3kd8g~IiTiNY_h6CDrr@9~RQoRpyU4*>_6 zS`MQv{3u}r2Z9dgo}_34?s8Dp1>Ic`&JeRtJqu(Z(Ar^yHP4}GZVP)&oe^hrFjfhL zp%4Mpt(baquHSc(_Ht}ZRqjl)-uPNWbd!i{@MXihHvWzo!QsS!x+LBS%;oh2mL|$1 z@DwuZNNmP(nWF}~;*gTW5iB2HuATyUSU88N!`O*K)5%#iHUj(>h=Q%9&b5^?riNt^ zK(K&jfn##djSbap#yW%*fu%Gcm6IU;{AH}v+-q$ce6Q!&*j^5?$9{4)fl0jJ;J}sy zlqv9xkFCV_DwcVZC55~T(3GN1Tz#Q2#;c@8teXp;kzDwR{!sW8KMLbhe50r&S%m7*v z*WYJq$yQoA(~`m^ySkod>P6kS$z^1brZ(1u3LUoPOwF7)ZNZwHsUqkPG=M*+tov;N`RwQc2zzV0zSgSk?zFq@ z-JNE4y|J^iw_EQ#J@ZmQ9??V7gD7|`v;R=3{lKCZUTJYvYNE82_q={6*0 ztM4}VI?xsh24@~OrTjO$Wmeg#^3ts`Tea#oEc|Axv2*5?SMAVsN!;~X?pUAZI;<+dB=BBOm}+GKaVO}Cdeoi)EYKl=D#r*dptvxP3u03yJ zQg@9#$(hzy&8w1UZE~iD)cMQv=yto-Zr2;T=RJ{B8yjXK^m@C#QCqLJx{?OMo-4FZ z#i>gbh6S(gH6f`OraWxdKRG+Xt2lM04S4l&b^BrMY2pN8>i{Ms0@o*54B_kT9mr3( z$I`}L35U_hR?TN_?fd?R{h{f&;CusM$!Z8lXEjtCo!ijHtfqA8WGRskSbtF9fTwLR6d%Ll< zy9GdaO)yeY?J$8x%%BihMAjN&yn;f8>Dp#U+(rC=^J47ZvD!f6w6@9h1t0441y>9A zCYbT`MB%pzC_~z$RALf-3B$mSXi&naLH8@9RDzURLCG>h${v=$04BU&1|!pf32tLF zD1|gCvGEK26561;EV@kURd?(;;LqQ+995<8Tfh3j6z+Umk)h4NG3}mFL9?VuyZ4(~ zbEIFBH3Tyh|B)gVK}z;5v`{e8n}-Ac^WdpMm|KStz>Usv4GX|I0&HD*>OAV?^%z|ddhf&l06=7Z+k z&T;$Ec8NuC;-21$&!s*Jo@h6vqB_l0Q-ANCo4HA149fno#^zUz*f<)Ej^-pW-vHF( z`i``+Osr<&HPe@AUx}mBI8{uY0*whIY{RDcrE4w{Vxf4IUmogXyPEY%Q6A$|zGn&| z0^_NspO09r_zx_WRky({k`jhjGc;A0F$l;w*c2g2kqJ{+Gw38F#rhef64y#gG+-*n zmK#h8rBo4H?ae9HgX}dE4-AqLA^5PQNN5m6T;y+6B@Mi{9RX`eb~*Vj7G#rxw-Ey+ z10*p5A*M@tI_bNnDG#hYgdXOM2qNyjzQGW@ zzu^1-4MVCho?1!o&oYJx-R4P&+D)9lWf6_XML^)erf#V`rmw zusNo|+5>C#laIGRt9wnbmZ*8db}@sgs&_q`*{g`7`UfmfjyC6>J`BZsI-6HH6vdoe z8Q{hBNkyGL>5yBL%Wj26DK>m;jHpN2EV^oYM5#?WG>m4+u1_{yGaSsoUmux=CjRk+ z_CgkV(6$}yT6SOV5~m;;apZ0;jwIco0Epp=;T2}P8($i*9DHX(1QC_< zpYbdP@T;-^Hnf`Dcl29nn*kcwzT)Da7YF{kI^h3zV&I2<1mJJuznC%La2z@o#&e>W zZ-C{`qXAm~?S<5%5t{q0IZ=nZS%A@1CYc@^{;WB+6KPfdg;c*I|FFK!3k01Z(Pf>`FaH{X2pf;4`Wk9 z;Y`V6Rn3k1aKOQ-@=9|H%<-U$NxP@s`Q6;1wqvp+GRG<+ zc>Vj+d!5adX}DptN*G!!qW7oQ#REvC4*!Jyza2Y<)_3cjjkRvIwS||alXg$FJkN|q zA0}ba?x{DOnOfNp38@-4lu)EFzNz)b?q+?fyHUq0cYe1c&)mysWe0@l^oRzx#5SO* z^+_qkWPEiQ7&WGF+=WH>kKIH?8}oOU9%Qee0Gx8^TtDgdbP~h9WN)nAlsbAbexq`a z1I(V5G%g7js;v zL*J%>3|~bepHoogqtp3 zHR>DdSg27QM*}iHTgH;+I2uqdax9(`#j|CVX5b&Wrs$h_)TP-U#pf=usp|5blVh4ru!8l8tuRkBVU(H^r8pG$C8x%YGmh~~0M!WCWeONm)9dLEYohx3!( zGm%MG0x7FOPNBl-{uDZBkaN>iY%Rm-iB&%nq^%nX~S4& z7e8#?(;YT{m9ywL5pl{?`MGjJ?&|~3KXM@eF+XoGqC3!O&yT=92_xu_Tr+}HVe+g2 zryk7YY#l?`rqIHiTgcL5Z9nLn9*=pQVe-cz#bKtxT%YdVW7DB;z*mAQV_ON{`;<*U zEKCc!Y~cWLGjxn~fCM!L9W0q5DogVVWdpyCXb%|izx=0V0|0v%*df|XIk6&Mh_?s3JMqiE|{o zeV+#;fM1E3=ExC}^^bXh4-y5AXpbfS`-_}?oGARLzz!LL+$@Xo82T$JmuJ|j4$aSfkF*z3&f7!BO9oHn?dTaT0Rx5tMoG+tp(Zd_vtS%sd7_82 z?UxQaSHa)~zswOcV1hGx6%ydU9;3ENi~gyTU84d^!M73592+F+d&&K&ivs=TICh~# z|KV5NM+tRE0RH5R>MoflYv_m1$4(Uah$*pDh{m!)ZoI9Oa(yW}UUGe&vRbo94yf`LK z>oD=p@dP$;`vMzlY&2v?EGF4`)aj5R4MVd}NiXmZXgXd(+(=phjFliKgsT6IODC{O zo`-qsp+lI;%F2R)U$8M8lo4YpF~%?zHaiV7=O#m(cu2xD2RwVAi-};vLsIdTEnas>n)CX zFi*#y^Hx0e4f_Ivx;Wu^Uq^WUfU^MJN*tc$i60#7L)Lp4;haRi?;;0p`JM;*dPtHr z8ZpjYP?OZj&t{2B24 z_Ryt_7gYo8yv9KLLyS2D2~zQHh9w>mj)9sUSh#UkOz``#AH3+aGX2T%@lR$LAJ z&g3(d3s7KzuL*392u#2$r~{W=0B?VU#9GbOKXwW`erkG55Y zgqK%g;L6JKVi}_py!oK9*RDOR)tV5OzrNe4wH{a1QK|9r-$qptgCFsO>Py`2o!Umd zih2}fH`GOwYw+q$t<$QnC-^2lOcg#DRDYWp*mL8uYNxtZZL1rul7S>bzoym{9<02d z4FzE3z}{7B)OR_v)_z!TqEY+fTC2W^F$#Dqs$cn;NLpet8az_h>+g9669aUF2KE{L>sgO5k@j_MGTg;7dSVYKAO=%(q&# z?OL^6W8TOj{T&|_Rl54HGu(_GH#g>MpGVG4!*BJ>#pUPKoxzh&7S`70xAx5qZ)d&X zE`R)jMuWxb)+dc8hvoXg*73%&Ynf}Ch@kD-daKp}BB&s!y2Wzxw-Myqd(B!ab?PJz z@!;bPYw7smcGEsOGCT9;{NwPGc6(vCdi;3nXnC~(-M$gG(ycb@-A?0S4O8{;X@W(N z0GZcVOg!5knvN%2)?bzNkuv9v9qU4ew`GMCK~}>7$nvkt@~0uB2!Q+AliJ#YM&qH5 zfJvF`>V*v&K&1p%J6Dp5DaQNY&7(*tkt*@3#2y z;|sVC&=C2!0<#N~VX?F20R@6cnowNj4^FBRT7ffC>+lCjiS@5@y9;f~ipyyFnUz@c z?Jim(=*-168XxF18voH0v!P-A3O#D*yWmk zn9jH&&$sPcW4OYEe{-7}?$Mo5Xy8m-vzNYxJk5n0iKiypt}tuJ(Y(g5E9Ws0IZck` z+J}VM2wy8yj>BPrbG)8-VGno)v&DnUiIwuod@_ZK zW21C^JM*?d1Ss6J|;X~&WQQ3J9h%>nOx4Cf7uJE!{B3MMFbaTi0cXd z7`>Y3ml9=}w!t8=CL;M(Q8A=igI8Rgkb1*42L?&@BoqXx^$x!p7ui+wIg=u(8M~CdYWvv|T-n`(R61Sy8kJ7MCh8WNjUF8x%?+ZV%uS#K85Dl8e}}1SG=8y{F5)Be|069; zj;ExwdWtb$N!Gxj;o|y=AM5lLf0bu5je8U-i#B!lOK^oq?S!GTw5>iGIMx8(K_b(T zSy%HUa3QtE0VVrm51lJR)O$c=NfHiB2xB*cu9s%d56s9pq?j*m1`V-?en8Wv9D>xR z?5{CYCG%4P6^h=hL)CsL4l~(g3NP7L=Csp*AI>R72tU)(FNH27_4*riNPYy(KTGNm zM(1->Au;rGkBkz_-+~~L)dH*-E6fiav}qb0z*mB|tVh^+q5LFi>a=%x;#T6EJ)@F} z5XoFHj)tL_Do~_LNKs8+D#fJ8%vJT~XUxDmBMdNuQ^>6$4qRl!zxY)WUtsAZ>xnG= zZ%^h~XQ{ykCJK^=7>5CRk--GxyCsE(5RMOj@1!fAi5#D$A1DW5 z7e6e2U3XaiSePqfui~WK^-B=TSRRbgz>G*V4m>bWJjP9iSgpkIBA=L=lP?^ZR_4&d zK2j$hEGIOTFrns?M~u(J^I|WP3#26-m2x?$K+FMFb!V}m9Q$Oj`Q3U+OT$D=To8oE z@fqP!_|2ERb#zeg>D1- z6#~7?=z#JEF;HUb0xGxtBMz2<-w({;5N?>@`iUM=FDluAb4Wd~qIJ2mbLPH@wU@Hu zSFC7-;VnQOFPsa6+C|a9VP$2d3^x4o5X-<}V)wWv9ZtIw0CE2Os{t@4b_{_o%(X*T ziQr_MOYT*q>v+jA=SBO+F&VslXhJk~Yd}lzToBFcOeLh2yhbAn%P<%fg4cg~LO>$O zZ~&L&6NM|xRZR5+yMl}ch^XI_5ZiOSBZt~F7&tV*k;2q3!$2-hK1(`C{W}6uqy1w6 zrtmj$X%s@-01{HqCZlmU0B=rbUqB{A7r@lFqk->H;$yP5gUBC|L&p@_EwUf@LnOG6 zqHP>Oynt~CP#-$OkxR*gPNzwldmX3`tPFve>NJcpfFJE2&xQlVbX>V%O#pqgpO=X~ zyQm1FX&+8p34q=Bh7Wg0hZqax@{-EiWzZr8Zp{5tf*-aCS4AJFlTmaN@JaCz2fiL=aIyfHj zKJTHNok0?8KgFIf1@+gQ6PGXI$x)A3AtR#w-sj%(y_MA@;^N>FhgJU0>5y?6nl$h$ zKl+ha;B>laKtgc~zi4=oqTUhh1uxXNe2VrrU0G;!8`xvGm%Suh?L;;M_ne`ip zPp;FOi^me5v>JiquW*3_el>V4j?Y3toSU!)@5|Hj*#V6r8o08z(*D?W712Wu_u_~? zr$hArkRy6LPl4;EIN^?Hk7tDl{r%|33@EYbzT;7w1g7oyB%l^`4l^#Bih*l(e#$wB zjhv)r6M&Pzws^b=ggfh!>d%wvq}eg{8Esgh3OJfz>nv_Y?2g!%)Qr> zqI?1t5w+*fze7pDNA&$aU&)iguP1U?GQ;n8s@vO*C*5kNQ(b?sQ`_xyHyTfNA=4GR zJ{g%oWcLz2FlhR3Ygwqp1@6FzsH zh#GTqbMGC{z@zT$DDa7#2T$%cAyHnj=uSqlI?1Hn6x(W6nTdnHjYKLV9(P>|jrR8W8MSYWk55}421O;9+4)b?8ZMFPPM0X$-ixwO}a&5KmD z7f&-n9Y<&;%%$<3cUB@Q3a5&yI3Jg{d#O?k!SQ|ON?{fBf_Tt^l=$h_h$YCcGU&kr z@Aq;Kyvaly=u4*S0@3~MW@j5hpml98?6R;0j@RGuZKgmRIRoFNYQoV3qx%C<8)6z@ z%A&40a=HS0*x;}UNtva}`v!{D?0rlqx%UMS%S(mtvOpo%mnI8^>r0aNw7oF<>RXo! zg*)&{=zRX>2_#k@*-O!qvtjX{1a-yvT%5Yj>!|A=@-ULDaY0>oX>{ZV2c;v&rle|< zHP^QwR#)n-cjS~{mu5X0DMXN)#6xjygv6hCKF0C_+xwqNE>yYiWmh(;_J$2FhvMm0 zEQ=m@$;NJ*G{Foj#;xMgg0ZwP7g;02_Ck>tsU)6{?ZorDWP|RTV>ja3DL-J{fQ(pN zDX$2Xk7y@ZKE%Ff$!2Gp967E_`Vig&FX2fD_xGXG2j<_R0oSI{sk@A&Es^5l;)1cb zh(ndl?|Gn#qFzLz0VO>OkrjJXzV7*D&h%0>lcT1$We9u_E2NTU} z)pb0jY0&$Dq{4J{=`MCVO@2M9^iY{!Fe>v4vJcHg=Uvi<5YF-G2RcO4vLHql%nYJE zI9^19ebb^4@;o9TjnEAPRE)Tq0S#ei91miYnx5?sWotbOe1I9U73O0*nSvN(S`ZTv z4CXGWi?!W=*y_PFghyon@$+8b*nRqQ0sQg!VEDEd^1v)G3~(GF;lLleHbhhnX#}&K zHeFYABu18@j7Fl^U9#bXX3wP|@uCs207pr9>{(2?8lUvgJlKYShLw_*58Bp7o0sr}>I zQ39Sejw`I%OJrK;TWDl)+-6`gaUmUF?F|iasW3qpcyyAh!Q6v0~(+jf2z4%`?$NlU9Dqw z>&wuTJe?+AYV`Nt{_{*D3cvF7ieuD4n47G3-rG988jKRw5Qls5!)E~wpFc@yL;ofZ zWd(hRqo?W87lM1IApvtGe}+eZ$7LP(k}j$#YW8)`6#Q zHJRirSNU8T*Iu=Iu_?l2 zY#Z~B*){LAe|RHBaPRVvgIBLHiVVL%kx^*4>d^?|XF=r9O9g$xYDF~QV->$fWquZ8 zFN)pR(vtNbN%alep*e3KHp(`7E~Rj#RDuw_0BTqH8;JdpJs)0I8uG)aA5eY;xNm<2 zrO%>HEcu__XBf%`T;j_l&ItCD#5t8Q1R3OHpjPAuUTFMp; zBY)KG2c|{4BN{lq&9o{2N_mUJK@R7a;yz1+BPCOn0HTZH&t0sb5jsk$edT>NXDkcy zTx>oGL@D>BxP-W$AW+yyI2d+A=W}Y1;{4L`5QRbxg*g+{VK+9FI*=F2K%mWrB;|;c$M3Y@rgu6}v2+i(g{X-ngI8(Ap*i6B~f+ebWtv+$O8+(rDB5kLK#l z&HBf@cuxC#tX+U`1IZchSiFdwA?bU6^wOs)sIQd%?4Cm5&Nu1#~~4;Q)6}$=!|1 z=Ri>JV}n$Li>LCm8~9_-_V@P=B zijd@qm4h1Vty;BHBOM4MOX{0sx6vWBkL&GDJJCORpO1i@o_(?gaSl6Je0OhqTijTX z@{Cdt_o(aG5T3jBer5TCZ24(2t=1TtRyPbR_=TwPn(}2M{x4J04x6F)I|h4YkAOooZc#?U5b(=Rha4>M>I_Dg<1en{R|z2kvSZ79WSH-c%A{V z#z)A3`{M|okk3I!tJ%nMxl+RaNx5v`|A|dyjgLpRc}m;r$eXSDPPO%vJghw>?*mng zQEo<{68pmjeJBz~4#)V5PZ|8paHyJzVTJ!C$}q|ZTLKCtbs;|B82`a<@IUy^38aNV zfl5=n@h}S&>QrsY+`-|O!xcj#UfUqo@KIsFU^qYFq)3X`~LC_ znT{;>a%F~0Q(kcuTV&5G_h!iS*yjI$Qp52J9K<}l3Gj^=;yl(2{Zceq#4-yoXz+Y@ zp)vgN9qbJiT+BE)(qQOf@NZQN%n7CB{yX4F2M@mVB+;(!f8P3 zp6|U-TVIa*^+!>Nt*zjr&@b_wT!G0QIv(Y(aT}a|zGZ4Q5vn3p-{aEcH!7WrNUO`Mal8RSSuB% zc^L4)RVuJj?mXYR$~^WOIxrIf5S0a}5?Z3n0t|HDnigo$*u{)o+x7JhL3AM-4WL>N z>bqMhJl<+mcRQqA+pev5W;p)LkiF(cwNslR8@26Prv~k*{0af=La|yjcGgxHX-HI8 z;=%6~Yca%XBHo0YWHJHc3dFC+(3~`e`m6O6k%M z`dufnSo^+fgKRM^3$blc?$HQgS}J9CiL8ifo>S#xCa6Lu@nmv@)*Qh5mts&Tnx5xJ zc-t2i4dn$PMX>%T8syWsFnnZH_9^zpjvLjzqCqMGsl}%#fjQMSuM4YFG)R%pxR`E$ zkmA^}p+u=fwwO+wluF56Cgd0CdPVAi6jWs5Y3A*~v2)*n;r+Vr`T_JB^m`_ps`EhW z=7H{4KFAER2r5x1U6o5oenhG+4WckxTFg0CS|n%l3uU!Gi~m+;SfsXtf>&mC#eD~$ z{V6qrc4P+8j_*Z-qM?d9u+pPJ$M@aH8Kqv~LMwY%bRF*ifa257e}nRx9{{dAXwz6lm>kco)(9LG|Xem zKzLx+qXEd&zH7&JD)N&W4c4>9VdM`rgTc3^>+}Jair^!dSp?Pqb=?MPy7j}xLITRL z8D*!$M>8@)ijEhKs0BzbsAck_sZ|`BqmkqF(|{sA>iQ(RPUMgNcn0fZMpTuuN(s&KI%ICj(5!Br6`$-;aRy$C5D6%8`Kn0fpnNnF^cc$`__ z^Z?RC2tGxAVrt;02*~h&I{g6vZ802X7Q49b`w<0dp53e>+K#mRBWM~SK0OaXL7+e} z(>5WnzheuVSLvbn6vOc_i+u_?6`x`_@L?r&XqQ?dK!t&S^njW+_NyeDVxLs`B2%?; zMib^ynV+r9R#LMmva2STXnH1^g*)Y7Cl{si@Hr!;hfiv%SR0NCECwjXoLwJv5G>du~NzkR8b^#5(L6^lAjgdMWF=8 z^0OR3+pi-hRMm| ?GNiogoE;~^du>3wA%T_s$ikr|jn8qq)p6Y`T+AKMUzHq+f` zMlL_`ktG5iHAVkf3=*F-?GqmjhOtkk6MfqnxPnjKRA1^+3E25&vG|^8J}O+=Tn|F-*<8h?e^6XX`pc2M+tCEO(z?vgE&0%@rdjLbXD%jdnYv^i#4y z!LS-NC@DCJXTewuWe&0vrGw%qpx}xq*g(l}6xoHTWZ@Q7F+DOf!dEe(LrC8aYoFYw zGb(k^;~DST748P7(S;+OaV<)$1Afgri$y~6Gw;cRTC2uuu+fwIWSTirO_S>GhT7V8 z{b7wv$7pLvbeMy*BGo+_nMi;L2#<&JGyb%6!bvhPLL>``RUI;anlOUmI)@F$Y{ z%9fGRhpGtS4Ls)@qTm4kw5<`p^4DtT0@|k_r)Bxg7;@S46v$0tofIY&j;RRujcKrh z;26-A=O2}p2Z=>4Mm~QdJ4!b({zKtu!21%Gq)q;B=11&!Hhn<@!orjWV5#=vn4sdB z_#XQ5jQr8qH3MP>0RaQixqzBh^en`E1ewb^0Iv}6m+7N(7aF5tJfuT2a3E`Q^KlCk z;?Bz6!|@2%rjYdfXh1rC5P(xxColmTncvJFiwjd{Lkwq09h{wbFChu3Ido|ly2q@? zw(priFhg3VjlbIt`vn(@7R86e-`}TjY+?*1mqwIN96ieXeKu;oCLwFq^TFu?hA&0} z4ou?_hG%jDYLoYmD6xI;dof-2gW0rYEnn|nMJyW8_r+W&_`umk2e;fk37QevaZ|-5 zREbQlF0U>d_?7QSk@A_iUic+3C%%ExuNh8(c*evB$>#rXf}_|I#o6Nfc$XHKy`B>d zpU<%yA-l^B55d*>`>pEQTD`OLNF)R8Hn;b->boq@Irc27OXr0`0X&hf6d(iLM4@mE zQgTo7-u_=MzpNww|2`-GqGLh+ z8zbs*gLX0;yOC4E+jh$bm$EQ4`{*MU_y?4G6;yW~HIatyJnD1=*-p_o(Mg)d`5q_? zbKRo*5Z=zf?}Y2KXf8o{PFYJG@mX5FHxC)k=M4ZF&Wn^t1s}0*@L^;8W(i_DE{IT?&7e;s zgRq-#Q7+v)E1&0Qs`Q)fDdz_KQ_)pd#y+2s&z?uhzm90H3r{|qMGkTEIYibVxl%h`s=s7ns#6?9YqSihDX*@q0XS7i(Nl3dTn< zHgV`5I$$x5{zhPa$N5E!*vk1d;Ir18NU(BXt&(;sWgX0-hik^O^lOgG$5-y;xO{qN zQdTuVjdXkvUp^owX)fsA%Doi>zw)`Dv$&IjpXSEP*cb(v5JAY9LKd?VdLniK{M#po z$Nq`dr|x^t$L2BQz6_`hZcFS(4g0~KTpY+(G(djmpMyfefJ}F;BV#HB&LPC3*bjU! zDnWEt2tnMUw(UgZC~%^PdJtftH4f8fo)RR9kJk_S{7(h8d1_A!wOGkh4o8xJs*_em z2UjbVaw@G}4j~7)nK{p78GPUf5k7wVOayeI@DBGUO@*}9L^}MC!w&pP@@S46#!-Ew zg&id>7tGYL@P|jGQb6byd&^&39FkTwkVJ}twZajIkXUG(Y>-y1-61SK4~HcfCh3{h z!5AV|I3VVtgv=#iEEUk9A5mT^O*IpcmY%c%YT#yK_93DTM(8M60kt7@tLcUS_fXSd zKPfnbH)0JD%rb78teR%Av3SOH3+}L$$k!=qxB`xR_+^e9Y(QZ%;G_7X4I<_|q+@HK zm$}(w2^gpeo>gNHbOR-Dx*SnE!N|llau^uCRPW9ozgu2<_n<%iWOHrfc=z7=)6eUl zgvYDB;ke;Es6TAcRcCqq!HTuD{Aj)T$+I21@}jr1NNL~Ug!(1G0w7`%nh_uW>8(5s z*X@Fu>FPTA7g^&T#^Xpkso#j-+C2!6A-w66wX*K&YQ0sZ-Zg7rdVM;?&h!rJYE?KN zM3-04TMcB#H(QOp-Hr9VHI|-oW3AhH+C-W0Ry^FH>9`QvCyzb>V~t^jq^tX@CzZ97dTzrIxw9(`oXotl4?0z?}M$L-@K3qCFI!Xq_{BLq2 z^k2$JA@O}Fe!EUDFoWaR-Q1|$gVj<|@DuIfy&&6&$1VQ?bmS*^X@4{vK#ZY%<^s=`M2gtY5QD1nW&`*G8F(q{t= zobX=Iy!+x<(Q_N|kn->etLdT>Ke^Zi6lt~Kke?T zzT28IKsM#d*;0pA{g%>o=JW={dQ=ZgmdQ9CgD7}&ySnDUU_U( zoUi_HIi)7fe|43GmB@}&A^E)h^f{5!eoX7{FD^3|ew&V{XNO(iW55EP#jDD}nR6eqq=uG|yRQ-Se5aSDJ2SnBji7=e!^^Z9xl zSr^{xID3f5f+aAF)~r3p$(s_FC2xt8F4azQSbZ+2spd8;Fe_Op3$Hc!@PMmmpwU%d z1il}!)F$FkR^Q@HBKw{l4Mx8K8z?@I^&M4dbBXTZ69vaz@M{XmyW_yYuQ@V@jX^WN z`K}rCL$#k@XdC|Xdj7fv%XyeVnE)teQONYtX){LcST|5(2EPJP6$2@vq4F=dzNYcnEY(^{b!^87tcnt z|3%-~|6=6zzp%pai)Y~%j^%&xV(5Nx0o`fLt( z*4Ns#YU|_OtuBU+-fL|`$2@tfnyC4^h=n0FgBMob*6GxDx7yui zwevt}N;15c3UJp3+-piHbmP!;59l$vQHnTZH`QKa4)!)5Mu}lJM74+ZUdIEUq70?ne?pNFz{?18S%gMs;at3pvFjL?o zACZw`vQW4)bH0j&JIeJItIB_j6@?LIDX>A_%ic9A4ahyiy;Ia;UpYtbZ|GRx-{$%- z-mhSMQQc@Amja5OPncDAU|I)YE=GnlGNHURAb66cxn4!*m70#K@WKX?j4ZVsZhnwC z74-|t531qN^lXEiXWy`9<}lS zz_aIrKOkLKd6!6h|Lxm8C!(W&M3(&Z%8o-9D_6`-Qdis7_mXkzS1dRW?Cj^Yw2X%Z`cjp#&p-?$F?TgF!KhY7oO+o0y z)(TSBh6hcsoNz*75}Uf?7nHbWL_O=645=B817>3e**egW%`!dl0)nad0mTbc+X+Xm zc`WMAh(F*(6ga&xvWzU)vUNZs;`(8jHaUYwK8>6M8q8V)xkd`9gKT>d*^BU z(RNp)O+hj`Gy})9dvJv(s*vE}=j+cGYoGY*i+iKha$|9{yYH+l*bnx4wdF?T2nZK% zOXwNYwp8;ovKbxv^H0Vfxt}!Mqt%VA=hf$B`}39k7a!gG+<#Pm4sE@`#?@*xI*A5f z<%65yb5OH3sq>m*^@ZME%t^glxq^oA*cnTxvQ}MxxYz8ma476RVGC7JRzlW#jn#ss z>o)gVTZt-am|YW!PK9 zac-=2A*fWlhWA9GIGE`OAby<}?~XkOykEPPgTscJjqUAfr{2H|5>a-L0*kR9>_J9o*PhOClHtD~MSv#&!)eU-wu_Fc{Is-cBc(1peIrW4$t^~7CX|P%CGee34KjU+hAuBGl$UWk zp>U&>*dH~G+GTIdGVNT4g00$S{bLo1a*MVb>kpH=uI`)|lGq6#3F012yi2B294^;>SHx_|;H?d}y`IaG!Huu)H z>+2`gS49XAPe6pY9pB6#6^tc`t9B1X1CiHH2Ce4+AD2{8RZYY)L^W52m@ELQWVIx3 z%PUCE2N;#g?BXomV2U$duV7J(xp8ppyW~WQPUmz)FmRX2}vw1f2fxOv{#6FgGjM<$aZJ+EaEL&Su7K0=0Q!bAsWDU5RaG5 zk5|N}8mS=3X+lgA#$5^3gyh)r-U-qI@uyDVN0W^v*hqsEPvbyY?TjSB!ChWdB07K) zh|MHtLGf)G-n8q=+exjf`ZS|9DpyVs#_p2RIX>7mA$Liu_Gqu(s%;pMqp-dXc@c?F z8pVaq(XeP4c*KXbr{I@(e%4j&Dm1d!I){{~R;=wl#_)dj*aB&sPX?Mq&O8}v<}Rf7 zF+)ngC6s4BgA7@G8lWy5Q_?#omg%~nLHIc%QRt!OL%p#_N~K_k9%!S%h?qg-gr-Lh z=&=zuSt>!~nxD^-b$@is6EQ(Ita-@CqzqHA*an&2>#U0lI2aM*(;*9GX;18ua>9U{ zcUjyuePy`VYeKr;+6IA3BGT?OTGg%EOM#J`W6r=!H>Q##M1Pd=GW^VYX>%3w@k^N= zXS^}q5NF5*d~y!iAVpxBm8a{m#9OqH#@Szk0QbzHL=iZQ1o^-Uf;rK=k`(qd z0?T{Si0Q_u0=^t2?@Y>;&+N5_|hPy zvDevZ)OWWCaF3*2Ydx;DUJBG8Hws^n>;m|6u>&A>05cEAGV=K93t+E7^ENN?6ku@) z0SeII8wmjEHz`IqvF=zkmRk()!&PM)bwF4O_-}(u3pqyYEjFXF_9wO^3ZB#$m7ZfU+aP_GR3gK63AHWUA%Vw<;D_Lv=zAw|(W+d88q(&O z++<{6f$P|3=(|BI$!J_5-nz>?#o$-ELd2;H7qp_xn^Kg4_7y4QGW`4EBFIm5BFOi+ zix@Pm${!^bP?=E3RXPfske1*Q%^DC0k^-UN(vpbp2D9eKv<7t657a9ORxf$+024#t z$Oswu3T{}!>?H%_rd^P+#SFb4q1pT z|MHozWdZ{TfZFNCoJU`bLsT)HM1TKPZV>d>60T@5{naNaR1I!?&xEMJth)t;6klNj zC=@293cs8ng+k$0;g=J43WbScp)m1op-_0QP$=9k6eg||3fI2;^2;x;6$;l2cMFB< z6BC7BUjI;s7XO!m7SL@8I}zv+I$qzULNDa+Lxjy}fQVqau845LvVY=5ay0mV+xz2?*X zjZkbn&(4v_x|ctuwz|4Ht}p>D6TFXLi5wH1rxmS6JPcwu23Ru`Q4UP0f)L8^V(N!M zWTj?>VLm@`4va1y99RDCGqVVxC&M7-)LGPZaxU-_%k{a^SqUtIfl`fC8kS?K={n}0 z`gB8YbN88kC^C9r0Jrw8G7!r-nHRY$aWrONKM6t)!|RS*{SV#154_+mKPc z=-nCB_YyM0fq>gLU7v<|yRbD;YeXgDRc??Nhy~BQ);R1TA7Zn=>}z{AKzk}`f5tHR z(xzN%zMU+&{&h{m6r8zVb3K1(aI1rfb$bm)+_i-@-`0%1jrd1SdMMQiFDv|BPM4GbH+Sh)`44R|07`k{C)9Pl*ySYR53GlR9wFc?`kwN z0$?e^EtTR_K^O(?$m)=-SsKR$bX;liWR-KjlB@<;hj(CqhWm%Qa7vvUUv$~I6KE*c z@McszZ5MAK=`1tTa-rO+8g4H33O0wTWHffzuoUKsOIhM@s(N8bU@^v@$4k6-{ArDFn)pUM@+2JxUCe}?W9qX6D|c!6Ml zuNWrEQv-~Zr}O|Z==gDO)bJ2v6eT=U;LvvO&a$yRoN#*y9$vd0)KDHCK%=*(FLFD4Tq7`1T;>s%bJiH@rccU1A zNH0%9BuiF8Caf)vQ>J2_9dmDyWMgb&StdAE;UQeD*lU$X#D!IEvX(bnO)F2!2gi?V z#sWbSb6;m-#RE;6XJ({H7XT@M(OE?-zjtb4x;{e>sFW%X0UapR4*WRqc;(TP=gyr` zcdJzaKQlEU7}t*_ycCW!i%TF37R5k)2Q3_tbL8E>{rDgH&7b_gnE-N3K|{gbO)W77 z7>tuJ6x?2Of>j^ho#Ps2FnIOULdmF#yPjl|U5>mcRb+T)#tkt76dyq^oW7FC^5c#h zT}i)3k8TGGipd9oKrjRb^%kQiYkh znlK?U+*~9#n83ttJ)ERSW2S(BG=mn%2su$;-=X@U#tc13eYW*UcTZ0z^-v6P$X{%7 z6G9!k+X<~nEo{+E>Y6nQ_95Oc^T8}u7QL*)McO_IAB;>&~zAY~(3|Fm#{GWw|2q?;V^qP2;PVXdkLPelm!Y3eGjZ zb>{J-N>?ia2p%e+iF&;`GXWbnwNC5D79ijUW=V$agT#E_5^;X6ue35q3k9XA*^#r< zzT^B^d0IqRcxa2_Te^|(6;~C$;>;u2dt@de1~!wh$xX95`t;oKQ|J0-b zOqTf?vbbBCbC1+Soi)P^i{Fr{7!yL%=|YZrB&Crqv=_Bp z^9>zKz9L)atrJDO^y2lOrJ8TfCW+4q|P?rKs39&D@q@=@|-UzaX-vzzQ zNID{zjX3py#8NZ_ zs;3wcEv5aBR8g_=2SQPScV@w%l%HJ*fVT#?MKr!juA_XO;KVMJpQ{oWZw5o-E?U&*phia+CD2DONBwsmqY^igK|Tuf+-P2q_PupZ2Ro zG}6~XeiN*DBQv5LL^j8=8N3$rhaxOtIVhM^CD{omhV>E zR%E-Di8wDWGyo(8g1;uT?Rz97y7f7ab`QmH52vv0d+tEGU5yWrx8kGsL@2? zgU#yCl~0G;Yb&mes~Ie)TKZYY3}{9Amme1M6W)48fg*jcoN@*R1OVOup#8StcrEsV zwwP?bASas=n`TuJF)n;<%{opr-gUymGgFPpii+#T{49Azo;yR4xG2`#%%4_(H=YR_ z8&Abfg@_JMBG-=fZ$gYQ*X>e(0%Kd=<$rq-1AksIu-Pk$h2bj;203w3<%^5l1|tho z9B##H)g-9G(IY%ksaXQ&NuHHi&5L%8YvsuPN*Jv2XtC?Tx{(}8sN^c-iXzDHu@FJM!2SwJtAw5z=)>K=nPXTrsO#uJ~Y{!o}ou(-otFzn3P*^ zAXG}5+6QcrNNJd8z5bnsfgnqiG!*=&O{z;ESwfV(D@CI0mh0Nf{a3lIk9TG?5S;$b zLb0;336_%;Fu3kkMKUUG>x}nQ5#wFRGoGx1;ytSm_r84f2;tW*AK0FpJ4NQd_kgmraAq=m9RnTzT|0z#gc)hX{z! zf4HxAJaf&qjltFggkG-2ybYNrU5wqj;>J7*{Hlskn@ZbjP+-SPfdRZn1FI-_2|&&} zYROW!)AB9XOY^=>fv0gWP67PR8ef4YMB8(XmEr9wff1<=Q1|#?+O2@a21R!J>ScMS z72XB>S*+WHgGM+E!o?iHqGty|nDD43%Q0ATL>0Z|De&ncPE_|5%G?zLE~Hw`{o?pu zCGfqq$|+naD6CPBO1qWHvo5yTDsiv~IhK!CF1WCKrEgMHR?Vv60YNfJZC9@y*{I%T`9>bJYUXZv7lk=dm%z0R`{0d*A z%?G*yaVhBV{X#zGu(YxKwMvjI@dyVbY)+Z1T7H7`a%Ro%i%X2a`o?n?*T${T+;D<8DRP|`_5YoTzidDUZT?NC#a--gy?DRW#A=7AfoY7yD4c4R@M6udT%8)%k zDTB=z-#uUkTg{k1dH}D#=Qlk)J&h~Dru9(w_5UB~6l1d*4QNPyrSEdQ#NK6SpKrv6 z&%F!J%|Chi)N_|`?)uWDW2fh5=T2!I_votSc~;BHm@yinjTMn&UIkUKErjzf7?d9( zl^(qKYT#Ags*z)b7*`s!c{;cci{E0{RjPyE+CX&x-Fxre>E3kAO$(I`GDck8$gK~# z^F>4M?@8fD6ujA z`;1m9T7PUP;Z!PpX=(HR;>4Nioa{4EZ%isJ0fxeU%atN>)Z>q!sni{K+PFL&Qj$LU zaR^>-GDf5RJqY&Iq(%I@Z+kxnaJMqk+0>3zD-t*LI&|HN=?bc><4h+?X(;xa0Iyu= zq%j-A&Ji!eQXJ%9skGqoD2gs}si z@j8w6?R@g==~L&9pE{_^uFcCN9mBAFi}q6SHY3 zD%*2QlBVd~ElE0@2xs)9$(M|WTrDccV$UPCo@UNOqy=E4zM@L$!eE4^wbT> z?oIDMjmQAp<%Buy`Zvt%QkQ3taZq+X`@jb7m9Q<{JD)wGZC9)~wtV+$O{1vRv0QT% zks@!lFsu91sZmvNrZHtW!!}X0^?C7B(Y*M%A>ofmS6)qw7lbL1ngVG@DZxO9U0{Ot z6PJtI%jL{haebVE7T5n}r%Xc^-it3o4Zevvl()pCB#*P=$JkSdBL{B&NQ)RPYsXNf)Ippz7@HN4i$J?RvcLL!>&s zn9htvc5~{0T4VRY3uM8!-Q=K^on%Q&{94~--XO!J>B{J-_juayDsLr0*at|uEtf#1 zZ}Z^`ln(RkwsUjmgezGIm(Gu-L6R6z^vq}+4@q~mUX?b0FXwFlG0nn|;x~$n1k0P3x~hgKI`S)pSkT)Ct%H<%>yh>_&Z(sgl6z`Cu{`Jk)?2I*q{JwIn0 z8^hcH%1jTEA%uwVu%JrHsb>_qz7}bs-k64QV(JPVIZxCZb#1aJ=!^<57{AeO?Ii{K zv#(E|g5@<6vN84}!~K?pqs&}4lvjhu2_qiIVAKN7-~&$Uy6|e)LnQ{Bmbv!Y6$c%j zDWjj4?*WI7n5uy&Td|yL?_8w@#FBQ;h0|w`ojH4aVF7otDPOnp0u|FsY&P#~GPTB) z$;?GSHrbt-(o7WGV2Ns#ER&gIquNW@z!_`7BH$-BSOolnl-x^fidCyhyMvQtxOP}v zZ&eMwZrk+_lh3(JK3Sn$Qf?Z|{rssd=D^ImAGPbz`uKtI$?75;-%3L2Vz! z59%H~;(>Se3V{bc_tZ<$`w zw>9eZDe`o0rcv(3(=_H?!FL(g$%9_-{M4cIQ^&eZd-CWaKYBWTrnT$|zvXw% z&z_x|J$5APRTwQ)9;o|Qe>ePfl21rodlHg(gzB4mKkYT&NTMolB!P1|HBAmbZ?#(P z)jpHAk!apXR5@jJmZP_?HZH&88RzuG$@x=u&}tu;z5HZ!(yKpzQ97BYTS64YpshxB z<`vSL!eGB?-^dep#1*tFH}@L}(y4Mckq`7|_t7z1fff0>M6}|JJ9=rcokD0M9&!QF z-K|{>%Cw!p>3YWg(Jqlj219d+WVh#&al^lcAh;^`ZKVY4H<1rJEm%iE`HWoeCl9Fb zdnhYoc|1zQ#fz!x@OC-VnUOf>iX7gU&w*bR{-g4l+$A%~#Vd8>-T8d-XxS~(%W%kw z0x$64YP7(_i|j%mMs4NYn}?5MuV$msy3=lzCB?BCt zai_pS(2Zrv{%{1-8hmzXh9G5g50Q&s1iej_c3JY5;}GfK+u1Nj-9b zpCYYK_YrWxD!y=vow@GhLfI9_pVHb?`j1uOryzhn70&juNh|&8oSoWdDq3!#1j1Ff znEHiNW_b9PQDS;kPR3_cFAF_%cJ7_I?u4C5CgecbU1ol{D-o_I_)yxMEDHx+5N5Lu zx~i3Zki6g{dTxz(tKSbfP78O@jhlGpHKYWvV=b3TWcb z93=P?tJ&HOPNO6&h|*{!`_p~*Q%pIVsL1;O%oxK^oFPsv}iVK|-y%iKS_Z(lB0sq+r++h=O@j=UDxv9X7* zbgea*C137_YcNU14u-Czp{yH`09%)w&K5~dKPGomRPG8rN`^{rdZ(kN7M;&A{n?sh z5-IXj4&-Lnk!N`zV&vp2Sz=C`XL`!Ttf1s437uih&7h9hLZ5oM`97okdd3tXLFMU| zU~^Kz^fcU2E8|*-9Q|}$;Jv3_L-d0$Z_Rfzap>W`99klW=}ObEla4HMVc`Wm%ATj^ zo;`l#^y#NEx{p!MT_F}rl@5$y&6Vy=zSM8cr4D)jlmy5)t=?%~6D+nTi=9bw!m00Ubf=EpYq!i&01&84GoeW!tbq<^SL5&engTp zvR0%#cg8gSB1b&Jabg$+IJFVuz=`F0+{wUMt)QET!y|KNDwgA5LzT{-NKXej5;wNo zjW*A(&7Cne+GSFBPc-KY!VJ+w&w|^?%fMd|{WYc!9XK!}p6Y<*Mnm?+*9$QqJ?dN{ zyN_>*?5^({ARUt*m2Ls?PD#=V-8V`-*kcj^?-W2^B!Io_UYfl-RdPh#I9ZJ4i=;#m zvUOrTUqq}&B(aLj6}g^t+e_7uM`W((FTI2yUY2p@_JT5JQq0 zWRE9q7G*{k6UpvkjJaXVZ7f2#z1ZH#7%MTx8ch7*jZGdH3GHBv-NIOT02f7+cEM0% zJ08e%XT+~`d7M;nR_=D4Ysh(YDU5-+7@jxZEml!MWgiGClLB>Xho`CPCYhxp_n^Bl(9qNJN zudT1I-yw^~`wrrxYoiF?#;(zY7`LUV0?)PAF14ee8zS`pW9(j;8gF>+MdeYI0kdEL z%YrG3dl+Nu>+7r~6PwjoVT`rdeDi*Bx{=+7){HP#UI(txOw@|qg&+6eq3Q{OV0SYF zX8-Oi2rFnP&?sveTQC~NZ~PrK-taw)CPLms{|sznto#BxXBu@RQXp0R=!h7B7*z|B zsOp6>V+^x_mDo#7R2KC0Sy{#)W9$|A{q7RN=bMKadk-k(T8S`l8)Kt?K8R%3$|VjA zP!acm^xnYO*yA&F44~49+m*fKqix<@j)Ld7=LM^9Jbdi*g;RUqvTOUp=T0ne-f-EU^!90Ns-jn~|;5(V7^7hKVC?{_Y`KCI_D zI7obW?^(oEXD^&+UO2J$T}Hv>a=8p_)Y2RrH@tUTBncdJBRUg|JB(?I1~<@!fyQUB!P1ik?mqHq?wM~s zvVLuBPklVg%@U#_%RVj|ebqs=xvBNS0ApL~=##I1sk>7u}zP|p1 z{9Y&c`Q{fHdx^2%q33TCkA8`<(&rf~eSxv^HSp&(=!Lf72;Uk_HLa*>t+sJ=XT!v! zs0Kzlyt7$sKN7i4n^&KnJq35L8)t+2jF1glqBT^5p5QfA{9e zw|)A1KKj6a{@(xfg#X;pkw3lnZ}0ia&;8KgS0?}Z-Mc>h{C9lq&;I2vy!H7X`1Pqr z-up8b8dsi2k;1iDb!qh{WswI_uAGVX%5MyzFUW83;F$ad4^GJMVaC{3Wpzi4Z_w7$ z5(WjTsTnh;3deAuzR`5`4cXzWO#7MLj2$e&00Ja(D`W4$D8c?_ef@9ON7*$R1b5H? zzC%vrM4cFX4%ZBenS> zl{0g57bh!o3kq2cfvlU)MI!8AqfwU#o32kE0K%?Y10ZeBoS$$nkFf2iXp2}KvO|iY zgX8rViZL|UgQ0KJ7^)XzXmYZCu^~VlK)e=-~LoghtRF8u?yYXY7~PS(c%<^`OgS#l~jQ zHFxZ|#8v@AZ!MP;S&baQ6?T|egZ{YM1u*RjGmo;ySIJrqlSO9K8#a+Nfazg+Vy2FN zMfiDE&Im`3)y|%tuT&Ib#_JV9Nn1`#!~aIvqv50T@nVA2K3X>->)EGaBfSlyW^LTC zV{W`mCJT_W#8K{S=7^|`Q~kmy;NfNGRKXVYa+Z+m1wFWH_ZPfHppV9 zmFd^fp5{LnTZvUjuL|wRZ-1(~tYN>PZ^6~KQhe;RNF;dua^Hr&mZAudd5>9Lv!61E z+=<01$YdKe8(I(GlJp^Y9w?i*8UznO6XjaobaSc>dB^yv^EJY5s-w1>g%bn5eyBuS zUccO5la9J6T<-V>T$)6GQ@Gsu54bdOdG=-o<6S+te5V}CH=1N`W;lL5^S?W6BwrfO&jbve) z!!mqW!HzCAMA%|uSiug-{GibO(%(!w(6w%ZYOFHfyp=HqJ@EsKl^$fQ^hQ#tkveTp z`Ee}B#R3MVgbs&hnf|bXml2&faaAzX^l5Mv_f$r%!&NlUAfs12%a2t!^a?Fp->L$D zhE}pv!}hfbLrrC3?1k8V5_u|acQ%e+)j2`!AX6nISk z$nHc|!ee`}twL`>s5rS+i`Qb9=b(K2?J?ea*nIP^N{sz=iLq~#*ce6XbRoCBpzZpK zT<@1D@rH*msO+JbqwR^(pMXrh1Th4KJ3R4uQjK|nyYpgI_+v5)ydM+l*U{S5SgS7&Ne>dl;h~7yL#Ooh3UIwyiP;!Y73ax7-I*ICfo(@Jm5R{iEYSt46$P-1}sD zyx%9Jr}qr7zgk~k|Hk_I`kjpZT#0RG^Ub>@_q>5IHo~saF2XLxO1Sa-4#7EP_D0|W z8hSRy*yy$OGD|h}w_%szHjOqYY9F~5ppaHZL}MBc0*|-j0r4!YbHqJsO*cg&hJqCW zz;)C>K})rk>pQNmNdvo2X0$lftrCMe%vg_Gv1ZtO&H}Z%I(O&Nis(x*Pdg`{gQ%j; zAz!z++w!^Y@qFwG_nkoV3OEDOyBWg)fp4k+BbKLpZj` zuD!(AI~ZgCnlbhX#@M$B$dWBCwI}?%DhJ8u8Dozy#=e^|_9A2Kdl+L)#@P2U#{Ly! zr5|UE{RCqSy45EspKEm1_6+o+-5eptS*%J+97yKk32 z9!2sY#=b-T{uV^OLV8&7_iv5w&wy-4aKUKow0;CXeiV-%(~FaL@0phjdRuR6@MI8 z+E(jL9P$|NU?VSamuG0zbP{m$cszl}+wgcRt-}F!&Rsr?kVDAG-!P+Tz#YWn03K6# z?8D>Dcsz#3n~dh(&DhwBV)0;gdXZ1r%>$E-#TKu@i&?#vSW%lNB#o){ zb*<$E_A*ws_W)yriM#X{jXy6A+#qkTE%FAt9&c=aO?jg{{2(Y&6iG_hpF4>TI4J?(fSEk=5A(Gr;6v3>FE59wI@^(@u~yuFCmd4m@HG zq;QCVDiji++)lFt-{-cD!K@%-V6&kTAbS}MQyx9#8R29Epx`0qN%)BFbWjmS{!!P{ z!N|w%M7HNAHP_FIwst4#3Byg~xNKWx3zU&N z#M@mf(lvy1=(amy7zJ0?YHjZG$kHr9>2xIJpy~*0q*0wkP1X>|+z=)eS_TB`3ezUn zfzy>l3%1xl7BS0Q2ZA(3AQ}&0lNKDRWC(k1bVdC{1D0J!?v~04S4E-$Mx=>M@R-+3 zLMgDS6xa>-xT8x0N^0lDkJc+q)M{A)bJhZ-J*`XnwqYfR*PPnJnPO308zr*7t zGW}D5$ilahuoG%-qb#V0da}S;)8|P|n&4J((TvIo zHi(lRJK22mP3zantb)gV>etx%wH_0~(IMQUi3RlV5Sik!zZqm@HZ%nLLp=Uskd0#B z>wgTga+T~K|0e*xj>lgOvQh6nqQ`{h4zs^%L`#*H%uRuOI&FLH5|deDl8#YSXY^yn_v}AvVVT z8}bUK=GsTSM(xCbt1a)q<%97V>&)akPCa+_IDhVR=gK=SKh^4d%Syd<=<@l)-xPU~ zyaLquwod70i1J+^HeGZY>q_Y@P!IR<-#P)$WT-w!crX;=srYPR!s$Jh^A_t(2HwP zyb{(zE1le-6^N#4usMWmAqIw8YzjwUGXo@u!^N5vrHVI9W%>Bb`~vdo0r2AlM&GK5 zMKC^h+e@ufhfq{dWch8KNv@{dRy!mCQx$Swwj#2z(5Q8NV%eNWr;WB*3CwO*)~LISO-W#gP^;au^Z0*y&_e7$2wc;4z~ub`WaG145+{wP^W? z73)~AN;Fk#cf#uBz|{nT(bY(l3V%7g=Ou_-JFD96Vh&wbfYsC@j+J)h~|(+C7ZOHkIbwL^O4kCg$_wGy4>vA5k08JQ8*VbN=4&q zrZx}}R4R&1x;iBmCXwa1U5!%0Higt&-wry4>`BF2&WckDBfjWfRmBsQDO?_VP!Ui~ zL_sIe#bL^beRpv&owrbfm`91N<#EqhnGac(thdE$vB-DbiV;FvC>;=iYU{tY#G3CUtQj{>9k-LqUl?^Axs^}=8#?zPrt5ck7SH-F<38Gy3y@fC&WF*yucS5>JO_c_{c5jZ6wc0m@ zszasqjSY*(3@*!L8CF?+EwX{OVtU4kVN$Zhvc4xB8+Dx0N&5C`Q|rDkS%c< z0^6uK<}oVI{FJ5s`p*X$oi8dwP#XRA2War5Z5Dar2s8qrS8Rz>$z9*Iy`1t1H4x## z#e%9S`nwy(*bJaQf{R-{-3<}~$=8gnJBZpVdNBtGI@sa_ni4=gkj_1vGx6NEWN%>jmC0FJ!wqfI(&a~B<2!cc)sv3rSO7!wyO zVQU+Y0Xzor7{X&49>aKy;4z9v8ISFF?7#zul`sO1UL2rNhu+HYVGjuI>w{p5^3WTw zdos${3rO)#@c3>#Uc}?wczh2Y-;2jT#RK-;AXV~S)*+?dT?Hqu3=BDLWT-ePVs*7f zgHz2)yAVT28j;=jBihl4GUG7y{A#mmTObqF55OV*@FsJEPof|GMC@~wFcedoj*%q4 z`wQ0G_A+q!GCT|>op3R(*;W{LET6_1+)$-*;>bysNrfu1tD&4YF_gKW;25_3ab{~C z&3A*O*13`-wN?}?8*>d7OhL$fI+IV!W>pm0t71$QGZxu8x47F9gOzCtfW<&DL@naB zHl9GWT1m7RSL1F=9?(>UV+w4iYjboGAW3b8#)+tyYY|w*v=X(!q|!iC zL7G-l3wh**OQt#x{VF3ZXN5-zE|%0uAzynk@K!z)IfHi@U~VO zp;NS%b$M5sY8x7m#&Lf|$7EA=g07#|H5->5(|$o_jW=9SPn1}xSz=>%)Mz(OI-3^? zqPS6FV-Gv_a&#%Ql1tX1=j_X8twZiq>%?>3?9}YrsLIF%6ql`hI~pe+u8Yiin8~%oGu% zzS5`=Q=lJ0ECBSwvWb}9)b~mHrW54KmO>1xbdU!}Z|N*u>EV7b1AzGiOeh90=s8`7 z_pF>Sesn`W>~P1`suK{m4(yqhLA()<18;>#HOn!zq_o^oQHw!Dx64Ol-?ACSA;=iI zOIj3Yl{XMep$^TEIvo6oaH+@Z9k7kaj%91ygVc-9$C9 z^y~oewAhIQhic++y zXvcHyv}W1XQ>m133X$zM8CeA-6%f&tK+Juv?om}S#N@WlFc+ZCh%)k0&6L5!B&scn zBvo9xH-=G0WtLp+q;zMN5~llG3LaLUol90%uc*Gt?ge7c28kzvncTN&yb+9>tC&w$4gQH9rxZ@3ju;nT)9+zF^~ z_>oZGa6h2FXN_;TB2eKFKh^h~@%@zX{j~9Y%J_c2@%;hD*n_g|A2h;Y*01vaS>yY| z#`n(~-ybpX@QcRxM~&}aGQK}%eE$m0<;yyJ+z5vas|x?55&p9A{VC)7SB-DDO~FnT z{(jwP<8K%uJlw3PHvT4~JHF`ew~TT>#~92V>F>9V{9#86I>6uO4froGMl^xH-!b67 zXu$uj0skdwhl{`8GvL2$!2iAh{}luN4-9ztFHrUQLj(Sg4ER4b;4h4T&gs~Ow1LNR z1Qy+7S$mHeg##ACuSZFR4`G$F3*qEoXj>+HH^O&eWwR|)1UBJE;iREA>Rv?6s3?^i z8Ry@J+;$_0E}b0fz>`DyPK4j5;Hg&d{7}A&!ZW2~yMMGYAWGLssEEgqMfk@NemIlQ z355SzZ}>@sf3`P#4&i^^oBvY?|1Sui%j6HI7UcuI@TU+yjqs;4@b5>sP4Ewzg!BP~ zbA-Qt2(CC9nN%M{>>GPa`4GaM? z!hf_zD32 z)C3rgBipyKo$&{N`ZEQUsl!)$>yYb=KQ!y`)q*;J-Ez1fx(;83$Z0*_ zjr6HCtUvIFcUc^SEDu3amYQFv(6GS3c^&-W{cQq{Mh2RdKaLwn;YJ2K0OQ@b%B?u5 zHpg+v@c63hxj`~chwEU$wfh1ERL(C{j$1L^%aRL%{0kkpNQpf>(vUL7E{jUg`?5GG zM|+Ss#;NwE)m9POjd1W{iQFAx>j$fVF-Y-a(1^lMl<>m6gR!w)>eNbg*$)!hszj@3 zm0;AzRFvn8X~@Egj0i3nc8F@-iFJc)0LlAUcOs0Ijb?;e)molwX}tk(5Qe>5+fW`v ve1%6bhhppM8X2+ZWI(1cGRUuyv78i(ldkXR^0C|%p`?pjtdbR^s@?wvQK*wZ literal 0 HcmV?d00001 diff --git a/MikroNUC/20260630/20260630234001-Config.rsc b/MikroNUC/20260630/20260630234001-Config.rsc new file mode 100644 index 0000000..5d2e85f --- /dev/null +++ b/MikroNUC/20260630/20260630234001-Config.rsc @@ -0,0 +1,4981 @@ +# 2026-06-30 23:40: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" +/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/20260630/20260630234001-Diff-Config.rsc b/MikroNUC/history/20260630/20260630234001-Diff-Config.rsc new file mode 100644 index 0000000..37a0798 --- /dev/null +++ b/MikroNUC/history/20260630/20260630234001-Diff-Config.rsc @@ -0,0 +1,6 @@ +4978,4979c4978,4979 +< 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\"]" +--- +> interval=10s packet-count=5 thr-loss-count=4 timeout=5s type=icmp \ +> up-script="/ip route enable [find comment=\"Route-AWG2WG\"]" -- 2.52.0