diff --git a/.gitignore b/.gitignore index 2828dcb..8533720 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /assets/images -test.png \ No newline at end of file +test.png +token* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 2b526ad..01d6887 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,20 +28,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "approx" @@ -54,9 +45,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" +checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" dependencies = [ "proc-macro2", "quote", @@ -144,9 +135,9 @@ checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cc" -version = "1.0.77" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" [[package]] name = "cfg-if" @@ -154,52 +145,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" -dependencies = [ - "iana-time-zone", - "num-integer", - "num-traits", - "serde", - "winapi", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "command_attr" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d999d4e7731150ee14aee8f619c7a9aa9a4385bca0606c4fa95aa2f36a05d9a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - [[package]] name = "cpufeatures" version = "0.2.5" @@ -277,50 +228,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cxx" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dashmap" version = "5.4.0" @@ -360,6 +267,40 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "exr" version = "1.5.2" @@ -575,9 +516,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] @@ -616,6 +557,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.23" @@ -642,9 +589,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", @@ -653,30 +600,6 @@ dependencies = [ "tokio-rustls", ] -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - [[package]] name = "idna" version = "0.3.0" @@ -716,11 +639,33 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "ipnet" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec947b7a4ce12e3b87e353abae7ce124d025b6c7d6c5aea5cc0bcf92e9510ded" +checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] [[package]] name = "itertools" @@ -733,9 +678,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jpeg-decoder" @@ -767,26 +712,17 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" -[[package]] -name = "levenshtein" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" - [[package]] name = "libc" -version = "0.2.138" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] -name = "link-cplusplus" -version = "1.0.7" +name = "linux-raw-sys" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "lock_api" @@ -900,9 +836,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ "hermit-abi", "libc", @@ -1070,18 +1006,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -1118,11 +1054,10 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" dependencies = [ - "crossbeam-deque", "either", "rayon-core", ] @@ -1221,6 +1156,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustix" +version = "0.36.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustls" version = "0.20.7" @@ -1244,9 +1193,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "scoped_threadpool" @@ -1260,12 +1209,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - [[package]] name = "sct" version = "0.7.0" @@ -1278,9 +1221,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.149" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] @@ -1297,9 +1240,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.149" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1308,9 +1251,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -1341,12 +1284,9 @@ dependencies = [ "bitflags", "bytes", "cfg-if", - "chrono", - "command_attr", "dashmap", "flate2", "futures", - "levenshtein", "mime", "mime_guess", "parking_lot", @@ -1355,13 +1295,11 @@ dependencies = [ "serde", "serde-value", "serde_json", - "static_assertions", "time", "tokio", "tracing", "typemap_rev", "url", - "uwl", ] [[package]] @@ -1421,17 +1359,11 @@ dependencies = [ "lock_api", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "syn" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -1449,18 +1381,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -1478,9 +1410,9 @@ dependencies = [ [[package]] name = "tiff" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17def29300a156c19ae30814710d9c63cd50288a49c6fd3a10ccfbe4cf886fd" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" dependencies = [ "flate2", "jpeg-decoder", @@ -1540,6 +1472,7 @@ dependencies = [ "libc", "memchr", "mio", + "num_cpus", "pin-project-lite", "socket2", "tokio-macros", @@ -1584,9 +1517,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" dependencies = [ "serde", ] @@ -1692,9 +1625,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -1705,12 +1638,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "untrusted" version = "0.7.1" @@ -1735,12 +1662,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "uwl" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4bf03e0ca70d626ecc4ba6b0763b934b6f2976e8c744088bb3c1d646fbb1ad0" - [[package]] name = "version_check" version = "0.9.4" @@ -1851,9 +1772,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki", ] @@ -1963,15 +1884,14 @@ dependencies = [ [[package]] name = "wordcloud-rs" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5515f5e7c0c62d324af1e1853a1e9427fffbcd4feecbfedc639c20abcf62d585" +checksum = "e77722d7da9f1c762ce9adcdca686e30d031854e0fa69c622f413328d998b32f" dependencies = [ "anyhow", "fontdue", "image", "itertools", - "lazy_static", "log", "palette", "rand", @@ -1983,6 +1903,8 @@ version = "0.1.0" dependencies = [ "anyhow", "bimap", + "dashmap", + "env_logger", "fontdue", "image", "itertools", @@ -1992,5 +1914,6 @@ dependencies = [ "rand", "regex", "serenity", + "tokio", "wordcloud-rs", ] diff --git a/Cargo.toml b/Cargo.toml index a57c390..aa62865 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,18 @@ edition = "2021" fontdue = "*" image = "*" palette = "*" -serenity = "*" +serenity = {version = "*", default-features = false, features = [ + "cache", "model", "builder", "client", "gateway", "http", + "utils", "rustls_backend" +]} anyhow = "*" itertools = "*" rand = "*" lazy_static = "*" log = "*" +env_logger = "*" regex = "*" wordcloud-rs = "*" -bimap = "*" \ No newline at end of file +bimap = "*" +tokio = { version = "*", features = ["macros", "rt-multi-thread"] } +dashmap = "*" \ No newline at end of file diff --git a/src/handle_events.rs b/src/handle_events.rs new file mode 100644 index 0000000..08358f1 --- /dev/null +++ b/src/handle_events.rs @@ -0,0 +1,49 @@ +use serenity::{ + model:: { + application::interaction::{ + Interaction, + }, + gateway::Ready, + guild::Guild, + }, + async_trait, + prelude::* +}; +use log::info; +use crate::handler_util::{response, is_writable}; +use crate::handler::Handler; + + +#[async_trait] +impl EventHandler for Handler { + async fn interaction_create(&self, ctx: Context, interaction: Interaction) { + match interaction { + Interaction::ApplicationCommand(command) => { + // only answer if the bot has access to the channel + if is_writable(&ctx, command.channel_id).await { + match command.data.name.as_str() { + "cloud" => self.cloud(ctx, command).await, + "emojis" => self.emojis(ctx, command).await, + "activity" => self.activity(ctx, command).await, + _ => {} + }; + } else { + response( + &ctx.http, + &command, + "Sorry, I only answer to commands in the channels that I can write to.", + ).await; + } + } + _ => {} + } + } + + async fn ready(&self, _ctx: Context, ready: Ready) { + info!(target: "Wordy", "{} is connected!", ready.user.name); + } + + async fn guild_create(&self, ctx: Context, guild: Guild, _is_new: bool) { + self.register_guild(ctx.http, guild).await; + } +} diff --git a/src/handler.rs b/src/handler.rs new file mode 100644 index 0000000..3c5670b --- /dev/null +++ b/src/handler.rs @@ -0,0 +1,125 @@ +use itertools::Itertools; +use log::{warn, info}; +use image::{write_buffer_with_format, ColorType, ImageOutputFormat}; +use std::{io::{Cursor, Seek, SeekFrom}, sync::Arc}; +use palette::rgb::Rgb; +use dashmap::DashMap; +use serenity::{ + http::Http, + model:: { + application::interaction::{ + application_command::ApplicationCommandInteraction, + InteractionResponseType::ChannelMessageWithSource, + }, + id::GuildId, prelude::{UserId, ChannelId, Guild} + }, + prelude::*, utils::Color +}; +use wordcloud_rs::{Token, WordCloud, Colors}; +use crate::idiom::Idioms; + +fn convert_color(color: Color) -> Rgb { + Rgb::new( + color.r() as f32/255., + color.g() as f32/255., + color.b() as f32/255. + ) +} + +pub struct Handler { + idioms: Arc>> +} + +impl Handler { + pub fn new() -> Self { + Self { + idioms: Arc::new(DashMap::new()) + } + } + + pub fn message(&self, guild_id: GuildId, channel_id: ChannelId, member_id: UserId, message: String) { + self.idioms.get_mut(&guild_id).unwrap().update(channel_id, member_id, message) + } + + fn to_wc_tokens(&self, tokens: Vec<(String, f32)>) -> Vec<(Token, f32)> { + // TODO: also convert :emojis: to images + tokens.into_iter().map(|(str, v)| (Token::Text(str), v)).collect_vec() + } + + pub async fn cloud(&self, ctx: Context, command: ApplicationCommandInteraction) { + if let Some(member) = &command.member { + let color = member.colour(&ctx.cache).unwrap_or(Color::from_rgb(255, 255, 255)); + if let Some(guild_id) = command.guild_id { + let member_id = member.user.id; + let tokens = self.idioms.get(&guild_id).unwrap().idiom(member_id); + let wc_tokens = self.to_wc_tokens(tokens); + let image = WordCloud::new() + .colors(Colors::DoubleSplitCompl(convert_color(color))).generate(wc_tokens); + let mut img_file = Cursor::new(Vec::new()); + write_buffer_with_format( + &mut img_file, + image.as_raw(), + image.width(), + image.height(), + ColorType::Rgba8, + ImageOutputFormat::Png, + ) + .unwrap(); + img_file.seek(SeekFrom::Start(0)).unwrap(); + let img_vec = img_file.into_inner(); + + if let Err(why) = command + .create_interaction_response(&ctx.http, |response| { + response + .kind(ChannelMessageWithSource) + .interaction_response_data( + |message| message.add_file(( + img_vec.as_slice(), + format!("WordCloud_{}.png", member.display_name()).as_str() + )) + ) + }) + .await + { + println!("{}", why); + }; + } else { + warn!(target: "Wordy", "/cloud: Couldn't get guild"); + } + } else { + warn!(target: "Wordy", "/cloud: Couldn't get member"); + } + } + + pub async fn emojis(&self, ctx: Context, command: ApplicationCommandInteraction) { + todo!() + } + + pub async fn activity(&self, ctx: Context, command: ApplicationCommandInteraction) { + todo!() + } + + pub async fn register_guild(&self, http: Arc, guild: Guild) { + if let Ok(channels) = guild.channels(&http).await { + if !self.idioms.contains_key(&guild.id) { + info!(target: "Wordy", "Registering {} (id {})", guild.name, guild.id); + self.idioms.insert(guild.id, Idioms::new()); + let http = Arc::clone(&http); + let idioms = Arc::clone(&self.idioms); + tokio::spawn(async move { + for (channel_id, channel) in channels { + if let Ok(messages) = channel.messages( + &http, |retriever| retriever.limit(1000) + ).await { + for message in messages { + idioms.get_mut(&guild.id).unwrap().update( + channel_id, message.author.id, String::new() + ); + } + } + } + }); + } + } + } +} diff --git a/src/handler_util.rs b/src/handler_util.rs new file mode 100644 index 0000000..dbcc733 --- /dev/null +++ b/src/handler_util.rs @@ -0,0 +1,39 @@ +use serenity::{ + http::Http, + model:: { + application::interaction::{ + application_command::ApplicationCommandInteraction, + InteractionResponseType::ChannelMessageWithSource, + }, + prelude::{ChannelId, Channel}, + }, + prelude::* +}; + +pub async fn is_writable(ctx: &Context, channel_id: ChannelId) -> bool { + if let Ok(Channel::Guild(channel)) = channel_id.to_channel(&ctx.http).await { + if let Ok(me) = ctx.http.get_current_user().await { + if let Ok(perms) = channel.permissions_for_user(&ctx.cache, me.id) { + return perms.send_messages(); + } + } + } + false +} + + +pub async fn response(http: &Http, command: &ApplicationCommandInteraction, msg: D) +where + D: ToString, +{ + if let Err(why) = command + .create_interaction_response(http, |response| { + response + .kind(ChannelMessageWithSource) + .interaction_response_data(|message| message.content(msg)) + }) + .await + { + println!("{}", why); + }; +} \ No newline at end of file diff --git a/src/idiom/idiom.rs b/src/idiom/idiom.rs index 420d5e7..3219a24 100644 --- a/src/idiom/idiom.rs +++ b/src/idiom/idiom.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::hash::Hash; use itertools::Itertools; use lazy_static::lazy_static; use regex::Regex; @@ -22,14 +23,14 @@ fn tokenize(text: String) -> Vec<(String, f32)> { counts.into_iter().map(|(k, v)| (k, v as f32)).collect() } -pub struct Idioms { - places: HashMap>, - people: HashMap>, +pub struct Idioms { + places: HashMap>, + people: HashMap>, tokens: BiMap, } -impl Idioms { +impl Idioms { pub fn new() -> Self { let mut tokens = BiMap::new(); // reserve slot 0 for empty string @@ -39,7 +40,7 @@ impl Idioms { } } - pub fn update(&mut self, place: String, person: String, message: String) { + pub fn update(&mut self, place: P, person: U, message: String) { let place_voc = self.places.entry(place).or_insert(TopFreqs::new()); let user_voc = self.people.entry(person).or_insert(TopFreqs::new()); let tokens = tokenize(message); @@ -58,7 +59,7 @@ impl Idioms { } } - pub fn idiom(&self, person: String) -> Vec<(String, f32)> { + pub fn idiom(&self, person: U) -> Vec<(String, f32)> { let res = match self.people.get(&person) { Some(voc) => voc.data.clone().into_iter() .filter(|(idx, _)| *idx != 0).collect_vec(), @@ -68,8 +69,4 @@ impl Idioms { .map(|(idx, v)| (self.tokens.get_by_right(&idx).unwrap().clone(), v)) .collect_vec() } - - pub fn people(&self) -> Vec<&String> { - self.people.keys().collect_vec() - } } \ No newline at end of file diff --git a/src/idiom/top_freqs.rs b/src/idiom/top_freqs.rs index f10f7c7..028c3d2 100644 --- a/src/idiom/top_freqs.rs +++ b/src/idiom/top_freqs.rs @@ -1,5 +1,5 @@ use itertools::enumerate; -const AGING: f32 = 0.9; +const AGING: f32 = 0.99; pub struct TopFreqs { pub data: [(T, f32); S], diff --git a/src/main.rs b/src/main.rs index 83525a5..5b93d56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,28 +1,61 @@ mod idiom; -use std::fs::File; -use std::io::{prelude::*, BufReader}; -use idiom::Idioms; -use itertools::Itertools; -use wordcloud_rs::*; +mod handler; +mod handler_util; +mod handle_events; +use handler::Handler; +use env_logger; +use std::fs::read_to_string; +use log::{warn, error, LevelFilter}; +use serenity::{ + http::Http, + model::gateway::GatewayIntents, + prelude::*, +}; +use std::env; -fn main() { - let mut idioms = Idioms::new(); - let file = File::open("assets/movie_lines.tsv").unwrap(); - let reader = BufReader::new(file); - for line_res in reader.lines() { - if let Ok(line) = line_res { - let record = line.split("\t").collect_vec(); - if record.len() == 3 { - idioms.update( - String::new(), record[1].to_string(), record[2].to_string() - ); - } + +fn get_token(name: &str) -> Option { + if let Ok(token) = env::var(name) { + Some(token) + } else { + warn!(target: "Wordy", "Couldn't find the 'WORDY_TOKEN' environment variable, using token.txt as fallback"); + if let Ok(content) = read_to_string("token.txt") { + Some(content) + } else { + warn!(target: "Wordy", "Couldn't access token.txt"); + None } } - let person = "SPIDER-MAN".to_string(); - let idiom = idioms.idiom(person); - let tokens = idiom.into_iter() - .map(|(token, v)| (Token::Text(token), v)).collect_vec(); - let img = WordCloud::new().generate(tokens); - img.save("test.png").unwrap(); +} + +#[tokio::main] +async fn main() { + env_logger::builder().filter_module("Wordy", LevelFilter::Info).init(); + // Configure the client with your Discord bot token in the environment. + let token = get_token("WORDY_TOKEN").unwrap(); + let http = Http::new(&token); + + // The Application Id is usually the Bot User Id. + let bot_id = match http.get_current_application_info().await { + Ok(info) => info.id, + Err(why) => panic!("Could not access application info: {:?}", why), + }; + // Build our client. + let mut client = Client::builder( + token, GatewayIntents::non_privileged() + | GatewayIntents::GUILD_MEMBERS + | GatewayIntents::GUILD_PRESENCES + ) + .event_handler(Handler::new()) + .application_id(bot_id.into()) + .await + .expect("Error creating client"); + + // Finally, start a single shard, and start listening to events. + // + // Shards will automatically attempt to reconnect, and will perform + // exponential backoff until it reconnects. + if let Err(why) = client.start().await { + error!(target: "Wordy", "Client error: {:?}", why); + } } diff --git a/test.png b/test.png index 3156cf1..9aae947 100644 Binary files a/test.png and b/test.png differ