From 20d74610ce5f3731acdd41ac8566baf0c6625b2a Mon Sep 17 00:00:00 2001 From: Alexander Mahr Date: Sun, 29 Sep 2024 09:05:27 +0200 Subject: [PATCH] create websocket-docket test repo --- LICENSE | 50 +++++++++++++++++++++++++ README.md | 81 +++++++++++++++++++++++++++++++++++++++++ client/Dockerfile | 5 +++ client/entrypoint.py | 21 +++++++++++ client/entrypoint.sh | 3 ++ compose.yml | 19 ++++++++++ images/screenshot1.png | Bin 0 -> 41853 bytes server/Dockerfile | 5 +++ server/entrypoint.py | 23 ++++++++++++ server/entrypoint.sh | 5 +++ 10 files changed, 212 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 client/Dockerfile create mode 100755 client/entrypoint.py create mode 100755 client/entrypoint.sh create mode 100644 compose.yml create mode 100644 images/screenshot1.png create mode 100644 server/Dockerfile create mode 100755 server/entrypoint.py create mode 100755 server/entrypoint.sh diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c03b4bc --- /dev/null +++ b/LICENSE @@ -0,0 +1,50 @@ +Copyright (c) Aymeric Augustin and contributors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Modifications are +Copyright (c) Alexander Mahr 2024 Berlin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.DAMAGE diff --git a/README.md b/README.md new file mode 100644 index 0000000..e22fa73 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# A compose containerized test of websockets + +* based on containers +* can run rootless, in docker, nerdctl or podman +* uses alpine:edge +* uses python3 py3-websockets (i.e. version 13+) + +## Adopted code + +[https://websockets.readthedocs.io/en/13.1/howto/quickstart.html](https://websockets.readthedocs.io/en/13.1/howto/quickstart.html) + +## Usage + +clone the repo +``` +docker compose up +``` + +![Screenshot](images/screenshot1.png "Screenshot" ) + +## Copyright and LICENSES + +* client/entrypoint.py and server/entrypoint.py are copyrighted and licensed under +``` +Copyright (c) Aymeric Augustin and contributors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` +* all modificatios are Copyright 2024 Alexander Mahr and licensed as: +``` +Copyright (c) Alexander Mahr Berlin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +``` + + + diff --git a/client/Dockerfile b/client/Dockerfile new file mode 100644 index 0000000..2c7dbb9 --- /dev/null +++ b/client/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:edge +RUN apk update && apk add py3-websockets +COPY --chmod=0555 entrypoint.sh /entrypoint.sh +COPY --chmod=0555 entrypoint.py /entrypoint.py +ENTRYPOINT ["/entrypoint.sh"] diff --git a/client/entrypoint.py b/client/entrypoint.py new file mode 100755 index 0000000..22b5a48 --- /dev/null +++ b/client/entrypoint.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import asyncio +import os + +from websockets.asyncio.client import connect + +async def hello(): + uri = "ws://server:1080" + async with connect(uri) as websocket: + print(f"Get username via:\n name = os.environ.get('USER')") + name = os.environ.get('USER') + # name = name == NULL ? std::string() : std::string(name); + await websocket.send(name) + print(f">>> {name}") + + greeting = await websocket.recv() + print(f"<<< {greeting}") + +if __name__ == "__main__": + asyncio.run(hello()) diff --git a/client/entrypoint.sh b/client/entrypoint.sh new file mode 100755 index 0000000..e8651af --- /dev/null +++ b/client/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /entrypoint.py diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..41c95a4 --- /dev/null +++ b/compose.yml @@ -0,0 +1,19 @@ +services: + # the server + server: + stop_grace_period: 1s + environment: + PYTHONUNBUFFERED: true + build: 'server' + expose: + - 1080 + # the client + client: + stop_grace_period: 1s + environment: + USER: ${USER} + build: 'client' + restart: on-failure + depends_on: + server: + condition: service_started diff --git a/images/screenshot1.png b/images/screenshot1.png new file mode 100644 index 0000000000000000000000000000000000000000..de9b08517f199dbac66289b6a1c1fe4a0a41fbe8 GIT binary patch literal 41853 zcmd43RajhIoBml;;T{O?5F|iw*TMrKxI4k!-3kv*fm;hYQuy;DsdB^SRQ7R8>{CG%k+|e>hM#YDuW}t(5UAUFTP+s>T;H&|6vD zIUccEYpAPTr^F@?&d~S&IRm3u63l9ToA%=c}q%u04lQSdx2t|I}2%;4KA*hufbIiPbcZ zp4KWO)!p6ko41SVOj5ZCL+YroK!33N>Q_pY_Dbv*yLNrz0g;@hywn>Rw|& zcdI@VUkXJ$m9oj=dLY_vl9bpVEA7(E#4?%)rv8B~5GF1jF0$cV841x^1eU_lLVeEe zLUsTaVeFy3ns5{T`G(Wz=!0IkT`AW0F^Riipgjl2f}-7IDz`DAbW&sI&B`&%s!uIdS%A3OYdLSfz6qWVRToQK<>j|uq0Eo; z}+CMCcuiL!XLU0;(#0QSI?;Z#XpBwR#=1w=byA_6yt=?y6LGzi-n-yBcA@5WzM%jbp(whk` zV)bd`CXQ!RuEKsoQbi=%E=^~c(N)HupuNp!z$b@7@5)P#0Ech`_am>`m+}<2 z2-ufDlWD3mFBJm-DuQ1l-nSa`tPdXNogG~*t0fd_$f5jG zbozQT2^i(u^zumRCl^r-Toezw2-Ijj;xiD3m03`#Swv{(%h|QV!kL7RBr$y0piAv7~AJzfM zuSM}z{%&kNic!-Jr}kF4gS)X6ONnUej*<6K1gqal^Q^4@?2d`0yPVTYm8FkgrtU#!CEEGo= zHIF<&B_Ep48m1dy=KrEYaNfF@o!NeX_1I7)<~KSTRZMmwgs=A)(xKzSV5TnRx=^g_ z)l~(1z6qTmYP`Pr#;IsH9<~QJEpx9X*7{>%Gepl1dU)``|8+nLy}dF_*f+LaVPa+y z(teE#RSY>T1JU_3hi}W3?V}vroDSTBoy)VptO#ZyL2WWM-sKRqgI*>il*TL5SVP1f zP6DoKh7SddgTCMFJ$7c4caBYEE)(x8o>!Sa+@v21LWkVQo9St(H!_8zY_xpPwI1?<##Z-qtiV#Bn|=T}><-NEe(RcFM!fr(~zNy=nCt%^w_3 zk52&UUY-uRREuy0((+gpoClfAzps>N??(a7O z{j*qB(2JSCG_h!#fQzkBd%9SJw*WZk%X@GK#sEAy_So~7*YyKyR`$!GbWr_g z)ExhBP+%6^;-MR$s+)or!yWz>6u3FMgAIkdCHe}_=5M@425p(YWF$xY3b&8Zkid=b zw;Xf;i+p2Y&~Xi27v&nd0!)p2W@|oF1OS#Y5U>ZHS}8*81dAcl&V=?z-{&mBkIg* z3sN}P7V*X+Eqy0_7M7la?m)hG<8YJX@l4!`&wFY>9oCS>%5r)CR!oGo#(Hp~9!dt# z?r7o=!8!%*0#Ccup8q4N}|41`93X(wr+iDFC) zinvC1>IX&jz}f`mz+kNDe3C##xCVF|;MfD_0Qf^WFN-yxcz?#VBW08K1~3XhVsi`h ze>#qAea(_63mX7NNJcP+;op3OO2ef2g8@)wD2Y9StF(ChHue;t9sr=yeiwjN0yu|^ zgM|QVNcM>C)G+O%T@|3ztxycb9Ssq}gr&{{p@^k66Y7P& zC&x%ee9M)P1v-Vp9BVLRRR5<(Sjg^V*NE5fSpfD}`U3zSNbapVJM^%tfF?m5s%$`) zMbX9vex9>~{Q*x8IRlr)b5z5SR$6Mh}uLG1pk26!xJ8mQtxQF2%f^hdZuP&|Yg zG8?WJVF6bF0-yqL#}MdX%*jdVzSuVd<^aU7a-bK0C4@|YJO-$<_HGP`2Ua$*K@ZIu zF9y>bqzo}2ul$epI;&wU8Hr67H*5g_{gI#ma@4N~xS&t|2~cbUBo;Cj)gL^#6>se0 z8yxWg~98j z1|!fRbo$pKFMkdH872z2^u&mZL=% z;+4E3vtL=)Idm+`E`3w5N$RJ)=Ldl|gpU!S*R*>ZFSx{u!L(xKQ*ZWPgda>zZM+vg z9JCJ^uc41!i4~u{Ifcn%e7|>dmCE5+(OURZl1N0_bmECfq)#rWJ^moekwAg(TCb}) zNix0ddv?Dy5*h8U24NuV0F3a5=7C%uLE7?^sk^|UiK2UO;T8VAVE4+>?-DO{A5}G1 zXtsr4-^HqbVh9u!`eqDP2C@hho{%4Qm)eCP1@oL=vt^+EBHlWwoNnn0G79LQoUKjrsn1lYKm5^QRzRdd<*EF(K+txJRuj zA141SbD`}->VCxgksS>=5zZm!?PK9c_Y-gs)+sg=XbP0xi0}jQoq%!I#m1T{ia_pw zBgm61`sFVbl9mv+UwY{aM7OeE?DM#|1(hkkH3j!w7UmESqfX7MHjWyVXUw@VZY1#V z=HGnBK0xTT(ZjM~v-uGb>@Q;@=iR^Zp}C9w4gfvov)Q>G&=uDvAY#16(AA-(ysjS@ z8rhlzjO9fP@tkzDmNpUWTNY#q?076)O+Lf~=c`X!WgVSete1OEd*pF>UUb_r<9uqn z7##bFp5eP%*@hjmkg{7?NnaIrtiw|=`y%4stry5hl_~7@xu*k;NbfpxkG;F8nx?*K z&7}L7tZt>><{B-{XuaOoe0G}ZfL7IkEo zErKV|?&Nzi66b)0MCXDsO3;?cf61ba9g7!X8Y0E*{#JLmNe4 zZDu)^p`4JJ;eIn%m;>YB@p=AZ?E$aPvOM-BwyV!Wj-O+$tUTL$TX;2=pB@aUXQ15e zy6eqs*ChGP>&x9@MQ68{L~_;49&FDdoL*9``fFz;9KbO|)vT>P;?c?~dU?~3i$wLd zaiD(Wcm)TOS@FcUNvkeJc3 zj%$wzsK81_m;>pM<7iN>sD~iP!b<|-!33B{k1(4NcIxmnpeFzo7^fe|^g1QFM1`Xf z$K4_}A!|_2bck)>4{S>Sc2OCDhY-Rw0fizKjHg8l35{9=E|`5Nfhj_szm4UjGER@- z&3DUOYDKn-TFMufvs=>)<>Nh0ujBYhz-|?)!$a=EqDRz0R}&F|Oo zEAu6a37FaJmOUMHk~nNdEpX+25jI=?*g^8rN~E7?@64!M!nvTtR)@YV?DtsnrN+d~ z$aOccH`BY|nkLWF`F=X+OX^Q_8P{sHRN-b{SM`dlz`*>Q-!76e5$!^;8aFCG`69I% z4KqBROQE1==0P4plE@DvBsQ0FOG7*COpF<4$u8};n$;PrE$)}Ho=Do0bG~ElH*;AH zX?ChGGOe>ESHjWbuae-$ zh6U~`Y$rq%iggOt1QP*6K@O`18U(8wytl{y3IL2@$ve_eKEp2n9{jPO*kj5+BA$<0 zTmsP8Jwe?556}}pmjjkem(QPEh7P|-?s%@JE>7zeVr!;A5Tr0(l7;!gY1?$gORwqOcyiuTn#0dw+{1Q0PwGw0VSbS;=h(KOz^9iM zc(B~BYEk|(PaFnNexmde8>heMkg^MP(ICYq^t5;|Hu~mXx1RiAM=a2nFpq*w%;)z0 z(@$EwdFc@x)9QnZI9-h_K6krDOu`zIPBR&-$yu!(dcl1xwolCf#QM*9M_=@|XVh?Byy8x7i%4CJC+sF*s&}B6>NiL-2d=D-)A$pE0o@Em+pL5py zouJ(~R$F=W?kAn_p6`!a68+eeZ`1}B2bSzYU1d$=`2|c~;XUh)9?lDsJGIuW=$?*^ zqD|=WlKSdITiN|6ZzPB_5cxRyXkv^kZC2_Wn4Be?73`aSOrq`u1O>?!Il0Ag0dh|& zk8cN-eYtcX%Y|Ot2@CmW(%!v%h)SKdzrwmv^g=_ak>fwu4DSPpbhR}#K{#=G+o@=y zIR06_zZF~!6`P_*wp+gUPl^;`59fWCx-X|*QwLv)g>`S`qtrs`w6rLIv$^&E2EO`6}*qXhvih!Y#w zLw%Z;w~^QxhDKI8n!WXv2ZPZ=9DBB3r>w%ZIE+YOc{Lla-h?m>4i*OG*3}UPTM`yD z_&kpJ3~6Hf3Ubl!Xny%3Qkd!kae#1t`Sl8w+^yzfpL+gU3>H{qkN4^DedJ_Si zG6CA8*w)!EmDGAsSoP1^%E44c0B2Q2IYre#++l5sAq*FGCs=P7fEGFCn0H&Oq;9&Q z6AxNKS)KwVcsw^4jPbazad40Hgy;yJ{JS zwTH-b!lT02ke3u*GJ+8mpezQ!F%ku=Ogc$+QqB1H0dVG^d5Cdl792u`Pc{pBajX9x z7-tN`0%r+1fjE1JkLPO6=W=L|f@6Z!OZn;IZ5hh=~P%Sfrtsq`vKU7z5B+XRt|ks^l^ zlbc&rCHGdhfW?>1IHr-I@{p9aHa<89_Adu6V3rBTC_`gmHOs4DOPE^1Cq?O7E-C@S5H|E!%KXQJ{m>I2U%q znX^wV#~4n%6KX(k2Sr2EahI6@dN8{F;_s;j7!BUV!Z0S>iki)<$XM8Ze=mx-2_%7u z4*DAHh<|3$)o`6J&8Qj;h^_Jq*Ja8A@^vuLhFe3(9f5o^ z^1~G*6Y>bW4wVFs!K#K59=Wkfb}!dI;Lwvkaay_#Zs$lQzh^6%fUEB!Rk1|HKe3%M zHxJLxEiWt>zz9e>L&_Z#lytw|=o8OR6lh4g)O@MD);$#vs#GE+nbXzPY5tneNWjy4 zZJ;jD@*+MZ%R^rzgZ7-mpx^#G!5iipaSrz={^5#xofWSv8ujJ)gjjR;RHj62NoJEn7hm}&ha z6bFobYU`bjU>@eT9j-hxt~S{6%Mpxd-p?$GxCF=1Q=JM^0FwKC`GNs$XhViPLF>V| z6?>)MU_w*`mtHQ`ZLRW0R_eO$2crD?D}Z4h|Kp>_^nYA~J-|JyagBX*xIt)uGo z!f7qR&=}Gp<+o=Sd+w&}w>^mZ!k$0+&QE{vx9&dvX|bfx{c%nHz8*KL`b2_SK69_} z(aX@lSSz9wKcc)x)<*~-3 zmxX1!p#a{+=ZXgB4@|9*#U^9(Q+4{~8-95ll?vq&r*WC@kR&fFKeRa0hsn?OE`53# z91Xv^pTZtT77gJ;WN;RoDN*Ie&Y)qjzr3syo$#kx&u*`}lq`GlG5)Ng;rzu@r1pE; zv;uQJSL4rFvai`pft}gxqc4r4XL#+J+P){QXEN5F1ll=H%*o$6QVt(EZeBJ3Y-k zPic&s2;6_yHL*z>D%EmMdQ$C;&P1Dl7z7vgjg?>;a+sJ~5i%+3V*nT$bBHWv#bC5S zal!4`U%&2oM?_JiR0g*)V7*+=z?uNXfN~kw{n1T~2KFUy*`wRT;euwMf)EsZP#M&9 z4V4_W)87M1v&NSNbe9>RCFzIh1qFlA$zi{Aw~oH*6dpKtpcRB_1CHw@!^7x=&_fRt z=)a~Go-_0Z<3r|)b!5ymKZkNX7LVm4;Z z>)lizDXP>}@}EeSq@;5d$|iL?{ddJQ=W}n{eFYm>j;2zliQ6rx1Rsgrr#(iGb?0i> zmTmfq?p2-R^DZ7UceJJ-B#a(Y_4`0sC?g+k?mmQ!m+1Q5H%?!$x6($Du=V#T2KF%z z49P}d%oAaCxd&%4?oI^JGQ;^?{y7#Zgo$-}(MyGjGP4TR>IqBosjRM6b9H!L^9aXW zcRreNyV=cq&`T5B?PiCnm!+Xq5eRf>iY3r84#|ZHR++w>6@+l3zz3^dEctB(M2-s? zAz-9zOl2siWPT$iv8NPC#k0!HTxc0Rj`4^~l#6=sl=r4qmY4bPSD%7IaKZ~~65QO% zKh>rQ_Ku3_CTC)`JtU>)EA3PHGrp-BRuNA(R!gl#LhxHEs^CsSyX1V>1^P%|dCkfk6@_Tb$XeWAvF84Bn{0m1Jr*A~U)3R9T zYDvXfL+8@cQd~}o$_EG0H8_q=+<9ZfZP9TnbXyW^mkW!T{Xan+wh`3*Wd8$3iCLYq zp0#yZE*@k1EwEJ6(*8M~&MGb+^x&hZd+Kwi4qft2$%Rs3%r{-HC=2LsrMIUlEpJ%g z#kk%^FTweKingU{k$i7vp4nz%rfS{xXm}60}`SL+C!Kx%skU7F300ud!#gzPeBf%iAC$hI6TOz6=y`PsjaS-sV z-RJ%CMllaYQkV7vlHN4?&3WeB2`;>f)`z-inFo^%3?kIQ;Q#aj{Iom98|6xCbxU)b zv5QmFVf;m~3jh!Qw%RMHeEQ6>8?fUxakFD8nVB%P!&)z6>3O( zP24(LVdio1{|ok1O$QcdpKhzU*$A{k5M1SQ>5&MFoBr^4ZF9vq$7}S>k7n8OO{AP~ z!7G+(&&0BXG(&jDBq&SHAp@`D9Y6=7^cRLa_Hq*f*BBH+UmfpP5#ZV?Ks60O&gyQRidq4bp>uF|g9E3?@v5Gz2I zkPEQILF=93Sa^jchBa`?pB?-rj%Y5AOHfsj*p*p&pWw5MX&Vvu=Y1t-;az(Jty(pC z?i1-%Z33Irn@QC$+>Y9mXW@i3s>QZ>2{%$UuZ}sBPu0q}2rYRq>j!I@Nxk&SB}}BN zznumJTTMsP9SH%ti4?w~ibqE+y;p|IQK*eqY{g$EIGnDMD=dr8wmnu6%p%ZL8N!z; z4}KH5j2%`%%)3RnxR1$CST8X(G+2I|3weo(8@V^qO z2@E`Vz-gwiuNLbO)J}e@`_g9G4k)MhDDhN3C?)VaYz+xtyKdU)W$t)ClVtR4^{AfGn3O(eFk+*v>luxb58M-WQTQca^ zYuNm?`;jt*(l~XXK~6@(fiku1x)Gx@8+j?DfyYvpkJ(p$J(-t(?*bc0S+ycIbPw4D za3~0y_QCkV-2+6RB49jxjARh|y9sBID}a$4^CPIne;v$WU+XYZ1hoUhRv@nd#9(kt zDLMS#gWqAozXc{jn8#jEiV$HaDhL(en}DDEyTGsjNa9yGS@B1-8kp;dn8Zmh+Lb3j z%_(f!3iR`u&?!IE{zuS+{i^$(%qiI)wA|0ac^pzD{CJ**iw$yDeVjLUtKVW{1DzPf z@pxL3ZbL)hOB``B(|=c<^5bK*%zw%s*HMZNUKO#3b5G!KS^U%{OQ$)^i;2~;+_(F= z^w%(KVlF86Pa6eYw_jA6txvQJuKsbex5L3MI+|u>q<0;HJ`&o3KwjLBa;|?_Zaldi zRTr!Zrqd0E51OZhH#cWpb!hnPNfHr4Li84d<~6({8~ zlas8qoZ@9_spVvks`-sXlfU~viY|=!)WxPxNiQkW>AE*YyYo15GJwA8gSd_dv$RT> zaU`yvB37YR;iy>)7@9sb-7kyJ^s?eCEOCXjX-mB+}c)%k@1IT;kHk%Xk!&ano3gtFn9PD>iz7Ia7Wy5ZeK(THD z5c$`Eu`0gb!E#Y=alKX0)ulR0R~+b^sX}`hn6@IwzR_=D+#W2|*J~|%V5sMIK5m8Z^>BeShBFMyzhTG$&tuPT2^mwW@P3CCw z_MOlxa(QpaJt4jUSKify1vi*jXX@e~RaRP3;vX)}>@*y*6Ao4oUZ82NpwpEybJCOE z+{;o4Q{?m&c_yM&s;<5Ny_gB^!jwz8Xl2qj+RR8Y;1A z|3T+ItdnkbsWH=bxa;7XOZu$aO4SOL&KjLPIuG1$I8lfP7!Cjdh5+H9E)d%1B-Bm7 z^~T~ABm?R~E2Ha z^HUWS7ny^l!)Q%7LX>7_ltd-e61$DmW7AWpjEVNlD`V+xJ37y+!tdV#{Q+zCDa`G^ z7F-?Dd3uI=dVh)WlSUL-L7m}Z7-kow_xLLKou|x{Q;!cun%W&Ogr#(&0yYg2SZD=v zkk2VUM#wXgnT`Kpmvaw^Ds?a+SnVxi;Afir)l8vOPMtUz>4Ldwyfq_*-$cidkCnKv zVEMMlB5ULNOzh@^+2`A?&0qlJBoKKhSU1%el(2z z$b0X+Z<-}*!Xev|*cYc{v4Z?YhtuXWPILiqOjUwcLKRJm!!DRncxu=K`xFjf?hgiE z+=&vpxP%O^bEq_>iiXXiHPuvP!K*DS*g)85j*=R`(U}UPfkwe zX4;OWSf`rpO~~i-9UG|p1WNRCTs%P$d|~MUVg`g+6s+9AX!yVd@SL`r3Ct@o!v|QC zr}$?vp3N#qCx6X?7(!vQ__JWYX2eDI(U^oAz$}2d$nTEhIv6ivNP7)yxmidGfS!Q7 z*MlaKSf&`dCJu(?c+&%d-OAG<7wmyO+aIKu7@`fu05g%p z9zyOMW$YxoXj@v*Uy-C&rs-;`0tMt^ZWNa!u~ zzdQECIdeL(jN1pXs-hzK>pKqWSCbFky!#DF%*ApfrFd!BQ5uy%OCw~)bYN}gh z`yqi&85RW}$r$j7y6sGpD?v%CEAD^7H`cD(bPBYOI4{I7#5VqnO&t5yyH@y3qr^6h ztPg?3lhW;F(dvDD-Rcg%5is^OggLuhKMIp)P`lisbe}&W|pxF+0mel?^s;6^bYEMX?K?5#98 zVzE@fgheG~?Goks`Kptilm|5+s5Q&(Am=PMBuPFEf0mI2D zFTM}LP-IcGBU0GojlH$wfdiA+f604!ozC~^0N0>Zpe8gg@fcp|%Kl|Ew7E2$&ubC= zfPtH|rD)BB4On2U zA4tVqXpm+&<>RIM{ZRk#D`JxL-PR1y`MR}WbB|px$&VQ|QnpxyYhUn718dbSR@}-f z?(@PgQUWY$q)91SrZbk54q1%V-W4Jd*p&Z=Ws72*V zTev<@eH@MFPK4Is`<}-Z{E7n~$VeYUrd?6s$Vr&+0&=}D=T0#!gT3@+ps&R9|V$Y=jf=Tbd!rN6d4rX&nGLQa;sKy9L)aDQuxhWqj!q&Q`wpy9lICw#;E zyuocXbh6B(7xtOi$jbS6YkyFf&E;tk#Tv4oFXbmVWwrR5V2DOQ%lln!NF{A9ZoM@F zK6w|igsPTEaM@0bmFj3v?l{SVm&X<6yJKP3;Y%j5-IZ~Pcp`QKb0Abu zhF41#G9!G6zZU?H1zyMh2uc8eVBQkU!5rc;SBwKgV7ykf45*^EB@q4X4M;1H8qf;> z8A|8U2#&8NEs9tWqCdXFiEc`dF z=cOQgVsW&HI8?2CjF2AoM&7?fZti8cs&lU`2VU-JFitfkiO|b=xGm>RWTL|-rHfN& zNIHGBpwx&y=eNGR+p7l<;-p-u=I~h>*F@K-zk2%3;Y#plhRJ2uk|O;~84JGA7mqU4 zR(+1%@;es+K-1ciW9I4?{NWFq`I*{+!joPTa}rssd}J_}*; zbl+;4Cr|6Z{=uo@(OGpEKSyPCO%`M6UTvVD!8!V*&eV^8#O`_;`(EydH{p(8U>t@P z>z7C6^x5eI6L-|d&I@{GNe>FAu7k^Eb(e`k=X;NvPH+mG93ch>4-t6}Z z0ds|zhOj6Wf2++7+>mFZ8+y8H492GjF1*!kd!sylYj@IS0&^JsUM!Pv9$5QV2O)qQ z0TG}FAX**Fhu1)2giQp>fJ?hwG1(;E0-PW{P}D5K2Ebd8eqFa88OaBodYM<-EUX)} z2<4~$5Q)&Ph3KnQRK$DYqVlhy6YSm|Tk-nDU+3h1nZ~C5REII*y(s>1-@;0NwEMS) z+}?J^5=T$wGmO>>OUNOP?}Xf^Cp&1Rt~=YM49U;umufrH*_fBQoWBOuj+Uzy6PMJ} z#ns8?$|2j5^d{D-jZz{y60h?0i2A$d$tnN72&WDYt3Yoo2*IgQK?`s8>vhdQtV5Ld!Cx$Gz#|5qDP~@T)N+DAz zB({LIi?hoN0k@vxf&jN#iUU93hBR>9XWSh#d7{RRB{zel!R;hhx-isd^?2z4Lt@dV zx>;_q(Z>;Q{0#^`;z&kuSIW z1y?S%Iv&@HTF1_5K3X^^FW=&|H8l=t5C}sMRv+zfyS{wW%2$iM*&BMeT*WV-ERZ2# z;q~Z!Tde2eIxT6JaoanN`(x#kTnQO|y@zzoK#c7VWoIWl64E@PhUspoTaAi0bCMb0xT`*Td z27s7sUhI?=@-!H94L!5_glM?UY^ve4M9w{R%@F0b;*sg_@7~oFGBC*y?{qzWn zWhftHG&w;n}|%#gS+7fsnK6a6`WB=2vKe^XyH~oGLbeL)q^}I>sx;w#>NM%rgr~> zV&Rg6sl9D9nnzI$w>hl+V*Av{VF&#`k(b5*F`Y|aIhW5)UaITS)JTIU&V}Jc;$GE! zwip3E6pEqn|6uZB6TS}e&qZO3tkzDN*ATnQ>x>Hgek}=Bh-Qr3(cglUu~%nV~t!D zudCY4ZP>nFDCF+$CrGP><$-4_w9-I87DM%SQd1b>HY3j6iOX9A3_@WN6B@v7=K)b( z#_)Tr8Uk5{W(3H2!ApgX`_!V&)x07;+w-yv>sjdTq95q_D=}C$Y!U@K@_b z5KFTogF+qr^PL638;KJt+MDWm#TMd8*j}Q@xQ)e!~wfv8!z5VYvso^Uu3eZ2(s>9V8zkVBB{h!R@$$*YwI6 zZJ2-I%9c{xU)nadeTHv%bx@Lg`CO{&8n#ECoUKUpVoDBL z=tW^)(?r~3cT4HDuPPNwJyXBul;waRzTe`56@Bhq1s)%Rv-5ZqY?>S7er#-gF`D>E z5+gA!XtIk;s?PB#zSJ&j6syHELv31?2JC@RkYA74Pc`HMIks znVB|3a@05D7HERyP4NK2F`ed4)z-!7LAKiDEnjhZ)KN%<_M)t zIP1I3JVmviI!#T}eeI*k_4@w)f3k#=T4n8jm&z7~YP^pCz})_2#-T}CE8l5r>gm~Z zIf=#>_xXZR_kK^Mk0g>_B@1z_%j-Ls91x3IER%>{cT`a4gL^0h3wSF5lXZvg!}r7e zOoqZCVZueb(xXzhXBqmG_C0 zjMe>b;js)8Y-VQbd~7=R^kamvFc_-%(9?vA?N(R#^7T!)YfQ9eoK&=cGK&shTNe)4 zSIbvOnD4_S_R`MYNuk31m!G7mih8?-P()9zEY`+6t533Nm!R)eH=E{wk4t{t`Tv&_ z=1e*6HTsjN`-g_jZl62n6h(hx8VsyXiUh7Rkz4`CFy^ zqugT`T0xl5pBc;Eq(Oenh1#mGiQ9!QLHate^|t@0lr!aUpQ0$P`)ZEzvY0{tGfFy ziQMb_S0WD|2F<~;(WC1A->Nlhz?f}KL-yeb>JRc8t@~yNO9+sXP%RYU{FfpQvr7s; z4_x!ma3`GZ@;GHtM;JceJs8iuDg`T2zWpN&^dHj_KT6D?wFCcq7K5oJ?@U$isakUC;U1L9h1Jd*l@x&rq2sW(nkb&{H|TOvxB($Ag#5 zQ>7=mB4u5|*Yq~$T9P%(cyY@7!|YpK_qcMjkuZ13RwucmS622&r98{7&E5HLT_1EN zEPC%{Q5O7qY-$%Lq5`mYj8GO*WAJUH@ahdPb((T^1FS-U0wq3qb{fS#TmrRP#XG0t zVlLs7zCt9U);6g4{Nb6zq-TR(Eu-hW4L?*UF>&8X_8O~zsz@Yu^q#HU+CQLdTR(Jd zOw=}9vXv!@m?rSg& zrJ!Mr3o#A(n2S92?-BtOlyBT5?nGj7aWnSWZ8~L*awqzUZr(|m;cV}3qnYgfZFL!O z!&)CHF~OqE{lF>7IMluGQW#2bt);{^)1qbJ2&Mn0kK(0t`bi1<->Npt8uO~{#+N}j zf*hY-bW*mSLcx4CVJcp}HF03qK+J!-EZR4%BK0p0+yTxVjax$PJuCykOwso~cTL|L zl|w&k@>J|!<*botXZMw-4dM{o#+sp~J;WJv@`b4y>&4wlGPNtAb#jS@b*g6sXqf_kl3&OE8w${oRJql3l=S%41)w-TigD3J5=rU1DJr=iUn9_>A{>xA*LKtv`O=!qTgPm zPWSmfQk2LarM~`62x9xW-(Ix+-*YU*+Ws0e{^m-4)S&)X&Y zVh7`VJc2tUNR2gyu$pi0*C|Xy$h{vQZ#wDWB1S8%NzmcfuLZ>sI#Pd!JkNsnyljO0 z?oPdlsNr^sjZOqjCNQz#%pNMw$XMgRw-t{!qR1#C%B+%j1M;U;Btj2k``(YIWJjkY zjVRuY^^2+kGHN+e+Dd25K+>9Xm2_^yg9R`7Gw%eqXG@u@P;O$=f<6rewOf}~+W zu7BkV!pZY)0gUf^J>jkdvCo_5>j1z%{|mrlPlWb>a<;}n&_-ow3MfLAr>wldSQ<_G z4P-~(cmb>?;B#OcUyp523gfghi+n^-=l%iQm2s}a$QO0yfzMBrT|J>qMVa<( z_$~zisEafv!%zG+n%SJUu@->F{4jb+@MT#uPFwes?aK{@w%z8$e`>DY1KM#9&iMPU zSABQ{>G~TFvtmL3#em^Y*`M>GoGMuq3Wvim=9^92-1E$tJyVdt|EbgMPC?4dKqBZ#w-1_Z~zu_Kg_Uvu*&O|3U?m9brbB1;NYwCkQ zo;=BxxoOr&*`N=p*Hw3$(VO!9hs{z(ln_&{2vi0hT(+mz_?L=BEM{~s@0eBl$`fo< zxMtbWO*rDpqw<=>jQ_xpcq3ws=_`yVgLnMrET0$mH#oM^T2x`iF|x}vV2<=lw-8_rF1+I zi0?Ynz%}`jkL7K=$lbgSn9wRb#FKXS)O`;APiKh_6~j$y!d=F5v|_8g>(Uau*PT|($GcV@z?*eYhQ-7!qZdU1j`kOJkrtsNZIqv6> zxaS#`WZl6&KAK@%z@RU;OYT?Krx0uC3gn@UGc;ZQsfv2j!-45dQx#)D@Q>^A#`3ZZP`EDnUvq(-7WCt8)~Dtu(%)9LPobgR&ZXikg={sS_#VelNy@)R}C)jo)vj`GulA%Eq{w3#0$Zm&52Dq42AqX%s$xm?d zjPC_+w!8Mi;HlW^AW4l?Y?@hMl}LgotE)~%n48cY)NX!rg-=ytm<{LUCYo=x1Y`|Q zA5%Pu_uz#yOADec6t_Y@kb}wnWFn(R_EI#`C6!VZ)1iOT!|Dz`P)9#Q(D_fa00BK3 zSAn+ezATiF#F>K2UTspNod{qK{oqHhuWMvJ0P^_{i4^u}Lq=7r1EHnC#&{m)2Xw&F zV$lZ|=0}3c=0oxCKHz9ZT(qzK!21&ydyR(p&6W?((6KTEos^fKHJCgU!tVc z@O}v>G`sbIg8+B$*d0hr!ysE&YT+jlJvln{2`k6Mr;?lWq|uH3eh%=(;Y^m%o-kL6 z`cFxWo_{1U2q3IFQU#a|1&@M)2giRg{SJA!41Ox(+<<7sfkt)Ka-TWAvz6BM5{8VW zqopbFO>A_+&58K8;`a|og?}%8k3Lh^>y2AroT&KZ5<&&YW{WB&Wf2TCo7grbgFdWO-0H5FFAAs&7P@z=7w#j*$zXhQMcut2-h zpa;Q|+6pZd&D%2TWwbz+C@lCG&NVm3sw&JWDRP3!G2EbG_Lb7e_NPg3i+XJIsW)W( zXB5IS-%ZJgJuM_3inK5pTni1|t+=9k?spshQ(h!wE7m+82YS*12Q46;;6uR%PT?UN z?5qulf%Nnv27DP`b@t^oKl1r_8{>xEYtt;t352nLqsxUseM_Ohfpw~`C3CXZk27bt zo>$X5C&id2f?)$wN&`Qk@(Y_Sc-nMw-sCrdRlrb(b>oYkia)^&0uxkS2}nJ)s=oh9aMsh$rnG`bL<^sQICTdZ^D<(-y0)vcf0lA&H6%N23t~!C zLQ5IGT6UrcWK=v)fW7*7xU9heKt;0X+#X8Vknpp80hLxoU~l;f2v(>w1|{T>X1B*c1n(a` z(>={MScN{2ofKB>rQh>tOxh7d1Ozs^a*UsEAzp9p{@A-#bo#$8D5JGi4}^4PFNF|u zaWgTck^^E}-_+kw^W5MX7!prR&W+*iQVsfLuC4egAb^hF#e5mj|K$I|cVll^8HrVm zJzMs%@N!iL&yGuSy^@R{rReImu+<;B>cXZ}eI34<;pX*A0i_Xps-z|@ZwfpRFglql zQRM+D35$%VG%(eSK2cXSk?5Ts{Dr(SBX2E&s}i^@&8W5X!2k6=CnTAeG+SSb*|1s( z>Gyogn&pW#y6lLT*yDm0{%%%+^E(XvkM*~_PW%{0c=yU0D|DHYJrxd#mE#q}z6v=T zq_JwJ?VSU2@9*P%6@|wta9hFBd??BomS={*+|qJr_l7`Z8He2gq9Q!&!qdtGpf__u zgk)28_8M>0R~ja4ZC5|J@nsNn3KZq-&|VQ$l5>EJ_`wlG#2;s8mgw^Zm-a~qJ7|%2 z)vn{)WnZE{EYH*M+ryXgZwDHNJoSHII#vWxp2)`!Jy3bpRG!qNxj9T;bVa3IX+M5s zzd2fMM9R<_j+H;mu&@wnIM^B0#b#b4uzsA>{Tc*28nB0{<{zuvof8pfq_^jqC8+|C zq>ZM@Ihx=(SN4y-tq!p5L~712>+t@C?(eNfKQWVg+Mg9K5zWeDC%fDS?fpLMIoFyf zT);_HM$T5e^~=?;Zrj8xw4k{>X#%#S8^fvs6S0{oFSho4Q?KS-aovdx{w}U+#){d% zW=CA7!4ysi1wD!pfoEtuKHBND3g02K_DsLjs1#%9S{#F7;zOLOU?-aGoA~V~5J^Wl z8~Et-kBv}@@Wuyh|Eo+KLTKq)`_O?hy)8HDJ7RksE(^j5E;XAPTx!!d#?kYs=dxe% zR`Cy87a~V6Ta!{f2iu}zem*?27c?M^NOwH0%|?XIk*`Fg8j+3dU>q5JA9y8riljMP zMl5EN;uqAS>i4j`IGY(>948gXwW34(nV%O+dJjW>UYsCW2b< z;tDM&_H}jL1r2{wL3KI*+5m2E#2M+Nc{yG(EFHnsb<+P5^BODnjRWJ`>IB=9P>CB+ zNVU)VkN8#Z>{cS39D03l+!a(EmX4OkEvU}}%4Meb;#OGI)8e%>IhD$%fc zH!jmlGm5=n4@(Ayu2slTevrJ9Gm8s12erUN^@ z=;>v?kQmDES<1sxHxU??-J?7}bt?8-U}ttKhZcrb+$#$e`fo{&TR@W^zPu2KlYds)(U!7r|%0+h94!xC(9q1nPCiB z!z@SQ=A94WO6*70HW|Yc;X7o?1M?t_uRr7d%CKaA=fXAZpeX%v7oaiJK1&3;@tzoB z^P%1>NWl$vrbv?9TShW4&~y8uyg8znp!>cWBzIuQ#oJ{A$CbwsD$AU7pWW>bbJPvr zQ;9#ka!DU#uUSKsPdW3Wk6lzTrK2_Cbw$fK@N!883C1089k#E}2PqzWKytHvsszuL z4l^r><&&KinF*N+^)C{}|zM<*Y{&Yg0f$rayZ?SR^ zVM#Jf0c=R>74%FB`5@Bs%SXY_pBlkLf8|#|GTRCTsc)it4`PpRB!ym2H-;Ur56v^U zo*xMfnk{D=9jQB%X!%YlH`8<8@GfkB+@-nM9)ndzgE*mh0+}25Jc|xt+Z?op=m=Je z6q_MW=hYh#XyOVhWEfrc0@0jwo^?_;i{POaPuhB8J{D6ov>L+Tw zu#}9rXpLFFLl^(WnZ1q1RZ+*#3I?uq<1%WBg2VFVg}dvVT44}<$O#RP?b~6NwGuAb z_7Pl4<5k*JYGPjjCr-Sx`t%HyCcb1NP@bZyS8nDc1T}cT=O>?GG}-?e0MoWn`#f z40Q}0APLc{^cH4vwUcsN36#Fe%+-DOqnWXH9C7$J<}OPkqH-Po4+!4vavccb~Ldd*B5+avi7P)U^!*#?sw zmCc6iM{iSm2vorP3E4zHVJ91PS|`GPDdq*nmLAETaLHdz!#znquZar_W*K+c1C=#3 zMlVNcTWW0ThlGe^i*nx;!7nY{!U$OG?c+WL?496P@?IJsAO(q-k}c*|l+>5JU*I3Y z`eo_dYq%vz9rv#vPpD6EV_TGNN2pMNC`&bITyq1nzkSNkf&~{EiBIehG@BQ{D>f9v z#K+k$pB_w~qgplAe-C_h9CS5Za>8fh{bQzfwJ*t!7fE#?Bo;;lwW}pslu zgwR3E(9n-%%+aR*xf#Sv0{sXV4OOcL={bnWYejb5OR4S>ctu69)H8 z$z?p&U7ksMCLqeRYZ?SrNCMCnyvg!ty89mRLr%1x!m`6}Wh~B7OGZOlomfs+#Z!U% zF7WV%q}6nJEyb}^5$}D~4m(k?ETDk2K4^Jctp_AIS7!ERM z8dP-vkE;X9sj?L!Std-hvq@hrkVWN?i@*9A#Pj^-pX+bC$0z}e=pnyugMhA#!J-}f zjVR|*XPWtX^=r~NL^B1+&U7oNMbWtG{X>sMcF1txaqq~hHYx8%at=HVTRMnI>&~&m zk`qqxf{ERVWOhjo6{=w;VoA?w{RD0O3z5%@AuLMWf2s1y#^T&Jc z7w=2!gp4H`e8E>5WU!e)#({=;^RMbM3ePTX3sxd(#D`BTR&3aFx%2R@%iP9V`6M3{ z%jVYJJi>m%X&u5w)S7p4EaN`%PA_+~AejKhZCmib%6d*1*x#(sNun`j-ML@FaRRD1 z@`f{w+88Mouq47**usUlCKxR6xuj9cXvqA2h7UvMj3GBaT(sXpH3z82yio1G9+j_r zn(?e0AM2VU%;d>U1YKJqN;?Wub-itE_Fs=RJew>!36!UWi`*r;3%8Yeiq``V!c-pf zBO%3Yh$}Z$g^Rd&^HU&!4npZASS>R%@yB0(hDuUC%wQXt@|%8!i9m2JU6CmPCp2%i zTb7I7yf=NFewLxMw;$CmKupG;L+*n^c7CH(@gr(28m!T{;mITgmG)HA=(A#hkRVzI zP5%^<5!Q^3K61XUhf6EM;zmdclDmw0=Dxlb)(|GwBGx0&0xOlr%8e4@Kfg6!O;`P) zUN3Epz$OzM%Ba-lkK&)iC?&GtE?!;dT!cCT0G zMyWnHy0(tMaJfk{=*w5D=MO>>Acq87zI`+}xc}2URda1EvJuO$>i)4v$K%4%+3RL( zJEs{Y)k|0`ihy}ls=8XQ>5poU_NH$wwxNf_s>ML#0K~v%I=hy187)c|#i#fUQLr_> zW4Z7ZAN6q>J2n7Stg=REe$0cvfOyVOFaO0Yv_!Cd$o~?^j4w4Ytcxj~z%p;iY61Rr zp>|2WRVo8M{gAK$T?7FGAp?LO{`Z$y;wcsW94n{hCh%h8?ddUDtpr&g0RDNGDPB#5 zI>}4NEjmXya-!)ulKbgL6cY9Fs!@*SFsOf_^1c@-wnONIeMc8DV?XLe*k#TiiIy^z zoXhwT6P^9!X1}8nebjNg-tKg<+Q6UbIcry7#@-`#%{bP5%=+L1I7F5tpq&5jt*oXm zVEDZgWSw|@%BfH={iKL_G$xW?H49 z6zOuh<~4*~3OJIk5KsUfG0&;J1a~-(QzB`s($m7qulS zOzr!?1U+2AdZ|EB*>@nd0^VVte$*T=3J_w5a?ByAsHfaRE~b7P#eMq(E(C*A#hV`d z(*i|r`A1#?g!;*k&<_+>zTeN6cHa$1{Tj9(OiYbp*Um{x;t?rArBBh>;94Yh0MnO_ z-q2+77t`$;IA?87ll4YB{xeHm9EK@~E@SIhS!wwazk>mVcrAFckl};ftK$E3!Y&f-(aRbn<$L|EoI`3zk|XL|T7&+PLu4=1Rh?>F+5W?zOvY!p)m5e>+tvs& z9i+tIu;Cxdy&KOhNXpc}gp8zx07~4a`?ZFB%xoIsvE@grS&>}tuS-ssqx*)sY#RRC zf()ZF&Z=J)lW=c$&WI?A(7WdISSpm15%Xd_((mt>r%4Fhpk#Hn@HU3z6%4}DWECwc zm?j;Bd*|{rcD+JRWRhi!?jY_p=f5`ctPw#^H>2-G8euJHYzFe_I;!B6%&(_9T9Y%U zc450^J3hxE+D?Tl&(F^-$f_H&K0nrm-TBz9UwA!Ibx6{{g!1~wRq)u&UkS9dzjE_7 zcEQOa#M)Heow(Bxf%y1h2fVMGQdu9(B8-D5cBx3&jRu=QOO{D+y*}9!VY`oHJSKK% zAM40WAwTh{l`u(Kw>O>GtIvC1`}z1}siM#AvVL!jJ$ZyNRGvOM$J2ScnH{YVyRR?O zglW4GjafYi^_976I&F-)7~eIR_s$uRs`~*VNkPi35C;n})BQ-UKNhzf4p_mIVMr`z zXbDv>eU-m>;=W*_>vk;QY@MOl$WEBv&jOugw-*?jlYOl%bN54~__}=~Nz1+=#nAqt z=|lol&i5zcW7%ax%W>6~S65cXmpQzgcFyFsV98}53zWjY`7#uics*VbX5BmxhbrJT zZ1bxqIMN#gt7ALR-?t$v4DZh4({eG=Fgee1cv{*{MKB@H`=vzf#8|B#IbU{GQiEvW zoO}@tZH{!;V3M%Om$*+TzpOYqM^Hk94;V~~({Ii*JSZh%Bav0WG^H;%NoaMu_t zd?|%Pm>p=nnR@^HZ0m`yDysl1qbmRTi!_8j6_BH2dbbC84Y$39a zmj#i!SAi4;b_Y$j&LcxrfJ=bM$bU}#S!zPrWcIw_Urt?-U`q^&=cz_T^3HJkBLy5t zKmr=&!Ch4K8#AHSy_9HjvB*nC*zHp%tL;k~CyA(+{@3$XcY>Kl0Q73_m=^&anu7OD z@AUBlKB1#-V(F@4p!f)<#e<#6Bk5mA2#&lCQB;7wKOmQtsy^RLtF0o+mc61Wp9Q?r zOGR3x%j#GZUf2VqOo`xM))6Y5Y@fD1X_Z;iHX_;9cvf=~tK8>CuJgKMqGT%RE}NW~ zba!(5=N5yH1Zp>YreJI~{4T`<)97#-7erAq!ZxUm`|!$=N8}dedXCM2l{folEOzZ4 zzZIe~vq|6c=8;=Xb0>})<(2x63cRzkSje7~>ExNb{;ybwRM^4VkpE;G@0vfF)RS zZDl2j?SxuA6WRW(f5QAJ+%%T#7)^V3q0)PE#MH@z>d!4-aygcS>3NVApk)f74rc>1 z6=!ut5Mtx!`Nds5#ZI0Xt*%pi+#Dm$w0|B2y5}CBo-MuRsgiPL@-0_vvi!TCdn_{8 zO(-kjZ^i!!U6x*dFl_Zi2J4`#TjZRAYr-b*3jLJ-F#a|Q!t-m(H|5f?`nl!UYyvba zpJ91?b#ZcWv?TgFJ^g3Tgs1lwOo$A9u}D(R+<(N4nf?Pucfg{!sL}OtK6IlK<$g(F zVJynt!k|)y%A^qV?Bue|MP+`ek+lY|3p!rhIGRg`{}+Y=TNlC!U6Lt!zMmc>o!4|Z z7p;DASbnq{k9}F?4va?fHJD0u6JCq6*r&{<%<9I(Cx9G6K|E=grP~7f0 zGoRe*m0yNtH;ls~hVfG@X^?MVx7h}p%oMe-<^v#C><*rP9VHx;wo*6ohg zR8W-$LYkA`s3aPG%epSeMOi5F3!?T(N8~Ai&M?kANcjTPjZl>{AWujoo z#{PZwA1hyZ>FdXhtS5==vg6CFTBDQrLhdcs&e7Pp<6;_>+ebr3ZIg%n-{TzA72 zq&1C=A?x@Qb`>N%9fH(XWz)L!Vg)B3+Mq#bxxI8-@)AEx_5@LW{Vac4h$)qsqBs;x z0Yfi6*^s)Zs1!93XXJzd4f4MG=&kJ-y&`NtI719uKda4ZkHrtobuSQNfCh!(G~obbj1d|6vPb4(^_ zdgmN!I6^JlyI0+P`0-x<-yQ*ortp~#fg!FA$OWRh0bDR2HDlGL*4oI1Gi)84%N+P* zgK`%L5^x%W42(V7R2hhVCR0vVm?|fE?2NLg_M@(Zbb@aIQ03d0U2q6t22cT>6Ock%Q?&LcSYsm4VJ&XJ|d1}n3%t&9Eu`}Pe9IVjS|vXJ$ce@(DW?1jTkD@WVb&GR=F%{2d{=hvG1 zq>Q_c2G0x!vBMW~sJw9G&M_3s%QM?#&?I7~i4PxAl%QK1DJ;L7Iv4pBuU#VwN)DO8 zR00Rb9J0{+yJsWzAAE*YP&P_ZT1B4v`bHk>m?glwV1zlFvh$Psf^T)!!pLMP*t$YY|}>Tp7tpG2IuQG7}bcx;R8H7*~x zp$#t1WuxEjr)h>rwijdc1(9@W5ShD!4s%;kQD?p6+6^jf1XKtpEPwHUPZrb3O7=S# zRb^LBxjA_p1i?Z(sv;c8;4RJn_NZ!*fbu`IH6KR)9zws)t*bnNXW!*tim|^uEIueq zn9=HEv10^bOtF*0F0~BgV`kyhz2J9f%5j=fixg!f7yrW);?iC3%ax@f%k&fWaaI3L z2u>SLllSL5(JNQuxv&O25Cde-2i)rtf7$wRbYz1%?_j{dF!CRGxzGp+pl6S4mq)Ba zrYdrhkK5WYXpl-#65e-WLng$t$e_nSRXJ*oWu|Y0X1qp~puQ}}uZ7!)!y`G6{g{Q_X z+GcT^zhC#9&Yq{uzK{2l`(j3CB4Q5bL<%kH-Df#%hv0syH}V?px0|xPdhRbPzfcm3 zd2gV`2rnh-E}hl@*O6+CfxG}?apUh0G*ho%{PF^Jq7V8W=&M*`rX<;7x*eU*Q0ey2Q;_cOlMh-I=$_$M^b zH0f1K(!^0{A*YfoNTA>jy}5?$+}O#|##lu2lt+8iz>$2Iutmm^HDST^T$u2TAwl;7YPnVID} zv9gBHi(>pVPj%lvRK&E85B9hf!qmfrX&wv*DCc=Ce&dd-uk;FU?|vWsGt^Etj}35#p!sQCQBQd;h6JqdAAPRUOtO( zJM3C#ao_8utg$qi9yWbBHw9b@5NoPnqdqfuS40-3zE_^LIG(6$V2~&^+h!4`Hds#w zJ|R|0C*`Dk{9>aa*c1|J?80pLZo#gjVLT|s)pXBKt`loSFX)j4Ac?~8)R?~L{(=14 zj-H6gb;ZQQ?|tn!?Ga!=Zc>ujOxyJ^Mrdpk0}WC@K9~fUJI81oS^@4F`n|_{&uf@0 z*Sqlb6%2+fX!PI05nJ)5NPv9Ifv;on{6m~I`m_j1bIYhiZnTSy7c)6l;==5BQ_z4~ z|1h-G#6c9AG`aXf@6A-9cWqR4#BcARs=q&3C9CUK54splJBS>Pk*5NMo2!7gerIJP z6Ajp@@61qm?F)9GzvituQ2U|v-sKQBV$m$Sp-l|VHlTznK&bI&$Ik3RmiGOVw#TEy z_^2@xhN8%KX+(@g?A#Vxx6J0i3<2JUuDNMrg1W8dll*k=uHbf!&f=F6H-9Wg7^3w-I(;bXOAlrwzazDJ66Ywk| zV+ljtU$$*dKReufEHEM-z-Dc4DB3$!(J(1Ze^UvbnB(YBFG@#%l$D*jVb|6hUGUmJ zp0CsyPi6gLErSG-a7sxOeCmB;7lKjG;BwGYGoX`H)X{v`M~|;6Eh!O=1{Q8Oz@eXX zX%AXZ=a&1~$}_%3-jF!PemNF$9M`%9O220bFaYEf$azC{QFZa*$irczPvpjHx0mWt zl~b|5|31$1`^I-Xy1Pr|C9X@1m?Ie`K}1O^7+OSDo(&#kQ0GW9fo?uDQPV&>o>-Q> z-j+>$pD6X_E?-VYa=4kv?M&D~N7j=>#HR^TsoaqD`!?qKRoHMqDVPfuh7QWX^i#eF z4y!~*I78IbbWe73jNa(^QdW!og;&&~2UKR^uyPtd5GtW=zu19x#nl-<9uwk?INTTQmBs@Nh==Zh@wG2%-<| zUK}<=Q)n572>{}WU za*wy;OSKG&B@tXVKc9;~gf9NB-#`JiiJ#!np}-$L^^0?$+rE%;_+8Xvl43ZR9|K5V zXqg!qITy?r^w`{sIqn~0>&=sfk25#gqWrYD{0dt&{M4r+lFp*8|3QJjNGs5c{CTmr zcr8%7Y?0GRUUS|y2Rf6eUfZ*z=U)xD9T_e5Q{%P>LF9SDWn2owPok75D_D|6Iv)p3 z`L&L+&Xnl1GLWq<;UzKas=k7y@mjQ^CF>H6O!4&594Hzz9!eMY+DKqGoX8OC$h z@MBlgvcGA_6)`3z3*UM6K|0)>UAT0)5I>P)>IVBer8kc#ggW)vUFB6k_(1B+wgpG#GK~qN} zqR_IKc6MJu@%i7d=G^5wc80o7y%JnDoFwnsAt$X>UoS`57#d|UPBelZjbM}b?+)i& z5nV|;)3z=fLUz#mZ#{z$6e{?^pUMH`{*^vnt2InLf=3vl&5gvYFe`A)RiZtH2C9BK zA;U*VPBP>WG)01SxD+D8a=#IJGG1~_``?HPE{tA>6kqP|uZ|!rQ(Fwawt_wdr~C#>X-igD;?Viv#(jvJMXGA5}LSrY)SdVx71$@H)E((1cfP$M}X$ zgMD>xN?(-qs9GE8NLmk`H^NJ~k$N$1KW5Z-5OpR}kJ>LWKNH`?(Kp=FBkHb#rReqp zwgN(6MkHb2Br@3CN87#hNS{D^CBgakDDDX>=mdesyrI_-$yLsbzR2{JB*tuJz{bp%8*nbjlF(}0xe_#`C&m5=c8T}^8c446S zv48*A$BV(iO}D`TsP;DFryzi|vnMxxl|J{ujfL7f!lL%IKUvaedDw_t4KiWy-E!c+jIa{pPk+U=!%KF>wCeeVU}tPPL{Tdg(>SIl?M{S_Cu^$@c z|Cet8HbVjo{@u($;-O}!-)H&b2hleWWZh+xMHP*|#dRpm6&cLxaFRV0XkeNS(jyOR zOTs5UdvPz_5&}``$#o7M^X=r77XP@$_bbw?jjj9d-mf~OJKhVwnyZJj5Y&Q}S)V|Y zeqxRGG3rA3P40@6kA*gb#rf?irJM+=LYl?>IqkzUsvhp8jNg_AA3< zARmqF-OK4k4g+YKrKy$W>modao4~j_z_^3?z?eD^3}{+}_^7>zAcN0%euEacx7zTB z2XO#mIZIq#44BRLHJxT!Gj`E)aMfjXH*To`I9RP%`xj3DZbY}b%Snhlq9-!E2Zj+t zqTS=;Wf{AHYPO{HTUuaDj3A$MkQwXnBF`;bczFLlQPga*|9s(1@y^BHt6Q@C(eB*f z`gC8zt@=Jo)k<0Rw1CZa(-s%pSt}9=oRLj<;7Qvm@S9Yi_1aW>Pg47KV~$-%$(OQ< zWq5tis)sFJkUPn;h5G!w#Br_p0G|~AEs8sPj5lLW(z}=l4+-EVgddJx6?aMX13~}X z3YhNgQrfJ3K?sIGOzbUsRC!|(B9dBjdIkJgJ!re>rF|vhY24gjF=8rBR;A^>uh7*t zYHSZ;Y7(!U1DCbV;$L#B*)taXb=R)N-ShySed3NVNx*68@zos)GT?qY|tneW3h0j zM1N3-BTY~mr4YOMD)i%M#-`BZ|A(7aFaLv^I+~PyNEj%A2&j6U_b{n@+O8cRYnAsw zjAvPt<}c=xaW6hSU*-#9KP>HUu+Wgp*0asvmsEXU12tT+&bxBWutSun1Ot+=J5Q42 zm)Ph`$!FJ1vDn#DZ0jH`s!YoxS4VO*I>U%ges}`npz0%=NYgWC5m1kl&w60GA3WJh z*1rvWV+?^Jtz_1XN{>%XN@J;GI+C8x3@2X~o-IrJZu77B>(#K?rRAj&xAMk>!veqN zVEfCQW#7agjXGDj%(?9(KhM+97&Z%$i#al@Zd*}f4I4uKQ$48I`t{VG3rdnB>AIOK zlA)?mS!mAJvn(IH)(tn4uO}iY))LKejdP>&;>CQfv(hWd=s4@9X4^>`4G+4~&yXg0 z`SH&>M8b?*`6sc$#?wDrWvd&W#*UB@PRql@=NY)V->m!PnLJ;I18x zXhpWmlP2>KdOmeW<$-yYMHh!phuFRKn{cP&{w*Ho;{k>yQ>(dg>#r*-X!sg8&W~32 zROF}B=%w0c9tqo!$h-Foe>5)*TMCVo0WA&%37EKZ4DO?8ba3pKgI+vF#!7W=Cz&OR zh-*G~(ozX+>+}ATuH(ne7K6LY)^G}mg_ENT$@)8jU!0ztEeWLMtJWc@slK-Z*F3ti z20|A`XUbatiN!u3zSq-;me}m^2s4>)<-lGMYNXQ$GQPD~sI4!yvM3?$T{Ust3egj8 z#VU*gtXQS=)H1*hZx7Q>#DN0h>9kufTeT?S&R@KETGPU7RwEjuWT>3IDro#0M^CKs z8rw zd_T$84oAsO5l}NM(LMN9qgQhc*ayE;m}rRjxVNlj$t5Wey$6$y2tc^3jr|DFIb=GeXJi228oOG z@FKk2O&3Z6jPUI;e0iLD(PL1Gfz+z@CV=o^$Ay*lO5nrLQOOdMwiW9A;(m|*Fy|Nw7&L}YiSxn5X6T&)8*g|g-F`~tQ1zXQ|0r8)5Qb~VfOHD zmvh@VjyvlhbN&V@f%$el|D%+Gd2O+)^z-Ev+q%VtInnH`h?tvDV z!uq`yxCgy$Uz%+s1tAa~ASkMYFI&rlHLKbpGw7-G*#;mqRqE)+ar4V5DM_m3h7=Dc zE0L3uQzrRhiiXF&SHgi5j`jz+S*qHI>4)tf^@8*M)5pfOvU&MB21)FQrsLYEO<5Tf z*ASU!9Y%wl$FFGx{RN2$%5FZyWNSkXvqF-T+G)jG-0XI1^xyeOX#J<=7mi0n6>!}p zJ#4{x%VsH#?Y56Lu@r%A?&K))DkwOYaY(+%E)V<_zkc2=fEvgK*JcT*9qh`xcve%; zDa>{&`J;fU@4HoF|E21$=62eHYf=Ql>f*kkcyvHY2gXbIqG3IHjMxsd;q0tZyvl|U zr@d5KN^<>>TI#=k4bW54>;pT9%R_%J{7&H$hi_y!-7$S7`jfc)^Y_eu@XRGNsB&nR zU42eWwKXkk92|9_nK3Fd;`b46G*4ZvdulrO=1Y=%5biH?V`Dw)^^@HKw-)8);qXGh zEhL49j#Nty>_LAhB0I6Km7Sg%`x39n?9oo3SGya|0co!F{wjSPd3kHWx3B^cWy;u@%3$&;vNPvTXV}SMeAXpj@YW@DMt(KdqB}QH52}fy43DH&4T;JC~5-_@~ zSe`^G^E&Ao5gJ(@$}2L5p1Pxo`04tpn0m@J0o+w^cXY%TywWhOAkJM;8e- zSCh^>(KBTbP!8+MF{AdUJAztIIoGC zt1Bd>tjEPT*=5)5vF2*R0S)y4I*6vr2*qOZ9woe# zS*hOJL)9MOyY2d55ML%84&0bvD{nn?{cRIeS6A2)_q0Hh#@s*qtCs3DI~W|FKlZQ2 z(Ye;7mEva76l1ARMM25r!g&JH6P%nGJ7$ixw0W}CHzbry3X!piVUu=BwqzXBd4iQZ z@~G&&GX7;dGBSvu;9zR^c{(|Ld+Lc}F zu;kC{KVRih(UE523)Io@b$$)fQt&}0XE;$Q28>pD{zu-)gkcz`E0&hF0V@+KD1UiUgA)1@CoRDIwr^c&M#`du4_3a z;zRJQNsEOcKm~mTHmxj%>;^^5;o>_ZrpGt^b&o0huIO+pF1*&Ipv~k|A^7x~++v$K zRsoxbHf|9?I#>O9yEe1jYVIH*5;Nk4?rbqj;I*j9w3VeiEw;VTy{GK)3$O#$S$oa8vkLofsraf1tYb;M2^&& zckHCc%}-6*;D=LMMHSSH&OHiBXSTcc(XHc4;(@U4uCl`>NdKF;g~q?*^k zb^OIXchOwyFruUOQ$qzZno_u-7{@<3={_l<`ehU<?}BLsqBOz9S;Wy?CR=FSDGJ$?o~nL&~dRfM`jxd z=IZDq|2@)k7Tk3ogJk~hIg5TmNy{&RHs`4IbvMONTd!n#YkqlewYbv(4JVNYp{cf} z4xda7+lINQ!at9Innw@-*rdZiOqA@-K&n#uv=?zfza+3ULSO<$Q~saI(A+B+V7p~T z`mzran^24xmEEGZI0LF2^EJ#=v4HNrecUFGYin~^)DyOj#%EbeYtzrt$_d# z`s5!8FXOdl-~-Q2;H=1CPp1Mh72Z81EI?sqeM|{2L=O#wRRYB@Ek-Cx3Ka7OGco`~ zxb*3>AYZ|S@KFvW3!w@8QLk(0=RZjsVPh$5hV}>B2GEvvY`6-n3n_Mr_?#E#zmbI8 zmIRFh`8`;Tv+i1To>ec&l^w1unI=04nOb5{y-+!uzPQXf-B+HxFPB;z*|{1LXsZ{+ z=B7PNW=2wJ;o9Dbj^EpidB~4t)9XCL{Z!@j65F#r*c-zFiM!nAn|iQEqck5mRrulH z!eu9_Q50ty9IgsoZ^$wuo5^d9}`?Z`Xw$Q z1Cn992IY_W)wKLTQa;c)&`4J##dNwNAhhC>v7S^?p4Ys1p(-2Pw#i+yZwcSlmRY%7 zFssE5!p)=K21Dpj+DVBFL~iD?`Nj;TY_xfbuV3rdsra-K(C(PTYEm2oO{V8g(M5|s z)Md3DN;*cp&#qj(ciqAhy{%?j?!L!UFKl_7rvR_38dr%fHs)ZP>8n_;{knO#H5o6D z(5p(>C9*%0Pvf<_)NW4vRzsgqQPo#};#WuGo9wnlLQy&}t2CtDa_jIOS35W4*6(5n z^VL^e5R`^sWUz&BYLcIEe4D1tNY<=x1#{>H`Q{pM1ANA+##_&ryGGXxyxpfy*%%B0aF?jG>5lSntoA9O?#z4tTVDVKR*|3X zk>-#{8nd+us$#oGylRZ#=1u|;p=k)xkUhOAt~POH-lfqZuRw&V+1{MG=>q;~ zENiyCc{>?p>q|TAPM;ZEkJE#V5YlWz|7{5BJw!IFY*(gIN0Y%gY0jkSU@{_Xy}#rx zT_yw3lw;mu2lWe3dV;GcRvVVEc`j6kmOo<4Y`v}ljTAw?AmeAHV9-5m@7aK{hWr6D zDGDSpsdsV}?uh$2YoNvcu><2X+0|&Zy7@DAvgKky_-IB1@u_l6qkVb8>M(33@kG`W z;R3;a<8*MJ9gL8ey6nHEv9?kXKpecTAKvV}8u9WSUkBz8v@oYzoSiEuyqTTLHd0E- zP{zyN6O5Oh-tAu(!&~2G+mkonWR*1G*|FFr)np_hbdu6vdyis7pftEZn?#@ont^j&knbGt`s zmzf_CSI~_8?c*1MIK0R8_fnsv8N9tyt{MiDwMaykGXgDw7U!Zp$d&seA3C^Y9?osW zWWQ&nzb<0H_UouwovaNGY1ZV_lO@BRs3)~A{Vg#bv&-K?qVjOE$_}-l-|9sG(I}#j zswf`Ke;SW<6aTs`egp;l)0w=}M}>=PMFNoRkVByQMC%%7ajFEDg`0<+T3xi(lX?C{ zF3X{SoZ-K5Ow8OE%KblviQ#)a{6Y7F7FSD#biI>_WtNVqVmf$$o@gxeTq8k32_p_9 z_U9vTEze26U)zSeJua?BX}o3{oillhyn;z-hYe9;#pM%3L6k7ZMhCj}`-7w_*Zz1;lheUcrWwAb z%K*~kdZn98S)v3hoTPYijr3y#CDphK-#_QrXQhbC!6el~>i0^Ez$LFKU#yX3WiT~M*{GB-g6>J8b6}}v_Xt(;{cYDy1 z`1fQ1FQ7pgv1YOOz@61@`tHktm;weH7?nsfN_O~nTeKq9>&^ggyBq_crGsge-gRCI zOQ^8_wMc?w_Ay!YukFbraa(|i!u=0^VnW?yd!zr!uZ#`fN^yU+#S3|r(dqOUe;}5X zFek-2lo&n%2s?aU3hDtOd|E1dFl=zFcyQgBT{AirH~L9D25FBNhs)bLZx3KbzjwU? zUi!ofZ+Jflub+uK6|3C=x3IUjrVqg}-mC5=ACmyz*KUsK_>00XYf#G4g(v7~6p_Ps z&;1FksDA+-|FGigZrzcef2*9_xgtd`mvRWh)Xyn&gmMH*B{Jpmso2vKLFR1ZUaf|f zZPTKboT2czW@GK?wy0JDUI%GT-DUT2f_JNgZTw^so56C6HJh0_Slph4?CP=eqEj~k zPyNpSA88na5*Cag$x7KVkKeu2f{)VsI49{?xpCJ(mjT|#rKZ(wC+=0rr~o-t-8!b6A6u7iL? z0|iQbITe4~Wv~FF8V#AhjN467P-$`5xIh@vkl%met~PW3k-Gu|m7&sI@{~FM9x3}O z#ekn5ZF`*aVuGgyT>P~Ek>-_~{u|A6C`koJNZ9Wwv%XitAktZG2;ly?Gl#I)OTCh4XD8VU%cMT#f|Nk7P2Jv&;N-56Na9OFomuRZ3awTN(1HihmS^ z;h(7fttKoRNL$RgzB)PSR4=YGavG7MNp7Xxwq@`!Hc4&*U&!LrSEVd^5M7HYZVs-Yz2Mb_S5uKCp-KX-34hpz4lB9gQ35CL(YLm?ru)f}z1K1KyFdpT%< z=(3Wa^N>N@gU)KmBKklQ2snBy3Tt!CHXqXmJr^JEm)2c0cS{aNu^wxGF|k+Kr+Rvz ziRsp)HdJO-Cdw0vYiqyzr@wu6q9bK$%Y@Siwu#6w)G;PWb-j*Ri|L=WBFa-~WF%#w z+`V}C+)Q5iCZ{Hi=$qJ#qVDdla;!@|+>|G4{^|SNOf~!3tADu5qWwy*SA+ID?gi^L zQ(?&RLCvoD2|F1LiRGbsNH1Sphr!*RAojfOM2**ud7j0(`*BdGc7*iy-ta_z zVtlJ6@#G(q=r$Hmfp9SfLIi5X$_2)!TsaCX^g@gD~-D}oK-j$)pNW^qJat}prD<9@k-9s)diRsW~5vkZ#k>DD;D zSdaiAc!CFacMlre-GVy=Ptf253GU8fA-F8=?hYZyB8%&yce(Q6{@<$m)}3!t(=%1m zH9h_7KF@PbAs4wJEZ8fhY8Bw{jqkCfLf=FKug$*^+&*2x(9&vMp?hbsuYB(JVk3+C zP^Gzm(MWlWY@}hD$(bo5UM`r#ZNh`asQrEfF#^F&@DwB00;6>^{;xza@7>6zgWlS9 zpnNc!4x+8?Bv+`naiI{hHo2|spQOA}^xPk(q{~~I2SEYT@v7gRVg=7T=!AI@aaEIo- zkFKA3W%8lhnZEb;54Gw2JNxnu=KjIX3yyOE9NJQHbPnXMf#*fHhr6Dp+3s?$Dno%B zG1Pw}Pi+`vWzw#zrEn0E3&OCu0ugh}?GyD0!O^Susg~OuLLesdvhoJtn*(_B&~tmQb{+J&Bf%Yy+Be$Ce|Ci9DtVl$an@4%W(Bz6}$<^5zLbeFPQC5CC zg^iDRXTG_OL?_GEgOdO&$0D(MCY#2jC~KZch6%{^MLRYKGRBmLew{Q;PKf{)O|lP! zL^!*KIH(vjN4UQCN{iudW%s9s^;lL=Dtfe%iCIWF0M%y&)@cXrupPHc+FctnWp7{# z8f;Ec{DA)d_kaERKh?4QC52;J_WJgpAno2WbA`)1Z2$0o-5P-RDN%zyD{M7sN^FC+ z3G81$>P`F$?ENn%*M9@mZqHb_0vyP0^S*3E?B9r0MfG99QV&HnIJi?64}Km|d+y3j zV$vrReKP0zq9NoCa?cKP1&YzsuoUCXSrn!o0SY+6t$uxf z3-fCfb|&~?#_WgtJ+v)kU^FUHoFs#H>+N7E6ZavBKdCfZ(L{pcbWvog%K)$@fy7h4 zEU{p}54{lV5C0OkOzo@Ph>9e7(?SX6Fw*e6ZN=1V8VgqA$?o`?GzFk1RgGf#=ue!C zUPD6)-MbKhJ$WJz7EJb26;V`(+0DCiYd;!N=tl=w?0lQMl>` zhb^_#nFoLUNQ@2_;q!oB>aP6O{i$2rDRp*ztGs{rXLhCQEW7!8(lMAgcN%l`ruXS$ z5V!$?vM$=ijbX5VVYk0}ry9I>xLAvA!wuI-igd%dXDr%xEE8y7F?PUC%On#UKeMDw zS@62^xzP*kqMCe|cCJY@rS+Z>ywn*7SSH%|>YQXJir++bU;YO0KGfW% zG(lR^*h>TwG*?PfjJ{)DdN!!Nu{7dq@aRi`l`7euSXh~4zFq2S7i_fK`D=24giJ&j z>tsyOZoiN$;NdolQ*1HkF}=X)yQU*xbFS8d2}$tJA-Z_p%sF2R39M+l?PN_w4RX%W zR0d{Gum=i7UQ|40k9aS7Pdg!nYyVO*@~UD9_ui_B|ID2Tt=W`pR6^9B0~umj_KzzTEwpKcX`HHSat}>2(W&>qTa%GBM&wxxH#nf(tGJy;-uP4E~HpFp9H1F!U3YHTaZuv{X96YLmSp+m9*$B`VT<1;9#)i=2+n44NhaAnzAL^!!pk&IhQ( z`G_zSw*MA5F@s$1Ve>c%JM*;8CLvo)r}REQEFEy}eE3*gLJ#Ce_-fpMj zEuz)LO;Owvua&GkMw*=9&^us9<>;G8+cPZDjkMO8pou5QiC%38ONmgSq%t!3!SLt^ ze^3FaM%5S(&ZfX$?V1@G!eh{RxwN;PpG3v(XcM%9 z-gSH`VP&GPFiU%f8ux0;4tS2mb(K_?slwyBg8PfDewy)0h!WU#>xovi)3|+{-qm|` zb>Dk0pICQ&QC;_A)Bj%Ok;UHqAv={hqY)w$P%mcPf>4~_-n6?F=rmUG(0r%AuZukb zP2HY!I#?Yd!@zzd76vcwR0OG!y;_C^`ae6Bu?V(iveB$GkO}9+QxE;}FL`5yW(_3P z&`e17d&=iI+BGiPkj8mRkDj(&Ca9Aa(wC6G+JR@*i$8dpm2_bEKrA%M=k^*(#ShfD zP}TQMTT*9ZeZKIKc+~RTASfZbcbNLM0cyO45ED;{3fW4WY*rhqEVg=F?2ZY>(Sr9C zKjl8vEvQx55I7!m7lwrBH54nCAWFXyROCj!TXoJ1QWcPP|(;?&i-d^R@^F8J_X(>2HO#*)nq)mCZcD9h)gs-CthvK_-NIBA47AIlpsSdm+(m6@CxRI5 zd(;bidiWhHijxZ<^UTjx?9l?eY6>3cMA~Bs{N+BoCSF%OxE_>(vvV%@Be#|tf@w$f z9y<2hnQ3MSN&)`Y-?Cg9?R_vKoGa_jW<6sY%2(u#CZGWbqU&-6&{{#Ytzdo9AEVpkDY?82m5I3j|@xIiky*=M;XfWIG+5rY~< zYPt#;S$a`m(@b23)-^{oFBgn@hw|q{g^q$aXqfz&&L&CR)u?^tH%A;u$a3t!xKyRO zwBg;B#Ok_|-TaZ<;B1mbFI{1BYD;GpwZg_Jec<7Upf=>m%_7d?XdzGx5Tzifb=;gE z+#MAGt-Qw~eT%!;UHhX*GOEEas3+UoyL%BU?XZ!CC~Ilo72`;<+sEVFwxVmeZ*2^w zt3N6}>n{g&Rx*~oLX_|gcfV-RD9`xf>3>(x{+Ld9{J8sD^W!qOV=8XlGPU#>R<1;XWb0<&+5XJLu-=@NZEn_G?Pcvhi0%7E6~B;C;~1ftQ}r!Y3kL(#yMd@oeym?w0Ufm=}g4% zmP%bOSywUHrt0OPq>607n%FmTe5o<3`L~o*`}40qir)uF$-G{ouw?R8gL7}TkICx{ z`)U*t{`Vxpn_OSJmL}GZY)~1vY?nD+;(((X905_;uzI)?bd?R_`cvc`y{Ak^F-kK^ zx<8vXPWEAapbB{9I5Di4p!MpKhF2XgE~`p}YQEzuE|Cqv+Su<_FPha_N2tY(k;)bN zrCXzujGN3keI(;xoms!7;|msvO) zvQ*;a4Ay$XKQKR*({3`r-F)M>?}mq!Gb3>4?$1!?g&lYkX2WhgvF3n>y9zmQu&ECd zn0;GpLBO|}=tAaLb$=Snoia?U<+U{$kzs`UywG7d)a7u-Phk9xn(^hc0FE?P>*0h9 zM6LZ9w-m*0p^Ph-RN=1+nva{m$b3;kw$knD6TE066LD=cUjSB7rcm?6w8lon9(vPW z2c@tNnDltLa7^XWzyMoIpwd!Jm%r>!rbf#FVW&YR9OpB(h{cY72XSLbH|3aS5bPKZ zMMD=KF>A}buE@P^s`K!pAF_i*H(jtna%&#=;xiFzvQ8dHG$%Q}OP_+?Js=NK)E1g9 z3EHa{oLe84`sz^er+d;2rx+@HWGcV#rBS#OmCdC%TK;PGj1A?i-%zy!)u(COJnpdcS}6{qOn@`!BxmtN>SA0%NB4 zD_LG3(?W1?)z5dRH@)r6T{6{0QpbrWC%i|Nv{Lceq1!!?pULe3+dZqF&V|WUR|PA> zIXWX8W>Q;lg)7!o+_?(+Pghp9MfO-dVLi7@W;4hCU9|`Gw7&s9EoG_W$KH;`pf5*L z_cy2csb+hW%GV;%CSzpQY_^0lv3Y^-#@}q0{PN;9e(cTpZH=M?j$#ubmCSItyee?{ zFtBhjl~S05K;N!&Z7eiQ@k@|7%*GNZAT`U!VM(4BDaVLVI+5sO_g$iPvz=luWrVTX zBsE`Myq9$Sq+7e-mL1L3jy}zFelhqyacRT4^BDZ6HOcD`|bMI;5XlXCzP0@%CU2rB&4gw*jIc^_%| z+?iEUQ$>j8>(;hUM|afoXjCd0;?Z<<(Yxxju1%)WSnEbrS zd&@yi0g&-`oTXU#>N2C#n92+46b@<(=cG#hH8T0_7@gC&hg2xrQl9lEXe?U@&VSwi zao*Ss|BiHbtn-Fsonk=YxmZl)cUK!hHz-aw-QqSRokU(awZ5Uy!Rd{kL=UIb#7^-m zM7kLbWSd%q*~~2sc(UdERp)z6>lGfpeJB;V@ZKRA^4A2_-$O$3mwpDmXONo6F=ln( z=7Tk5q}xMFssgXgbYu-U;xVm~2JV$V)JKHbO{f0*MV}B+U#W^q#`vr#Ca|@$=E}?o90#KGG(xDtmBDcbb!DjR!or6+x7yr;ax0sr>V7jk-h!*idnLw&yhkZK%v{a8 zngg6e+PPm;1~T|x7wA1HEcCo)$(fe>PAE*Z=PV)V1Cv@LR+S!~&X?uQ0!vy$P0KV< zNz5&=oi*9q-vbJ4s^C8H!i>1i1fY9GGv28p*9EAdjA}zO_HXAe-V@?fR*qF>ZVMXf zgl~>57bt~*t2Q(!{vRPmG7W4@2~4RC*^Bjctb#*T^?BP4$LY3<(KLX?M|N8Rst(Kh ziIBz3r`G`NivX`?Q}m$IJvM;={>$8~^sEs+r&;mhA<6|QH%a-1%Ok04pjgj0hW-$693;b1~^q%Y|#TU9j&d{3t_)31cA#a-t%>zB?G8|wln z)o}QLC$Be7N8%*M>1lF`5nzVNngf58i_vmLDdkdcbGdut**dntzq(( zz91h~s(0AP1B)(!=s;lrp%%O52_euo9m~W}!XV!!pDm)!qE23~J_M-AG`EwW8(qyg zuhzF{USdV>VK9P3^vW_Q>-vp#BHDFsa{B1`k4+;ZxkkPWgAsjX@h?b3mIIe-y(u_C z(LZGn)slF0G)3M|TDp)K7#{k`7ybjP54VfnVP_$b9NKERCk6{Hsy^Z67!5?u%W2`; z8u{f^(L5Yd@%Z&5xM8M!WNj6w2DY%-RR$juhqIWjWQ_;L_M+wMbB}3qGrOE|*^W$( zldew0^iY4{;J_JwZ9G7JmCmarY`D@l^x7w;w_rG{;k%?u>BAn_R)<=5QuJ*VZU!a? z@i3BLWo%aKdkpemKO7bOvpUma;uDAifTEPrHyeu_3T zo2SB9IbAhQ|6A`@x#jxYTvUBJ1E#@2sbfSm{7X{t4Y-HL{cTCfPfJ_R)G-`#Ki!b) z=IN;)HZ1gbYMKfw3N=pdW7uS)bqoo$eEN$9HQ4jP9-d*t-7&&~BKx{}}K>ts$~G|HGh^ zmFO=pI`sX&+R&Q6t>v;BO_c*SBv8ZmKR-bg#Kby-frT&RDn#77^bdsn=k5Q(xc_W$ Z@oaz7_do$@@`QsOAElM0sw7N;{sTEMNag?l literal 0 HcmV?d00001 diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..2c7dbb9 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:edge +RUN apk update && apk add py3-websockets +COPY --chmod=0555 entrypoint.sh /entrypoint.sh +COPY --chmod=0555 entrypoint.py /entrypoint.py +ENTRYPOINT ["/entrypoint.sh"] diff --git a/server/entrypoint.py b/server/entrypoint.py new file mode 100755 index 0000000..133fe3a --- /dev/null +++ b/server/entrypoint.py @@ -0,0 +1,23 @@ +#!/usr/bin/python +##!/usr/bin/env python + +import asyncio + +from websockets.asyncio.server import serve + +async def hello(websocket): + name = await websocket.recv() + print(f"<<< {name}") + + greeting = f"Hello {name}!" + + await websocket.send(greeting) + print(f">>> {greeting}") + +async def main(): + async with serve(hello, "0.0.0.0", 1080): + await asyncio.get_running_loop().create_future() # run forever + +if __name__ == "__main__": + asyncio.run(main()) + diff --git a/server/entrypoint.sh b/server/entrypoint.sh new file mode 100755 index 0000000..a55b7bb --- /dev/null +++ b/server/entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +test "" = "$*" || { exec "$@"; } +echo "running /entrypoint.py" +exec /entrypoint.py