From 6ef46820f4a7466a466d6959e6a7f51ce4e225fb Mon Sep 17 00:00:00 2001 From: tfkhdyt <me@tfkhdyt.my.id> Date: Tue, 26 Sep 2023 20:18:30 +0700 Subject: [PATCH] build: fix build --- .dockerignore | 1 + .gitignore | 1 + Dockerfile | 52 +++++++++++++++------- bun.lockb | Bin 87404 -> 94510 bytes drizzle.config.js | 4 ++ fly.toml | 16 +++---- package.json | 4 +- src/Fetcher.ts | 2 +- src/Scraper.ts | 4 +- src/app.ts | 85 +++++++++++++++++------------------- src/blacklist/blacklist.ts | 12 ------ src/main.ts | 9 ++-- tsconfig.json | 86 ++++++++++++++++++------------------- 13 files changed, 142 insertions(+), 134 deletions(-) create mode 100644 drizzle.config.js delete mode 100644 src/blacklist/blacklist.ts diff --git a/.dockerignore b/.dockerignore index 8ec76ca..1596d6e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,4 @@ node_modules dist .env .env.production +drizzle diff --git a/.gitignore b/.gitignore index 8ec76ca..1596d6e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules dist .env .env.production +drizzle diff --git a/Dockerfile b/Dockerfile index 13f6b6c..0a24ae7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,46 @@ -FROM oven/bun AS builder +# syntax = docker/dockerfile:1 -WORKDIR /src +# Adjust BUN_VERSION as desired +ARG BUN_VERSION=1.0.2 +FROM oven/bun:${BUN_VERSION} as base -COPY . . +LABEL fly_launch_runtime="Bun" -ENV NODE_ENV=production +# Bun app lives here +WORKDIR /app -RUN bun i && \ - bun run generate && \ - bun build --target bun --outfile dist/kbbi.js ./src/main.ts +# Set production environment +ENV NODE_ENV="production" -FROM oven/bun -WORKDIR /app +# Throw-away build stage to reduce size of final image +FROM base as build + +# Install packages needed to build node modules +RUN apt-get update -qq && \ + apt-get install -y build-essential pkg-config python-is-python3 + +# Install node modules +COPY --link bun.lockb package.json ./ +RUN bun install + +# Copy application code +COPY --link . . + +# Build application +RUN bun run generate + +# Remove development dependencies +RUN rm -rf node_modules && \ + bun install --ci + -COPY --from=builder /src/dist/kbbi.js /app/kbbi.js -COPY --from=builder /src/package.json /app/package.json -COPY --from=builder /src/bun.lockb /app/bun.lockb -COPY --from=builder /src/drizzle /app/drizzle +# Final stage for app image +FROM base -RUN bun i -p +# Copy built application +COPY --from=build /app /app -ENTRYPOINT [ "bun", "run", "/app/kbbi.js" ] +# Start the server by default, this can be overwritten at runtime +EXPOSE 3000 +CMD [ "bun", "run", "src/main.ts" ] diff --git a/bun.lockb b/bun.lockb index fa6efd37d7fb2752a4a1614c2859b0238e2ac25b..3722c456cc9067ad239424adb847c01e13baf7a3 100755 GIT binary patch delta 19121 zcmeHv2Y6J~w*T3~Op+4}>A)m}B#;7x38^Fy$RI6*5D+8=k$@>fG9)A+g{sb=C<F|} z4IUMwUqOmeRGJ7DlprniVgZ7J(tC&ZTc<$oy<Fe_@4I@x?>+bT{bud8_S$Q&y>>fO z_MBZ}_~3+LYIyU4J0^eU)0Xw`yRB=NY8f*z%ro<~giSy9jSjxF>O^7EjM>FZpmbwe zy8m-CugZD6<Y#4BO<6@{WtIX<Zb|0gI?!?z1p5t?JpuX_c%nBz>w=yH^#nbSa*=pY zXHb0|g?67-9h$rkLN^GP=>@?ZbPISgT3l3GmRn*e9S)w1<z$vwOoIzc*E<M8J@5{W zit+3`i>1U`B<$x;vIe%zkWv#mv`1Roz{|CZhL)FE3rbgkC;38^W|x+lM&xCd^+6@7 zw;M0yYF1HEK~`p|B~x{|xWrOcX0^bd0&8KJAk21A^rx#d07g-Dh<&0e*aZP~tH3h6 z5H^G@ih_N<N|%8eQJ!P56q~H2!Wh(}ZkK?P1G`X}_-Mq0918|T75iY6Q;eVFPwE;x z8x4-6)>2b$K~a`qLAXh~8RXP$Yi?mtiA50nRjZ_wSx_oW@lsmxQuPK~3v*26#X=u% zrCt(WscX<YslLMLp+$W;1fHyySchWt1wn2SeXHb`bfLDF8z|cOmQgfjLMXaNJ`66% z#Ei)iuEA~Uk)>1~3Bgi2tlUyEO6cdKShbWQ$nf<9^k~-Xft(_dnO#&;2zv)0r=H*P zRpeiQK{xC<McHK~*24alH4spPBl4`-d8lA5wTy&4LFcD5ydM-ju@`5S<(c4Rc3x&d zKFZ&2tOPMHv(yA<b0>o*JDL89{tAAnj=#G(Kxr-*6rR}^@j!h8+mb-V*)dHN=e_}@ zPUeGB$SU9y%-IV;;rGMgWkG__2z(CY6w<z+q~APPvF8U0k^P`bw}F!HmJp@$2`EH- zTiE@Op$48oN66{XMLEa`!avTVEBv@VH1Jw8rMgcGrP=2}DaMx4Sd(RBnQ)XhH-y^i zK}wAewG1t?j<#6JTPj-lpycd8P;#aZ=o6rMnT12FXmgE8DPIgqLD1%h=1Wbe(n(8c zR=KqxM>rKB2p-Ve2TCKp85ARGvwxr#yrT&0<5h)nRi3HJQ$R5%?Qtp}0E$^?cUI*$ zS}BHWKxwK}gHnSVReljD)t?SZ4%k4w1sjG>RY+46o>ciG=mEt`n>y7hzaEqt?h>y= zWE!X&_;65?N3~HJo&`z~oCZpx^&aYxW2uN9*^L1;BC2+C0v0W39nglLzMz;g_Uv{_ z1ErRs!!0F(aFJhf2(`W6Ua8(EQ8E7(D4EMD$;`HtVld2#JPnk#i$$PRt}Xu@Yl*yn zJl{c)&xSp6ucoaa1dtxwBD(lV#T!#cWdituBAzz;BottE*`X)*XH;fMF4`5gbW+?M z6&X)USXig>m7SHiybMYWEry&n|F=P1K`o`(nFX0Smh1v+xfS)Ix+;!6hjP-l3?b{- zZ!_~P@^Tz(EwB_=OUp7#M-^tHAjdiw8<Zfdp6l4b!D-Cd+#M4<&NSi2T#nf)TfJy) zFm1Cq<;;mCarX0f+UM@hd13sJpw5@s<)1z)+kCHpttf8N`N*=O$d3xnXU{&H(?9L| zcAGn&{J!@$)^mOXKeR60zuoh*`gaSyyqEgi$UzN5$7SxD8}(G$-m^iYi`<@d4)?Nk zOMdzV=k7xTUoW3ya~Uu&-Q$L1<)>Lw^>;IldR(f%w$Jh=OAm*f9yxqpWseFU#;19- z;ul>Tve&ujYSwu$-rY5k_2U(;X10)5xtc|tP7pArc2bRF9eA>vS)2?R#>cMbz1`y2 zR$c{Jy*h$`8DclcdINZ}yO}NK74BxkA(Vvk9j*yF2R(Q8NM!Lm*~83oc!h^q+^83X z?sB!>?r}Ot19$dJWSw}jr<sl66?h%XtMIy&i(Y2Y=qL!ipe%5A*Ep8PD<HcH8RBhs z<h`Tfoty*#6PXw|JDDeYo5imoQyg=Tj%Om@=A9_^#T2AYIq}}EapD``uv+YOs5{~| zaF`o*9d$&khc%;cR5kz{X*zKCh<GP3ieZYvVP4U|EP7&!kb)AebS^eDiwjg)9X`!H zj-BQekS)e0Ms)>V4F^uDoC8M?#1>d5s$J+0<`qUW`;k}SHI$1!W|q#A@ydAxUU%^- zAH)R<zO`&^8m#pKhe-<`di%tQGr-YUp>qhy54_6PEVjplS6Zj`MuDR)LLX7x$SWF| z#e0xpjgt%-FJh&UeR;ga)!?X)I=So&II1o8PHcs}OwmMN4MpJE@yZ4X;!@-&-~xw- z>=$0q*ev>CZIfrRpXin7Z)R`tWPh``7bT>Pc%VP-crqZH^2`!5z)_iA9-f&h=OA;3 z!I5W*rh9#*o-(X`z$qTkKu!hMO=$uBI{}X3p=dV7wn>u+9DMD=#U^I)UC2mZiN{ai zA`}i0_HL-?GhQ7U&&8%@aV#X{2PDQgaUD2nS(y_zcvVxgAqppGB2RTsV8uK+$jsj3 z6+ve4ETpZWiKz##n)oOj+VqYSv%o2{qS`x-&E^%sW_FZU;k7vzL(J?oo*ZHp*Wsk^ z01J-Xy<wcFYox7H)Dhc(dkST8<P5Kai|3Ud3F24Ck$;ZVsv!)UX%tU&O%O9Er+9;f zFa;dVDtTELPJrtu*A8l|^cCaZ01E^h)xk_bokieiZaB)b{EVupj9gQkF^RG!`Bn%H zJGwHb4C}#R-nb^P>%5|cS$sM`iLjo#`@|W>gNx@oyb}zYkb8=|_#}v~fr2nVsgJdo z&#S`Bn8{*Gvv?6D6jR3Co5ryaUeVI58{dR)b4?TvG|?7{dy_cUh$oxO;&V*}Ax&1t z#AhpbmC4NfxEO9Wqz4H?Z(iw>pj#ZoCpJtpd=Ehfo{9*z!y{5pR8nFv5gd&{9Zo%~ zQ8~5OAxb}$c~l5aU7PTFF;9+wzmWAoZA8$AR)1B5ncd}Lq?v{B<Vdq<X{L<>Ik<sW zq2xXnqs(IK=Gv^IjjIqG^&2a$aUAXC(AWnVwI|On7Q~aI%?4`=$atz-0$aeV!2bju zu@6cR1H-UyLmRUg-u2=YF=o*Q85AHx>xTEiMbOSJ9!8G(tXMYkideJw5}qcgH9_81 z*Me&&YtYQR3XaAbWu9?jYdp0*tSt@)M{@{era19)a8wU1g1ZN<D>y_Gv#k@{qjiXF z93GbPsyMT_95Py|SiRnHI<Xb+9-nBq1+6YTH9o<R7%2!za_&{+lI7e^<djlRw5!Ml zAgAapKu#$={jfALS`bp@x-TK8)c6LuUb3t{b`PaA8#%?w666%UE66GK;$t=0Smcx% zJCRdrcw_WZWP8ser__BPIYsZfCKJtZN|;4?n-~QSZej~`4~utFi*OubuKdKS63t?7 zY)lkuL@*{!oCi+X*J#fC0xnsOoqI?e>&&anX7N2dFH-z5kAksNf}>T7(SUvchF2X$ zj7=6e8gJ}#Xl)j+>R>h$;^FaWzN15e;dA7=$T<g?%b=WCgdF*z)cBN(9nIn`$jFMa zqer(_X0ft*tl$tIdtDlD!zyr5@*Z>&IYbq9us`S;Ci01$62$@VlN`rFf^+l0ky%X8 zPI2N<a8yCw;zc8#Nl8-<lb8*zGdP2MysZF7^{_dj!@q)~?i+Y@c)Sx_N5E*EH<B6B zQyg7@BGSVV2}h5EOOw0a8V{>V?f`OfIK;MyAYwzCBsQpWo}6ShY=Ep6)o}6Zg!zC{ z0SP?`3F94Hoy5VAV!GMg<w2|l2Xl6Bc~N-4d0G^h0pW3CM{sB!eqoOj7lL~R95$-@ z@lIgKNhj)>A-)S9#OSOxyo_8|o@z=Ew<4$XkX9QlJepcqZ8)70z	%OF1~Q#o#8b zM%hi5jgZm~QU_A(B9V&p45V0bc4t|)TG167p_kN_yibdL!BJaqv~isH8aSm(z1{G* zT2G(sg1Q7Jz#E`Ml-jQk=zxX*rN>b!_W^XgeK&s_)$#)<5hcDcF-VW2o{*ylvL3vY zDTYH%L>&RdM)m>|OD@M0l@n32k7&sW)F$YW#gGdWD8yW*SeAkV&=a6U)BrpUkb%Ac zrN_}=$VbVg3MKu~Do>Q`+Ew08vybBOGC+#2s0F`8NpBpW<I9u%`KQTFe0#DR-<RxW zqskNL1?h2=YF5al3MGCbKpwoV%88P^Ql*nsIt7#xQ7WGbP{_E-Pp4Q?-`)Wz5hcZ$ z#30qC)Zn|aRH39dTjhU?lHMFuk0?1XU*+eceYwE}s(>gp^f!Piz6VeuO8g>#3@lOk zrJ$6EQu#7skZM!1_W?lV9|Dw!62F4tPnnefC89LDJ_bns2|(#_lnk#0NN*iLi73f8 z03`nup!7IO9kFe~3n^{}C~cNs6)GZMrSe3{@V6>Yl<I9)`P!89b^=cTN7Qnnq<>VU z-+{sboA9G5I0j1T7(j~00ZK$^9sLZD;nM&mqSS*k0Ljk*RPQ`Mi73f05`#pP^sfL; zz#V|m_}@i_5>cvfAD|)7!5Z;;vic8{91<a?B4<_a2~gV0nvn4SmD2eCe^vN5>r?x` z=>SC_2tA@s1*<(HO7c*ZHdp1pMIE5uO4W~4^+Ef~n{PY>Bx|G6wkk~^AretC@TAJu zrZf~?At#5rspUi&uk5KD6+MVUX{uUUn^LV_kkjEb0F-7%j;dFiQY%)-i57y=kx{Pd z)uuGVMyhg9IlQ$6DE>PIc}k8!1*-UxT9GJa>7b*T_=?JpBO#JTDNl|Q$nk$ZS;LgL zJnl$^zI@bD1bl$Mj#lBXqxG+&RXJ*DQT%nZ{&lqePmbolj@Cc)Xzh<B`u9iaKk{^> z{;s2S^}V!1oqG%%Ffr|b&hLY5Codf`Zm-w(*RMUVZ=dzix`W|Yyj%_ohRYT8-c4PQ z9uv%ao;nsV<@N_PV=ko3W1}-W7F>Ax_VpLhIuq`@-p8R`#jD;)=ObsjaMR2b_AMVZ zGo3e^<-)%Qw}UsImCg@=tDKdBF9;5UoA9m+Z~txztL7E&rt|pOF8mz0y}ZrrbbcD# zyR%c+e*O!%x8}I;)Hx~aAb)#KI`1~uh2H~rm?zIo=QqJEpPPb@oo|6#JkNz^%u8Y4 z^Y`ba^M3PPxXb($d{Oc2{B-WLz=dxFcbtn0()n6&Ll>m58oq8pIy=eT7xu!Fh|-gv zD9CEkm$n?1)^69J_0wE!mzNeco-oOyMVg`g=InxTA<v$X=8S&h$Vcx!zjbk+=eH(r z9n`Sro(qz`Y}3)a<wDTCB^Q2PLEDRFm!9smq(8TB^$xGZn@twQOgXvi{X<;_R@s^@ z*f-$wz%$P^%PX0CeaHciMqB#3=YDm!t8{*2ar>SdmVa@uv~RoSKL>8RusH1X+J;YQ zRzdY2_b&)8{Bn5QEM1_rz+OIP^vb}x`qM{TOXvKMdgN$<eeL*;`&Z`W&Rc%$_R#Sq zPjy=#d1ZC|k$q=$u-&wTmll0XPkoxl^!`Ij`aRN?9j6<+WF-aJLwbDF>rgw#%IZ<8 z*Pgz9boIykB6S`v;?X^KMtvP{WXgzC;XR*<q<eM8)#;n^;r(*~GjDud?*$mr?Eaz| z1=Vj}Hnod$c4&&tY0>g=%R@4Jwr&s3_gVCgd+TONubqGG?2Mid4sCV(Th!>w_dgyp zdC9tCm(AaOweUcdC1PXHnTa8#^j=0Y{A+E)yPCgu>da>=!aSY}xVQMg?8=NU>)-6y zysG=~mWT3Ygg0`H>%8{p`LYjuMr1e*`*4rZme_pq=MiGTvnST}KHb)RpwzLJVG6gF z^pVXD?buQo@6vcP`(;<B$iIy(`l8;dDW+847lw|WxcbawZ|5o1iQ<`qmtI?Qde#q3 zp5EcA-%$2_^gxe%aetr8aTHa};j@}idYh<!p`dfqO)FLu4g26m_NsM*TMeBY8FRv~ zkr1`MiF-}J(yh^RS8QtUe5dPIosy&Wy>k69o7v@7Y`3W++CCW9t;L4<w`v)tw~AWQ zy9W09&eQz;*u5Q&O>cEKrp>hKJ^j0n9CB&?LC0seRk|Pk>63ZI8MgG}ZeHz=pJ?`@ z-M`~IG0Ut?j&Gk(ai`zj#lz_>pXRal-jj^$dD7A&h9J{}dV8ZPCO8eA)~0Gk!q%Al zJKKaE?bl3LY1<nTS&=hl-c`TpTQ>N3B>&B0N{^R(_Uz2-_NJGmX{I!%mfZ`=`v@`% z!-mkxIbq8#OkMln%2U_%rh_+HroOQF!B;bHE&5j1WAl5}g;Cr5t`5tanq64_PS*W# z-WNvZ_Wf{K()nI#o_;sm-pPaw&0+1$v1~W9_k<}{pSM2Z-_)yI`POO227A7!x?1(k zz*V{NwnZmf@84OP_;dMtgDP#_8DHJWXnd=(@5#0qAr8k3rfolNnY7{>&0Nj!C9<d{ z{jcqxxt@`w+jVn`z1#kfdTFy>EpE2#+GoyAAGLc)aPaN9dsFLarnKH2FGgLNRr$`~ zK9v)W9osVRM4h7PcNRQ-KV>0}zh?NdW))Pwtku-Md&T|_UjOK3(3&2f?fJNK;DK{n zo~ompu`zh`zGnt}Gx|`Ys_nb(_WUx$QgQrp$DE8=?KkJ_dFIW814$7>zkC}uG`sY% zfR^+*@oVxP%<;Ya-S~jgr*_30?`Qjd_zV8Ycl<_GM6CI8K*7SI1rxWt5noU<dw$Dj zZ}7o=`(GJ&BD`Hc;gzD+pT6YtiSpTkvSVJ=tb*#lOxLfwx94WtZ$;f5_r!&}PKQjF zo?E(SOjYq4uYb5Rr|IU~^IY~9yn1}9fAwo({*<%hO7ipbZ6j{m?j{cuC!KwX!m4?E zt+wF_!b`y`zA}z13GDje+%_-vdr&ncqivYElgEpl5*;Fb-tfwtq&5AW)~uK}_|!}x zxq58ONp^AIizALqJ$G=)vf_51*Rp%Pwq4g>7T%uMu5U)GU99_0EcIsGPlGbfTz$F0 zx^Knzmwd9~J>v|^j_2HO-TL*{_Dg2Hwy6KeuV?LS-1*I#geOPLzcZ*Y{RBf!8EKEK z7VC5!=>c2&ao|`!cA+PJIPkyb)q5;@ho0N%%{wKcV|njIp57c{H<VESwO<Gx<?|MK z;*|bFU%ii_WqeQ|w>+fOG%9mwfl$PcFZN`A((CHvC1V(Sk+Wr<|LWEDqObDn9SV=T zpYl^AUr-7eM1Qo*_GdKm8*N~l$G0r@Ke5Mu?~Luw3-#YOthOs3bN_*H*POBaq2Z@V z{R}`mLq@1F(ZSY|q5q;f(|=p>(JHy38d}=y(JJ}fGK&B2uKaJPYop%(TYB>QoUuLJ zX#O)%i2fbn=e<{Y;uj_J{%^c`-w#(75G^L{r>!6IVIO(2|LCjj!*-93SLaU3K8~Nt zTSkT}KS2G1WwuhpL->Om`HeQzlS}Kd{CK1O@^*DsB^o4CeuXOtnv9(OpU9}bocL^y zCaVX1QXa<VdIbF0pzmLfsn7WV*ZB{}6K%r%2TCIE=xrfB1(Lr!(s+snVLu6w=$#np z(1%vE_ft}y_LaxSeu_w-_dlAgGBr={T}Vb>ktyFHVK~SiYJfho_y!=u^k`3t^kva! z06mx907!<dR~Snfq?-U`@h~F1j0_o8-ayNXPiU{|-B$JJi{3W?O7eT3WMn8R9H2t_ zZk6iP1GKmG_f(lHs05I{@_7Y*B}kqk^|;MLYC?FonwCbKnzAYS7H$ZT4-^1H`KX#u zV?X4c1zG@Mz)ooI0(SF_HNN5@<PHOU`SBW0kv=(m4#?p5YQhXw<c4t5$p*F(<Y<Zy z2SxxRfoLEGhy_{$_=Ohz>_un;L;~bAbqWut^s{vOjXT{Bpr5wOLqXpa76EikY8X%g zlmcZy1~3q41YLad=`6&6p--Hf^5rL=wgn&;2<QO=-~c!QI)Ls|eTQn_13v&qfGxmQ z;8WleU=6SeSPgsxECZGUi-5(z`@jNVAutD+3rqnf0+Z-1_XK3d1Fr#gU@R~Ocmbd% zg;78^fCjrekOCwDQ2-w3<Zl<fLFsEunpvX(`mS_1K;Q7vx4P{BnoVY)0}uiP0kf!u z<EVTBr~ys_^x@+%;78ynPz7uQ)&c8*wZO*!{kq}<U@5Q!m<mh--T+<)UIqF9K7cP! zA7}v37sXuw`d+&kd3^!GUw~hMYru8j25=Mj7T5r+0Nw>E0s0;~37{$dHZU2W@1m1| z?!W->Wq>2{-+<B@d<C$Br*%g8O(a9UDKVXp;h++5jtYR0xAB5B0bg;4)&z=LRH{}R zPtDZZC^+QXc7TFIGc*(+8MRMTlUF0(4xrVt0|*A>RRc=X>N{W`U<URAdw|gZ^??R} zWcz`GzyaV8O^n0H90ABM%}=dCnnvUtjpT7abD{(C)BsHz%@G<|a(X;a1JEp}-7(Um za_S`6qE1o=LTTDiRB5Cx1DAlG0UF8kz&U_Mheq`@a0)mB&<HmNNKTZt<APfJ7366K zSb?iRG(Zbyt-P%XLO?T&6p}OFa<-|(3qz`19&ys)R-2_1A%j#1K*`qaJAd_gYGAk% z8`wHLE*z#}P+~;M;KSxsX{Pv}9+jk`L_~>pFU5(rbU8i4{m!MbOrC!(3_qZj&IPjJ zeBHTJ-z_M0MU6}GV^%D`H{pSfT4@s=fsy2a=Tq@{e$jbjYl;QgUHs#fQ<v6WjD%K9 zcqD1j{C)x@LA^|sIip`$ijqi_z{NU@e|Em1?_208DJ<Fj?0)BM9+k2}6jeRRFP%5C z8@%oXqfTFkhg|5vy7STt(Z1S^O}D+x{D%gmd!R~ecm!2h&p*Fl#5Ws9FBpA~8{|F1 zp6n{T@l?plVO}h;eMER9{JF(lFB)U(;u!Kq!}dV~rZ_Ix9Zcm^im0k3^KTYL=(qX| z^<ph#IreRyanb0Th;mF3d%5+4y-8iVUVc=b!lzv{vgi2Hi$-0lz`wq@)7J{+hEO)- zcF+H@ZrE`*G(*Ep&VG2w7^__;ePYS(-EH-=^3WN08H3(vS4I0BiOuMdb~6AaQA$u( z@q3qyY%{OEQlFbH`>}02<#IH>!m(Xeq(;8#a!jmt6*YNa?&((+=(oxBqbW@Py5ylv z-t&qNliTtAZ?qeuH@~ftifa`9SEK(sL(~;M_iBvXF(W^J)ralo;<ae}0>Aw=qpy1N z(0+Wv(Xy32w>hCZG2yLgK>PAx*NnJPGznVTeaH4ai%0pr`b9e>oS1Tv6?_x47V*Q^ zQrQ|FdOg)wyU00t-m0{FFYZc{Ez0xg0H1Q*$WCzAd-eII>%G}E?tTN4(1o|W5yiat zpc~Q5UrKjn5qd<k8UF@+G{1w_CwZ%z(Z1SU*}_|GZl(_D<Ey%gEvaM%V_q{JFasMS zO>$*MHj%HqX=Ky+w>OQx+TGr?!^gus{8L8DzROD?fIooNd~UoI&6e@hTSm5p=OVw0 z&%9;yonKFRBwk%*d!u9SvS8GU498T#A<lRGX8EoezVIQK``tF8197*bnT6*gKZU<~ zJK8r1%cCx)?lZl=?V0KJ(KoQ$T3Kg(_}<&8EQtr+F|tEE;m%n0GT(S7mC4=X{&)RZ zB{$#A_0_K84sG5zYsvJiUOI;PC$GVN{PVlfzS>pWx2AXaYTw#^4behucyu@dMX?WS znvL@m$LhnYx0zoAZb^E}cnx|n&_nRaj+cWRBst3`-D{)M`SGvs_3+j1-8%o0?q{C+ z`fb#r=22@hH{Ca~w|Ub2XzY1o?i+nq_$x82?zd!j^3^#%>DWu^K1E|+zcGRt9H2Ne z&b-gJ`|hntFo$-@oSE0$Pi0GZ<bzafp0)?Z*4nk)@s7=Br!H8v2)bmh4G!ui%7&WY z-)-RNH@c_DB~jF67Q}Zxh>~MyWHd4}ncs{xELw>wO&VYAmTs@*)g#UwG{(w3f?*tg zl0Rd{Snb;G*_&D45At-1fdaM?@=?3Md*+D%W8Ig7qvWb`$q8vFV_{-PoPY?o{XJ<O zEV1>{1|rqceg+qgNFv6L)ky7iER|i6Ch1UCBK?hoyQDde4`<Oe$)yhaSXZxw^i>@e z#e5{A1B<K^)B1TSOwWwI+6~_yb&ki@4e@*vUQi6Ng&dUzL4iFlJuon%^oE{&!j4M2 z9FX@iFb`>N9Twmh8?D{^-Esd=^NmwJS@)>^Kxri^vV7@V5MS-)aMsV4dJO1NQVl)K zJBowSfa2K9-}HwXkJYXe@46EA)eEu1JHrs2Y>2pa>3IH`(P3o+u8c<s8VHZYvzM|K zq<lv$I$gwT;mQg5q;w9!^40G5_DS3w*2vF&k)FZ5`0%I*Azun~LO*O$0*LSQR`MGF zdql@~2CZ>$)jiT%Bo#p+R=ZLBg>XJ8<QH9sM+(}VW9zzhi(?#y{`{z<M%qYKwM)wL z-+yVyd8gn#j})|P%uBmkGfK>%vmTWMNOeWHrQLgO;n6IbO{$sqNFhZsLm@U3k;Vgx zJ*)m*(beDU`XdGHD)p&1%P!9|U*GVkWV}>Kwza$0JmGB9A(ih;c%<-w^f?sRX6Y#D zX_vR1+!Mprvgz9&>79~Xo#BIa2fF8HtIWe~_nTvYqLs}>yDjZqFS&nq-odu=pvKYc zm?mX7W6+|cX|TYiNlQU|wae2d&#w2`9B@yWOxOwWFe&{A1znv4X+>SuQ0JQ<O?3k` zCrIISS!d^g3CgFaYPo!%JknOQqNKZZSx=TNZNhQjn~CQFXT)tzu)Ss2tClQ!T8P13 z9wA(mY)`<m+0tqv?@OniV7Zv>y<AXh5gwr2P-|1mnMV@R8g`ebVic_^FX<H*sP~c9 z5P3^FL+aYC?DkhSuiWMqOOG7rJk9Ae$-f@@y+~?P59-fLL%}`lo#JmpmLP2)C8h6d zl5_!Fqs31uk2#J#$BZa$e!ioQsSl%q+D+xaj8@LQXtlqd|M;lthS5EPK!{rC{3I7Q zjHP<v+rIUem}TWFemJYS93^O1!e<Zv)MVXbD3MF>IQTCH=h2|3gT)q0H{8%!WdNkk z?##ocHLl&Z-&Hd<qDTD!hhUqIC)n1md23gx<q~;+(Qb%qx3EzXt?c~T9dhlSHcBGH z<0<Ugt#$Qgxa|%V#o=K-O3*H%YnQ!I60eMvc2!-w4z4x?bJ}Hg?ZP-pXp_axq+NH{ zu9V9q@@}SGkf%%NHoAq5BAR%ZL)smB?VdVH=y4n7v|IMtO?EOz$6TcFa%bhyLA%3_ z5^@`n({A8vx7<;p9?IHZn)va(H{E?}J5<XbSdbr<Y}(Cy?e;tD&?H8=_BR?mTYWL# zxa~_Xl&ELmV{OW#_OR_H$D3{zo6xzUKI&+nCu+CpQ4$#u9^VK3*Y5ml_v%rEXBP_D zV<rx{fJ0vUzX0`8{v|Sp60CzqkA@|5au}V9)i;4Fnn`)?tOMRhP?8qAvxv|XJeSeC zrg8DH+u6u^_m8Q%Z>oQw<J_)1J~1GY?>=kv>M*!qR8CQuwWt;Tu_WJ8LM}*SdoWKa z{AuPQ#d~16WtA72%0?AiO4s!EV4e<ZhBjngF%2F{TbWut%$p*m#w}S>X>MQUxn_C~ z*1<`9XjdAW$QreNT!XOiP|IRMhr<!YvfR<7d6t3#)3EZQGP!Oqc_~0yrY2<(kY28( z%!I#YD6o{6$o~?{uySjO#e_eQuw<7Nm5eH-zhcQXjj-mF<tcTGGfPS>5=&r>8rKq( zJhUyBmzbg=C09Sz)SYHIm4TROddExdq0GN)ZM!J0&6?!(LC)dtN`_cXC6-*vNc<tr z;PQe3*$-28QI5q_Tx2bjhDEYQk&iDcgV}#kB7GLlf_-aQhiWa(6fbQKXH7%@DHu@u zCz&ZqGIwA>L4O}dX#PWFP1q`=;sz|xTial$QT1;zOwn?%8p*p7<T5qM8xqA~L`i0` zN&4K({2R#G%)(MDv>xV7ak9fL9}T(%#kDw-ydjdQENdZI4<8~8i)W2ov8$3B_@#lo znaX41K}!=-A|_gs;c+AnbXWe710~vGlLBH`u&caWA(bh5GB*LkP8&I7L@?}Fiqh`B HY}S7PUFCBy delta 14457 zcmeHO33yaRw!YO#C%HhNvoB3d0)YUbLsmK=BsY<r5H{h3MVOb676Mry2}@uw9Tj&# zD4=w}g?)7a6by)fiY&5-;vgUs5K%zIVN^7{VSN9&buU3b^u4E}^S=44FMpppr%s(Z zb?Q{z+c&qL`_TH?I_qQc?XT6Wj_>Bt>dRhDAKm%(Vpshi*Nq%@@AJOnpLjE`xpVfb zYd#pl6e=Ih%a0s+?5tkL$ArSdVn<<lRh6s6HMXMQu8$#Wp(yT?pv^#Mpcm1TpiM#d zf(C#dMY~EoXcN#+ERs6r9gXd?6+#<?b3GNs4|EN9GCHxmvTAIFt8zGaGFDVj<#OCr zR=LVcQJRDQ8z>ncGtT9zC@xoa^BfjZv%y=MzU(8lwt&}rmzPeiDlVyf3Ox0G%%Ec` zD;-nE6;x%xFLLb_e2}Y!<>e)X1(mMuhRYKxTvb)YF8EVYTvnwh5Bo}ew?Ui1D7ub! zPca1BA)slMxTciBhO$vAxaS!3anLZd7r9&$9mSQ(SoEW5j|L?Nwxcugk%$R7<_C%{ z?!joM7&~~5C1iLcIFc4uI>wfi7b=4g5z-EXoTgnowyeCug}PytlnP2Jl^KC@6vfcH ztGKMlF?phr6(sv5^Np5}_Q}DL`zr>dxx4|MtXCA5V)Ye89};t`=2tASHDw`EdxEQa z3e7kG(<2}5Dk(t56e(w6g=XZc)K@}rRZg1hs;E}FhfAxjN(33c9)=z*?5mJdBnrlq zSCqltYmnp5ecmqR&w;@-+(qSMsw#@hM!1$kKm$%4S3G7MIuuvBroo<axuqO<cZ5Ro zbx$m)8s~tQW5yMfOhEgr+hh>O6;wLl?AYnx$xdFR)L+7{ctrZeMakj(K;fC2^JveI znnlsl*|DvpbDKeFlKG$%vdQoX=G-Gd;rET@{k*lJz%BPc$SI`RprjwvM%q(BA#%TN z&@G_k`=GXR^08<{d^@;RRA_*7OoW`CUS5QnqWmzA>iJpE*yywEWOuI)a@c;L6k}Iq zs>3y{O4-ZXTVrdkwU>jJx=PE7r@LGQ9i>)2C^_2~l$^-|Z3a57plp0G#$4`@?F&FD z2xfkmz8r~_lXO)UPA)DfQr=Ba6o2UL0;Lt-0E!i@asSC^xL*p~m4-rrA<r}9PEh2e zyS2eL14S0PuXd91bD(7S2q>k>4p16!t-*6p>hA_6y$PT}N)49JP{=V990va;W<c>W zQ)h?4uL7lk6VqfwW`f$lhl7&*HY^Vf{2(Yra3(0N)_n9M$D$CuFwiSq6eThSm6uQn z1w8~xd{9?uxYAWR#Z{pw%XouVY|Y3_>5gZXu`r<IUSUPS7+0mD>^9`yn#}Owpww<| z`=a6s{oL@+mhyqHM{X^|sBp;*uXH*hM|x8aN(+Ao6w^`NnW$h_xi_czWP5RU^D|+L z`rK4ndiZ+n#d+_=ue6BtvwO%U2=r3U(`}mNYlTm@Ww8uiXVcgNyuqfa=V1rVyBXh| zkjAV1G<6dCz%92Yuk~}X^}NAPvtEI$E59C<!8Bg&udzpXoxf(?jzQx2bz6qzq6hC1 zkj3o0IzUr<c`8b881UdM+NnMRt}i$%ul09YE_w1Ufmy6AuMX7MXkLfUQr>{i7kPS+ zrdqufWdM{Fo?~;eA-oQ<Gmzm7bbIUOdg6MMqI0zYPBxua2W#pk$gpe}VmF5PyAQt> zoTX+X{xmTkUTbrzcY{NUz$Z*f-2$#VI18`so95HVps6>qh7`SYMK?HUnPRbr*M(~8 zcZLQde^@u39;T^tv6#|4JYdIo9b^lzfYevv40hi(IN0@fs=>IH7<QpQnAe4C>@D7a z&j6lo*I>08pEbM=pWAtZ9g#tnch;@##&j~k^#sRwt=*~Cf}<6~<PehsyrHG0w#Q{m zj!xsbz|n-EkGQVobrG6+5i%q=WUN=353-ER>x-^F4UVRR_@MJ~aMam@!mHY`f2Afk z>qu~2`Hs*GRiH-kRyZ7F@A0}wO;xew$T8hb3RRTG?&H-_nz|D$q>XUcoR;f>{906& z+6Na!5_;+@{eZ!F5og^4E{!r!y^b0=AeS;L*c_G?_K)Bw^<<O}gQFNo%_cY>D09HU z!7QHMT2mi_jP&6krgjuug5(fY7Ao~+%u;!JjHZ@CMu9=hps^Ah`71Nw9B+uxtheEG z$>KBpGuWNHx{b!>^SU;g`aY!OvqcYK^Ki+*a<EhF4^HL+3vsfCd0ktL?d1*l4C3jr z8mr*dv6{LP7qx6y@a8#TPW3W43M?WO;#AwURFr{|!-ks%j(n8c?*(w=qqjbC2+ois za2C#D(tH{jbejQ=QVLDr4uiY>h7sFsrpeu**dpM_2y!LBsd8|X9oU{&fa8XyT*4N( zuxII-<mL!)WK+*7>nd=_99stakk`d&sum@~?a6Zx+DdRV3FvBRo?r&@OnZj<U#Q(F z#n_MeyrH9pY)*G*>IczgOmkwK%%9geG)rYGe$AGpzShdzEIF;6%!gOU<BY;>!B7~| z7oIKQ4e=UNd3q<!+AT&=2Js#C49kKTzAY?E-48*wKIZP8X+F3;=nn>a0g;#jj+zi^ z%<qW78FR-C1kF|-K^*~(7R-a^gyNV@gu{>wh6GWx(-F{+sIdz?JxOCBygEr!2emWT z1F6YY^9HnB;_1nn+N%AHB*aRM07t2XeHZCukMRa*?1GF&L%IcHv=q%cv;!o3re6k| z%NxKS1&`>r$xwacaEL<}NsMuDX-L)736McS`fgnaE`g4Dbq{JrD4=WQb)7YJJnl_s zEJZ&sSAgrPk4$NL1{}ozZGld;74BAU^i~VOQ5K;s-l;wdj`|^5;4XrrT=dkV+zIZ{ zw!}$}MgIeDNYm6OAft_o%^d8sd>zlHr)OEuL#qd$nVw<okf<oV^x7oU`slT7sL9sv zZpeDW1F1I`HQ9O$HQ6H~SyA%zzT;7ovdyRs&}9}JCbD$^YSPLg)TCZLYSLazswpc) zP4?J^n(V=_cDcH}eyGX53sIALADS{X&MCvJ>Su-v90kv+C!R3cyy+BUM|p#$shQY4 z6l{br)v3+~C(k+B-0y+wqX#c1*2xlhL$;>Q$Bhy#0Me)}wm&%9xL6Ks@n%?GV;SR} ztUph`U9(1ID9Rl?^Y#pDC2BqN+RLboqM8~FQ#3m6D&SEWukNO)>mj2=(a&@BJUC1a z{*g!aOhrLR+)ec)DgsBdlPB0Ba0n)B;b5>F$mG|$XQ|EM9l49$gM7IIoXmID1Cs?u z9rS}<eaq0)gQEuF#;iNq^t%zY5FGWBw-!slL7CbDoYs@z(&!*p?U)H^Nmqx0BQ2av zeoiX~H&mbeg&Va8aj&KaMEwjk#Dwg(ak5xm*IToeB9a5pBYD2fv!<z{-v=r_ptyeO zg(z;y=yp}H0Kov2TTyBc1&D?NRBlDdo?UO1l=zm4!ae$j)zEmj>(<A_2GS`uh+c@| z_KS{UeGG&_Z^xwcLX>P_DfB|r6UYYW4z!y=v6&SwpdUbms1+E5FCn^t!6Zew6~(ct zOx0T@r2(fIJW(=yx4{#oV7LL&s|Be1IL(!c_dtm{-bdOfKSjy#EM4pSlyTpDC-0Z< zQ$v;>HZ+J*k4Fsps6l6gQXxw1a{!9jJcFMPN>h6rphA@No*)M0W=i8N)}=a?F<oK^ zeu|R8Ck;KK<iIlqPn6o18az=NXgNUg6#x~Y#IFRX{~B6$608NN5G8&cF(@}vGPnVt z_GbYqL>a$6Fr>y4wavr<F9K9<Majw*fb_NkREUzi4j}n9fXb~XxxE7*y;lJ$pfzTN zzRW3mbxu+;yx-u7QpW=Ze={Y$gFrLjeWRTy>7Ov@Nl<d&w88%cl*(Udd{V5}SxK#^ ze*}=>j}19dGH?+f`KJK&`wXB$l;oF*K_N=|R{%PB8UWJ222deN`hTbS(-KfC2`r@b z14<5gLrzUThF%j;I<BHf_zP&wFE#KB``3JL1{8r-m=R5?wJ|fIByVHTSVMj*N}JhX z=yfvmh|&Su8I(j$gQn>;Us2LYib9kOXa;{Xr6uSNIXTkPXeUb5UIy)L&^{#Or9&e1 z+|C6@z4AbPK}Ud6Mii3P4=9a7<)3uTO40`omp1=*7cG80FY@=xmA<U^Aa1m9_Zji~ zDN1WO%h0=-{(iao{c?r0`2BMA`{nBY|K*Cli75Z4%hl$HQSW!>dCPLye*VO=eE#?{ zUw$3jTfEQmeBOJxFMoD<E<3~<z<mpD)QVhogs)wZ&)2N*<-RL(@lAU8%6vX-r7zzB z?p>~~%I7|-e0k}rTzbF>?qzTht8>|LUc5S=k6Z1_4}m+u!`9^U&^5k%=9*k~ith)v z51eCdE<4Su*XHwSYkm1?aP_?Xx_sVloiCrgE|;C<C&0ZAE^~b@JI5bdKY)MLB9JH8 zJXP~w!9{$~rU3SD^qE<Ng~dIETffmnov10JOPu*1VfwOcgC5Du{|@iv(JuwyY3Z-} z8MGH8<L*cwd3>d#x}dZ~8Ob+p31Gj`=jJP0XE0XGYwH4j?6amA-|z7)MR)RgOZjgq z?+6((L`?aWWi{^KV33~}1GlqHF#F$d#@~0Q=C=#=_Xjq{)o<s2<u$(LzzPb+t@%~M zPkTNcpmU;6tnt6q@5wNK%6`cH2gSx+Qg0X%?HYH<&z4dAe|G18Lf;yi{Xb<UKhK#O z{cJ|xe^C^A{rrLD1x3CoafwIo48Z?c{*6C_F5!PCw2^4u=4%cGJm<9l_AmXk7U1cz z@8&^!R>w67{g5&VGwm0xAm{Np=WNOTIIZ?yCPiwf~H6T-Uy74u0N=?u{&^fXQO zquY!BFl6<Hj2>-N81*v-rH3th0NAKe&Kg2`RPzczM#x2Sgr1jVPzB{2Kvir;<uQQD zM*zuidD5TlQrme$hGkGL0aWNI9_i7e!i50oe*!R)^d`$F7e3}4uD0bvuC}w%Gn@W= z;nmnt?NDnE9E8Fl;4p9mcpEqhyaP~Zjsfohe+G^N^st@6GZdhZ;Bk{O3>XfK1V#a) zdC)g;jtQuh0Hr_~P!3E4CIMBzWMB$Fcj-yI^qUZRpGSXEM6+oQv;c7TMsFU{8!gI6 z_<JWn4}j>QR}v5fL<97FtSLYL%^fxLz|jNn1iSzW5Ccx4N(=u1K#x;j2i^p>0WShC z0WSa>fla_VU_G!BSOu&GmIBLwCxNGc*}#LqL%{vOEL!;cP`MkR<GmV~4$K6m0zCm7 zJM@n=`gfYr14sbuKrlf6SfS^5^ysb&KuMAbWC1Y%Jxlx(bSMkY0A~S8Dq3pF*3&fV z2~_q1`+%*$%fJ?Zo{B#UYyj2*Yk)bxT;MU_Vc=e1AP@?K0f9ge&=&)D1L&b;Ti|oZ zJ^?NOUjknNUjtWZ*871v;5lG1@CZN;8+!nhx&n9<pa+ihu#p}#jsRZ)SW({tN?WGZ zzz)zH$mfAB1U@z7q2vL|i=Ro_r0e8lo^rJ%tKVFEZLwz!MdSeR7C=7v0<i$e?lh<= ze;a&P;1F;SXagJujsT|tN*&q|8t@Kq6rh!*R6Yri>=^K8;630tB>?3atp*wX0HA?s zK*|+zj+X5VU^<bFIt`Enn2yjYlG6_WwA5#Tn<qwk)J~H;1JERC0<m-kTreUt+Hc`} z)`7B2iNZ0y@8ID};R8p<`>@fS<DK!zkS_e3g|T!I%~&4mF3K5;WBtWq#-iD9v4ezb zL{ke?wu&|umS^7!OHE+uv8yYe*%sk(kA;m+itikshQNr~&`YJ5zyWv60LSd2>G!UO zLSlSke2P4@u(jPidQ3aB_Un4tH6b2zo*_=5t9>t)IsgirJPxjjXf|xV(G~mTq-f#6 z!YrRyL<bMnk2%Ct50=d4iuI7%pR~&N^d8<>XFK)5xmkfMD-$zA*&^zp(D@B0Ag|qh zS}5}ew%s_16cXYSq3{7({LwP<1)pKA;`>Sh$=MWYx@;Bko-EYv;ic~b_p)!x5<K^X zmj<#9x@lMx1)hjYw3q>6kB6QOmZl8pXl-+N)|^JYEb%-PEd9L10Z(?g^H3;~GuE+v zC)78MJ8Qe)t@#pl@IPAh*!ldsb1;2pd{Sb3g7P;nao7rH%on7GolG6oZ|JvCdhkg> zc}g%ZII}@a@<Cv>hyh+K*}mHwFD_!JMZ@0evaT$zUN@LP>KnvTZx$>zdoicwvXA)8 zi-nrhuptI}vv4NO-qeI{Vc%zi#i!m3??{Q(82P3qI5HNt^l`rMVG-;V;j6+oRphAf zG**lRv1cPyC~U5~FHWr(es;AnBjm(DF&7G{<~zEX{U=sO%zCXW6tHy2s6_KE-Y$u` zL&9t8H=qTfpta-Tw2H)CF5;T7yj1g*<T*<>4ZS||$WZ8|Ko3zcUtV7PM9iyseDzmo zLBJ^?4~t1bEKuxj!tQ8kz9JpAZ=QO3)(e!-$w+8q*k7B8gr*EvBQd5aX4X`UY|Rop zVK`8{MS@829TA5}Zidw`UlK2CNdGo({9wE8M`s#sw5V-{kc<^`TO%aq%j!{6`o2&R z*moE765`WoJhwOky;Sox_n{}w#`#C)PS+KZXhOyd@9q(7$*G>-JUtODsWK#UM1(I~ zd0Y(g#Yh{)1md=fr-{?s!tCbD@M()23!-25-)~`eCtxqYtW<z{WFXZ2M09hINRiPT zHU^1u;%114h)Whbh|3bko8zo7Usdn4I&0U*vkGVGj_T?9hR|BDT2>}@x4=T_^A8qn zY{-&o(ana$2@vaTFw|WX_+cGR*jS|9eD(bC1=(*LdwE!>KDp$ZRx8$zg|r@kvo0K= zGG9n9crAKQugAl_hIUGPGR;P}X_yWZ3;bA$<#J1Lz>oE_n=hj`xtJfJJ@e=_bV3*E zG+iY6!=G6q*B{f&6E)ycmtb?zS$K5Vy0`m$_4Ii}%sA?PBxjJDe&P@riW8ssW9ExQ zY5)RdzL@?<(SnVyoZOF%isg(?#X&PxJQ={kTAFXLXGHcMJ$-)PTsW2}GxhV<;++8G zpdQ0OFTIz(P>ZqCwg<vlnO&*oOY8%lJv#OC<6)`#+~6)wNnG-9@o=w?9=7b-tnXrU z<NB;$7nwoGNb~*nPn$)BHNB@zQe#;3eG=C?9@inn{l=Tq+y4@LYTAn{s-OrvG?7(e zCoHiY;zST!dQ<oYgB%e#!3aXVm>Z0?(PAwLw~N!n8CiB#_=T{SEZ5qLeIb~omxu^w zi5@AP`-x7WSON2;_<D=?nY!@-i!dNX4tr%Uj%iw(ORG}uf755~1>I+T*Y*=LL)j6w zSLB6Z@GP-73{y1Up09PRIQ_-aAx_=%Wb!;;93h2*j`CqZ;m23{-Pxn!sIH*rvTiR- zy0+N<6CTlkVQhi;JRJ7S*YcNrIz6uH&MzN;JvzNG;EhdDZod%@@rs>wRL{ms9~|O} z9hP!L)0VJgz7`*P?ONjC1D#!-42NJ^d{TlkL1<7&HD9DpxcyIm*y`27(x_m*YVRA; z{o+&4P1)PnvR2F{+vdCZ#dTfRq<EEn+^BFw?1ln6Cr*)``ELLEp2edowAdw$dd)<O z2>4+B7@&iHyJYsrxuuN?=FbI6{&U%Pf1X#D)7X+HMxm>{0NanQ(Vr};`rA_N>h?y3 zN-+-#?0&JH^vvHU>^?tlO+ZMmZyNPBh<fs27Yqf!kdI$h+{<ji!A1r1HxHfr9o({X z{)^KaTf&9oHrO_QLJ-uv&xkSOj(5RorQrSv24{+b+i<v>za>~?bLS+Fdaj3_9;vu` zL*aR{VE)M9qtCX5?~1xE^QSXeSSbDwgNy1*U4%Q5g|;+*T(CCc^uG5t^|)$b#+{mS zrHeQm$&y4-B=c`#%a9jNqZ^Y~EN}hZ#gJKK2R9261ESzUcbq4*C7y2M?ie@ARY-Rp zDL6$Elx5=HDCF8ev6aY3@o5wrdy7L!-&?p|aZwtLxGUm;XsCON?L_*DFCa`cf8CJz z<*tnfY^mcA2*inw8E@R+HplRu!;*#^@>;hCEyl%ZxX5e;3;tqKtHwYW-iP8&VJE~) zhRJUJxMAMb*IzGgmH1#|ct!|cTt1C%N%Cr(EHA3bMhF6;5CVL=ao=@9+ceaXcHYJ+ z#24tLn&DK1A1%hA9MLfbH(cg#E1sJ2ilg`)YlS`=c>TjwvmELo7R0dbtxY>WzKYUK z(KiyeS3#mv8y4qpc3R_y8(@?RexgG=mMtc>V+rBTrt*IJzO>YXY+Cbwg!)URV?)HY zb_h<=cwCAD+q1B(o!hejFST%TnWL(DqN{RipH3_=MNFK4qp7@z`EFe|hGqNU7a!Vu KTf%%>`~L=uQEsLH diff --git a/drizzle.config.js b/drizzle.config.js new file mode 100644 index 0000000..967446f --- /dev/null +++ b/drizzle.config.js @@ -0,0 +1,4 @@ +export default { + schema: './src/db/postgres/schemas/*', + out: './drizzle', +}; diff --git a/fly.toml b/fly.toml index aa29732..e10c46f 100644 --- a/fly.toml +++ b/fly.toml @@ -1,17 +1,17 @@ -# fly.toml app configuration file generated for kbbi-bot on 2023-09-26T17:57:06+07:00 +# fly.toml app configuration file generated for kbbi-bot-v3 on 2023-09-26T19:48:53+07:00 # # See https://fly.io/docs/reference/configuration/ for information about how to use this file. # -app = "kbbi-bot" +app = "kbbi-bot-v3" primary_region = "sin" [build] [http_service] - internal_port = 3000 - force_https = true - auto_stop_machines = true - auto_start_machines = true - min_machines_running = 0 - processes = ["app"] +internal_port = 8080 +force_https = true +auto_stop_machines = false +auto_start_machines = true +min_machines_running = 0 +processes = ["app"] diff --git a/package.json b/package.json index 642a7c8..654c80e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "format": "prettier -w ./src/", "lint": "tsc --noEmit && eslint --fix src/", "build": "tsc", - "start": "bun run ./src/main.ts", + "start": "bun run dist/main.js", "generate": "drizzle-kit generate:pg" }, "keywords": [], @@ -23,8 +23,10 @@ "telegraf": "^4.14.0" }, "devDependencies": { + "@flydotio/dockerfile": "latest", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", + "bun-types": "^1.0.3", "drizzle-kit": "^0.19.13", "eslint": "^8.50.0", "prettier": "^3.0.3", diff --git a/src/Fetcher.ts b/src/Fetcher.ts index 83beada..d84c8f2 100644 --- a/src/Fetcher.ts +++ b/src/Fetcher.ts @@ -1,4 +1,4 @@ -import config from './config/config' +import config from './config/config.js' // fetcher class export class Fetcher { diff --git a/src/Scraper.ts b/src/Scraper.ts index 51785ff..1e0ef0d 100644 --- a/src/Scraper.ts +++ b/src/Scraper.ts @@ -3,7 +3,7 @@ import { load, CheerioAPI } from 'cheerio' // import pretty from 'pretty' // interface -import { IPengertian } from './interfaces/result.interface' +import { IPengertian } from './interfaces/result.interface.js' // scraper class export class Scraper { @@ -30,7 +30,7 @@ export class Scraper { // get prakategorial this.prakategorial ??= this.$( - 'font[title="prakategorial: kata tidak dipakai dalam bentuk dasarnya"]' + 'font[title="prakategorial: kata tidak dipakai dalam bentuk dasarnya"]', ) .next() .text() as string diff --git a/src/app.ts b/src/app.ts index b9e9b61..1bd449a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,18 +1,13 @@ -import { format, isFuture } from 'date-fns' -import { id } from 'date-fns/locale' import { eq, sql } from 'drizzle-orm' -import { HttpsProxyAgent } from 'https-proxy-agent' -import { Context, Markup, Telegraf } from 'telegraf' -import { InlineKeyboardButton } from 'telegraf/types' - -import { Fetcher } from './Fetcher' -import { Scraper } from './Scraper' -import { blackList } from './blacklist/blacklist' -import config from './config/config' -import { db } from './db/postgres' -import { User, users } from './db/postgres/schemas/user.schema' -import { CallbackQuery } from './interfaces/callback-query.interface' -import { IResult } from './interfaces/result.interface' +import { Context, Telegraf } from 'telegraf' + +import { Fetcher } from './Fetcher.js' +import { Scraper } from './Scraper.js' +import config from './config/config.js' +import { db } from './db/postgres/index.js' +import { User, users } from './db/postgres/schemas/user.schema.js' +import { CallbackQuery } from './interfaces/callback-query.interface.js' +import { IResult } from './interfaces/result.interface.js' interface MyContext extends Context { user: User @@ -81,12 +76,12 @@ export default class App { return this.result } - private createUrlButton(keyword: string) { - return Markup.button.url( - `📕 ${keyword.toLowerCase()}`, - `https://kbbi.kemdikbud.go.id/entri/${keyword.toLowerCase()}`, - ) - } + // private createUrlButton(keyword: string) { + // return Markup.button.url( + // `📕 ${keyword.toLowerCase()}`, + // `https://kbbi.kemdikbud.go.id/entri/${keyword.toLowerCase()}`, + // ) + // } // private createReportButton(keyword: string) { // return Markup.button.callback( @@ -95,32 +90,32 @@ export default class App { // ) // } - private createInlineKeyboard( - // reportBtn: InlineKeyboardButton.CallbackButton, - urlBtn: InlineKeyboardButton.UrlButton, - ) { - return Markup.inlineKeyboard([/* reportBtn, */ urlBtn]) - } + // private createInlineKeyboard( + // // reportBtn: InlineKeyboardButton.CallbackButton, + // urlBtn: InlineKeyboardButton.UrlButton, + // ) { + // return Markup.inlineKeyboard([/* reportBtn, */ urlBtn]) + // } - checkBlackList(ctx: Context, next: () => Promise<void>) { - const username = ctx.message?.from.username - const result = blackList.find((value) => value.username === username) - if (!result) { - next() - } else { - if (isFuture(result.until)) { - ctx.replyWithMarkdown( - `*Anda telah dibanned dari bot ini!* -Alasan: ${result.reason} - -Akses Anda akan dipulihkan pada: -*${format(result.until, 'EEEE, d MMMM yyyy HH:mm', { locale: id })}*`, - ) - } else { - next() - } - } - } + // checkBlackList(ctx: Context, next: () => Promise<void>) { + // const username = ctx.message?.from.username + // const result = blackList.find((value) => value.username === username) + // if (!result) { + // next() + // } else { + // if (isFuture(result.until)) { + // ctx.replyWithMarkdown( + // `*Anda telah dibanned dari bot ini!* + // Alasan: ${result.reason} + // + // Akses Anda akan dipulihkan pada: + // *${format(result.until, 'EEEE, d MMMM yyyy HH:mm', { locale: id })}*`, + // ) + // } else { + // next() + // } + // } + // } async main(ctx: MyContext, keyword: string) { const html = await this.fetchData(keyword) diff --git a/src/blacklist/blacklist.ts b/src/blacklist/blacklist.ts deleted file mode 100644 index 68fb8f0..0000000 --- a/src/blacklist/blacklist.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const blackList = [ - { - username: 'AzizNaufal', - reason: 'Spam report', - until: new Date('2022-03-22T09:00:00+07:00'), - }, - /*{ - username: 'tfkhdyt', - reason: 'Spam report', - until: new Date('2022-03-22T06:00:00+07:00'), - },*/ -] diff --git a/src/main.ts b/src/main.ts index 4678565..95b41c4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,10 +3,10 @@ import { drizzle } from 'drizzle-orm/postgres-js' import { migrate } from 'drizzle-orm/postgres-js/migrator' import { message } from 'telegraf/filters' -import App from './app' -import config from './config/config' -import { db, migrationClient } from './db/postgres' -import { users } from './db/postgres/schemas/user.schema' +import App from './app.js' +import config from './config/config.js' +import { db, migrationClient } from './db/postgres/index.js' +import { users } from './db/postgres/schemas/user.schema.js' await migrate(drizzle(migrationClient), { migrationsFolder: 'drizzle' }) @@ -15,7 +15,6 @@ const app = new App(config.botToken) const bot = app.bot // middleware -bot.use(app.checkBlackList) bot.use(async (ctx, next) => { try { const userId = ctx.from?.id diff --git a/tsconfig.json b/tsconfig.json index 7550e50..94f71d8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,54 +1,56 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ + /* Visit https://aka.ms/tsconfig to read more about this file */ /* Projects */ - // "incremental": true, /* Enable incremental compilation */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "lib": [ - "dom" - ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "target": "ES2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "NodeNext", - "rootDir": "./src", /* Specify the root folder within your source files. */ - "moduleResolution": "Bundler", /* Specify how TypeScript looks up a file from a given module specifier. */ + "module": "ES2022" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + "types": [ + "bun-types" + ] /* Specify type package names to be included without being referenced in a source file. */, // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ /* Emit */ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ @@ -56,46 +58,40 @@ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": [ - "./src" - ], - "exclude": [ - "./node_modules" - ] + } }