From b861029b4d1f3d21fad036b002ce2b9526e90248 Mon Sep 17 00:00:00 2001 From: atridadl Date: Tue, 26 Dec 2023 18:39:21 -0700 Subject: [PATCH] So far so good! --- .DS_Store | Bin 0 -> 6148 bytes .dockerignore | 31 ---- src/.env.example => .env.example | 0 .github/workflows/fly.yml | 15 -- .gitignore | 32 +--- .npmrc | 4 - .prettierignore | 4 - .sapphirerc.json | 16 -- Dockerfile | 37 ---- bun.lockb | Bin 63236 -> 0 bytes go.mod | 13 ++ go.sum | 40 ++++ main.go | 174 ++++++++++++++++++ package.json | 44 ----- src/commands/ask.ts | 70 ------- src/commands/borf.ts | 35 ---- src/commands/hdpic.ts | 79 -------- src/commands/hdtts.ts | 87 --------- src/commands/hs.ts | 35 ---- src/commands/pic.ts | 92 --------- src/commands/title.ts | 36 ---- src/commands/tts.ts | 88 --------- src/index.ts | 55 ------ src/lib/constants.ts | 4 - src/lib/setup.ts | 18 -- src/lib/utils.ts | 37 ---- .../chatInputCommandDenied.ts | 23 --- .../chatInputCommandSuccess.ts | 14 -- .../contextMenuCommandDenied.ts | 23 --- .../contextMenuCommandSuccess.ts | 14 -- src/listeners/mentionPrefixOnly.ts | 10 - src/listeners/ready.ts | 57 ------ tsconfig.json | 10 - 33 files changed, 228 insertions(+), 969 deletions(-) create mode 100644 .DS_Store delete mode 100644 .dockerignore rename src/.env.example => .env.example (100%) delete mode 100644 .github/workflows/fly.yml delete mode 100644 .npmrc delete mode 100644 .prettierignore delete mode 100644 .sapphirerc.json delete mode 100644 Dockerfile delete mode 100755 bun.lockb create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go delete mode 100644 package.json delete mode 100644 src/commands/ask.ts delete mode 100644 src/commands/borf.ts delete mode 100644 src/commands/hdpic.ts delete mode 100644 src/commands/hdtts.ts delete mode 100644 src/commands/hs.ts delete mode 100644 src/commands/pic.ts delete mode 100644 src/commands/title.ts delete mode 100644 src/commands/tts.ts delete mode 100644 src/index.ts delete mode 100644 src/lib/constants.ts delete mode 100644 src/lib/setup.ts delete mode 100644 src/lib/utils.ts delete mode 100644 src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts delete mode 100644 src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts delete mode 100644 src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts delete mode 100644 src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts delete mode 100644 src/listeners/mentionPrefixOnly.ts delete mode 100644 src/listeners/ready.ts delete mode 100644 tsconfig.json diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..05d6da191e04857c8b5e0dc09df229b786c21dab GIT binary patch literal 6148 zcmeHK%}T>S5T30$MD$P(1-2AowJL&)~&x zc7~b|TRe%%49tF;`Ppo~4VerPxqMNMiH1a!LKz2}7`_npv-V`57Ir#bqneE;=S@BD zMVjHaGQek-&;iZpif%jSx0CO;eRWe5)2f-mmOj0_J(ds8M_qrp)<3^&y&H!CskBYk z)KEzmbV_xcEvUlPO?w}sSNFayw!N>r(aF8;YvtN?^SRIO#JBgo=k3+zgEKJ(jDbJT z0BSZ%x+Q3%F<=ZB14{<@`{1FBnPL!(pAHPM1pp>6N5PzX3C{6~nPL!x2jV0ZD5*|c z3@7QZdyUH!gP^37)8@nJot<_lF5VsM`xs6x6SUD7Fb28|Y{=!1>;LiU{=b`KSH^%b z@UIwfNiivMJd)Pd-otUN_0TgY3&#b)WeO&-6eE^P@h&t9?4BpUOfd+;0hNU0eERt`2YX_ literal 0 HcmV?d00001 diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 602bf0a..0000000 --- a/.dockerignore +++ /dev/null @@ -1,31 +0,0 @@ -# Ignore a blackhole and the folder for development -node_modules/ -.vs/ -.idea/ -*.iml - -# Yarn files -.yarn/install-state.gz -.yarn/build-state.yml - -# Environment variables -.DS_Store - -dist/ - -# Ignore the config file (contains sensitive information such as tokens) -config.ts - -# Ignore heapsnapshot and log files -*.heapsnapshot -*.log - -# Ignore npm lockfiles file -package-lock.json - -# Environment variables -.env -.env.local -.env.development.local -.env.test.local -.env.production.local \ No newline at end of file diff --git a/src/.env.example b/.env.example similarity index 100% rename from src/.env.example rename to .env.example diff --git a/.github/workflows/fly.yml b/.github/workflows/fly.yml deleted file mode 100644 index c2caa68..0000000 --- a/.github/workflows/fly.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Fly Deploy -on: - push: - branches: - - main -jobs: - deploy: - name: Deploy app - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: superfly/flyctl-actions/setup-flyctl@master - - run: flyctl deploy --remote-only - env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/.gitignore b/.gitignore index 602bf0a..2eea525 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1 @@ -# Ignore a blackhole and the folder for development -node_modules/ -.vs/ -.idea/ -*.iml - -# Yarn files -.yarn/install-state.gz -.yarn/build-state.yml - -# Environment variables -.DS_Store - -dist/ - -# Ignore the config file (contains sensitive information such as tokens) -config.ts - -# Ignore heapsnapshot and log files -*.heapsnapshot -*.log - -# Ignore npm lockfiles file -package-lock.json - -# Environment variables -.env -.env.local -.env.development.local -.env.test.local -.env.production.local \ No newline at end of file +.env \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 4dde0ab..0000000 --- a/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -# pnpm only -shamefully-hoist=true -auto-install-peers=true -public-hoist-pattern[]=@sapphire/* \ No newline at end of file diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 24ea8ac..0000000 --- a/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -dist/ -node_modules/ -.yarn/ -examples/*/dist/ diff --git a/.sapphirerc.json b/.sapphirerc.json deleted file mode 100644 index 3a901e3..0000000 --- a/.sapphirerc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "projectLanguage": "ts", - "locations": { - "base": "src", - "arguments": "arguments", - "commands": "commands", - "listeners": "listeners", - "preconditions": "preconditions", - "interaction-handlers": "interaction-handlers", - "routes": "routes" - }, - "customFileTemplates": { - "enabled": false, - "location": "" - } -} diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index c664ef2..0000000 --- a/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -# syntax = docker/dockerfile:1 - -# Adjust NODE_VERSION as desired -ARG BUN_VERSION=1.0.20 -FROM oven/bun:${BUN_VERSION} as base - -LABEL fly_launch_runtime="Bun" - -# Node.js app lives here -WORKDIR /app - -# Set production environment -ENV NODE_ENV="production" - -# Throw-away build stage to reduce size of final image -FROM base as build - -# Install packages needed to build node modules -RUN apt-get update -qq && \ - apt-get install -y build-essential pkg-config python-is-python3 - -# Install node modules -COPY --link .npmrc package.json bun.lockb ./ -RUN bun install --ci - -# Copy application code -COPY --link . . - -# Final stage for app image -FROM base - -# Copy built application -COPY --from=build /app /app - -# Start the server by default, this can be overwritten at runtime -EXPOSE 3000 -CMD [ "bun", "start" ] diff --git a/bun.lockb b/bun.lockb deleted file mode 100755 index 64484fa5665633110dac29ed766c164fbe7a2d4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63236 zcmeEv2{@Hq+xEsLLuO^lTv9TNga{F#!4#s*w#-w=REALIp$wIw6q%!<3?(T;BuYsV zB`K8#74csS_xs-8d%Vx{@Vwvg9smD-UB_{D?R%|tp6goITKBr|eeb;^Bp~7A+iP^h(x>$LO`AX7~69@zu&rap-THB4yqR(tfaPbr@ zZa(&N`Poc|khqKt6N$bq?{Clu?Bcv5Q2&b$T{w=)UwA=qB)M3-c@qe zo)P?I@C@K7p<6oe?w)Qoc3xftJ6}6jZ!ZGD+Q;7B&XYhO+1c62Zj4+(1i?Qj4(yl!<3{`e zC`9q!1dsTg3w|s36>#k1YD=;q5eOzAit4?+oJrQ8*$yPs4++|#ac+WRKJdnH%nDuz zjuHRRLi~GygW@h)&`n=x=j|otLh`fo41i;#uMc7<@6K+vR$h(-LNt`4`N8;DT$2lW zL*P;ST<8>y!`96k)+K?^3FRn`YVhpfuR$Ypj205|6%ZWqx!{q09C)M?ym0Kg;G>{D zFO=JW2%2{TDCY)`jT@`?hIWYW1Rmwp3OpJwc6^?5VO@HOd3)N~5!|`v=c5Ze8n>;F zyR#kS!OPv5gtWXJJ>7g990_+8+T8$;bnQLeT%eke4tkK9(6|NNLyjaHNEdy}4PdJZU+~EY; z$v@x!L1#N3IE@HSUSj@MF3toiPX~7^PcJ)rlB=zlk2@h8gpjWPf_Fz-gO#@(c#;c@ z%wKT69-_2CIg%He?|%b$#B+1C^M)l&fOSo>5wmyYhjKJdEPl3yV><9CzcZkR=KTwJ zlpiLD6P165#mE8vIe182D0DS9KmHJ~7rfS4JFn|6waFFLZCN;8Co-?&w%}R7qxG{4 zJc>^Y+M)H%3m(mzotKT3vz4u#jWfxI1Ul;L=lhQYkJ@*DNAdBB&g;GqUKqasH+xYOd+Kw!Wv_yH&Ve?t0g2>x*8M#{~K$EZ15Jj8v&{ zF|qXBxI3^cUu_%t$Foh^8Ye^oZpMcj`ONI$)A(+(CyOxi;b)5=x!~r63gUI9ha!9~ zt4&xuszMVe63Q-{UL0%I$Z0#2@a=qd4G+apX3nOQ9G#mUom>C>xR3Taoz3=xz3Elq z>ZXr4o1E(-3$XT{I@RMFdc`gsU^%v?Bq<>L?VESyafI^%N~hmveqlFkNPnsOGF7f^ zqhk7d-7XGO*}Z;t9kttq>Z1xQL+n>f2}owdcLk>F2Yfn_n>Z>g*R;_lxbM z^Tg_65+%%Hg{zbmXxxVc@IH+?#y0{FU*AHDz=e)_J#2-q@ZvLN_fb zH1Pa>6^m@_%~!WqhP2tMP)k#9sp)qL=B@Nd)jZo@n9F|s`>E-ymu)YLsb!Pa%EV|t z%qo!ao$S`;Wxi|ndf4o~<&2HlwFmm_;R(x#+CkUilv?vzPx+s9>LOiw;_%kx`*A+M zE6;j*2L$&@9~#@6df#o9^KK$de?-}(m-JftpR`V}G`#dxKCPhNo#)Qh?0f9EG{(o` zqM1!Rd%$~;>^X0C^^arUSFfbDb&4$c!FafQUsA~7p7Aavr&A|G3m7#CQ}JhJqIY(C zI^Nvt%SK^Ezn9Ufn!fyb4zuoZZ`RKmH@HW0OJtI%spCs7wd?O;r@6g}vFc57yc3y% zdz!NBND5VP+SS)?=S7Y66RNe39#hmgb6czSGd6Cl{_v|?@|#9WE6=V8-5?raXMJb1 zQHZt4UI;t3Twdqb-v37VNOt9*^k(f1AFis_eXXP}=cuC2Q|fWPZEIe8#fAGz$H+WYC=o9n5AXh%(WRI*R}?DDFg>Di-1^UP9mPlaf8 zEVd5rU2foJ%oILSGPFV6+m^)~tB;rE=C~Ytgr%BQYS?Ulxqt3hfT;Jr&&N!ZkDjoo z-q8~Gye2VlRqgiD3S`Z(wj8`*yt`H3im9^sv5?ZT2>)Y| zUoCc>_~q5IUgE-*hSw3+K|!XbJO{ocuW~o)*gIn48B({0)JQ(_Fu`Q=rTwwYWG724 zG=%6*X1(<_k>-gF)NjG!Z>1g;*3oZid!(;5kz87Y@mrBds!$SIzFZB&}kcZ%xSg81lh!^ zI`4*G&SYiof*MbG1P6s0XauXx$g=u0C(pVUxk*(zkK3GiXKQBEuhgJ1+`qe-^q$AE zlys@5jkRv&v~%MxJ#)YP9Bad^y0fKs89jWrI>3#|RA1s&E@w+4o3+PkWmP)HO%L`K zc4~h)`-)baEo8g4K_IP;i@*t=vmx~REKOJ@m&NjtzqH&vKJIQ>KTetT{JqG%*gyt$ z@*Y2v-Q9W?6ZZyhSSJvtE&4wFYrBr2Ba*r+_w{aTw6SSl``I)3nPUz8-oBQV*Y*?( zUV0+@#?QAqRyUdpYe$AQ)BnK87r3k-qzL;uI)v*k0V_wxe`Z)a3*c)4A8rc?a7&7A z=N4rc|0WPLfRA_-i(<|7V<{p$m16Y!BLTqn`BZc&Ey9|!czNp1-{7wABzEbYddc%aTx+3p(|(Ar|Ue2 z@pb4mmcJ(8qxG}celZ=4&j^!-@zMNYn}lAgM0&zu4R%O^lxoe7I!}MaO@K z-wAx|{6#sy_7T*_pBdIaEj5844gF(nv2yJAk37~+3HWIL!P;Tv*zq5Eteq#0zmz$| z_!oh%0sW)3x0G`T<4*z~T|ZFYsBEz@VEm2nCJ){k&CMU;E_Mzvz7_D*7x-xI;BZlf z@r!|g<{yoJDTeXU1y30lztAE@FupSI<$;gJ4%fj&8^-qrzUBfSkzqO(Wf=c1@X`D) zcHS^?jK3T%`q=esN0v{b? zWq-xR^ZS6W1^q9Di{}eM($M_}nm>3Rx9AwK{_KH|KmU;SVhrQw0Uzz(=osxESovRR zVC{N=jNjL!)ViO~HADq919u`J3kz8&z*7UD-`i#-Q1em(F}{Ak}rbAYX* zzX}55Q@}&l?Z8Jo#9ypo{2jm-13ucfAeH~f%U|hW`~={m_Ya7V`bT@nVhrQo20mK9 zh=X!~uo%Pmbnp;SePR4?E3s(9_}0Kj`zN+`mfC*?@X`K{cxVom%7205qig3<`NFVq zqWc%L?m!BCRPHakM=(_|Hziwe=G1cfRFJo-FdY?{ugV<2n!bRvAqk6(5w4SXnru3u>HMEs?me?Guh13uP2@{2L7|Es_^1U~8)l`WM|2M1H2%N4 ze#3!}#*boJZ0=E8EdE=-N9zZ>?k!dWJ{0Bi+Rs#yD7}mcA@KvFIH2;4m{_y{>{~HVaWAQ9@3|RYV;G_FrwEmW27+-G9 z{Ppkeu75w^qx+w~yZ$PHuLyi}-}QI${{i@D{bF3q|5Yxq@hb}x2nPQFKLhxhz(@Tp zC5Kr5W5AaIK6-YA?!mBfr1xirwKHBjfB%Y}d!ex}b^IrRkM2LQ*s*#1v%XOs)_*hb zRe?`jC|m40#P~8I^XrESiGnRPe*XnN7XMQDRlqlf_!k@Ze3btjKi#_d{co{(`&Yg; z@O7Ynl#jnNf62f{>krE(mIth#Kk``nC&0(we=RkC%yIw*(f)Ch>OmH#TeGVE$~tNh>OZ);~ILkp{L8{!zbJIo5v(@bTwA76;bu zk37cz0(^A;h~h?Eto)C7SUJ{C78W1MA6hq98P?{{Jl56^_$Ysi%^RkP@ymdZy?;Vw zOWpro0w25nEjD*32CRP;iTVCfzf0MFkOqA8F$dHydjF0z;Bn&IJiueQIs11W^$m}0 z<_zzV4m{47Gvv`;AUuCK=MfJscXP4Bwl`;ZkLuxcn6rQ9Q9YzpEI`Em#oDR(WTs=I)m^0)N56+1>!+TT?_lk3dJUWJRf9`W19q?$p1^{Hp zqvL((01SE5-U1zf{T+|u-VZ=J2Nt{)c(nE%0m$$kwRf5;{jVPJ4=$ABJ!F0MsrMfDCzboP`d+{+&nlrvWHG zX94Iq8-OeuKno}Yp!y;JGQ3Cai|0z`J!)6FaEv^vFI({C3%&w8GUU-bTt^3Bc#n8D z=1S*1I<8$f{yQG=>i}pz8UZNpEdXSAkMh_KK<)1XkS)a{{sRE=-2kN1v)~_rM~3&P z{xJZppFRLOeg;5!d=5Z{_o#jVfc%REKL{SRA6hsb29NBIJgQ?|0Mhxrf1-@cJqw=8 zD}LWZ<>(mYNf8}@A&-ug01N;f!0-L@_x|~N|D50V{{P=UXBcPD2Jqi(WQ2KLyfo1( zLyUMvW9)qEj%{Bj3%Am?ReVnS(x0%yfB!4z3*Dbu&L;JD9?H{X5&EeA%%z-xaNmz# zGTGq?ZOz0$pUHjQ`D@C&F1kiwMy#gp3kjA;pM6?Jy3a!<`LJ?R+TP`a)tq{E887Zd z(TGutWGr*OK}#q8mG4mEWar1%!o0Ef(|%^8+iDeR_E4hh8x}8mjTtdg!{n2m@rgu> zkJl;IIw#ChrZp9=V`(5WFd9hcApBI`@;OPBE5qgS1<@YI18;J<-C|Z74}dsN=_ICXp59`du# z3TG0U5*30*K1#JIJ26FV)@fHHwl59j}+Eph4b$j=JSi5Szt!= zX)6p@eB3R*In{Tcadv^|po` z=m*a~@6xBvj33iq?x`?4>ivf9igL@#`cL@*{k%?FSZ`W=f7Doe%cC)xju^k+>A};_ zJ`R?I!|nk%UHo$s;_+W6UA-^QyxUEqU$?PGJM?^*?tQrrL+h=onc9@Ubt@iKO>b>zM5lZSdsXAu-PFZ0`>r0LoSwE=#cHGv{{~k)tdOWe_VXlkq&qH6E z16MH`sY^!PU{DrMZhwW-MfWn85xtJ=4$`joxw_J!YD$V3UQ_5|p`NiudE~}BvJ|`;Prj8XD45(h(_vm&!FBL0| zW8>kUC3V6ytv~at2X3)dKC0%wYht6+x%becv3E5lCi}xPcs2(5zwLE6F#jHC{(NJ> z>voA;vOhg;xT%M0fHd?d+3Di&=kT$kyXlz}3xl4Rs++x`>r6}ev6*~wno0Y~#)kD< zqCY0M{AiAzY-IeZ>pS;e6^j?$%VS2|>gKPz`YG>0jzISbXB$byBpUvXLqap9dYoH) zavttA7`(-I^rMJkeAUGI8=~>IxZi$bKdiIrz|+_5aZ&_|fEp0ObkX|)%!o76>Devr zo%^Y@X#9S?e6Z=GyvHPaW!X9v4H7Or$|+nYqq$}=15M5GS{&c7$0 zpEvlL;XjPHb9aB?iff_r89Vnch@7OU~Mj)M}G2abQJLLN3Yt4h$CBq~(fPxU3&NGfQQ8h_Mb zAderJEDQ3GIb%y1e4kU({({2GBl@yNahg|E&FDuxgTGjG>s)9qkV_fC>2l(APi@$m zcC*QvPwl%8DM~Xdz>SiLv(1uHIZRyURA-&GJEtt)*XZ7Lzb>6;J3|>Na-HMT3YX~% zVLN@fa=#@zz0Skwa^ZDbDmrJ^7_RV_2`i^iYkcXq^T)t@=G61am*0_v2(}B48NuVHd8i|x7nwOE!ua?nc{hZHn`hdlXB<|HNxrg;&tDcdR14p+Pd6(Qrd3F zzs7I7&HWO`J7g1%Yn1Ord~m-mkz;&Jxb)q~io0W9AFEvQde7%U7}Gi}R9|&?TfeOV ze*N;{bzLa!vTil4bvQJvz$hohm_Fd@_hIZ7qvt0Vd z(=B9Q#x(5A?weFyu?+e^{~{07XR?6qmDGp-f5Z5Y+%Um>*-4}cZQm?+6X>QTxYQ*UZ;B{@~wmAQ&ZkKKP z5u;W_shm~J78-g=S(&LySIQjAgu;Z>lqSmDeLA?)Bu)#o zUpRRjrz?opE&IBu+Opr;{Qcf6_eSQ%&Z*o818dgG39?EBZD)0AKKtFr?;ovUQlw&1 zeHp*DArNtsiP9DynF4s|vRhi9ujo)Ud=x~uWJ4y(i7 zJ^s~`d-K7n9WQb&iMX4bEa$nrtKpQ^z8vE+OV7sl{DbFu#jYKdbEM&aU(&fF*5>w? z^v5-RMh`AX`pZkN{=k7JCFs(4@i=kGDpK1V+lUCi&j`v`xZiajq!^2B{M*dEw>1}>T zaq+Ij$1A++gyKk!w;FBXZja<#KI2d}V@H|yO^R(QgI|5UFUeml7kIrZRlj|;L~Zyf zQ-8{R5>bH_Ndl9D^*dJBzYREt(-pz%CZBw9wAPQ2AbllN^mdK8lr5I<#YcdZ1cyAH2Avtq@4HQrK| zWA|7tp3ACkdNcas4(;+U@7icaZC3Rd&#Yf_Y({@aaGduydAFVXQ>!A^axv+cG+2ac zR`&9oV8ZX~>+!ljJe8hsUI{N~YFM7MPpjYsLrZXsu|-r>U2@ePqrSc#n%$6t645Y98bCI;idkN25mkhLMZyE(vkX z#mQ$MM-T14u128by(+BR{c}1ZCNdKv5#Y-P=MDt9j&*#>bl7XZ=#&r)4 zr*`uB@2O~Jws1-J|ANyM!|PgYjM`T-Fl;Wmp)KoNqq6#?5&Q6vl>SiDpsUlO7bk|| zsw8+<@tnR#mBHm3@hN|&^}S9KT%(>l|15R>Ixmjbjh0Y&H>uAmF)=`+ zJFuEGbuM-*L%D%=zFix$&b7kik>X!%E6JY;^E>=B)1i>JqMfGTq5jYx6nOWR#3im4zDt&kokt~e8BSLcuS?LB;_DycFc|q&oNKsz<>xBbUD3?lMusB1 zQ}Ps)&M)COBADTn4qrWy%6~gVy+G(KjBpe+V_JHrYnPqf+tdq8LFsgd~IqIr)x03 zbDWSHE!;TX^JwGD{@b5dwXS?ult(#n^nN5|TS->w!K?$<+7;IdwCAhEeJyfUHh8@n zfB!3s*ImZW!M%xC{zUUZSLof*$^e!-nFlfgV+OX$X`0dSt&1G`nXFe4^W>e5Sas$( zFB{emTV;3Gkc_UAsA>w@_YEN}EZ&WHU17C;!N%#pk37d7nZ{jdHsza1KlSC-MAC3* zuFI`4jaZw{5yBf<1)9@;saa3EJ-2uneXnnob;}#pBmC^|sLr3l>B`}C8&oxWrc7SS z^QTN7U=yNAKOHaLow?TLdDywv=MoKEK4{v7B~Z)sOC7J*cTsV+w{2`Ov-jLPl;kIB z^Q7p?`U#w_JYKiXmSuPI)3l`Z5BQwMhu=IkdN(>4l|SHq#j{^}c87725H;gW^|#%5 z3Pw35=LMq5o6WPSS;8VilAvk zMeO^&LsZQSqmjs|Fvtt8A!q)8JS+7qv`B=q)SGiCQt1ldGGjmfe*8sQNjg z|9!NX8RxO5d^CA{6rh34+h)9Oa%|i9i^y&o)>e+Sr{Z)~$S>~sQt!guTB|wr{rfs! z(~HRsB87Hb4lCFi+gfB2eA4m{UX9g^jaqwyx-vM!=?G32eV%|BF(e@2Te$Q$<?k9D)my8oOS(hi6B|C zqwy5=H;0qeEIzG|I5=`0xmGsojMiT+03l3Q850FhtS3=0j~`AQVXZL^6C5&75qr=Y zMaOX_z5D$KR#VXH9caa9~pRyIN;qri)jtN$u0m%NYiO8Pp_VJ%zX8 zbkS?fhytG|nrT-GD)8>BwX|q>na9B9d~s0hY0dPGBqKge$>%MGL1W*DO8w)Lk!@c$ z_8UAG=^x?#db26WoA@N>t5~x(^lwD9gOqmyj^&9=Fanv`gaqW#@~W=GpH?R~zdXsrYlLR_sl? zsr^7>#kQzkoGyBgh#8T8x@m|(W!r$CGoy97_>HOrGaG*6?E;(LTkPf<5?%IO@SaQ* zE79JtwtO~p?NmXye$tr887no_K=WuyvI~|1AcV!c4HE@V^p7T48^?%G8^}pcwm6f@ zXO@!$9u4r8hP-GiyQ*7madr62%dzw3*}l`)S@-1#4Mx^{>0>NEEE;U+WWGq5sHO zOd%|nzQ83XxO7IHblAiir>lk66%IBlv2H5$4Z7Z$%-$PRVCC6Wu0s*A#>sH!cCsLm zS}7OrF5PmPdl?UM6n8b0XH_5CL&|+N^I>nJxQMw!GI~~ofCU_`j&Hh2Q zs1sgd!vR4Pfvg6<>}uVnZnRh2<4(6KJD~ZjzOk;=v-fbEJT0>qZRJIC&NYeqDLqaL zkKp%TZM-f+=sqi*!|k=1ckB;@Z+gBZ^7$!Rhp=+dq(8i^4JIa+n)x$t;yt33{3h`F>{CJXQ*a4e>VtWdDwx8f+v2P z)uh^A(`bI_ZTeVW*11$Eg?uI(qk4&IpWH*YPxzV1-gmEexqoN-5fcrTj}G6TYChEt z)|cWw@%&bPt;^K{bgjg6b@95_mg#iGMAB_>->|m4ASL+LWET0xC#&;U zTU=!I-SMMi@}M}ap|22!Ud&M zGjO{4c-`9I{gXqt1ic%@-Seq?6mHygJG5n*$3><+)a=ljMlRN(2a$y{jDwy`{K6Xf z(g|xOK1&r-s@d!_z0zlQldKAV|A3y^Vn*~TO|RW8?9g7votCbs!sH^|Ze!Zsr83aG zj&h^gNX9WavzBd&m-{SKpGXaM<`g_~HdOoUBgfD-$Zl^pyh^ADgs}YX!bHInXP7sS zTQqzapDoTxrfRF?j8EF~wm);+x-Vq>1ef^R*{R_MI-j3rtEfEFhocNe@&zf!t4l3* z3vuq@-D%Ka9FEi7jo0mGthvS-mTnwRZg2WXjwza8eqp;4`85`UN1GX5bG$iUt=*e9 zY5dXE%Hi}L1Emy!Oo49eDmMrAq&-M$+hhwwkzG;~x%oWeDVf5W zTR;d~55|}%c;egriFcn}&n9aOVYBaidB|mV1+y>he+UqKI!taJnXVU8n4C>Uw^IXX`a{s(T+FW6?CLmSo$T ze8a0KIru@e$oq$}B`$l&`A$U#B< zwSgHiuJh*sm#GH{QO9RB^>TI8S0vjiZyFhAe#tgG%VXFQlW|BesJSy+M{IZMu1@wQ zxyvlAIv%z(d4iDzHmf4Cr9lYOMbCmUBQ_|9`kR(pFgfmzlcZS7A8V!?`R${3Y=*t$ zx!TYB_>9-{(A_gCwy1VceD*GtBX!lolY=YHJ@IGkux@#MH;4wE3z#nYEEzLmV~11N z<-4B=Jhj1<&J7(u${iUKK18}XinB*tNWON^mh!>vBU}w8FZ1n558GM4+RJ`` zUT`3M^F{69+Lm9UC0jUUBWx*749Vejt?;@R2P9_N;|W<*d!BxsS;=~?>pWw=a`F9j zAJ65l35@hPl3peiV_BXYxsHx)R-f9#QfzRYy3EHYIt#&%I||)q`9KSc7u{1~MvP|a zSbd(+P+k4~gAT=(Vdv3~3;YvD8k(vcNBwQo!X#FzI6d}S(fUbo^|s+PZ{FVQb)DIj z@$_iB)8j-}pZj;lKnT-C?=>(Z5-a5OEey=PKAfywGqC=uO^M)-qIEX|ia1uTOwu`V z#rN^i%M?>0Qza`ZlLjf8rOm87@|ay6j}+?2-lUbi`x1XYX^V-1Cu#)hil4R1%DKbC z`Qj^?m4*=afn_W5gKH~nBRz`;28wKs7#Ztxd@s!H)2()TFDmqr@2mj5#x(&dA(GDJ z)x1KWfyHZw*G&jK*e6G_er#XV@5HcqZ2vc(Vjf%WhEG3f)`{-E=I>u>*(U6+XQg+* z*h=?RalYi^>GTWv;`DJ-)_r~QTK3Cvy7qY84)iV#g1cjqLv`2}CDw0|RL#hHvud0=~r+Ix`y<5r>%tI^C zy+5Ywgx6&~y8oPDz2eGa6UIc9o#ieoc==aeU>YS1M~Ky_q(s*yb2}a8)5v^ePe&-#~_ak?mW%!tNoZ>;0>QoqE_G_%EZO}zl`@JJ>X=QmqD z(Zn>mPt+`#gZ$b)E3!7RD0N9h-)`EzTu$eR{{d^C+uAA-M^2@oJr|1?t!2!JdgLE0 zH&^phXU6dn&#|nQEzdkMaP-o%4x8jP|m1lLEQG0QXU-5$9?TMq` z995l*QnSel$nt5jMGHvEcEM&t`8I{$X5|`;y=Hda=}e(t+vd}?M$ou3;ChR382)*x z8(!DBJTjGM*KKy`GeM_cMThMVqlzrNHB_5qwN`2C-ugsx~l?1!z}n7rX{V| zW$vyM8S?pY<8C3cvxR-%Zd1`W*1c`%$LV_Ebw6};SSY)vR5Tt@YP<~JSnBoa*zuV0 z-N@7p>2q4wg1n_@9qbD}6)}C0%HU5@jeUB;D5$4uPsL}Kq7-ZMf;(q$y54wQr=7c6 zQxsz(znL%NZA~QA(Z^&jQ?ZTOvHfxqm92dX#XD}A6~_I1cE#&U9?JZ5EnXwJEGR{3 zrT>u^H?N5~U!TS4`rvh=zUhp<7II^Aeg4Alm;E7@o79qtx)*6h@+}@rDHcB1{*2tM zO)>ZA%@;3}KTif2e!g-v{@Eo5!$5VYJ?P#a%dan9S1iw_YDEfVd|ex_1M6pZ zkHY0GhVjY6b+%qxdzAW}Z|R;DKiv|b%Juy5%#)CvTg?14=KMO} z*4E3ngDltQg9a9F0AAO7kfl&gbn;@F%{onm8~pLf%Y41p`P%TY zy&qo<vo&&{z?5Jp@SXAraKIutOzBmxHv@39@6=hp7r5o)lJLRc(3N%D*T@D z{8yZA2wpehoJLE=^*ic{ELUF$@ym9+uz5iilXClXDw$l|gyUQ8G{)UL3Hd9&yFGjv zP>!Jd|kcS?yW zm03qVtgpykc|$NbI>a|3%bS6-uqolA`a!k=p0Ue(`SGo}-|p_Y^L_xQi{5);MilpE z9=sC7PiWk>P9i^#s&3#tQ)<>pg*9@-vY{q_CaO?B`GAWD+dsXjNzU-TGQCI3@~g2` z1@G`n!pJAGn&|`(!s11rsbfYYJH+XB>GS<^)m{2kyk348NpBBLvItS^ihF3RrB$-U zwW4c%`7>931>Y$8J_|-F`(w*)^p`9S(R#MNUyjPj$&jcIh9OD=XGBk5=k63Ze-TDIO&O@1rUHfKts2nWXeB%;T zw@3qMVDX}79GDUBel@F|KI*|?pIdglm}JWv6@7QjX~`T{VisJvaIEj!qo0H$-5e@x88pi#YGUD*5h`_mKmBlOlsfqruv8IL~_Tj z9}0aXrw@Hi+0MU8o*$=s46m!5L~*M)#XGPv=zY0p-tcEqXF*C0_ms-OcJ<-c8wLWO zGT2MLc|Xc)MqjSd>p7~d??1M@QZds&RCLFNyUR@Q=UWtB_kH)}cD60w1uw|P4;K~m zZnL~6lGnZOrofoFh@)rrH>XD|tD4N19d1u+o*b`rSJuB?eCx57q0+k70xcgdik`r~ zABx87R+%}kac%UK&mtEfIfwcj4k#C(Bffdc=ymqF-qSwk&}+xkddeLga*AL4bUEg^ zYkQ5<=gTYo3{zFV+lhO*wJyWuAqKD8b+W1b4KKa5*6L7U-O8Q4O3Pv&_+P&E=3C$8 zs%|NA7J8b#t||XJ{7>T#=@Ig|qP2d}g;N;rD4luFpE-Gl0qu?0dN_{PZ753EMr*Tn z2la*G@M2*LuDfk(4su<{V3rfniX4x8m#RP8dw1D+>41rus>5-G)h{Mgp4~q8=$@Tq z?dq`@VXqmS?g_lEO~?vmfoFwV57H(&sP~O)`xn-X{mAi(Ip>f(nscc%K%TQhHPyIg zHGB8=7Z==^BW?HBcg0Y@E9|J&xLl=WIE>Rp-z~t5Som_6rD4}D>u(!E_UsN&GNI#m z*}`>(L*&inJ(>53-DUe&TjdpX*ZHpYy2t1BD@|M4U}b&q+fA>3o$s=f+Df|ugs?os zVWQxP9qaN(#_GyMSq?3Awl~FYqUUWO8|_e+I)B9N7DGk6m>zea zOF+!!vA7kq@ej7pj6MDodG$@!b3uc4M&fnvei=)agET#Rtis>mbkW|188OgVL#V#2 z%*~@@(x#%?Ucx0#O7OvlgZQ|{wsrY*6|PhMTg zaknrTWMWeud?_*z%BPkgTSO8ZY>SGX+WYXL?@F9*5?(i1>(W!o)nc;@52d4{sO=LK z?xiZ;l|4AsLTFdM8dVS>knrxw`HUg)r3%O0kxKrsHK_M{&BTc-{80pdvxloo0P; zYt-EwokVuGAGmSg%1-*-t4yCox-)tN$UQT+NFhJto!eb4^*!`W#KTuBPhaVyRNW9| zX`8~rg40dI>uzHv-*R^<=EEnq3|gyJN}ly3wkPl6XM7b5AFQjBQOQ`lpK{y4M*H;5 z^zI{f=w2i;oNW1}A2|4#f8dT5P0pHiINg(YUGH~w&*;+ni_11%(GncuJTK3G;^1+E zsUs3onI4Hj3cihb%>yq!NE@Ho8lrV}M{9UajKZZO9`7G1ZzfV%5;myfbWh=R-zpn_ zv#=)b_}&z{y+@eAC@aEUr08oMEp??*FoB9&zq4L0QOsz!+DGZnS@6_yPi)kS7fm@Y zDcn+~@(gBnoWtp+<8_N95)2>Gn9ZK3{J#3CZe3TEhQOouO}i+_Q)|}*bQ|w$4(MHV zudQQRzfssOdT>I`)*?%}!M4jdP4SVIq)Q{x!qx-&tOzrr?WU`avuC*{o*m=&+SMUD zxVPUokWYJx$3I%~YU4iVb8dMtO-Y-ahtA|~Fr7@cH#GZf`BGeIFColL?nW@zl_?Oy zbTctg@Wiz>0ngd2$(1(BN=Y^vT|XW?vrJG_!KT`RNN7LZqj2ks(zDE4CtsU<_8yT^ z?I0@e-Vp8)T->i-Cz@7BNfE_|)6K%`Mv2C+k~&c2GsSoCw$O~n1!cu{ftwFCMr&wu z(vKURYOERAFHAeEoBf(H^#R$*?HfN|Kbt3S#!DIFam0MgED~KSv3O78b$_k@FxV$~ zk78?Zl;#B$$I-}1Zso3G)wjgpgZghYwXWn`G45UO;Ty6C+pX2j>=XLh}3m9o4Z-mjWo^-lCeMuGnOIDuDgZ0fdewK~t&eqVcX zuu_#2L^#XseU;}#NeaoF$fZKQMnHfjB0@n0gs^zgUV$00(S%t2tLyvbRvPjt<}K$s z-Uz3hG{4k!_?~;xiPHPFLJdE2=zE+a1{3%mMA96Le;4j~@ZiCaLqDgrEoqLw3;qN` zm~J*E3Z9r!v|`qH?3`;pRf)piOw;T$o#~@qD1=i@8+9hV;OVbys;F&Q z5#20F=5}k7hRwd~-fh3UEYyA9;&gNHx|Z(LwT!$nqvWIK>6(kg)i$iC2z+<^@geh7 zJfAumn&huZ0Cw_whhJj=aad3-Ry$C@O_B_ z0fyD@rlVMe?`mEMieDCwtQet7@z5M{kgqT|L_K7 z+7#Kbz1PsWi{&8?uUnW9TFV(!(HF-%8B-D5eEQbzx*6|Bxm@l=ohct?B$n@+e80Yr ze4;*dpl(2RT4v;FMjX{G3x@Ul`)q^0kA2j_>7sKQGa~Ccnw?)Zu9tn}`IHnI${!?D zS$W>oZu?+p%hz$8K#_dv;5G-->;v? zM8OkX@=ROuGsIUNH!P@UFz+|l_mL7|7_H|un&7>^Ut{=Z+U>nE;%*<+4r`d7xZl!a zUQ>3vRLzc1zRr8l{f16!1!!P-K<||?BQlPj`n*<9`E0E3v1$lgDSFSZ*eYgJ>?`$yjRr+~x+fl0`W(mXI zfMD@W$tLHfS|3j9f6Gg97%28PmC+xKhQE{gzh&sZ$N4)0zccVV1OHcNfVdDH=FuVA zv(bAo8Ui6yg>(MzxTAUu@bV80aordG#`$`#`M<6HhmHRqt3YETb zO7`C+^jqh527YJYcLsiE;CBXoXW(}RerMo!27YJYcLsiE;CBXoXW(}RerMo!27YJY zcLsiE;CBXoXW(}RerMo!27YJYcLsiE;CBXoXW)N41Dy+B_}+AJzH3?~FHakBlB<`u zm9w+BvzyI9dy=!AxS^+=-4=dnNq#R!I?eMb)cZV(yztp+MXZRh~V(C;iz85ID&KQ?FR zHxj4}@tEgI=Kua3Dx(G*T`0=|kILYCdFXG?&S~T>l%e5l2OvYg2|(>opV|Oq=K!b- z{U!mvRyJqoyZERK{XSJ_u7p59X+>oWfJX~u=sWPJj1h2kp$w%Rm7y^_Stu)5C_`g- z4nTGDzcGQouedNzY?q)Aj8@4vhxk0%=V@G2}p`x*%IXwo50z?C107n5~ z05^aq0R8_D=>KZ)1-JkX0-OO#0Q9>D6@V&WD?lBf0nh|!0k#9Q0cbwpYj^~HfB--c zfaYU0U=4s3faanRa2wDBxC3Yg+y&eNTn1DEssL92Xr8VCssS~CVn7L?2!Q4UO#}LE z8Tu^=`kmDYfEBpByov?H0pbA(fJ8tNAQ?acYy`*w@6@U^z0U!cs0Mr0F0Lm3wU#KmbM>Jn(-dF$(0D1r;0PTXz;F$ny z09F9XEt*@DXOv?!$0#>w&7%3|2cR{I)~f&jtzEQk*DgR6JX$|005pe+0F+-ehiE?0 z{9<#2a*Nj2dH|Yxloyn9#6#^-TQLA?gW94tNE>S-1+Ot(RFB%C^|G+N&!TvdmePVp zv1|q)4q79KgUZl(i`r@c)B#!mlzSBCE`UBj51&z zO&Q`@1FQgh0VV)rfGNNNU=BC{KVPXaOk=K;BZ zvw%!M7T^rvG$0$013<@!p9eSx!2V#Y$Q^Wm&Ruh9ISZdnaMK|7k86|Ef4ym7gW<+X z1d-Si8)&G>B*iz1ql`i6+&8J?Ww|*n#~xv!CbN_hR}hCqDF8K`z$qEppzdwUVh%OZ z;*yAjRv#$t*89Y zI(3ntW)p-UB}s5!_}=(4OUXSIqSdibBO@+_BCuNE+!<{YVr{Y)f*LvKM|OS>q6M|H zYeF}OM%YwO{1livaUW6(&E+&?~8+Yd-kbQmfNp^O2@*8Cd z;YL0)d-ycI&(%nyStzBLyI%@rdMLu@vf6|NYW`p)l9QRPXNG=`8>Aj_b_${e zjx!C4L>b20%0hVKH zN|FM?$;qU|<>XMSNfY=9c+`4wX0 zXypl!6ZZPqb<}PbswaoN3KllBwej?K_Xe++{$96>!}R|&cP&s-RA;(}jfX*$=O{*k z5^$AOYlazKqYH+dB_fKW;L0lksqU_suAyI4RZmYFQOt^-L>?xtL33C!Aje%$lq|%@ zDrz7Q6%it=8U+I@KERL_Nzg>ee*b@O*RATFuIgp??AhU*>3i>g|Ns8?f8GB+dKjr7 z+q(@v@%Jzec_ZYOf=$!U!yC8m-Soz? ztC$0xP-ztFR&%lF8hIE7N;bOefr*J@1J^bHM>tkQw+rdmJk#0vn9;Oa+x!DS5J5=R z9{_>8@w)uQD@W~q{3LTQyuLXS`6D2t?S`6rm(O`cKh7M0@1{t~Oc`1{8rky8hd=pp z+Zy%a>h`t1syDRaF5(kg?Jk0`+)QTWOcS~d&gaOH+8E)(Bp9& zJ84GIHPUSd*6I_$PCuP7}C$9fRGHyxzV=(s+c^I| z)(?AQXCL9)348rt{?vP&iP%e#0uJ;&K#1#Iqw4;C<~zr`*I{+0fZbGy*GVG z*&qc)c;iw)NZYlw%hs=ZwPh+GO;}~HmaLLE6P_LT(W><$2#3Pus7T~zfY40$|FrMq zGo1d!8x|9?#9fdmLCSO3%G&P27l<0>b1Kv-b6r`YF(l;u!1bC-rnTJj^d!oWNN%S% zpK>q3x=oALsQ)VtD$9B)wqIoN*Wa3a_MHv?`Y2|K6^ms303cXs7VbW6%lZw|E{w3Y zk=LkiG2i~s#sf{4Pr8()!%0K_u)7|K#Szo4m+Scq_~^v0gjQW%Fgc>C~E;@7h(U(5_ zT;zO>pk`;2rUTp$mJd8|!QwZOL9%Gj@@wEwR6b`@?dq|6lUtb@@8>G>Rz4pHlYhz~ zG*bd)btj?CGYY@9pFg>G>ewETD8*55ee7$3gYjp*d;jIn*DhfF@Vbysm@$O6$d1&~ z6RWQ~cBO=1KBf(+0rJn6PXEmf{SLpOaDF*RM6uI9nm*_o4=w%^kHgd|XLgmXs#M#c zMmsRFosyln8fQO}5D0Z)e(j0vCwtrvopQ7U*Pj7$4j_>m?_G9u>$HDRkjlPYIl}fC zEb{Ar?{!(a-&xm8B5LH^!y}MQOowz9%$zysJIC++8X(Q^1#r-cF9B=0Y^2xwX0LNM zO|b!y`=8>syO+Lockj+R<~@UTkoTLr~P98HXet$ zE__}1_OvOlwCH^{ZU=-`TG-AV5;F0Y{y&Y*Iq(P|a*vVKa6_a!#v4P&c3)Ju>rO!A zOaU=d@P-Ft=j;R9AGzP#v9{t7`mq) z>`#;P@cG(*+&+Br**CojYIODluH$BFEA{xWvt!Sa$=^ShAv~Yr_oz?5I`#F%xs`%9 zoU=tDzr9e*=k)rz+pd~)!!5vRtVdQ3&B1tVP+NB7;My?@kN%k;WB3FCe~l=I?N8*K zi|)|(Jk)eOAW}bu9n+I~-1s87<;!co-8mN!ib$Y#9*$HgCO&Si+q3$RfQ zkiLMtw{OaJ^LK;hFobcg0E9I6WwPdp?2rk+0fbIHVDW{<<}}T8!m*lThc-L3dnZc- zwW}r0xVByKTkdM7)2}A-;KoSzVPd8q$7kKRZ|n;b03og+w|;R;t7gU9SDsjz1%x;NqypC%r!uv_gL1JMUwGr| zA75~Drq4`Jqm>pocLGB8*3$5cRbPF1OC2E6Lb@D>wOwHxPTq0(=d&b&bla8-Egf)xAhLyHme&7Dgo90aKccS|e;2?*^o?DzbKnrI-HTAI} z>)xVMDatybpDt{axJEpn`{kr*JI_Y%`N|iw-~NQC0fKRQUMgg8ez${jrW`#=5+&OK zP75GpJ2Rg@dfJNpzZ=FlILyM?2_UqWnLcIDwX=`CgcY5sVWvL=gf#ckl3RKW=(e|0 zuG`?Yi*cmpx)?`Fr}8*m>W8&(VZGq(TU%nWS1xA|X8(~pR+AXhd*RP@ zYpj2~r;h%X7`64@zk2M^LGzz+Y}>FvbN1_lZ=c$F&1WaDl%4^p-O)U1M@?t1lmAov zVcU85Nb-;36L);LBWSF2MGzU^nk-;9Wo8=Uc#B|IWG&;1e{_ZZ3Yw}ElLal7Nw=D9 z3V=PNY;p=&!){1t;>LJU%-fidme-wFf*WF%nROIq+=yi?-N{(Cr_Hp{7{}lE&~lE6 zjL$SY&{j)N8TpJgPa$NJxi&M6m9nkPuvD`Ld0xTN8?a$i1Y?|NH1xz zO*l^0zPzEqGTPuaR-rzfO|{$g8LJIVvQLAi${O5^@+qh7X3NmydNgThz%%qzASGW< zrsr$XM$(pzi0aXd<&cCOE0atb7HGo?=aTH=h7~Y|zaxh5H)wo7Pb1iRtD%YefFN?J z7tvBRjb_iR24Ksp17OM$#Kt!V#6SR^xQqarC}^}B86JWorUD#M(D+1yaOiY;^hmAU z4j540JpfylAgA!@xs++t;wZqBIt&=ERJ|Zj+Js4CX`%_R5Htsgg5hX(!VEI8vc8~P z)&|`{4vVhQ(43J}2-NABK&5&ofMinEVqAFSw zGBVahKA}7LHZ5lba^-d+Ufc?cT~INiW;{vmja9)mGwC4P#AHf3iy4+Qvb}^Um2QN9 zO&@SYIv5CcM4y&`=&Qpdq(!i#;+d4u88fn2SJJ%DYEi?{>qq=Asn1I?gzGYPz}}fN@!RbKWVeg~aRM2_ zFkiy~i$B>|!lo#%^aw{>?p1{)FAsqwOSDD^XA4k_Khgl%-@ps0STS-bJ&pI3S}MrL z5~>$yd1??P~Wh6Kt(H<~k($aHN_6PdV<^xfA;8Na8Gg^a^f%?CNXuOH@` zx7jQ>%&Ez+>b3bfBWH~7z^My1Bk8uBu!y;wTFo_ZC)Wx>(~Q!zjDEc-IF(%`GEG4Bwk{Y5GN;gd;#=w1o(kSe{lz_>_2jK%K?Tj4VIr zv}(;-2et{=0|afp-6Mq}E$|;Uu*ca{$N;{oke)*0podH`7=cK!2ocCFfs&V<&~X%! zFk77f+b>?egI2Mq_NoDRC83k%;}51#ZWZZ=G7y(pkW!q3O862X;x_O^0VxOH-G>-N zX|#9o)dE12@Gsb~y08gOnF568(WMG^7++P`q{Eg*bkFb!l0X$@j+$8DLzuS952QfF zcR&RmD6E5!c-ArLqCC>7qACyO~eIY4O5Csks;T|XdjCg~PJFrCo^AF$tDy>L` z`p%|7tE*;>AN|m*Jm@*h8tHLW`ijfWL|+z}+v>GdW`2rQbrKH$4ByojDY?nJhmzYS zGoG-+1dlk>hQjp_Mc5}~mS*)CkGfYNdhR8IT3p4ojJS#k(1yCR03B6TmoZD+S_3b!CX2XJ;X9RHO zsd0M5*!2x0H2Hrf1kX}-0wr03;mR$FIDp0g!Pg_+ZXn~W!eH?pi5x4XXXSZDR?p$t zxu7b3<3N>9Jc4#&B|XK&!Iz7R$Xl0+n?o&eN*TLfqSG5OB+!y@hE+tz0h8;@;ak3(=x<6EK+~6)!polliEptiqui z_~2Npm_k=j!f!-de4mdrf@7GDJic5*n;W@9v9o46?Y$>wA-e0Y^oqa}Ecup>+2&Vu zm@PdM!hkk-HgGCtn*sAyoj^iWz&3qHF*q3Un>?WLCz~usO)@?C_%B#ovJ)uCQdRk5 z@l_wtibd9NSm=~56@ey8%Fe{YK8?JHJ;~^V=z`AdJiS2AQ{zohnC*Io1GcBe$wwG= z@f{)!ET1H*nFMHBzETuuw3}fk=g|t8n3EAlNC5;qy+FiMt7`S(C#1mRPaHnUn{K{4 z7a0iM^=LN!(1xCbAr@~(3|z0vGTwe@RxZtY68*3f$F2--FtQqyswcIykx$}oC14g^ z^afX5w6Z~mDZ|4CQDNqpsj5s_KC}d;ELCLyxX$4Rl|U;Nt1>&U_<-xJ!hmF`3y>U} zIUZaCw^*!d+>}R!#IvVbRcLl?0myE-D(&!vVbP?3?W&4%cSUg8oWKG>YXPENc$0?%88E71-D6E#wqc2na#5a!;ROD1_vNRsiM zCz~(y^T$xmUed*~1GM*ajSiXbdJQr3=kEw!gUVQyP%vV>booG(u z$!P#vs*_8IFc8!5nmfdqJ>dz+o*J-mSw`5D+J^1|@O1;=<2NfZ>#=(8WMA zuCFjq%1oQMRF5SB=IZtVx?AQkIsK1Xddv`474(!XfKW+KkSl3n@rkpED8Av4j^U{X za|h+(l`hIb2mY!PD60zAh49yo{^5u--}t;%g&pn`AKe4HSY*5~dvIk6M7PYDQrNL- ziTH?ZzcQ3n2dIU=MgUy?gj>lI7dw~D;t?{v^Tx^@fbZ`JLjDGLygUaCpiz9R5A0&G rs+^B6%7G+aY0$BDSRkaY?*M^5SjJ(2+%Fye4iGtxcxnIt@ArQJQ#4Z( diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d726ae7 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module himbot + +go 1.21.5 + +require github.com/diamondburned/arikawa/v3 v3.3.4 + +require ( + github.com/gorilla/schema v1.2.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/joho/godotenv v1.5.1 + github.com/sashabaranov/go-openai v1.17.9 + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..faeba57 --- /dev/null +++ b/go.sum @@ -0,0 +1,40 @@ +github.com/diamondburned/arikawa/v3 v3.3.4 h1:UXOjM7PRlWLJ8kVAydX/VetqV7W4/d4xU92JRy3SpU4= +github.com/diamondburned/arikawa/v3 v3.3.4/go.mod h1:5KMSeB9R2Kzi6K4EcqMz7mwAFpAi5jglX/Veq0+MPOo= +github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= +github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/sashabaranov/go-openai v1.17.9 h1:QEoBiGKWW68W79YIfXWEFZ7l5cEgZBV4/Ow3uy+5hNY= +github.com/sashabaranov/go-openai v1.17.9/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go new file mode 100644 index 0000000..24a02eb --- /dev/null +++ b/main.go @@ -0,0 +1,174 @@ +package main + +import ( + "context" + "fmt" + "log" + "math/rand" + "os" + "os/signal" + "time" + + "github.com/diamondburned/arikawa/v3/api" + "github.com/diamondburned/arikawa/v3/api/cmdroute" + "github.com/diamondburned/arikawa/v3/discord" + "github.com/diamondburned/arikawa/v3/gateway" + "github.com/diamondburned/arikawa/v3/state" + "github.com/diamondburned/arikawa/v3/utils/json/option" + "github.com/joho/godotenv" + openai "github.com/sashabaranov/go-openai" +) + +var commands = []api.CreateCommandData{ + { + Name: "ping", + Description: "ping pong!", + }, + { + Name: "echo", + Description: "echo back the argument", + Options: []discord.CommandOption{ + &discord.StringOption{ + OptionName: "argument", + Description: "what's echoed back", + Required: true, + }, + }, + }, + { + Name: "thonk", + Description: "biiiig thonk", + }, + { + Name: "ask", + Description: "Ask Himbot!", + Options: []discord.CommandOption{ + &discord.StringOption{ + OptionName: "argument", + Description: "the prompt", + Required: true, + }, + }, + }, +} + +func main() { + godotenv.Load(".env") + + token := os.Getenv("DISCORD_TOKEN") + if token == "" { + log.Fatalln("No $DISCORD_TOKEN given.") + } + + h := newHandler(state.New("Bot " + token)) + h.s.AddInteractionHandler(h) + h.s.AddIntents(gateway.IntentGuilds) + h.s.AddHandler(func(*gateway.ReadyEvent) { + me, _ := h.s.Me() + log.Println("connected to the gateway as", me.Tag()) + }) + + if err := cmdroute.OverwriteCommands(h.s, commands); err != nil { + log.Fatalln("cannot update commands:", err) + } + + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + + if err := h.s.Connect(ctx); err != nil { + log.Fatalln("cannot connect:", err) + } +} + +type handler struct { + *cmdroute.Router + s *state.State +} + +func newHandler(s *state.State) *handler { + h := &handler{s: s} + + h.Router = cmdroute.NewRouter() + // Automatically defer handles if they're slow. + h.Use(cmdroute.Deferrable(s, cmdroute.DeferOpts{})) + h.AddFunc("ping", h.cmdPing) + h.AddFunc("echo", h.cmdEcho) + h.AddFunc("thonk", h.cmdThonk) + h.AddFunc("ask", h.cmdAsk) + + return h +} + +func (h *handler) cmdPing(ctx context.Context, cmd cmdroute.CommandData) *api.InteractionResponseData { + return &api.InteractionResponseData{ + Content: option.NewNullableString("Pong!"), + } +} + +func (h *handler) cmdEcho(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData { + var options struct { + Arg string `discord:"argument"` + } + + if err := data.Options.Unmarshal(&options); err != nil { + return errorResponse(err) + } + + return &api.InteractionResponseData{ + Content: option.NewNullableString(options.Arg), + AllowedMentions: &api.AllowedMentions{}, // don't mention anyone + } +} + +func (h *handler) cmdAsk(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData { + var options struct { + Arg string `discord:"argument"` + } + + if err := data.Options.Unmarshal(&options); err != nil { + return errorResponse(err) + } + + client := openai.NewClient(os.Getenv("OPENAI_API_KEY")) + + resp, err := client.CreateChatCompletion( + context.Background(), + openai.ChatCompletionRequest{ + Model: openai.GPT4TurboPreview, + Messages: []openai.ChatCompletionMessage{ + { + Role: openai.ChatMessageRoleUser, + Content: options.Arg, + }, + }, + }, + ) + + if err != nil { + fmt.Printf("ChatCompletion error: %v\n", err) + return &api.InteractionResponseData{ + Content: option.NewNullableString("ChatCompletion Error!"), + AllowedMentions: &api.AllowedMentions{}, // don't mention anyone + } + } + + return &api.InteractionResponseData{ + Content: option.NewNullableString(resp.Choices[0].Message.Content), + AllowedMentions: &api.AllowedMentions{}, // don't mention anyone + } +} + +func (h *handler) cmdThonk(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData { + time.Sleep(time.Duration(3+rand.Intn(5)) * time.Second) + return &api.InteractionResponseData{ + Content: option.NewNullableString("https://tenor.com/view/thonk-thinking-sun-thonk-sun-thinking-sun-gif-14999983"), + } +} + +func errorResponse(err error) *api.InteractionResponseData { + return &api.InteractionResponseData{ + Content: option.NewNullableString("**Error:** " + err.Error()), + Flags: discord.EphemeralMessage, + AllowedMentions: &api.AllowedMentions{ /* none */ }, + } +} diff --git a/package.json b/package.json deleted file mode 100644 index c85c057..0000000 --- a/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "himbot", - "version": "4.2.0", - "engines": { - "node": ">=18.12.1" - }, - "main": "dist/index.js", - "author": "@sapphire", - "license": "UNLICENSE", - "type": "commonjs", - "dependencies": { - "@sapphire/decorators": "^6.0.3", - "@sapphire/discord.js-utilities": "7.1.4", - "@sapphire/framework": "^5.0.4", - "@sapphire/plugin-logger": "^4.0.1", - "@sapphire/utilities": "^3.14.0", - "@skyra/env-utilities": "^1.2.2", - "colorette": "^2.0.20", - "discord.js": "^14.14.1", - "openai": "^4.20.1", - "replicate": "^0.25.0" - }, - "devDependencies": { - "@flydotio/dockerfile": "0.5.0", - "@sapphire/cli": "^1.9.1", - "@sapphire/prettier-config": "^2.0.0", - "@sapphire/ts-config": "^5.0.0", - "@types/node": "^20.10.4", - "@types/ws": "^8.5.10", - "prettier": "^3.1.1", - "tsc-watch": "^6.0.4", - "typescript": "^5.3.3" - }, - "scripts": { - "sapphire": "sapphire", - "generate": "sapphire generate", - "watch": "tsc -w", - "start": "bun run src/index.ts", - "dev": "bun run src/index.ts", - "watch:start": "tsc-watch --onSuccess \"npm run start\"", - "format": "prettier --write \"src/\"" - }, - "prettier": "@sapphire/prettier-config" -} diff --git a/src/commands/ask.ts b/src/commands/ask.ts deleted file mode 100644 index 19fb2b7..0000000 --- a/src/commands/ask.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Command, container } from '@sapphire/framework'; -import { AttachmentBuilder, blockQuote, codeBlock } from 'discord.js'; -import OpenAI from 'openai'; - -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY -}); - -export class AskCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'You can ACTUALLY ask Himbot something! So cool!', - options: ['prompt'] - }); - } - - public override registerApplicationCommands(registry: Command.Registry) { - registry.registerChatInputCommand((builder) => - builder - .setName(this.name) - .setDescription(this.description) - .addStringOption((option) => - option.setName('prompt').setDescription('You can ACTUALLY ask Himbot something! So cool!').setRequired(true) - ) - ); - } - - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const prompt = interaction.options.getString('prompt'); - - await interaction.reply({ content: '🤔 Thinking... 🤔', fetchReply: true }); - - const chatCompletion = await openai.chat.completions.create({ - model: 'gpt-4-1106-preview', - messages: [ - { - role: 'user', - content: prompt - } - ] - }); - - const content = blockQuote(`> ${prompt}\n${codeBlock(`${chatCompletion.choices[0].message?.content}`)}`); - - const messageAttachment: AttachmentBuilder[] = []; - - if (content.length > 2000) { - messageAttachment.push( - new AttachmentBuilder(Buffer.from(`> ${prompt}\n${`${chatCompletion.choices[0].message?.content}`}`, 'utf-8'), { - name: 'response.txt', - description: "Himbot's Response" - }) - ); - } - - return interaction.editReply({ - content: - content.length < 2000 - ? content - : `Discord only allows messages with 2000 characters or less. Please see your response in the attached file!`, - files: messageAttachment - }); - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'ask', - piece: AskCommand -}); diff --git a/src/commands/borf.ts b/src/commands/borf.ts deleted file mode 100644 index c97705f..0000000 --- a/src/commands/borf.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Command, container } from '@sapphire/framework'; - -export class BorfCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'Borf! Borf!' - }); - } - - // Register Chat Input and Context Menu command - public override registerApplicationCommands(registry: Command.Registry) { - // Register Chat Input command - registry.registerChatInputCommand({ - name: this.name, - description: this.description - }); - } - - // Chat Input (slash) command - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const dogResponse = await fetch('https://dog.ceo/api/breeds/image/random'); - const dogData = (await dogResponse.json()) as { message: string; status: string }; - - await interaction.reply({ - content: dogData.status === 'success' ? dogData.message : 'Error: I had troubles fetching perfect puppies for you... :(', - fetchReply: true - }); - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'borf', - piece: BorfCommand -}); diff --git a/src/commands/hdpic.ts b/src/commands/hdpic.ts deleted file mode 100644 index 7fa7e79..0000000 --- a/src/commands/hdpic.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { BucketScope, Command, container } from '@sapphire/framework'; -import { AttachmentBuilder } from 'discord.js'; -import OpenAI from 'openai'; - -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY -}); - -export class HDPicCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'Generate an image using OpenAI! Cooldown of 10 Minutes due to cost!', - options: ['prompt'], - cooldownDelay: 480_000, - cooldownLimit: 1, - cooldownFilteredUsers: ['83679718401904640'], - cooldownScope: BucketScope.User - }); - } - - // Register Chat Input and Context Menu command - public override registerApplicationCommands(registry: Command.Registry) { - registry.registerChatInputCommand((builder) => - builder - .setName(this.name) - .setDescription(this.description) - .addStringOption((option) => - option.setName('prompt').setDescription('The prompt you will use to generate an image!').setRequired(true) - ) - ); - } - - // Chat Input (slash) command - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const prompt = interaction.options.getString('prompt') || ''; - - await interaction.reply({ content: '🤔 Thinking... 🤔', fetchReply: true }); - - try { - const response = await openai.images.generate({ - model: 'dall-e-3', - prompt, - n: 1, - size: '1024x1024', - quality: 'standard' - }); - - const imageUrl = response.data[0].url || ''; - // get an array buffer - const imageBuffer = await fetch(imageUrl).then((r) => r.arrayBuffer()); - - const imageAttachment: AttachmentBuilder[] = []; - - imageAttachment.push( - new AttachmentBuilder(Buffer.from(new Uint8Array(imageBuffer)), { - name: 'himbot_response.jpg', - description: `An image generated by Himbot using the prompt: ${prompt}` - }) - ); - - const content = `Prompt: ${prompt}:`; - - return interaction.editReply({ - content, - files: imageAttachment - }); - } catch (error) { - const content = "Sorry, I can't complete the prompt for: " + prompt + '\n' + error; - - return interaction.editReply({ content }); - } - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'hdpic', - piece: HDPicCommand -}); diff --git a/src/commands/hdtts.ts b/src/commands/hdtts.ts deleted file mode 100644 index 10cc4f7..0000000 --- a/src/commands/hdtts.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { BucketScope, Command, container } from '@sapphire/framework'; -import { AttachmentBuilder, MessageFlags } from 'discord.js'; -import OpenAI from 'openai'; - -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY -}); - -export class HDTTSCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'Generate "HD" TTS every 2 minutes!', - options: ['prompt'], - cooldownDelay: 200_000, - cooldownLimit: 1, - // Yes... I did hardcode myself. - cooldownFilteredUsers: ['83679718401904640'], - cooldownScope: BucketScope.User - }); - } - - // Register Chat Input and Context Menu command - public override registerApplicationCommands(registry: Command.Registry) { - registry.registerChatInputCommand((builder) => - builder - .setName(this.name) - .setDescription(this.description) - .addStringOption((option) => option.setName('prompt').setDescription('The prompt you will use to generate audio!').setRequired(true)) - ); - } - - // Chat Input (slash) command - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const prompt = interaction.options.getString('prompt') || 'NOTHING'; - - await interaction.reply({ content: '🤔 Thinking... 🤔', fetchReply: true }); - try { - enum voice { - alloy = 'alloy', - echo = 'echo', - fable = 'fable', - onyx = 'onyx', - nova = 'nova', - shimmer = 'shimmer' - } - - const voices = [voice.alloy, voice.echo, voice.fable, voice.onyx, voice.nova, voice.shimmer]; - const mp3 = await openai.audio.speech.create({ - model: 'tts-1-hd', - voice: voices[Math.floor(Math.random() * voices.length)], - input: prompt - }); - const mp3Buffer = Buffer.from(await mp3.arrayBuffer()); - - const mp3Attachment: AttachmentBuilder[] = []; - - mp3Attachment.push( - new AttachmentBuilder(Buffer.from(new Uint8Array(mp3Buffer)), { - name: 'himbot_response.mp3', - description: `An TTS message generated by Himbot using the prompt: ${prompt}` - }) - ); - - const content = `Prompt: ${prompt}:`; - - return interaction.editReply({ - content, - files: mp3Attachment, - options: { - flags: MessageFlags.IsVoiceMessage.valueOf() - } - }); - } catch (error) { - const content = "Sorry, I can't complete the prompt for: " + prompt + '\n' + error; - - return interaction.editReply({ - content - }); - } - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'hdtts', - piece: HDTTSCommand -}); diff --git a/src/commands/hs.ts b/src/commands/hs.ts deleted file mode 100644 index 4c3cc8a..0000000 --- a/src/commands/hs.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Command, container } from '@sapphire/framework'; - -export class HighSchoolCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'This command was your nickname in highschool!', - options: ['nickname'] - }); - } - - // Register Chat Input and Context Menu command - public override registerApplicationCommands(registry: Command.Registry) { - registry.registerChatInputCommand((builder) => - builder // - .setName(this.name) - .setDescription(this.description) - .addStringOption((option) => option.setName('nickname').setDescription('Your nickname in highschool.').setRequired(true)) - ); - } - - // Chat Input (slash) command - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const nickname = interaction.options.getString('nickname') || 'NOTHING'; - await interaction.reply({ - content: `${nickname} was ${interaction.user.username}'s nickname in highschool!`, - fetchReply: true - }); - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'hs', - piece: HighSchoolCommand -}); diff --git a/src/commands/pic.ts b/src/commands/pic.ts deleted file mode 100644 index dacd611..0000000 --- a/src/commands/pic.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BucketScope, Command, container } from '@sapphire/framework'; -import { AttachmentBuilder } from 'discord.js'; -import Replicate from 'replicate'; - -const replicate = new Replicate({ - auth: process.env.REPLICATE_API_TOKEN -}); - -export class PicCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'Generate an image using Stability AI! Cooldown 1 Minute to prevent spam!', - options: ['prompt'], - cooldownDelay: 100_000, - cooldownLimit: 1, - // Yes... I did hardcode myself. - cooldownFilteredUsers: ['83679718401904640'], - cooldownScope: BucketScope.User - }); - } - - // Register Chat Input and Context Menu command - public override registerApplicationCommands(registry: Command.Registry) { - registry.registerChatInputCommand((builder) => - builder - .setName(this.name) - .setDescription(this.description) - .addStringOption((option) => - option.setName('prompt').setDescription('The prompt you will use to generate an image!').setRequired(true) - ) - ); - } - - // Chat Input (slash) command - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const prompt = interaction.options.getString('prompt') || 'NOTHING'; - - await interaction.reply({ content: '🤔 Thinking... 🤔', fetchReply: true }); - - let result = (await replicate.run('stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b', { - input: { - width: 1024, - height: 1024, - prompt, - negative_prompt: - 'out of frame, lowres, text, error, cropped, worst quality, low quality, jpeg artifacts, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, malformed limbs, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck, username, watermark, signature.', - disable_safety_checker: true, - refine: 'expert_ensemble_refiner', - scheduler: 'KarrasDPM', - num_outputs: 1, - guidance_scale: 7.5, - high_noise_frac: 0.8, - prompt_strength: 0.8, - num_inference_steps: 50 - } - })) as string[]; - - if (result.length <= 0) { - const content = `Sorry, I can't complete the prompt for: ${prompt}`; - - return interaction.editReply({ - content: content - }); - } else { - const imageUrl = result[0] || ''; - // get an array buffer - const imageBuffer = await fetch(imageUrl).then((r) => r.arrayBuffer()); - - const imageAttachment: AttachmentBuilder[] = []; - - imageAttachment.push( - new AttachmentBuilder(Buffer.from(new Uint8Array(imageBuffer)), { - name: 'himbot_response.jpg', - description: `An image generated by Himbot using the prompt: ${prompt}` - }) - ); - - const content = `Prompt: ${prompt}`; - - return interaction.editReply({ - content, - files: imageAttachment - }); - } - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'pic', - piece: PicCommand -}); diff --git a/src/commands/title.ts b/src/commands/title.ts deleted file mode 100644 index c344dab..0000000 --- a/src/commands/title.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Command, container } from '@sapphire/framework'; - -export class TitleCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'This command is the title of your sextape.', - options: ['title'] - }); - } - - // Register Chat Input and Context Menu command - public override registerApplicationCommands(registry: Command.Registry) { - registry.registerChatInputCommand((builder) => - builder // - .setName(this.name) - .setDescription(this.description) - .addStringOption((option) => option.setName('title').setDescription('The title of your sextape.').setRequired(true)) - ); - } - - // Chat Input (slash) command - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const title = interaction.options.getString('title') || 'NOTHING'; - - await interaction.reply({ - content: `${title}: Title of ${interaction.user.username}'s sex tape!`, - fetchReply: true - }); - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'title', - piece: TitleCommand -}); diff --git a/src/commands/tts.ts b/src/commands/tts.ts deleted file mode 100644 index 8968fd2..0000000 --- a/src/commands/tts.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { BucketScope, Command, container } from '@sapphire/framework'; -import { AttachmentBuilder, MessageFlags } from 'discord.js'; -import OpenAI from 'openai'; - -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY -}); - -export class TTSCommand extends Command { - public constructor(context: Command.LoaderContext) { - super(context, { - description: 'Generate TTS every minute!', - options: ['prompt'], - cooldownDelay: 100_000, - cooldownLimit: 1, - // Yes... I did hardcode myself. - cooldownFilteredUsers: ['83679718401904640'], - cooldownScope: BucketScope.User - }); - } - - // Register Chat Input and Context Menu command - public override registerApplicationCommands(registry: Command.Registry) { - registry.registerChatInputCommand((builder) => - builder - .setName(this.name) - .setDescription(this.description) - .addStringOption((option) => option.setName('prompt').setDescription('The prompt you will use to generate audio!').setRequired(true)) - ); - } - - // Chat Input (slash) command - public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const prompt = interaction.options.getString('prompt') || 'NOTHING'; - - await interaction.reply({ content: '🤔 Thinking... 🤔', fetchReply: true }); - - try { - enum voice { - alloy = 'alloy', - echo = 'echo', - fable = 'fable', - onyx = 'onyx', - nova = 'nova', - shimmer = 'shimmer' - } - - const voices = [voice.alloy, voice.echo, voice.fable, voice.onyx, voice.nova, voice.shimmer]; - const mp3 = await openai.audio.speech.create({ - model: 'tts-1', - voice: voices[Math.floor(Math.random() * voices.length)], - input: prompt - }); - const mp3Buffer = Buffer.from(await mp3.arrayBuffer()); - - const mp3Attachment: AttachmentBuilder[] = []; - - mp3Attachment.push( - new AttachmentBuilder(Buffer.from(new Uint8Array(mp3Buffer)), { - name: 'himbot_response.mp3', - description: `An TTS message generated by Himbot using the prompt: ${prompt}` - }) - ); - - const content = `Prompt: ${prompt}:`; - - return interaction.editReply({ - content, - files: mp3Attachment, - options: { - flags: MessageFlags.IsVoiceMessage.valueOf() - } - }); - } catch (error) { - const content = "Sorry, I can't complete the prompt for: " + prompt + '\n' + error; - - return interaction.editReply({ - content - }); - } - } -} - -void container.stores.loadPiece({ - store: 'commands', - name: 'tts', - piece: TTSCommand -}); diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index d7c38da..0000000 --- a/src/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import './lib/setup'; -import './listeners/ready'; -import './commands/ask'; -import './commands/borf'; -import './commands/hdpic'; -import './commands/hdtts'; -import './commands/hs'; -import './commands/pic'; -import './commands/title'; -import './commands/tts'; -import './listeners/commands/chatInputCommands/chatInputCommandSuccess'; -import { LogLevel, SapphireClient, BucketScope } from '@sapphire/framework'; -import { ActivityType, GatewayIntentBits } from 'discord.js'; - -const client = new SapphireClient({ - defaultPrefix: '!', - presence: { - status: 'online', - activities: [ - { - name: 'idk', - type: ActivityType.Custom - } - ] - }, - caseInsensitiveCommands: true, - logger: { - level: LogLevel.Debug - }, - intents: [GatewayIntentBits.DirectMessages, GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds, GatewayIntentBits.MessageContent], - defaultCooldown: { - // 10s - delay: 10_000, - filteredCommands: ['support', 'ping', 'wryna'], - limit: 2, - // Yes... I did hardcode myself. - filteredUsers: ['83679718401904640'], - scope: BucketScope.User - }, - baseUserDirectory: null -}); - -const main = async () => { - try { - client.logger.info('Logging in'); - await client.login(); - client.logger.info('logged in'); - } catch (error) { - client.logger.fatal(error); - client.destroy(); - process.exit(1); - } -}; - -main(); diff --git a/src/lib/constants.ts b/src/lib/constants.ts deleted file mode 100644 index 97147f0..0000000 --- a/src/lib/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { join } from 'path'; - -export const rootDir = join(__dirname, '..', '..'); -export const srcDir = join(rootDir, 'src'); diff --git a/src/lib/setup.ts b/src/lib/setup.ts deleted file mode 100644 index d4124b3..0000000 --- a/src/lib/setup.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Unless explicitly defined, set NODE_ENV as development: -process.env.NODE_ENV ??= 'development'; - -import { ApplicationCommandRegistries, RegisterBehavior } from '@sapphire/framework'; -import '@sapphire/plugin-logger/register'; -import { setup } from '@skyra/env-utilities'; -import * as colorette from 'colorette'; -import { join } from 'node:path'; -import { srcDir } from './constants'; - -// Set default behavior to bulk overwrite -ApplicationCommandRegistries.setDefaultBehaviorWhenNotIdentical(RegisterBehavior.BulkOverwrite); - -// Read env var -setup({ path: join(srcDir, '.env') }); - -// Enable colorette -colorette.createColors({ useColor: true }); diff --git a/src/lib/utils.ts b/src/lib/utils.ts deleted file mode 100644 index eeba90c..0000000 --- a/src/lib/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { container, type ChatInputCommandSuccessPayload, type Command, type ContextMenuCommandSuccessPayload } from '@sapphire/framework'; -import { cyan } from 'colorette'; -import type { APIUser, Guild, User } from 'discord.js'; - -export function logSuccessCommand(payload: ContextMenuCommandSuccessPayload | ChatInputCommandSuccessPayload): void { - let successLoggerData: ReturnType; - - successLoggerData = getSuccessLoggerData(payload.interaction.guild, payload.interaction.user, payload.command); - - container.logger.debug(`${successLoggerData.shard} - ${successLoggerData.commandName} ${successLoggerData.author} ${successLoggerData.sentAt}`); -} - -export function getSuccessLoggerData(guild: Guild | null, user: User, command: Command) { - const shard = getShardInfo(guild?.shardId ?? 0); - const commandName = getCommandInfo(command); - const author = getAuthorInfo(user); - const sentAt = getGuildInfo(guild); - - return { shard, commandName, author, sentAt }; -} - -function getShardInfo(id: number) { - return `[${cyan(id.toString())}]`; -} - -function getCommandInfo(command: Command) { - return cyan(command.name); -} - -function getAuthorInfo(author: User | APIUser) { - return `${author.username}[${cyan(author.id)}]`; -} - -function getGuildInfo(guild: Guild | null) { - if (guild === null) return 'Direct Messages'; - return `${guild.name}[${cyan(guild.id)}]`; -} diff --git a/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts b/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts deleted file mode 100644 index 7c5f96a..0000000 --- a/src/listeners/commands/chatInputCommands/chatInputCommandDenied.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { ChatInputCommandDeniedPayload, Events } from '@sapphire/framework'; -import { Listener, UserError } from '@sapphire/framework'; - -export class UserEvent extends Listener { - public async run({ context, message: content }: UserError, { interaction }: ChatInputCommandDeniedPayload) { - // `context: { silent: true }` should make UserError silent: - // Use cases for this are for example permissions error when running the `eval` command. - if (Reflect.get(Object(context), 'silent')) return; - - if (interaction.deferred || interaction.replied) { - return interaction.editReply({ - content, - allowedMentions: { users: [interaction.user.id], roles: [] } - }); - } - - return interaction.reply({ - content, - allowedMentions: { users: [interaction.user.id], roles: [] }, - ephemeral: true - }); - } -} diff --git a/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts b/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts deleted file mode 100644 index 039cd9a..0000000 --- a/src/listeners/commands/chatInputCommands/chatInputCommandSuccess.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Listener, LogLevel, type ChatInputCommandSuccessPayload } from '@sapphire/framework'; -import type { Logger } from '@sapphire/plugin-logger'; -import { logSuccessCommand } from '../../../lib/utils'; - -export class UserListener extends Listener { - public run(payload: ChatInputCommandSuccessPayload) { - logSuccessCommand(payload); - } - - public onLoad() { - this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug; - return super.onLoad(); - } -} diff --git a/src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts b/src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts deleted file mode 100644 index 6ec16a7..0000000 --- a/src/listeners/commands/contextMenuCommands/contextMenuCommandDenied.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { ContextMenuCommandDeniedPayload, Events } from '@sapphire/framework'; -import { Listener, UserError } from '@sapphire/framework'; - -export class UserEvent extends Listener { - public async run({ context, message: content }: UserError, { interaction }: ContextMenuCommandDeniedPayload) { - // `context: { silent: true }` should make UserError silent: - // Use cases for this are for example permissions error when running the `eval` command. - if (Reflect.get(Object(context), 'silent')) return; - - if (interaction.deferred || interaction.replied) { - return interaction.editReply({ - content, - allowedMentions: { users: [interaction.user.id], roles: [] } - }); - } - - return interaction.reply({ - content, - allowedMentions: { users: [interaction.user.id], roles: [] }, - ephemeral: true - }); - } -} diff --git a/src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts b/src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts deleted file mode 100644 index 8253a74..0000000 --- a/src/listeners/commands/contextMenuCommands/contextMenuCommandSuccess.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Listener, LogLevel, type ContextMenuCommandSuccessPayload } from '@sapphire/framework'; -import type { Logger } from '@sapphire/plugin-logger'; -import { logSuccessCommand } from '../../../lib/utils'; - -export class UserListener extends Listener { - public run(payload: ContextMenuCommandSuccessPayload) { - logSuccessCommand(payload); - } - - public onLoad() { - this.enabled = (this.container.logger as Logger).level <= LogLevel.Debug; - return super.onLoad(); - } -} diff --git a/src/listeners/mentionPrefixOnly.ts b/src/listeners/mentionPrefixOnly.ts deleted file mode 100644 index 88974a6..0000000 --- a/src/listeners/mentionPrefixOnly.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Events } from '@sapphire/framework'; -import { Listener } from '@sapphire/framework'; -import type { Message } from 'discord.js'; - -export class UserEvent extends Listener { - public async run(message: Message) { - const prefix = this.container.client.options.defaultPrefix; - return message.channel.send(prefix ? `My prefix in this guild is: \`${prefix}\`` : 'Cannot find any Prefix for Message Commands.'); - } -} diff --git a/src/listeners/ready.ts b/src/listeners/ready.ts deleted file mode 100644 index f7ab85a..0000000 --- a/src/listeners/ready.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ApplyOptions } from '@sapphire/decorators'; -import { Listener, Store, container } from '@sapphire/framework'; -import { blue, gray, green, magenta, magentaBright, white, yellow } from 'colorette'; - -const dev = process.env.NODE_ENV !== 'production'; - -// @ts-ignore -@ApplyOptions({ once: true }) -export class UserEvent extends Listener { - private readonly style = dev ? yellow : blue; - - public run() { - this.printBanner(); - this.printStoreDebugInformation(); - } - - private printBanner() { - const success = green('+'); - - const llc = dev ? magentaBright : white; - const blc = dev ? magenta : blue; - - const line01 = llc(''); - const line02 = llc(''); - const line03 = llc(''); - - // Offset Pad - const pad = ' '.repeat(7); - - console.log( - String.raw` -${line01} ${pad}${blc('1.0.0')} -${line02} ${pad}[${success}] Gateway -${line03}${dev ? ` ${pad}${blc('<')}${llc('/')}${blc('>')} ${llc('DEVELOPMENT MODE')}` : ''} - `.trim() - ); - } - - private printStoreDebugInformation() { - const { client, logger } = this.container; - const stores = [...client.stores.values()]; - const last = stores.pop()!; - - for (const store of stores) logger.info(this.styleStore(store)); - logger.info(this.styleStore(last)); - } - - private styleStore(store: Store) { - return gray(`${'└─'} Loaded ${this.style(store.size.toString().padEnd(3, ' '))} ${store.name}.`); - } -} - -void container.stores.loadPiece({ - piece: UserEvent, - name: 'ready', - store: 'listeners' -}); diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index a7a61d0..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@sapphire/ts-config", - "compilerOptions": { - "ignoreDeprecations": "5.0", - "rootDir": "src", - "outDir": "dist", - "tsBuildInfoFile": "dist/.tsbuildinfo" - }, - "include": ["src"] -}