From f2bbe90c454142c7ea66c1f4443d01663e9d5731 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 16 Aug 2026 10:00:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Aug 16 10:00:03. --- .../20260816/20260816100001-Binary.backup | Bin 0 -> 388993 bytes MikroNUC/20260816/20260816100001-Config.rsc | 5533 +++++++++++++++++ .../20260816/20260816100001-Diff-Config.rsc | 4 + 3 files changed, 5537 insertions(+) create mode 100644 MikroNUC/20260816/20260816100001-Binary.backup create mode 100644 MikroNUC/20260816/20260816100001-Config.rsc create mode 100644 MikroNUC/history/20260816/20260816100001-Diff-Config.rsc diff --git a/MikroNUC/20260816/20260816100001-Binary.backup b/MikroNUC/20260816/20260816100001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..b55a541b03907fd71f51406418f08121e5ea9ccc GIT binary patch literal 388993 zcmeEvcU%)&xAvYSKtk_D5UElWnDhii>4*YS1jL4^5GlbBnxYg%u_0DOMMW%N@7NHr ziv!830NkPCw55{@r~uxyUCB8 z5{X1dP6q)1+&~}+3qUynR6!sK2LK)bfI$F2R6r-93OWG-Itf4tbmAoviG%<;ErB-Z zw8SN_1c^G*E_C8yrAjcHp6nJy!W6L}F~%i@9h2n3;m4@U@6}b_Fg&_~K#~UNMD$=e z*ap%YbUGMyy9k?6;qv&blz4G>T1|d;Twbbx!%J2$GC^KB1_00@r~7uJ9S{HxLXHCf zEO7uNfIw0#KoDu$2tWt{7=RGe)d4~P17M;I5KBzF5DyRs_dpV41s5QAIBGAjfPfg` z00A+?0Rk|_0fLeV4&eJE0N`+ds4Q#cIkt=J=++npHilpTf)`rhfI2|%UnLSrSCh&- zaV$?n)CP#9E{DctF{yN_6P?CnJ5eYs1_}^M9X{$xr@B(Pu0)cmm0DQm#1mFPB0C1=~!Fwz~K4Ad?e1=IoOq^lA;5{}f0ogr%yFKaMD(+Dij+rSA z5U}qcC(I!eTCsqDk>329l(FW~)1@~rBfH;x-(SCZCZ9^Cp**q^n@iz2QTP;Ak2g>4 z@#d-joj0HN?dJ0iB3;uA52ql!e|0$cF1ZLC{BDy?)l($k#_*g1Jc955v7d?rlFUHo z8US?zG7|tW8vrl|05BH-FdqO=2ms&#fW!iT2mk;R0G!qU5dr{`001hWQ##gg%qIZ= zrT~CU1ps728Hb{t4d9fJ&RZn_2wHdm2>M(A2$&cE2+G<}J?X(~aZuyL!_!1qE(d^# z@QMg#O@@gW00K7!0DK`2fK&kRD>(q90f4VZ0Z0b`{|pO21^{@fx_oX;hQ%^rEja*4 zmCH1EWxD(%4*){;3;+m9e9$FE+5lL>$!w`-WA7g{!Y9<%!^_9P)y6);-`imp0G8@D z_MV~s-hMuT9$pUfev+}^$BenK1?gPvK5}w>&E|6zIt=PLoJ_VM-vAB-lAx|;C-X$9 z3MKbr26&y5z)BSGSsWgR9|JWqR6|fV!5>uoR&siX^twz3Vq`j4I)HK=C1aCR2U~$a z64Z2xA%;1y2msPKCd~nLF*lZz?s0iAeQ4%!|!3;>q8XgZ&7WA7EQ)Y3dOtQ(k#8-jB(M@$F+3532=N@Q zSeU|zg|i#}e7hZeK_Dp+0AwNnAQ=GZ42J3{4x$4n@H_zUcohKn@C0fasIdr05=Ctb-$MBj${SRc z@COH8LBoMpL-Mm%tLlLlJ^fBT9+?hNHVHNCge$ zL_tG2QP7v9wA2#iORZWyB9%2LCwMvdz$vCaRGF5rDod#%#ei>RVEsLo!Th`ol;a-D zV1Hf)_Wj2)7$}ywZ)FsVCstA-R5?&}aOG+N9=USW42Mz+K>Q}R1rR$yMM1zwVUxmS zL!=MjVge9h^Ku5L17L|4B#L>WM4q_I;3-x+iY|y)X$t*n30VYJE|7m%88Acu4+xS{ zX%>k*K};-LD6-%P6ZwLe6cGz*L5o;cvIQrWl^Db0_P0oi=dqG`79w7WGF#sT30HEH6IvddjKstr>06_Es zfI1B-FvJ)Dq?b&|g9`v4<^Ujl06-wTfpEtG0K^dh#0jcksXYaNDP0Q!FaQ7m)izwH z4gvrS1^~DNfOr6aK;r`O%1{8nFaV?r*I)n;6dDX700eU?0C+3_7@A!D(g7f-Fd*Zl z=3x{7cr*oo(eh&~{6_$hEFwkCgzPwYf&&0zTyBC~DMPFa*Fr)7@Q-Yzn3~`Yfv-8l zyx2M|*=qTc^oRqOwSde>e?Vk57apZc>TA=wOos9|(;*dqXg#0-AjprZ0O0qlNmT_t z*cZT=BK=-9>`ud0f0;vsBpAdk* zq$0)Q1mrWk(hid^2q36_MP#dk4*9VY0fcM`OrkJ=K&ryXrqK}$z^|EsfNBiD=j#Db zgOL@p!~#6zvlUkMzcp6&KZI(_Tmi7f%JTJv`EUT(!6N_|d#tRd&?I4ILN7u-OyEo; z&4Sb%d4+S~$vl`8U;#m8A*2?;#0dd}fzI$AUEqD72q5Sa5m|4SzzliVZ?g}=B!!k7 z`?vEH^M977+ES-eB2Vljcewri%*Fomn2Y<*OjzwSfruB+OHNiWcl_Tzb8-JX=3@Ud z%+*WINaVzdgo(nGF8+@B+xe^X&*SfZX!2^s3dPAu!Y;+Y{yTV!bhBi5Xj}hz>{a^D zOkdq3&P1n_q)^&yA3!nDNhJm&lzJnoLiq%oX-9*8eo6w4BE(^w*&lN5Ky|LrV>0Q;ZEV&(q~i_H>J;>7|PYn}e>XiXZ@{pa!e zhZTnEziEY`AqT-u$qB4@sh2ECC=y4<3psI$=uMV0aYh(wD)*W|=a?*%pbRPKPsT_+7E<>ST-i(gvDvm#zqKU{6j=f@ zm{`LEE{C8C!%`)gClavY(X3x;p7=}6WB%pliO>x%VsTTsF4Ab1!hbeS&MbHw2Lee7 z*Pz0st^uTe4XLkWBu469Ctx8%x*Y5WxRw_sCBspY z`3Ge`=zM~Wz#nu_X#iP3AC|^+Kj{m*EaRn9yf8-4>AgVg_%F84G7g4Cf1J7Wpm7M6IvK$HMgG4yAoB=bay=vRTfRmdC#4f8CJ zUxz*qML+}wJ`{nHItu~t%`McTnhE|SQF=GlYcNjXKGA+E+IQluc?hs-D08u-%m;dYS z@ANiQ3}14FK4*$0oa;&~Zh|23U+>M?FkDw}V7#V#Z-%fktqR7iz87cGdCkOe!LSXV zRi5YAPrcV?S&K=0`OU|&-fU5wdBM8(P~~CChmMar?3E_%jnv1!peNpZ6KpAOV2n^Y zXCZIg(@dqjkqSBt)WHosot84*j99LllMmM{9^xgDP{$ao{c7viluxu|)ppCxQ;npQwTX$ZfwJTm3 z=_O*!!z`1rNVMR^lL;}lrx@eB*N;uwmJNHP68J5O2}Q$&#bPOhQV|4(M;L=uQp6~v z0wWbisjC)x^RC#G_h^<@&K(WF@v4U|@srmS(0%gK4Q-XT(?Hk_$j8JQ1jO9uLI)kC8 z5}+qoZi=Ai7h?lYrS~E=BvQL#3Dt*r*DMP3_tJq^G-09v6BxCCQ`r)_`z#?pEFojM zJaWy3 zCF(LQUUJUDqZbGy9ae)*)xSvKgLLpU^rt{0Tv*#eJqMiaubEgF5>iwJoo z1mqD(ULTB9Dd=qo%nvuO0BD`0VSi}Jq!Sn-AsD)sg~(v^jDTpuQ#crkVlbfgT`?Fz z`mq?Ms~940?GME;JrskHd0A_ipB2M?UkoEPwq3=rKP!gop%|{K8184qlzJ$p)KyH0 zB;({C?oIjoVo=;4_pP6-7KGbv~|^a>i5fwQ&`0C-gk zp9u;&ke|~<>tueT9j19tfK!UY6D91ChhuDE<#MbRCg0crr{HopeSs`ewOD=~9@6DB z1V_zsKnJt3V8`TryCcs7|E`gTt)Nb7l+$oZy36y!;ANzohQd7w&SzOLvx|n#6M2MZvJ7ReyYGL$SqVT6kA03Oqar>{<0py5Ga@?!;VrU z2Sb1$uQFBz0DP8JL;>6ZBg2qz16{ru0B~>Icv~nVa4~>^kvMD~0QgcEJOTiJ4g<)Z~{mKp%)9R$rqZlbx!_%&Dzg2Cv3b(pxduj4Z<>Ty%Y@ir{VMpc#Z zjx&Z_bi-k>s54OwB`9OVamsp_@GznVss>L~^>Fa6h}x(oJjLoso3V)ULCuXcNK}-G zl2Pezl#vDno{~spDwE0_^)J_#ifSvXNPB}pl$HAB;D8mD4R8Z>C7bVWJvm|Gj@U2w z-qvYvRyR&ozv9oF|2W_!`^snZfL>;^v3DM^^;OXIGb69<_-JYICF?kO(@A=&u*Jlt zvC6`Y!RfEm8k9WYXu}!qJl(y4m4ls!#AeK)9&Q=*#iYy=b(h{-Z!H9o;BZ(BqKs-O zW5avv!E+@Piy#uTH@u*UGsNkao#{KOpfunTmGgCl^%>V~5$}gbbmt>B2B2IFnjWzI z_!Orrt>Kt0AB@7^kNWcb@nw~YtpyJ`7lW!7n`%*A4V}>UCizb8WqS+8Rqn^2CgYy0 z&CF{$()j5(b?y4V-Q!;6ww!FuEk1^M5?)z=(rS^3&GtvnC0eduY}fy7=YUxNXv1j( zCu%;7dgfKeLOSR7(Eo2A`uR}A=K^5q&LdLzd=86CWuqvGN+%Ohl*VDPC}b9qL}Zaj zTn3LxM^Qe7&t`J?3@V9_l2~+(by~!k5A*! zIV6-zK`C?|l}DqpXiPE#od}ViT;U1fu05zP;^98F@USp~OiAM8N>URHck>p+2)uYA zv4wvkM-L1ScS}o4bLI+S1RS17?934+^nx0+E0kT&4u@e3NYCd>zF)a;#6 z;uxr5rCaqo2^gqUH(Se;D(ictiojjQ?r@i}RGlJFoti6pAnO2Z-^;_9BN96&@e-oZ zE-kwoi{@gtqq&$OnU>YY54aam=;^z&G-blss0F8oq8iEs#~DLrv}quSk`h2!-|JZe zR8OX7)fEjw#t=u^Q`X7RCWhBgo_tVr&Tvy7%n)H zPZBQb_bQlhuJgifeXh~z(GIIf)$dx`uc~?0Z?5zp-BPcA{VAv6(7cg@_nJQQw_lrd zi-^0^w#)EcfNGFRdGk=WaeK4M3XgBM!(b7BlyOkj54LB@uIm4k{)cC-@;{jS&ooldioAa|1k^b>4 zOXCLI*7Sk>U9;mhzT9r0|NO?RngfjZ2Mgk7OSlKpY$MU5rZ#2w8=p7%W$xW$gI7Xd zIaR4u`&0)XsCfT!8KS%OO#Z25l<0Y?15Z6~Sh6JHF0`FcL3G*9nlK-Q?ffm!GWGm} zPWdg+zXkfAE70E%5rs@)uuvY8LZNWz3=W$_qw-KHgG=Qxm=p?|!evnSOd5^E=8|Y! zHi=5-Gnixwi$WoD=qQm!lp>;@6fHzVJ^mp@NhpKN=Al#?6Qz(SG&Ye%q*G}u4wK4c z5;=4V3!ca;$(u| zsikaSxYwA-ofjbx0TvMXfK#3h;&JQk0pnzm1FKXmel z4cZy;`o#MwMGy5`^#jjs)P869Vzbp7qLmIyP}uii`pu#~52x@qr+6<(KC?ygdaUZ~ zD6jilcDZW3AM*8j9KD9qc-QyZZJ*95)*XefCe8E`H;cR5v&^?s9!XtlQ= z&}~*Wn$P?La+5jVL za*~t2TMVJ@UAZtbDDqq!7F;VOf0ywGv4ZOrKN6H8)Dzx%AKU{;NKlk1?l{`scXHU~trvJ52UjYuoPB+i*UaX?fQaa=H7EBfSvW^smJDrO<9K7o zlC5W*hYq;D@uM)`;VU}WX8NJym6I2#^xr-+E|uI2!b5jOt$K9e()*^TTAg=3oj9N^ z$|2yQ>xx!6lTr%mmXEv=Nt{YYa(3|(9D42K6A~^b7JfL;?tid^6gD@YPmy>U}fszyT+3X z>uc*?$6z~fqbv<~N?r_ZYRqkY`5|hdwT6x365D{<(gjcIE4R!P?|z$5R($(&L)CQm z)hf#%1ceH<3xZByOTF{Zee+w>k*L2l-EU3j@>|pWFVJ+~usMf;lBj$ppF^YaxqJ?j z$|iGI3=W6RpwZb(7L`F~Q%Q6>lTKz*=}aP>LL>7z3?i9}vS}nLiz&tCJ=tn0Ht)$s z%dk0zPi1pa8i`G%p(H+q&SEpDD2YQNb2xmIMuXV=UqD_zL*|R+&Q4{yvy%YA+&n~r zNy0>yItXwJ;|Rs#WG*Y7rvZk$`3b#QTy6&RcjAD5BFDKO^m_UUEu@#_?oOUaEZ_?` zEHTf*BSjo56bZx`7EZs0*|{r+N&`zq&$7B>c09!F@%BB&mY^H72f*InPv|7=l@kON zPThLt)YU5|Khh6=(X-GmdJ@gWY?YyP1pI&k(41-CgJXG!39qLj=NeHD)q%KJMNjn` z^N77rV|YZ@`P^!n-iDJFUdVvyZ=dA(ueN*fak^>HnmxCZmYpUcJCz2h z1hsr){hb-7LAjxrX;?8F)^N}g5>p$NPOBi2L-T&#x&HJN$zg|3HiXBLJ=}#OZ z@ZEjSPX_j3DDMxUI~cd@9^3mC%5z3A% z28YU~@KG+EO(mmDBA*T&TquLdqw%OL8kt9;Qp0sOMR2hRx z15`0CaGGDH!e+o+y~a6ALXy@HnxFLb&mX zmuwN{?BQ$)qtOuFq$F5;Lpksj3SBtI45Bo7mJy z*(~Dir0%ec0Ablc^Pj@9AEVQKWPNaw_rXct2PZF&A7EHDgkgu!Tuhk^hGB8uN4wiN z7?GaZ4Tl8>5Uo%PnNBnLDW`~bs4dK+rf2@={8Chg)ie1i*Q5}PK}1O;A_=9F>7$G^ zNK6R9h*L?kBpd989nqo7FbjY4@npA<>)Hkv6bq z?n~5~aRtQ`eY!4)EH!41oxsgFg+y%`NF2(@yn?$j%>J20^Z=E$=L3^(zuz|ebVS3b zjZ0^>zuz2NZ#^T*u8v@LcuS&ani(o;y0`ZEmHXlplPbqmlMP1TOs{(}w}#uFe9-IA zhog_SG5pErQ}b3ceXguq^-gzg$fwLlPFby6qF)-W-syDt;Q0lfX#uup3mL-OZ_a-y zFfWQ4)G$8iiIRP@e}>+$gF|k+d=_10)^_%}K@qj?ajzpH$6wvmaWWd)XMgbJaV8rD zcU8#twZTh1Zya%M#Ef8{iqHOk0=~Y1Eqv;Qb1D*>k0&oQA1XZjU^?A5rR@*ctcOtb zhVrde7ZJA&WfJzKAtiIhiFTxWKWsGHPUx^6(q83w;y^fVuXfoyySr++oJ%ct)#(`? zqpf!BSne@qL(;waOKInbl9L|I=yiqYanR+6lisLQVbHUn9L>R_u~utzkMKw83_XWEvB%NT zX{pgY=7_^XuBO_REO%1gaP}4R%I4dvm#f)fs17^bmpuLaY-M=9iSM;aO6$~%%;h&3 zZ$sLK_lMvMY72#*5NZ$w`1&o}f2yp1N%{O1?%%@w-zMDOAQzQGWl(4gE|tosGI$gw zokypTnG6z_#HOMs5oOc3L>iS$r?5DD2AM_YQdndXmrbWJI1~!X;7K7@Pdc^>aw(%Q zBrb(rKexp&6#W+_9YV36BLgY!3Jz%2kVUDLuY=gxCj4Pihu9T~ z%ps;Ndx4HBL|%{Weu}&lo2|P=u={m&$tg6Obz$Q*bPpun2iRP%8 z48a-n$X22=+8_Rp)wAuolRslF1ZYGQC3W|aF;F6r(d8ri=X5m}^T&OM7`X2cql(GJ zARt?pXGGIHejC60(w0b#)aDCzR|B>NPh&mUWD!Vk;u0D!&RTumt>V?kx|+tfS9e~U zOt;tR=XZ;F>aeTHp6Cr#Q%cG>p8HbYRSf92;L`))rB{X)g@i#5o{7&I?^#gK=HAXK zQ+}IoJfYBP;NecaH#7Aoe|*%Zp#D+FDV-Q!e8{;O`XdG(CU;JaUnwyxF|G5{ZOPwRzakxbG}!go`8SP+R|Rx#S6WK#6FI6Qpca5`@8_(gY5 zoK|nTPug~V(SVTjB{6ea+#)*FFA20vwcgwIjUy+tCHl1|dp(}2xt_n%A(J$n6F%#N zM$MuTsEOW|t5fJ3UygN+r;fSpab5RTg}++UjqKTkMeKsT{5sqG3)A=S9K4RHbrZAd zrEb8I%=d9h72^!2o!=kx;F(^@`O&#o19mr$mfU@uyv2He6R4(!iwXWW{73f6T1a~m zvQYDV-zjE+Mu*S7^6Go3+9TS%!rS{p@zcnetAhJPjwm10A?CR34CWD@jP!K&>J_l( zjjdl)JI;JjeP7?9!8yC`9!anK8e?$(Y~*xvEzY&)?1R^$!<}D@Ic1N>*)sQQ_N#ao zP-LbuSR=Hx`L#=~);FyB4Vs2!RFB%!Z)n|!?E5!X94&db3(G(E2Wa{SzhXD6st$Lz zVh}KLlb01fPzBcu1 zSn$#5D^A4)9h_@9d~HEEd%=5w!HvWD4^M1T>v&!Md|0@nvPD98x`RneG&*LOjZV14 zW_jW`$9^qpfvdCH5XNHtC0;6oFXJm(%r@)JU9)`L#$^+t66OrXe>rp6XS=Zhw@_Cr z+1~l&1Dl|)BVWDw;L~Zf`=v|4m76{+rJ5pPd8%msoVm2^)f!3{C(e7aLD(mL!$kJU z8<(bu)ZfPF%u-*e7a*cE9d4M|v}xj<`v>CZ4%8jXyHypbyU#05^7w8bUniulc-t$Z zea|K?n0wd1^A2_5S_8YqW_7HGeGduCVr(tPKDpCd%{kU|K<@Te(_-@KmxYLT9w^S9 zCCN%R8Iv(|yz@f$Z2@%0{%23K&uETqqm8b;XKRb>xzkH!Wf~ZKsj=aeTYdYX!RPa? z39~Aknhs#vT8ozNrQSLovt#Da3j0;o1(Gp#arbu&f3MO4oa>`zo(uI@^5TQv^%ky2 zc;>t)%#u6ol4A#2M%jy}uU}DFcv@YhsdW}jOuA-Yvtx(ay_EsS&8~fYDA?=go8z)J zck@+_chZS@5-0M(t@9`EP0HUk$fVBQ{>Ilcg@?u#TeXHh8F{i#KGCZszxVZ11p~b% z^xkNEcZ!?gTlVML!txHOqx6cT%TWq#jsmOy*8l$zQ}tW_|JMJ%_5W}E|KFnjzb)r@ zbS9a@WTPZLm%(BY$vhH~Lne|)3>JsOMj1pRlSU!4=@cf1!DKLL6gJ%Kz@^YQWG;on z=8~k#xt?%I*K*DRCGxp6E*a%>SY!^1N@OvJY&M5RL3v~*vqS z_Ezkht!$|NsFSv05o>1kd|A{XAnzbB9Fb(r$v z?x_`rFD?1_=KLkKgRIdZzEf8@t!^pcs2jafNouW|?(9Bo<+H&a2?MrXU8f&KOE1BC zwP@?bi|%gA-&h*A*Xv?TqqPKUR`y`fuf+Lhbi?<%Zx31bx%A|XZ8VSbIvfGp;qb6i z-A4->3s4@02(})67e82c=w&D7aaBT5@`rrd+Pe1bS3HGl}ux}QKOk{CsGzx>yAfgl^ znZcox8GJI6$D)u)D3MGiGs!$Mi^$+`P!fqr1Khb zR4SFm=I}{WSS^)I<&aqvl+UHI*c2+A^z&z4{K)^(GcR|3b!VM1815D(W{LO$UOe}^ z0IzJ5U4Q6M{FXsF&AxdQ|Gr&z>i_yKJEyL0{Y%^I?!)-*9W)msH%mQivz!0DAMqEr z+1aAjG9RK@SI53%sC>sA2}NbDM2BzL{(`N4z2)v7*W++VV84Ex{}r=SIC}Mv zndL!5yr>;_Fb=aqdL#>YAY42xI4R7hn~gCJ^pDVhlq{?ujbydY8N%yv5Hzg zj+*`|(d59a{*BW|H;BK)MW39tUORM_Z}^A(DP~Wy9*x*G>{GZX5@YhoXkye$b9)u> z$&w*Q;`e-5x8psRp0V)gE8lTh_Y=Yb*xN7kQR5tPE&1{_11w{S>-y%pbKdU~O)$|Y zuC3QhIZyvGXI4$rTXt6T?r0wRX=7zz>L~Nnag?LamPQoM-u)(P-G!<~5;1c@-Mwg? zPjQ?4$6PL6e0l9d&S*mF-U#;nVdCCP>-*N@aZ#6+X=*;X`}O#%K80_;dDZ^lCj0sJ zyG(UbpN%)stP@jW%h=_m7dNYnw(IUyQ2?VlYpnfM^adF~QyUuUn9 zp~R1xd({tBKA>k=9Ry{XlS9A69U5@rYDP%Vg~AUpDkf(J`cBe7rkou4G~r1eYP8(u zg!VmL+r4*L^_x^Bp1tYKiyw2NbS=^tS4o2yIVnq%+hgbNJa@6r$ye5hXF1WImfmB2tM|5{Jwtak(gm#pd#e92$d1B@>x! z2AN5uQn+k3olliQx}LOd7o?MK%lma(4qXR-3K0H!i(ZX(;Lo2ymc9HCz7Q9mwXO5J zCmA###xi8dK5B!h0|0;3_`Ki>{NfLMZ&m_)7+LxfUFmzXe)4RCk(Wnsa-c_$lb44R zmFP*L`cP52H%iw604>M8CFTuqF`fTZQ(3pbDzm!U4ci`g3$w4P?>zOKsk7THbSZaU zD!rYx;exx-+p`yQ^}f(5_e+? zyKc0^hlH$uyU>2?+ZN8NDK=kU2VO3wM{wP3%;vb2O=}!jP^r;rKu_K1d^qmo(2_fa zGd;&8GySWNS2f$Z*Q|5pojT$(tzXTwt?7==k~RI@H@3L6w)9ee(^7YO=%c%Ocis=2 zngNK_&0{+EhK;RvE80nHR5rI&*|PLG@;2?-*sT*Wyuv+)$9dfxv1fUj`7(FE+8c)_ z&i}lXcTHpE_SC0WzKlFgdl#kd*h|v+DR_fm#%tRT91B*!%82R(#b@@bwD^7upA%hC z%Ih#XdLi>=T0E#%3bh(;mEl8~G>3O(b@{u@DdfA?GF9({MjkHc+cf-=!GJo$EX+JXH+!o-hIT))%$fXQhl$`XV0TP?5H}ksx2}z z?{-y7{G9c}4sOmb?N~5C`9M8q-K)7tjJ_ct6>+Di03#aBDy)sTDiNL7gGy28_+ zcZBNQ^Tf`rYG`V`4GP~0T8!-OD!u-CT{)sdSUb?frF5ZB2-jiv=dk75Pt0F(z&mTr z5%T50v%8NsEw+6>ccadQoZI94i`Q9SDA10%+h4NzT-FYQyxaMN#YJB0>PL!7BL;p= z?dyFxvMPL*_%(KU_KFoc%_*Z*7u*o{KKbd>vYILWi2`kBZO>b$zzmjiTDbGXjp`!x zRpolK2W@<06Q4Xtf9(d3aKb3%Gb497-5wzPaC3a{SA6qTtEN?jc{_p#8<$?to%u$6 zX{*(~Ih|SCopW%@qxH5QJ39O`(Om6AFU^di+r^EWCojA(@aj|?{b|aJVGm2gcnQ&eucgD&QB3cGn-&eeE)l^)0wl-`v(ZQBEE)j$r z%I&0(nks+7I)n9(y&k6QI(ItXIkDXC%H=D45LMGjhb3pN8J}$eAD2>MJ@kzlPdu+( zyeaM2n9QJzV1pJ-Ew`PHjF_!hyU3!l>8mqB0vFP%UM9TOnU~>o!#de9Gv~|Z;h%0c zP4=BRq-prQVdFkedgxKC>w4q{;m|dSDUH|4e782kIeqblJafMzlRreLyA7EXj(vTq z;sF~mD9QHRe{IIH;>m!sSIp*b3TK1e}CilVGlejdb=3d+Dv*|lzskvxL2pqT?W? z^c>?^{wNl{?N1#p1o3Dt@j&B$45^Jw$+dBJ7Y-UuC}~eZTdG~cEovI zEO}P_W!#FS-Hmr#Yd%l%xj%HV#T4e={NUvdOMUtouX~KHclGG>3h@y2%JM5i9zA@l zH@P*zeWCfs2@7_P%WK~{X@r5zc$HsXGwti<%CuXgcY!x*!^Z&)g>Ax;6d&B)#YI01 zu=J57yIISsQmY>hJw18LoHs7Hb&2>=nlff{mN~Mm^3%xmjV0Dwj^ZR%t*uo%y^!g% zF;*4cCU0(x{J>OI{kWn=-yHX#ElZV4w%hG*RNHSmoL+Q1VLbUj z+$eoqpY_2V+h>g_D7Q*?jJ|08A!p@^Z7sct~;wfG1EM~zs|rHdtj{h zp_-?I=J{W^a(4uK$(>u%)I;xkM!$dLwlgD6a^O%su(Lgs%fH+Uvvk&;Br~H+hqhE? zUtDK`hAzKsYIFG1OWtYbe95iZgc)ec)@@_e&xe>>P2rUj&g`c;V9nBN z2VIWNB_Do!(JbA%dfjLgY`Pvk;$9V|CP_u&QgCX%&XE%{iY|rB`Kqq`^u5{4vuZ0u z?t|VP_ck8!@KC+Kcf?N0J?+=CN4;rvyB5^?Xn}_N!BLzUlEd2qI^ac%m!2E+4L z&!3sf--^Y5mkkcdHf^ zSFw&hDI4vjy)Mf7`b(qsf|`Z%P~P#qNBjC8&vTDU7JPocQS+3BP|Lur!+-Y^-ta`N z{_D&aW;A#%rbnzgL4Me)`AhI;!M3Bq%ND(2j(Xp(oLYEdQko7mF5iEbg~qr&dp_9t z?9d#xOSd*Ik+r4v)2NjZtA}4Xag{Xj-P|*=#j7N?eb!to>^m(!-qO}Q%+=V-hAr%q$v?W{vSr%3#vpmrDCUV z>0f=UX`jUtF@Iy&(8>GXewec|F#K&uy>s@%b9#PnLiUnfxyAOcT5jHy=)Cl7CcjTe zRP9}|YALq<;pr=q4*M}duX*8ZhekQ15k^N9xj1ARJa~WQnE%ewPc^HQ%;Pm^#}=}FpE?{A-edd17iI47-` zWycSHusVJ1;;g$TY7_OVhQ)sNW+$##QB?fMOcFP1BBvyGj#;E>z0nnB_=`Q+6F*+N z!X0wp_L=4~Q*^A*(|YS5{OWc3HP=T%hlL4rSQwzWNQc~EG3hF5IA_idj~~{vzvOKA zD~ClNVsF$`8mLz9QGNcUx8jO$)XWZEoJzK@OQpE^IL~7Gc)|9umQZeZBre6Mp8w zJ4x*$LJo!QGYs8W+ON={X=MtSzSp5>)PtiQcGf!2-)W_}7&nbEJ`hAL4Rtim7<6u? z`r?9VF9n{S)$6rYL*vk#3cJOLEp8l_@n)G;`kDG$Y513}6Wu31X`DT6PyF*V%1)!~ znV70+p2N?n9aw*(*Vh@R$||Ow@R4~dLeb#w1G=~!-JBD%d=_ebu^4h=#hJ-ElwEwg zU9Wd6`R*->J=xqJMqBE=Fdv-xuzJ<26LY-nZbY(6S6d%kqdWOvYW9pR*9UKn&~doD zH9L0UzSBpZ7jIWFW^D2~x;?Va8l&GZegFE)=J#v8RylXv%HJ|0;?CWdGoS13B%$NU z`bVx>RVFh%A~#bX9@UtgdZ*%m@F0V8*zJ1n-GkR(Tfc5z`osIY#Aaj1bDZAAnOVL* zAyLB|bUuACe0!#s-j}9_M)Wb$+h+ASlpDI6yps6%^NO$qq-T|Ex9Jy-?;G=Qex&ut z?K205YR>#Xi;gQGu0HlLchKQ$ItP=V*O*LfA0Q-+I%zw`tkV^LWRW>2i_Im;9{cYJcR^Q1k9Z5d5mTG);Y}u^L>j}B;pvG|r0$I#ak0Dde{FMy zp2Dqh5q`Z4ZqC@jkDLHEXCOUn&iMJm8D^0JUxAat;o;@sMECY2k-bP{FD9J^zrnLn zt0H?oVRf{v3-R)Z$T-gPvs#Ytb#Y7W1{q&^jaAv#m)+)dWpq^OJ!=EbQmcavGq-2+ zMi(#!FFxOV>*yoU5Wsk*w!{0q$uz@7k=qZn^&66lJ3V>e1n)J%OYK9?o|=}jY2>xU zXz@K2>nqRV)0GZdRnR?Jk2jrC8PG{OGcssizURz)gF3^H*vIXkb7)v;MA2ZUhCMl9 zjv~Wt^;fJ;tL&zfoCz6wrwOijPQwqf9Y=E!e?^EEm*rU1dGEXLhoN?goorb3KfU|E zT=V>LML&6rBT9VB7{ib!gfH;Y{aW5{m~Pa>{-IOW zyfLwE{*}i#y2n0Et|@ERAJQEVz47Tj)$Pr_+gn;q{`eRNIzD_;gqeAYdr88;VRw>p z69yG+IZR1yT6EU$(1god&Yu|jd@rd^Z@g#X~T#np)zA}F4j8>pll|M{Nej@M|H zGwlVhkrUp-R(_nbvfRQXv0BG_<`|=UZrhEUJnwH1&dM?TGkpGgkpGtqGTe9fKSjI9 zzXivjGbt#S&m^&E6h4_qrtpar9-YLd&`C@li^bz{c@zqd$YD`XDuu)5(U^QHi9|+; zObUz zVe*~H_=CMrZJb7)sr}_tjo6_wX!RC-<%_v?&#TRA!5+^v-Z@6keyE>-n5 z#u|F9d+;bT@0dgTyws2zHMdg=k8j)hypFud-C({=LON+fWVp5Fr_*l-ex1AKY@q36 z$)p3pxq3&&7=9%SorkstTv<$8oHc1D^NMCz;8e+$huY2eLkR=R@1W5hmpa)md6ca= zJ5H`@tr)GPWkQ?fe0-hK?Z>XypL-k)&xQ8lDzq1MXf6^Vw-?|U#lGG21;4x2`*k({ zS3WYEuJ>iB|D1%Y8|ZuKIzuN`t)VNjDjb(Z(d(oIDgg^ zjVUeu;?4_h7i#~)_4N9*LvYFSgM(|8H@!YTO3&l^NW+%zTu(jC>+Wonf3_3zhLt}0 znCjq>wU6%W1xLD+O`kt&o-@m8_tLkkonnScLj7w)Mo;@vt6bX0)Y!cKCI(Gj@%n~Q zy2*s{=e=>RwUCUo<1J3ck=&S3r$k_VOjD= zv-0zhtHGa`m7j!MwY@xoornxyZ#sqMLuC*tU7!CnfKdz`G#B|S*U^!LD zMrg)2+-g~aF-jjd<;61Rq%lFmu75qSq$Gg7Y7a3rx3cDOKmu=O{7pN5|C)kNHFprr zDLUJk?{#Ji1H)Po%+B4k^ab`FuBt<}PByxeQGBH37G?c;v&dnAE=H&BhVKa7T|7K7 zQhQ-;=z)tzqo0+C?=4-~5VHT84Q=)(E#Z}tico z6Mky#=8Jt*DD1$i&AQh%B)VdUsNnR9ZBpwF>W|%C$F$A0dh_awH^s9{f5~wDRSp#twsfY3RgD#N~s= zj@(u>ICL79%IDG6Sb9wMppfn1d1-Cv)4RBAY?9ZKoR65G^CQ*q@UkbyU zLZ!n!Pk%5o|6i8Z@V{tzDGY`$)q{q55K)hxTi(8t{JvDY{`&rh$@Oosyxn{BmzMXj zy5-o~i9rLOwHaIt*?*+@?tqtz)|D)uvvb>t*^|eagd2OE-sj=5^YzmU>*gO{VC7hU zBIc8N$6$l&{c>hC&AFt=2*&$ zHH+>o7u{GiL-4$)%&mXaqWsjttxRg-)h67k%@WG(*!#B^8!_5nafjv=UYV-ZhU^Um76)+dC^(@l@2X>*SK>Uwj%?5ZuW}| zi0`lqo^S9gmN%h1#;;`f+FG^h`}EkTOCb-Bx}5zlS>Di6H{Z411_a-0Z=&jKkCv0U z5#heW+P9kPt}h+9uw*30RN}BmsRU~mJ2HHEiR!fNm3Z3<0{ZQ!2dXr! z&(mhtKU<@7j?qgS#?)`dD#mM5Ry|YU9ln2b(?O5Eez;o`uWO;v!%x{rF{?Cid zH_OXmve{fRolZyT3jkxUBzZw1Yg4$*y# z3<628Ashe*0y^>Ve*^%+a0~#J8aDP`0scNgBccO6yd0!;YlBWpRZhHsmne=-U~vq8 zXiC;nbTUs2Zy2gDYdHfj0028VjRgSomD4x?Z$b7`>Xu)*8x1+2s0R-Kly2$I9)Z^b zNeBS>P}9gh2Y_^SR5_SPMX8B)f5?LA%AyM2c_j`I@Oi}l$KJcf$dO%XV!>{LR&SB&AsvrH;ysstQ$RL@FY(ilrvonwC3+(KU=VAXa;Qh0J0spb#KMMl}4EV?Xoi!F~F9wi|@0@dAk&#u! zR*x40I6*M8BJShdbI(2J+;h%7_ZwryUN^ia5%it>Imi0^Vi(9rqUQ1B21f2IAr zy>R)v4>rDYZ{xw^hnL&W+ie5mmwXueNl{|bli@JS`k+++_KpC9?{|Xr`OgNyPhSfz zeC=z0@50x<_CFF>Z(%i1s~3Xc;#p8W+BxiZqi(+;?c8t%sDDFTeNmqORuBX?g7tYU zv@iWEIDg@_pAo6O_WI9)b3gsXFMe_MXTiCDOpD{^=g(aDS#bW$8yM=jpOJ8wQ1Oqr z;`!%<7Ssyf2!2kIlq!CarjS`Gn!|br;N6|wwgCPu2k?9H8|&v|`Hk86JM#PAJ`)7r z3fAXe`!G2F=9w=9=g++Uh2VVfxi19gKmU3B_XRv7qpK-46m9#_Jf%uLADRd>EYj$7cfu=Ysr!A-v|oPlWJy zr1v+14=+3je)7Es!QcNc9|Zs8Up@%_k6RB76mNbQoIxKn)Ys6_xnnv4$bj*z2P5_G z4_%WYP2^sbc6WCBGMP)xWVW2iq`NqSfF^TUo`V_c-yjS98_ZDu1~b%co_C&q=`}p( z88|~T@P>@_0)`oAR1zt;s60D=ueH!JH9vgz#MC@T2ttk5Fz7e0+-qIAdbRy+GtuAr z@9z_;gWrAVSNbBlJNG7Yk=M~3{y-ydp^>?(?bem~>+NOR?S%)Xh0jvAZVPAK7T&mW zueEU1R>fTFv5r@Lqgr**=@le0x$0-esxI1IL0OZlzByL)x}6*>4PAA7a?YVDh#uI& zvdQr0G z&b+3;%#`q~&pa<$n{oH73NBu_(Z*Gm$XrV6t`n?}SAWf~e)hW$SLW*~Lm2e3a_;%; zU_WZf=1fbXzac>4`)@Gc<~_{?S`MO=%2=Z)>hA1nU@Xu*f)fCJ26V;Y@Ho`0SbB`~^bx8)t&4 zx7O#s{)?ZV4;21;`%Ex(VSQfx;^*fseC=z03&;TUQLmSiCJc#=EhRqqz9oY5Y!INr zFWv}(b5jHie-qiA;g=U)<8MemjF3e>x+uDPac|U*^Y?;)*y0;fE_4+Y5gb&&i(oR8Em>mU;-2G+&PHz zBXv7EQk$_!fqW-DNb)Ql#A&IXM0pZz_v4~?Uf$_`nLf6 zOg;3Wg?@D}?(VC*S*{)rd$_cIlOQCh=a+W-*>=>IXKQIuM*TjQTUHG&=|2UuzY1un z74O~epBCuZdZ1Mu(08N#Sl!B#-Yz5hl8@+Fpm~t>W>?l*YY(@n9Krbc%ER9o2k!jW zfX;6u-vT<%{bxUAX@9Fuy2K=2xfRvBH_h(^a|IZAu#R@B`e z4OJ1RJ(Xt84Q*wn?yN^ie+FY{`D3u8%hg*<4tEAo(idv|%F24pOK3L{0}H+-{OUaR z;h9ceL_P7OfDaV9o+tJP-DE{wLsVUkn1?pTb;-UBNq0 z^SW_f&USNXlJt37KP%!A9Nt(O^d=kMvhyV&iyc1YB>*zNfOL$%_{A^&EwaZ|_l9gY z(1Pzzq(^-JNw7ZuHL^bbs-R6!b>>3wbJ*wzMV}3VGe9xe`S)G}I)n4CW9E3T_Bo9C ziyB9;X_CYuX8&t={MV5~;cwvASMlGkOM#1MCM5eJ2~CKM3ttI>i*NPgsEAw4P0z#N zmyWu(-(P*mQYr|77zC+FO>dX*jhzB;@txNKnlV7b6Zje$`ju{+4qJQu!$E7fmv6Uv z-Gd*U!*kl^6iN22q}$I%y_s${cs&R%{`z{dpJ(?UufA7P;3f=?UnmCUa0cMa3}RFR z4Qe477X|HuuRf9u!!yA>v?To-t3v5IQ4IA81!#!81(Od=QaGMZ8Q-%w!4@e zb>5wX3qf#h=S4sHOc4B9Blw>L2L3?V`O4}C4<6sX`@zb_oz~jJ)=IAj$O+jeHvXF1 z_%}YIx3r&5;IzYniuu6!TujTz%zWm9t3B;7z8kGp2J=loNxIva7--0VicT%$@Z-vIQKul zN$!+$U$`6)ck02``ektNFM}WSz8nPSe)=mUE67$$EStKAQi`s~Q{WA!D5EmzVmPN! z*Re>Me-*dS1i{}u69oVC0;AyHI}-$NVR2)3@_G>bh4?Fe3~d#nAqay1^z|TEf|P=v z=WG!Cqp$Gq-#!}zzx6Np_wSqyf}tJLn-s`pACYJ z^!p#24T6WTrNK{hHV7WcM)7yg2Ejj&GXLS(AovqFn}Q(ty|Y2^htke}bT$aSD4Wc` zcQy!iz?_00_>a#9!5(%5K@j}Ivq5kbIDn8o8w6jK-`M)jNc(?uHV9HF|KFSqg5Pd( z`9D1y1mA_U4uas%&IZ9-g13M8KfD(F(+lhKUk!fo_s;~M74$BZaZ#QNeon5k{UA8^ zul~FLCFuvjxzF`}Gl2yhsE3O8t7n2>T$h4IdlpT^L7+aoaOv#}mzMfj*J#DCg{r4& z>4({Nil5Jx-@YIP;^)K7ylelyoAhH8_n%--ZJ*puay(FBIT-E~vmxGy7XIBRDeq=E zT5+DP?L69x(>whF58uA9w3Fp=)ZG)ET_tI!`BYVOxSb^Jsl&ZEj}>74I9wrPx3e30 zCsfa-R4?m%WJbd({gbG#s_o70#--j4(D+TA`1w%q)0FMPlc?Xi8KT#4ippMkS^QFzMd3P)Q!HFLtHT00AI~khaGX1NLx@`WqJfR?g-(jHf8^wH(Kk|`J zWW(s|ROXN=$`XMfdHE$HqM?f`*-v9ms?UhXK|P(&z#cx+O<}F*g}loOwGfp zTPQRFw2{F@ihgF1((p!Y09fX}U!wC%3LnHPbeaSzP4SEhXY!)E)ozEN%cWkXER)!w zdHXdv!UMF?r|MKk%m%P3w zc3GWcCaBsOCH#z2&a%SdsMtTiBC{OKSr!USrkTs}3bRt7mi0|omCtHgJ>7sNG)PN8 zFHn!;F=u1JAk2;app~jD&7MfLmlz+im@0T?ur%}49Ao+A$*l^5(`-p&zsS2a%gFSA zW%QDScZI>PW`kirF5`UECdLXGl%r`ceG(qnr3IPZhe%!aNQ zB-X>-V5$x1YE;B|$FE<+xf(`AahT;j|JB2&C{&SmTkwih8Jj-{rARolc^32l)dx$6 zuv=No0vLFYKt`3D4O`u6K3tcWCHSfBmiE+dy`h!Zw_GK5&edXj|MkyEi~Z<-{TXSo z*fhZI`+M~&>>E&F=b*#>!C5Q$@=*$9;z^vVr%NOo%u>KE4Vn!N^&rlRBuk&Euo+^2 zM!}N?R`;Sbjr-upCQ=Jd1OLW)5FF$=(>LqVY1xN6e4-kmcvSyPDC zlx?%y&x%+tL8hE=idckGMuw@EfTl013lyWMrY|Xsr!R>Y{>2wGFZ`jCzT4KyNP-{2laUAlC-@MLriiH(lGH~dA%}V4XA*` z4EM@%SS-!X=2(8G|p8>uR$iZrMpR9lp7=FbhOM?%rnE7m9iCV z*ARNnE8UGezND)VQ}|AN^d#z!;>|M4p@*ZQ-ioaZ9uIq~8z0=|q53a9QwP@qFR+gZ zNM_Kk%upQ_VoE~w;sta0?JVn)I8hzyNEo;#!uglWmLeC3(oC+S9IY@9Io zp|(d!zf96rnU9LHRbX5lQgZYFT)ii3h}rLP-lryuSzNx^`i(ody*gCAqGZ)1%<=dr zrpKlw7l>p)+C4Oq7VmW3Dy07m_wuNSJE3WSe7)OIk#xawcsNOxs>4|2F&a_jUaXv6 z$XCdAlB87IN7zH|#yLHiQR{HP7Na~?@$;x#_K#G#mlP^X{WhARj4_;2Nui3p>@b~? z+4Y75bn;Mi1@+XmS0U0iOJ%flY3Xm=2}N92=ZJwG=GhRTh8;9J?Eo((BIijbjOP}2 zcDhmT%67Xwf92Z3T)Ta>8%2u?(cJu8G(X?F9^bguZtpDaY~SeaT)#5E)1Hs6wXa9f zmEM)BSFaj0dzKV$k#jP3R0QnGj5$rc0>6i z+9sE8So8YU{*ZjQttjoaM#G*4i#C;Lr((OoJZ0b{om$JDwjOBnj%qloRM%kFaY4&n zufxVBX`S>;e&3G|)iZ5hlyy*J<`e^C;czN^*jgfy2r!&UxtSS~rBr5ocsVn2kMxKv zrM98Cn!47c!ILR?YG|gYkpjD{vma`PK3!nvBMpejC7e>(s2q;U4mLb0Dq08Nqv~1f zX4s3$h<9J|shKY`8B1l>&HFp*DH}7T7k;gtjUgK}Fg{Kl3E)aJFObE3A;B!P&(*?! zcs+|75jKy9_}iI4EfyXRdjh1#*fmqsElw0Kx`4azG$n~3)w81?yEcAWj${AcjIHHyX2G#0^)K!_u^^fa;G@alRoC7K zfkprS%In4*BHmYBd3D@(5{p?Guy!Xay~1H=d(d)3JB%-_!=&8HMrEs;_dgvTM1(^2 ztcKMeA(9%-ipQTt3=weWc~XF_n^ipFaqZ&1M4ZE^ANZI8EsTRwPVb)K&N6U>~mX@tN{?RDOL-f*b%}SlY_{alX?q!v7Bt~4#;YrkACb8Y24!9RDIusVSbTrtu%}z~CNx`w^ zfMc({_rCu1dMdeD_jqc#*}AFq43MT^X{xqMO9~pEA})Dw_LYXFbyHokR7Tp9GmPC? zvRMq`qKJ0mPIE>*e|AtAZ`LPn>Q_UGw zVH7mVZ{q){spTuc^0IL+34u*bO})+0)_;6Xn?Am#O&_y?0S@rqW;n*2aw-RihY2fvK(S>G*pq= zSXpo3xjIN9b$Paz6#Jwhlt;t3LvbXE{%;MU=dB|7aom}2udAY;m7P2qTsB7gUXrVD zmIF_^(XbdqX=^AH21Sq!p%}K%og$}avSY?MP2O4N+6W^~VFyTd)TjVPE{}%0dDM$r zX#BE>qqi^2CTSVxJ5e{DJxq2I-8F47rF%ArGytG(gFES<2ZVK?$<`oBX9rOl?Z){l z)xM-fOA!s?mN+}uKf&7oYqr&Tdmo1~Lr^wkVju;9XR;@oHKrlDHZ!*{t$KNa2WltF(Q}-u z!z7RUaZ!++=W6@j`#)A@LJAe-M7BeCTCyD?>LMOSIgSd1x++Xf#E53pqdi!Hl7avU zF`tLgK|F6tsJ*CA+i{$#UUHE1;vQ%@Ny|+2s8YAoZ52ygzxAVTHV6qiYLEVIMcr@) zRn0JOcCO9L&BM547G0TXYQay8pQ`AEzmw&ofxqAmlK8O9hDmp3xHtS}*_m(8FU~KJ zy;a9m;dpIQ#@!M&uO#h!q{1j4cqW&c56WC!bazwZpYZwht3NoYkO~LMu-J~nm(DiX zYO$I`7%)R}u0g2gv8Y1wBj4~Us`xF5P5 zSz|Al+bJ8c+j&=m7OdPMw~&Te3nG|^XVimKMQX9VIF0ijF!V+xt!RzEL3D&SynqRX z5MsqJ?j}1)-1BJvVhA0c@Wo~w^42Wi{}uT_?nb(}^Y4P71O{-3=0Lxf%;iWRzv z7k7D1*Ffl{`2+gMAAZ%}c6khdHP9uTs-phzlC^TJ)Gq12K^!bR^(^X$4OYZabu}9v z<;m_|shZs>HQ#PuY0bCiukwCRakT%pw2$Q``-4zVxKCj?)3oNmU{E)_BE9$^>BcS4 z;55@Cx8v^^X~R~DFeODIf#F_s_9*QYOHv!BxfmPtNL#q>$A^NCkl+3Qf+>X0*_2_c z6c;r;^_;?@AVv+H?(|kqr7*^%^{d6aY)(cNz zIle@4&clIgp$|W}OQM1NYqGI(-(T#gov2@kFAa2V#gfonl7157O2wLj_=}qEwF7}; zmdWf<>$DIQzmh4ATC-slr)&Il_H}NWuq7rC{MMemgE-&Linx$2pgo=#fNQs+8Ct|(rzqEMVE$CoEp{cTK$4| z9YhbLM{_U=E7Kc;b%YF*+6&837S^v^UFu|!*9|*E@Sbb9y@v)o?JM^Qp zyO-rfI3x@Z zJvTm0nr;5LQ85}EZ-`@25Pp-00TG=rw{Vn@ySQ>nwoHnTxo?qMpELLKH=x~t#_Qb*Y+cW1Yt*kgA7 z8AH+f{v(`DGCNQiP!61AYGRS#*rdis+Ta!tmE(ae*x+{Q{0$fEB?VF} zaD*h7L}jdSOwehTW(d0kLy#1v-n5F-Xd6)hGmh_^yP-F+fF$H78y+1eAbQ@JYctDq z&M%OPp;TjIK;n!V6)95<*Tw=dhQ#{$qw3bC=o~ZiVu#DDjQXuXJb;b#nVMA##Di(c z(9sb)CU^MCiL~}dUpsz_f%*A)9|QPQvB1w*SCsyJFV<=vv(p+#Aj{X$P*c!Ol=S1? z5(}6+d9KG}0n>^PbwkRXeJ50dpjwiHG`NcTs>4=znH@+f5s)rw&u~5m5Egc_)PU^5 zK-*%EHhh^^4`KBMjKnK+q&sWm+|u=DN0S!cBBovK%9`| zs`x&>?Oqlamtm2pbOF>oRSnIFz%snuhcl@hC|lnTPzsAj7tTd2 zw`}m?wTtZ@@w?DWO326;BfD(UJz?i834E$tfj`_nIN`*WbmdW=YLb)6&RQAc7W~q< zq=;j1HP7BSVG`FPB1$t_by-Gu`It3h>BYq~dx*JfJrhDs^3*+wVUE(k7EWW(iw>*$ zI0DdfK!hg7FCsP3K9y{p%o?O+!*oJ`Xr-5zu~-rSf&`P=L9v-kFYOHkTVe@UvH(qE zgG(0l!meQ6h9K1FLBGcid??ne+;Rj`3+k>7UFi0+?mkfnW%YSB;bocN$g;Jc5Ghuy z7DhLslPns5@zA&`L^>?fn9i*g;3L`>xZsD&&LfJM@+j^UQG6AsegBC)?5th&CW5b0 z&)8_h#-MFj0=%)%c(HpYTd??uQ*}3Dtyp#n)G16gpf4tLA9}Jv38=zVL@%Y&j9QH| zJ5L!@C>e;b3o2T-j#{({X3YTIoQQoNiqqo0QtPh>=6$HgFz)b!m0pi9N;9!^xQ7E| z5L)HHyduE`zUB#DKI%^A0EiD(cNaV#v^@p~kqI~K!iVs5vq96$LbYxi=_vCjZaT1k zbW|%1tgt(0=ZNsBq%7PGsTqT{CIE#Jbq~&W>uB}9kTz|3DzEfD)9iz&EVg21gx2!l z+YGU&7+9E;H+7CV(y@e_P;5KPG#654&kxc&2gv-^3}pfr+Qsr#3cehkc1pIwhGS&e z0t@99#MGdhp08b-eMm%QGWEGw zWY>$Jk-3a(s@3B#{J?8A)ZQG3Bks0<)}f!HA$zb7qJC$gjl&477&Fsp$P@|v z)N7(!>DuK393@F(6(h?ZZIh?T0i9-tYVGnsl}R4=W@cvSkA56s8P05_k0_Y&W18|>!wL94|h7BmnkF;VGJ2?-vl2n*kbpUW|hpZ(@ zH=gd%TR=n-Tkov*Tms(iG9a zQKUOuqL)R+p5$}=_RI#B=fKl=*`^7`L8|C)llkOByfR4*AI(nh)s z=v0#KGq_d%`d3cpR(0z^zm}0n$l#I~?&Y|&yhXV&SL(IXxl+H&sZN&E?JO--nT@)8 zyomZq8eg=&32m}Jr5D5wV!6lgj59v_lnSn0iZ zCrRU;N+_o%C|M3AB1%gez;z22ZtjS&c!IN4uM%5Wya~LN^lQrVxH%dWPjIyBNbi_z zmt5a<+sUTNw7@*ISWBfVT>#^~9dD0zMLk`zjXp%e9ezixsY`m(Q?10Sfa3|ef)Myg z$=u&;*oGb)#Ce|daAMvNG3^Sb#^J$vS9J8C+{39D=8CEb!_Bm!Jb=^0zYuGct70_7 zp}8s5jZ)0rs6b#tp$_AIpTdLIR1bk`rOIQb?4E?L#ofJ3wNOFz97IrXSRD0KQwJ91 zMRz7X%<{oZFP=qK{@L4iZauy?vsVuKm#Aus&+$y5L#-P0v)x@J?j@@MVvvdsi^ys2 zjR)3YLmCIC3G38xm5({R&N~Sz;d1q{E0Ddqk=t~CfC^3Se!R4=>2TP+IF0kD|0t8= zqbB|VWsP6mExOuObo3DOBWkKAgD5$xn|!oYN+MO;wN9=2DBF+IwVn#EFWhKfzj}RP zesO7K<=(;%dUJa_Pd4V)uMY36?B4#))xUkP_aI*U_Kj=lQ8qku2dSGvl2)|TnrpWg zt}e_iv~RSpE`-bKQn+|!ak0IyxOj!eq#hTDlY!wa8_$$VPa_>5(^WKW-~zjOHX34b zHFkB!kM{C7>Y?j}w(bBbydHI{iAi`8n{UP(?Br^~)%Yvr*AMni}tczynQ`x-x(YhS;1#!v-?H z&6gb6@Q-A8uxM9$cChE2{dTL~jrx5YF|&1}VKS2plbxfPEZ?PiZ(q1{NoR$YWBD`c zVOA8$c0cBr*J26E_jW&?R{bp9Z6Wq$S|Qa)lI~7}%;J7F#Gx2F0y+*BOF{z`a(&?0 z7QvQpBTnPPiJo=siiZKkh<{2L1Jz8gI@8W0n7QT;>t0bh7)VidiGx;%Ux7Fe7V@K3 zvCz#Ibbg_5F>RN73%UCg)Zz9ArG>hit9G|m6r;FzhcYrby@L4A9kT--*<7oehMhVH zuzjBAw-bOtYb?7Z!}ChoV}Vb>j?-#bokBiqIE{)tITY#9(sQ37?jtHxm=#3gM;wgH z)C540X@n&EgG~lt9Yt&x%c|kNS)Q7~zU7k23NB+oNOn|o5G8$(8ts1A zUSA04h$u#U?rgME#t0E7n`blGO|v{M)Q3A+-iD zcJv{%DV@S%ryMTL&hnK`q?u|h{nqJqGfs=7Ob!qQzXK}) znI@7{15#}Q<=y%;sf^@mQF`Tc^+D2&(;}AAtomR-z54#fG@DzBq#viqK2hu^!>MX} zy1u(D+1z|zeJ4H|2ea72W4C+90yzO-l$cmWudOOnREb{ptdfkOx7PU#%Y@EQWIal~ z(IC9x_NX@rK6>Sjo-mAP98zI9Pu(=_2x`e>v;7am_NB6jkCsB?t-04#cv{I8@c_x=If`TidCOmk(3l{t#n%erY zxQO;ZYizl8x(S9p3dm-h^NgDL^}VIiD}@#bo-l3f8LjnQ>m$tA(Q$GO@H9iZ!RPQEY-0llP(WEV#t4rAgy*i;`$DrGB| z*}SI(y!hY52Meb;zrW~ufy4-Z1yNMc>|w$EvKR!}&o1lexBi7T*M51-Tnn0ajkW*9 zmD@i=!twnd+@@16$k7gC?WTJo_ifM41yim61cuuG`_E5lsO41)IKW(lqN{dVvldWh z%trhF$#Iuy>mV}0g?FAO<%91SX(cWu9s`9wR3g`njfI@>8>95w*|utI;+`cGcP&#p z$#WzYqDK^aB@aLY+@W?{UNY-&Cwcw~ob%(*W=^lHDuhu?I(%R|;S!vL$2=u2GX^jY zWH7wd1^ml&MLD*Sia`rhrhd?pZ!QPb+)MayRRWuo`UYu&(@-bkE*Yyluaz7`wJpQe zoRA%kjHK7*Z_Ms2&i57*h>3*6WQdS!OEv1?WS4JhJYXCUVBZk?M za*xDJt6j2S0=DfVj_g2*tei%Lc6+IPWvRWO+H*7QYcn|RMUE_t6bhDcZwb%o#tD=i zJ|za%pJVi~Tu5?Bs<}n%jO^%ProWx!D``(hMUzH8OJrP(2F1(|i;RY9>ljmqK8Ae! zc@JHDa}PxTC%H|x4v9pxIcrcX&MdGIuaRFUt+z8(L%h$g>rm5q*^fxx8#!N5PY8@7a*9~56Z^Q?Q&W$WHOwTvtJc;`EDgR5Qx$P%$z5+9t z*P5RpSJs46(|`od!$2jGTn3RNgkZ`JR3^0Mt1N?a}cG>#lGdC1oEbCF`)Aj}@ z2VEXpU53!;6v;Snw|&uUJ7U#6OHPT7qm z1xe$|AzmBoRn=kan~JcoFD8f!Tfi4S_IXs1*b>0SC$ac&iP~xi#BwT>|EiqAWqG9? zlh%kK*)e8$P^8W3l;%(_b!K>un{9YQ=hKupsoAJBf3Z_3s&}5SugIYfY}k=`&g>+8 zNGR8mK+^I1eW~O)*n&9@cEvf>0m}yUV=Px1QE-Lv2nyY_Dzl{utP=mphk50QH2d6+ z#hPGC4m0Si@H$g>v%zh&ZiX@_Nnt3r6x0!jYFNdhZr%kdDI_Phbk9f{AtTX72GU_3 z?(br!DM4K@%GEVZd=1VTI3fgPhzaJ_t%6J|&v@0G0!BOhB>r|r^m6Pz5&c96u#*^wwdr$_D>@35XM62CC`Gkb3Q zRv0gGv|yaw5c->b6{MJs4`hoKPMxsO^=VYHqtamjy79B}%V>&~eW8^nE*UWWNS!(ch3R#Wn=8wzPuWHeA#e-Kh5ReJ#Sp%N@Jf#Gy3j2VtySz=SC3vJ2~ zRzgU54TM|ZZ=4(f-$I-OFHp@nh+C3uv;%xkUG)!-ct>G`$ZheaQ}tan$E#LK{W1e{ z*pP^jV;PU|8KAR{NGx(sl5q|N)mHL3oRyE@&`#T0E}Vi@iy{EfUBaKZ{>BPcX$qi# zm|lr{QM2AMIe~Yv>H%O*;8UVh@N#kl*(V`Ydt7|dg7TRc-{}S*L9w3o$hM`qOO5Rb zlx$CpRSQg5>i&Z9xZ+p~Wb3RiR=;1{S>a^fL8mMXJadc!g=Gc6;qF0=HzCOm~vnV%c~b_>X|TvF=v7^7-_%|%Y-J7NT)}K%qn$j$*F<| z87R8)$>+p!db8q;xWZw*X`s7~($OH~Jtyz6c!qA8i|Ux?GwLefJXLZ}oj;9jv(e(V zv;wK;MmpM{o9^nHX!?M@tLC9Z@;!QfXIgK!Ye1Tp8~m8g+%9o>oCT>KKl&oQG3?;Q z7;b#*NM9B^Zs!&^)CEtq)~zLBmA*BQ?*Md}rfRDN*6CN@-v|+ErQLM(rJi5E>gxSY z{ulw-T|`eY7j###Ie^LuFUg6AJyWRkg>JR=&2Fnw&0?EJ)@(T4wr)z|S?YzdWh}7V_t?NbMw5l=cFrLZn%0kjj`zdFn z@tOu{T@yZdc>KR+O47AzI4~HoLE4OuKsS^{4@meM31g49QC@T6!>yah01#V)i-y$m zR86j&i!aM^Mj7dtjGo*^L4L0mUwmL2^0$8mF+3V?jp&7^2W<5Q2g&xO>t{0s3~d2v zb;8_*pQUY$xr%#nBz<(vp}MMceoU{lxHSXzxy0mJLh-GgBp)0`IWTPO0#z1YQ&uaZ zHDsc*_MfKZnzF~VEOtN_Np%Z%@}N!5aA%5~QJYm>SMC_N#+G-(VF;6qo<%>03p?2WuR(; zlkwIpQuvonr^8C{gUvCrvMh?snYiw~>9qT)0x6A6fUySmBK{rZk4wQ?BB(AF3BE!) zW3ymt<=m@MR7HO}<#dWF7s8mWmjG|R9N9d>MkSZ_rHs{+W?&L<@i~zTf{JXnlY7+| z@W^KpH4E%xDgdpkO5SGEXC2-zcI^9VYg5M$$^@J^Q(JA~RHHHg`Z^_VH75!!~c>-wslO~VW%c}p?I6spUIH51%WO}VHN4%?&P{TdEj ztu5+OsFRfAe2F|$&)~{p-K=8Ha-*|M^jD2GLRxrz;pRs#r>x#*g8rOyZ7E-`}TJ}Sik?^ z;osWWeDwIqcfa==e>>Xl_Tru0z2t}c{Xv=yf0P&H=-}}A(U046^9zetu3o!-gAIKX zp?^PqPOxkBQ-fpgbN^&OTL2hHfB|a(VcB4i!zND!05F5oX`QwzvttmqIDcdD#?@={ zH?ELflr0o`bk2zv1dfr>VlVFMLonLCqqShVTHNj^8gp6?7C(;@gve$aryvTsfwJH%<6x_duNL><$)yH#OQ?DMedC9v`HgxKjP2CLc{iHM=l&@Co<)(|ay6$pD zA4wZ<1Z5qvduo?bIvio>3@*EwK7e|unr-#2!=mQ7S!d6wL%nS{w|!}KLw{F0J}Kha zN_%lyZp4&}S%YYHu@CbY7mt{YzFC&0#1p#Z-6=6|N!}flGoS@qLP*Ap$O#yQ@?)j| zLQIcZmr>YdG#iYwci^%i$w|3}8v^)@3Pt2L2v82fU?#xOJvti34{+POUX~4>7;hK; zL7V~{?GmafAqa%h7p8LEB5dm^~3Dw+u1e(>OmYo%(nTQzc!>+ z9I6Q8AUBglINO%!W){YB<{NCtbDnP_hK}Bq>)B=Qu_tF~@)m@x~@Vm_FdW z`H_wDCTg%F#ElxxZ8}IV*ut$XS+5h4@0OIkmXlGra}*aX9WdMlHqbFA8|;jbMJFm+ zL^%ra!@a1CZ1gaFD14}MMPDK^DQpu;4j~acuDf&}#wW%}h|LIfUgM9&yjL>#|MLBb z3L+e5jA}r8I74ZReF8ez(HYkMN)y6-?fC(3PF{%;PQZ#G1rrV|8q^RHM_aoac8NSw zEgSVo!rs2~ZUm_;EwJNOk5+Kp3$*~P6w;9xw#l|?5GY#Z*UFPE;&DS~$FJqD3<$~q zYdaj*Zj?TGKT+k=2q6s!EUe^BwpRG${U+`km+)mqp(rd8Z=hN3n8xR6v}X5AY@gL_ zsIrho`khvG)ZA6Aba96af+xv6wNjKMU5WRa91jHShz%Ne6Ms5uTupjAqrxR2mfD&y zS`nlzH7}Q$5&Wi+s{Ur8mIQ3fG`9ea*VAkzAbmo6;|)2mXzf;df5u9nw0@#+bq;D( z>1nfcLMy7oBoTTXIQ104*|~Z~0k*K+H0CJ;$Ouu1vJ$$i(AE9Xz#+OtJ$WvPF|p8e z@@(E84WMsHTsNT&&4lfcN6eLTiX9W(1Q1eCd0xJY`cq2U+AQ;D&s^&SccFv3+&%=y zZj)$=V$<{TnVv>N+B9wnE+b&js%#@b`jz&mNZTm{Ymj!aa#@cT=+N4+nM00wy24YR zaj8xp$yuLU=nbIYZaprFXg7AZh-ThIx3Z~~y%laB>r?pNYrV+%Ax{)1dUiQLed6W@ z;GS|1`uo&EU7OJx{wVO)CtuJSexv3!v}_w^W~O3}l?wr$a;vJ3SgGsWxVlW|1Uomb zEvrM$Hr~0hu*`>3IydID9MY{d!>zenPqAZbP)n_wjXCh50X?FhD9|3zF))|BKgf0= zzZ|wn(;B`fHj|sk(FCqW#_NX6C~GP#*bJ&ZR?YDHP0ma9g0#~YbeQG^jyz6nHA3(9 z#!BUm3aN_hLmbVf@ji`9k_KQ?fr3e08XfQheiF+kxbiBi5gp88>hvf9HXxwhCpg3 z3uG0jUCP9Tw57&gc;Xxjw&thWWCzrMD?_C-hx4L{=+he8va3OInX~{^U0=9<{c8KV??bBGU+UeB z+0FYceX~7{jE+2c_n@9sovvZ!{XgIJ?rq3DQZ&d(3b_|v?L~z`GHnb-?ewE!Pw{ZJ zN1Xc1_!+|l_uBBK9Wn2Xvcr^e3_ANYwYu>Erxrgd%6Onr?IM!}oI_rxn$i22OOd|Q8Dr#_3R#Yci0Cfdw1$z7SUyR)r<-+Q|3;SURj@D zHnuePmL<)P*xxDq4g^W0_$UY@J+^pOYTRn2im+>GoDG!=1*~TGCm@FJ5 z@d@h_u{5JL_p(vHhdU#(eFsa9M~BoT9-c^rI4_?>i2wC&8;0k~^DG@R->iC>gO<`_ z>t-13lOKz&?F0|x_46e1-?>iGbDA{?wdY>gP z&LDgzKB8UiW|`$MXWNk^NQ_Rl;nvM?^}ZaTH^OQXad-EkB&Fl`^<G#jIQ3TaRA4dVFAmCbZh26%@R>JF}IX-*T8pcXKYnorI(o$jf^JYg0d z#=K+j?9Z<4_hd^iq6<580}UIB_u{A*=dGP2?)N&)kFa4IL#1jUEaRo)AZUuS-=Ar~ z2+S0-#7fPuW^_D&6gTtkwAd@k7IHp!Y&C6zChAa%&Tz|&*_&LMxSiuaj<~C+ui7B2 zHLTi+Jbvoz6kq0~!AKriBxRN#UDP&zW4kOfHP{C}a8-tD()SJz>E-om>J)K3M~|P( zm0g(;u5E7IUvqN(wRIk?so5otS0`el-~=&Bl4w8JU&u?p$F zEZUwKH0#eP8>!dY`16T$w;F^YTH2M3_z>riOcjz!NGF-{ScTjoU86)%E5e;!O~}Xu z^>1XI<2u?<-&bXcc~<8~oei0bz(dq|ik%;_5+*;!l^7l5B~{IjJU;dkrc5)vColSA zmpvg(_L9i?&Y?br?G%7xY#W46gXKYmi||Ko>Z_fEaXwQ!_u}aKCHPEu*>1z?l?75C z=|4|pWD!Lf@F^Rr!%l}$Q4II;sE9jZ=rdP88$dmFUY*I}hW_lT~;f8G_Cvgw0V8Tm>0E z~!bG#x-Y4elb#{R@N5Q@R#`|D1v|y6xCHwVQ-SKSp2UMw(FF&(r`zs*3)dY?Su2 zR#vy*7?U+NJa3T4&4pJGnmvO}<3kvW#-ztCWm`9K2FC)iD)V=&zsGmd7+(PxDtSPR z1wr>8dY%|@bZAc@@|&%3tm+)NKQ#exT(5`|x&j}~$-XQwbIzAo9hzbDHPp%Tgm^CE z>9&L!q|h%K;q;tlM~X+h7bi(_4>M)@lKCJM>4KhBqrxSGZX89esp}yxTk>P5J`Uxc zSe=5@xCYs=1&G#732*Cg306={Yh+vUxq+5%b`OZ$tyLs*@DCQVcJ$4$9o$^*EVzCc zm1UeeXTL3~W$=>erxI$_&=qyOSAs(zL^eY7PuYp?=*-e36566UKTN68KAqySnIa|u zLv4-0GSXIG4GWbcR#1sT^MC-hIQaylPhg;6KFzZDjRx$cBEwC`a5ViI$|kc`gALDH zSk0GPKYuiPI>z4R)YzfB;h@#!;rsPu&Dc0Hw<(G84ENXXJ$iV@WJ5%zvWMBBlGs}C zJ?hkRanX~x_I$-pd@BJxd2wr8v_plbRG5S|zK<6rBkny#1${!|cSdGwW+j33Y(JM# zxH6bv#t@nf{Qr(LO=H(CFz$7hU!nJh*`dEj;8sisFVn32-B>ePjNw9$2E#du2!L1W z9W~c(qj@wd9lIsxRkJ}AW>L^7L44}bU^tJ=C3xknEUGk*-MIiVqZ+r8)Pz|X_F)Q! zGoGCTAF}`n08QT1=QCOfI10fC+FlTLOG4@W_a1=6r`6WF|NuGG{Rvj#OB10X)HHlUincgz_%z+LCsfv*3E!cXJ=S9tmWs~7i=hVdER`p# z4YU%?P&5mqvFI)g)nocB!nmxnj>h;r@#A0mOc6!CkBcfcIf+qDcTIFJgLNdlY%d{^ zb?_QKx)x8WR5Q3R&De=;X)(!W2X2pviEtCPLU*@H{D)tJFF7zq>8J&+rTf&KA$DOe z$|d8bp#v`FQG$~=-%ONN!EmAKMDg=DO(Zsr1B|qKB}=qc`cw{4d25<8%WI}geUIs| zr1!88;1qbUxvsB#PtCQluq(~e6qH$7#9bani2*ga?HON+(P%>4e+HHA88c&i_)mm? z6gu8RPGR|12vm_baUnVCq**3o13gw*myOI`loyB+nR)tbtI?c#@y@&7{OIM@mQGIW zsPumcYm-$ZhCvpV$S3l9V1YNotu5O9GbM^?7q2H#E71c_ujX7e6njUfd9R`zFCKhn zGV>xUy&FGeXqGWbS>oOI)%dEa@f*G%14R3ala=w(|$LGP%L zj3bUz4fP?=P=)NJkw_x(P*81h^Ty8;T=7C~4UXq=eH$2@r;=T4Hh9z@tML2H7hps$ zXc!V=)YP#`RBDQJ2ssSm33EaMr`b2d7p#t9-JD3AfORt~6IIpdK* z%vSpT#CWwky7lUfJO;5wjt-LjJY#y_&a%D@Dzvx#b^CnX1c@m|_yJv<{1oO6{9?_@ zw0S^GlHTH1^=&WP*>TdghVALa=_}LMrms)WwZlr`^>y^BV>7wiW`mk&-JGAYzZa&e zHA$=@vo&pe*4NE@T5{af$8^+Ub_V}&FmE+`k@+5ao7iN$s~YQ5YECU^bZMr` z6MuAcbTkRRwlf`b)#>Q0SRu&1jqBHv)(rtm_hWsGlx6>{9KTt#hAp-w4rz^eK}D# zY#=v?faXaFog5Xy%~kozux4qCw1AF4gl!I8;LhO7m}oWxxiYSI)Pm(to=P@udj$Bf zv}&LyQkWI}BHA7LBv3>-t`8PaZA`6au$84Gv!o#tx2ppNOv3i9gHl6>62MjCh!i&D z?#FzDXfv+Ats2};##Eu(NiIsDW02*63qk~y{A~O{8r>=n_eW1({l28a8P$-6h1O-U zBze^muKSlhN#01YI<$`WKQl7}V`mrknz*2t!uWYol;h49vtoFk?Dl2M-4xZW2jae_e7+sp1bC`XTlv|jC+w|E(c$j-ljRJo!W>z(Ic+?l!ynhf`5}HBrFoM_TXQG=?kDN4r7Ex|8VJ3L$t-`DPk-V=6Nyh{GM#tCx7Jr+|kdB8R6h1Cm-QY%6XzQ?f53kvG3tdpv0%T|8o?<9fg0WQ^X zX=OsY<05%Iq6^9lrxWRdMm?5|2UV6`3knC9Q)VNdg?iwP17dZ9sGrAC?}$|chma$Ne5&?5x#zEUbyr}2&z(F zb*_YM)0j!1jI(SeL5(>}fG6hTt`Z%_$|hF;N~uoDA^846;@n1GV)vF}qe5 z>##B`K2KD9-~_Bg>pt^twjxx}e39uZS9 zS~un}zwIdyEDSx2kov}84HCwzp&xJUrfL^%YnN1sbC!o$zF*Lp9-t$RQbpNnifPqP z$~ceuDogc=El%pU7Z-8iPEVyT6CUm&UrZ@77!Q**XO*gJnD1lKY2^e7?BJDm##{4| zFQHt6jm6DUFSha3loXO8taW30ag3W8UMl4#YSc^BRHQZO<~-w-A@@nS7~%QU?4=m> zOUe?;s)!DR)hD<(7}nk`DGhqTHG&oP1)7RshrFm7p`?>?r%3r>)~YEF*KKVZ1}R3P z=Zq=iO@ILEpzy=COpjj67z8Kh=!CRbS=}Hka({SAxTB%CQJW#W=U>NN;^~K_|U7^B{Au8uj}z&Zm2%!8D*r<42bnIOTA_5ei7J_PT`2$+UmW#wKapUW;5y!_o5fu zaT&eHyDzr3W~T7`#dr<`sHDEl=za#ylJw-hqz9Y&R!UKlb*DzA2v)0V7Sp-D!HR%~ zOcIE}XP#Ex+Ko-k6}DqZph1vhN^XO&3?#C<(sZ}SMe^*BGMOg+aiHOa|5rNSM# zT|QJH=Urvjvg>^oJyq(HrrL~3)sLg1R7hi(>?B>K8;bf#c{HVR#!q39Pr(2NZ8#FA z9-@s{u2#0u`m%N=&Br8jg?~T&80BX@=iWD~lnH6-+%3EmYHT%NJ9N)hGn93p`=g%t z#(@U)LK!1pngb5*#sJwgC`pCnoNKgIGkg;Ddvs63r?vxd7g`k&6GKyYbyET-)qXxl z@h|M#Fsy+RV#+f?TvZ)r1cK@5jwk|z_5@mEmdF=`B!<&E!J?5tYY;vksqn{fo`uUQ zBo=@l-~#y0yP=2F)AiO^Q%&?XgI_b~Sf4;)o~lJ4bNwWxa5i(fG7sk2w0{;_5aUPV za|2qWv&3Mmb{Maf)hu#U>uYF?bVsYPv8GM(r%ro*SGAw-?6}dR+DAOSMLJ*~Rex%q$02c_rW16AaJ*kx|C$*AyPi*5_r43;>n(LfA z9g_wriUi_}3FA{!X1s)!W2S>i(28{|G*4%>iMy-oncq0l*kNv3U?=5zB>9Ky9l0iU z&dCtZD|x~uLmpeO9^}1PLi#EgtdtSruE~{3NUD}bCVPT}QRKI+&oU!%IRVhRDc$kW zjM|i})Xi}IJ&l{Yd0R$tN~PSy}*b|~85>Et+f{J~=x(wT|y$Qg`B zNq0XkKLI4iOqfxy-5bc2WystovBc_RI{3DDu_3XVr%4N9LGY%YW`jFMig0A_KxDbB zM)@);g9xUp56f{LYNQ{>yN~CX8fl*ZW4;X2%e9s{x?56rj3;7ksw%}Qox*Dhoq#pXq;InL zLBZ57+XWM-)WuS>TU!xfj`(6viJ+7Z1$w$}%^rP4WFFP+$xc&Rpq-{4X=3+?a(1H1 zu^3V$EM`%n+rltCSnNiOjRv$4yPs)rILgEYBq{X$zo!L)DWeL<%?`{3;{dv=q6Sgx z$f#73@Gn%k7o{2+fw&k4c^U~<;}RZT2V^!!@yP1bCZqh=C}KxhsXOam)mp~H^ZJo2 z68DgXqi8CfRv4^TifY_9bY*H)SS9FlEiL2xAnGfaN|=^s)x17($&w2%D1=cL;YNgG z(L;KDQ~8Nh5M$`;6l35JMG^IRN$Q^JpvR*2giQ_m0^$MFmb|R4<0k(h3ngzoIzg3(8m>tq-TDA+Dv-;D*mW4dN zs*5tpabjXX%L~gYOS>_Q1Y!ZJxEd_Yyl03Cw?rVt6n0iUWa3wcQ9^MQID~@;d_L+* zHZOBKfXCYXtzMk=+*`d!p{)tIFaoMl9^PDqpkA!$&@r6`z8U10W4#OwM!f zfXFy3T1l6c>Wx>sT|@x8@AOF+$$PGauzrmA%IVul(T(yR_A3*vQq!FyrDSdEt(y(E z$tkjzPAIt*l1<4iPZmH#ba-_%9a}fz=Fwm~>)$$hoF?6@$9cpwY#K3gjfHh9QZpJi z+9~Kg&D%!m1|ooa{gPvt`p^h79Al$ zkvsES*Y9rKYU>yvc%8CCO4cf`S5~*KU#q+x4V{-)&C71okJDb1+rDosY+b+Nyw7_r z#J07zv*%8sYg^YBY@xj*{CPr-nhPXea^ng zvjUMzxyHne+gsOH&FeVI%RSrs_0_HGx6R9)B#-U88;e^jc9=W;Y&bk>Q5d3`g&Xr* zx9lwJ#*C#q^IJDowr(-*d8F-L`kj1SVmaf21=>n8ilJ2*LF^!Jt_r~JZ^}D7KM36a+ENtDdlQPJ* zL0rSVEOmih*t%sAG#nLs!^CZV!JYJ?n-oQs)AGUOtZrSOH+>h~JnQ%U-YvZpgQOoH zwT4k%;;Na}PSkavSh=!w%M!Vi#g|(YJzn3swPJhZHV=~&5nvnSLD$KQ83r-K$@3^e3x=?DGzcC-` zSaxTB*3_74>FoStTc5h%H0*px7}*4Oq`hEECu$Q(BJ9bW&4g@EB?5^zi}DOQw3beQ zigDe#+0+d+Xy&GHfJ`RW0U0i|J9oEkUB`mPd{>3$-7I(Iu@##&N<)FjA?c$=Ps;YFSGdkqE^>{FT;n3wxX86ThPC`9qdxe_XdBE#N@`!ig!RA)3aB%Wfz>4+SSxPd-`t3ctk37L^j)L~`dY$W2>pXt zh0!pXDdT>;n@0oWkM*;8jG6TG6WQxpPPbOv9rgQ1t67@HUF=d#f5PN^aCotpS1=-M z9;ID(4K@54)`;PosmZS49Ni*wh;HvRm1K-HBQmXwk43*)jkY9nf!`P#Vb=_mjVeFCepO;29PF}D47k0Yxu|vVn#X;*0W(Y0#-mlz z16SYQ;2Ti{Rx)@!zkb!C++XrS6MD%cSY+9#M{8^}ght*X8U_;w@q1=aQD?=``BeGU z`8Lj0)a&JOfdxl`AQ^gN75|8(Kw%B1U1%k+?#d(d<@k8&)_;JD0T@JviLEPn0S5$I#yc0g0 zEIwhHg(|GvwaTGtgd|3{K8D=nqiB>n-%kkZOqwKa5b|#D=}LXfxGGh7$y#H!AgJeQW|?vS|{)w2X1}TR5eZi_DXg)pDm(Aar-w+oRhpadR#R@lgHn0;9#CE zv8_odZsJhsX57UpB|aNGnl^PDs$R!~SBxh%I9U|bnS$BT6`^te|8u@S&~ z*`XQ@?ZHva7eBQHI3bvvLj{%;j|o)8HWx6(&P z!)Xa}ub*v4eO{!vPQ>HzxWXeTZ{vkTvQ(4& zL)BrU`DGoj?+lw6?o`Fp!suE{i!$o>`6?Dde4Sg6aU%$ZGEgxNF;ckH{rC`l^M$`+ zcu_X|P0!g{n&29c5w=~lghd-*xYzv68uu*s#jI|8aMznj#=~ls?nscj_jV08`X;4+ zM=@kG&KN|cy`0gjMHjD%{AjQvJrA(A*ybRsI;^q)$|nPv5=a$+@&a&QMINGN(-pav^3T zlcR=|Z6~&s!6jd@UNPD-VNFlJY~T~$h|1?aC<6lxV5LXb7^U%{gwV*jB!)vvj!s_i57BXoWOh#O5BgPe(T*OfhZ+-%HgQ&jEXp~ z(}9e>;+<}_IPq5Xojk`j3|J*4n6(rlQJQh%`k>7)yFN6Tj7KE0Q2#jmzP}o9#?4vf zOqblEWCG*MT6K%0nM#jF`EqNmr8~&P*%;@}utpQ8oLEEFpgDSENRAKK-j5H3v^^HJ z7xzXuwn>g23!6N4Loku??I2FekCA?No6zWX&$b~r*S5Cl_6myy3yE&GiVEGGU_%3? z5hCo;>u;F#lkQPSYPj2O>!yC?D&_c0Ad81+D1l8R+1ZJ6vPtoDdqG`{WHZXkWTr~W zLKOyF9RnudS8LckG(NJQlP~{lH}M`uE~dYAGfZ~VEGOGr)uiQ*%-IyKp$eG5DF!pG zqJqv<#zh%BH{ReQ;jo^0OQ6>CVt5BDNi}Se8e+26g>fcBb4~be$B4=7aHuj%doLzm zaI+m1ach+0h7bsOino+)jc!#+j`Du1AVte9>u@RhJXK5GEIq)zO=TXX1wvFhgWeTf zU$Ym>S*JeywJyla52#&0Va8UaLYEGm*&fpR>>eqy&R{7X$%Sa{pR$=ezl4jeR7Y9U6|0g;tNsO@Z%AwVP7+O(IpmcV6iHNOQD_sHEM;+ zQS=-;+-?eDKyJdEpmG>oaGC+^vOlS2@hR&TqPG$S5Fgg*6+(8yvGXKJlVWeo14or$ z5jG;1H^{^+Ogp$05zEQx=_MuNz{fQr;xl0hh{cZ-u~YJR(fl0yA3i)rs7A=Z!&U`O z7R9OF#rL?_UL2FBMl@J69DmHx(#_ZKNoZmf&FMH!>CvIhi8&e!j%IM9R|b!gq(7i= z!fzUbHGiyXlPAMcA2txkoS8WNpp5W3R_F-|>gAEcI6f80MfMmS0k+4>J zu*H^pv0mhZC>=%p{?Rwp=6*6151ZV2ewo#h4s?UdDoOP|&{}H82*RZec0BmhBU?p$ zT)`w+V(!O>^&_&&#F44@GXZx@s7_>BKIA7tD2h7S_h63V<=)NvJ08S_zIe46lb4R` zq6^q-VZx$ZS#Q~0Z?a2H+mj0;Xgu`JfEb)Q$m~>FGFr@w7kUIVu1V#_DXVrSm;tbY zT{nnJGZ!PnJ@S+U%GJ>+C-IvxIxJ$c8y- z#`yCo42oUTDt5yCC#?`ib*ot{ZXzkHmbRK(l=FWluvDMYTVd7E3$d0R@%v5<1Pz+Wzsm`Z}10!AJQ#*X{0zGo_8chOh1{4o6JDrlh7Zqwdj#Cvu zaqw3$sk3pvh&v|e>o^c)W?!Lm?I0FGQBnDhp>l_Z5|Eq-kAAcH7ax)C#_#c96GU2P zh(q`Su1OtZCS$3priq`xiz~iByX}%iX7X5RKMO4S?&i}Lc(AF$_DmZlM^d7Q1&#SQ z@UbM}@pj@uZ0)Qea91*q4TS8d70ZbS8PXt0N`H|DgSdp#;3ylx+(_<5Qq^ZBaH&Z` zfJRiEo!jd`QjL-HT-81jo6diZt2yWYHliA=v&hA$wE_ed)(W}vSOu8$4qTOo3Vq@1P zYekZh?bKYBcjOm8(LymJLWChFX%9&~wsr$H4fqnluKsbANX=9V8~F~J(j8W|+@MJI zC)Rc(O;lc*hJ9k-jRG!6w+?OtlCS%-y<9jm?PVM;{cOwvK=bYyEZAl?VISs`=n3^` zOXi*Fr%z3-k8%BC(`@`uacgU)_hOLsre(q^Ivi6!_J{uZp_r^^*jE9d;O6sG0lq!QA1bigB%g9<` z9|C2|e9k}{)$9w+a@tG5PG{RI~;q zCzIWuh=)CEn+YxNP#*rCZ)$XzSlBweP$`X z2&|`Sd`x!FYD=#8rPhw40)ieKT#U12_W!c?uCbA1*_l}OJbJpRZLPGDtjEfWTgjZ6 zWDhf$nPibwtj^M~s@PTRZdOr6R(H=7IaR^TAQ@em5jhc&WU*U4UKsvkd;MC-`!IF^ zLGTa5um!In>yKf;fB`?&YeW9&Ct&|r|FMCECI4Dr#`w-T_Z1n*S68>R>rJ(4k{LJ7 zJ@?#m&+DFhF8JMv>B@9i5AHJ1GD^E~bknt}2Ix++P8PJYWoDz4qRt5~o%7%<0AUfj z>^o1o4pdn@MG|wg&35U+G`}`(+x)-{oj}YVZ<7@t%^F%#@yKEbROiK*rZK2vzZzJ10JgaOM~ z#^Go}(h2Z-*xT-{GFcmq_-M#bFYs~_3dh3Y*O|GM&>g2VRYlS2hUXxTulK4`bBJ9&Mqa{@JELBjQSG{qX zB6-vAY8afVIHa;ReNLQ=k{F7`HP@?BeK`$}qws_b0SSMyq0nHMjpR5?2f^t)_8VfA zh7G82uZdkBc2*3}c;}^sKAIi$(x%-IE3ZVmlu9euP^Aj$LWjdx=Kxa!0nz8tZ~ z9eFZO`aVnIU13axxulOd*j>osRHl-Js=m$Ty+zCEdBCS+>Z&pvvZP2f%DU#??c1^- z{3}i+wsz^sdKjK$#EmSNGZmyE9q}v3W9`A1V9>?6iKBiK40p?npv0t+c_fQh4|qzC zRtP!wc^3OLjn3@Vv~v1IWhJ9^we7WjUOgt9+-F+NDkNa4VS=Qx?5&wg=G4K};tRt? z2pIRl^?sZTJCT!Sf7!{_=eU;6D8oL52z#MyQFZ!GpV$zUHVEVDsc4{dqI=kA=z?jPiz6}?#{v* zDrPDegD_Y3xFjw* z*W6OOA{qRj4UiHhrGyUkZj$;KT%9>dBzpZ%@aQDV_#9dWVuDd8*-}MPU$fipPn(*z zW+sKKrl?h}k8v9y$vqszN>9~iEZrT0kDBN_3+5c{V%4i6c-R`{TZid_L0FyYb7)Vh z(cI-*zi}h?b&79##ROoGM@R!E9U~Jsp$1mdKB(BcU zHWNRh<7ynr=A9#BW>>Yb-w%)P;B>w&qO=?9og*=+`r+~2ajJc4vRl`jvu0i^Jm4nU zhmgnmPpxKV6{^1l19cLT6-9KDi~5+Xh`=%RB^F8RgN%vy^-m<0byT|6LeGUcA-KG) zX!WP&gbP_FZ_T_}`RTP^5V*#o_@l3Hc7C(+v)_BI@|P++cdL|5cXyoS$?%QJPb+z) zQh8CSyz$fj#<%Z$@>=DmZ~QCYs8oiP%9~&Rai#LcKYH<6<*-t@_RV9Q zg*m8HuKk1-*jBIvc zob9*$SptPHEi-!1lz)lyf*AL`M-One4SnzgX|`upcDSWVytKNokS1A^7_pg-7xLj~ z0V>73$5ju|pU~KLw;9C2VVHhD>VwgMmu^Fm2wXW|C17vO%*^Qe0{YP{M(?uhQ#0K= zd|WqB5o5P4ZqX{P{I!Z+wLh;{tz6hDEA}<4*uVete=EX@{mCEx%?K;@Uv*dPANMMS z75gm#5mqYKzO9L@OGLb-hs*Ov7Y*NnM5%o8HI2(Zr*WZMfWx~G(Pxef4ec7Zf9N<}UJq_d)=C#i_i<-fPoVUUL>!72ZJ|0BPHJ89HE41phiYutN8 z6&)QUrHO~CVCvwqPk;*8Xy~`znnCdv!HgPtSeh=(*ce;yeHbD%Z0Ty0q&b1&zx_9g zkTwVhP>3cQ-wvWfvWL@P*g&b&?#l9wO_hd+QI@BtIHba2A4^sL*lzZd-it78MhUL5 z?nh}i3-X{1R%6{&85v#mOPbSqLHZ&LM`603JsGo&qkR7F{ zA9ktUMn|J&6h~)vrrS_oVcx?JH#+)f;&wY_=MM0!uDA=K#6^b^j7Fs}@jIa$jBtYc zLO8P7QjVj3Mlj`%i^A2Dn`K9R`J)u)vUY~-S$5sjRFdkH8WoMSfDAK* zlLv6VM9?V+K050`!0sla(=<9f%2mBLr#h|H^+u=FS?2TJ>K(E$zOU_!+hkVG0*Q$% zv3b2|++At5Z+cCfWS)6G$3k#KN$_xVL8?!ZeF_a?%M?HmKcWkNWz??I?(F$qBv9k} zYlRR^!OPn|&GK+)j+#1YisB+UIAcR3%GAS0J8FA<=bCyqdZ{+ByKus;jEvF*3knvk z6Fa0TS&jxjGc@pS@FG;K;3zO)!A^vjTx?&R&#sS9GY)d7ctXokny)=3EyTE!%5+b@ zK*xdwreOrTeJeE-Ci)?5?pz|HmAdqV=`NgTEO5_dAtIm|#X&ERj>B%86d8)*zEpBf zTP>3Ark*MX&1?PBI2cAf*WS=5>mY9-t=6@KxX9q;L1NQ`!?>_+Z(dn|Y}K^z{AF#d zh`idl-?-yjE5GzV{0WK9m4EyvBs%{M7M*YWeO0Nr((^430%WKE3nb^iGkbB#$(WSM z%_mu?cBH^6wsCt$LCz=PC~uWRD>Ts=j&fk>1aY6l^n}!0`D&!5tRIQ2=;KrukDr-v zN+E7@iNG2i7b?7NgKlQe87VVUqHiGSLXz6}4DHL6M&r188b#y0J7gXC`&1QlhLd1c zRt8AV_}!x*U!&#AgO|KE_woHS8IL$UsqAr~V3GUgMnBt0$2ed=75F163pVxf<8h3# zr=`zj|9j(JIc|@GxStH~g@eGaVWbhX)3?MvPe#4L9aXd-K*^^DY>HCMDYVn{hjn~X}(ZN$QDY>P3)o0Wuu%+1D?^j zR@J5kindD}^+)?>u6a^0%YeWn00aTg+G<#AWs{K2zRI7*$kn} zGOIg$GgZ!ydfjj?*1aGPjB@3pA8LcjDs|gPUtBVI6JnUw=#A178k4%2KQsb*?b6X| zw@{MNxA@deaJX1Ieqle!DgQ>Zo#+5~FEF_!kN&r$R;9Cz#UJyYW$(sq+Qaz{Zgn{c z`_DM*E9-vy`kilgI&u}*w>uqN7+|ZXIz=sME%9e6t3Fk=&9#qJOVteq&aA11YP&}^ z?XHo4rK6)8>K(OYH#vGGu~ zuTL4r_tbTF9QeqseD!*512=(h(9y%pNj)=pgyxGj>q)EA(5JDb9lJ?;^hX~=NC!XU zYaewXfGaOQSyu0DKY9prtsiD#8U+JXZAGoJ?k8CiBO!Slb{S>%edIOf&5e7yI4iHw zOH{8nuBvS4M{&1a12T=6MnB0ozoP z1@{!puc?tIObcnpDa|6ydDtd3)S@Lz%^)Q$H?p<(6n9sg(s^5R22CK)*hhoYXL<6> zKp@LNYb;rx2Hd8GW|pG0L!G?D`dK|RZfkUjj5cSR_!XBh=w7<2O`e8cPQKv5RhuYV zAY5=;T_4WTrPYSOx4Y#+tK&-o8J!sGj?kwy$TTPI?+y!&q9M0Ap7<`xFuTpb+mp=@ z)j{yUZJCEJO91X1!6(}2PoxsqDU7mbc`}N6&J?2;x9GZdh$e+Qo1n;|G^-<$SatVGWbPov`l7SOs68y zz^&XQ0m`dPZ4uPp>^C)xcDBUk}uyYi~>w_#Lp~uw; zgc%Q_G|Qh6he6O$IIv!Q5Do@>PO4tou&Juoko-;Wn2a3zsXmHPtOQfzXZ&~zhyx9( zs_Iz!95uW)Cj*>UfxhaM0#7>7bgeqacc$9j*lGmdNurH@M7p0pqWaNDEK*2DuLuE< zXsI@fdvxO;aMG(lb1@xJyr$lk^S3qM>Bsu!a?3nlzw>?KQ$1~9G7hAGmUFwc$s>*U zcqSm;5t0?gY0o_A1uKI_LY~{Dyk1vkUGp)S*JKk}3kXY_p;`j)H)6tliPqb$yZE5fy1qdyu|zDPrE;P4<~nU?x? z`eV6BlCG_^Rdrpz?W~J7?1v`ZPn@$4@tKIn*pDLv@Ud9&DH;!{$K#mpl@VEUHfM^z zBXd4M!0=_*8|S);ShrrK!*{3^-Lx}Kxrp9*D83@WWOV5ZkdYCEbSut%-^CSj090gD zRXykZeiDatdopqsx^=q0fv{+`9HWdsng^Uf8`ObD1)!=ZOJmDIdg4uDwW`z77YUVC z=8(Q1shGLX(1hZb@ujDt2aiHCUAh_y7bl!oSso1rNRp>Xjbep#zr!>c$NeU2FUQ-) zr@$+k05)dpj-RK3G~o1QD&H%(zPvRU02>cs3 z&bV=-tkQI$r!UK>dPQcH`qB(5-?W-yveCuTJdq+FR?AjC|2DCRalt&v`J(k?&L8U; z6-845;fz&8cQ-R~kEpu1#f|u+P-=od7lZdOkvLov>te!Cqsx6Lm6Eau3kt>Ku;KNr__hg(a^GMaJTuxmaHTwW{@X8#os;Y0?V+-1eG*CISD4yyq`#eu7Pun!c#SuU5 zY!9d0hp{WREU2o3HlVn-{J*qy_exgXm+rJze~^sL*<9f^h}6$rH9X12XPUQDXl+*o z`;JAZb^^Hz2|?W(IE13@{K zHi@|;Bux~g(kK|QByI4i1$7SV-wfXlT8owJ|s`eg%et;&nDRqUI@7`v`b~W?JDP#xU18Q2?1gd zIBW@M?6j38cRiV$Xuiyv76Mox9FB52;0k@mipcKv%nLtbx2G^{8x7V&*I6$59R$Q= zpHTVH9KT!&$Pl=Qo!B+IpexCf8)MdfRe6chfCdY*= zY05mCvQULQp3z1f>2+kurj-CiY-86}lD|;V7l|UeZpti@D(KDy$~KPr`W$spn_g63 z*~yP^AK?C?IaQWD zr)!ij?~z7JP~RZaP&*n&Myx#!0G!N+pzG-LSJq zp*30&6I@fq{UgcDN>SN6u#(bZK~Xtmibj1EWh@X~Z9{1zoV6hLozXDeCMEeClM(l6 zee)jtwBAc{LDRKf~3UxYzqR zwm&&qrBrn^2%?ytKEuPe@y}EIvxk4`_-79P`~?3z$3M6Itp@&7XYuHJ_@{w?y7=dJ z@Xu%X=X3ni#6Q<;?_#YP{{e_|o)`_Gak~Z*x^|fHAGPyfF8v^4(tDEcUyUQ&=xE&z ze8l{E30*;rT}FgveVGdoTvEz=%|}7%=Fz$)rp{@hneGaqxlgSi>ec7ZpUdVIfu}v| z=g*&;0=c>+J*Ih~E`-TA3<7lZ(kMu_#%}|(M4vS6m ztefxTG_8O3IahPmf-fD%xbfaiZPkx>@8|7j&%8~K&3md|eM;-eQ<+HS&_7^h?74eE z6AUW*2H;Q;Tf7bX?AhB+K7wHKK^Sk3_df}H#xCUb=S}Gcp|1OCjmH8TJ9*6R8u%cL zKZxQN4(}D-8$|I7+GcuNb=W3y5kpe1gW>lS{?cY6{{`^!A{nmKe5h`##hc4FT)}3A z8vOj1Iq!QLy=C?TCg^F0)>8Gw8%r(KAUrhP_%FhP35~*vb0X$!(i8V@c)}#t>8+6S z@GDZzqQ#m^?Fsj3G7#a5C$E`zvGb^jP-U?@eUp@?h(>1oFJ@8*DmsI>c@N(`n_`lT zW~M!+-1Gdes-NbF^-vtPfboFLiDzIJHL{3h@l6`>S0O^yi?S?9>EdxGBg!QKmyDKa zQ71vB@+47%B&Jih_6nz5qlV(oxk?TW6o~;t-MTRSP&M>=lSMhF-d62aODzZk4k>!} z?Ptg%)fA~dGfd}d$Ok(+TkL_8B77G0>8O#GlTB6qjvSMJ<~kJAXkrqwXb{FYy5W$b z`W@Q!bPhG>J|#|X{~^!v+xZ4)Ofya-?g~Tt47-F|8I#1d=`Sw%gF%oE8@)l&dr?pTMY$Jcp5T*s zbr!|FkcFEq5<}pq)<_>&#(UoSCc~zzZ5|D|ZS@(Ig+PJ){ZJ+QSuzN7T<}mP0A<*g zH;610)2E8{+TatK&Z*Co>`QcO!pbrma{aaJLtKV$wG*l5Q_85Edp3JYgj`$VuE#bB z%%t{~L!pW*$HAc69+n;FHkwG;v)gr3YIkyn4V89Fel@I2B8rQi%&HMKpeYQmsqbwc zPwr2fp=yU;zelr1^D{|+D{VZ8VK4{})4)Lg9Q)l=g~VOK(*kWlt_ER{}31V;7W9p9;@4y%Nk5iT58=gIYYJfExjPSwazVE>ieQwTQop%jVk`$|?ExO@T9|b87N1%dJno7WZ|$!Yy_7>lxSU`L(~x*){)`WY_Gt#d>Th z++Uvx6xlR?<(ooJn%VcwR-pXq98GYuis+c4^CIuBy=6+M+A%mk>an;lWA59C5eqP^ ziyQLJq1kIku7d@s>NUJ}jkX`?YYneas+54)3A6l`ngz~zLdv1q>A*U7`*t8gTxtoN zCt02>7&##aUr%Kz1d0; z)SzB#%P?IxMMo-XZQ~qDiUkm`UJfum9fiq(#<&Vf=r9Ek0Q}pkmCaohK5HwNWX`c8 zgHZ$WfQ>1mx)aq)Q;S&4ZjYeUnwM!!_wkuu)5dCCh#E`S3WrTXxSl0)bsMQ>T+d3km@3LT-aureFOsB1NUXm^89>1!t zr>TOxR6VLdUgaXgfB08*_Tz7ANxin&xI=}z8+@>ReHcUoS4uyA{ZFZ?+CTiiSW5qO zk<#CCrSz|qfu&-!dRmSx;I3+Ym2AOKy=0|f@ z#U%i$) zL!bTAs>!q#(@WpkZP!1o_oLj@vvpIk?}f)3@jt39^U|lx1uq*Zp?h@rb}xp48@&9%mNu>tCa@UsfPm^I1`zV0{ zFpo~RD@|2fF>`PaF+~~cCqcZ>YA>{Ju1uK*1RyQx9wvFnobCC=oHBZ5qEOd14S|rXk|wHC(|gR3dNkll`p%PrIW-sMfzWeJ@aWrbgFz3J z<_?Gd%_*)?njA-cps2ZyV&<7T!6EhikWeOQ^r0)2sEQmf+aHgk-U}?y@rW1PFp8sL z;C3~yPL6OmpO3wBe^`tMgYadv9}Q5JkS^sV^E&#JE-9xT*Ix$1(Eum}hrG;5VIY+3 zNk~;4o;H{i@KU_;4-`N1>@Z!Gf%a1=R(AoYw63k(RMpK>@dvCRqm_eQE;jh&UyrFS zkDYc|`~mA&u4rzvz&IqKgB8|i)X5LFA*DZXtir@6*iPl(tG3RxLb3T&#)XCQ)__er z4=x0^c>)spz^z8I(2WD^LbnYkz}rVpweYd~1(2G1exF*!*&dg4>Vn&&ZZ^@MelSJ6 zS_&7&s!Ph-ey^AHtD8;qs~=oM#QGx^>1sP(F6&rYP4p|je{reoBSq6jqSxZJQihjp zIDwjdR5JCD)I9Z+sacX@J}Z_@bl@!EkZK^$U9w zz5=dvBgPLyS?)n>g)cU6tP@B{ebofz{MhAu-o*wiz4Z#Q_`)KC!VhD6C8XW#vCEo{ zU^xAm3cZG7SwVL-BXP}N%tF_hL#Gn3UDPC|OqG+;X|aYIDK4=DooT|Yruk7g^k!*t zxO8EGrJ9U<+MSyCOu~Rrn}p$BsEPOVCOZq2Nhk$O^!}QlqJ^fLj&uQBG!#2=K5Z|G zjhMr6+=kkaWwL*t_;Xt%&T^*WWy0h(du@Q@ehS#K{<+`YaYm~~E zOs0-I##!|NorpC)H2LC>wmCwJ>}V5eoOpr_G>EE+2k!82-1cF3Qpl*+8TYQTtLbKr z8Wv$YaHvFOrWefis9LB^_ODO<@R3*cH#B9+&NmlHL&BA`y5#e7lzJ`hTe{GS($60a z`h^jeyx|%jX0TXQWo^In9ZQg|l48S;;$C4h2J|m`-P$~csv-i2n3NhM*RC$ak6tv@ zLMavipC_w&OGf4qyE4t)_y@`OyJ@Hbb(kc5wV#|`Q*(2>@y2(DY7mTLK2ZiAQfgyF zvAV9Zu$RPrw|F#Nw5xEvuCEQ9V1TnUa62zr>Rm{hCGdyY&bu6kkS|&`Kt4Q;BqB-c zi3g`@*O`c2yV#~2Huq(UVKN=8r>)3Ydobd{SvXSIt$|uk4Zg{>F$&T=LWl+)ptBze zFTFh7&k;9uQvi_4M=?m4L=19XrQyLigG&to+(VHoD!HJOnZ)}Q!9pDt3KP!ClF|!? zb~x^bahL{!oy4i@P{Pd4(}N`Kh1|sXm4Dchn9an4?a-?>!Q6QFD2U?_){Ct}Q5yW5 zRdQ8!rba>DJCgTtehsy>OYp^dO)~DL%5jp9lJo_KGoEKTu4j8r@tWtz#BJnZI(&`; zz(6RYxCDzC=_5K&n``eqdfeRHc=+hC8WI*9Pohn%j=IfXmN{;MP|I?2X{p&(*^6j2 z3j2%%GFz~jbdUgKpyKc(OjSKj;s%XuPG8vuu0Nn+fkya8ah3;z!J~L^x`qBVf|{4b znP=ZYUpQUuEH_(A&Bc}z1@7q!=5W`sxuD?W2b4!)MrrJWzRH%AK zVef?+B!@hG!z4vn_dJLO83mxNIIZF&1A!KNcdMJY!#R&$*itdUI8TN_9`*Dh`;PF_ zZdDyOYd-zRk_-FAe%wBwbyn5$GU5GvUhlICYoVRT{Lklct$Z&OT=3jkV9)0@=QFE& z+>f?Xc;dOlS+z;-$v%FY%FwDU2b-o?JEE8Qgu(M zStW%NN!DKmz+$uQ10cy^)^Irjg&^c{n6^#U9jC_}hiOOEI16!B(P7>m7gQJqpCo); z+c1g~3Z0IUIE-_tk`h=lm`wE$hk<+fCSKj7w=@Q($#IA@Eo)1`zU?P@TRmN+n~Z8` zP;FL?xdkVpMlHIk(PqHsrP%4+6;ecJ}nR#0aX_cxb5QOMVNz z%8BJ-2;EJFBW%XOWISW`k^{A$HcIiou{o0 zltBz%=GbgXXm#=ipx%tKn{abv)zLPe*_z_o4N)w4B(|~HcUk3e(95#%0bCprz(Zq` zCQ~&YAzaHnZ=w3l^b!&Fu`fAJi1GKB#n1!HHNA43pJA-2+9F9hrKYNWvmVQpzj^%S zDu3$cP`oMD%i=JaJ>ut(Y#za$YrQ_NqIfjUp-L^$o|9<}uVxm$Nkj({Ovx(~rCfFB zOfC?`DopC7g-3ClYL=%}?D&afa37lm4KOkwPVeIZnJ(3L?Q7qKiD{Ake57&GYb=jF68E9KvEE9!UzSDk8Tzh9+$`=Gg+YrkdkP9opGUQ zn^oI%aLO8+D=NxW9EN?IDaFbUUycS*FUpbDLPf1`eJ&22#)u(lJp8@|d1gl{eX2Lw zVBsWVI49W05Q}vW>BM{L9kpUZs5E+g=RkGA40kvpOE65ckZb-sQ>NFq&r%4`$lV1K zaI&N1q+xMC2P}r=!=BEr24Ru|x*D?W3dg%HG)2XsA4UD}T$HsWE8!rKOa?zBN+8)) zYbz_gGy_iz5>6fjMxX{!o)5^wmeebeA+UgW#|=q1c^Jz9Er|+tI}1U4Zlt`v8sC9hX8;KmoW?oOe4o2vs%c52N_|{bU$KF;e8Ks>+^*LM>MN zS5G8Ov#P4hbZu=yd0vPQ-gqH0ZxaD#W2q5cbfF9h9f(;*kM1P>Q}#goSJoVw)Rsta z%4^yO`OcG3pJI>p#V)>ELE;uf4mz^Mh4| zTgPZ8HHc3UV?BQ|xC;TpIb~Bm;7E~W z&a0IUM*W?l!m1wr?ph)L_^Zs8=tN6Co&TR?L#fB3()t z5!>_=UF6K0O5)c%=@uAOORR2hxr} zeTZyVy3(pC`CPOc0H^mjOp%Acq}IoEEcF=v8A@lCrjF)~#?DiO9ucxXlxsIUc2*0; zB~iW*r>{c21bOW;Nol$1+ttouFCHoz{8;;cY_`O(^A?*m*htKN-b#HYhvL^}if0kXrBn1rn0 zPV6&5w63a>Q49s|QNuEa2p=JaXF=nH$8?ROM}?lJN>{u1uriuR#mvXF?c}h;b8kgL z{JHycuV9Ii&BaID)NznT*t*5Yuubs79G#H-hAa<9Fobq+ssy%?tdl5?a=zcXua3eL zPDP4p4CFNj`jP-FAb1?S1!#AL_Q%}+he5g%q=#WnyF{2yXOqZj#G8-Xc}P3tyvP|Q zep9unO|?+AnS{u|SP0|eC{1EIwIdZQrh#z9ERLaDuT3^Kfk7e8M9aXz4_hms`mFlV zkDT4ZAN`1S56RHnV0!BCD96fmQYQ;2EsK35eESD+ghjj1iLqH?i{EA}YxR7$@a}WO zKjE%rVLp$*DqV;a#n@h_s4eG}H1$@n>@w?0I%jAG;DbTa6NsSXLT{DLfHxiAd@@q-8h;R=x? z{6<$*TTS{uN8C;FgckioXMw4CL2`!S**UntP$11)(X5lo(rcj5*)goLE}z!OB;lOhjKw$Q)}7rj z$*A1U>6siN$scyx>t~lCQ2`L6U(4=%2)%97`9}KlDRu2S7%};fmfYJoy#$S zoj}jDGb%Oj9W^L(j{C?|7{jQ-<`FId1|y!Jg_)I2^&UBCo72n&sIsC8_v1 zUr1h|vZ(rZ%?svtNk&8bLx^Dexf9~HB~Eu!btkxM5CE8cYW0j40#SYf&>CV%0%tlP zia5D&$f#xu0HG6wNFl~;P(?&pf-75;VX0lbd*N}^3splsHzl7bXO1F>f*xK*7{-=r zQI|=7RQ2O9ZE8P3a_r{RZd`K{MjgAB10;jw5P78dR4FUYt_9P;mS-7CIwlopK_S*b z67&~L!;G>`3*+Ijqb7pl_z2mWdbG|{Bseh%oFAL2D#Sy$E=A^N60h>KLQ#~wG7Zjd z+$j585f&JK)mHGnA{5GQTvfGx$cc_u1al_|QiM+2#LFv!;?znNQ*I@L^z$vR2-mtH zEMiyHG0=^AQzX$9ySX!t27L;tQJj`{4t-&^k0x}KYVV4j@L2XiboK5>%3M>ps%lOO z>J@1s+HG*GqTfdD$1gycZ0&h>uN{V?Ae?{CP(C9D9q#`}Y)w=puVx2ox8 z`Yh0;g#g2J@Rqv0#l9%dlH>42zkULQ3?17TML3YSpDX3)Ux+LxzOK~M)jZ+6$0}vL zvI*&gbh@fd#;W&|8+oDVR5$G9!Vo6+tfRu=A41Cx6QEsD5qeE zoJd-ZvWMZxqm1ncBLhsTtJCg-03O_9s*a#$y4JQg5Ktf>I} z#}ae|t7*m%{b1dO67T)Oew+z7T-e!jwmQuwp{$y1E+<#?u_#W@Tq)+PPSUX1dJChV z_aZn97q}fAP!}Jq>%XgaVTzs8WV>`KD%7UBJ5EzfvGr@LJL7mMVjDV0Iyaz|-PG-M z%>$qaCJRS=DEVRfXldm)4w6(BF9Nf4XMJN79AZqm47^TsH6e7LY%D@tK$==|1k(Hs zt>B*fa0)^AAj!RvJt(+07zK+{@Aa;#J`Bs{i|?7DW5))t{!~e~@Ex+e;W$_5U9w`o z5E`_&{Y*-rUt}3iPJ%-m%w6`NQB4tdMuZXNF#Kq`ytBf`Y0Uz(Ak50ZmPoV7W>dtO zH+-WT?EWHQJ|X6K*3Tpx?)%_n=NPuW*E>%N&w1>Sxk(-x?NzfYrcv_u_@;4#)E|Mq z8=w-`DT=V=30aRGV-mfKGUZ|7sbYAF)C#c3 zZcVkt%0nzDordz<6H%qKK5xZ1v??(HxCro=tSpNrNSI`rcy`DRtODKFvp4z)g&OHPU++1Z&x%w$DvU}C>ur|oW2RhBr+ zVf*54_vkpnP2YAtSu?nq0Y)oIIGXKCo6s!_qd+fX*1Yogbh$NJlITv(f?zjs9C@<6 zh@NRj91Ba{^MJ8ajg@VkJ^olh#QUV4aJDv!UENZgkLv6NUO3lUJ&t_6xcId*EbJSV zS>T>@Dhg&-*Ya?1Z>?|xRp`Sux8{Z%x2muWHLv!=UNFu=oCA=6^Orlg3H`>QGH^l*2i@VLZz!;$aQ9F?jyO%`Yt^a6}@rnc$Bcj9;^5+3dNwRdzTHN5R zq2gMUij9Fdqh=Ahr?rJw&vF#>o+&ntN*QI6-7TwYCjwPPA@mp+iY+2@-qJ;ef41nJ z2x+^y&N>_}(hK=Cbd^tl?27!mT$YkkXyDST6UwPM35#UX@c>M*FoR&r(oiHx*-T9m zLe`g!=DQfXk$od`XNd$HY8M$h8mFTq3voWw=R1^{<()75&H|s2bktIbijz%%Q5s^q znW}%7{>x3m=}V?aBvLq{d%kPp{&X@=7k8rsr}5}84f;-^2JJd&F?if8h%=1UdFa@Z zsxHG3&vI4yuBEECR$hesQ zei+6M7Y$A|G!FOCQ38F+2(du$vN70-DN=?GrNXMjE9I3@J}OSEJaQ{{RpvZifdRS(f> zS}pNQl+&cdx2lhk+#(?h)t4tuz}I9q`w_`J9JPi1iZ%wd^rnqf5z@|9BfUUr>^k&_ z^j&jQhd6*NUreorW#gSliK@;i3jZ0|=pn)4$@__~`Qu^?Wl%YjSo`i}JoPHN-SYWV zcRDM_lKhc-5?6Xh$kQJ)(a{+&{oUkw@F0-2xEtFGy$y-EnK0%T=tv+;bfoo{o%bpD z0(XByc{hG$K5{)d<^~PT=DUt)ShOduQ$Oj(Yp;xA%7AU2K3w zJ=*=|Z6@9mlb`rzVfW?rhxg=F6J3?_A0*&d`HA!pBMe!`C8xy-`L?o#CRY`Hq3Ikl`E+bbK*4^fj*z6D^48mV-(1lbYok|MZlVlOeiqV(@M)j2!Tj;%Y)x z@I=|&K{DF@-3ZMty2RChC@A`~E5%urBAg?`qymC;hhhDUW$qzKllb{oQMVCYtB%lVBcN&;GkgD#)39*9LG4yu|6sT3^rKJ2 zv@#djsU;(gII?>)c4+2e($c{>RfhE|hGxWlT-KJpjz_jYG9PrrN2hW4dnh`}pl7H| zV9h#FRvnb^1DGhyI82V&SIC3x1u?!psv<%byWQr*or{qk&^gnqc}Q$#)@fcbR8{jA zH$GZyxUc(Y*e&Wan4lhLWrj}~m8dhYJ;^U^9B~GlXw~ulCt*)#wKY|B{qf^RkJV!) zorot*G_~uVkty;d+oradEFq;g5HTW{5clv>(QN;E2+4 zcw~;b{v*sDyAAWICfp8d(!?a&O-rqdT9+jxp|#}v&O@_3pP4=`I#=$Pmc*jYs;X}# zS>9mVWKKQt!e@6Ltv$YH6g?#qJs!ul4(z!pM8zdU#HU)WrHMoxySGthyGS6KvhK-@ za_p3OYp8IizL6sHQ-sRHg>}Q`kN=Xy4x5r%`FLM)Lf)mDxSZdGYVV%#T=er~idCS72~Mtv0Kq(C69J+|#|ganpKR(;L=87kflD>| zRvll|B+)ior_`R}V+CZ>E^6DU#6BykxZUa*3J_MPBwuK z+VZL{j9Yb!X$Ds73K5)QE|d|gYmrfbktpnY;SarkwkiWh>RX{poYLjIuCmNY@yNX3 zpc%G}LzO_F=hh8dC+)hYKs=OLnpwmt|+H!j>qI zY~I5`$6BZ*J06ZTa%m)LZjRh;%pP>6H-g1f>Fauti1EnIn=Qn}jm=&W6`R*_UmK?N zem3FYNqn8y;(5Di?r}58W}d#W=4V5Y8gm1w*6)+jIc`|*&z7$X-i^OFrtz?&mVMDA z!^=?w6Y{5Qob-ygLpT5{V$5U$ow2IjMt-``^WB)zNzV3`JvO-@_mHr>s54a}Ng-Q_ zj>NVO1F~2poysL?|2zi22}!Ll{M=+NvLluxNBU_G->r$mDPZ6P(PT$KCWX}WMn1|? z6wSUVFU)Hw2pB`cTRZZ(!ran=$H^Vq924cYu8Yy#QZ@6Pzdm{&#kxO9_WA6Us;#Fe zO#f!(r`P61?n6q;ZWbMPp;8D@kBdm;iCW(bqQM`0eY5kMm7o3IYn5NBFixvT5|>j? zTW?f;TFEPw%8N?njsN5K|0EhzDsTKx|LvbduT_5f#_xWkQW;h%Z_fR=QhDPay?Cv1 zSgBn5Cfx*bP^nz|3oe|nQc>@GU5~4HN(+Z?Wyp7r0u=NQ)5qax5cJ>;>PPEYFBpaI zCh6nwa2={$KX{T3*5h8%N8?v^6by){I<5%e6Nn}bUp%+<)Ep5*rh;=j5LL%59?!C& z#v+_O)qrVGAs)dEPic)l-3PN;644hoK;*D)Lfr`_wV)(}^zz3g81s)O>J;xxLafTV`KKYDJPJxO`bOh`zK zdM`=|M!o8jy1#(*7ITfR+=`x`lItkCuBoaEVj#O9QCFZY^szl7jl}JdT^{$njkq7a zM8&~=__8_3a`v{Qg;`lR2mNTAt2VAHGs=K0x4U7AJ8FVK#u~`F{&*6R&b0?N`E{+~ zBP7>7!lgp?t#`w`cl2O<7^0T67+cc*!FU+N;2V@+c{t{?u=?S~!Ol?_uQM7J+y}|w zRvN`zqENSe80MQn_Cl)S3Uv8#*~5lO`9acqK|ec#e>aNz_rgImbVDe-OsEnWM0=*H zt#)T|>H6}@jhoHYg|$0(@2$Uk|G~z4?|-oQ@X^+9Ki=MX^5I7x|KNwgey<-M93DlV zyci7QWc1@S%g4tjFHb*h82=YXRyHU`&~2gasNS|~t33w?yQi0*j>6;sjLKL2IA}dp zEXM1#Ho42yIaS-BoA~$wj(9wrJkX@;JZ}&w@F4@!hEp8s+z<0RX%P309LzMKy=k}i zTWXx1s?}Z+ABSmb2jf$od|f%yMg zN?+D4s1wQ@ACnsfG1vM#EmGJd#NJ*Q1PWcmp_3>t7K>gDL>h-GSc>|3)p~Up#K9qo zaGw^hM)j5{uWVYy_^MYlEnKf*J{<9C*4(f~Y%AAF9`D8|rOU+nB>knF&cuF#WUvxx;t` zLzsdw@u8um?DgrmFADesy(3~f6~z!3{V=23+@eoIW#^y|ITqz%+J*7WBBq}mt7L=( z*9_mAqd^eGM$%A}ojM0&sygv!<916^(or#(Dv=oSD~nCAkddMEzF`vPOKxT^{BgRg zR`tEA=ds6DkkUn_bGLzl+_Y~pqI*6k+Rg_f)Z9!ewvgm>vXz|J#QJ6NLlC=cqww(vf)tN zwtS|%70mUl@#kErYdG6{DIDFOPF-3-^w`aXz_QY;;ArqyObJ(P7Y6Rfp3m#|p49&e z`=raBs#?(+Hs%fubx*Zhoh8nBzH{beorNMoO0+(#Dv1&7pHL;#=jGLiOg@Xa7iEfz4uN3LW2D_oKmLM8|pQ!4Op0*k{_daXSFMhte+q}ph^#$Fk^ZDFw?baq} zZ6^33G9rt!OuQ%BW8>aqwdkg2npd|6L3V_BVe?-jOiq9^92pPoH%fi{?)uKQ2F5zwFPQO%_zWD-F@l*6e*IMS!6b7WR0I3=<+*4qB zd?n{r)u`(|7_d)O&HBfx&QIP^i=8=D2br4Ocn+<7k{O=ChtLqej;n*o9~O0=-XLDR z=4tH4?Ha$-xUJusQ#GqlWyEgBYz}JbXg&-YpRP52xcBxdIE?>T-Q8{Oeb4gT;;Z1f zcISpaDCWnnlIfNw8R*OBy2+W>Ghp~`xs36aUB+{n%cmk(<4UY#jk{OFO1ryF=AKsL zW@GPr5NyI)pLOOxFBe%?M&4Sk7e!uH9j&4-FmyNPMo%rx@PvwAhhM}xejyqy=+N|n z>q0G%q~)XRyH@DI9_(6EP(t7Qi;jB~VpY7DG-o|Mqf4aE)EGHI#SvAW3OiReNo#G^ zUqC_`-qm)8Rv{V&DG`)%oX~J*yJF<~AZh_2X$D3TSrf%e7RRTGbc<=WH=pn#02()^ zxFUb1&np;Ph>XD`6UopO3y=K0e`52px$MbSOrsLf<{IINDI8@}y<%FWckgKZAukwbL(o z>7FeX4aL9ArMvtUF5RhQzU<psZEMGQ-5H*_@t0;=Q zzUAx$?#~l=4}!QqfWX_QO&E!C+CH2`p*?%iHuF##d_h`QUhw)oujjQs#o#Zc&GKZL zkS%I6AU2`RCbyyWXS$xliG_<;D~@r)A2B_;Mmu|IG8wyS(k>jtJ!#qCOHs5J=zAW8 zB^H+_z~wT8#-ccb)WRg|X==ISB8tayRe^J@c=1MfwmtOcfpae^WzHmL!4A7T=S~$! z|1|OLMM?dNdDkW`7WWe&6r4>V+5T|EKvmT_Nn#T5N-_EIatJUH->>OV7Ks6dz z#Hh144JH6VXc#E|`znP>J`VtIuuKO5i}M^DWG{wcuzW=KkffHf?%WM{nU? zJU{B-b)69Q^Ex3cI5}*D@v)l;_Pam${wK)T{2P)1R&g`HY{urdjTh zI6RcuHHTKCGcCKHZrVk8N+RLq*t(6AygY#Qk|PiH_)58X|-B6mKT=~R+d(-uXL8fPJeOf zMyqq9)oLxF?b7YbV_@k0PO26=x8KRH0K-uEowTpo%Rr@b6Ru zYhK2HZd@L~_WI)w*B?LIdU8htO0I7l-W{|T?k~UGA1r^el5GWBOFw-0gU9RP4;~GF z{KHS)-yeSOxV3-tlPBFvPr%&|Hr5~RINd#6pkl)ZK`&f5iPG?J9Hf1UM>q#SPYxSK ztnaB2U4zni5uWbhKtbi`HLYcS&DAnSK_FMw)i?j^55NBjP73_z{}St+zbSg>wQ{}l zi|E<|7FH_P{%=Qjz}}`ra9*6mn9h3^= zWAb+Fd(d9HwAq6CK6DP&JXC3th z{2vN|zgbS;nczf2fo}h=Utb-Kb5+$F>M9xrhf?!63ywo|6b<1fjkA5Cb#;a=eP1|0 zUaA_WgKl+N;`EC!1+4_-M(-%a#8g#3!`%z{kw1ab3{7Tc7^ikq-{XS$v(s6fv24tb zz!NlSdZLYE+HN&(Hg7gN3!No(806teaH@Z6>wY?>pZzrIABL2Q4v>K6b*7)|?)Fn~ zm5YwF;EeWuU;dVcb_fPhjl*J7tz}ts7~@<3E|(@u(D_J@g;3HCw~VWeahWx(@e%qh zHPyqlomDjqvLBB_s`F}A7LrL14x%3Spm#M?9OTqZ?yl-vqPSs67$OPV zu*Pz6BoB6Wwzk#H;$S`s_tle)OQD|k9MlQmlf*&8DkG@R<2Z z%foSGEcuVt?>u|5z5ZBK6`hH21lhUoZmn%^%e$W@$q@gajtAkVpI#Km_WIq&>pRch zU;lxCc^t&|lHqpPOT&Dzvwb?;Pd>;xi=FG8mCnt@_Kz=yt2Q zNdL84t4m9Znv*|B0xtT>Qrj#a6ybAw!&0AAN{&o(7O9a@uBwryYIkN*+vsP5;51Af zMbuAx8N1*rHkn8+a^|R_X-Z3vm}-O)fH0}weAtUM|0=_=K(DgwXTfI3imQNG7&(H z?^c%%n#f(N&QOg_8aNpkY;+Ejahzw?5^NkMuuu;o?`20om^b=;sMAY6&Aoti`N`ozG&}@d&IU_5 zdzOsAsu=KRzo2Ic%fyipWo|*0Ox1ptm!Pq1M~AF|JY@Hvge^SbY~=^@-c*l3KldC1 z6gl-IFjk~>Cq#-fds#OC;)Q#wDq=Ti=nSa3GtLuEGTWugtV0-<-6cbGX*$lrl!OGn zOkL(3b(kjOQTHT`@^Erxs!@<-CrR4xj>7CkkY2y;b|J&DG88_3#+FtU6b&=D?++7JYw8!oPKkVnU%`Y1}n`*4vP zS(qM&sbbf7ojkwjp>0sac~aMq z=idCL_9%bV(T$yx!^Sw4LKd!K{Oh%Qq!9ljH&Fb?uT|)1|F<^jv{s5U!oL6~&xHbg z_=n%1<5uKbKGEPY_r~gn`9N*V?!!3^p@^u`jNXHGeQ2FrCAj3{I1UGtfJ*7hip3bz zAuh2$3&Lg+M|qMq);_x5`RM+hmnuf-CIavx*qjT?Tc^p$8$vAhsvZ}$@LeNG@Nw?ZYsbW!eNVzR6w)Cr7?vqVY_Bt(FGNRz#cTP;F=1sfI zM`4&|Wk5{h%OZc)izNt@44?5YkWAPXUdpt0C<@g%Ic$Cpftu;8e-?~x6-H~DB0nHp z$@=kyf|Uqr3p+{Xij-?p#ltzT6w2LABT!Ln?QzMJ*ZKF@#5w=6k(S!XY3RG?8$DNI z21_h4|DG!`CySx{av2;%Isac%#e@&)I|tXxgKX7X<_!wlWm`dhq^br!8DnU~X{8$o z?J38^plE4hrP*mU?vXDkk<;p}83XuUmfwp~Rb`_QW)(eUh)>c%xBQqr^~Lx!75_DN zYo_c&y6Io8Yu)5VI~yO~+m7PErSboGn`!(XpG{*#Ipu9=qu;2{s_qx~%*>MO$^6-9 zsLkj_n(Ra`OgRZv*CDA@b=;YonN=uD(P%9<7F(**S#8}|ZFgqAAQCgPs)ed8xUg}Q zhE&rci}EnzdX!v$$z0B^3zj>%$t)MF{S0pt#RK}?IN5WKb>b8+Ez-M5oCi@Hrb?ip zl#6qv9By7p4V0#m+of@-9My#j5depUq>qAEpJc&dxN4Rt7iS}XQ?4hrW-J2d)tS#R z>@{4z%~!muEKkzlFfW_tZfeh6Pb(jyh{_w^fTzIrAAYq#qjG;FEw1LfCrs37x}zvVCWj zLhGDGQ8%y_RJ*lwKqQyt8kEoUFpVF-jd-s^G#s^?t9OvVlIbD zL=JD`Jlr_$tNn3Kb4%k7`%U$3k`56M&Q$m^U-~V^U$e#*0s`v0r{6`intb1M2oHij zLjM=xsd~DQhv^WPXe~rBe$W|Wi>*D~n$F$hSy#0?Z7OZTf8Dt0P!u1YG1Nc^@etGpg!5VC zqBZAYf_svfVVpHrprb(eqDT`dcQ&`~Z9JZiI5_)4PL{)c;`UwP&3>HGYeqRPWas;1 z!YFuAPHeA0xHv#cY)@mu8-L0HVqv8r*oE%BTUF*A(nv-Zp36|q06AdF*R~R8b_po+nA^FvLbzQC zCj62?=E@GH(3I$T5g_i?C^?($o6VK$%}(nE`J2Ls7xh&2%Qx-I#^HnWUB*tnuj=D( zUcQRE>VvW|t20?zoKFAU5;RJHxK6A4zA)}tJ||~Su(MF=yBhg>CYWyB<6o2(v$CZ+ zS0eC9%Ul*_I)0W0@nM)f6H};}X1!AImyywDV)2v7k&VWKL0P9ns|$PRAZ&@6KEpxB zl3+tk7gL|c3zqiQs@P-Wq|?M9bi~n7d^-)YJRSG)aT+?Q7`$SPjiQ5B!X_p}L}6E6 zwC&cyV#{WUARa5L`6lu(%rXW{9bxiX;dR69#U2TSj&oDg3vj$di;08^PrkTaLA<~* zT5Q@Nwe4Efjf2_`VI=dl%bdzZRynRBp_?~@!QgbxrJ5BLhIA5v=Jkbb+v6Y)krCFd zF5P`_`tWG~QFgPlxUhcu@ZRBg^W*nE?jIg~(Aijga{B4VTPNw-!w(N1WZf!RSYb@L z7v1*KO1ouTM^2J5v~#fF_!@9APm2hd47Kj%(&=CnWk*38_IWUmA0EHEveWJzUe8ZI z?)~7m<9jzBUca+GS`Is{;}75a?VaP+{moBKj~_f~b*n0j`{X0)1QU0D+AaH-b}Fv7 zZ**EJ7z~n=uy5W#V6xS0Ewt1qOtUDpW zl-bMhywqNICw=mSM+pL#I1^CzwxR#IhXP98{;D(E%TO>3e$mwSG9ZSnFO%C|2FQ@} z!pZGrP$sh5XJ*y)rn>iVd*(A8(o2IA_<~QhBZ(l2IFfs}bR+G9qA;O3QJYZ_jnzB} z(lj_#HHO6XFXS*f*C&;?oq#2WZ4Ff2V!oEaW#wq7-=>zS&r!kHaW^l-);h=|S%Ic2 z1lnfR!$&*otGFuw)#wowjWW9H6%=7_cAQd4ebU=SKifaTL``@*8^#TC8jMZpoppkW z*O|+{CDxC!9Ji@=+at{OSw0-mW>wv^g}cR0i626__`GpMNicF_R6IAts~kZngnCLM5gNZ?I84&h#!+;5 z)a|rdn=_xg8w^3(JF+`2X~+Ri>IjdbJS4BA0pFHFG#nvBDv8;r@!pc_L!7q#B;QIh z^;D{P;nixB<2?)vVHu_C(BpBvChu@HEnZZ|VY=r)4dcEGm0)PF@H(HGiZ0xei-JW1 zi4F2%D+Qvk<;!j%qO!v%PEv(sACHFyP3o_uG;jy8Yw%g4B?%w>d5vAcdi?{ z+m{>;7rG!|;KHG~>Z)px4wsj_aZt;;yDv&juyHB9;E+8ZDyh!#=*Ts9K%NWmqTqg_ zsxNOWp92o3TrL2R2G&(o1C2|xR-eS#cm%&B?0bB=s%S_a8OQ0s?mizz`O$d4*-M5C zc{JQlGn@G5?1?X=;UEmMFk2V|xcFm1pJiPjj3j6P?f`-$2Izr_BAy{Wq=Dkh&?8A>H<5@4nhdn-GqPTK`#66x`{z~51wKjZha1 zHbXm0v=jQiY=k2I1Ln;YI4xJ5>&x%oX;S=`=9~gp+hMqBu64K=>&`|oE%_eO#2X+D zlN3BlfMo@3IL#dFxfwXdjFakuNAcmRs$nTLZgVjz&A`l~#Uxv5YR*ZS#xxj8{Z2#X z;zkdd$cPjaCS2n7xHUip1G?3L?UhW9^ScN*@*1ZPkniEID;tU<5<6BEY!`rXkhF1{ zV$97wcjp+O?l}ng-DcRm z4|?>|2y_-fQ?-{j?|e_CA(O37msp9YbBTh?ap#!5x50bxaFPt2~{b=q|^bLK=3!x;H3lDF${26v&;d+l^9exXk~ ziW*5u3*VHbuTQ*YpKl?l6YMJ5B|WP6vKCiQUr;~j;|akq7zlWk+a(KS6P8-+bwM52!Xs{ANT#)n6AUzthI z6@)3Jqu8tByeAbXq#_JLTER|Bh(9=pUhf~Q>vfVMYBhM|gblbDTx8Ic7K1xmyW0t97JhG{XxaY5? zVqoFhZ^~wd6oD6ZcE&LsJP=uL(#$QWsX2L)__fkhbl6O;s+*a0&fj-)5jPg^)6ub?+q+Mh^8bnC zB>rak(XqEYKuAmcFMmU)X0E}Jtg>Bi@-;uira%&_>M{7VZ68n>9)@Nbf0U#zIOT*g zHEm;l0F7>%(4|RmZ}?gQjl>-~bFAuB{?aIE@*$3WZ|*!%)pqAb%XUJ)t2%O~dq-1W zL|NvlY+V#zz8~bFdFY@|kWr1zJjAAl?Rh;uR&^Q(hDA9$b7n-ia*M2)>Oz?J*fWqC zMs#hI9y!{PsbnEF5fpGhZPJmiA{EI_&ZYx?Y|N?ii2pb|j53s(wW+@HOQYT7GeXTM zvEwGYvVb!ao_GngNP2Xf-WdeFL^o8JUL3oaPWU$Z;qe0Vmi6O|14k^O`O>hF!NpRZ zegf%>6r&7c+ivaT5ElW2v8t{vEwz@G>%Tc+K|C+T)#)12+C^zy`K2AHfXBTDdjj?y^!ghuL7Q@$%8}KmL<=UI+g8C#(^q zX2a0(yR275`xql=RT3(&%o%)BRq5mso#VH^Q$O@fcV?fV!?{}%h2z){GyI znwjO*KTA$0W|Gf_+n^S&M^;xevxT?MlpSIzNPs}w4Tyaxie{6PU(w5 zojMSCeHuSX=pG>m4y=ft#uW})sh>ZBddX{380RV-n!yp8M6=Q!$oBdDF!nS0JJn&U}gjT zyrLRwGaJCo>?zM|03&aAcxD5b*nQrajqCEpjoULbPq)G_-J9VnhM8fKOH>$}S>p_) zjVyqmPzA_>Lg1Y#ve^En#4;4>Kh~1U zM2xhB?ny1m^hfh@iFIM1Sl6iLP`;Q^wxLc^2zIa|kmf7`iYjfS$u#k{?v(F&5uRo& zhwbPllf=&%+l2sPTm}xEfI#Ng-A51K-MBB?tK|c`HGLZyYYHV&RHh%#%=^*8IzM1kZw0>EgQ2muh6*_h5X zZV3>!S!k0(gKVI5p9Oj5&7dZ0r! z^DL4fl8{E+BvH?x^p=wUtEr(yKkFb2^{tDq&=WB(`3c!&^t0_(Z#tcZ+5F_T6D@qR zSV-Q`Uf8thcLpwlHsj?!N%Qb9NnYyF(H~||WU!93a_b*r-!%Ag3uLcbhknF8S|Ri!2eb;YVu z5}*QZy!@m9jFgpP|BVJulXj`PQtn(y1QPxHv%*qWDJ&%uRj4fGk{Gn5b^ZUV(o#Z~ ztJaos$STF9W?y-6DW{KV)ukY6{9q4B3$9!WeT{-sUrBW-2ieP&mvTjt-QU76NODL{ zsMxa8MJtXoeTn6z{(0Hmy8i%M+zIYvW0c6n`3p08TkRM0wpuo1R3WbAHvsqs=Te%~1*L4a zt4dumJ?m24mUW86$uDuNl$UA7YQJ@wYCFu!PhfGSlyU6LP52Ua6BtQ6=Ac&ORve8^3HpAySvq!(;jYapwo71>f4)ZALISbG?cd6cs%uK2hbOnr*!`S zFPDVwrZoq&l7QZ{=8NdO?9zs5FDwx)N^WJC@`61M)ZM+*nM4O(-JM{2-Um%^g>k8< zvn4IGYG8rqo%MmYvse6{I-4Hep_s+ z`lC@EC2=sA)3xsd#Yr`NI%flYfRs2K;B9a!oPFPZ`}3UXB|2EA97%Ez=f~L**l({o z!|MwDhja<)7s%}L2Kkj|b$Pv8!rE__%IFekm&xXmPnBxDyZ7N*Adkye@#lZXeK|TU7{O{u;q>EwrH9*|55Km4{ z2;#}f$>zl&0=I8`tx|d858kL$EU+v)B4Al|^sESs2l`Er=3DrujFbTTI>0{tMF;G+ zPEJl5SvCM@{ryQ1{KZE9Edc+usssG%!6+h}gHd#4oZkf4Uw6TND;PxvXZfBLalQyi zT<)4dH~;Bt4)$-QAk2A+w6z-UL39qdFXJ9lJXA^nKeUt-V^?8jX&XR%&~VN76_;UR^sg+QY^kdS*&9y*txA z+ub9rjInXTVg4jwZo+*T%oReou^j^jY%nBoIKN;>fRJ!sfdm47@ArGJs=BLtdL*s9 z4$1ei_HSm?U3b0r>b+O*7@3&ryW4!~@l~inRGX(s#Yz+nx zff$FqeSLjRio#RJH7H6SjHUNH7)uL`*0ME4QJNZHxD5@;o0G4*yz}R2wWG(;E_vXw zFW+fWW&NM7+Vh5AUr(NWT<OeEn4wZEHO1th7kuYCi(h}><}0^uJAQd#*PC{H`+42x-|~yU(ysg7Bagk~m!Ey+YwrBO zz_Sv;Y1-|MT$|w~gNVuV=q|^ld+R*>7(7 zO7i_L{L^dq^gZ#aO~vQ-y!IC#FTZB2>9K!(_uWfh`1v=C_78r%_bz4Adk#JFiFa+N z{P@F(fiKY2|nK&MncWj~sq{@im_rUA!d{Y5L-S zT=Q>l_{Oe}@BGa2{C{Qtf0a|iDGlefI`xBqlUak}q<_x)vL^mY4gd(ON5 zq3t(M`+noL%eHbf^HfTk`C0N)IEWMlzkk|1-M>ju?qWCxr&W`p01ZHF1A5p5o2qOE zyxRgFfR0V=iqZ@5VMB4_;Rk>GdF9#-7hl%2eE(;xi--U31#PkIJGSKSd)eGKzp!&4 z^2;CJc+WQzU;OCBy~&=h(K9pp-WPr4*8f=e?SWt4U)}wKV}pIU#;^YAv2WfGY53Rw zeCOxRKL7pEp?5c3^U|65gAdaq7rn9l9f_asdv?=j=f3>#@C)8`=*3OHKXKp4k3aO- z&p!V*ceZy#Uy^v!1^@hm*2nL9=|v|qZ`?4Oz3-cU_V<@Rd%ow@kC}Tu|FN&-&iP*X zK;e~degErvHvU`R9d~PQj6VLaFPm(A^N(KNGWv$wAG>f z?H^6t{pemGY1$hfxb1CI?^oYC`2M&4&A(hcw&CRC_7hv0Uj4uquKxGeeDpVevl!Dq z*#F39=C0lN&WFDGy-)n`_ir!Eyz~=03-9>y&wl@mSA5};2d>F~@-P29x8tGbS5Cah zdG3$Cd)rU$|F3V~_JZ%vE)D0d`ad6^+FktI&OLvZ&>IfyIr{5Yz9hEi zv3I`W>;L@evwqvR@!!6-?XiV_*>mHqOAYV2c5BbjD<8bA_~k2Z{r%YA?fd83mws!0 z{L0rJeysPx@BGE9|E&7FH+kB`JI{L7)jJ1_m`fp5HM*Dt?$3;kT-;IH1kc*}P3*fr1m_|G2y zY)5kBn?JvO*MI!_LzS2P=JuC7^B1vKpPjwuQEl|in|~d@{^T!?_mAB5`I$qRBgdTK z^Pac;AERIUQT}<~{ow^CUVp)h|LSv(PwZJ3c=v?e?dh|HHAiKe_vw``&)XeQ(|Kjln(Bm)`yD8()6Q=U=<< zykCCDdDX6Omy5tuK70A$NALXXi@tf}v3<|G;85r-Ls-?R({o-x6YOhr-7Rul!wY{uunr?<~3ZB3it1G$!M@N0weL|fw-b4rJ@ z9X__qDIJadlggaZ2eN?zME8k~n)YE_8~C_G+HZ#T8{PJs0__jhv`+)$hqj>)I1k+R z8~o#Ma@%kAx8E{YDBc>;aw|`w0Xqn390%Vl8b|phw0$Gk{u#=2KO_T&&4FcY02Xs; zX$dU`#HsgTzH9LPVXScSE?_uM-qnmdzwsa|aGRni7b_><)Sz@JWi1ye+l5_Ax{)forlXGk3%R>#KF(Fs^Aesm(Rz#_sXwkQe?1@I3*Zxe3b$%Yo?Wb0Pt z31yRVvh|6RZOTQ;$+m{=%E`9IjmpW^d;YCKIZrv+xABpi7Qg<52iujCoGXRu26Z6X zG+eG<&cLj3kWcJPMQQBgzt2*XMwS15lcFet%5;CD0$|#pD0el&Xd9aprE!b$1PYU| zdS@s~BT5}pu^EKDK3=H-pt%t$DvDeII>ZWxcm=hlLIaLUt^^~4O08%aYETEe0<@8) z29BgzYJqgnsVK@0K9fC)qU_|q6Zm|(Ux>m0_?-=k(zX)>5*QjbyfaW_PlKYgUfF>A zg)_!d03*e#fhN?wCy-C=WZ9=jGu$uT1yr zigItG(#~qOwqJT_hxlDS_Ws#jh`digX?WL8nQBE@V1FMdiqSRR-Jo=|V#vao`LVMV zrElZUt2bYoc`l&8WtL3?u0voDKraNQ!Z#pVGcME?BU@#p6$it;gD7wE?Q{PzWlqTI&Y0rXtvbH4~qSpNHBMNwV=)KyWG zOB6-f!^>Z)D9RoD_hpKr{4M`Y6h-+spJRujDDQ=roK{`V%K(Yk!G8lOcsZO&ilSU0 zwGXrma1#D?DT?wtULHX1d;Is6ilRIUlTZ}pD%cA?&Z`wgxtq_uTTzrM{~eLmvqw>s zS>8@mQIrSyZ-BX9@VWOZigGXi4Wvx0+ipcshM-|QJrrdRU-KbFQT~yS8R&5bZ*y2t z6vD?a0%POP;S~BFhtF#iMR^u)2biya=k4r;wS$dS6y-WaQRa9%Pg4}7hqrURq9}jD ze}i;U;J-&+9NOe%fTI44w>hCG%Io>>Nkvf}=4F87f0X}*v!#{)21TxD zbHAb}Kj3wz6h(O%p9?5;Kf%k-z+M8Pz}hQ{0yql)<^Z1gIv-FJ<@10XTsiy}UM8g| z%3l6E4J-`r3zWkj;JErqqd!xyICFX?HlS2eLN^0XSLihYUI`7Uh!nXG7SeF4fOW*0_8t@ER~$dq?7Zh zbbLHBn@mh5W>RpfxA4kWNTX;@TU83RHjKwd=O^W2Pm_wBq2a&{&ipBc~0Cw-&6N~+yjF>R+{QP4GZY@D0iH=9b!Jq^oc^O~b&H5*rP z)7)$-J(-NBGUKCAVLM;)ZqZ;&s<|mwgc!|b+Sfk-9i8D;mRF^wiEE@L+O+&4H6SW6 zG_b{O0G21Te|e404F$C!XsLZ}esn4^7F;7Z3kCcJN8j$H(v_ELw^XGyVJzr|?kqzW z+eT-{k7QDb8{?U&#B>7Jw^eM{h*T?ln1j&9Mh4x`pIg-jwtR*j)YoAv@EL5K8IMZGHl39r0yX$!Q^P`KJ6)ox6 zsMz@9r6>T3TlpMcWl`!9?Db;K?aW<0cEh*djYiu4V!HPr3M z0F-9l>=9}>)-r}r?y-w*dD7V~)&wZ=(f`hFm5y^T&UoUr*bev7^RsU#+zO_>GAyre-ohss$t6$q`US&mAoMv2;X1k#vPXuh2gK|8QA=8IFw>f}MxViX7K zf;7fA9EJ{@kviO~Y84${hb<XMx=;>jod7uZXvs zb&DXI#hZ{BGBIEG;AS?6wHzid7{(>mt0H(!Kd-u0rc_K67)+IP0>>#(QD(;6&fU`j zI9jZ{GAQH8t4b#{)~OGqZu?kn%~0xATM(Q^e3VKAm$lL|fTrSUPm=b-k+oSqQ+5Jpw7I#S{L_dEKfymX_i=fpd2TX+ z{C`_k%PwcD`TQ~{21Clt2g`Y^T7z;aXT4guE1Jt`ZDhVNz*;pZf_aK_=IQ#&LYSup zWipi0xx8dE<_T@@SV-!qJ7|fy@n*9Ale{tPB>#^Fc9OfDy2m)dg+sf&td`ZO-9h%z zQpF}Y4gB%I=mSpn+%aO8D6Mpp1xg`Nw%twgx>lhUcql-|m~K4uooczv^5{W4yb{$c zgJ@Z^%91WtAU0&q!C`dS#Go{9lO^3LLV;bHP0H0=@k&ytLjFGRAPcYwQa~VgCf={p z!Zf(;`mlU08;EFAbN=)Ws>8j#s~~n_93u2_qD2S*w)CKeiZQ?h%4IC*h1JmYjsL^< z7|4MK_(%)R-cXMgG%HaF$2XvC1hUzd(b(AjWIUD5jK|V3WQu_*0x_2byR-P3@h^0E zV?w2<&OFI|4am~##%NB<6;W#5dU$*?GZq`$7tidQWf}#y&n{_@&EQ#$QYQ-n?rYnl zL>m;)*@Mu?X64eMX5jEvqf#LHL0vvRxAF!Y?)m&J4#j=fl8B+Z znCqv7O<}Y!)hK8ogAYLp57BG{^f7tlMynNKo24okJR^l7JS;U*N*3K5gq&Q-tU|zg z%=;?XSi3&i3Z1k>xOhw9f)2<~Hg&2MpO_=y+()q1w#-O6LRgImaq$7rX)mG~76zu; zeaCQRZwOAgW&Jp^mtl|lmLbGmKoERZL+JqYRe$dx#196AaIOa0cghvjZ5&E5I99`e z9S0B~-y_2#!zz9VzN{X12z=50(+EL6B7rpVy-oFKq5^j;_o8{iz0C?}X@L^7V_3nQ zmd`J{7~hV6Mqmknp@Cw}QQKM!7J$sngck@fb;RvosoU% z^jwN8S-Jx;2X%u?nmqwZg55I)5|rr@;V@XvIo&Auhyz;}gl#Uc)T~)J=3zy|Y}ErK zEXuGgpI?pwn5Y0hf#+dZixom}H-K@&y&$d&Y=f?L?;cQh?;dF96GY*jtAW_O7&R|x zjOYjlMU^;Kl_Fl|{u}nm13X05T^rYetc|M*AG!MLj+VhCvZ| zfa_FO_Vo`0u+`mQAI`3AV-O47kRO@aWdwCz&1qkA=7u0C;30=IH3*5K*9E;GDc)GN z8I^Rwy-lV}9Sw*=7bg^iIGRh#z}uNs7;Qc3Tg zL8ip0As!>c>TvJy01^;34;)iq6l`swM`}1Kxs8u%M@KZP8&OdE@+bNNxrua;$N~w@ zI_eHDpCvb61D?%-=^=#XiYDp2$v2Ig-Bd>aky33QE}s3%VIBs1%2kY8+ENb#Lx@zs zhm1*1j%o(Y-FmDlV`#Lj*$%}EB%letK}O6KC*kf)t_YFwv}{h*vbnWZ0WZU$q1|gM z9=<%%lDNnVX*A#_fm&tVhIrF9@V`O{2+6hFVXxk(Kh=b2WI@L>LVL<-3!D%#*~D{V zq%Kp}q+BA&kPUqxKsJxf&Et)HQ?6(Mf9C1B`iG~n#CRgLA9{ikz~Z?j2K)N)?!9R` zJ}nA(Y-58Wn7MXLx`Modd&aauvJrg5P8q?mJ@3Mrdq!9M1DmzlYs7Os?gPC(9Q{}{ zSG2@{)_;_23}f2 zAtV0T=7O`NSyZC^5)Zb*Mb)*mTyd63MYHWC)5>4xKLLuCkMf7F7eal2_;n5K84=7A zBbYUm-Q7mj+hlC2=1?F6Kusl(*8%=b$EQ^SAjJO$FjEOY-IL(UdQNRK1dt%&e57$L z3|50O%o(VTXw|l&82K@36m;W6wCI#evKq*RcAwr?fchJ1uhd;o2iu5RP`^anU*&Ye zSVr~@en?Ez27Iu7m~RPVpRN}$51k39pVk~pKOusiESR|}Fia3?q^wzb3B!BXI+*Bm z6I!AL&0zXko93z(_4us-K42d`x)GId0B%vMhPdUGuVdNt5r6`BC-^3~LRc^Wq3UU&1UaHTLXNhM_oB7!y$#B=KEo<^8LL^e-sK!Yv!hH%p=#rOrGTue4Xl%K;8Hq9N1Wny!4!jMpY@1f%A(tQU% zoHn7xBw!`B0k5b1V*T$3Q<1@Uh|2v`#CmVtq!US=$oet2Eevy)xz=6k*u`9LETBp| zm?H25D3bxFlxaY83zltz?Q}O`wy!$m&yrCVRRPqh(VCfg8q}BN=^`zel`=J)c0Fer z?6Mqek?Mg`TOxGG_2IM>t z-LF#x)ca|S*O-sSgsKRnvXb#Zjf%i4hGYX{;*ri?wU-G=A(RI&M&-F8#eJnPJ`nh+ zx-2fXN{ECtst*o;{M9|m*^$nhh!AeH0onwHF!(0OCxFrrKR}ZR2YT}K16h#%AW7TQ zQjag)tn^FrSPKte4&-Fp*zC;sd?KBRjgKeeDQ0t>GmU&z_g!8#gGLi7T;^7onwmY7 z8QT{h+n<@3O=jlj#$)NYuK{3y1s!!8IN#sEM0`A+jHMH^$XJN3!SSo6=7s*6$@pX< zm9DKT8%2XROUG7G&%0k0{m6F7EtZ)(X6Lg|#T_JEL5;>z@eEu%#b=n!H(#HGG)T{Q zwv7BP=#*d$qtMzpVgPPybFuV3*hA42*x-u!HuW)^cz30zV~1hrWAn*md?uYq#t+QL zQ)%By%O=vT{k`m;Zz9kLuZafCa_`+z@6AJfL*Ucau~`gyZZ^pb$wRqMh zg-7GW*lB@mnV8{G_>#J^Zle;Bshv31Hp2xdTkJRz`PQ6iEK00g3`L1?ow_Ymg2Cz9eRp02= zPuVtwQMQi=>6!N>kv8c2FsE&_v=D*d?=(+Lqof;@r7+^Ps1=}L(k)qVG7?c+-Hbon zxLm7=ryc5$Kv6hn<3vXcSN5IWt2>971ekm(fN?@Q<1v}YN*+h0?SwvUX z4aX!JLqE<_pv{Oy?P?j>jcTQ2YI)nug6Z9IFC#*6iMT$>j`a5SdFvn{EJT%$lr=*G zPeTLPpb&u-y$w_b8g;}~4ii57U2z9Ok$9wkK*bMX8d4)Ka={S4u!#^Z#sgTDtO3Ze z8A5jO%-clZ0x)zIt(3GJ=qEY>A3E8_boO_$5U9j}XsC9D(tIRq!nW>O(n}?ft%I384rDLWmT8{s%2pkc(+pzK zd{qnpe6w_8nPhc`_at47!@r3Cx<0p15X^^kQGY95%e(KucPCS@v>eS;sHK~EmGlkT zuvkv9TFx4pUZSYZ0qU_t^I#4RiU-}O!g*rob`ch@TEX?|>$Qot;7|)ggxkfc1NIxl z)(eJKs=3LBK`?l#xx>GS0RZHDXa6LCfPY#p=UOSiKWnL+SIJepI5$8$^Oh#IypNtnHe8-ors`T zxKD9oJ_`oJ;-HYbsr4al%xA$JLKN;u#^qU6);T#=Twy_IUVq~~DWHoHPkpjTPTaW1$)fU41Jb!cGE2-I&& z#>W$>U}f)oY(m92SY3MB4sQ;ArnM!X%>ZWskO4K$aCwa=H#2q*SFtwrj6QL|*qER9 z{me?dZxi0W*7Cj=d*ojXUF{o+Xr+p7;DuRZJ`4JZ+M3eA!S_mhOQ*M(3*4vGHgTcX zIopLjJ^fPG8}Q-!kNquS$NpdNW1o*%9QxCsmImwcV>&lk%T?j1)zIi%FV<|)o*G!#Ht4_20t@xJp@*#`K$(Kqx1!Y@>=n1nX zkBq9Mb5+VKYRSY)C7oi^PX}=nUAXj3C7o=EgJh7EtdZjS2K-W7EfjF-T|?z?=T?xc zhSrCOa6SuKgQxS`>Rx9yDiB0l9AM=5WM6J>q*%6(#he4j55@}&1!3YtJId2i6;i^izU!X{751_3dGx zo)e(%E`@-0VFSHX)E$~NH7k!Q8v7V^z$m&vikgv+z~ULCq#vUY2cTfpN+w>*D+wo{ z))WDIyc%^t+$$jclv(J&F*DHH2k!SS%!Z&qgb2vk>p=`+KrMrbM@|qnuL&6Vhk$J0b!sqmP>EJEaj!Ht#i0{7;Li{wlNv527|bcekG8^{(u$a@Wq6B zKLGPr$Vknc-i`4C7oyx~U9qkSy=GjN$qP)w?MYh}bc-&SCqM`aDaFIrh*EG{B}!c& z0;4T0E%o@^PEe&gkx0fTW)DN^Js7ZPK|ah@3mD;--yW%_#{;8Ukl!BS1wJMR4QyMY z*$9r59d~1)$6aTbX(C@4>8KNvSU*Ca8HUiixE#hil+hU_C*Z?5M#~pw!akH)?>I$D zrp;URQb{8dX4S}}iY~y+@gBg4N)odqQ}M7HG~f9r76oyD%MhzC9+nQ;bc*T19S2^| z>CSS-tZ?2DuY70@SW~rX;BAmW^JIs~6Z~Pd(8Cfyd#@C#CW66g{7)q4+*=opv5Oi4 zA^bNaqt5`H8Gwjx5a7!_)L}9My?uS~v=5##kYrQ$;?VUKR*6CfmpbD>O`a@V$S8>v zgx8@}aAb38Od_$B*gU{su?Rsql<$|oSc)jkZ=Am+7xj6W+3{j3mSmMqM^ z4j?idRT(!7pZLB8O?y3r!~%e({r&wabOWen{x@$3!%DUbh?U={D@n?%v>YI=T(xPn z>|UB}WPkfP;B8$Di|TN|;l6^>jhLeu1!_0|h|vYpwsp)#xRL!_Kc<`=hAD=NDbSBV z6k*$1)CQCZrd_L%D+x;wTZ0VHVZ_dr1Nb0B zu+@USUYje@+%d3)u#wf~xZ`>pT-RQ;N|B1@6jc({jXXW!KWMRkANqJQNbL@UBLLfd zl|w}s5_FD?pw>k2!~pi`r($^lwmFC(XuqzmrI+8$(C1in&O|aX8B51A$#iPYckR%! zVCeJ}|@+Ms5Y2jDUfIS=BdxX0nlZ>L^w|mgQ86^17P5d4K zzTE;I9}4%^z^)$U7U=Np7Hfuyb@CF&cnSD+OKdzuv&r;Wy)tJ&C~aPbalGO29(M}+ z#B51BMzbX?cg!wQ%@T{C+71|RF^a^NcKw)rP8eo?OK`ocb%ENNAzwX^nS)7wtDsrH z)j|N3QpC~A6fP9Z>`|Hn(;|oF;6}QK#7ZT=P)IufVy$jNWQGOPDpNQWb6Sac5`#Yi z0rQ=rX63;}p50O4X4JMLkbtH7WaBRv+<_ z46Q@#lLmTivV++p5o(y#LXqHH9TP2%FzN)d`iq*Sn^lN4!E{rI+X0{DmA4jaNX#d= z&SyKx4iT3e(qv2PVMDBGrc%ynrChbd=%|)87wg>)U!Zzsvqe>Y11I^(D<=L zIcE$lbPkW~=~MB;Wu?X4Exa8Z+^ymVA``aEstpbf!i&MEldGs4kHTJ8^ZwT%Vn!$- zg8OJ5GNBVG={^4|pjk$8q|N`eCbpwd+2ImUkrJSwJJhp>MOC!$AZ5Yc`~{>q2Hr*O zgY^V`5#Sc`ECU4=`IcE!N6fbj9_8^+5npJuaR3+{1AnP~J^j0T1|tw!5(0k1T5P~K z>nC^ThLO7k&d6Zfg4m@bJN8MPIpu)iC0fvezRnFJ72r5;V_TXOINE_N*(7FM(!5w|9AiW%oNa8)%mS>8 zT&@}TPJb!56$qAsb!WnXh$1q&LKKm3i?k6`6owsHXr0J~n_2Hw@`?v)&>i)CYBF zeBNj#EHW2J3hq2eF5zm#aH~RV-#Cc*m|DkYQm#&JW5Lvxv}M_B8f+`jDQ)wi_-HDT zjt2{S1glXZSQsD&MoTq4xuhF~28HQpe|JSfjJAuvXUKO)krZmb{!$-Pseb!I9&JQD9_X^U5jSJE>C29J` zXHq_w8XrS|{JEBgTsAofsfv0(sIj1z910GHwPdbXMKb4~sKi06auL8C4Y*>%B}JZ! zLomPocwu}!kx2PD49sv-yXIKZhr&BxX80Hx`N08oZ~(dVydiV`>^U&^((@emHqV3kPHQz^0wt{%!@@eF|$AsGP8U&));fwIPcIy^W4eKJWR zgyKeE@Q6Qq2U+?Wn1qa1dtnq_tK+~U%qG)&hk1Dz0;c`BsWTGEYjzP$9y+^8C-|jy zGEa~ip2{Ux9#+=SBjMS{UbshyKlu&lg!Z3Cz+|)w!5(SXZ7bY8$OfcdqP+_zC%^+^ zKAA`#;VJrrNj5azQn0iI&CvEp9Gh49+}g?_r<|zhQMU#+SoL;~J!P%CB+eyZUU&g; zv;`5Rj>B!Zh;q3{ig*{|86 zQQ>3+?aHy|&f~5IkXclC9SDCaR&{ndmcURkP*o0GvSC3hv4ob}ppW_F6vVU<_n1%` z0=WxzsZ}0S&`r%vk0}S@^69I?UDVuqa4fO1me4^aZuAd}c7)BCXxLcGMub_!zW~roQO}I>2q@=7_$Iuy+=7E9<2WX+F z&UuB+ASo0LpYkOWP}tf;8$dlkPg{o=y67k^Up+Dy@PrZrEb7{D+O)fD6YCN6(9!Kbl^xD=Pg~uC zr{2|exM1lgPxo=-PWTQH0U;Dutq9l=X21mZJeqCmwxby+ZEuAq5%AK}h(FoWZuF4Yfl@ybJx5B@8 zxr{-VdX}|viB)jrJCW9vrwYcd;a{#oA_iHxsqoj>3)v}hx)M2G02_ML&QXKftY9!r z08bQf)6L{Mw|%L+JHW7V9(^}H3 zJk7%|iGIUB$ErdMqfEszPv~AgTlg0dk+Gs8HkP-ZD(TRTT&bFuiGpwKY+O#M9D5Kb zLk;RH;=;j`h?stUJz51vK!AqgLGXf2^}_VH0Ka+?Sn$luF> zecIgM1yF0#_-F>Q-3KZ_9(0%87kq~(FSr{RJnz7lz!M@ZK-m_EO~C^o@^UM-EAr(^ z?aImuIrQc^)3LFAiJ5q2DjrMDBxWXY5>iE*J#^7^#LFebop5;lis_ zQwAYl&06tQs^tl=ULv@f^lV!gvU8#imQ^6NwnQ;uyC>F)V(hU<6xj}{6HBvn>XqEi z8+6|YN21jw;Wl11pY`*aU_J>tzECsDrh1p9!tM$+;RR~wRXfT$kZ&B=lWZuvdY#t< z5X1=Ra0LLUeY+#f$ud_ot$c6KKu>R^Qecnl-k$!R-oW;chW63vu~>^*=@?z+U+OS~ ziaNGrTFju2y<9&pd~ul2ef|a(f{ZPK;DHl3TV@*#cAvtq3k;lneohDssGmgpt6-|k z0ME=i>Cq|`=^}(h=nBt2a)RtSYg%OXe{jBqVQZ;e9piDgs3K2Ol!%vA&`cb z5b2DAB+AR<^mB4AqyYon? zN`SulAIDOuL$k?o!AeP#R26IyuOJal@Cj__np^!hokEnpfNvK+js~m$RK&|CN zgTTS8lCD&0ypEQi?`}T3W|bHE2EjGJzSBhrC8h)!@jZ2~;be_WqXOD=IE~MX3VSWx zSSVFbp!|K2|K#(;8uR3dH3+b6&fFf;|o0X(5Tm@z@nhe)(fSTrw~z)1tSD6_7Y{M z9pqePZePc%)LO>W^B7k%c%HGn(414fUaNDk@vZeCu161oYl}z{AU<8gy~9I5eTG)h zx%jWWM6V2>IOatN2M-TtymI^%V#rNT;cEMU2hrw^x@gm+_)Ka<4Czg=`SdK$g8fRf*iX5eWlzcsRae(g-lsp9|TRvWi z_Z3rEKW1JUhM6zrn8`a7h#8wpkW{q-AnRGYFKCMZ@u8mFy0`f&S#HXf? zOeAJ9a}X%gF4BAoGhokK=-t+Ol$J`%3%Vg=mN0E!Avuz;d#jOfCDV7$Y~)S+sDtki zyb8BqyEqhJ_;8V4i9gnlQkR9@S%~;yqIrQ+6JZX@C{l5Aw!o1k>Id#XnGp6A-&2Ub z*L6i9DzA>dd^ZpoYLJPCoPZOJV!1xVZ$8=p?hOve&a(DDZMR#G``$`!$N zwM_f^QAx_mgS6?DUzWGmvGSc#|Qikf9pXK&S67%pdb?*vj$ zt%KBp#K3!pK^p_(tck$OhAL|k*%K(db^1u^z?3V1<4AE_gVIR1_{Jqw6QZ)040_#y8V4J%pP_LcU6Lxg0xJN3R6xaq38qPxVXRM3j0|33tZM5Ta z%wGYU4du@bln;swY~m6A7?i6}YpW}o;7Yk2Y+@bYLqA6|&M9v!jPf?t5i+E>k8DQx z*{^C9Emvf&CHQ2C>)3$MMoBvX5gVp7v@1_E5Jge8C{HwWC`v=8qBLBtD9ROzqHI-^h7F2>J+>-Jo3cYu+8P>^ zC)!9D5A_~y4TF;s)P6tUAXCd>WQv_WgFz5I)v!@5m)8QhPQkC z9Wxlji2-%-y%U(r>j^CNmGR*zWFhjf8OzliHP{u06hDq&dG{iE70AQFb(lJgGg;R3 zI;+M;fS&?Uu(iOsHr$J8bXl|%ED*B5F1hE1nesMc9m0yhQmT;lN)Uhk(p75CC#O`t z*HJdM(?jgBpE{esBwla^VM_wa6nMtR7U6s4%Dn!PLf!>vsrEq z%j#X@?owp?$fjkAx2qRV$C8QI_-G~>n;D;-_JcCC-I&kH=S?f98mLjm${}ZBV?GO3 z)#7%MoS2SH*vq$8%|Wv~dnm0P+8Yz9!C*~;lhL=ie{g6TDZzAndNz3^GnJT5 ztR0T0q;WO%^$$Z!+h^w(Rhf=YyNNm1G_Y`J6bi#NH8CpMBL%Hq7DUs2FG;K07mZDRG6Z)tb z>#rQ%pYB`EP2O-Y7au*?doX@{ZiWvVGNYU_{gZRh)U!5O(?-ej_s*lOsdy@tn4LM* z6G^tQVJ1RnrV``vu~;(W(LmU9iT24j)zrtZVB7p0Bw)kzkg3FtYe#q)r`EIq+YZL2 z_Q#L-c7Uw|n2-ouZ)EY7+fvhz%Wn9LqIyK zA=~I|g*JLZstB-SRb&&OrmTh#zXb;)Lk=q|Tbnfv4r9t)HVk1a#LCG`3!firGL?=c z)9ERu;lMe{_YAyfEB5XyCF@OisWU|>mOykY&JzBH40ORXz4a0&FDoanFaxSH1W>_Q zmQKdz@H}mEIV5=`JXN1t<49_1c5-GCfUs3CQl8qO0S&%EA+Q@+Ylw#n3K^CMrJJ~2 zxDPll#{PEI1|bf96I@?#eVD$WMcLE949y#qXDcW}=1C;NB>V`5%&kx>f)SSX_K`>g z(vMk@oMuz@FaicJ_TFAFYb|QvVn?lrO`QlEztAtC)oY1G;t9OUR1F=%?PYShtWtRP zAAT@O$Jvq$ty#L3&#HZBmeiQfJ}uN7=_0R&V20cu0Rr?TSJ!fb63yyHl-W{uaZ4q( zfFuG|Q`E?tMroNWQ=3F07Cm0oEld$eE)UR=E_n@aIGxQZ9T#|WV`Fqptzc%1dVv>g zt>40GtFnBCr3tq9aUu z^|9^DtV}?VP+);wwaA+Rj`VZ;=AzTfsRL6H7V(OE#Y_IF;SmExy8#u|YHqFJ_wIGG zTzMFSvcIdb`9mW%jwVD$vyzyv0_t&nM^ITNR@3*I>B~Z2iKA0pRZMLMjR_>T!>0MA zYh5J7LhdS`KGer{L)I%mdDK<;o_2_yjHg=oe8g(GKd@M>x(#lTq>#lSLz9IWm4J+c zO%dW3nK0cqgHA#Mte;By+*(1222ADHa)U{sCsnvsdr`@HkiBN&fl2}*1Rs_s5~@TJ z7x`ORNdxcmx_~u5yX=4G3bKB|TXlhw0g@Pj5Ys(*+VAHVQoxmqRnjSm(ZU(1<_aJ5 z3ce-h+M-Z9OlG|)K!T5wn}vFF@SdL%R>40{ZKIMQ0R)~!e(j*vW(c|#EFiU-R;FX+ zT2(yK>L5wr2*d>l!2o1{nI$Q-=CS&G#TXOMY+Z-<$Pd#~DV3P)0+_IJf-?_3Pftk< zcJL*LE5T=sDlV9&+@ZH~SxtZ=4iCf^6-s-uO*jgFtr2q6+DTf<-c5s?B%>K~6= zb|oy`oUDn7{evI3#6=OIhgl!M#>(*1Cc2pcYcv*k>3P!WCAM*BPeLQ|_qDrmNeYuev4^M(tw^gu~sCmP7G25%G zcU_y=tB5&!KMN`pnx8N{WXBa@Hm}}LBy)0Ufa}*Mb%yDa7P&>a*R9YfxrUFk6>5+a zi$t5MP?{%cYCAn-#v~IZ%`SGsuN$;Q4SyV>S=$6x0Q_sTS}NH^-3bPp_qwaEXaa?R z$XMu=_mSH*Eg({+pj(&MAifR}lLUaMBnUrPLjrI3w-z(k0cH+_w(VfoGIM&DH~`6r zBRlHih~FIwfEcbAHZt2?_tJpnD_k2QhzO=XytSUq*G9CM6&J3Txu%A~nUcr#idG76z`?1qac&aK@t}*wd{(~m z+dNJ4y2cXr;9Y4jm_cJc`*i7gw=WW*M$XLZMqzK)e0pNI3vSqQ5e&oT(7jz_;sKF`tz!&uc~#9wwnNpOtSqYiiYoNC?!}B!z8-@wJc5 z&P*gGGvf)oa_4tD-kIBsR(3#$R*z_~MQj6_TCbFT&5K3v1(9_r9Cu+6{$ra_(Z>AU zJrA;tC;$gsI>%PJJ#ECWFSR#T=Xg4LF@Cjg4+og}P|`RkT#U=wiM(kUx>2AO!xqeZ z8u&wE8~FOJpK4wnMl~Zm$`|4*AAz8qGL!=aEt`32d7-|DV{!`0va+NPBK0hri#o+4 zS+kaVNWy`tvJYvN5)A_2m$V$_Lr3zPG7ZhLx=B(4>eEeT7pY}aYIT!&qk^t(7Pfu^ z_^rSTY}sahayCgH93RE^>FM!NY_Lon4KTUeD>R!JAHzbkv8q!f{XM-{QmZ;eYB+k1 z=Z`V+UX?k^Tq)Q>Lld zS`DWsRs{)(1z*Q2I< z)gh#VR-iVsJmEyNO?iTx-yrYk9|=RA2*LoAf3r+UktbPO6oUp#i|n}yfpLAAmZi`_ z8}LuRlaO&Xi}(ZIA?$g$xx{7Ov1L%xhGtn;Qi?U!+4>KgE5Z(&7jqUJCn8Rn7(Z85 z$P<3dFqcXYteKxT82ujTv|&16pM=>4DkaT#KNyD}4o) zXqF{Od@g4n8}al}S8N(_hc8KeZSKSMV`5hrCf>r6{kZK5Y>czfkZG}) zWcollP0G}^wE`ts%REMd@eK<3sN^=j6eZnus6j1UDE>?BP)rDl?Ioif88H$>{`oo1pTn$rv`|h`eqSXF(Im`I(t`GL?zX z#73v$c%K9nWaXddoOTA(z@$I%%-HPg{sj7wKzZ4{bQ=Z%j~Zs4Mth85bo1N>HG#{> zOw3MkcOs}Kvl<>*gOK80eLq?AivvC9GJ*u(F3IwE3eXzXYO&7amZ}jqMUcB5+B2dK4(%CsPwJ49culY>#X2HbBLJE;?gnpE3dDdFNPddCdN=N`jISWCm zz~~2c^=^5HZ7cdb%^G{R57}Xqqz_C^&pAuS^Zh4sy)*fv!ZO$@|Q zED6`;yY0yueQA!hx_r0BY4l+8P3Gvpp+VcWD@%E2W`1H|S-&-P@Ti{5&J~VNPmCO3 zWG`S!4&cy^cFs8nPa`F?NrdueDxlMn8{1Z4s*<_Y|Hp)=1?q=&Wy)L=h3B%7nK9; zY-gZ-J;oe@WU_cS!;-WJ$H0&tSh#UkOz``#AG~OnH2uc@L8(pQ<;M%k5ZLA$!)Dev zi-#*UEyqy6QJl#OrlvCB!3U?dR9sG#g~iYCitmpf36%CNA`W{CD=vqAXY!fM1xT>K z*91041Sa4Wc?9rDuHemMn>-Ic zn907o4G04~i9pEG$}r|%+JHAYUk=b8izCq+%%yBFYgo=xK5WABA24i{5xbs8dPq@k z)NO3k{>9U!3GNj+oVAJ$spds)TFlkW(IWzWS5-rImIc08v_v(VMUXio8J~*BQgP;u zEK&vXQBkR@>(k{4=iuCUfBaTQpPn_7S#9^w@z`|n(2WD5qy3W$+PE=2Hd`7xe1bZ~ z-Lc6VXAdp*CXP)mj}Mh{+UNu#XevIIjHiJJN(d@%u{Zgx2=b}g>i7+pDP zaC!gKTz+XuOZRL22kjeEse$sy^1;cap^;hW_AIxROl&TZNzd+&V_HK#O|S?OAhVst z#LJsyO*e$g`ZljVQs#PN$GXtrR)Tzp zZ0-+S_4&aSovvYC81DZ`Iba`L$qVzzbfLJ?xo-NMu>ozEEA~%MZK}iI!@quC^R-NM zU0tj^RY^<;Psu5ybkJR(tQVxz5}jGUM&rsbjmC%DnHx9jSLjh~vs85;H6C0;g6T|2 z;B`q7ghFr~s8;NH~W@2I1P)GCXzV15}6Oq;ASgw8W znT_zZLglFU2F@lm@b3RJrXp*0@y1nIlY{sANelK7qMHgx=<9WJ#kq2m8qlx{4|kOF z>j0lG*2F2V!8&Npn%7`8-3B&PqdyBA-OP@reKqI!aN>nM;2F#o4=yK$dx!h|DO5uC zGgNz`640(-n{IxdT0dwWnJ<8kK983*V!rFn?ZbK|mow*I_QKO)@UgKXf{Rnd^#p&6 zUd{c3zOqc)pb}RT;eRWs7y_-qE3Qrmyy2PymH2xS3WC7;u(5G%fUI97(kt}6mb2>y z#C)G}H&mim35xuDcAluNk`VSyHpUJ}>i!YRQl=0eg?o_5s>I}W=5iiP=Oou>+XEsm zAEf-Up6pnEI35tJ2^;;RO{D@`!N%fE(LctOCMTv9NDy@@6Y{3AS_n7a%7siKAZ@Cc z?i12PuSJ z7Iyi<1W4JM&QqhDKNCNk9-Eq-AJ1S^YZnD2K}sIFSM%&(&49{D)+)C1Bq)hFOnP#G zS2dlSPo?AI8J4{s@9w#ZNw(qhYcahA3uuRBt(d<`xZ=~N6vb?5{CYCG%4P6-u~ShphdO9A;{hDV%0snbl4MemDmdA^c1Wz7)EU!0SJ%L+VG+ z{NJPwVRXKZD#V3;?vW8;`CAY~YPA5v>M-*|2W?u24&X__TdhafsY3Zm($s42>WN#v zb9RkNDndNwf^jqq#bkjZSi%$4!b=616fbjCV(w;U;9VmOFoRRbtsxFvWW;a#LlR$L z={(jGS^B#h>se>X!3G)>kB6w*7J8Av1l?O7iH8u54{uxP%BP`@&(fvRL22g9d^XCy zBva{)?8*8M%d5i<%d3RBBK9gy${8~Pv5dWg(J5*UajKR928zqL$q=iR=!RnwE#&00 zD_XAR(8E3gCmt*(H03j)=95Q^uZicyUiud(lyvm<_WBiwb%0ggS!}3|eKOemPM)Tv zVIn3j2*P7^jqoUZzXKPkTfqJ{W6^vD*3*9nd6*me!*__)YJy)a=crJ)$8&e6%C0nVJ!!is; zh2ZtT3IT~A!vS29Hz*sJtC;KwwgDLp5K%AoAU3NTOFGR{tEf{8M+#GKhJmb~d`7|` z_0b%MGy`8aN*%=`GVcla4mk(vNbq5?j*MHg1?sRP z41ca4tH70`_4g-Fo;;6dMQl?P@+pB;*to!}8T^aII;yg8Mk4(&4{{{_#B%{qM>oJ_ zngzZn;OU?n#JDw!a&`@pVEX~~gej<>;+(jA5m%0S#0senEo5&shV~4P_z@Qe?>nsO z?_3x%c0rRWe$Zleaj@6Lv(tuE;=zro5v`|DNonXF<8vrVa?3>YnE;xh+f3SL=H z6WXAFQ984J1M#V(XQS>|+>n*ZS zEtMqE!yE4U5j`4)=pW>W?#@%-x+YGzC7R_~A#8KOS<);@@^nErXr5SFUN?zFb5vif zaoLm%Tx;hiorBoON@_L%I6iE1$LmA5wJvEG`VIH)fnX?m23GQ{3IjcUDag;lU zpGjxNXAjLlrYm-RQqe3YpY{2`uq^?AZ;8#h=*LUSt~E{Z`6LwE0@nJOvH4^&J~PG) zk6E)R$?&(^K3>utsz#&HD~?giprxLQWgewDCzIE$D-eD2S7q3*aB|XQkkzE?OnLW+m*)53b z+5U={{59)VY0PK+0?BLY-UKx$W1)VgJ0$eaI+khZIXem~6v=1%Bx1_fV*Qk~FN~57 z@=VceO9UC^+sAk&kCeL3r#wUq@Jmq9O9yQl)NpKIv+}?)Q3xrCQzALt%2o9m2}7c{ zyi@VbGeAi+cl97M?1tmoT#@FEc@hTrtFwC-LCmg`?>}fDQW#r`dK9|e79o8M3N?IH z>nNawoej%sd@8ho6Jt)ARDOc+r+zZvx66=_UL{mefALsgwMu-L&)fA;IF-cbll+Ab z!Bqh~VvKcZuj-l?$!IU0)(CYRp_woj#CzUZgvclyC@SN;TRxu+lwt^u?`y6UMnEr! z2Q5g6H$8<|g8V9j9z5{wt$W}#`r<%0X{8bn-3untQxF0zlQ--P3wEFzg=sU-6o?hQ zXqKp)aMZ`>UMgxsOan|=l+h}BMqm#c95&%mW>4k43B_vm-X)aW`vQn%lX3wI6w)@> zs3>iN9`Bhq?AljnG%HF6yb?N}SFAu{!y|i1T5>il{^g^tIG^=X*Zwf-`Y9epk~J=< z>kM_4OzT)=NzYRf%ahTPnS)qefxF&{9)VrzF{mREL2eQc#X}<`UTc^b%L{Dpuk*N2 z>Asg;*~r=(HoQ6%FKoqX(PIZ0pGlE9FvD_jt8;Kb9UO={xr&-MY>^i!BA!?CzUMp0 zI9<@HC5LOLOpA2`GGga&@32t$h<5ztZS1>;Or)pCl3ps20)+R#OL$_#{k^Rhfcei+ zi)+(p`wqs^Mo8!G-2>|G-8fXQ`FR6WQPgmVQ=}wIArgbWz!hn8Y;%Z)+B%Dj?;D#V zDZ~{%{~Zz1IkZO|+5;1<*J?>Oq^3dd2a*cY)djnlNzd`?QK^T%{sFbGf57V_e7+DQvRDH#Eka6ACvL$L0!>N!Q;3%<|jT}?1 zx+gs}54ItoVWlK#g0^)w#mk$P_hcqL7g!s^sbrwV46gp7R#Hh)aXOQFnelk|iAl zcuw74H#%K|QjsK~>4J$1=QzwRH^m1d=L71Cb@4WNF z^lC6lSVJ7{`VXH0IDCE=(1yN(hq8h`#L+WXq9+9RPHh6_N@h2Y0FTRBG#!)Q9{{+S zhE`&_Odh&e=YVEA7S+l~Kyah%vSv9XebDaauK*dd<_TWBY-YjaDNpg5EO^kdU6&3y zrahI;tRAP|E=7h7q-Zb$;Ie3^;T#4{6s>cRi)j z1_ko^3Z1a0)4gzVf(5!tq>qHCgj))hwxAhWxI*~c+R8#DEGo(h;fpo%%mEiU{jk9)?oE%~f#iqPoV#(a`p^R|9;Q-I*^;2{TFS{X%#Z=lF1G_)Ah zf%sVvZ?H)TPgpI927IjITkY%b!PtvpHzq^L`c6-M!*(cYEWk$P)oh7UxKfHhh+Y7- z7XAief7G52XG#s3wo|YuzXDvyKMAGJ?l7_B@m&l<*?=Q_nZy~vo)R~wGKL_7oD4MQ zm=;;mog&F+@wl)xyiVpTNAj9o%$k~&CyTmW)k-{waFE<1AlDab%nEQVoLYp+I84n` zE60eHM^ad0|FhEegpua{-oZf?K2TB-iv(1b5nRdV(8kO`D(T`QkYX0XKQGK7GF>=- z0Vckp5w9gXSEOaFr&6$wyR2)SmTHUIj#8~bo@rG8l{v5V!5t=~#j(%C;a zR7RnYLt&i>hG9206*!OwdV5FIfmISL*OG2AR{yn1Z8?~*T&6xK@d2Z^FoQ3_Z*jF$Je04e$RiiF3@U;@s9mlMQTcPKuZ5l#c zJLPVf%m@y{K)6C%t$}Yo7d&}9Am>kg;YEE2&OoguxM&B5^A0kJN{Eu!W$|2mlRV8< z3qB2PUZY@Q1CT8{!g^I^s54PAm!gTeiNs-EyiWUktQmlC70DUzSiErbGR+ut$u|P8 ztGQCOpc^QL>xK?QIXHs~E1?o3Q({`;n~ZbW6vl_aDoumk!iF|xA1`GO&r*S|P838pY#j#DK&%pBY~^E+&hY{97v&ELzYUsuD*lbmvN%ZFk#N zE>qTW-Fe%=@5Pd4;6u&EXBsPl`T!_{KcEt90Y1SWPOh?0(h7Dr29ND_OuHLkRje+| z*#Xr<2C}{2SvbN+1+Of+U{Sk>yC>!D#^uu?sQ0o;`h<(8^fY6cRU>aMEU2Vhl-k3t zRL}@HnZ=RJ73C|A1m9-Z5u-ATDSm*-$4)yT{u8SXYK$f0v2>iIA&e|ZOpuw`G>IQh zq|zy0|6~^*0XsblWEA2Yrm^_U{M3}Vu^_$OQbF9KjGl+^+{s;iLs!<8?;>4tjk1=@ z*j5g{A!>X``Ciriucl_+cGgi7V1}$d8O_F#t!d(#jbm9;S-pbo>X6k9R^PXrUA;Z{ zpKq#q1zSIb8UWH&?>(B_Jgm%A(3IyNj@7MHY1OR{8I3UQ?!$__0h>j+V_D28N(D2G>C zZ)}A3h*<5eWP8JM_r~|)09#mFkw zDQD!D<6wl+45QLG`R<}zCJP54`qfG$_1+_z-dX%GX^IX{qw*wsmqq4j*t~Db#qd*h4OY*-7OK!yB zjsgQz8HN8*Y6hTtzIR)PeomF^2XREu#waNA%c3X8(6~*QPDN)jAV=XYjh*m)!C_K zgV@EGo`=-BJbUd@hL0~TPF!0ymS(QbOf8=k{Bzb=S(u(!o;hnw&s?2Zo&h{vUopU4 zG_97v_6miIhE#TK9u&*j6U~@<=rm^Fye{=Lc`E=`WyHjb=fYDKhEX|rmNOrLMACJ( z1G^!oBW0QJmaEMIwbXnOuYpF~mb=>V_+fjtCAa7CryIH{y&8{PI?AZgcnY*@Jp3^8 zYmx6kwNF}xM=t0!jKP%hEtfv=U}Bfg8AUFZ?Ha~li!Zt9Sqwh#S*B}a9v~-0S|`aF z=T3bjQGZ5E50lxYAGTMW6tdQQw}rXInIsYK7Hu8PFu0XU>s=$sNM@d0=f`}aib7Ol zdQ4h-5Z-qwQW!Kn&yQ)fFB)uU5r#2{-5-MuWKuE#*4;;Ylm@^Pc7T5j27wEd{V>q z$JMvi!a=*`(8Md(y1_LE)PBP@!==~^<2m1p*9RN=R0o(2)|Y+Xjh!G9MTpk=aL{$U z4G_ggKb~N4FfcvarBGG5EK%&-?Fs>+(~D(Q9&ZxMQnX2u&!8`+w3~)V|wN`&5!AQya=>b)(qt6jX*{P1k9Imj-c0 zFoy&Vz;@kvvUC?$n-WPXBQRP|$)})ZhB4@PQD9qOdMPc_PeHE1ju`}w*Ul1(e02Pz z<%@h2Ce0@KPRjQ^p1~-#gDl-8Pf-_9Hra=R?a;QpLX1+nP1Eh7Kc(Zc^i$AqJiC_~ z+O6HoR36>Zk1h)2lZXWQWK-p;I_PiOVF>pk#LFpS$Rr+YhfZt3FQThKNRr`tn!OBT zaBHw()JJl`x6{IfKZDyj1ZOLd6+(Ch#fVuzKZ9U~%eK>A2hk2jogA_U*L**=A^t7t2n3S?SJsrV^9q~K#|LC1&Y*7dF(f@Xo^;uK4_Tma#d4rQW{W~_{{ zf=m=?n}h*(oyNxoPt&A?>hiH3gtj(In$Sd1T_dVSzM~9a!0mQusL1v!e~K~@iUKn< zJ9cb`MKqB-z48+bIcRg+O+n3*M-5rh;n9LDKhHt($;v+Y6mS@Sa)lJbE}$zsy_)(! zQ6*{T+sG1qR`BR_)r#V8qpAQ$;!&uQ_VRBdOQI(KC}gFD^#6ZQR?xOWSvc4Z{cbRc zvF%<*1sv=PW8~~54z69%lLaM1C#n@(#n9T26rvR{7=$*CB1$zdQ8)(qf@z|{ zjp)<#*vw7724lN}>)V0*bU*FUs6!{7X{}w;Zm1cn7}B}cqWL-y%e+IBBr-ncy>WSF zaYp3ewKpypXZS&?GseWVX&u_t*()=~8D*9|^D%FJae8Lan7pB-${?3!tCrhs*%K~Q zutN2H&^4PjDQL@N4@yuf=uGM-iT0IGlgvJJLx=!yOK6Bv1N;vSM`Fpp&^i~|Z5#8n zyuPU@mp4zN+%(rElSxfu8pL{Imh9jh14ViMR{7j|3dw`9FJh8{vfu&-2Ri^X8-rC$ zWN(K4R%C}7a|C0uzMf*wLl_w2ES#l@o zg7}-CBX+!&ecKKV?xr-vPPNB!M<^C;3N-w+H5+&+t z)rG4omu9c=d(Nq3nLRnYz!**Im1PY-+` zIXxg-+ZfS2LZ|SsfAN2=Nc{hd5dX4ZDgNgJ+Y=kwjZW8%oieSqTRx7IMUmO2Gh(5? zVT)4*6LZ%WNQTZ`UtX4CJ0s&{Az2ycdoURGJQm%Cd%Fhxrj9;Ka4E^N3oeU<&*-`H zb=+`XZ-8ioE>bpX6vTgVhK=i+W%za+hHuB=2KrV7k0FG9h$>V#0E`ltQHm*xy`lyC z?swz{v4K-II?GN}Uhf_0GMnT+ZGGGf+jiVA_{z6Tmj%z$_c~{)m76cA#|rpIWl^h% z@AI=s*sWOmYs+q~BIM&kq#<5>Fw~jiE0S!ddb$?V6~ev;dLZ@JpXZVVZzVR81F84m zqAw)n1+6;FKld*y&KJpwpA??EIB+Q`xE{sT#E!q|V8=L}H^TNip)ZohR-vas&zf{% zsmfuuO7>7GFJOpHxaLllzQ=y~1m<@3%U67ulxIyzqar;>pbz3Cs|B4OJ3rQ-pWa%~ zUXGLEoaXd{=opQdh(U}!xh&=*bV(fn{E?mAW4}^(rtaBWU2_|EUxs!I$Cmg@cl*IZ z_D|$v1w?-8&tZ^g;HEp*Nqj1W&L;d(tcAW8m*L$NE{GTHR?CTvto~?vSOu2IjTvDs&>Lm5nYYds+nu`dI)mR&DnctET<2uA<@SV z>`8!@*!#qJ(u_;%0#App3fjRt5szlzaEPpOVH4 zD9IRRo z6s2Vwt&tjznV4;Ov>^{2Wh=B>xVqJJBhWo^Hux(;hlr6lBr?k#)8yF-kWI;RN4Kzs zt$DuANW(E4?bFrK(;MyXTNfv%x38U_y7BhxThaD-v(ue-F3(8X`Tem&*% zjhBUEWEQGDOjF9!?C`rJaRE1K^{*{78 zky0?wFZ4?(UZg_shH`p5J2$alP&-q4fVfs&dHc%RRwTfTbB45HSvcmy`4ulu_;NBd zl_0GH$U=JhB9K^>dxUqmc3pENFg;9;#DY#e>T(IjQ~QupVnJaFtD=i?n)24xR$6LPalN!yohrF_qvScVl+3D& zKuVd>#D}Z3^U`}N6z?6I05+ghyEmc{`joP_UQab!MSPKF505OAg4}2gwJJiqX?|IH zp-3xIFQkX6FD7dGb{p=P)flVFQ)_sH!&Ta#v8qRez8~|eP2`}g$I?n7{~Zd}gAbtt z<%3(_$&^+}6^|zlw^wnlDKbuXLx+AUMumU@%tua}VLQ^x`D7vBzhrz2$xr9tubEM7 zwYl%ughWZqjaRFwhU1aRbFHCR^~hwjxz$*jTAW>2UcESbbq2z~INkECE|ytWfwI#w zF+b2th}9c}$KiWW(s1|E?C&r>HuTcXp#9Fxz;3_OcGlhry!Ja*6uomZddIQ+cW!sw zceebsHTtQ)oJWv-J8(ifGKQTlpn?)uRv#gcxh&0@y%^hr%HmZNEG}== zFKT{c{uJxT*Uze7l*P+5-4EU*%A?N_cE{>`1NmVNBM z6$)mFon@kiLQIs)k-2Fb!&ReZlwnDB?M>S?w#*QYedLWCeTc$=qmdmqVLQKGChZ>!!7V z?P5m9j!mSu)(slTvRbcZ^U5?wH+T_%v?6P_9I^c%H!2n*Rt!{~j_I`;#=Z0#<;)F+ z0z$?F)@!(uLraf=Jkxg3l~izMC?Bhiji)b2EYLP3(@NG)?f~6YbjS^45=4sc#S&&x zTk=#)!lD>SBBkJGe$$r-qYTiUWEzf1xPVV_I;HD@R!HEXd!s*yt|OvI62AXW&&?1A znSR;G!Ajf-AQA_rCZ;aWtWM98@n9h8_!~Bk?_n}5jTv<*j(3yPDmTgOqjM8W%QK5o zS|_ky)LOH37+>tv@pRFen<_$+a!IrIKgWNiNYGuBf=+g=6m?4oSisH+Co)Wwmaxe^NH*0vA>(oqJxRdTkB08tmn4;>2eRI23m-d( zr%(Mw(+ugGs^%o}2A}A)sBngimQsoxi99E;9rAm7a_+{`^{cD$+7uF_9W!*yRufBj zGK&#jd3);C$jn>*)W}LOUY#EauB|y^!>!9J&6#uawJiuP0ZZB$bXfZKGvi{s>DS-r zzUIEQ;BJjiU%EAMtJ->dZ0+`I=im0P&)x#8$9TOK=jWGG03Q;SJ1g3t-`b?N>nP7J z?e?-w`r67N+89rT@e5TZC#J5fEUa>OD11QSh^lFrNOC>Qa{+a$3oDD4Qdx92!%3C_ z+%7XcGr4j}2d}r`sJxKJr^})Ao3do{Ue{_f43DzsMj9JallAX(J5ynI*}rscbj-T7 z`I_H3e{*=lZmqpJ8+x~{1mm5}mHM1@0WBuDS7zu^EeTg26%W~7d1VPHkD|&~=cgvF zrrM_`KCrSpg`~%kRNi-^H&CafGkY9DRV~F7N7hnD){$3MN_8}l>FMYnW>%9 zOBg8N*z{zY!30^sXR)kXKiz!YE3u;nUltbN7_OML7bY*s7b!mxagffTj z!g`=qKTp2s=pMu))01lXMyAHdEo-zVmPgZ4{4Sb?o4zEVgTuqsbJU$kJinOgAN?A& zgI<^4v@?K&i!&Ez-_)t7XVRtlsVnJT*LzM@N%jOpf?P+Fae8(8+T^c$V0Lce(#-1B z*-5<)`YO^mR7IWxUFH?w?so=ZW26@9Jsp>4nWYG#q` zB!R%W=hcbnY0^Vw*1Cq!{w0W|^6WB-)$(LprT6_2n1|N=HnI#*F>>inmY{R=L0C}< zVU!dL(^z4G^Mj; zF{KX+imx80l9sP7t-e09ID7HN>cY&Pkx)cr`jBv7lA+y}ljiu$8xz*{}r7Jpx;uwqMyyKK}m7gsm8eLeKygECz zQ+|C4f$0gR5GU2j3`s#XlQ`09($qlS>z6pK7X+_TYFAa2)Er`>=NzJsfmQNWlBbg; zNTCNbYPF$}AzHzdV|=lJMcd3xmE*rj68rm~cU&l%cf5X#uPq9s$XN$2E|puk z%?P*ZBpYrsi-;m8F0r)yPVqdTa0#(^L!`tVsogm{i;U%%r8Meh9xev0)I8*Zs~+8(1VVmX_xiCoauA5E|(*%r$7yjhiG5qj)G2X~dZKz}hP3 z#}A}=Lh&YlLqZ{!^eHsthA{}GSqq&~GmeHKr4F9yNumBW3~0}SO0)?_BPBj4gH%om zq7n(an}8K16-?6`yH4HvS9dx0!ReQ76k=W^$vivRN%`4N=70@DO!ktp)ErO6 zX%oQ*Mk(_v%a`V7uU#^rJsL|hi?7ctJ`k!QZ4^Ja>;n3^>;X(Wpv+@fCLLdofO-wf zTV38$K*en&_$ z;ZDGCcXw(!h6@SRwC&6T>vhNnV_|WAnOg-!gZCw33v5y8At`HilCk$ws6O7)#Mihw zJGnTqC~qxz!B}2*BJC2$b_*9VLU(kmjr%ECO|u(Dn_nuCQ@f30zWB+cXAX$U0pa_IF)%KnaTjZHVZk3 zzxO`{`Ebz`-LZz z>Pr$lUwYvI4I|>nO)@A7D=bldkpXwpj^ofKezt6JJ8h0El7&TT6-GmBs>C|5AV?fU zaKmyC+E!?rvDBf;xB?Rk+H}^y=GSq)2twK&9Uv*{Afl(CJsqogu;&t=6V!%x0kz%3 zxkTJV$(>jn=AZi~x{nkQ-Io;6p&Ute+|L7FX;ar>(vd~yT+*|)n9nV5ExzdtNC1pQ#@D4LSL#g`Q78*Y5h zgjZl*+^{B!5Ag~xRyxY=lnln$33jJ+im}olW2MuKu@5rFPBK@l^it*s%630RrneFRhFi0C}2 zXgRb4KZ0X`ZOstX!d5CjfHAz7`mrI>sF*>Jg~!i<(Zz#9(%*e*6e0Aa7(^dBi@HvX z1zv1Ao-J)w9Lr#!#A20%WteKLit(p9R#VH|J%%5GKom-#d#o6fs&ez+U)O^3PRh>Y z^hF%lzDEx1$C1IV8%K)$j);Ktmb_gK691Iil2>76(-0RT8Ra!Y!mmVuZMQb;*jn!f z*m8zKhpcE*-<4On=e$fe`D&l?6;4PZhpzW}hfx_RmOW*V>mEMzptVoj{FY6^qi+ zb;1u(+FM=6;)$^&j>Io4a=>^4FeQGxMVUr&Mn)u94{=K+KUENfemgWfwAU<&vw7;sXxj0X zZF@$%Wq9*WoJ&HpB_X*1Sega+TS$?z^uwVjoFY=frEH81kBv0wM|U7puj6Dp{Z?WtXrS&1 z9jgNLWZjlXmXL6gu2!v|Z_qELE23UX+)HWL|6TAOdoRfgGB05m5_rdY3OwCE4|hBQ z0n|^&1X^CAS7-~w2bBCd_ym^%cq{P&#r_#CCI&|P7#kQ>0^&f+i}F#24>Cqk+%p9Z zZRb~(wdLX1w$|a{wc9}r#o+-o`e+ife=#vtq$NBq#6(hnloQLUGRE|^C1Z(3ZkjxM z=>_C&yzYk%?kn1~4Kp&fY}ds*w5@I!ArSe+Nr-sejA;vN)5a-NKF^M9ca3yoEMu8^ za?HX5xLUEcjTiZaRZhH}-L{%ko~92iFREx01VPL_orx6-ngnJ_(j*grB!SjhMJzu! zIy_cw&<{*X#tT3P3blMM@?G0_>GJaOg1lR;jP#AsVa~W-B+?7zNU^v`1YuE}sHf1v z5xJJQ`?nYW!@l`r{~;4VhABuC4DM-)(Z{ek38LV!O3UB!;N3Z@U<89#Po$>2~d0ly>R%7L(_{oPJ|nSN!QZ@_Admci*$uw6jKGg2q^|M$6Qn7 zgC;Y^>v@_uXPMe0Vz(Ugy*8EUHAoC*`fNf^k>chey+Kb*?3UvsNg9m;0#XcGpd(~d zfivUP@mhm^$b7c{LHChdC*>%HIOPA_<06DSc6S_BlS1s~@rCe3KaqiCv$E5VXy2KPNa z2}}{aiFxr3`0PQ*BRE_+d(e zMq;kh4QMSHUhq{iK2gdr9+pTX{OsztaNegYJTj>ug&w@M6W)js3auro$2I!9pb@EJ zA(c?0E}1YP#Gc6oC0$VTMk#ar&grEg=!nwn#Hr^Lo`pxny>$mzI7>#v#_#rIW8MD1 zjjxz=oS=OjvVRoW_d{4=TdFja_KOB3c8VjsrL_K$7%DdZfEz0CPE9zZ@v|!u;H?ro z#2a5G*HNAwZkl_5smcRA-@OKyq6JC2$8+9YznW%{2>X8ZylH5 z71s%(lKjKXf|-fR=;%moq``mXXoxWqA;T0t+{h4=f{!$#lx9?>Av)TXQD+`VU#0n$ zql@^qe@}e77q_(XT>3Bdbd6W)!7rLm+D)_T##?6CvOP?PHf^0*j%l&`=a<N$Bp0y^TS{-O~|YN*yk;)A`~&!ta?$10nSg{v7%m|A-6km*x~^xr$p z$0xkihyp|Upcr!c`gj7ok$~3Q4oBOO>$mw}^K~)Ul-L+6^N1ntYipv_3WvI_;6h`x zHe$%QZe%Z#SLE3Rio`{+`eOc=OnC1=*r}mJ?Uab<*m3Ask@}kuqm6YZWI~RyeXsJr zR>Z)+B^g-n70JTzm4yvCQCzmy)@%!m%(vp$X0%<7{W2Uq!XuR&C2*ePbpdLQw`)il zN6s37zh#GOT^G9>$)Q9>O_j9}D9Ks_lUqF?P2^g?%*63Vtd?8omuYswHRa5ytT}}d zO>HQE2^CZJ3=fZw)W;h1lN$Fh8rDX{5*!GX@+SKO_DQ59CYtYkr$G>;iIPOY|GGzW z2{cQnvQH*R6y37xe!2fcEbHT)k_Md9KU%0(*7m@1q5>M%JuFE^#_gZ+t`sre8(GE^ zRgk=A_TawAGF@_HwY)SviJ{T=Y{iZ(J8*rmnJ%}=B^x9Px<}@c7`jLPlG(Hk)A!6` z0!Fe|EyHDF;4=CFmtm}&dH~o1mFI|n2>pk9ddJh}Y=;>3nt;#;Dv{lW&XY{W9^P~! zJM_J>jFF2)lS%GaWa9{isZy#9?y^(jTwboA88a zxsEn7d`xE0BIN<4&)qhz;YvZi8+A(TTQOdDu*_ye{u=aH zd&qdh!OmCPjJxpW7U(F6j!$ltw2(E^!jVnOZ}YoasVwZ0 zIg5M#+RmmOVJoe;AaM&@&V6IUwgbcT411dvALt6ib-!bu<@&LWoi?VoZTRuJ9pZq5 zWfPDs(~FT`jI7ypae)!+zHyzk?IAPJ*B$s}I`=R)l|5Q4LVEYT7#-7_Vr6e(RYCKx zT&vbcI=#+ah&+lN8|sU|U{0!%NLFhk44F%mFjzzT&Lz{>s-gdpOSt=*-`LpL7_I~x zQ=;xq|5Kz>$Yv=GNF;x?=XAWpKCW4x@5P7DGfOw-F3w-Ov5IrotEWuC)T&;~r zD*;4dujxt=IV$ar^k_F)Hq46dsR!_o|1Jcm1NQV^dzCE z61;LvOwY|;D=6r^Cc07uJ)$znW6$Xt>Q}QoEiW-B^QDk7Tpz6pnlgd4bcbz^lTcJPcT19@=*%riDx3&tVSZ`((&EgLA|O4V?zt;X zLMoe1LVDI@!L^TlPz+Wl_@Sq6K>Oa*`qL2+fV-S9rtN;i%n5mU25k;Xk7u9T!Mzfe zrSs$2i)!zR8AYb&+^#4TRXV1l&mt1!O%#H9HW?aa6&tlt%^9|iqWzDH*NVo)-#Ezq z5vj_{f$`nv_JpASp#TB`S+Z65toeU#Tik7s(fPl;A3hE`0Zt zsfsVAGi{PxA3Z1M*gfz9UGO9KDYVjqEUAfq(sP=3fZ@_~ zx#=l?Jnit7-HiR9M@#|F4rexlFZDuE$pp9Q8aX_5y?SS}v z(fIX0%Xu9kO%7fYbfKDo?I|rt5`MT@eqjWA^WxNu(XykB*bj8lHZ~oT3|Hc+G7LA( z(BF#eoUKY5(_F{v-sbDg70!fZQ6bBW?Y19oCtlQOdT4gBlohnh*p@D7^aUn?JYpoV zva~CXDY0s+a6Tw%0U=$?zbmuau`!Gdpv>eT8A6B%FL0_PNI5C8yA^4;S{uXW#KaXk zAP-k-RkdZ2(-{?@F@CMx93%t#>+emUg5?zxvQzA@AMQ0R>}UGCVPMM-TR~_?5g4_= zGk9OCxodhU_E3qwR#TsQorwI7ohqZ3m+!t79Wj-HC|$9PYwt{@`uLRgkvHZSrxzAy zmX>fQoAh-nE>O|E#HL}Vv&lKG$fi#M(%GI!m8PTM9!r$7WRXpmjdCqv2WPAWlYqaq z!zAFRh2~yjqpVz()*YNA!?nZwdaH8acH543fqc%bdbA5=Rk~@=*YijB34!T%KPuOw z)uD4kBjq(Xz7>Sj!>>)E9w@@nJD+0Dp_#q^?yeH)nxg@SkQM!3QWM9<40i_k1Yt++?$%)WY)pV7~W(iSJoL={UM z81_(ndI|SJZ-XhRLOG3`uUUoKR6Mw?(IZYWaXOyUQ)&?FO@UNaW)e1r(?;6u8TW{Q zl$_yC|J0-^6<6G}RFdKr;z=dr?)4G`LMg4@E2VQSR1Pnsg?{a1uTg1$r7p8hd98ow zt;sj#o@Xi52h6tZ#b>wdrtT&EP_0@WB~SNy9_em8$)n#jY=jrb#+sv-&%6HHqvI>1 z)7`o?GPPz;%}3Xp8!q=-{>0qG;_SrqWZJ7RS|~kG_pbg)`0FH}kgE2iB-tUVuiO2U z*L*FDth|;5&gJAVdH8L!*>rCA7`(Mib6O_jn%UV1U)!qPeCv8^e)#g-HOp_d&rRIC z7+!X(Z@(#=%+oC)l48(S4V-?3^qw%-YuMLd;*Pk2c4fwXEkiO?o+R>t{p|g8%$8$C zyjF=;T0<>0wKz&4v=9%y0QsI$t_DTkj^FCK+WOH6o<{`3a*1q@WwY6ue+^D>W!tl) z5wO=lez?`d?kE_a0rgq(fC|5dqB5pyhcUl+F)+5xbI5f4*A*#MIM(!nf zJX>nVX(n|&CK2j1Be@UeVSdG8z1aFl@s8^UrA7r?=A#YOFE*?+uVVR&qknnOQ3v-`J23L6} zZlb%CyG>_Y7rVuawA#j)bPXhKaj^@)h}INuuX!n{Ng4{Q3A`i93KDkK?qBD;UZiur zeuUe!dErvwoJrhvPPt|j8S}VPV9D=>mTg?K<1IhjFy@jCpJ@X&ht+!ro1777w~jZA ziB`w)8loCNq1-?!JBG9#F~Lue)}#9 znJW=?Q+z0IMwf+yE-15U2VG_33^cE5gZ~nrOXpo8u10aS2FGfTS%W~iKA<(S!cvbE zt;5BWIJuD>#BzBvgIqSd7{hYeM0hqihOcbxY%jSNT~tbqSGmM$jLSks7K~GcIS^5l z!7>Yz^WiL)K@Lj!JTfMm1p`tpH3DJDw|iOe(>#@xoK!}EEclO~uM8tItH}!wErE3D zULEC9CCD^sXR1#_6yv8XyQ5w6V zL7rSOs1)lWSC&C}YU0mKO7JIUvz2=sMoC%VrO{0G@AO7H^iDoFiFq)xHsUQkDD zp-#Qrf1eS&oJ`>wRF-ZjZAL2?8-qJ)xw#f9M=u)}c<;&sL_hfQ;cPb(hhFH(p(S#d zE-ekm>ByqZcX+{$^2pWM*Jmc@=dYw}AN{VgNi3Ea9T>x$D_NWTe6Kl|JmmeiMS_&m z>f`z~!F+kL*jd$SxeY|tq};MM)gKyDsjBXutCotm>W2hZ$?_#PO>|?wW5)1C*|Lon ziEdy#+*ot{E#Zn*=d&5tx$T4rOJ!I{f*)$xx|emGhYDvYuXz4yH?l)anm*jndo{&# z5`2p3_JUfX>jGV+Y~%gwoS`RUdB z?Eo;5A{t_ztv#Jc;|#!EJi?~% z8c83--}@)$&aTsE-+h8KQ~Ygj<89;jf7PYGdQ)&NcY2 zJYcz#$bR#^LJUZcI+w`q%X=cb-F*WjGWjLp77*`*B+bD2fUtwTEE3?IBv2O#U@bdO zvri>Pj<6fWYms=7mdHc)Ppm6N#5yU6l>?XLdf92OmjgSb?TQ{wEAewu!ZVGSf##0> zPjt+;ea~1L?oF1WoMhXFbaqoHdUbj*mFTYX5df) z2+VP~iJ~7##66T4($t`Pyn4SV)4G^Q_5@?h2_oCVB*eDX+Q%7VCB|5d@ds}#@?ewD zF~-5urm1Oc$_^GR8hGVB?KL zI)HtIvC=_YLV;^3K6Sms4&z!1+)dFxz}UdSqvE!TV+cNuA5`LKiLrs>aH-pOfU*9A zufq4!ol{3dH={7#I@ANj-?@AD?&G3(ydNh%y3>#FL+lP+i1BEW%Xb}Xd$k?<-2l1! z7-P?f+<3!tFDj3!^yvj7u_%~8Jk1!pd-pDDip+X7HW_10HdlX^pKfIDM{ABSHgFfX zMl(?>_9Xsz8b7k0APDvpLty%Oq9ClGp+KXkWn^O0F#g6frM=;M7)^w}iT>$3#Mr>6 z&^cqMBas4`>idsy3B;&c&_rb~3^2wp8kn)QUKgMlXvvEMVWx~;5o9+a;(_T&@qIx1 zKB#>k(!O6QF^2v12L%#QH=>Xdh~qFsBQX#v_VUrWx^eeT|Iz24zYZgQE)QHvWb-^d zSAX&Dol}oghr;eq;QQ{77n4V=LI?+TfKb~Lgo8UksP75Fp&cL$XCd@6#=1c}G+Ufc z@H{8*kN0zIu6`2id6ZuiBbo37XaIC$20>X2`B4>&#u&RG0K8mc?2`C~yZH}^??1nL z_wK9W`!1!QtAB&B&olPh^!uBPeTK1bGFJL6#!6pjY~T(!?G9`Yhj2`6o5Wc&ESpclm;9NifV?#uxgv_x4!4!EX=<!gZ+`m2 zZ~wJl9)0oq|Lz;LTW_OC?!KzLzV&0G$nz*yNV`Gt4IccI_y!N265rs#Vex&KG4?G{ z-6PsJXzQv-g94@ahjflUpmRy`4Se{K_U+`_8CYC_HXXq{kM1f z*&Q01j?vI`ObiyF&JyI&g(?PM{2v>t8ZTMhFtokc-8QgStZlc9%iDn+mR~nEoiOg2 zuJQV%3FD>E-fS4vmtO{z-5-Pj#!els-X0wu9q#9!wUOG$0j!npRB|6Y^RRzMvs$=JXuw82aq&+h0#kQM8*ilrp zNvw|XDZ$YBq3WlKF?1k@p-(6bRf{n+GE$v`j2U?PvgHjR6SLU|ARUFPb&PM9~v%V z7_M^+KL9Wahc(I6l15MMT8G|PI?TtSG=k0#4G${>JwlT2^LH8hnY%2_ z(1&v9(pj;#S9Hxz&j@T4F!cVQB*>~|2(GZh^cwWW)d?h1?iRBst9^@hWMQyKNxj25 zlKL<_j14!c_*aCV*Tsl1HC;Q{e%~Zek=bC>bGGnulUM1QA?`&cotH37Rv*7ZFDL!g7NhNsipdUhC3snTj zd|I!r-cKn+9>-i9WO9g_jhQuYNptQj4-DwIIshJkwZk#pvHEBg`i}Oef~$qyS4SPy z3nvDA?@)=hym!zaNJrflE{}a1mpakk7cP%~8<#pR7w<Mh_M;IfkKK@1%TkBT9_fHB4gii|PuAk4za9H-W@Kryc{!#Iu zRV`jN?gvGFynm2uPoL>y|LX4DyMK1~?%gLC`^geJ%I4}%3GR6xW9$gK!*J8~3C2pe zRr_(yIRoqiz=eUIQ;hZBxjVpY`~jPO=~1koJgU$JL+y*tAQsxn5#E^E^T6X{_#unB zs&i<&=C*2zLJS2<1c2+Pft;2qO~-3Fo+1sbaOlzed<3d+l&;$W+8Lbs_HzI z$;+cxBRlDwECgOf1tD9vsN3{x)#KUNP1|evidVoH(B738A*4CchHzSml@iF{XJO71 zi&38@BO+s;JA`m7!`%5iV{b9W{yWCl&ojn8!BdtjesMbC=W9YpzRehWkumn&jImEM z#=e&^hBc1wV~qVx#!5fR82ej{G1yiw5}Z49o^t_qQ^xwIA7SkK1v$OKSpQ!pp?_!K z$b^Wz#9047I?mXvi2U1(4Lo^81YQ?`9~JLk0uA{B+%w|+oOnNvv80bFnjFA^e;QR@ z5bx9CeOtUg32@&f{&)%54>IeXyd43JA>DcWoWsv3e$L?MJMi-|emvwa#EBTPK0zDWLjy6+x&gYA4Pd*KR}W{Cr#+1Y_1{--~KTzk(L;@O@I5t^O7v%|AJ*&#GL`(OqvYoEfZ zH=&0}5aV>qk88KZcrOGG9|I{IYM=~-1}K)(EZ_5NOT}PR5HZ*nATt2G2*!31HRNgG zM@>{v7PY^;<%D*-Ylfq;CkR6S_I9Ohdv<6lmY{qpk|4-B0vkzG zXHAhc6f!5kmI|ce{VJwicA#jw9Q%!tFp29$Mcq ztw7VxQHPFc#kQ;FPO>mGzhg)1w7pCQ0)%JAK_^h$Mj)t%av(6*^z67Iwt1FunTdo1 zxgg_`*%HA=j!@ADIuk*5GcKp*$LKj3vF6|!APF1omoZ+#c&I6YoghA!$&rfcm|i;( zRY0=Jr9n%Dhvt?nT-MhKdej#Yya<5Mw`W1{6l|WtMl@Lk)L=i^vx&3I|yQfr%>sWKtd!R7_KuMo`Nj2rA$_;mJ^@2CW4_ z;KRcqKa?V4K*z|$cd*M)R4#~vl#)OtA}le|pko7srR^{f8{ufB6Sz?&j5dQxU?!s% zroG{4CG@+HJQYs)l}I1`K}T8=KMK|=W|&x(Agi-dT~8C$A1F*_-^Q`IOwv<@NnOlS@`*Fm z6U8Uix8lB7)B6GghK5QsFbp=7%`$^UCvqZE&U8$}5U7C=X<|Es)*>;LL3QACqE1T9 z%Rmtc)~s^e3Dlrk2&EZ?Y6Q(-ghR0sXsLeZHX$kZE2;B!nCPyivzbfRe_J(}%cGjNr#61_uFZj0AVr!;|0icblIY(~xr zNjC_(QGj{8-&F!p7imqjSTa_TR*PPq3c)~J;i@oDA0(0-s@A#@X z3>lH>4;mCac)lSel!}MuEd~OPA;8X3!v< zl2;l%a2rMIf(A7=6o7N#L3BQD2wC!p)7WN9$m<{^gWR?i%=4yh_c zacG+z1r^dD4>hH+D-1YRS$H&(bpj#8VfEa&Fv?59cwHpG`*k)~|FaS<8cw58kw%{tM`bKjI5HC5U3m)6ueA z5n1rQ!shCS-$fRjIXLhxOqF+7-@9z!@k8$d(eJW>$ByC8621<;3rB8oCs>OrmKj7H z(<4cQi-2TVHp&u3P_-msR#1~D*6f*}@d(@7qtxm&5ajrArE@EeE6vc~&_*LnsrX##te9siXi>Mx#hJbuj_E>iib`l(YX1PL)r`Zns2p{h;vAeT9D`&# znVzB>38LR>b|dOznUX9=XdI7XSV5!bKndyjMgpAOxZWmjhIVXKw%T=bt1Vtb;N&%2Tr=^; zrPZnVYZqrP(LP0j ztvWtbtDYaKoj*Txeq4cAoSBy`s1xPJwN4h%cvFWEp&*2S!%)=#Cs+ZWQ z$3;Q>a51-JMYR$;b-HD3gsXuWubSgGtecDGxHH#p3vRwSRzH8eT4JaA z%??&CJhxI}xDXSYcOedkO6=6LK%sA)1rw`Q{?w$`ljR5a?=MN9_hpp&Y{wwztZVmY5I`n=dstL z4}({$+p=>rU`Y9H-~}DKc8R59Iz3~;o-t^jtO=|?NktJ; zw%hQ%P>xN>^rXsV5;>ko6>2-?ASp8GBrrW0NIa9QV_Idpqe0e7+KxGfs9wHj*L_bZ zuGsmMfkHOxG#uG|98R=u2Fa8;T@UnZ*@=9YuHz}L2c>as6Y7vmkuHIswU%*UjHHfn z*!4DKl%!QjJFa6THA}aiN+rNaigdq;$TBFYfQWASTtjhGkIIT6CbPze5gTYSR%LFA?~xAe&)$R`zxi}qyu&{wHQZ80Mkr&S25B%09ay0KG+gR@{g)HdFs6JSzH zFN9&h;Aqo8(U6d=M%bHys-3VI%7qHD?dy9HP(!u(GqaA0vZC#%UT!s$7V) zK@=7Q<)-h)5j+c*;bA}`DTm)cH_yX@qEHAso}U{i(FL?ek?)2?dlEYvDk{7$M2hD@ zciJ+PtO?MHv+HRcr(Ya7#=E~XSAU1G&-bw>n5Kjb(cZM7uv=l+Blr0}_P)aC#%Q%# ztvsv8<@6We1#;^zVZ9zFtr;tAFjo3BW2H|rMxF}(GJhYv2~7hN>?1Db(Qo63u+Z?J zG*nvtmPdE=-Np<%fE`ea_7LbWcIvEbiWvlv+5?9TaTVK)wh>wh?Z6+}HajjfS;8u| zsgMU#i9RqM0=13cpgsf%cLJ50zS?AsKG32h5YUix5Fx`L*2Ekb+!fg=8x6ORd49`2 zeSoWM82<3Ux{0v`4+NV020Zx<`jqJ*%jGxTG7Xlbi=tuF1(`W*R^tggE8#Oq~|v^_}K6ZI>7T=TKcau zMl^xvx3%=&(9-{omj0V!!!VxT)zbf-mj0h=>3?5K{|8!nI9ibP`9m%JKhx6xb1nTF zM?mLvzC>)+$8-c+&uI(r(|Qz6f^Y{UB^iDYvz!wMCvQ84QsE~NegZR_L#ZO3g!F_r ziG0*Eh-%0v35`w>ydTg`B8w`Wyv)Gm#K02>f4@vmwStR^fhQ?ERr(neVf69RRTe4& z*0_NmNfkli6@>pN!e5p}P&lr39r#>6d<@|@gqc*8(tilyXAwS@f&(u#1GRkk1%&@h z9?k^9zl`t;DL9u9{*UtEml6J}`S4kU|I0l5D+vGJ5k8xO5APcT=kn>VA$$zsS5xUf zi*Sq5Kd%$g=MZiq{Idt)Go+Tv^##O!AYaNC5sr=<_(CfEk0bmv!oQdb{|SWuS9v&p z58>a3@SjK##!rDT9ul;dj{5tEdQ?RHR4Uh(5cOY+s8TBGA0TQ$P}-MLQ9p~Q?-RMc zgxd??KZ%Is=MeQ*Mbys{euh(dzl;R`tT6A-BkG@vs4r9AdMfWPAi-ggAYI#c5e3Bt znE8cNuCE~K`$a?2x&9%d{+fvT3f#}BhI|!xUIVnxVM(d4HYACF`1Y3(-A43RIT64@ zg3J{AD~MfJu}5Y+@`sV?;9o_Wo0JBZJE($xjp`&?@^hkWiDQEP>qzkfBE_%q;&GXY zD*iW+;0N^tha4~H#woP?CKCOyOq8m^*YZ`Ur>pQ=dKJFLt8hlI!q@dGP=X`ER+Fm4 zZ|jx#TB;J?$X8-GU5Ve(EAb6p3Ea-6@bFE&5|rSu-;J|~_+7mY-$>Qr_Yn0z3LO1> zQD6L1r1&F|;`a*b@cT&c$9jUpQD}K)x-oEVogZBVGu{m%MnzFFZ!-R$j1`ZbC!TS?D9Z3cn zmA{PZIAOoVI?YfwD%)lhm+M2gad&9TaUDM%qEmn{d!KxR0*sX?W@Sy>D{!GOZ=!ys6w{Aea2mZiIsu;R=i|;b9hV3R?UrSVZB_io2x7 zQu7IUyr#V2`7tdyqg6CZ(CVWvXJj@-(M~zq46>?in3VHQ%o{`lNZ-f26Ja!M)Fb4q z)^r_H*$se$Ajoe~fbk%-H|;R8Vc5F8O`DNaHXxI47Rk<$F`eX-ldjiN\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n\r\ + \n\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n\r\ + \n\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n\ + \n:global MkDir;\ + \n\r\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\r\ + \n\r\ + \n\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=LAN +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=WiFi-CAPsMAN-CA-7DB23C23C976 certificate=\ + WiFi-CAPsMAN-7DB23C23C976 enabled=yes interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-name=Europe/Amsterdam +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260816/20260816100001-Diff-Config.rsc b/MikroNUC/history/20260816/20260816100001-Diff-Config.rsc new file mode 100644 index 0000000..78d08a7 --- /dev/null +++ b/MikroNUC/history/20260816/20260816100001-Diff-Config.rsc @@ -0,0 +1,4 @@ +5485c5485 +< set time-zone-name=Europe/Amsterdam +--- +> set time-zone-autodetect=no time-zone-name=Europe/Moscow -- 2.52.0