From 20505c7e5e63ccdc9eeaee4a9ffa21933e59af58 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 00:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 00:40:03. --- .../20260628/20260628004001-Binary.backup | Bin 0 -> 356045 bytes MikroNUC/20260628/20260628004001-Config.rsc | 4790 +++++++++++++++++ .../20260628/20260628004001-Diff-Config.rsc | 51 + 3 files changed, 4841 insertions(+) create mode 100644 MikroNUC/20260628/20260628004001-Binary.backup create mode 100644 MikroNUC/20260628/20260628004001-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628004001-Diff-Config.rsc diff --git a/MikroNUC/20260628/20260628004001-Binary.backup b/MikroNUC/20260628/20260628004001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..6d6fb5db1755e4d23858c1e9445e863d92459b0e GIT binary patch literal 356045 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

    pTk0Y z5E@`ccosj0!GDR{M8*gOnap3zWnfO95aRFnyA(ovgy+~Ogm@>s6hdTx)VU0>g(m(E z&XZ2TOA+IJW~cM2iy;C4W<`z5#21F(aI6+-M1LX2=bPZdJsxSgwn5P!no z!E%}A@4I0x+|C}rOa9&mZE_jlqCe#}_X;6i%is42As*y1z~(>1-v@vO_&az{Rh!oc zA->Ob4+tS%%5wov>c_eKkPu=c5DLIv2mv$-pAo=khV!*Th|dFY2q8v=5O3r%V?u}? z{yq-s5%+aa2=QM2{@dR)2yy0M*A?iAZNa}A@$dOU+VrlPTYGLwMBP(a{0i{(?PYqkAXDM-PsT z_xDWJxz z)H$#3>fWJ!!xO!Ig9Aeo{e!&&2h0Yx8V#((2B6Y~u}UMoV`JA3kM^Bvy@O-@qeH!e z{bs!@js7lRFxEiljJ>17L*xBJeG>;qW24<<)Lv8Zf^gcm!H?OvYGmL3;jwYEr|o9h zq7_<`R)A1kKQcTvzHhXDY@%;BR5*u0-liJtHEOP>q%~!`v#T3AI#*XVp;FVtPNRvn zR&maD!>W(HpQ#%F@TB%{!srR9sJ0b`vTo$y?gImRk}v{UsPH#A`gLRddq(@mC$8zg z(FE0Iqsp2Acgk^{Z~?kFd-rhPjT2)7PwSsJFfcfP@Li*pYll%QeVAKtX$ZTaKV3D3 zwmid#Y6#9Wxv~Dy>-tC49CZWdA;F$2#J$TzP7HQEBt+x-c1T#!Ubc&l^?3m%r#Rkt zo(6u&o6h39U0nX6_BqRMFFBL#YT<7wwF9E)$`{5et5TO@uTN$TXXGhp57G9@DsZ{O z?!r^}?J&Jv^P%;qL2-e3d-5D1?p-df6@y*NQEvfr1Z+W=d&l%l!OuBfdth5`+4D>G zLg$wD*jN;>=u$s(Eyq%u>^ymsYFUjoBXBp+I2**xViLmQvjv%vRwc|r5EiE!*swuA zQTa`?3sOPtmQuEJE5XnSvfWU+=oMydKZ{4^DxTwpxnM?@mzi}6Ccuer{$qA48ZV@= z!6K*$V^;lzm( zPvqBYg^=g2f}{9(X)g1+q8$|cyzEsB{WfZ zW@p}>lvbru;>x%Z2RV{_tf^QdX zH*~C00K+RgZXW-x3v3^*K?w5eq+T}oE~Qh(9#esc1t5sma*KJni$nAE^78K*+)6xO z5{7Y|Me&TwP`|c2=lGrrxlCyyQfhYHw>uAaa-taFO8OnJa%1U|ua&6C-WP{MF zRP5q9iB}_lE`pVLrB2Qs;v5I-f;Gkq4nqgcD8IcUZ&e(8od!&l(s_+7SnlzLv6PY2 zt6c#pW4eygSs2rKfUzRcY7!Yx2v@8Ks#)BGsgSApYQarZS*u}k!89$gUKOQl#&y-D zDy3=`pkU@@1~g7Zil#EAJJ-?zG+L~@I4Yy$%^MOL>(ob5-9DCEGM4IUBp9)BEt+n@ zQRnCFNi2|ts7dWZPh8Q#b_Ft0Z}j^F_^|^i6M9r$DlH4&*8u}~DxUf%c|VA(Gfbwk z0W_mCR@Ae9mWd69TDJlsb3i3oF|uy}_5Zb#*5txuwOCxJQ|@50T%@Wsh|5^@T8&O? zXIg7otH6ovOoM9GAe8dd&&tzPPf1ap8pOU-O{cjOkn&V=-0Cq9QMTW%2W==f(M(QX zk}pe>s5A@|R z;EX8~5ZicA!^H?OfpYI{t_WPrH0;i2nDIY3IK&Lq&(_uV3=i!cK!1jAz_eahwHK_y3|h@+ z-O#shVo&d${rwaBhsmR$t86p|xuQKRSD=$s z;hl_2f$0Zzd3?I^N*wM*{1%6z-z^Wzl`Hto1?|4)h3z>)sdm3m^4zGERrEW~3g5l~ ztxWtEyx3%DvK`qV_NTJK4UzsmZTWUMGi`hRw2dMR7Rz9U8b+tj3zw(y!m(wF7ZQFb zR(QQVnFW7LQC-v(c%>@ddx%$w1=R{Bv%_={Uco9MN28mgg3$A=X+2Ri0Ao4`(e3~*NsCd!sC1zvf}^}dfATcJGSTXkJ8KP zs708O^_s^JgJK*EdEz^l*W-x-_E_%4ZMJ(?2=LMZCs@~RhlTcnRV*%OieHC6vj9R+ zXrS1N_Q0P@76_csmRumgjkpO|7}(K-CTcXNGoo%te_AFkCg;;y*aa~CQuFqt@><>3 zK>P&$)%*mumF{g29jVOZ+z89D$9xxnZ6)=7+OCilz%>3V;R^WxD^S?B;uw1XxU!4f z9~E!j_NS_)HpsVR`xV~_>@4P7Y?EFEZk_e2e!*rU1{IL_3S1pSH>K_$A0HW$^S%>? zwhJd;_j$PlC&BJXfdnncU>vR;g2Lpu(=p~ia6#Ozae&afu5`-7mq^+92$8TTQCqRN z&<<#l2mDDqPor9_kV3l&ird}+=DMOb=xX!k?)>J>-RpRQXxy_Kh~`Dyykrs6v5!GO zgH_67t{bGw!XIIsBE&;tZCSMxVqJDp3B- z#rNhR^%-2bXHi}}w;0QlXYcmB*+5n)L7FS=V@`lKIqo5VMn1H-SJE&yOv{aZcjf?3 z(_Puw)t#VLy}&V=UE0DR6&5@ey=Cu+e6UZEOkUn6r42u@XbJCCZVv3=5U+rjP2PaMeq*eJ78GCAvZ zJ-p(iJhBrK!YidmO1m)Q$;_eHG>+)RR&ZL?s129CYOoZleo1D71KKg}Tk|%rV?)GE>CwMhMeYGlem8DTx{yagnL z$u?0=?5L~MEf<#>He^F5dc5|G9K;>@<%Jm^^fR@mx}vjd`yiI+8yLF=dV&(5;^`7w zI=gW1efePjpehiljSWI6bL-lRPB4qQf|fCB^&^rpN@IK0B{lnu+Wdn!wc2gOCqx<> zo+t^O+4jPWuLgAbqGVN?()KE)w9WWRgy48S~L1V^cLkK}tZiyzFGQFxWqsmw-b28_+Bd38Mxdgn&f!`j?SiarvPCM?g_L;C;GTlHL+hcxTh3c=X-BQ=64zUrnsD7#1 zzbZJcyMXE&d@+=$mH6QFaej50`gE1TdFV`G{h$^4&M{diIkp?hDX&1PP{nu3mhY4n z;P%77fe@WGX_xG2%O!tpU>6{Pr~?KK=vxVTz&>)0+iuVR?4nj(waaU6$I|o>ff9Qs z_$CP)7(J#j*dff*2qztuvzSS#|k+gD_)P&b)E> z;ONAjy%XcZ*YpnoXFp^7;LuS20eS$fu1D9?RLSyfBlHAg9q+{m+j|?t(-|`x#3drE zx~^T4*{thj!RF)$3BT%^F%6e2R!lS+{3z<)_*zVk?DVmFO&WGztFRj`Y^S*^9N(jm z+b1TEZ{;}7x=mWLXxU}F7Dy z1IZ0Wuc`uK8>5QhdBnt{62@290GG9jyD`O7#a&7hCGs+cGTbCbG}K?zb)@|~?!Zgp z1yaM8>Tdb1K#z}7Ue+jC352jl?R8U=UeV5yL&OYEY>4j=u0KgJ-XJuKnL&u=a~Q(< zk?VUWdPhc7Affp_(y2~v-8ZsuT8|!+M3mS{+@6k`_5UTwMF#(B7g;2TPQ|Q`;ATi9 zb0VitxoguXcY(d`M#q|Rqp^T1?Yc1uPk=MIU@LiWnJ?rfVnVh3Hc7Fs&Sc4sku16b zY`?~9<`wOvzpQA_*(I+6VKwWVg6Gm^Io=Xe*W^lZ0)%$Cg8TZB>7}=LAyUgJ!(Nld z(QL_^_Hq>$!l+UrnXYub39$3Pbl;U%SRdyxQDZS4ljztl6nF- z4e<|n5<#GkKK4kKzvr5Xy~Cpu2S@sP$NOUqfC8-esBYk*cmsR; z`}#+F#|MT{u~1#ZIkK9Xm&9w1_U{`Q8?UWv21UbZmd3MGy=Z?`^<%~*ulBvdY*3tp zDteHx1+}|(tbYPFrTT{`=9}R&U<@(}o-L!k3qB=)VLP;Tp&Ed0ZKQX6Kdhl@3Sw~8 ze3y4pjERQyVDAku^gRbhNBf7yCr10PJ=i}s9)q+QBE6=ogFdl|KqFie1DMU;+l<~< zZ0+2-4QigD20b!7N`d4lXmQShSM_rw19QMRpyv6iCI>4kQKcF%EKOMfSxUZLa^SKih=vJKpR|425~6!=4$&-*BrPavH0K9~ zcDaZNMa5%`|CGR1hgojV1IFoXmlTGt@WDW*yi6hfhM4uo++$%&C1$x9Y8iPyw}^K#=x6pefLJSLcJGBXr~FRp9rE)!Xk+bOYj25f*5 zu=|n8ui@*lj=>>L#hN><1D-qG>lVlCf^Uaoq3^iU2Lrn}?u|Lq?)Z#-_1N&x_4eer zH*33_ySg_6LtA(C_2Uy1a|;92Ek{hOK|s(B>}Rhbz)fJsVxv2K&bB^{vwcum&)k>6+Mw?nn77%bsqBmw zgmzJSZV9eim~w!#Q7zj~@GxVSEIAp2shvEGKRvodjWx#e>H!ZCJ)sMBcXVt&X>4tT z9eH`m_{HR9VlVGL*Hdh9rFoki{9b@-Sa=baRQn!+|58^ehDb<|pR5~Iw+u~3MPW_o zK7&I@C1O5 zSVRLmeF8iqjQ~HhoHu0^q!a~)Rfv-#DA zXBC5Xl~XX<GqmTvAtBRSPzrK2~58gQgH5DpF%iP%AGUFK>RWy@qWlm*L`zFn-U0l?WT$6b(Bo~YQq;}!F=b4vig3TLY2N!M~pHo9|wd(7KK2nPqpgX2~~ zp14jh1Hh|R5MG@ffwZPV+ozk=XR09}v<#eS*DBTQFtDz&UE_wyv zCBUVVM~jygCr!)#ys`1o;h}vKeY^D`BB+&qrg&L#5(0*mpfJ0sJwv>#I0?rgRN=yj>OL?saKvK zq8+;$no|e187QIA)&X^L@|IG*9Rhm*YWZ4_T-DjN4IMU}`7N8fyHv@i289{BZ1q&C zlO?ABa63aAJ&9gTFUJ$$7zDT)qgJtdujl3Pv9O6pr z>f+=CXcmAOP~%*!YqWEiu@L=c=hm!M zsyHrgm@O+#f`6j6rXe_Zt<=AU^j33${j}O9E{Qs)RoIs6GP+)g4^Ln0pP451U&Ugd z$E<|@Ah@MnIq%Fm7)xIC3RMub76zSzTN!QymXlSd1e>UR?)2=Ss!-Kn*NXtEo6VUKHi5haWOpJHotdcW zqB+urY&ljx)>>~Xb{%$D4zC{yf7s#JU9YW&6_Oot_-7pshaVgEhdvbHkR{t+mSX5Q z=iZZfGxKJ>*xcE*2r_``%=^CQoO{l>=aI4oJukOyCGF1QwaRW3l1$AR8#$n&bE?27 zI^HPAf$&Yuc^T`Evv`ONWsZIc=Vy0G6%c@pW~^h=(4p@dOCe%JX6kZUInBzq^sDOS zHH#_9m%)le*-jvOWY!duQG*pvq`Xv1C0+(A(x$%%;z(UY^vz&JvBYUIC`x9exawR| zipxTQq}~}ShdXzSWVLucM1=F1Xbqmue?9j)bF$D>4juMvquSeD9t?x0weaI-59|H) z?T>aIeY*I`#^~1`;@BE5}F51YS(Q0r73cZX(fB@?1fOBsd2qBe%%^{S6_A!EZtk1G6#+ zb@fl~hyw5%6}SqU!Y}|fG`6NC*owH87?ZI)GdEMsP7E$?vt(Z}JbP+)P1lU8UltF} zPxEi-X#QJLi<}rUQGUfA0Nf~p(6_r|(6@+<#nZraP3H*qnsG2#;>tOIV#PEIdL|g` zfVFe2p$1n-pAm8lGd)j{??hL6u}-gwNlYb&yd}%v79skj(0OWMu~Ieg>yS%xHIPH5 zmS-y!1HTA1@@(1AGbpz6Npu`5R5Ua-jabHs_=q!Luu%$18B9UB>x@aT!m^peM1?sa zJ$%(5kVOonQGYwvZcaGa5R-jzsj`d=>C5%a`W9SyJ*uz7*HGL+r9ezX(pNzLj&wkP z-w5AhONDhUlD-%78wb1>dcHj@%bvjJLFMu2@wvTxZtIY?CKaTzbwXE-F&OGb**Gw} zwqu7!u|)#?)3reNVp?#UGcrS4yh!O`WIr>uikSJ=KeBiZj2*LN2$Wh>Nbaq{VB2*F z3(pPwA_dGb2kJ*JZovSIX|oZ4zc>bfRY%?H!~zQk->cg@YxPcj`yn)Voj(WX4a+wV zV+fwWC<8)H@RTesR%*<7);C>&$tOf;2j3DD)3^9lSSq2l2oHT4;*<+Al z`P}Q70#yY6Aoo&D^#Ywl^LxS4K|nDG$qf43z8QLc+2F2O862& zeG-7>D`cd$U+l&tfeTS?C0DGwfL=2Z%M=Huk@lolAf@oW_Z);EO(_|^hDyP06_t7m z0uX|R@bK_(CgFC1D&6%)v%bFbNoRGl0RyHM)WhtgfDuXg?Z3{`7sB=F%9qty#DvJVy3 zJK=z{E$=hiaZI-Eja>^>bSY*|_5emyikPLCitBFB66YT+3gUp2Ax>UAESd|ztn)a{+(7p$Njzypy0ORBn1v>BL z!ZFs*5D4<$P>eoPbY>1BxOAs0T?L&x^C!QKq=ECeO;N?44Tdn(REpc_Cn?+ozEc{R;lN;J+qL-f z+P{%&>6LUdOgL7( zvEFQK)Y|nC++@K?wFlf+C5fJwIl@p~LJyAg))m zPN;3V>eU0=e%O?^3Z@TSEd)?;N|5IpE)=}(6W#;UqLBB1Eu3LB#{mq5v;!d4+5tpn zfIDgqr((}^geNhWg)=bU8JNBWF7o1z0w4FxA@@xTA_?&y!AjoY<^dOP2i}lpWMeUf znFHJ6s+v^%TU8&)k^-$Y_DOSfkKbCXp)jA|I^Qj_I~123Zu6D(2q4z9P$~CJr#E&49W}e&LC(FihEYbQUId~u zl1QheO$)$7=97lmLA@M9+^^ z-_&8%?@J~^bWCW~2UHTF3_;`sLuh%Mz`$uF#p#Q>sSBVJkqHQfaRw+zumPe=9n#re z-XbE@r*&v@-U)QOv{+L?4?Xm7Ux=C3llB#Yjo|Q%jY~ zth{8KqBcuxO2%zeAEET0W6v>_qC}3VnuP$uN%Ou49mz($#HL~j7vrIL9rIvQkcdKf zynXis7zVl}Q3?sy@8uG%Nes72TKmZb%*S+fZMzleK`xsfgj7YnAJo{losfgWVV2BMVEV*F6%OKri-4qJBO*nyii4Qn zWV|rBo=BvU90sNd%C0$E7sMt)(=SeQdD-EvjVUpxoqUU_~8qGmHpB8%~% z1oZ+rp|T9Ofy?u%bb&oFuu5d@%@+Frt{#SKgC}JI(gJC0QPZfN6hzR4kP&GZ>)BLvflF!7sHa zJV7!%RYb00tjy3O^6V2Ya(F6WsUEyQz!bC#!5(SX=f~VVs0O5QqP@2_*1-c~ui0on zmMQwkBpVvP()Z1M(>3oZ99vxZ8`;W~Q;sUe)NLXPR{e6!o{H8Z5*HD$IJ^Kj+JcCZ z<8Z4NQQnP7k?ewgA=#IV*R0O({1oXe9Yvy8gtBuYNs1YuV(|&r3@DwLkobzT13MU- zj{TWX3#4of(xgZ%V2b4$dM;Re0OdMHt%}OkBr_#i%ONoND~SlF!PhK!5Nsv08)eH; zKC7WE%ySYnti-19PLM7J1 zTWd1f0H$k_!3ji8#tY_X^iV;%c{0y8%H;t>D6_m?6f-F8utWtwUb+swtgVof5wxqu zp8KYBHGs^b4=1ux1o={<|f3np?gdy4T0Q=UAn3c zD($8grN>kQaV7NCdKa~*o*Hc@(Noq_Evgffrb)%miiTMhZeC^Sa~qMUEAj!DAA)Ro zvS(J*Ve;)@q>vgu z^Cc5d*leN=pdO&7%^`*nIx5STM+Q@#P&81|)x#`&Qh5ImxAc<}tMxdLn6)wW)+Op>Sn zTpD-6uSNufP+VCN@Ex526WsHdL0|`=>7ulK3!apsN9QLrzW{_LoxJ0(5TQwQLgePY zH{?+k4hV!xft{1d0FsrHhMMA?OyuFYUv)r1=q_HX<3kGSNm+6-2`rEK`{cj?AGWDn#-f zrFE65g0XA(^hT`Yvg4c5zs6qJ`;^m_G5(zQ*yt!6crGgqah0QeVkOMGWA~{oIe_Bz zM0|tfl*P_uq8vyTz9R~YLM)Y^k|n#RLZIEFzZrz1=sfT2d;X#6Tik-b6eur2 zpw7tmp4)L&SooAEzErvLC8B`8nTLNs)>3ob-@!97(0a(hQCES;rPo=$8SyzC&6loFx`K-nu0Z$Sq2 zljV+WmvX==?WzK}8cy8`~Q=38i8}^q8wuP-&U1l^UK}O9b_K zGt<$do#unOh!Q9#1w1P))`jXU#zXvWPQ6aMu~pyMQ-^v-sjk>pOUr55M@}JWge?oH z;y`knZwZ%V%}%Q!HAECkQBKf4YADT%U|)}* zCd;OpRvn-&v|1R+a?-4TiELFa}x zMd|d9DLznil1fr9qE2ZwYkCGu?jZ62Da)eVjV5L(k>5$q8O~#kx=qAsR_ygtsbv{c z&NxWuA*0+#-UO6Ht$GseDalu&eITdudFD)l;iW5Q3W;Xm49#ms9*J=zuS^@F1RZ{`8e&Wt z)>*8fWK#aBOboEi z=$1?7DibYTXN;&*bw1JTm!o6aF5{l?wGM@nL2IZ27H3*jjZ~whOMt2-fd;9A*@9{TJEmZJ+&GK{arw{eg8!fLWL)sm`Zm@I&X7*6AFmdOUmcK4m}bE`Y2 z2`obAtZ+gFJ$+nyF9ADs4f`+OA2NnhOka2h@i$jz=5nvBIvmGu@JIEs{T$!GY zxqpP|)G=0M`aBbRVTNbDGRisFL;awM8UJ5ud50%F-fGpT%SJ2+6A)>Qw-g z#Jm!d;1T0YTu#114EfPlxY`NeLA3c(xoC5tkT4UWxX3qZd+i+=+aH>{^2F>t&>ES6R7Bx>ewXSozMKq8mTB#$8>Zk7Ygb?->C-b4c&!u*!$|s|* z+zmvA3^L(FMZCd8c-xW=%~EJTwNzP#!*y9XTs4S)40Ol0ZyMAnehE$RJ14?H0yq(y zfA^S(xT?D86*O)6AtW}nXV6y(koa0{ZL6`pRcmZQ%a_DkMdf&-Hvo6cA+OG&l2nuj zX)`W=q?B(Bk3_IUYzgxy;a_xn8(U?F62IIVn0~;+d*g6_Y1o~AA4om-A95d(-QHUQ zZ48Vv6IE6WRn;VlCs6p-*5lU4n~?yHBgJtoC?ma@7?)CwMCrJiY8&zrSZ32~pw?@m zvyADz=usWuwP;PC-gQxrY{NwHm{eXUz%~Gr@fw=+VqFv;fbWaAjqCUvb27kYL-}u{ z%BMvJG4Yao49Zogbt@80aHXOSZio)>VUnX+la#lnqr8(jLWUB{P|OIwXAN^?_6EXT z0-qd89UBn(Qi;?`(z&Z7;{n>P>p`ZZpI!api!Z*IguGBEmMy&VTFi?JU|kXxBD7(J ziR*=*O|U|taI^5UiFXQxiDIEJ@ou3|c&|_>+$t0%E)@#cz&RYGAXL_l>b=5E>X`gYP@POPcQooUt^Uu%eN5^)W=YkXa|jW-OOEYOpH_DM=i`^3mn$36O_{bC^1eoj5e@oK+Je zz;8knY%O)JEmbfztcU=D1vCpBk$Y)usBSaXVWJ2ur2(m&i1^Ewky3N7xoOC~E{n0f z7-En8yaiw~EB$8OFUIcas(yT3%QsY7nk>Z)<iv3Wt=V`eOg-PV>|kVuy#e>91xqj;xWiHCnupxR{!ZFTUm8kSdx}4d72G>wb$MUmQIE zVc*gO0ZSm+G#%SY&NzUr-$QBWn34k zvrEv@%R9S*s%+J_qGXe&8kim$Nnv=ZCU%qoU6)Dd%EIvFrDnah2@Aj8+}S$y%Byzo zw?8SZlfe@F1+%?Ht=_NK*3Ljf)s;)qP-(kyCNkPrrp>ljTYPh2-Cpr(v!hQQw5vzG zjgKDo>MIW`59`l%F{!)8p5#>PtL9b7vo<+ZL+bqF^XOKq-fA^=w$FMZsWvvkMCjF4 zW39egYj$E92z##3J{6}fR0S5?-rI$wVwm!<)%f)E2(RMQsW#yD!`kM9`s2h2B-Q~; zNC?*_MGWCD5Bo#YcEI@tz*5v8NEbC!8=YIw#*C&4 zft{$LngCtsnh5bXaWLx1DaxwWW)~KgFpaDj2H6UUaw^jz=O>zMwQJ3GdsApQaE|Id zgDiR#dry>7^(L~^t5ixP#ayk6B(|D??t5OmUUc$`a_R~Tpn8RX3eK{2v$l)p>9vSM zQb!`w&qXyJw>Eb+wl@F>uM#5_s~slLh#3?D^T=95j8{;|2wmGWi@Jy&NM20*J5n2H zoYvN*zTk&CeZiH&-3ehlJyH180?LpUE0u(VU&1i3Bkq?lYS6tZE0rLnmS5_b0T&NT zU;q=`tALT|zy!B3?w0}{mc;l;zl1iZj)*Rkdes@bHu&>*dbX-k_|>2PU<&WNuE@}) zZ<|)vsG?cYq}BaptvS*!#5IT+ivCCupx=*lEgu!iUE3`S``{^QsU#MVmB4C>yO!rV zN9>3PtW@&(v$5@C+B^1cijGXFYxvQNY_{;uTk+h)7$Z|Fn3;&Z8wf=BJ9R8%LqYU=OZb2B%^7=yBZq_O#V zBQ}mkqoX-a%r^k_q`o7qETh#-ycYU0?JGJujT6Px>!2}#gl*WgymZY)LM#-m@{2=# zY*({hDavD<$oE`_h`@NN>E}bM75#z5vg$UtMN-1>YKEo?GX?`02b*FnDKcRSYXO~% zrC2|MRij#Ii3UvN#Bzg4VJuZdR(oZ|dQiL;;(@_ZB7_`PED{<_5f|lKRY?QyEt_C1 z$u1|~MS^Tn@HQf#6o5n{ATd3br<1;GngWqrY_OstMw5d}<_aJ53W+6`+9ItTrm)^5 zkdUL4W})#M;^$|=D)_`|8-pzp5X2Vw*+HGo5cD)yz_OZFp<|U=RXoyikR){k;sR(e z02yFmNeZpStiD_^!Ndz&*Ck41Ph7LXJaGZc#3SO&)6d(R3WJ?~3F1onnV^dMo|hN} z+0e4CN#KYh0rACv(y?rF9g`tR4Q0*>5zIt?a9W3l#v zG`I!?LzW0Y*777|#-+*XJ9DFxq%$N$;$$=Y#n*_GI#RzrZpD?ba&vMjCidrk+_DHo zNDp&L1fjdHuQ3GgFXaAz&5$aLr&iMYvnx!+s<^pg$H>Ru(*5;bqwE@m)Q^{%Hgd!;z4zb69aXmjrA z!%)1Zvw4k&qL`B_13bSzsi@N@9Y~AvxLeXFMTU<%Bkr=6h_2ciac;3T55gI??Xh*o z3ev`97(!E0T9C#!)0N+ z8(kWR9DJuk1d7V}k7X7E_|@2d8(K~7+xo4vOdkzwFFW|>`GNnA4*3852>78N0{l(< zmoNq#jsv@g@tip38xZ;PxR2IfP+)z^6mzh<3SgbR#(0AdufYXwkeX(;9Y4tn8Bpg z{jqYrTP>Bi+w&~j?cbZ)Yp*X&!3|rlgrUVkes5}(9zZH}_$T!LdgK_|*lx7fRywuj z240#@T3yxhJTn@7n1o5ItKM{GYGp$tq-xwyLXpDwuCMNFuQxV2YYn_|mv=kynR^kf z?0^uR9MRw=Z3CKGAD2=rj;|gEMvW+(bYUU?v6oQMCj8xF53BY|0JEnhjSJ*rJT#wMp6}XjpZfw^Fq>!U9}3$b*Z2HX^IaX)EL{{= zP>8Q`1VlSkCkh*Yy2q)--|oOtYN>?gyOv(`?Tjp{tvZt$zf5EARqa4uqdvz}gSjR`7j$ zYi$J^9P-cvOrDMl?KIX_vCvL!91hs*Oa)7t<8Z*;(C*2cD6SP(+4a3c$E3c6M_roz zQGD(ao2o9~ao%@iec$uB1>-QCfUJj=(C9LBs*-i;kavZBcsapc<=)dNAezf2kt=%4 zFC}In=y_BM9L|q>FGMC?38btBJAn$P`%~zk!Ol!mv9%1RN2`K_C$>PR$0vm_)Le9( z3&U}6sq=QJQkgsP3M?kV22)6)ezFOzAbes@H>Rd?)gh#V+2?_D0~7zaR`?lvb3)zG zuL*<2h%f-?Cqk!8O`a>q4tWz965KxnH+x&D z2IrO-4OEFa+KDqHyZt~0B!J%-GtH4rlJ)mwfe#Y}4tZB3{`)hMeViz~Ul4~3LvB_? zc?>*xyzoN1>&OgSwW0Z$=d#vZ%6WTeyUF0GvK>96MKE9}V3fpM z7-|A@H47%ORVI2k-G1q?a~TX?@XH(t11315mmvWT>@jMawCJBY*)=M#6nq=e%&|eH zzK^*-by1+-9LFw{=zsWS_c4Y#Bmn>Hl2o{jA=^bk`bsSu52Np8HQlybaS zbiCkrGG#rb`25ioakj#5guxew&%zK)pdkq;pf1GQOQ7)eLsFW`r1koA5lTEe-gE3x zz)R8a6{+u9^zi(cIHkkHKOqy?MC}tc*2QSp7A+>*`ncU@LmmWXpR=y-J>}_m2~i_y z1u#~EoDi!1H!7XLs`xz2TMr$=RF{_K4E%zPk)VtiQ;jf2sIbLpm^n8E;zUCt(;WDe zAUBSoymLIk9E(^Zx_H7-I~X}vh#c?>TjDATtGti^k$ zf+axv7|ap%|6wlbU&JVuq{fRP7`?Z>wB4z1ukSQh>z!7;-EM4ev^wjJ&3b3I-rQ=S zw+SlGF1Sms8j&|@;x%ZZxwpMtZ?-!1?b^y_9q*H%f~x$Bozq@{8koqY-dWw*dC)*V z5-6{_m)^#})@9eTczMQMLO0LbP!qU}&ic-#bSHv(DytEbH3*X(*H4nQq&P714MC9L zHzHXvPXSuPtQPB=67yi5wpVtST=5P20)sj~;rT#Ecz#c^0Pa#0p5>9}Ki!9{_i==C z7J8n89DL7nUC`Gxf~E6liANE8t|X6wF8z&cLf``k6x+lfZ{)y?pNdB9KBP{|9g7 z>FUb`hyhDTenwq=6!%v}+RH8vyWVqP^wYZfs652Br9NM<#y;vpbr@ysk2kh+#Biy(i}STph&{y)Hj$Pu1uvbIj`UqZ%*JgDo+cCv%S$f*=?j zTH*HI`rMKIS?l2wyV>3CKigVg{#cN`lqorY!*#TC?m>7G^-|+v+aGh-t~XY9nyt=G zv$4_GmPs{gn;1<;D#X=@c1aIQ)k|F#{y^DdWxZ4{bXjs;DS4`$O8c3g+#`vZvZU%T zC%npTuB~())f(;2T76{?UCLxpHPFt>0QD&EbA#3SSwsOdq38yCfh--rE> zMW>YMPtGnVZIYLtET}@@wZs@+5{-*cxk}S&3BJ)9uy2auYUp<%pQ&7c0t<3YU~?!ifvlhoJmvyq`+Kc=r`1vc^TguPyHU2T zlB?cV=*Q)NhGc@+jOEiKMhFiOE2_jmSM^#}F(F2v<;Y`5#phc$InYP|e6 zQB@@1hkj6fiQBzZUu)D*kD}~`x@hql+}^6Un~l{3-z0~r!iRwB*M)(-G%l;PYb&*u zy5TArNFwyhYEANBmHn(K02>eNUA0DITQX~{2aR1cYJXU7Hr6plfow(fD?b%UOH4+C zN9uZAT$~fZ^%L_*;geb+p2ux<9!W4$eRn4i2Bg!sW@%LzOD=878(l63=#S}0j0f`_ zF_=><=a~;1jla4aii+5I9_gB*@u;tfQ70GARwj6l@)DaP8`7s!zE#XWEzzR{eph4H z4vz?5eC}{F5D{cvY1TLEwN_ntBUAc2IV!Gn^~3gXJ$$&kHe3HJw6}J=X4jlwd{)~U zJoEynJepvpso69v)%?Gs354i z#qs2CA;`D(cI(a5sgpRw{ZH0<3r7z&cdf%ivps9hJ`6r>wdRJ)M-Mj+7ngUS+t;F2 zIK;~r;6VLL7rtOl;`t7(rQs%s|V_oR*R$L)PkkxPiarsx{ z@+ToA1;BmzQGMn9&dviJ0gGj_E9W+70F@G4?OaMKrUdVUH;61z{H!hsDm82Ndwb*o5MWd~jTypy%5owT^s{ zlvw|~w7bxzthk7#pIM1D-|iw}bN^AKK7V+u(>2Ts!~K6L2keK(d0~E;E((!!ZhJ{* zY(N{~iv5qzHq|BY;Xl8x`Dvzlt}c$Bsv;)n(@c_-4!R4J_0p7Dp)=>#Xnd&CX#8i_ zg$<49m-MKC=Zr&0_XgLHU^?Sao^Q+Rjo}It{w-~4q(^r~p@B1T%x?M`@+22-B%YdV zyJXgoqj`;9SI=T1a*`Y?wGRoi5xG{V9F5<=*<_^N{r|>PKxg&y)3omXY~V_?Yl`IVI*t?%WBi7jn69 z{uM7`9flm6C_-GELDv)VF?u!6E+ondZG*ugO+@moqGCw32Ct+#A@xRT4h)v;i4+8> z^$}x}+5lC*%9K~_%~tYNO3Y6vceN6|!BFIvvx`M_gK5||)fhV^sV9e_N|_`+l6w$k zRT6T$a5+z>b5iSz?E#Tj4pM!YCp+d3CkBF3VPkT%tyXF)#8~1fCdYW(H3XE>z zteO5jq8*vd#sX)iok(OgP84=!-LzZbxUA1 z4RV;FDJWk>ElO!iS5&^jyPSn{v8l*Q3!05rIlrHqG4jDg|P z_c+(At_)@S)w*6JRD{9mgT@9692KMa2=ECmd!N*VLXEFG${t71^ zZbILgm}2%CC$4p7)tc_|MF^0pHC?Pmd2_q|Nqcp3XK$^8QLQ5sR0Jt?=yA>0(=}5n zCsnI>T_!|)ZBx8kbp=3c8^U+ajB?09!CRZOZ4Us8)1FIY-DtZK!QRrHF_gjVb% zDveq(aiT!Cx`k5vkF=LksVnMG3RiJA5i6Z8e1%FUvWdEhW}}CPhvh*yjB^txK?cb$ z_OA+cjm9tb!g+jT{sq#~-lDA zA8^bUH;snaL(k`FQw}2aDf?>-RVnrk~Hio;AcnZgV9l{x7&kcV?h5hBmD z^h?r(q+Wlm4#|(8`Nv5eBItaMDkOq_>5)+q`CAY~vRZ&8V@dd-gEmd01Nc(#mh}ib zE0iB6O`Y^EPuxnJv!_&2F%~lyf};^ArV14460xYJFO_0a;>=Zz-Jb{p?~E`Y3{D}p z1|7J_h=2a`B)-7X#jGc?^uIotXPu=68<;4>Jj6Kg(TfZw7~L%?JOnvD{Pp9md?s>y zmcFALlwQxXx@GaBm`Yz3PtJc>{-W-%{2rMrVy|>kZhIw&WgH)j;lK=8IQCsIP&~p- zhFGo4c0-Sunv*XWnZ3-Rhkc|@JXlU>Dq%t`CyyAPiRUF=CKpIcI;xdQQh}HQtm@8U zLpk=zVDr22f|f>zn7AMakKdrT{cXqRRtMHIc?bE4H1vmGjn%9^AN@Nj zKw8}A7Mkdme6GfNxdPq#{7VFSnbra2cOsy~)(I*%y+a9>zSsB7;Sg?^;QEOla5pSj zzJ0)5u%dOOvvcOYNwk-;;#aI_$?z5+PZrJvLhYjH;IO*1Q~?|Q_z=s$VPf~BCLK<@ zV*qjX?5hDVM>~c<7v$QZt3+@z$|ZLj={jC=l-+RuC?bQm4orxq-W%`|JSU=gO{j#_ zl2>`y!!iO!N$~nFjtNKv84lo*e4=nkxQeNsV3&~501@?DF~oLl_t54R_XjrjailQy zi!hM$lh2Y4QvWJJYPf$yU<@-SFWK=@4VCQdv-Wy9`>Sz)iS+#^8r- z!c|cOW>N!f< zXWc#z#Sw;oo*%2gmE-C^|Kf`;zA3XJUMm#X-yy8R#tE;s@n0g=(O85tqV&fy$PxV$ z&jmmo+Xa_t5%{8yr-SV>_p>g_*%>6k_EYSMP*8uxIdSD89v}5c6f#2I?|$Yk-d$Qw zA}$U-aaiT=oDLbMph*M2@}nQo0;khW0}|3LfcQoH`}l=#nF4c&PJc5Ooh~4!O{RKR zDVrml6mRbA*0&$kSDK$_RKM4}5qA%H*MF|YB^-2jVmThRxyRyjn-lZjFvm~ z?T0XhxNNKmohX1&y0Cr&@yW5fe-~lb3?> zEKHY(1^j3p!X`-p$lQCqSd>q|BBJ)}*>@-j_=vv$^m3jQel3y1QW$=}UEAE;dDN-3 z+qKpETlMXBXKm-vHe|XI*C!*>53O#(2S#iO0Q{BOZiIfar0QC8p}yCIVmHBBzq7j6 zY}U6|h2gPiwxt;UP6y8%JLE>WTz>B<_g(JHjC}72?}Z)9_TNMFwYs1!n1KYtn*7tU zQPqbcvevx?vC#I~J6ny7X00t;2gDduBk@U&*26EY)z@o#o9)hKO=M+~Mb%5MoAt5p zjd*#5`;LvZUZz@L{PF3|w%Unmp?_u9^uyBH3a(6JyVb63uhwHTn_KcSGNE?OsrZ6)IWXszOZVTAZJf zR&|tgL1u~;TS8=1ZlB#u5hjtBZRVB4$5O?>~4dQkYnZJPMt+MMxjh zLXBM290fGl*@&#hXQ2&}7)#P*$P+|9O_G76U50}62IEBi=`mro!4jA++f7h7gVpz% z@>TD*@4rS!f4VtJwPmIxGb zZDF!dxV8}Uo|YSAU%hg%P&<|Fm>yTb$J_w&Hj_h}D(4>mAu8*rgelhYAstCh<^Q8zJ%gu7|O_!1n$J zF&C=b_lheUReK|bmqYP%E0#r%J8W&c#dg6AE5fbf!kn=%R}On4!*T=4i&UcLV=M9e z4qM~<=GY0PcFOZbHy|Sxmnuu7@}YK;e5|ow|C|BsM14qcFw5I&c%IbE;{e9 z7KCt)PCw8gn!O&x$by+cv;7JwJZ3-QG>DjT??S&|(Ky|G;z%HdPVBOr3sr*g6F5<9=WV zxG@qoV2$KGSj2-3Jy+^f#$Ztbo;J2aR_!G=Mfw&V_9SkLE_3b-@Thw;k7OxF0iIL8 zmm8gKqA*e4$3m1mMF1Oe7#%?$>a`^L^@SxmtOnV3g`aBfsZhG<*ZfZa?ZU!M| zcE|?YM>YQS-Oc(Zoz=}+1G8ISgr?%tY4U|efB*GADKw(+8=qcLj5-K&ll9JfQ>Ryh zQNkMHaL<4E%)#OF&r;gZza&FhK_7JV>^l58aqm1}V6Nm%%LwqetOGCfqIkR15)kozuBH;kwStnIGv|dx>SI? zzN8b5b-HhFtcyTbjrL=WO883OH}_4~)GLI)k*!QBVXCMqgfEYVX0L})OwuOa7u|iU ztHuLS%EqR@)Hs$_hji2Qx#=JRj}B4MtvAiaiYiGZRm{sMG=Z*9PB`NFUy={dx&YK> zpo)1dDo%ct&tl`+D^@o$MVO3iWB#_d=DqscH&O)mjtn_?<*J~_@Cy_fg@!9G4`nhdD&13+GQ@k|N7fm3z0JThT(RU=Azr!|A3E|K#({uV|7VnPx2@S1faxk$0 z$lf=dfaJEg$~F(z9q+K**j;aYB8%s=FUQ&e2seB0JJ%UYo>)1kvD&QH+I7~3FtV(%&bD{jto})()ovyFXCKHB zh|{yrRv^w{8;fu6ZEn(y1*=Re1#yo$wgut2n;%pcKg^b&VpD33q1o#Meh+>jYP_a= z#fbjP)U<-|95n%E*vXSI1030@CT<2emQ$6rN7$|nS>0gu6U#YOnZf@OQ?*Cf`Wb2f zkYg_=PZNv6LY`bS?2S5zHmtQ(+uS_4f<5XWf}OTJXg*GE71J(-$&xBeb&ovOprh4nWTjFq;s2~sG4TJyri#YLBg;IY zZFS_k&Bj)(`ItSZKV}~QRg6$>novpnVSzpriKB#L{KcmN{-z~VO-HaI|DrO2GQyUC zLP=eS4<*Kb_)GE+`Ev|u5m2Dg6n8w#LWMe2iwk#fII@tO%vL^B-zS#B9S`$H1pyG6 zZP@J{8iqR_POI&I2+?Ks>Mc2s4=U4a${tO9IL$t&!k@jN_{S{#qc8t4H_fK{eeq%* zD{!}GihnOmv#B7QipKlF;xwBId*bENG@IhG;xe`%Wqv#GHq{{f|jYb1-P|e0QK<_Q4#~v1Ds=SlSx-rQR?U9n9m*MhA<^DCc2L@T}y6 z79HD}7jrvUEbaSdUp#`@_`!i}2s-NfqV$4Tx`V}1&p#TeE3iQQl_0GdwzB}sB=nNv z-?&OnAlhAk6~Mtn>%V~0fYx2leXh2?81?Io!T?)a!bgEuk~_HslRL0oE??s|*nMC0 zwggRk!DwGuj%8>e82NU{Oa7>btu8|==JV3*g7~Lc9ofY)wi!V|wE_S$a$Q?#v|52e zja{${$0n*(u`kRm z(4NY#Fu*PptEsWmwjxMFqPijng(N+p#FWF+m?Qfk@oAJ50B&WZ;3aCorz91lBs`Pk zBNq{Qoud&qNE|6q!Do&l1uCV*C7uD3curRB@bUb9!y@xM@frJ0g0F@TT^*&^X!vNj zYxoFf=8po;1=c>E8GPtM&tSy_^G#Qu@BzUtTQdk8xVAG`(Gpi~Y8569zMkoDkON34 zQZ|Vs&g1romVQc#hp}|&`TdTaSgd`|u|T$%iVKNtQSQ+g6Iv=|cUfE!)jTK4$3jqr zPNK==2(3AQ_b)}DP&8fF3-PuuC>qKO#)@G5Q8d_(qQdZrtBOytJGPy$;T8>6C8VZL zF$U&T+p;dKPSIdRIpbow0mh1B+kz6M659|uaaJlNcbT!Drt1~C3sO)~h^Lvi1KY}d z2Zr~np5yt@tKaXMaH`G%t(yh9U;QvM$Remjp>!=?O7=dhIouC}Y-yTvq_jxR=BLVP z|0McbnPHJzHVR&u*%kL~fcD4S^jo3nhg+T-4vK~<>cC2m25rxCLVJ{Yg$u3hVbQVO zrvMZ``boTk#gXZ92cJfRO9RF6+a4`|+;lyy%B~x&GLbd{`PA~oLxoRq-{y|B>bd)N zUju?giArhEm*ClxaFB+1Y#9g-?0Pr=nc8!#$WA5xq(+1FdgCDUhMK|PThp=ofJ;U2 z5zH)tXn;G;4r;pf!^T1cW!Q|eQ}oe{jIpBa1|!}Bq$g^b{Ag+whvsNxyZtnv&_`XL zBz>Vzw322QV<%?Z{-OMWyHhtJ8q z`rJCX&nTi>@}rUh`otuGKIu}Kp%%RZ?)wm41aUb545`A!zHeK*o(`^xKuP9iljy}* z@vvyH+4;=l=SkusKE``qpi0BDQBFtgajea{OyQ1k3& z714I2=^aAT2=VE85Cwq(#Z23b!TyeW*t|*)=~E2G!z}hGd-E=M1Tqd z@9;i1E$mlGHYGl(@7NmN)MmZ zRIxT3kDd<(t1wpxDv+8fmJ(iiP(qxgnvM6(!;i<@2bhI5)|Y5KbD$9+bVMo&Qe&l* z6{w;}>LeJ1>tsJJzKcQ$j^)QWfVSU2PN=3EYkleJ zsR(wyUM#t1nvV)sRuF!@RGKxCpJ=H_U;g!C(b8l;(PAZu^dFxk)67jv3yXc<8;@4t zY&+|z0FHfr2sQJ_p>p~MAq1+^6It!?Ej|&MreSL4#kAC4KU>Eh+Hlw}WV!qFk|qD0 zYOWaCA*vOMEwtNVr5~_04u;jJK}o?$Gz-CED07HAQ93A&d=9RN#0JI;M^Rju#w^^t zDyD~KM))d*dKXRt`Xe(XNFUvJiB z4KaFlk4*_jswr06UQ=7!Y&@v5sYoq5^<#IZxmItomB&h{)Z(JF+H=MhuQ|YiY1#Lp zV|F<*Xl>L7#Y-yTjQEq7`^uJyr4LmRA{%%nIYiF>HE{HX_`M~bt>xA88`H@jUs96l?H8zHMB4G+Q9`PjX?g#vgYUn)Qbx`{&JDx~C|l)n+Zy=V@fjVL_l(mhcEexnlL^F>YkNQO&<->AUF z;j=J&M|Sd`6h#y88`iI^wgicy|^uoX;8nG?Eu7 zu_`_i-{8Y0_{|c;cASIQj&la=ZS;5vp5PZkMf4mRSioi$@J&hDifZickHrRQz)6!% zqZ9U*Z;?*b2>X=#antYf&|u=`o64n|7v=N(OqG7KJ>}eh|A@Nk#Iet(<7dx8n<&!ISd|W=gGpVSWphh}ANG>0clQb7}cj@kufnWJt&}rOB!B2DTMQn@$ zOo$-tR3VGS2|W?H0RENZ!()G6>r?lwXJhjSa$ow~0=FgcqlWzuPtFhIOBx`5=bwW@ z!hlS7jvdET^6dkNN3rjFZdiiot`LH_$t}wc*`aTTA$K9bLUSCX&pahak{qw+_vN1o zY|GT1J=9_)PdOY(0;-N%868|LRV%5qdO3s~;AZ+PlNIoRBSiT4^-~ehiNf2`pEMQH zS`+E;hZ1(+Hztqf$QB&cM_SlX;&Q=E?H>Nfs8k9F-4t)-i}OR$vIde^kytAn0fodu z<79(1>#a5u@p&XH!7y3Z>^&Vr#0ne4T$GTd1dOG8KJ-E^OQor10@BixRzMBhOw2w+ zw801+rJm0%NZo2W0l+=fG{jE|4&e>0A%a=PO;c3UEH)O;xNgB6wi5X|MGcq0kq^Jj zk&O)~YzBN3f3!g;oQHI54fIl;NtS?tn&4SAc0o5#0;kInw-Ss@R3nFhkxTXN?9scG zg?FF!$DgjRtQ~FNU48sn{b@Q<=jzg1|A#WqW9j zX83dTLkE!%+Il6^((VSW98VlBrcCLe>C3vX&-E+jLOL@DB7L7>VnVigLsH_akocKf#b zJQ+1F3ixp0@bEAh5c9vujnID~CxyhfWAWRuyT0ikMegQC^)9TIih`eN5AV6SjcDBQ zUx1GC1TXE6h9%P(*)E1?NCNA+wrwS31dUHDN!-k>s8f7%ex|_CF$LGIO8%0_3o!(b zqM*l(t=cZbc_#P(nN~fD_LbJHP=l%93`xdPd*(y%E76~f<-`=0AjtzzC%Lf*XjREZ z_?WcoN+^M~m3vXsc+zJB4IJ~HXx=?KR`lFPG^9Ly!fLwc1i48$AoX=LFoaBOBg+@j zx}5~aSY4wTDzPL9k*D1~)puJ{2FPZ-a#pOv%CrXL=pLMGI09JyOc9Lt5X~gNoPImB zO~N^IVv}&5+>^&mUavAXD$ZAbxSUWE=fAux!b)Vvs*rrye)^oqX+NU%_vROc3%|uj z+_i#^=L%qf&Z1S7Ta@9H4-XHMR2#d?<6>2)6yyzyXTXw>RV9H~GR29{RjPL>dMYs9 z$4&vz085=b3B&iWWIkVyBkRC>9cK>_S+E3#(VDT!lDsK#S@M=h=~C?^ht-#Ynrdz% z0<*HE3VE%;M+RI)1C5UQ!uPyTq&A^LS$&H)iQ;=^G#LF7Y#@Cg>pQB_%8Bmb6Z%IT z@M{X#yJO$RuQDrRW6;bmziayaK<($J+J^rm`!N)MH`D&U8HByQh#eb)D3ZD5N+mII z_)zi8Z18RLp<-y<%37<<#%{Z_-q@@IF<|dnUT+N7tQ}agcd4*&=*5LqCj=i`*q|ia zJ_3o+iFbW3 z^nPNL%W(g5tS`6fwdN<=8yyTCz1Q4?j%D&zHBs|hh=l<+{pW7KEQ61t zKOeO62DQ?vx7&^FjaFy3*1oSa6*IgS3UJp3+-piHbmPEup7JAfqog=wFR8so9PBMQ zjFP}^h-we*y^06E$RQ+4L{(m*Dxf70+%Mf4{u{?>Ehh{ArerWD3eyESLP;h{rNBJk zY_7tptOS~5haYf<9hyD__Mwa%lZC=N(`Tz#ct^S35>@4ov7|7NkiPu(ZtM}`j zwFW8=t_4HyDF^pG5Qa&aF-zq5BuX9CMw)$ntJZ4Qn?$YSw%=FUzF4hPDlm|XyBiJE z6P;LYFI_IC+LHyyy#iGBii~9%k=B=^!O8u5S5Mz>#pPlmOabG6Q>h9d4 zE)*&kPy75b{`Yl+Zk-4nZ7q?y7ChJm%LzMRCbPIRe$JU=hTQEPu^~5uu`g`QAX^6- zvh_@tUO+Gv&*yl7YT3caF^{P3H2ncDLf`I=k!2LYmU~Zm$Q&;S(k5r{$m5~?l>0Ni zLA*u^=~PTJl!(63#L5Yx8a+->sncwSo>DEtrB!5lp`lX%m<9uQmec?sI`JUFHG={E z$zm~@oPn}Mvy(I~L($^`g8eYm;J!$y2p&*-r(Ap-L!N&1=M6K+Z$=|Wl2?2q?^0n3 zm6k-st^}?<;x@*;y|wkY_3>th(xxC89h$yvT3xupqbkgQ@Y(9K`TD2c>iphlxw11q z+TOR9=B)dB-TLBA^$-Y`Y>V^^YFn!L8CwqzyxB+NkDO0;ox|m|jc2uI73;I5{pTOu z{mlEg@eJB}O^mC#v(rvAcv}u`TFybu+N93wx~NZjdzzDaw{i&$ zw6?dk8wq8)8c~k2m#K8?L1VYmYN13(HPjH+*Q&F3F%})>LAa4P$S6e`qN{u#jtJF!) zO5OV_pdK1{eW-E`yP>N)>OsZmi=ait1;e87RZk2@#lmRYZen2_I3%{NTA9V$H!+Mf zK&UIMTccO7lV*K)^KpA8vrG$l%ap8^UMclhi16xl?5Vxk>O8DB8|#ldyY*8qf)2>k zBjLh=;Pr28)jsJo>#e=bHs(#F%}`SYUIJAklAdqwHX9FX?K-OET8~@p`c`MH(R!dl zD7di@=Dmb9JHodNVRUzIWwWt*Tzyr90PzGwh+EOk3{t^JlDJ}ZQ8b{uesR!x3Gh)# zB~{f#Btz74Wr)cFph{Lt@@9MmDfs}STAi7n!5d6EXF>688s4<)D%(k|tNJvfHmX!l5XSDX(iuM3HDh;J zv;Og3qgh`wAV*7!- zHSU2lE++#`B4?fqHFF2j`^G6)MTjyk!yZshOK&|BbkT^x)IGoJ|<6=S22Y>iNMO9G-A4OqJSTdQgHd1v6Jir-Y?_82f2vAJT2@b;_SyV zpuwcD7xxv(c+opW5PV^fva{FT*lBEUFyJ0ptKNKAZ@v(yL2eYjAlU`*=d=SLb^tRE z$1?Kx>I-16LGw0Cc?z(23;_zz;F}l#(r;3XaAMuDY9zN9;D@WqHtK+|6!6~$n<6=e z_7C~#u^L@e@<>Xno#^eC z=~O-MQSfD(jg@AtNm&c-u=cQKT(l||p@ww#RBkeHV1aAcXW%)0B*|!8A-;J>c#6Sq>J!qd#D+-hE|U*fkV zw85_i+RosY2$?8F5R1#=&+`M_FYAErZ$v-`{SerRI1l2cElP)pE``kJJUwIJLA4 zrUUTx<((5ZU*zrpu1Ls2Z28lt!j=gPAOLEo8)cV29|x#nI*$JS1!)lUuP0p5BKoUO zQm7i-c&-Uifkk(73MsxM22dzWTrd1=f)xsdn}wfEyi+Jl6bprkcMFBWdxb*bR-rI) zsZhB3#TQ?EakWsmR=86rT$`9E{OsBfb!hQ_DQE%Rmar2-kHB{O4kx{kw+|6E!vP|K z={OYOL}dTOi{x-%`&J3^+8?p5?V6BIc3Oy^E_cIV8ndgIeRv8N&$R}ctUGBpwZp^1 z8CnU@GC}ka5Rn6f^LU^opO3r%0s|Z=iYNd~p&I(Zhjp=rCEbXH=O0fAzTRs1Z4R7({&e=1AW=$bp{_`wzsLb2(b z8)IZup8Vl$Enwa)+Drjoh=KhxV&GM985}!d5TV}{`50?yV5MG4bbfARuj!Ee z*9h5_`K68##a6CwhITOGc4CM`8Jc4k4ous#?4EEpfqYZgFathH+M!HGMksO_g}W-o zgtA?!5%v$b>Uye}lYp(H-QyDB)L|1>mEq;I3ybK$(rAN5^BtO zg0Ypt-bjR|fqO+=SxhH<8_PZ$+dWYjuEe46ou+6QexYG3{JLcaJqX!;RO40^4c*wcS!b1<_@FCcrLH?mJ1f@>Vm#FWQ2O9LNL^IMK+xiQnsL8~! ztmRv2;;OKxxg08?(bzr3W~_u(WAT&B0ZeKE@hhoFiQ;fX5RNYr#HGZR=9cCS{89r5 z&CY^nJARkL-pGKl$8a2VpcWfjvRDklak*+`_O5~7SWXAMSU8VqIDanqAAgnL1vD>4 z7$kT%atWRqpGO290RnUt-2^N*<|~v7;tLV|x%h^l0`MD=7hvq)5@h1q!epUvZ6P8M z7c4i(zPfy|P{3CrG6gtoXK$93>*0|11`y$OJcJQS=K+}X_4v*C$;7ITOL$$9iMRuz z6YXd)Y;C*6T6l7!@a!fpkjLS`^KHmibikPzutV-R@ayy5*bm?#)R&$R;lK>hgtg1T z%T&nO0eAM1H->90Q!Nfs_y|I+^p4p3B4L#s9;MAz61xM-0iE_dH}D+JKDgg*?^15H5|lR<<^T;wd7`!^5&LBIJMf0O|rg%n5%il-7WCJSJk1f<}#vgIAR5ZyT_ z!wCjaJtL;%D!n_2vq_30|FTq(!Eb_Y2nwKgLDwE%p>Mju&<-GDF!FlpgYy%CH65?; zyHZq8yC5>41m;@B7&NIfUaivwKg)!X2)AtWt5%h%BZw7dsx+aNNMN&$Z%_*(x@9nl zK*P`gP=ufw9wEgFOf6TIs|J3d`t1CN?kky1%0LWa$p3lDO9*nidkM5AAs9Aq8 z%dY?pg8Oft65NTzU&Ez{8*aJ;b{IOecC9zjs{($n$K zG^#N68_JXOL;bpr&3ha{eblD{`rO$sfohUliarKCj#9*43~&a9JTu~<0>p|Q=L}a6 z%7hb;9CKOP)tW5@jxFx?v&1=qY4|G(w0GF|?|VVWz_0lS#`jlAS1TT1c~Ak(RVuT_ z9N4&#oR)_c0Kng_ArG6gi0QpW;Qaf!+{y(M6i8EZBW0=m`d(9diwFyk$fEf6*cY+l zN+MRA>XQA6Y(%^Pn@OkxY7)t@*`;Co6%@w6Sckeed*ni z9#E_=im$n^l*O1Mgu5*M4O~^BAeg2dEk?Z`!^jx47vntNGo^Cq; zh0|b!X#Cr`XsqfVgz^=Hj+bu7&(5Q1dJ&q9h`x zL)pZ->hXgBOPG#R^AXlbpd$PcYQdDCvam2;oj1fcorZ8m!jn0CK4+u=Drz6fEMS=h zDuZxzBBIt`$X+G!RxB6d?bTD^?Pi_!z(O3h`agSY*t*b}2-C?&yNySOX>D}ezAC4m^c#4uLdZA?dU zlMI85!V)N2%+Q8N9w<(E`}=m!HXWr20ZQq+1m&*MGF`jWH~pS%stc!ZqnO#=4v!Lt zqB6%E>{3c3cOf6O6rb%B*34BZi{u~yJmRnZycZPNP-TjU2dCAaQ%r}~$_I82QZtyK zYRR*ZnT!i@!5E6MdJ2b^=VzA;{7RgAa2i(UMbqR2mz{-9jUPQSqC)&&XAM#oseiPI{VET`=Y-RNnC?^M?VBO0E zGW72Ji1$E;c#qPECx<||XXapE&@&wjWYuo1t-#UfxjaHgi;o;n8q;NNxoASNqcb8G ztfIG(`QC8*+y)umcp_nz%u*-EQ7Oh^#!aRKzRlT5YYcZZgf0VX1i1XTN42E zYvq9Vf#->Zv6m0*fcu_XqBnG@biE7;>|o$2gzcL`xBV&= zkKciD3Y34T%nu+EV$ZP^$?!F*pu8jp>P!r#TU1zFP$aLfep}|Wf?o}O>UEnCpb-KF zL1GSX-|2bYDCB-w_M@QW73%ujN8qLo6DePzY@HCW6>Bx8h5H`!T<3^wLrOtmjk+rB zR@g%uuGv`N?E@dnXV@bfEMK`9jv<<3FzyZDM%(K?;k}SqJcP5~%qlYO%X zE}K2CFLJeFUtlFO4SD|h_5lyTlvXB4JOq<-k3Hpl#7vj*BfNaT6o> zcD8Db?MTOc_0V)2vuh^I7!}sW^f53mmC{@bfxin7+0*fUf zaq%FrA{GDv5Cr8otGRb(?+$kE%wT5jE*6$!+LUZ1mTX5!S&nTLVUw0**|b%nrI5Bt zkrc@)Q7Ta?%PLbYCyw(+mHhD!D|X5@korzPrh8`Y0tCqXNcjRaGu!9%>C>lAzfO0b z<~o(S15Y!T)kBKX!#<9I*E_VSeWn|f`!Zbf$m zHL75`6Qwj1>rKFyE_71J`uI3<431NL-K&~$drH=+R2oJ1)02jRC3xkUJv={myda@{ zTGM4Dv_(ad$DU0N^{ZL7$x93>d?};?*T-y^CAU&S$xyw``6-`$mJ!-n( z+Q;4@I;&0m&{H?0eQ#?0X;=i{E+_P9*S=w9le|2GHV2vg*>mf-SHiM%zCU|F?On0r z*z(n)u2Hf$Q=ihDVe2Bg@qY1mQNQ^00q&1TF)usDr}$PP z*#**yQi6sMtH6ZqC$7V9FBd&u+4XS>nqU8CcA1(jJTEWJ41OJbsMF+^l4tr((MTJe z=k*m4^)Q)EZpU$wxm?PJ+}((a2|Xxg0m9G8 z0aXs#MJJ*MNnv^{^cBSrS;@>RWkh0W`(GTH!+Vi*6{8t?<;uT&+dH^~RqZNXI(z45)SC~q%6iesBX zEfgK2Sw7Iu0z$U2b~HA5R-v zb(%>K_COMD%LS6|+kEILiidu7@XXv9?n+kRrTwG6A&Ivrs%O-Whoq}ouZlf@FK71v zqMOA=imw!H5*%H1niV?2&&}P%{nvQWrevObZDuF3p!HuRaX_41-vRLxMg7N^QRfg`S83n7cle1N6 zeVXU`-HUv^xymbHMKIK|5~m$RYpEACnjTtREMuhR9?rlIXUQU(-Zsj$gms*;7EA(ueVs|b?~0Lo ziA}LeMOt@ok_^`l^XsiDq1SD@{sHnix8Tz*lm+RgL0`|Gx`FPhl5k(p2^dT-naS2%&_c^PDpm_<`7?$OzGL#4^j=;xYJn=58I6iXc#_89o| zYTOIG4H~Tq#VqH1%_`9vaMZS$9xz#nO*q#lqZ8|`fs|He5;l#~M%wKeZ%_b6JHws+ zsY#UqE6$pMQk+Bl3^eXuFBKpLrL{Mz>3kF_yB8XTzPh25rYqu(`Ljvk$!ZcZKD=LMIh_CGmwxZ7y$ zIke~;IuSq7T=ux%@;l~dPtDC9K4^LsMh>M1>fY>chQCho38}cJL6Q@JeckS-yyk0B zB=cGnIG2;%o@V=)AQ^^ID|xajUZ&y=S$4{=HAwCnk^1A8!TC_TJg^k48tm z+NE>C$voW>A~gni)yU~rNMDx(dv*I7P23SzkgrVNuSH0^%FU!cu%F#S$85P&#A|_c z#h$R$)MAW6Xdxcs0>ryhxf&F4JAvKxwDqG+JdOy4vzH=vb4=1ieCwldr97wO||1T zQ(iYnfx69D?t{6NU$Gc_Rqlqdp@c#+TapG0!RhG)_(khuY~+`-=R$G96!YYRyEp>g zYQxDO>A2h=dPtz)tx&}93iIdzhKn2WCS~p7aibnqh&mb)p3cF-*yCyED(}Qi$1as_ z)7`H(yTyyN+Qt}@11Vcv>;f_(H^tj)9!xngslb}R3!~b@WHpGE%k$}mPt$ZK0S-a!< zGlC6>P;MZV1w*4AF~LuT)}#0cxnLGw*u~CVvvVPG1>;Y#+f?|EmBUZ206iEsOIg>I zUOby!`%GDlTPOl|)y=zp?vxoGz6FSPuhPl*j4Wkthc^4(nQL~~Ml?PS6vd^}%Up?Y zt$`24&5UK?pbLXp(?M6IybB|*+6;eEJ(teA1guW5`V5z9k6D8dx!$48WE)EzC~}93 zCvkEk+ll4kW;(fSbkT?9qKWVJCsh=P8qFa`ic5n5Iu zI`7VM5oD*7k0T-3DCm%K*a(Cr-|1z+H|tck<)kzU+JgW8>s7g&nbqVQ4-G-ObgxbE zmL=ETL9jFo5ErwiLxr(&nd;+pwRwcut57t4_%t-z+Gz;aWSCN-5jf}A&=WOcS31bi zRRxvJx`>snpd6d(X9g4X6SLXs8|+4Du)tHJne1=%Tu;$=GMAJEZ%Y-sN~`CCl{L$U z)n{1^;R?KZyt4jo;O{6S-a$tGtSf$yIL#nfo(R{Y^|?2&^fPCRbdK1Muug{oL;bYKi~u5@kka<4g;JmmeG0>N-v zy-~j=m@iKjJFD6$w}D7bDz>wt{?ICwV)e#r)#;*I^-)o)Wcso;&32QZV~Twe5oRpE+OuV+U*_o5r6RVq`3B>4&3(Y>teb*ONb@~|H)bYmyNr0HEVdZ`+9 zPLek&-Svo;^txcIQYq&Y2Cfruet{fz6jqqpejHKNjKWw)oUZLB=eKqEjZ{}hU$Z_v zaCu>uFhwQ)cFCS&LAySdG4gX`HLL!aL0*Z6yVfGi~}c@>p8Z8XElRv!Vizk zoh)0njRjRYe*Ra@X=azczPLTWA+i;XP6BGe|O|6S)buMoW`lk^a@E_wU_1 z!=Lhi<$6u_xwlF%P(3PKQoEO~OYN@h8z60y9~5o@@lI9J3f=pK9c;Hiz&k}y7YSf3 zJFl~Mrbdpa8z+mgc+n`4hupZbK3Sx!2Zge7;!<6Yy6vS(=tQ(#(Z^{eeojhwrV%sH z+|mEpj&&*U%cm!MtEEUM-S#1!-4r&x9Gm?4&uHI<-tzDsn#+MQRSY09BaHpnb3qR) zbO`|s=6<+|qK8!C9)lPfsbTE#$jypOD`HaFO^h)&jGY!HAx>+ty^%3iVvN-p|KW{A z9&8fYz!)_hUZ=|4_5m0gn=v)CKB5iV^^iCX_ zHjV8(^Vn&}x96QWw%Sg4XD4nybQfdekIb~aplNw!1D=miNc_o@J02QiZ1>oFqkQ$s z$k=`NokiX7lTeslxS*fvU$D7?_ME)yzgc6jm?nh7{B;NHY%I&Cv?;b!-^Qf#JDvFg*Gb) zz-9hJ;`<-3UcLH=`2GUn%r{@s8Drsp?EufNM!>6?s|zQx#RKXkeu_LD&zbz4Kb zF?KL=?Y2`nHhUaCPuC?tZ4_Dsu%XN_{hsAx{k1SE6KSic;95l;;oiI~4;6LMZFelc z>e{lRo)VeE8_VXhy(^Wrvic({C%)x<)cx&O{?ovdJAUx|+Q0cPKep%HfAql@@BN=Y z_kSMopFTAFS9krxw(o!Hqy69C^LHQE^hcN8|2KdAAAa{;mwxHXQxAOPx1XwCxP&CR z|E$W=>Tifjvkwh~Xl{3iZ^-`{@r_C|F1}G|CdKye-cS{a+w+Kyd5x)wT7u_x#e$k{Ozx}C9;_ZYSe-vuJ~=mcZclmc zv=mmI3+v{o2!ZXZ*K0yx)3xcn5ZE<2fY7$g`HACY1vUmon^e`&L8cPgH&J`0SVH|d z3B6BAs8%eYJ$q^g&sE=hu6p+!?4gr$Lc$F$;WvSd zl3`s|YN?~+*W{tMl!ka;WD02C#N?zB&=zX>E?;Hr53Vw^LJ#J|rB}uJ^@?lm@DU-c zf(m_OR8nEpGYVH&U^)lAWwi-t%HLyFWcAl+OBgx}qtzSIh17@cVR~|=hJQu!IV*aE zLx-!UPRy6fQZN&>GS{RVj!ez}dgPJuk@Z=qx=)E(w&sAX*@0oDrT?CKqCJhPRI_iTcYcUjoFt_RK>g8kzawBGEP?JH* zHm3HFB~ALXbzoGNRX^$g%q_0vO*f`$G$c;{RCKkln<}U=J#nglZ=EQSm$#1kn^I6W zCCd%}EK6PKZ%URM|5=v0EKl7`W4tLR%O4kQ`Ffq~%{0evCHJkZ ze*c1)Fys9Kn{Ny<_NhU(mCZL`lEu0hV{8PQadBDjZDQSlu}#AwY}mwY42kd|i951b z=V6QWA&EO6=)r3H-M^obVvSaY zfm0C~2LG0yM4p6gHet?1MG0mH5o^(w7GXWKlZJwgmgPB?Bnz`Mc1l&_mJ=?ttOdGu z2wOmclzGS~MGBHo#zt;aK%}T5BWXC_`6uJRs?u6o41ARVV#*7;wnRaWTAj#BoVc~v zmZ7&GlpoKk#%nS5pdfwxwrKAyY`*coON{+piLoD**f;@IywGWRLEH631R`oXiSHzp z*hz5lTP%t07C3nC=62AL+ZiK^HIA6jA5hLMjL~wzZH)DSb$h85@{O4?6_BJ*?o)cN z49Ajz7QPUKBzFae4r*{2O$A4eXRRr0l{N)SWGF1r;KfjFy1woDN~&0$(82uFuZ#@A z>QK+IVx{X@%A9+}&YhXK9J~-aY5ubmIE#uxmbbXu^c^LIEOf>3?Les+lnMi1RRc+A zZoDiUPGXe<&DVBV0>whovt)l`?72aNV+rNTWyapi82dxU*yk8y@8g)I7QghIJmw*M9(iuHoTRw97=p)<}E;xGR<iBtc|3j#VYJ428Lm;)=F5Kt z;7G~qZ3ym>qx!Qs~$Uk z?)1~A&& zabIP6(V1#B_U@@KHk~TGT-Bit57p&q;j~~Z@i9UY_!0U1mjI1^)b&&_=(IXf%kz_}>zkv(Rwv4Z;ihU_#w^KV zEF{A!5g=;t#8ae;t>Yw{BhRVCbT&$Lix@PttvHMV(3AAhx3(iI#69dZBp~l+5{hoy zgf@eVDM^fKOmHjgcASoko`)|vmX|E`hP&`)tXR&+ztDBMDf|vSeJZ{T14iyRf^8|4 zD2Q&RUw~jTfFZ7#CFloyafd;|iOIa8ASHkXrmiYT9h4~^9TZ0^fJ%bUEJ;wGTYza9 z4!@j^jjKy+M-{3bakcHLF+S3w{8v`p$Z2=2NO6eKq1*0+VH8|ktF|5Ai7ZtmD4qfm z1<50%k%BslO06*iI1qXMCtJP?`3V3Wr5GGz?* z+~|V*6B$@`ZL!cOI$Q}v1~i~9Ow=*2k%VHvDq~h}_8bss=L?1CCr5`9v1Ub#5VoWVjauTrU;tC&08~K+IKdpG+6v1wo?9Ie5 zKpo3(r%e@vtYT@@jPS@>m5ED!-LMD00PrM0Lf=}2hNocT6E=}a7Qn%cMAI0QrQ%s@ zPP8wKf+SGoD`EVWShLlU8Kcl7a+D%(73{^UZqiz+SS_#O_(|lZEv6Lj%0>47b46wQJ`6frmCILi>pz*5>`Vi z?VT{Kji#zm&_&Bq<(gfM_1+zNqM-zFxLCEK)aZh?I14u#NK8(12!5PkSF6lo5svS; z?WJZq2`)+~vi!CplHs)5Y=<;Ll8M}7G9oeyYf9JGn&SHl(VUAo(w0FTFGTe)56L5E z*b~X8hNKN?YSXc$~sf$F#h(f>3px7!_%tMaxgDSV2)$ zqN!@T6IRX#sr?MigBB?ry=3H`?E*7==p~jZ5>e4A3HFtsfk8J5I}3hz!!1~!*w=VF zyTZ4)0triHBhf+MX{*mHMl|VCu0#b>8h9*b4Ql#c9lp;+7wcC{Z`P?hEm7)X<1_BX*kck`<+WExM+-)5LI6 zv5H}7+)=iBq=tj%YM_mb$MI4njNBC~ag08f=mo9i*j+R#YaHkLHH=S_#kBiFcJX&^ zm<-V+k+p~;Yr`3xh?|d8Hc)d3=C&$)Hjo5SCjO2>92y%WmBi_Ubi(8sE_+5%Jfikd_KgErjTfzkwM4@QL^ z_QHE^Th$H7P69G@M2{2uL7=J%D$i;XY7C&mkyKhO)k9*ur-D^$L;ML|WNs0qn)(Eh=TBXLRYNbVpCL{%z3r?gvDw5GiQpvA2DlJP_6ST1u+7YLX@XIdUq4EvB z3~9X#-)2cCT#TzND~vmqPb~@V#*vg9P?Ac7ZcHjx73su@W+sG=#@KKj8|$UWpc^FB z&V?kYHltu!Yw9tR2|~xG)4((#lSx6dBt$n}V)~}2#oZ=vj`a0ejM9rSs*%%DE81YI znM8|mCGIxG(J+}fdZ2bX>q9XR#lUWMV=5zwNd_5Yj)P)HjZGz_naHIJ83!W;#Azng z(23k|NjDTh!~$s9D^8T)N;?%5;~NECh?A(>qRY@EX~}65OW2n0Yf&|R_^8K-WQFih zhHHO~>_N-CAhN1S5T-pF%7#MuL{Slf0#LZ`ByPs=10onIJ6$!~z^H-3+sYh)LQ(#m z#Vt)TZ^%FjbNm$rF=KUtuAlO1!pv6dV}dl^A7k^4$r39yN^E?nO6xrJ5*y!Xx0a)Y z&`K7p{ZF^fpR)G5Q_aVo_GYJM-xJS==g&}cq>j+wQSwiMl%ZuS_~q(!#d41R1y|08ec2JC{&U{ZR*cdIO-b0g|#Q& zylTLX)l#yB+%=G~dnE%iMoLgp#Oripw`CYykgXJQA&4t<@==tTGE4D@0NZgZtaL1e zVUq4@yIV|1rza%p2|@lOCzzV2i~>?|+BmdcNi5Y9pu^4}xxT=Pw4FX^j5BzWn2rV& z&Y*RyR)w6~OTMJxj!TI63Vf##_-c4^cb*Cv3{5(+_&A+-$qLh1i^&H{w&KQtNB5W% z^FcVCW1-WO7-kCmkg9}1@zb0pQP*FVAgQZTcRaV1a%Se9QYFGEhFNX`Sb-)LDA9$0 z_hYUqQOOt}nFSWK>);t6jh4)&2&PIR^CDGJ!qOEKv?7vNaY^&=m zSY0(eMMDsG;!K@J*WK+*Nj~_W@}MXY!#*=wEyP4r;vsRlkw!QfDBTFWeA zA+2~VZdB}Mx)w*XomDdoHHgB(u+j{IB!)}Y3XWz=A?4&7>Xtanla&ZzN(piuA&P)D zi1Dpx$WLnO0HfmAdaUX^^iEqNkeooQI3#P1S4PB9NxVl&^Nsg2cDaw;!L&if1Z@nO zh`LpV4N8~$*xiNTnWR^6_{a^n-o1?|?q#$qW>h%#1MW~}rqW2K*B3@5Xn_-Xzg zJCBhBrcuKlW=ofFw2=l;RT>mj?O@fX%grug9u?wRHVy0}&}D4=oiZy{7{+P?5_Th1 z9V=c#Xf<-eU}DYccx^EyqdfxT`caAx6%QK+v<{|$9g-*O zWf+<@F;hbAiXCGn&g~$6U^_eMbUn>}=?5DY`WF1qpviC0li$!!beH&{EtlVTOB}2h z$Zyz=TZb}A?@XZL{ zgqh2rk;GQCo;U@c2i*nGj08z)#MnKcwi!`W>g1jSN99NFK=?fppSZ%&`q8ZvZlt~o zNtF9|>M9B)u^Ul7W+Xx3RfPW(cUjy{(UpGG(ikEN^<{zC|VC&H%cz>C7VhC4?U^=sbq-FXY3IBK)iQ@HvG4Fi-z+g#Ryu&l&XLHDGja9{)JP zrxAY4!2dYHTZDg~u1L=z+(G!q2jJ_VZp3;X(EIbLynt{N-01TL{wEN=6X7oy;h#kK zzs=M69fW@v;h!`VOenEgr2I%(zIw>^mMFaF1fIci@ zy$HAOa6Cgw@>zg>QGh-}@|iT^z66B7D2)3#fc{c|UZS`SBkt#cFeDI6ZeIon0~<)@ z^G2*!0Q!i?h#BjT0s18YdIf&FR7So8IWL0Rb67a&t4~NJpnCgdfZG6ni7NptAc#!R zuK>EFpu&Dm@K=Fzo^aq5UnTrC;z{J>vm$LNW1{{i!1!f>@im@2Zh=tAe;o+_ zPDdDY{ji%DV)+J;{=GypSa>zhLc?U?n>q`xau#;!EPP96fe?m;EyiHt+d31k8ce*F zXJXQ1;yXGMuW=@@ey!y2U7ZO+7z(;cRuF%x^YEI%!}kFCuR=zjD=LdW1IB+77~d=4 z;rl@ND;;4dj#|ED7RH|g>#rr2!Nco$9x{dT1D%K03wS_vQ#=sDkR2yk9#HcX53gg$ zXR1rwANb)t%1@e&4$#UiIY_6IKA2$OR0n=|zmMZl%Rs&IOSs1nemY>ypXkOF z$BL6mV**!|POQ3~8zd8SdI|fIH$O!I_!stU9uq5Waz*L9-~#XS z{PGuCF~m7J&0U#ry2Ib7izRZJhJ_l;0>&|lABV*gj(E5)Uo0qYl4lz#%YKm1!Xk1- zvjmMF-RPa!u|nIJXgz*G+pQ+&otQU>4A6KV^G<}(v{47hS*_){ma-ebNDw(IP82&Z vU|n3J9U%$~5rtuXof^XA>-E_=GNzM!a?\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \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\";\r\ + \n#\r\ + \n#root.mnuc.backuper\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\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \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\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \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" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/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=Bridge-LAN registry-mirrors="https://re\ + g.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 manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1 memory-high=4096.0MiB memory-max=4096.0MiB registry-url=\ + https://registry-1.docker.io tmpdir=/tmp username=amsternl +/interface bridge port +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=*C4 +add bridge=Bridge-LAN interface=*C2 +add bridge=Bridge-LAN interface=*C5 +add bridge=Bridge-LAN interface=*C1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=Internet internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=lo list=LAN +add interface=ether2 list=Internet +add interface=docker-awg-veth list=WAN +add interface=ether2 list=WAN +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=ether2 network=10.30.30.0 +add address=10.9.0.3/24 interface=docker-awg-veth network=10.9.0.0 +/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=Bridge-LAN \ + 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.81 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=bfd disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +add disabled=no distance=1 dst-address=0.0.0.0/0 gateway=docker-awg-veth \ + routing-table=r_to_vpn scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set www disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +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-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both password-authentication=no \ + publickey-authentication-options=verify-required strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN 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 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260628/20260628004001-Diff-Config.rsc b/MikroNUC/history/20260628/20260628004001-Diff-Config.rsc new file mode 100644 index 0000000..0826ef4 --- /dev/null +++ b/MikroNUC/history/20260628/20260628004001-Diff-Config.rsc @@ -0,0 +1,51 @@ +4676a4677 +> # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +4699a4701,4719 +> /ip firewall address-list +> add address=8.8.4.4 list=to_vpn +> add address=10.0.0.0/8 list=RFC1918 +> add address=172.16.0.0/12 list=RFC1918 +> add address=192.168.0.0/16 list=RFC1918 +> /ip firewall filter +> add action=accept chain=input +> add action=accept chain=output +> add action=accept chain=forward comment="Comment of the rule" disabled=yes +> /ip firewall mangle +> # docker-awg-veth not ready +> add action=change-mss chain=forward new-mss=1360 out-interface=\ +> docker-awg-veth protocol=tcp tcp-flags=syn tcp-mss=1420-65535 +> add action=mark-routing chain=prerouting new-routing-mark=r_to_vpn \ +> src-address=10.30.30.0/24 +> /ip firewall nat +> # docker-awg-veth not ready +> add action=masquerade chain=srcnat out-interface=docker-awg-veth \ +> routing-mark=r_to_vpn +4755,4758c4775,4777 +< 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 \ +--- +> add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ +> on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ +> policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +4760,4761c4779,4780 +< add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ +< name=AM-ScriptInstallUpdate on-event=\ +--- +> add comment="Every Day Check For Update Scripts" interval=1d name=\ +> AM-ScriptInstallUpdate on-event=\ +4765c4784 +< add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ +--- +> add comment="Every Day Check For Fake Bridges" interval=1d name=\ +4769,4770c4788,4789 +< add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ +< on-event="/system/script/run mod/AM-TG-Bot;" policy=\ +--- +> add comment="Telegram Chat Bot" interval=30s name=mod/AM-TG-Bot on-event=\ +> "/system/script/run mod/AM-TG-Bot;" policy=\ +4775c4794 +< disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ +--- +> interval=1w name=AM-Backup-UpdateEmail on-event=\ -- 2.52.0