From 832773074d0d71d9e960c917a2125491501b91f7 Mon Sep 17 00:00:00 2001 From: gl513 Date: Thu, 2 Feb 2023 20:40:59 +0000 Subject: [PATCH 1/5] Organized things and added README.md --- README.md | 7 +++++++ part1.png => images/protocol1.png | Bin part2.png => images/protocol2.png | Bin protocol.md | 6 +++--- 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 README.md rename part1.png => images/protocol1.png (100%) rename part2.png => images/protocol2.png (100%) diff --git a/README.md b/README.md new file mode 100644 index 0000000..ccba837 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# What is ChatKC + +ChatKC is a specialized WSS (**W**eb**S**ocket **S**ecure) chat created by the popular youtuber, [MattKC](https://www.youtube.com/mattkc).

+ChatKC is different because it uses Google oAuth to authenticate users to the server after sign in.

+After the user is authenticated, they may chat on the MattKC site, or on an alternative, unofficial specialized client.

+Work is being done to fully reverse-engineer the ChatKC usage, so that others can make a decentralized network of ChatKC-style servers.

+ diff --git a/part1.png b/images/protocol1.png similarity index 100% rename from part1.png rename to images/protocol1.png diff --git a/part2.png b/images/protocol2.png similarity index 100% rename from part2.png rename to images/protocol2.png diff --git a/protocol.md b/protocol.md index c57cb31..5ee6aa4 100644 --- a/protocol.md +++ b/protocol.md @@ -12,7 +12,7 @@ Platapai: ^^^ janKonku: it's particularly nice for alternate clients Breadpudding: That we may or may not be writing as we speak janKonku: very little in the way of coding needed to connect to it and have it working -Breadpudding: Maybe reduce the rate limiting when you aren't streaming?](part1.png) +Breadpudding: Maybe reduce the rate limiting when you aren't streaming?](images/protocol1.png) ![MattKC: there is an argument for sending the time with the message, it could be nice even on here to be able to see exactly when a message was sent MattKC: it's obviously recorded in the database but isn't sent to the client @@ -20,7 +20,7 @@ Breadpudding: Wait, there's a database for this? Breadpudding: Your poor server lol janKonku: did you record the one test packet i sent MattKC: yes the chat is backed by a database -janKonku: should be type 'test' :)](part2.png) +janKonku: should be type 'test' :)](images/protocol2.png) ## Connectivity @@ -421,4 +421,4 @@ The following is a list of the currently known authentication levels: | ----- | --------- | --------- | --------- | ----- | --------- | --------- | | `0` | No | No | No | No | No | No | | `50` | *Unknown* | *Unknown* | *Unknown* | Yes | *Unknown* | *Unknown* | -| `100` | Yes | Yes | Yes | Yes | Yes | Yes | \ No newline at end of file +| `100` | Yes | Yes | Yes | Yes | Yes | Yes | From c02a2e078f66bd61139ce014264230d24d23345d Mon Sep 17 00:00:00 2001 From: gl513 Date: Thu, 2 Feb 2023 20:55:21 +0000 Subject: [PATCH 2/5] added emoji spec --- images/protocolemoji.png | Bin 0 -> 16077 bytes protocol.md | 9 ++++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 images/protocolemoji.png diff --git a/images/protocolemoji.png b/images/protocolemoji.png new file mode 100644 index 0000000000000000000000000000000000000000..13e2ca2c9311fa26aa10619a5f94ad2d5aa7f7c8 GIT binary patch literal 16077 zcmaibbyQVf^yj5PQbf8$>F!PulrHI%?v`%Bproa{J03`@6q8oyfn&=UaUSM9y+{LS3y@GvDai*fk$5` z4eM5J6P~JHp2at*a3tgQ5G2Is&!5x7!>dSo9v!TB&Ms5P4jYS8A9MW}+@?4*Gut-1 zyx3|H&L>h+#5Sn z?`XJTOb|3mo>a`ay83i%>{Rse?zE_gf#cWg>~(3~(#=8La+zM^borm7ySqClCnu<_ zr^kHo2YchkgRFRWoAQ>1;r4d@Or_DCu;}Qu7+S@TuCA)+_gW;pb_=b3emXiN-0MUp zwzj8-jmeD-4ZZoI4_E6Q!RUR>UPsNBZbwT~larHv=!hQbL+PS^CmVg2hl}$qK8-TD zS!UKgH3tU=!4gK&(kP!hJ@!iLL>{h;t(2kB-Tnu&tnBUEtE=%H7m;+uk64XvM@0n% zIq_Z%52xvFi10pGpM#8kXfh%DyDj@9X#a^kJwHztbc=ZV>SiZDy`j}I!k zPvqv$ORMgQwbfO2uL~QNgkqIK#Z&>;+mFNhv(?E-Nt_(xg>N1`kkKkE%A#b2c0YQb zsD@)06BAZ?O}05C@=ZMIuOdj$g{*fvxfT@_S?&LJOoFVzcW&;fdl?IMUfyIr$2HYI zaad#mvB}A@*<(~xR6MqxdohY4QGF(p5`O(l&0d0nf`x@6B_Sc*ilXg(%!5*(;~R+urdQ>;<`f$c|daBu>vL7thYYY?KQSf`~-y&M|! zsi{~mV#iBrh{)JdU^hHGMrPelK=puYZ?j;GG;Fw=nB}42F=D6Wwq9`D=HZ-U`|q~- zu9|j2s*sY?$P8RoDqJ%*}hU zvOaKxcCIoRvw9|xX3lc* z@=3|b_KOYN9w#QRUv~};4sx0gL;jJGmuEXsh~fomMc|#E1b4XZO~%E;FOH2N&>T2teG~uJB(_tO^W+bTEY{<#YEq~17{QUg(;vn?f zHx738*`JQa9v&XLx+w_>GlPkoJ1(AXb!;?SVqRX2k&%(1p`lSxQQ6t+j}MokUnM1M zyQ+c(|?0*?kcn;U8VVoR#?;Q~L_=hJe(=Ct&50%Bs% z6|u0!TZ^FqpU3;l*bWE>7nj3zH#RLTt*8hEi(LrG3P#-HSg(r(sT9V_g@uLLSXuRt zj2OVIoNK5x%i{2^G;SK*_e<>s+#KTi-FeRX(rDbYriUe6tmxws7 znJ6nqB_>|a+qa%Cc^$#|H#Xj)PgaK&}?^jhvO1Yz3uH+%N(?(Q3#=RPnKGqVU10;j`;*{UiItk2o>>9Mhr z`Yz4)7xN!Kexy}MK}AQOZ*XOVP!R)vg&N%LSC4?|2?`1Vz2RYqcNuB}4W`ci(P(=( zHAyB-8EgydnHCA(3k3K(RaJZVwgTmBuuQKjHMu~?0*iNld#NgL7Z@m!tMaZ?Ha|}e z?QtvFzOS%(vC(b7gabeH{CYbb6dn>(QfjM^!be2nH8?wKy4ryh9E_BinF$)z?CdNT zaX96dxjAhEgY^4b?8%v#+v{s0Vq&}5Dx{v{q2y(H78WZDi<;`{W*@=ZRJWxUs2z(w zHCvm=_=mf@5#PSuT&+iXd3u@+CB3ozqne{q2;Qj@)D~!cc{w>2evGcJu2JUujO9&E z+X@^QR}sFr_KBd!mg*!zN_u+6rl#-p^|7GSt)Wh~!Qa0Ns;kj^9MMCc*4ddZ{ETXF z+LWP*p~nw3{_vsT`A#n-9DafJ_@%e@{)(W8&8lDC6xlp}%tUsaET=;vmvI)SK`G*E zRfT7=jMfhQCub#vWA-Ig@7~o%y`L5zdU(x-A*-l}j*dRk*GDACbM<$%GloWp3pdZ`dtpMqZKj*Oa%169Ps09{>GqSoy}h&Z z^TJt#FbdJ7sj2scYD6R?$L`(-Ow!W%Cnrx%tVK9EooA~|P1050zt>a0aJ%Z|;^C3W z<X7F5;9+e1 zGEQd5thz7MJ5f&RTdrmtSNjHbD!2F&jHqn1Wc!fvR+9hfB3`MJ4Z z$w6ODCH3`^Kt`{CpxDK@y0y!VIA(AAj~QBaRWxK1B&TambseRmxiv+a0oI~U6LZMW zjRGbFwOP0jBSVsktHgh9CY;cbtZi(Iy!2e?n=5krjTv;;ObHe5iXbYjp>mdG@z6uWJn#9Cs<0AC3J z2$&AUA08fl`SK+IZf(Ny@!1=#pf>?Vn(yCF1;uAl_EyVs&dTFNKKw;SM0EG?uwQPG zFH|ctXibZj%|35D*U43hGU#h=o||bOsRht3BO{}sq2caMh=c9daZ7XF1kuLjU>nxs zyO#*W&<{>?0LbPljcFloFa=E3+Ul-AGf(%ud1q)inkR1$+7tOf+$~{<9>AO9xw1)k zSf5`;y{*yMxBT~<{QMr5JK2;9)9&WBrFZuC!$L!?ZETV_s3ip3IM~?2!cvaUXAGhg z(>ru%@I$qjc%TYRIgW8E7z_sazkN0S$%P?~;l+#NDUF$}jfEE9`-Z$6JIoVKOpJi9 zaL*8+jGbh{aYW&^~v6*0}+y2o;5|p zgxGizf(EpYmiTOj`T2E?jU=5FW7*Qm)sJv8vIDxhx=hT>aQ;%+$L>N_Xo%PI%5(*b zv0{d!qe|s!4E&@K&CT~mzwkDmdstc)#>B*kQ9}N0ZEaOmDzKaK$8S@mI(2s8;PhPE=l7?8e=(aqrs<$sSa*%ULNkMELpn0c5ngIJ)v~(Pe3MT=$fc3Mt-u zdr-2rwgv`QR7{61i$?XRsS*OZ#{2jDf`T!^Idydeu0i=vtpx&>rUQd1f$UHdT?kk<91U^nJkrc8M7@fBIH_6bNSi0 zp1=-q@bQwQeW5-uhCbl)@Em+-Wl)7=HX(ih!afD$45_3?_jUI zHB+g#xTvVl=+Q5>tZWz{|JJ*c0m6_%wW62O5etoOk=m%@X-J@@O-xMaw0O?~bRjmh zchny@AREsFXv^-!!F*rr;9wZc+CZz$E>)Lh=IqV8ckck%(%08t0yTn7A#yfn{YZrh zQ=$`cSb4`xagF}&Rdhpxr=Z~c$w>>zAy|IU2xxHrgi5XfXw_jz=G)4UmJ>?AItV!0 z{_l*7i;K@;J#uM6vokXqG%>QdxK%^MU=+WU3cEV;dn{ICFy)vH@*N1`s&Me~x&<}7 z?0d_cS&^TSA;wvJb$0CDbiOy^%tsn5p{1eW^>@WSsbnmBQBPObf=k=o{in9P_w50% z;wm#E9i3*C$7h_lK!!w z$jYQB0IScLI5?57hyc*LqL^44z;1=XE+&6UZ$6WRpgg%RCPfs-K_p_Ji6O$pb@~Sx z`(F~2z364*>cxBUk3Zj0Q@wl%fDPCgIyyRq9;hdB;&Axje>iks{r6>&zp?f2%POC; zmlIC@Db;^u`w!uh3Y>qJ7I(q;zx+D{2}Lm@@5TS-stX;Yju<`3L_;`?HMc+9dva|1tKnawiutMMYLVzTV%zQK7kc!SM-BPfyQ+;Q#pr z?jwSSI5#U2ZdXDvxasxri3zXO;8`Yo*;AJ;)KJxYg>1tAF8@$TPH4EwXWJe2*3FF< zIcREp9QT>C5)=M9A32%u@{nzgL?)lljhwjn3y6@AP%fFr6o>=H=0F znn?mS_UsdQXzBlPH}~lhGT=yU`#Yonez73~0^77{a2q=ZF zJ8vBva6*4s+1SXut4D?a&LL&*pyK2Ev-V4NG7I7FwKpf{>9=oAR^0|e^YfgnN5gGB zo$U_R*nt>bj>IDr+_3Wnl*uq!e>+24p-tw70B!~Icgf%8hwl=U`i{(vf3*Lc61pT3 zD`=STJz)OQnvT@Q<;r95*Vyew-)Q>j!I^ZGF8PF$XLVI^MAC7O?ZVYodk{);^6h=w z_^9Yskln)-8-~|my`{K#z~k+54?=ZE2Mtgpa6aAi$HmUq*^>|v0Zk+Q*9SJju&yq2 zK%$d6l$EPVNRl}?IJoG+4W;nd%Gj3^c78cLO#AtB!qxQ>`(?!X-gL9mnxw9=aW>8I zF%lAzmX1#MuT+l%7$!S=E3B?gS@w=TA_7Y`f#n5-F=6-iDX*<(qu*1*YW`%NW{>mT zDZog=!@|slJePWVzqA`pPEAn=6N63J_W`hjgF0THvoYGadEbj!+7FeIr8E#FD=X`Z zRUvVlIi!$!6l6^+*zRzu;PLUX70h$BBiOhHnU>aooY#&ib@=K}wjw)m^!zSe`P%lP;M}RdPHTU)QhJ^~2(qHf7>p&~H z3F>AhIRyCl*5m1jvI9?{@6k@-q*dS<*E$$+@#|f^O-m#97V-OuqzQ`0xF>?zi}&FF zJXjg+IC}dQS$Q9h$>_3S#^r%|2B?z>|6!0s7;^5~TC%d}A(&Ni9(PwbfJOs&#)$WX z!}R9Nvd(_n#B7O!i_87~+|Lq4GpK)XkgBz!I}$sQ-S}oWwUJA!rKJUs!iMlNE;WX! z%>fOwL3ag>msroA$0dF5?d%k$Y-{xsf!gk_os*Jp6`ogWdOvu7aCSxyF)CIsJv%uW z?5kgF<(rx=U6y%;%ahz}Igtz=_|z2lByQ(#1Oz6ipr{BQ;z>3(G5MaJjtyl?g*UkD z;6|)xX2w`q?~#&`$;!w8zJdltMNQn=mwEwK1m;#^qSnU5;bGaswI3!;3>uV#CVmnC znDs{@+z=yBf(QFHjw$LoIzZhL0CERu4;Vg?6ozXTK@ ziZ5arz+L(scu44Sfs3~;ii^0kvhw)vYPJoJ*ZmD_b2IMZ;v*v5_6V(95?9~oXhn0g z&(YF7L!8WMLo(03AR}YXz(8oH5iJdkr@Q-)#KbWR0Q1-n4s5%-X+rUtGbS8~!yR|k zbOBd!IrEm49RSlIH#aw&j6_ZZpfY3+EFr9Z1rJY8Kmq{bFg9MFo{mpRSwMRx0YGb2 zwKMLs7^rPw!3JmyLgb32>gmE>WmQ!&6FgPIu}|uC57&N$@{zK{^fme31B^lgLqa`K zbI_wXS$L_V(DZJdIC1zPrOZBa*H-zW=9{dl#$-OK9OVP%lml6N_~S#bLmY!U-X(?J}ueKNc@foc2v;CV9= zT438Rm-a7dO`NIp^Kfn6?IWeM%dvIumFn(Py#Z9v)jd1E-q}e6oW*?b3l|56cB`+R zsp)1MqsHgoV>|!w#~Ug~B_$kg1j<(?`~OOH>^XFH@vT=Ry>r#Y&25>r$aHs-9&E7B zSmz}4CTxGm$WrI%fl8)Pri%zx3XOZ3M_MVwE$n_mE!+1WV; z)y})@t(ZNA&-8_djV)vBZ%z(65>hZ>$WY?5&5ezP`T2>ZrA##j2;hH80Z;OnzSsd+ z2*2wKF0QkMdf)YI2nP@E_tX?~LS3|Cu1cXA0}>>XZf;0vVM}I0DNo6!S#35I^-_9w zcbEGsU&OOdo?p=I|G3-$NmfFl+vj3-+t&5~!XYKS>p-toObZ+6?L~d^1Opp;`_uz` z4(W)lqAA5)yui;Wnl1u9r&~Y~Zl0X1SzG%6;h-Xtk?8>pzErQ8p~irTftb`m7_zv0 z(pOPg`QmkW06aWA;L}h>S=r%A`*Ojo;$RBD@iponHd(0Zw*MOq0=qh? zC(?370nR^pz85H;DpXM|C;dqP$lsk!U?B$`x1${CH78NU$t1Aod0*Igzv~lLRh^j8 z*J(8!*q*Z`(acOtw3L?K{`$-d0=KC|>`$qfzPd4Eis_bw)f(uGX*>NZR93Zs+Si=; z{QX{^A85!Pc2B!UnrGY{o^S<{W?tjm!PPp^9ouL&r=!dtrtHR#ik;jXu;M};8dqXvG3^*!IDvgnI2 z&8E*s_Y&&?&)W>=TYPf`eaf|`Wts2YEx6_`4n!+vQ!bb0Q<9QCUhLPSJ?nnFT33YH zRvkjX#!xpmC-69yYyVLB+a{v5wKX|8S(io7>jKPL)ej%Wzdipq$lQgNfIMC}4~0=s zd@wd{c02NQ3K>{l29nD=MaA;QM(dd};W=v1LwhqZAyh1^C7AWd!~JteZ0FJ1$_lKc z6Ie-Q<)`6Ns)cIK+bv2!uZ@Z-%&ePdPJr<%fy2wQT+bH#2QwQqs-s?ZNL#>;6~=c_0y=0@4r`hQX@)Q&UH$_&q7) z-?Lt--wbRMK6w|IQKu(q4+bXYND99!niPe}uWzO{AD4?PpdUgW(dFgoW_HSjYSh%! zUw=h|V$RJs|K8I+F=5;twv(Ew2P8hASS2PW&(6;qG$%#>_)+729ICIMhWqslYy)5y za7|(T>V#*?Rk`k?P3K4)$(&`(!QK6RD=RCl#*0m$FcUn-StSDSFs_e8j;{fGof~va_qLuAY+njjg@9*my~!kWyhNeilwr zcz%9a?u)Zg-cfsOE%Ja275qva-~GphExe8M3bXsdBHA^D|=IBpivs@$>XFWnq88Ej{iw;g6wfDT4*-!LQU4ju9fm-0{;}E{nhWYDtWF z%&iwAwB|@eK9z_VP(OwDZ-U8A=)YR22 zS8Y1OwB_?m%(uTAXtuYvGpaM)u6|DMbwLgaF#4gp#LJ_us#=T|M0eksMxNfY4xCIl zh(Bz5e;@1;G8`NT;z*1g*%svcy}h*+#R^K?!puxYof_`~OQ7e3ucJevTyM|qSS))C zICq$&R1A!ax1fFg)I9{k&aV%S+S&~t4~yP2+AsEx>+_s;Vv7PyL>_s$Tg+MqgMr8T z_+(Z5gK^~X^$h?O$=*rv@gD2lLy;o8Iyi}`shH5=VRnv+rR>0cu9unF*75N+*p2ye zUw=2>&g||ceSJo&TkkZrybPeOH(0XkgS*c{Ol?$sn!OGT_+kT0(RF{n8rz*g}bDfiOww-Jr46l}2kAu`uA)Pv3u(J4M!mMEB zZq7;Ov`|mZzz^3Y*=ASLD*p z8S|lxIyz;}q}+IyuitR_E&`GuS>^-f(7BwcI!H|0C)CISfiUlW7dumZA<_wT^L}I} z=0~v-9oP9Y1lrdjOJl-FxL<)Vn%~p}4*}dC79P%yku2=B1qRdJ!NJhT$nwwQp_-br zh=|*#Y(ecdHZ~X-q%ldJr@n6>2yAX)zXx5?{3zuwGP&c2my%g6_MZaoAKS7&8T~$9 zr-kKD)kH?p)FMF0K?*yv6fwZg3J+&C)k{cCZ4M6pRa{<%1hFKPsz37Mn;B=jO;V6AzHAocT39_J+(dxq@j_3 zgpNnh~&9SMX0&s1UjkgK?{W7~|isJyp(#j|C zIpr_{3%uQs?T4Jy>(}?Iozkf(DSeF3$s?tiQ5=r+cYd(L+3=u1KXtK!+Hp|N04R@@ zq5j-iSy`ED$42Po1@JSY)h+}dKu$y#Ny;BB7d5b%q;4sSkBYIdxCjSu??PW6TF4!4 zPJrMW<}m|BMSHip@7|5_;7cxSZ;zyAp0h&239;i=WMBZ%l9FaewdbeEXzIu z&Tn&{;!R8h_3pVEuFW8Jxc@0iAs@VqnIe zj)Pt&UEF2+j%Um1XY$vpe9UxosGV$BpMjxatilkd=puHn`1j-lJWG|rn&#&4 zef^Y_6m4y7v_K`gSa=|q5@H~S{>u5pdVTHYdwcMAqc4V;xv)96u~FLEI@v5tx>Ozb zjevMxup%Mc`ejx~Q$z5YoUW*ZEN$WMlqaE zW+GAThH7eR-T=cg;ZMxZ>oopV5^_KcZ8v1muIX(P6T^}8@Yvj$*oG(>89{CDfKI5y z@H}53mFBY*0z%K#&COr;rpw&iJ>bo zPw9|&wPboA^OmbDYHWj7^3B)DFqD;355TlfPfozz%2P~_k8z$K9uC>3E_m~pv9U1# z0FarHQKs48IxE4`r{ol@%=Gnce@3H%(Fr7_q!{A*v!x^9+s3jh0W1{5X)_#U)oViV4-ZGdB5m@ytqlsg*Ri+7eG&}d4JpVh0mMjzK~qmIOZ!td z0t6Ze47_vSdGzwLefYFl^uD+^pdmUsTAY@MMZc-WZgI|Wy?d!C5gD>?Iu?Ke)-~cac7CDuYM8kAGEm4V zKk_XA#fpzmy_AESTd&rpuWs45T(~g*HMr@yxjFlVx4C8*?G*w_uT)j5k9?LMqYVUv z{QB+#R6q490T`ni<9qp?2~qrE$pJt8Powma$lCEV2K$ioIFfqwH{Z~}sMbWF&;z=P za1=F;Mw;*H;)137Bt+;1b_$hbNZ`q93?S8Rjm>M}+YQn3D07krd7NQm7~cZQ|Fr8F z(QmO3>=T0=Av7S8&B692C(W1tlD5F(83Kj(kGk`|_2erEvJgJU=eF!kGUio^0YdZA z=>N#d|6kQs0K&L2Uh8aUv-!^6dXc&<{WTV=T{dvV^KCO^dKZ;G&VZ3^`Sb_U&|i~6+`PPkej-nQ{TD|& ziR6Ck|AuN((${8ov3d#-F7G8$%)`30`2;Gr<9c#aLkT`{41Ue@qFo9S#I*%mUWjKU zpdXSV`u?lHbteLdPOwY9Z~nelyiz|^3ozU&SsA;HI|iTVO3mZ=1$y2Dga9Mrab zYRZu)ky%h&Ea-Vg2xB2TK0P(A{4q4dY-zazv3<0usIN~I;pFBnEh@?~ zyPH~Alm^NqruW?y5CP=m!k#^gsd)xUey{v-Z-BLRT!9oXmz{>D`*eHc_If*2`^|$8 z7|u}zb;ZTWNymmkhggAZ2R&7K9l->mxn@FeTmjGh)>O%>tfGd`N$P)TvoG( z@tW~lZ*Wnnt(6e&Ec$LU0I*NF`zWc^`tBscI$t!}=!~@^CsJ+kd@@YbZm~fll1y-V zZZ0pC@6678bcli?1Oz!^K&+zW?&J~K47Ay$&dwmpNszf~a^8+8FCPgbisDGbfKE=2 zUQz3f;zfj|EG;agq^4%SBjUBQ0nq%P?3|IG&kMlt>(@av1f3NL`r3|%x)Oje0onQb z_BNc5eeLeL3oylB-Q-7q{$#{osqXLFWM^;3M6zS7pI%dV|84c9hBTQjLNLqo@|NTj zN7%TyfTsN|GxJ4II|vl~lYRfm&Ne)^u$gI)W>G(|UG}oC)?`vHMuX^>n3`PZ6Dd&- z;5!$9*xAO0pMKrJ8bhet4J)@xc%{}j;>aYTQyN{2PoAXmvG8GTrk<5NHE`^&wZU@4 z72RmB_|flgjKPj^OX#VE?{~fg1Q<>g0sh@UNa6+1`raP;Qb^6-WRM=oJ9m_JpBP{*#{t zR`>RN4t0gNT`e3)(*<0MJ{o&_dpkLw)_fC+2mR8_%AbCT2`M_*i+5-A@$)`q3@05XJF!0YVOUndV)me+?;^bSEKy^IQ)=+ z`O}XZ#;?7E-{oUKR-DN``YtafCEJfVo)L(v`l7@r5@;TWr=A^APZ9qjQu*Sw%&2|Lk;73S4#zd7ujMDSu#Qc?=TwlHmOpBpZa>BJ9B{qE3GQ-jx8@hg-7xbetAiE(jp zX=&`HGM|85BFkD@QdkHe6HzCdqGF_h?<^)nPJX!F^VyJaX>1IL;wic;epmX{nk9*x z>AakrKn?@~Kd*fr$N_?21rXCwg8@)z1;P!GeS#G78RLq6{``4yfpu_sIG9)+yC{xh z%hcmo5;f5y1Il-wKs{Ov%_M~5Jr&$*K%d7H+cUUw#iarMV$;`|O8OeNm z!L31@KR&6vX!=`Om91@mLSyR1KjYP+J^r;4NjRZZg=-@wnNC}lx-Rm2`J3K1{a8<- z72Ve<_C>oTV^*jR&D*Z23v6r+@Q|ss<#r{kDI=kP2(n2~igzaL-WbEMi6x7xiVoFX zw$L4s(2gDTaz=qr&HNa{mP_^vvh1$&fndZhAaHWs1VAWI+^qe^!R*)Lu8B~n8%|fm z3#EQ|c$mZhsRe%oln?*p?Z56q@)*aIz!?FJTQP+%H}!4)gn(dUjVjoxb_+kTplskG3js+_$m>E51iKQfF`aiM-oEVx5Rd+=udnaoqC|a@y|*&RQPuYU z)8{~H09J?1@jb7cRkOk67(_#fiHZLcwGqcACW_zxH3Ej(%a<>KR#08-TvAf9HIhEC zeOguJ1Y8X|#X_CkNzfcICFIp~3$ZFg)Gvr$@$wQtW%}|j(8EZ0FJ=rbK}_RkVy zPkmEUQ(oQ$5F&sL0cSXrGzH!f{8x{~0ygXViSE2(X8K`YI&z%2`=K`8X zdo*810W~x>az|36gN>=L&jNv@fFK{A&GJK#N@32J1qSkK1A|r&yt=!71u|nqoK&*1 zvS@){!i8B91`_4A0JF)$m`ggtF0n~ol)46v@FvwT0QFhA*8<)M#gQ|Mler&pfP z$_?|OP^~(%)%wlgc34`bMK4Oquv5x!uX~gO+e98X&!@>dH!nTMeU-TuDX!WRbyJ4S zBfpvOf5LgV&yg;yq{rbf(&lno(0B$u$hL|`Klb_S zUe~5Jyq;r_rK;B42mTGHx%dm()FCtM%It~KvNAiGYJ1aC9{kWgv+CyNW?(aIIBBVj z2LsWGI2<25O<<8my^T#t(N$HIodwy`-v0g*U;}PoB-GU<0K$?!Lhz)~_=E2Ri>0w~ z?(D*{C?(C-O4Q4#f7&>x9e^EZsr8uX=`+VHzrZ+&<;8(KOK3=J7nE z0|11=?~VwHXV#gUo<@eOU=4+ZmR(<-PEAc+8k|>x_BXaula%D!-dtJvH8OIh#fLpJ z^BMP7EG#Tea}f|^bhti=yYbw`<4hRPtJ?T=b%hATanExG(jG*3@egakWo5qYe}X_7 zad=`PlyY)o0;qpLWKc*~@Y~6ab34RSN{hBNte!n;xm8eCk55Q&lb3gumQE-y2QTF1 z2JJzq0?1sdj7UD4YfG_Whxo&-wu zI10p{@9A9$s*qD48M()8hc$biM$isc>#y9fC&ZeB;*x+9Ta!BCW&Y~o& z029h_iwR3;MNDWln?<5e3Z05pC@mG&Po8RVV;O$>N7@%lC6n**RboWtxno&n)(geh zW5$VY1Ge?*i+l7MUT@Sl#sS(4h>)pQa8YpxCR@h5q{8KvAkC^Q8#wl4=r<({5G ziE<$Ke-?2_zv93LvUb2RrQS>e1pDs#)c0yNzpE>!tLs%DWl9REfw$w>jDh_Wxwf;T zBj^YqHhl!}=TT2nllKWaBW0T(5SM|9Et5B~;{wTLDt%W3502XiC-)JUmbxr;IXQ?> z?~Id+OHEZ35S5UjM7i#DN6?_qpFHU$X!fe>>gxJYQk0*sp{UsN*{Vh5pz;bA+Dz%I7kH`TgJGZKM+22y#L zC79w#MZEDK5WsJp3;wsM1o`=6_R`rfip$Fd-T&g`4!*u1?zvt3CZ1@~#wM4mVj0r^ zHuh4%%ME8k{3dm`>nJ~QT0$?Zo#ru6W{l{ziY7HD{!Fi`I9I~Mhh@Nsbbuege}B$; zDOqd;|FfkxLl*Jc1$hqen4p4^5)e`8Vw0;rX=rMa$W;0R&XYvK*a!!=w#-CD zm&3wzk|b61^&d*KI2}`@q+VFTlB612cV(Fl(M?b;9uXozCd7OsZ6Zw_p^eKP_A&N# zKBtQpAvmN`T2f+cd^h;S6-wtzVYVuG*HD(K)Ev`Z2H2;%TXsHQEkqA01WPyfrL<>s0)Gc!YN zCLA%*(IBkkyVwA8baVvx>EI-itM`dW_*pu-XYv~(3PeNqWiLy?VTn>Io1h^t;y35B zwp-*l6$7KP-HhITEMoU?*4z#nG0$I94P6dyW&F=#i>*?4(_ z&4r*7)()=GZOE^n#FYR+o#UZ)Jkj4nWA$8_KI1@!nzxLCMFEr+o?A@$?_&FJS6m{@ z@i4AVd~rzgfkI$J*bRU}laP3Eg1cOj;?3XH!^7eAW8lmJ9#ve74*i~=2iDy)L?fF8 zUOwPzBS4Fb_tPi@&!3Zt`a0cR9RsXbGXd8&G*sH${QB8_!IV{IMNeB>h95oQ@5jrt zGnrhKs)@WY3&N1N1Pw$aq?mvN;EX|&)6)^e+)p~$Fgn3W7H(K#5^-H!opqXs$jJre z&=si%mKum!JX|LL%dF9LuMW^9ko2PFzpkpRWamGY$sUu6Ae|i@wTCNy`>NsNfhWjn zs7=}&xIl&+i6B}Rsr!egAS6Tz1w|TgIB>85$eI1B2hmB4wg>GDg=!b;k%C3V#Xubx ze}y%+vTO^DYA?KZuZk`m5vnRUikKiU&aaoNv>UV*t@=YXoP$(;X?Q{eu^DAwh98}9 z`q6W^a$+p*&y?e*T7k1)E`R>U(mz#JR(7f<^&edj-`d{hwTdNy===#i?1_|Lva&h=nJhJEy|_B|WuyS(0l<=(Qk`NI`c0>65S|43G)U-GxF1sm zODL=J^q#GW0k?xAaX(77f>p1f$z=yfwo6G#Rws79LEsAnjd_>cnW;CyI<#z6%F$qB zVKsPNmW76%etG&5q=7@OaR1F{5RTvu9S53{re@f`2#XaAc({VJ=HClrK+=C_A|0Hu zJlx+O*Nq1d(+6y45OyFWBC^cu4DL95M!Sa35{BG1X?0%jd>q-t&dJa30fs*bPsvME zSM_OTCwMAhVV{fr*%vQffV4lq_thVe_5mCqL%q1V%IV3IC$MEf(jJ%_ia2==?>X)L zwg|iaN6E$c5{sSNjc=zB=u~xVQtmi1iCLBQeQMjYkEnTf2?RCKN`0mWWuZ1U=gnYm6f5c#mp86SR~Jtq!^jkw-Z#z6)E%c3yP}sb?C+43 z`aD^@5g`|Jx3VJ%Est*>sb8fS_hM_YGS^EExkKcUBu}3zD=C@S*pQNw$EW-C%*}oK z&k&#-qSfQvI!*w`KLh1xm>C&G9oHs7ln7+Y0l{Kn!GZ#Vf`BIXo*_<>O2qrBw=EqP z=c}Znqd7j)k5=Oztbay8h!K^ty^T%D$3AcdAnK(aov~if#bae<4>+$xM8vO`t8y?0 zy9N0+aA5H6veiI?ArAPI(vjo?BO`Uy)w2~wdcY6V(<4g;qXhv00Xay#tY;hz*lr-k z1p@Wl^w|6;OiWDiOj;!>g#b%vX<2%DBEt~HWcp!15@a_U{P8c^)HVlWMZh5p0rKc` zJUk^??Dk;vgnysY`+6KO^xa*nO3jk{SZz=Lzkk_iX{Dpy;!!+)PIzGi4l;ACf6oba zG@4U+@RtZ&Y?0eM6T zw;{LGB%{Mk7??l2TurzeU%i$+43c}w@JirE>eM^xP6KJ_*4(@~^5|g7-er>Rjb}cW ze-@>MR(zXZWF7w)-#Z;iRQsAbeBBP1qP=5&GUu>^RKh$wLqW22AI4oxpJ=>e@yL@vH>$cU)=T(k@Ah92R n$$^#7Fk+KK^8%Hl>0XqjSf&eRL#qqUr$g`FC`gt`7zX|?0&c+2 literal 0 HcmV?d00001 diff --git a/protocol.md b/protocol.md index 5ee6aa4..5c7a130 100644 --- a/protocol.md +++ b/protocol.md @@ -395,8 +395,15 @@ Minimum Authentication Level: *Unknown* ### Emoji -*TODO* +The emoji system is still very unknown, but it seems that they are pulled from an array or table +It seems that at one point it was previously located somewhere else, since the first returns a 301 and the second responds fine. + + +[https://stream.mattkc.com/chat/emotes/] +![ Status | Method | Domain | File | Initiator | Type | Transfered +GET | (lock icon) stream.mattkc.com | emotes | jquery-3.6.1.min.js | html | 1.68 kB (raced) +GET | (lock icon) stream.mattkc.com | /chat/emotes/ | jquery-3.6.1.min.js | html | 1.65 kB (raced)](images/protocolemoji.png) ### Mentions *TODO* From eb3cf1cbdff25c1ddef486359acdf9ce2e120523 Mon Sep 17 00:00:00 2001 From: gl513 Date: Thu, 2 Feb 2023 20:57:50 +0000 Subject: [PATCH 3/5] removed brackets --- protocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol.md b/protocol.md index 5c7a130..e9cb5f1 100644 --- a/protocol.md +++ b/protocol.md @@ -399,7 +399,7 @@ The emoji system is still very unknown, but it seems that they are pulled from a It seems that at one point it was previously located somewhere else, since the first returns a 301 and the second responds fine. -[https://stream.mattkc.com/chat/emotes/] +https://stream.mattkc.com/chat/emotes/ ![ Status | Method | Domain | File | Initiator | Type | Transfered GET | (lock icon) stream.mattkc.com | emotes | jquery-3.6.1.min.js | html | 1.68 kB (raced) From 7124b2d97ece805b2dd6dae692e42395aac6ad7c Mon Sep 17 00:00:00 2001 From: gl513 Date: Sun, 5 Feb 2023 07:16:17 +0000 Subject: [PATCH 4/5] PR Cleanup --- README.md | 431 ++++++++++++++++++++++++++++++++++++++- images/protocolemoji.png | Bin 16077 -> 10975 bytes protocol.md | 431 --------------------------------------- 3 files changed, 426 insertions(+), 436 deletions(-) delete mode 100644 protocol.md diff --git a/README.md b/README.md index ccba837..a854f33 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,428 @@ -# What is ChatKC +# The ChatKC Protocol -ChatKC is a specialized WSS (**W**eb**S**ocket **S**ecure) chat created by the popular youtuber, [MattKC](https://www.youtube.com/mattkc).

-ChatKC is different because it uses Google oAuth to authenticate users to the server after sign in.

-After the user is authenticated, they may chat on the MattKC site, or on an alternative, unofficial specialized client.

-Work is being done to fully reverse-engineer the ChatKC usage, so that others can make a decentralized network of ChatKC-style servers.

+ChatKC is a protocol that originated from [MattKC](https://www.youtube.com/@MattKC)'s website [here](https://stream.mattkc.com/) and reverse engineered by [Breadpudding](https://github.com/cbpudding) and [janKonku](https://github.com/zegfarce) on January 16th, 2023. After playing around with the protocol for hours, MattKC found us hanging out and decided to tweak the protocol a bit to make things easier for us to work with. This document describes the protocol that was created as a result. +![Breadpudding: He's here but he's not active +MattKC: lol hey i didn't know people would still be here +Breadpudding: Oh heck +Platapai: yooo +MattKC: gonna work on some improvements +Breadpudding: Improvements? This webchat is already perfect. +Platapai: ^^^ +janKonku: it's particularly nice for alternate clients +Breadpudding: That we may or may not be writing as we speak +janKonku: very little in the way of coding needed to connect to it and have it working +Breadpudding: Maybe reduce the rate limiting when you aren't streaming?](images/protocol1.png) + +![MattKC: there is an argument for sending the time with the message, it could be nice even on here to be able to see exactly when a message was sent +MattKC: it's obviously recorded in the database but isn't sent to the client +Breadpudding: Wait, there's a database for this? +Breadpudding: Your poor server lol +janKonku: did you record the one test packet i sent +MattKC: yes the chat is backed by a database +janKonku: should be type 'test' :)](images/protocol2.png) + +## Connectivity + +In order to connect to a ChatKC server, you must establish a secure WebSocket connection to TCP port 2002 and send the "hello" event to the server. Once the server sends the initial burst of "message" events followed by "join" events, bidirectional communication can take place. + +## Event Format + +All events sent to and received from the server are JSON objects containing the `type` and `data` attributes. The `type` attribute will always be a string that describes the information found in the `data` attribute. + +In addition to those two fields, events sent to the server are required to have the `auth` and `token` attributes. In the reference implementation of the server, `auth` is always set to the string "google" and `token` is set to your OAuth2 token. + +## Event Types + +### Clientbound Event Types + +#### accepted + +This event means that the server has acknowledged the last message you sent. The `data` for this type is an object with the `message` attribute set to the content of your last message. + +Example: +```JSON +{ + "type": "accepted", + "data": { + "message": "Hello world!" + } +} +``` + +#### authlevel + +This event informs the client of their current authentication level. The `data` for this type is an object with the `value` attribute set to a number corresponding to your current authentication level. + +Example: +```JSON +{ + "type": "authlevel", + "data": { + "value": 0 + } +} +``` + +#### chat + +This event informs the client of a new message. The `data` for this type is an object with the following attributes: + +| Name | Type | Description | +| -------------- | -------- | ---------------------------------------------------------------------------------- | +| `auth` | `number` | *Unknown* | +| `author` | `string` | The name of the user who created the message | +| `author_color` | `string` | The hexadecimal color code of the user's preferred name color | +| `author_id` | `number` | The identifier of the message author | +| `author_level` | `number` | The authentication level of the message author | +| `donate_value` | `string` | The donation amount given in USD (or an empty string for no donation) | +| `id` | `number` | The identifier of the message itself | +| `message` | `string` | The contents of the message | +| `reply` | `number` | The identifier of the message being replied to (0 if none) | +| `time` | `number` | The timestamp of when the message was created in milliseconds since the Unix epoch | + +Example: +```JSON +{ + "type": "chat", + "data": { + "auth": 0, + "author": "Breadpudding", + "author_color": "B3DCF5", + "author_id": 2268, + "author_level": 0, + "donate_value": "2.00", + "id": 15225, + "message": "Hello world!", + "reply": 0, + "time": 1674090083882 + } +} +``` + +#### delete + +This event informs the client that the list of message identifiers given have been deleted from the server. The `data` for this type is an object with the `messages` attribute which is an array of message IDs. + +Example: +```JSON +{ + "type": "delete", + "data": { + "messages": [ + 15221, + 15222, + 15223, + 15224, + 15225 + ] + } +} +``` + +#### getuserconf + +This event informs the client of the user's current configuration. The `data` for this type is an object with the `color` and `name` attributes, where the `color` attribute is the user's name color in hexadecimal and the `name` attribute is the current username. + +Example: +```JSON +{ + "type": "getuserconf", + "data": { + "color": "B3DCF5", + "name": "Breadpudding" + } +} +``` + +#### join + +This event informs the client that a member has joined the chat. The `data` for this type is an object with the `name` attribute set to the member's name. + +Example: +```JSON +{ + "type": "join", + "data": { + "name": "Breadpudding" + } +} +``` + +#### part + +This event informs the client that a member has left the chat. The `data` for this type is an object with the `name` attribute set to the member's name. + +Example: +```JSON +{ + "type": "part", + "data": { + "name": "Breadpudding" + } +} +``` + +#### servermsg + +This event contains a message from the server to the client. This message is only visible to the client that received it and is typically used for error messages. The `data` for this type is an object with the `message` attribute. + +Example: +```JSON +{ + "type": "servermsg", + "data": { + "message": "Available commands: commands, forum, forums, geoguessr, help, info, insta, instagram, time, timer, twitter, website, youtube, yt" + } +} +``` + +#### status + +This event informs the client of their current status. The `data` for this type is an object with the `status` attribute set to the current status. + +Example: +```JSON +{ + "type": "status", + "data": { + "status": "authenticated" + } +} +``` + +| Status Code | +| ----------------- | +| `authenticated` | +| `banned` | +| `nameexists` | +| `nameinvalid` | +| `namelength` | +| `nametimeout` | +| `rename` | +| `setuserconf` | +| `unauthenticated` | + +### Serverbound Event Types + +#### getuserconf + +This event is used to ask the server for the client's current configuration. There is no `data` field for this event. + +Example: +```JSON +{ + "type": "getuserconf", + "auth": "google", + "token": "..." +} +``` + +#### hello + +This event is used to introduce the client to the server. The `data` for this type is an object with the `last_message` attribute set to `-1`. Currently, we do not know why this data is sent. + +Example: +```JSON +{ + "type": "hello", + "data": { + "last_message": -1 + }, + "auth": "google", + "token": "..." +} +``` + +#### message + +This event is used to send a message to the server. The `data` for this type is an object with the following attributes: + +| Name | Type | Description | +| ------- | -------- | ------------------------------------------------------------------ | +| `reply` | `number` | The ID of the message that this message is a reply of (0 for none) | +| `text` | `string` | The content of the message being sent | + +Example: +```JSON +{ + "type": "message", + "data": { + "text": "Hello world!", + "reply": 0 + }, + "auth": "google", + "token": "..." +} +``` + +#### paypal + +This event is used to donate money to the owner of the server. + +Example: +```JSON +{ + "type": "paypal", + "data": { + // A lot of this isn't safe to put in documentation + }, + "auth": "google", + "token": "..." +} +``` + +#### setuserconf + +This event is used to store the client's current configuration on the server. The `data` for this type is an object containing the `color` and `name` attributes. + +Example: +```JSON +{ + "type": "setuserconf", + "data": { + "name": "Breadpudding", + "color": "B3DCF5" + }, + "auth": "google", + "token": "..." +} +``` + +#### status + +This event is used to request a status update from the server. The `data` for this type is an empty object. + +Example: +```JSON +{ + "type": "status", + "data": {}, + "auth": "google", + "token": "..." +} +``` + +## Commands + +Commands are actions performed by users using messages prefixed with an exclamation point. + +### ban + +Usage: `!ban ` + +Summary: Bans a user from the chat + +Minimum Authentication Level: *Unknown* + +### help + +Aliases: commands + +Usage: `!help` + +Summary: Lists all of the commands that the user has access to + +Minimum Authentication Level: `0` + +### info + +Usage: `!info` + +Summary: Displays information about the server + +Minimum Authentication Level: `0` + +### ipban + +Usage: `!ipban
` + +Summary: Blocks a specific IP address from sending messages + +Minimum Authentication Level: *Unknown* + +### mod + +Usage: `!mod ` + +Summary: Promotes a user to a moderator + +Minimum Authentication Level: *Unknown* + +### rm + +Usage: `!rm ` + +Summary: Deletes a message from the chat + +Minimum Authentication Level: *Unknown* + +### time + +Usage: `!time` + +Summary: Displays the current time on the server + +Minimum Authentication Level: `0` + +### timer + +Usage: `!timer ` + +Summary: Creates, checks, or deletes a timer on the server + +Minimum Authentication Level: `0` + +### unban + +Usage: `!unban ` + +Summary: Reverts an existing ban on a user + +Minimum Authentication Level: *Unknown* + +### unmod + +Usage: `!unmod ` + +Summary: Demotes a user to an unprivileged member + +Minimum Authentication Level: *Unknown* + +## Message Formatting + +*TODO* + +### Emoji + +The emoji system is still very unknown, but it seems that they are pulled from an array or table. + +https://stream.mattkc.com/chat/emotes/ + +![ Status | Method | Domain | File | Initiator | Type | Transfered | Size | 0ms +200 | GET | (lock icon) stream.mattkc.com | /chat/emotes/ | jquery-3.6.1.min.js:2 (xhr) | html | 1.65 kB | 7.43kB | 231ms ](images/protocolemoji.png) +### Mentions + +*TODO* + +### URLs + +*TODO* + +## Authentication Levels + +The following is a list of the currently known authentication levels: + +| Level | Meaning | +| ----- | ------------------------------------ | +| `0` | An unprivileged member of the server | +| `50` | Server moderator | +| `100` | The owner of the server | + +### Permissions + +| Level | `!ban` | `!ipban` | `!mod` | `!rm` | `!unban` | `!unmod` | +| ----- | --------- | --------- | --------- | ----- | --------- | --------- | +| `0` | No | No | No | No | No | No | +| `50` | *Unknown* | *Unknown* | *Unknown* | Yes | *Unknown* | *Unknown* | +| `100` | Yes | Yes | Yes | Yes | Yes | Yes | diff --git a/images/protocolemoji.png b/images/protocolemoji.png index 13e2ca2c9311fa26aa10619a5f94ad2d5aa7f7c8..1a8fe7b1b2b87e20bb6280faacbc4f571920fedb 100644 GIT binary patch literal 10975 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 diff --git a/protocol.md b/protocol.md deleted file mode 100644 index e9cb5f1..0000000 --- a/protocol.md +++ /dev/null @@ -1,431 +0,0 @@ -# The ChatKC Protocol - -ChatKC is a protocol that originated from [MattKC](https://www.youtube.com/@MattKC)'s website [here](https://stream.mattkc.com/) and reverse engineered by [Breadpudding](https://github.com/cbpudding) and [janKonku](https://github.com/zegfarce) on January 16th, 2023. After playing around with the protocol for hours, MattKC found us hanging out and decided to tweak the protocol a bit to make things easier for us to work with. This document describes the protocol that was created as a result. - -![Breadpudding: He's here but he's not active -MattKC: lol hey i didn't know people would still be here -Breadpudding: Oh heck -Platapai: yooo -MattKC: gonna work on some improvements -Breadpudding: Improvements? This webchat is already perfect. -Platapai: ^^^ -janKonku: it's particularly nice for alternate clients -Breadpudding: That we may or may not be writing as we speak -janKonku: very little in the way of coding needed to connect to it and have it working -Breadpudding: Maybe reduce the rate limiting when you aren't streaming?](images/protocol1.png) - -![MattKC: there is an argument for sending the time with the message, it could be nice even on here to be able to see exactly when a message was sent -MattKC: it's obviously recorded in the database but isn't sent to the client -Breadpudding: Wait, there's a database for this? -Breadpudding: Your poor server lol -janKonku: did you record the one test packet i sent -MattKC: yes the chat is backed by a database -janKonku: should be type 'test' :)](images/protocol2.png) - -## Connectivity - -In order to connect to a ChatKC server, you must establish a secure WebSocket connection to TCP port 2002 and send the "hello" event to the server. Once the server sends the initial burst of "message" events followed by "join" events, bidirectional communication can take place. - -## Event Format - -All events sent to and received from the server are JSON objects containing the `type` and `data` attributes. The `type` attribute will always be a string that describes the information found in the `data` attribute. - -In addition to those two fields, events sent to the server are required to have the `auth` and `token` attributes. In the reference implementation of the server, `auth` is always set to the string "google" and `token` is set to your OAuth2 token. - -## Event Types - -### Clientbound Event Types - -#### accepted - -This event means that the server has acknowledged the last message you sent. The `data` for this type is an object with the `message` attribute set to the content of your last message. - -Example: -```JSON -{ - "type": "accepted", - "data": { - "message": "Hello world!" - } -} -``` - -#### authlevel - -This event informs the client of their current authentication level. The `data` for this type is an object with the `value` attribute set to a number corresponding to your current authentication level. - -Example: -```JSON -{ - "type": "authlevel", - "data": { - "value": 0 - } -} -``` - -#### chat - -This event informs the client of a new message. The `data` for this type is an object with the following attributes: - -| Name | Type | Description | -| -------------- | -------- | ---------------------------------------------------------------------------------- | -| `auth` | `number` | *Unknown* | -| `author` | `string` | The name of the user who created the message | -| `author_color` | `string` | The hexadecimal color code of the user's preferred name color | -| `author_id` | `number` | The identifier of the message author | -| `author_level` | `number` | The authentication level of the message author | -| `donate_value` | `string` | The donation amount given in USD (or an empty string for no donation) | -| `id` | `number` | The identifier of the message itself | -| `message` | `string` | The contents of the message | -| `reply` | `number` | The identifier of the message being replied to (0 if none) | -| `time` | `number` | The timestamp of when the message was created in milliseconds since the Unix epoch | - -Example: -```JSON -{ - "type": "chat", - "data": { - "auth": 0, - "author": "Breadpudding", - "author_color": "B3DCF5", - "author_id": 2268, - "author_level": 0, - "donate_value": "2.00", - "id": 15225, - "message": "Hello world!", - "reply": 0, - "time": 1674090083882 - } -} -``` - -#### delete - -This event informs the client that the list of message identifiers given have been deleted from the server. The `data` for this type is an object with the `messages` attribute which is an array of message IDs. - -Example: -```JSON -{ - "type": "delete", - "data": { - "messages": [ - 15221, - 15222, - 15223, - 15224, - 15225 - ] - } -} -``` - -#### getuserconf - -This event informs the client of the user's current configuration. The `data` for this type is an object with the `color` and `name` attributes, where the `color` attribute is the user's name color in hexadecimal and the `name` attribute is the current username. - -Example: -```JSON -{ - "type": "getuserconf", - "data": { - "color": "B3DCF5", - "name": "Breadpudding" - } -} -``` - -#### join - -This event informs the client that a member has joined the chat. The `data` for this type is an object with the `name` attribute set to the member's name. - -Example: -```JSON -{ - "type": "join", - "data": { - "name": "Breadpudding" - } -} -``` - -#### part - -This event informs the client that a member has left the chat. The `data` for this type is an object with the `name` attribute set to the member's name. - -Example: -```JSON -{ - "type": "part", - "data": { - "name": "Breadpudding" - } -} -``` - -#### servermsg - -This event contains a message from the server to the client. This message is only visible to the client that received it and is typically used for error messages. The `data` for this type is an object with the `message` attribute. - -Example: -```JSON -{ - "type": "servermsg", - "data": { - "message": "Available commands: commands, forum, forums, geoguessr, help, info, insta, instagram, time, timer, twitter, website, youtube, yt" - } -} -``` - -#### status - -This event informs the client of their current status. The `data` for this type is an object with the `status` attribute set to the current status. - -Example: -```JSON -{ - "type": "status", - "data": { - "status": "authenticated" - } -} -``` - -| Status Code | -| ----------------- | -| `authenticated` | -| `banned` | -| `nameexists` | -| `nameinvalid` | -| `namelength` | -| `nametimeout` | -| `rename` | -| `setuserconf` | -| `unauthenticated` | - -### Serverbound Event Types - -#### getuserconf - -This event is used to ask the server for the client's current configuration. There is no `data` field for this event. - -Example: -```JSON -{ - "type": "getuserconf", - "auth": "google", - "token": "..." -} -``` - -#### hello - -This event is used to introduce the client to the server. The `data` for this type is an object with the `last_message` attribute set to `-1`. Currently, we do not know why this data is sent. - -Example: -```JSON -{ - "type": "hello", - "data": { - "last_message": -1 - }, - "auth": "google", - "token": "..." -} -``` - -#### message - -This event is used to send a message to the server. The `data` for this type is an object with the following attributes: - -| Name | Type | Description | -| ------- | -------- | ------------------------------------------------------------------ | -| `reply` | `number` | The ID of the message that this message is a reply of (0 for none) | -| `text` | `string` | The content of the message being sent | - -Example: -```JSON -{ - "type": "message", - "data": { - "text": "Hello world!", - "reply": 0 - }, - "auth": "google", - "token": "..." -} -``` - -#### paypal - -This event is used to donate money to the owner of the server. - -Example: -```JSON -{ - "type": "paypal", - "data": { - // A lot of this isn't safe to put in documentation - }, - "auth": "google", - "token": "..." -} -``` - -#### setuserconf - -This event is used to store the client's current configuration on the server. The `data` for this type is an object containing the `color` and `name` attributes. - -Example: -```JSON -{ - "type": "setuserconf", - "data": { - "name": "Breadpudding", - "color": "B3DCF5" - }, - "auth": "google", - "token": "..." -} -``` - -#### status - -This event is used to request a status update from the server. The `data` for this type is an empty object. - -Example: -```JSON -{ - "type": "status", - "data": {}, - "auth": "google", - "token": "..." -} -``` - -## Commands - -Commands are actions performed by users using messages prefixed with an exclamation point. - -### ban - -Usage: `!ban ` - -Summary: Bans a user from the chat - -Minimum Authentication Level: *Unknown* - -### help - -Aliases: commands - -Usage: `!help` - -Summary: Lists all of the commands that the user has access to - -Minimum Authentication Level: `0` - -### info - -Usage: `!info` - -Summary: Displays information about the server - -Minimum Authentication Level: `0` - -### ipban - -Usage: `!ipban
` - -Summary: Blocks a specific IP address from sending messages - -Minimum Authentication Level: *Unknown* - -### mod - -Usage: `!mod ` - -Summary: Promotes a user to a moderator - -Minimum Authentication Level: *Unknown* - -### rm - -Usage: `!rm ` - -Summary: Deletes a message from the chat - -Minimum Authentication Level: *Unknown* - -### time - -Usage: `!time` - -Summary: Displays the current time on the server - -Minimum Authentication Level: `0` - -### timer - -Usage: `!timer ` - -Summary: Creates, checks, or deletes a timer on the server - -Minimum Authentication Level: `0` - -### unban - -Usage: `!unban ` - -Summary: Reverts an existing ban on a user - -Minimum Authentication Level: *Unknown* - -### unmod - -Usage: `!unmod ` - -Summary: Demotes a user to an unprivileged member - -Minimum Authentication Level: *Unknown* - -## Message Formatting - -*TODO* - -### Emoji - -The emoji system is still very unknown, but it seems that they are pulled from an array or table -It seems that at one point it was previously located somewhere else, since the first returns a 301 and the second responds fine. - - -https://stream.mattkc.com/chat/emotes/ - -![ Status | Method | Domain | File | Initiator | Type | Transfered -GET | (lock icon) stream.mattkc.com | emotes | jquery-3.6.1.min.js | html | 1.68 kB (raced) -GET | (lock icon) stream.mattkc.com | /chat/emotes/ | jquery-3.6.1.min.js | html | 1.65 kB (raced)](images/protocolemoji.png) -### Mentions - -*TODO* - -### URLs - -*TODO* - -## Authentication Levels - -The following is a list of the currently known authentication levels: - -| Level | Meaning | -| ----- | ------------------------------------ | -| `0` | An unprivileged member of the server | -| `50` | Server moderator | -| `100` | The owner of the server | - -### Permissions - -| Level | `!ban` | `!ipban` | `!mod` | `!rm` | `!unban` | `!unmod` | -| ----- | --------- | --------- | --------- | ----- | --------- | --------- | -| `0` | No | No | No | No | No | No | -| `50` | *Unknown* | *Unknown* | *Unknown* | Yes | *Unknown* | *Unknown* | -| `100` | Yes | Yes | Yes | Yes | Yes | Yes | From 760d8c79c32995ee97df88301a9e2716b075ca5d Mon Sep 17 00:00:00 2001 From: GL513 Date: Mon, 6 Feb 2023 17:49:06 -0500 Subject: [PATCH 5/5] rm protocolemoji.png --- README.md | 2 -- images/protocolemoji.png | Bin 10975 -> 0 bytes 2 files changed, 2 deletions(-) delete mode 100644 images/protocolemoji.png diff --git a/README.md b/README.md index a854f33..34ce9c7 100644 --- a/README.md +++ b/README.md @@ -399,8 +399,6 @@ The emoji system is still very unknown, but it seems that they are pulled from a https://stream.mattkc.com/chat/emotes/ -![ Status | Method | Domain | File | Initiator | Type | Transfered | Size | 0ms -200 | GET | (lock icon) stream.mattkc.com | /chat/emotes/ | jquery-3.6.1.min.js:2 (xhr) | html | 1.65 kB | 7.43kB | 231ms ](images/protocolemoji.png) ### Mentions *TODO* diff --git a/images/protocolemoji.png b/images/protocolemoji.png deleted file mode 100644 index 1a8fe7b1b2b87e20bb6280faacbc4f571920fedb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10975 zcmaKSby$;M`1eByNQr`UsYpsom!Q%h(lNRjoze;dA}!r5(m7yscSwxx?#@x~+rJsI*ijW|Ep&DWr*0X z`pq=2gOj=F&9qH8+AjCC)Yfz;vjjGFTGKwGo_M+_@1c5&^rU+!cDpC6@-UeCCiP;f zrEXEkjj`uzx9$DSMYUydF8uBG)_COWq<*+?DpF8fLvoV%*2bH|j~2_1wx=GICxHN6 z9K6kZBbNss0dM>7@aX@0DAF|pgYdtHq=l6HAOCw6K@QcQ*6;tGg*A#lZXogBwU(IY zxo)Jp{#riz_s-IO#mK)6MM+BvznhI)H3zP7ZqoSdBe`0*p$HzhN(_2+Yz8ryj!J&KOj zd6aCfDl;n!%j#&Z9IE&{bn|!D;$m|{%EQsJ|4<*nwqUl@jbfT`etG#*fbLmu9IMVm zslM+@M{rkHm(6Uo&0OuV4dzthz~Er~8?B<^K|$YpFE6hKGgO8=A2dvSewQ6~1b0=~ zNl_B6JB#bV++zU3Rs)A?y^LDh+{AqJsBu3708r8L>fv}9%K6s3Iu6K6jploDhWN!NBXREaI4D}LCr)wE*j`P|g57a7o^TC6gEe95bm#op%6Rzby ze`0HBOn(24eo`D1B&DUL1r45`!?5SytI-)w6L6wuVhWbXyh0*vr^=3R4x33rI#Yi+ zX=)~@GVjL}McNWjQBhG+7I}H@O_k5gXk+*v6TjbiIh-a^@5wjb*H_m$@eD9AH;;`W zYVS9{uixx*w>?IE*K-n2 zE>HR>uP;XdY`M=0jabHEN!(r)@S(rO+?zR_Y$7TfqO-oMfS}@7+s~DW{r<{?JEMJu z<-qRd4Bwg;%)0nr4!@b-XEz$_nq=N(=;9!gl(3i=oYyb=#f#q-)xJR{ku5Z*MHJh4 z1z>!TknpRi;hQ$7fWcTXY-6%!M~e&putEbOvF z2P7mV73l!ce?yyxLu{R$&jyqEgjlR|c8ehJ zovo3Kx;he(aB6C_yW3k1+|r^VVTY9u<~&7H~T^ORAQPOny_}*G?_KS*&D$R$PiLhZ- ziNl+2F4nfTwyv(O9v&VO6B9bs)(-Y%8ifKpEb_SRsAy=##l;Sk>~G#+;ozhTyZ?%c zI!2!FMoYT~JW)_3Kxk zJLEF|`tfSV?qsQ(u4*xv(8S^1bfub_np`}ai2c&LOU%CxU@PY2JJd@l2*75S4kJ4% zKj*HGb{&mH7)c7wT^%58n1FY;iyFUEFuXf>aeG3jgW^caDQevDa(%RkIOG!y-aNhnE zLc*S#mq$Tu)8B>_D;s_Evkx&3h?(90&sA1i#cuV zU7v1tN6?7*-nX&pG!X`WfKHa|PYyG^e$B(mDk2~dK>OqCSAdgqX=hb=dOtKqxL6a(saMT!TmW~@(xPgWM;NO!mfvn-vll_ zpVLNYRTab9$;!$z7pIz+FE-bw7q2#qesymT%g_1Rz4=>c! z3DQSfT3Q}oIkai$dw(SOZnp9jcxY?OD%2=-2>T;kX)7mTD50dZ zR6~cQ#bK_eho9}hP*wG8fQ*|1Hz_GeF;x&T=-`K>q<7!X6=BHQ+c%d#YvhL+-Av5# zspU;8O{J|ih`ElcOFw?~3aNrIp|?>{>&q3-ihtY5ynQx5P}@hWyhM6&J@Dcpt`TTi zsF>X34%)kCkrgMP!b$rP5pjBU7W(5y84Py6TdD%6pnyF?PC+r4$c?FYkE)uRlhd>A z2rx1+Z8*YbW@d!b6g@ly0aewb{=UAVA|yxRjDUau*o8o3WF&_{%h&Lah!-XXtUk8l z;)YI6+|%4z+S*C+moe67F>!H@a0p;;Z+~=r`~s`3rRD3Kx|-37F+dRfK}`(-VI$#5 z@Sp`}B_AIjVBv5dR9ws&)UMU&!2@uRo`D5jLtr#X5YlN9%*y5?NxV@F(;dILaG{Gx z(|n^SFYoN=Nass&f4xaFuDzqk$;sIs^dutU)6vn9(vYy{37tZw67UX0^RO@laOTt_ z95omj83zWveK25}DdWroKY#wLsHlKJCj*0ma(|0{9xQq;TJGxnW?exT~ zz=kqZHrEgUc!Do~=k9*--dBS719_)6(YU zVz}xVt>S&G-D1>Sn`WTl1&z?tnQ@{{p9h-jDXzuV2b4DtvYe)<#BIknq*3wg8;r6oL18a>k^@PoF*| zC!fh}tx|Ajtg8A`5KU58zzk%RmzP&pU-&Yu8!kT-X&%cMt*rL+^c*}n&+W$k>*C(H zO$q>Nvk)4RaT%iT@^t1~6#!s-O1d&ypiE|9UHO8SA7}}Iy?e_u>Ev$a477L%Bx$Jt zfTp$J!kZs(Cp&2ETqV24X~hb-5fM$o*~rObkX@;&s{ZU8m5HYB>gZr3nLr>Oqx-#h z9~f!pE=&zb(>>!&p4S!y@gG$@H9DFQASJ)xefyS?jg5_w5o}`9a!p`qXQp}eGb~Ip z`}*c)ck5_nbyeqfuA0xhavBe>4XiBO_LLM#Ar|JG&7R&~SY_p_U4HBUiMSSDkwNaN z&a9H8H>|8SM@y~1ySy<>T>Ig^zP*ivr@tgJe}sfk@YzCKT=3jI$${G1vpgk6(m*`Q zt@|@ey%2<>+m6AYO!i1sRn^asbSg2QxrGIBnAU@A{a>S%yiccd^Oyh--CG5Nd7q|Q zg?;;m621zwm{o2&d#I>HJ0fZwR!>e(!6xjfAtNIL=W&8OJzxQax@`{>OsJ}>lTuKO zS-b^8_hRW_{NpjHsdu}jH^L$!JoOZHt#UNI6_>c$Ms`1Bl1)e3f-ZR%?daBClN{pC zz@6@^x<};LyBGC^Ryz13GXK!C77t+osuOJYuQq#hgA^7q6`7v%JWg_?1Kv3#-Hy20 zC27ikJ(|8>ED#bD+E(l6>cS;k$>#Mv0?C)t#=!May94@*h|o}MJUj(?c?isEy=KuC zc{*&&xs8>YU}iDH`?Reb+=l>wA2frbeXHZivY zi0ehYhe`q*e!Vr4t@S1jr2E?1+7wJY!+(iG>Z~e2$o9THP5~Jq;rEwZCq9?MjqhSW zoxy9j;DtQ1T>k~46c2QwY#L+=b0CkCkOMbRa;h~cy3?psuThpQ-k8+&`ot7w^$_bS z&V3AnYLL9qJR0O%93LMyF*9@CIA|Q`?}u(F(d4Vr6U(vbHE8PSKrJmF1GpH*ZQmb% z{P=N=bPn#c`Rv)V(6F%P>kW!Td3xfI%frRRJ3h!F+{yf44i0|ieZ>hAuEY=$)@RrO zUZM}hxb?=~^bb>a=}6iC1g*zeaiA6QtlYf@RkCi#Q;l??FxC5$^N~=l5+g`Hkr%Ue zz2DM6dI6RW8`WF!0EO*TAtI9&W4;G(&)KSpQAm=rvsKTt!F5QQqIYTrOEwh8W0Vh6 zsV9D0Ym1w#p{OrG{rZ3}Ol^lKh~-L5Y0#$Uh}!uiFE#{b zkV%;sDk1!o;qj9O-rgd>pOO;yqb0-l@7rhbvHwZ^1^h{-bd+}|>)v)kk@(<+XCAK{ ztOQ?2`_kYCahv`WOI6fp;^F13^SWlX;{LZr(!-MytAR=cOs(+57Xj!f82Am#`gJN~ zL=xg3_GQ?@OEWA!WB85^JN|eI#Jtw%-jj=Z+lNc#(ql4DR_E{Ep1_$6@+L!4dV=vZ zVfVVgk6b&DUq{+xDAP;^BF2u8Zs=IrPH{3*{Ug3ud4sB%AklH|e6@8^wVW zV}q$T4q(@4YX|N~&_`b*H1vHg=i^-YG(=7S0}Bfd^Pz6fi`!AA@_f^Ur)vk`_=c8n zCHfYu>`ZcrJLGjt@Rn?Lr`$(#77zn^RLJNp%bXqUV!a6@jnDg$iC8|qnADWgI_!4) z)Tf04wqrjpnr8gH&LzFs^1Ybl8z&^mQ-*l8zEofR&6#*+z;C;{Z9d|gAdyLcP@~s- zct10DWt3Mt_gap4fAOJSm04|;gSMqPE9)gaeKjn_d;n{puTN1{cIOBmHr(0i-U8G_ zGdw8kEVLSp9ZTGMATQk#d7vAhZ0Or86ifQIjKjku?PYUw4Uv%qA|mspO~{IWZb?BQ z)aPd34ME#{Wz;@VyjW=4?@dX4SSZ zG<*>yZBNO<#)gl7U!R|!PvKg?V3E&<)xKq2%p?^!R%Q8AOmwL3^ORkIi==cWqVE}HZ`5|rTWcGDyWL-qN(Zn063-->u~3Ls6FQ@31Q5w(swLByywmF2h~n zwfvRSdubB}TpU~L`}+#&Q3a(@uAHQ}a{Z!tpVGY>2OTKW`5m%$MlOzyenfoQikW)L zZ5Qw81{SVmNpbB-Kv7eJiV;-9>9f&$BaRpGqY4n*y!*4gn({t1(%1#hjm!cHoe%Du zNfhLYH)`6cL58JXlVwY-4iYnSuH*rlJz|$K6pyh2va~dPAEt=m@-uehxXI$i+^^V} zCc`N%LFo`<2F-tEyU=hxZC^u*hVJIOikBskGpB8h95T5FMob`iQ!*v*j~r*DUFDj*hO(Jb$_ayuy1mA!uuW^ zmN0a1^)4=|xe{@K9A!%hiIw$!4ZvWn=V`W71TMH~`?u_R4GBp|^zLH;C((g{r0DbpD0VyubO79g(Sp8UfjQi>;Ti@Ew1%Zl@PDc(<($*fep}F$_Ne1vv0FY{0vgc1?;>5+lI;yFvYH{cT z>0?%QHs}fjuhup;-hoOMG$OS1hAxkfiLoi3(1>ywwtd0G#H8#7H7{txpaC>K{MB~) znAicz>gui+d+vU;yw+F3qN3%#5BKu)s}sdKLqkJ>fq_KWK6_J4h@{uFE&6YWb#!#V zV(94jfZI+dd8Q&(43_EuKf3^>lt&skVlo)Ja|;+=wWLq=M(b^@2dqA?#%iwzHUGXGlXTO}XBtqBL~^~sP2VJW-9B6+q_pZ{c!Rfuy?aisbtqC1^i#|Qr68oU4aJ#(IhciST_iDyGgNpG;( z=2rc=U2xG{KPL!>b`|+LSKJ?WF5}dgNJ3V`6qpn}sWpvl9!(x~LX;`}YL3aVXGQ+C~Q3(FC3 z0z0mB=u_}|DY7v!Gou3JVP@9Ws>;eUM(RF3#<;j1sRB-lreu)&)Am2u-KV}%U zNYXIjFmHm(0ptio;Ks(rM0rVD+k;@jJfH<+e|5`0RFkw3{?e{Z*On3Lq42%E~QC(KXZ@-ig7dNpnm@M(W{o?Rpu&-b^ zh6x1}r|9A-!q@R1UcQ)={P=OP!M%TY`0#ILJ3vc-6;rAUNkM5D9W^>x>y-*e=iv#S zn}gU`Tl?G|GvuqvW;T%4tRFl7R!psMJ7kB!P#v%5;50$sZ}rvHtG|6~-9Z$(?p?71 zXZyRLV^;qmYh%4X9<(Z``R#v5X4@<@Y%et43=Zx?<}T%_sj1hZG=#W7_AE@z&dt5M zgCKxHcbQafxw*M{BD|z~&JEiG@9%F=xcHz@sA#hUBhks122Nuy-#eG|4=?esj%awT zEi^U1%r4$fmHVK6TRPk3244+2QRH(`^nu4|5SK>Gr%0P0;hKl1npVEB<RPhw)?*lz_P-+SR{ zeOnU~XOP?aDZSBe8W<_J14pK~0cbyf(t`fx2`Iq8EI_EH)x^>g4r;1EjLh`KgeRh; zuIqg~h?$noPI#D8HDUW-RSgYHfHcsuq@t|sPf?LJ%<56#GH5L@G3Bc=BSuCXK!YDU zK$we*nT<`)hLAV}X42aR_70cSJyQ*_6kfn7`L9#q`xrkMt)|Dsdszd-u~d;J~d;-(Rm+NV3%jSSMUTNeFQDA z@*&q-dAKQyIfXkiiD$0xKM#4}u3RKy5tNd9u{*=2uSOd96kS{$Q(fI#^#012Jj@j| ziBFQ7oVP1>A|anLN=p}y)nG8clarsHM74;?$P&}i^5HT@M#}$`2iWminwsEpJ_G)! zl1JpBDQ&IuOm%fhvc>OiZ(Besdlb01xluBTiWi?{#BqoA}t>&AO z{QSY59^mVVqi9e&27uu&;93Nl%V`oX^2WCNX!?mA;Q#0|xI*7U!Q>4zwxWv8)*azo ziLq+wAgP;}!0uU}nUQCUmzvm%jg9pZT^t=n1wgkjDd~d=M{KMbx;SWV&uF8Bbo!7` z$b=S3aGRD;JLnq?VkYZJ)CI zy7EC;#kf^i=FJoHf?4u?_R%^FtqHmnZ& z&`9;9)3Btpw!_Y^S_>Zs%ze2qj9X0rLDr_bh^{XT0AxOfXXRJJ=>l-y_m6yH)iOrf z07^e7O#GfP>*!~V7+Cd%ZQ!Mub69%n&XLKu3j#4F!paIuD>n|NhGq)AAx)h4L9d8J z4|^k}={ARSHQ|mQot;A~oJ3ez6*42y-f3wu#osh^3oBH>I9{Hpo>*N5N>pb*ozh?2{%cmio+T})CV0Po`s#tT_&BDR8Q(Ramm3A*% zQo_cSD8J93{D$~&;f_02mP%;CZet)3q&=hPJW^6YqTa~8sbhUp_7g;*hKfqM5@SqD z%Qn~yF1Fo<5)!C@f%hipQ-jcf70?FeVI8XuAXG3jUmYJuv`e&uk#te@^FTwR^0kg& z6`8U0_W*z$@b4@@e;n&$92_y9+qDW)InWjn5fM2(IVpv}B@{(H-xu>HP*cm=6%a?< z5BYob^BPF*DV}KVN$0ANvwXm~5PX9sxxLCPn7nb9?_`|&BVW$<)Iw02IaXamLq}Vi zh=^!olf-(l$@pYVMs=ro?LgR%RxyR&(8y?KSIF^ihIm2t2wmopJ0Dk|AxKUS7JVH? zHa0fE6udbjBbzJHy7$+|zV*IzO;a9Ovy8Y(RisgPysRuPK(9y%+CI*=SC4_KLtnS5 zMjRy4$B1LM#lNrk zhMC3oe@N+4qA#v1SA)crKtA$qOI24%x2?%p$ICqPQ8#wwe-Xa}!ox1_b3itHFb*gE z2`9)PTWjr@AjVqzr&{zsH|jQ4=p^VdfY0~uS<&jl`4VC(W3aA6vZO6iM8B(^8Rd3q z1Ccl^vf*AGRa9mi4#^zrR`b~^1<;$YBt?Rgfgk9F(+de79m*plLy0p{-owI>B*LIup%)F3k%cI5&+Xsk%rc{MjbtW{xHIZ62$#6 zk`qV;1O-t*?{uzeJ6Xi@BC=Q~QT}47HT#p8TDqN7wk0^2@W|KlC$8z@`=fvT0`0u% z?Woz=Sp;1A6ZJ#LZTW2T1D}e@>c(m*l&#wt?XX;O`r1eR+9$G z&?ys#2@2!O#RGN31 zc$_R_Jq!Lo7P|uldWP9+U0J{Pnjo)mQNA`b2!p*?A@U)6#6E@V%aH*XG~nP^TU*yc z*x(JB;+h&7hf4-e01OO_Mvv_p-lTiQ(D~I;?MzkgQoZ>rLF3MINlskT*pnoFRt3q z3ODW@D(1_KdmPaF=XFK9O`g zI0nODvD%lvlo-MA|C78tE^bOmTwYvUTxx3ZhVB;;?D+ou*~xDyLnuI>HXq~G)m@BccFvP4l`PVNGv{NXe02ODswxBycSBewCUAK=a+ctagYpof(bz&?y>vnl=fC^{s2La zZlAe5Y-q6VJzFA`@PuusmX_l=T+7XY_2(qBIV&$75LoOMhJ3v~66NgQRQIaM_20xi zSZ)sjI{Y5tY&qAdGROAkPL?jxX(*(N4lXXz0N~CJr9U|s`Xd+so@v+~4RC~`wwHkx z7Xft=K|!7h3akQI`r6v+Dk^v9llq{$@S2W}SwC9C7*rSn zPOe}$aD5#R92^103(dDDH~|vGgr^)#OqRD-4xkp~;elzC27;;7%@Zsr!d|} zb4tX~P&ajHZ+Z!|N>B{216D?J`};n_C*~U5p~$ms3ceBstdC$biHm1iZN#(df9@|U zDA0!4CnYE6gX|>7@7K)DjRJ8BeFt8a-=+%B$7@c=rDc~>1XB`)37%I<7y8$vGLc4l zYaV^?ph(^jmI@ZO5|w`05eWOTxh0uE@XvA2>a)A19LF%M#P5;3$Nn0Gj*sWZ1NbcT zR9tKSEM=;z|Cz4nCk^B=TZH!A2et8zQ^2b^@8Trp+V*r)Gy9=!-?JLFT-}QCOz2le zH!s6S2ItNtOBr$${y739z?6H$aINp>(K!oli=x{6g{cK9V2#UiYPqyW0Uh<5JpJ~3 zJzx6p6WkPsO+^a@1snULfLIm{(EF;U0Ni)G8a;LIX`drzGNkZgSaxg5%8nvLkru06 z=YakBj!UM}*7i21)i|RcEfMx~u@2w0SJTFZ+hJe{2?k3%-5AVN&PV^IrlFx*YyX*>FH?z>(#vq6 zLBR9!9`zxaQ9g62von{34f0q1f&RDru{j6nIa@^-8wKe2nHn40n3}$h@23&O9z+5G<4AJPxLWF%yr-o~H|0-NBW${aIc+I| z_D$1?*>c)sL(BQuSy{!-*g@(7>&|3pdQebzFyT%+l4{Zz$36nD=%&#r$Pf3+6-nN~ z1Ah&S!8@I-()VH>ku>XAI~VA2KK*TeBlm?s{vX1?Iw3GiAVX|^b@6)ccZ;J$Y7M{2 zb#E%h^>ZOoG`7c7EcKmhbFm-(&OL?QyF2V09umUN$_l;V4Vfs@90(yEh@)BjtEa85tG&%isAB8sQ-WQR&cJ(p{J&sWXi@DRK zy`1N|?E5n%bby)0_e~HiMkq291E_#+m8M4-t2Yp4JBQ zhhX0Ih8U;&n77T=hBZ3BSm`<_XCC*Q