From ca80846e0d407e2a24d7ae756f8a51ff275ab7ec Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Tue, 3 Jan 2023 20:06:56 -0700 Subject: [PATCH 1/3] Initial refactor of message handling + Split all message handling into message_router.rs + Added whisper command + Updated serenity version + Fmt, but clippy failing --- Cargo.lock | 568 +++++++++++++++++++---------------- Cargo.toml | 4 +- src/config.rs | 3 + src/discord/commands.rs | 96 ++++-- src/discord/event_handler.rs | 14 +- src/discord/helper.rs | 201 +------------ src/error.rs | 4 + src/game/message_router.rs | 227 ++++++++++++++ src/game/mod.rs | 10 +- src/main.rs | 4 +- src/messages/mod.rs | 4 +- 11 files changed, 637 insertions(+), 498 deletions(-) create mode 100644 src/game/message_router.rs diff --git a/Cargo.lock b/Cargo.lock index cde9552..53f2f94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.4.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] [[package]] name = "aho-corasick" @@ -45,18 +50,18 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.11.0" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7cc5408453d37e2b1c6f01d8078af1da58b6cfa6a80fa2ede3bd2b9a6ada9c4" +checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb" dependencies = [ "futures-io", "futures-util", "log", - "pin-project", + "pin-project-lite", "tokio", - "tokio-rustls 0.22.0", + "tokio-rustls", "tungstenite", - "webpki-roots 0.20.0", + "webpki-roots", ] [[package]] @@ -76,12 +81,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.0" @@ -108,9 +107,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array 0.14.5", ] @@ -151,12 +150,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - [[package]] name = "bytes" version = "1.1.0" @@ -185,7 +178,7 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time", + "time 0.1.44", "winapi", ] @@ -228,9 +221,9 @@ dependencies = [ [[package]] name = "command_attr" -version = "0.3.8" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e74a021f9d0b577821bf9f5b1a2be88677e95ee05713e5d44fb4236cfa1744c" +checksum = "4d999d4e7731150ee14aee8f619c7a9aa9a4385bca0606c4fa95aa2f36a05d9a" dependencies = [ "proc-macro2", "quote", @@ -239,9 +232,9 @@ dependencies = [ [[package]] name = "config" -version = "0.12.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ad70579325f1a38ea4c13412b82241c5900700a69785d73e2736bd65a33f86" +checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" dependencies = [ "async-trait", "json5", @@ -300,6 +293,30 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.5", + "typenum", +] + +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown 0.12.3", + "lock_api", + "once_cell", + "parking_lot_core", + "serde", +] + [[package]] name = "deunicode" version = "0.4.3" @@ -317,21 +334,19 @@ dependencies = [ [[package]] name = "digest" -version = "0.9.0" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "generic-array 0.14.5", + "block-buffer 0.10.3", + "crypto-common", ] [[package]] name = "dlv-list" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" -dependencies = [ - "rand 0.8.5", -] +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] name = "encoding_rs" @@ -508,17 +523,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.5" @@ -527,7 +531,7 @@ checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ "cfg-if", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -560,7 +564,7 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" dependencies = [ - "bytes 1.1.0", + "bytes", "fnv", "futures-core", "futures-sink", @@ -573,21 +577,21 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + [[package]] name = "heck" version = "0.3.3" @@ -612,7 +616,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" dependencies = [ - "bytes 1.1.0", + "bytes", "fnv", "itoa", ] @@ -623,7 +627,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ - "bytes 1.1.0", + "bytes", "http", "pin-project-lite", ] @@ -652,7 +656,7 @@ version = "0.14.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd" dependencies = [ - "bytes 1.1.0", + "bytes", "futures-channel", "futures-core", "futures-util", @@ -678,9 +682,9 @@ checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" dependencies = [ "http", "hyper", - "rustls 0.20.4", + "rustls", "tokio", - "tokio-rustls 0.23.2", + "tokio-rustls", ] [[package]] @@ -689,7 +693,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.1.0", + "bytes", "hyper", "native-tls", "tokio", @@ -735,15 +739,6 @@ dependencies = [ "hashbrown 0.11.2", ] -[[package]] -name = "input_buffer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" -dependencies = [ - "bytes 0.5.6", -] - [[package]] name = "instant" version = "0.1.12" @@ -791,6 +786,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[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.119" @@ -803,6 +804,16 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -953,9 +964,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -963,12 +974,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "openssl" version = "0.10.38" @@ -1003,13 +1008,45 @@ dependencies = [ ] [[package]] -name = "ordered-multimap" -version = "0.3.1" +name = "ordered-float" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ "dlv-list", - "hashbrown 0.9.1", + "hashbrown 0.12.3", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", ] [[package]] @@ -1102,7 +1139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared", - "rand 0.8.5", + "rand", ] [[package]] @@ -1115,26 +1152,6 @@ dependencies = [ "uncased", ] -[[package]] -name = "pin-project" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.8" @@ -1185,11 +1202,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1201,19 +1218,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -1221,18 +1225,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1242,16 +1236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -1260,16 +1245,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.5", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -1313,8 +1289,8 @@ version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" dependencies = [ - "base64 0.13.0", - "bytes 1.1.0", + "base64", + "bytes", "encoding_rs", "futures-core", "futures-util", @@ -1333,20 +1309,20 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", - "rustls 0.20.4", + "rustls", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.2", + "tokio-rustls", "tokio-util", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.22.2", + "webpki-roots", "winreg", ] @@ -1371,34 +1347,21 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678" dependencies = [ - "base64 0.13.0", + "base64", "bitflags", "serde", ] [[package]] name = "rust-ini" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ "cfg-if", "ordered-multimap", ] -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.0", - "log", - "ring", - "sct 0.6.1", - "webpki 0.21.4", -] - [[package]] name = "rustls" version = "0.20.4" @@ -1407,8 +1370,8 @@ checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" dependencies = [ "log", "ring", - "sct 0.7.0", - "webpki 0.22.0", + "sct", + "webpki", ] [[package]] @@ -1417,7 +1380,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" dependencies = [ - "base64 0.13.0", + "base64", ] [[package]] @@ -1446,14 +1409,10 @@ dependencies = [ ] [[package]] -name = "sct" -version = "0.6.1" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sct" @@ -1490,18 +1449,28 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.136" +name = "serde-value" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1533,24 +1502,32 @@ dependencies = [ [[package]] name = "serenity" -version = "0.10.10" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde86919535c6047e055d512641c5241320c01cb8fee54f1e5ba77c939a0ec23" +checksum = "82fd5e7b5858ad96e99d440138f34f5b98e1b959ebcd3a1036203b30e78eb788" dependencies = [ "async-trait", "async-tungstenite", - "base64 0.13.0", + "base64", "bitflags", - "bytes 1.1.0", + "bytes", + "cfg-if", "chrono", "command_attr", + "dashmap", "flate2", "futures", + "levenshtein", + "mime", + "mime_guess", + "parking_lot", "percent-encoding", "reqwest", "serde", + "serde-value", "serde_json", "static_assertions", + "time 0.3.17", "tokio", "tracing", "typemap_rev", @@ -1567,20 +1544,18 @@ dependencies = [ "block-buffer 0.7.3", "digest 0.8.1", "fake-simd", - "opaque-debug 0.2.3", + "opaque-debug", ] [[package]] name = "sha-1" -version = "0.9.8" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest 0.10.6", ] [[package]] @@ -1604,6 +1579,12 @@ dependencies = [ "deunicode", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "socket2" version = "0.4.4" @@ -1658,13 +1639,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.86" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1695,7 +1676,7 @@ dependencies = [ "percent-encoding", "pest", "pest_derive", - "rand 0.8.5", + "rand", "regex", "serde", "serde_json", @@ -1712,6 +1693,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.4" @@ -1728,10 +1729,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi", "winapi", ] +[[package]] +name = "time" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.5.1" @@ -1753,7 +1781,7 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" dependencies = [ - "bytes 1.1.0", + "bytes", "libc", "memchr", "mio", @@ -1785,26 +1813,15 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls 0.19.1", - "tokio", - "webpki 0.21.4", -] - [[package]] name = "tokio-rustls" version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" dependencies = [ - "rustls 0.20.4", + "rustls", "tokio", - "webpki 0.22.0", + "webpki", ] [[package]] @@ -1813,7 +1830,7 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" dependencies = [ - "bytes 1.1.0", + "bytes", "futures-core", "futures-sink", "log", @@ -1877,21 +1894,23 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "tungstenite" -version = "0.11.1" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ - "base64 0.12.3", + "base64", "byteorder", - "bytes 0.5.6", + "bytes", "http", "httparse", - "input_buffer", "log", - "rand 0.7.3", - "sha-1 0.9.8", + "rand", + "rustls", + "sha-1 0.10.1", + "thiserror", "url", "utf-8", + "webpki", ] [[package]] @@ -1986,6 +2005,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -2007,12 +2032,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - [[package]] name = "untrusted" version = "0.7.1" @@ -2029,6 +2048,7 @@ dependencies = [ "idna", "matches", "percent-encoding", + "serde", ] [[package]] @@ -2082,12 +2102,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -2170,16 +2184,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki" version = "0.22.0" @@ -2190,22 +2194,13 @@ dependencies = [ "untrusted", ] -[[package]] -name = "webpki-roots" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" -dependencies = [ - "webpki 0.21.4", -] - [[package]] name = "webpki-roots" version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" dependencies = [ - "webpki 0.22.0", + "webpki", ] [[package]] @@ -2239,6 +2234,63 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "winreg" version = "0.10.1" @@ -2255,7 +2307,7 @@ dependencies = [ "chrono", "config", "futures", - "rand 0.8.5", + "rand", "regex", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index 9605263..f106dc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -config = "0.12.0" +config = "0.13.3" structopt = "0.3.26" chrono = {version="0.4.19", features=["serde"]} serde = "1.0.136" @@ -18,7 +18,7 @@ reqwest = "0.11.10" tera = "1.15.0" [dependencies.serenity] -version = "0.10.10" +version = "0.11.5" features = ["framework", "standard_framework"] [dependencies.tokio] diff --git a/src/config.rs b/src/config.rs index 814095a..0b23c5c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,10 +15,12 @@ pub struct Args { pub struct GameConfig { pub game_name: String, pub bot_name: String, + pub bot_profile_pic: String, pub vote_phase_name: String, pub enemy_phase_name: String, pub player_group_name: String, pub profile_album_hash: String, + pub whispers_allowed: bool, pub first_name: Vec, pub last_name: Vec, pub messages: MessageConfig, @@ -43,6 +45,7 @@ pub struct DiscordConfig { pub host_webhook_id: u64, pub vote_channel: u64, pub category: u64, + pub guild_id: u64, } #[derive(Debug, Deserialize, Serialize, Clone)] diff --git a/src/discord/commands.rs b/src/discord/commands.rs index 1d834af..b015090 100644 --- a/src/discord/commands.rs +++ b/src/discord/commands.rs @@ -10,20 +10,22 @@ use serenity::framework::standard::{ use serenity::framework::StandardFramework; use serenity::model::guild::Member; use serenity::model::id::ChannelId; -use serenity::model::prelude::{Message, UserId}; +use serenity::model::prelude::{GuildId, Message, UserId}; use serenity::prelude::Context; use serenity::utils::MessageBuilder; -use crate::discord::helper::{add_user_to_game, parse_duration_arg, send_msg_to_player_channels}; +use crate::discord::helper::{add_user_to_game, parse_duration_arg}; use crate::error::{Result, WoxlfError}; use crate::game::global_data::GlobalData; +use crate::game::message_router::{dispatch_message, MessageDest, MessageSource}; use crate::game::player_data::PlayerData; -use crate::game::MessageSource; use crate::game::Phase; use crate::messages::DiscordUser; #[group] -#[commands(start, say, end, broadcast, next_phase, kill, add_time, test_theme)] +#[commands( + start, say, end, broadcast, next_phase, kill, add_time, test_theme, whisper +)] struct Host; #[command] @@ -34,7 +36,7 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; let game_name = args.single::()?; @@ -136,7 +138,7 @@ async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -162,18 +164,18 @@ async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult { async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; - send_msg_to_player_channels( + dispatch_message( ctx, &guild, &mut global_data, MessageSource::Host, + MessageDest::Broadcast, args.rest(), None, - false, ) .await?; @@ -186,7 +188,7 @@ async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -194,14 +196,14 @@ async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult { .templates()? .build_announcement(&global_data, args.rest())?; - send_msg_to_player_channels( + dispatch_message( ctx, &guild, &mut global_data, MessageSource::Automated, + MessageDest::Broadcast, &msg, None, - true, ) .await?; @@ -214,7 +216,7 @@ async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -232,14 +234,14 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu .templates()? .build_announcement(&global_data, &broadcast)?; - send_msg_to_player_channels( + dispatch_message( ctx, &guild, &mut global_data, MessageSource::Automated, + MessageDest::Broadcast, &broadcast, None, - true, ) .await?; @@ -251,6 +253,7 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu )) .unwrap(); vote_channel + .id() .send_message(&ctx.http, |m| { m.content(format!( "**{} {} Votes:**", @@ -308,7 +311,7 @@ async fn kill(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -332,14 +335,14 @@ async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult .templates()? .build_announcement(&global_data, &broadcast)?; - send_msg_to_player_channels( + dispatch_message( ctx, &guild, &mut global_data, MessageSource::Automated, + MessageDest::Broadcast, &broadcast, None, - true, ) .await?; @@ -501,7 +504,7 @@ struct Player; async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -555,6 +558,7 @@ async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult { )?; vote_channel + .id() .send_message(&ctx.http, |m| m.content(vote_msg)) .await?; } else { @@ -626,7 +630,7 @@ async fn players(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { .push(alive_status); if msg.channel_id.0 == global_data.cfg.discord_config.host_channel { - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let member = guild.members.get(&UserId::from(player.discord_id)).unwrap(); msg_builder.push_line(format!(" ({})", member.display_name())); } else { @@ -639,6 +643,58 @@ async fn players(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { Ok(()) } +#[command] +#[aliases("pm", "w")] +#[description = "Send a private message to another player."] +async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { + if args.len() < 2 { + msg.reply(&ctx.http, "Need a recipient and message!") + .await?; + } else { + let data = ctx.data.write().await; + let global_data = data.get::().unwrap(); + let mut global_data = global_data.lock().await; + + let guild = GuildId::from(global_data.cfg.discord_config.guild_id) + .to_guild_cached(&ctx.cache) + .unwrap(); + + let target = args.single::()?; + let pm = args.rest(); + + let src_player = match global_data + .game_state()? + .get_player_from_discord_id(msg.author.id.0) + { + None => { + msg.reply(&ctx.http, "You are not in the game!").await?; + return Ok(()); + } + Some(player) => player, + }; + + if let Some(target_player) = global_data.game_state()?.get_player_by_codename(&target) { + if src_player.discord_id == target_player.discord_id { + msg.reply(&ctx.http, "You can't send messages to yourself!") + .await?; + return Ok(()); + } + + let msg_src = MessageSource::Player(Box::new(src_player.clone())); + let msg_dest = MessageDest::PlayerDm(Box::new(target_player.clone())); + dispatch_message(ctx, &guild, &mut global_data, msg_src, msg_dest, pm, None).await?; + } else { + msg.reply( + &ctx.http, + format!("Could not find a player with codename {}.", target), + ) + .await?; + } + } + + Ok(()) +} + #[help] #[individual_command_tip = "If you want more information about a specific command, just pass the command as argument."] #[command_not_found_text = "Could not find: `{}`."] diff --git a/src/discord/event_handler.rs b/src/discord/event_handler.rs index 3dcb986..e58357c 100644 --- a/src/discord/event_handler.rs +++ b/src/discord/event_handler.rs @@ -1,13 +1,13 @@ use serenity::async_trait; use serenity::client::{Context, EventHandler}; -use serenity::http::AttachmentType; use serenity::model::channel::Message; use serenity::model::gateway::Ready; +use serenity::model::prelude::AttachmentType; use serenity::utils::parse_emoji; -use crate::discord::helper::send_webhook_msg_to_player_channels; use crate::game::global_data::GlobalData; -use crate::game::MessageSource; +use crate::game::message_router::MessageSource; +use crate::game::message_router::{dispatch_message, MessageDest}; pub struct Handler {} @@ -43,7 +43,7 @@ impl EventHandler for Handler { return; } - let guild = msg.guild(&ctx.cache).await.unwrap(); + let guild = msg.guild(&ctx.cache).unwrap(); let user_msg = msg.content.clone(); let re = regex::Regex::new(r"").unwrap(); @@ -52,7 +52,6 @@ impl EventHandler for Handler { if let Some(emoji) = parse_emoji(&emoji_cap[0]) { if !msg .guild(&ctx.cache) - .await .unwrap() .emojis .contains_key(&emoji.id) @@ -66,16 +65,17 @@ impl EventHandler for Handler { let attachments: Vec = msg .attachments .iter() - .map(|a| AttachmentType::Image(&a.url)) + .map(|a| AttachmentType::Image((a.url).parse().unwrap())) .collect(); let msg_source = MessageSource::Player(Box::new(player_data.clone())); - send_webhook_msg_to_player_channels( + dispatch_message( &ctx, &guild, &mut global_data, msg_source, + MessageDest::Broadcast, &user_msg, Some(attachments), ) diff --git a/src/discord/helper.rs b/src/discord/helper.rs index ddd6c5b..fae5e2d 100644 --- a/src/discord/helper.rs +++ b/src/discord/helper.rs @@ -1,9 +1,8 @@ use serenity::client::Context; use serenity::framework::standard::Args; -use serenity::http::{AttachmentType, Http}; -use serenity::model::channel::{Message, PermissionOverwrite, PermissionOverwriteType}; +use serenity::model::channel::{PermissionOverwrite, PermissionOverwriteType}; use serenity::model::guild::{Guild, Member}; -use serenity::model::id::{ChannelId, UserId}; +use serenity::model::id::ChannelId; use serenity::model::Permissions; use crate::error; @@ -11,201 +10,7 @@ use crate::error::WoxlfError; use crate::game::game_state::PhaseDuration; use crate::game::global_data::GlobalData; use crate::game::player_data::PlayerData; -use crate::game::MessageSource; use crate::imgur::Image; -use serenity::prelude::SerenityError; - -fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool { - if let MessageSource::Player(source_player) = &msg_source { - if source_player.channel == player_data.channel { - return false; - } - } - true -} - -pub async fn send_msg_to_player_channels( - ctx: &Context, - guild: &Guild, - global_data: &mut GlobalData, - msg_source: MessageSource, - msg: &str, - attachment: Option>>, - pin: bool, -) -> error::Result<()> { - let msg_tasks = global_data - .game_state_mut()? - .player_data - .iter() - .filter(|player| filter_source_channel(player, &msg_source)) - .map(|player_data| { - let channel = guild - .channels - .get(&ChannelId::from(player_data.channel)) - .unwrap(); - - channel.send_message(&ctx.http, |m| { - m.content(&msg); - - if let Some(attachment) = attachment.clone() { - m.add_files(attachment); - } - - m - }) - }); - - let msgs: Result, SerenityError> = futures::future::join_all(msg_tasks) - .await - .into_iter() - .collect(); - - let msgs = msgs?; - - if pin { - let pin_tasks = msgs.iter().map(|msg| msg.pin(&ctx.http)); - - let pins: Result<(), SerenityError> = futures::future::join_all(pin_tasks) - .await - .into_iter() - .collect(); - - pins?; - } - - let host_channel = guild - .channels - .get(&ChannelId::from( - global_data.cfg.discord_config.host_channel, - )) - .unwrap(); - - let source = match msg_source { - MessageSource::Player(player_data) => { - let name = guild - .members - .get(&UserId::from(player_data.discord_id)) - .unwrap() - .display_name(); - - name.to_string() - } - MessageSource::Host => "Host".to_string(), - MessageSource::Automated => "Automated".to_string(), - }; - - host_channel - .send_message(&ctx.http, |m| { - m.content(format!("({}): {}", source, msg)); - if let Some(attachment) = attachment { - m.add_files(attachment); - } - - m - }) - .await?; - - Ok(()) -} - -pub async fn send_webhook_msg( - http: &Http, - webhook_id: u64, - username: &str, - profile_pic_url: Option, - msg: &str, - attachment: Option>>, -) -> error::Result<()> { - let webhook = http.get_webhook(webhook_id).await?; - - webhook - .execute(http, false, |w| { - w.content(&msg).username(username); - - if let Some(profile_pic_url) = profile_pic_url { - w.avatar_url(profile_pic_url); - } - - if let Some(attachment) = attachment.clone() { - w.add_files(attachment); - } - - w - }) - .await?; - - Ok(()) -} - -pub async fn send_webhook_msg_to_player_channels( - ctx: &Context, - guild: &Guild, - global_data: &mut GlobalData, - msg_source: MessageSource, - msg: &str, - attachment: Option>>, -) -> error::Result<()> { - let msg_username = match &msg_source { - MessageSource::Player(p) => p.codename.clone(), - MessageSource::Host => "Woxlf".to_string(), - MessageSource::Automated => "Woxlf System Message".to_string(), - }; - - let profile_pic = match &msg_source { - MessageSource::Player(p) => Some(p.profile_pic_url.clone()), - MessageSource::Host | MessageSource::Automated => None, - }; - - let msg_tasks = global_data - .game_state_mut()? - .player_data - .iter() - .filter(|player| filter_source_channel(player, &msg_source)) - .map(|player_data| { - send_webhook_msg( - &ctx.http, - player_data.channel_webhook_id, - &msg_username, - profile_pic.clone(), - msg, - attachment.clone(), - ) - }); - - let msgs: Result<(), WoxlfError> = futures::future::join_all(msg_tasks) - .await - .into_iter() - .collect(); - - msgs?; - - let source = match &msg_source { - MessageSource::Player(player_data) => { - let name = guild - .members - .get(&UserId::from(player_data.discord_id)) - .unwrap() - .display_name(); - - name.to_string() - } - MessageSource::Host => "Host".to_string(), - MessageSource::Automated => "Automated".to_string(), - }; - - let host_channel_username = format!("{} ({})", msg_username, source); - - send_webhook_msg( - &ctx.http, - global_data.cfg.discord_config.host_webhook_id, - &host_channel_username, - profile_pic, - msg, - attachment, - ) - .await?; - Ok(()) -} pub async fn add_user_to_game( ctx: &Context, @@ -237,7 +42,7 @@ pub async fn add_user_to_game( .await?; let allow = - Permissions::SEND_MESSAGES | Permissions::READ_MESSAGE_HISTORY | Permissions::READ_MESSAGES; + Permissions::SEND_MESSAGES | Permissions::READ_MESSAGE_HISTORY | Permissions::VIEW_CHANNEL; let overwrite = PermissionOverwrite { allow, diff --git a/src/error.rs b/src/error.rs index 056c82b..67de7ff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,6 +19,7 @@ pub enum WoxlfError { RanOutOfCodenames, TemplateError(tera::Error), RanOutOfProfilePics, + UnsupportedMsgMedium, } impl std::error::Error for WoxlfError {} @@ -41,6 +42,9 @@ impl Display for WoxlfError { } WoxlfError::TemplateError(e) => format!("Template error: {}", e), WoxlfError::RanOutOfProfilePics => "Ran out of user profile pics".to_string(), + WoxlfError::UnsupportedMsgMedium => { + "Tried to send a message over an unsupported medium".to_string() + } }; write!(f, "Woxlf Error: {}", msg) diff --git a/src/game/message_router.rs b/src/game/message_router.rs new file mode 100644 index 0000000..eebc477 --- /dev/null +++ b/src/game/message_router.rs @@ -0,0 +1,227 @@ +use crate::error; +use crate::game::global_data::GlobalData; +use crate::game::player_data::PlayerData; +use serenity::client::Context; +use serenity::http::Http; +use serenity::model::guild::Guild; +use serenity::model::id::UserId; +use serenity::model::prelude::AttachmentType; +use serenity::utils::MessageBuilder; + +#[derive(Debug, Clone)] +pub enum MessageSource { + Player(Box), + Host, + Automated, +} + +#[derive(Debug, Clone)] +pub enum MessageDest { + PlayerChannel(Box), + PlayerDm(Box), + Broadcast, +} + + +fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool { + if let MessageSource::Player(source_player) = &msg_source { + if source_player.channel == player_data.channel { + return false; + } + } + true +} + +async fn send_webhook_msg( + http: &Http, + webhook_id: u64, + username: &str, + profile_pic_url: Option, + msg: &str, + attachments: &Option>>, +) -> error::Result<()> { + let webhook = http.get_webhook(webhook_id).await?; + + webhook + .execute(http, false, move |w| { + w.content(&msg).username(username); + + if let Some(profile_pic_url) = profile_pic_url { + w.avatar_url(profile_pic_url); + } + + if let Some(attachments) = attachments { + w.add_files(attachments.clone()); + } + + w + }) + .await?; + + Ok(()) +} + +async fn send_private_message( + http: &Http, + src_username: &str, + dest: UserId, + msg: &str, + attachments: &Option>>, +) -> error::Result<()> { + let dest_user = dest.to_user(http).await?; + + let mut dm_message = MessageBuilder::new(); + + dm_message.push_bold_line_safe(format!("{} has sent you a DM:", src_username)); + dm_message.push(msg); + + dest_user + .dm(http, |msg| { + msg.content(dm_message); + + if let Some(attachments) = attachments { + msg.add_files(attachments.clone()); + } + + msg + }) + .await?; + + Ok(()) +} + +async fn send_to_host_channel( + http: &Http, + guild: &Guild, + global_data: &mut GlobalData, + msg_username: &str, + msg_source: MessageSource, + msg_dest: MessageDest, + profile_pic_url: Option, + msg: &str, + attachments: &Option>>, +) -> error::Result<()> { + let source = match &msg_source { + MessageSource::Player(player_data) => { + let name = guild + .members + .get(&UserId::from(player_data.discord_id)) + .unwrap() + .display_name(); + + name.to_string() + } + MessageSource::Host => "Host".to_string(), + MessageSource::Automated => "Automated".to_string(), + }; + + let dest = match &msg_dest { + MessageDest::PlayerChannel(p) | MessageDest::PlayerDm(p) => { + let name = guild + .members + .get(&UserId::from(p.discord_id)) + .unwrap() + .display_name(); + + format!(" to {} ({})", p.codename, name) + } + MessageDest::Broadcast => "".to_string(), + }; + + let host_channel_username = format!("{} ({}){}", msg_username, source, dest); + + send_webhook_msg( + http, + global_data.cfg.discord_config.host_webhook_id, + &host_channel_username, + profile_pic_url, + msg, + attachments, + ) + .await?; + Ok(()) +} + +pub async fn dispatch_message( + ctx: &Context, + guild: &Guild, + global_data: &mut GlobalData, + msg_source: MessageSource, + msg_dest: MessageDest, + msg: &str, + attachments: Option>>, +) -> error::Result<()> { + let msg_username = match &msg_source { + MessageSource::Player(p) => p.codename.clone(), + MessageSource::Host => "Woxlf".to_string(), + MessageSource::Automated => "Woxlf System Message".to_string(), + }; + + let profile_pic = match &msg_source { + MessageSource::Player(p) => Some(p.profile_pic_url.clone()), + MessageSource::Host | MessageSource::Automated => { + Some(global_data.game_cfg()?.bot_profile_pic.clone()) + } + }; + + let msg_tasks: Vec<&PlayerData> = global_data + .game_state_mut()? + .player_data + .iter() + .filter(|player| filter_source_channel(player, &msg_source)) + .collect(); + + for player_data in msg_tasks { + match &msg_dest { + MessageDest::PlayerChannel(dest_player) => { + if dest_player.discord_id == player_data.discord_id { + send_webhook_msg( + &ctx.http, + player_data.channel_webhook_id, + &msg_username, + profile_pic.clone(), + msg, + &attachments, + ) + .await?; + } + } + MessageDest::PlayerDm(dest_player) => { + send_private_message( + &ctx.http, + &msg_username, + UserId(dest_player.discord_id), + msg, + &attachments, + ) + .await? + } + MessageDest::Broadcast => { + send_webhook_msg( + &ctx.http, + player_data.channel_webhook_id, + &msg_username, + profile_pic.clone(), + msg, + &attachments, + ) + .await? + } + } + } + + send_to_host_channel( + &ctx.http, + guild, + global_data, + &msg_username, + msg_source, + msg_dest, + profile_pic, + msg, + &attachments, + ) + .await?; + + Ok(()) +} diff --git a/src/game/mod.rs b/src/game/mod.rs index 1cbc3c7..2092255 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,9 +1,8 @@ use serde::{Deserialize, Serialize}; -use crate::game::player_data::PlayerData; - pub mod game_state; pub mod global_data; +pub mod message_router; pub mod player_data; #[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, Copy)] @@ -17,10 +16,3 @@ impl Default for Phase { Self::Night } } - -#[derive(Debug, Clone)] -pub enum MessageSource { - Player(Box), - Host, - Automated, -} diff --git a/src/main.rs b/src/main.rs index a7640ad..0c749b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use serenity::client::bridge::gateway::GatewayIntents; use serenity::prelude::*; use structopt::StructOpt; @@ -32,10 +31,9 @@ async fn main() { .expect("Unable to open saved game state."); } - let mut client = Client::builder(&bot_cfg.discord_config.token) + let mut client = Client::builder(&bot_cfg.discord_config.token, GatewayIntents::all()) .event_handler(Handler {}) .framework(command_framework()) - .intents(GatewayIntents::all()) .type_map_insert::(Arc::new(Mutex::new(global_data))) .await .expect("Err creating client"); diff --git a/src/messages/mod.rs b/src/messages/mod.rs index dd2327d..a1898db 100644 --- a/src/messages/mod.rs +++ b/src/messages/mod.rs @@ -8,9 +8,11 @@ use serenity::prelude::Mentionable; use std::collections::HashMap; use tera::{Tera, Value}; +type TeraFnRet = Box) -> tera::Result + Send + Sync>; + fn time_to_discord_time( time_flag: &str, -) -> Box) -> tera::Result + Send + Sync> { +) -> TeraFnRet { let time_flag = time_flag.to_string(); Box::new( -- 2.41.0 From 90ec453b7095b8f3c8c90622ec5edc511ce4d8c7 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Thu, 5 Jan 2023 22:04:25 -0700 Subject: [PATCH 2/3] Message router refactor + Created the WoxlfMessage struct to streamline interface + Message tasks are now joined at once instead of sequentially + Clippy + fmt --- Cargo.lock | 1 + Cargo.toml | 1 + src/discord/commands.rs | 118 +++++++------- src/discord/event_handler.rs | 27 ++-- src/game/message_router.rs | 290 +++++++++++++++++++++++------------ 5 files changed, 258 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53f2f94..711dd40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2304,6 +2304,7 @@ dependencies = [ name = "woxlf" version = "0.2.0" dependencies = [ + "bitflags", "chrono", "config", "futures", diff --git a/Cargo.toml b/Cargo.toml index f106dc9..0057d95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ regex = "1.5.5" futures = "0.3.21" reqwest = "0.11.10" tera = "1.15.0" +bitflags = "1.3.2" [dependencies.serenity] version = "0.11.5" diff --git a/src/discord/commands.rs b/src/discord/commands.rs index b015090..702a672 100644 --- a/src/discord/commands.rs +++ b/src/discord/commands.rs @@ -10,14 +10,16 @@ use serenity::framework::standard::{ use serenity::framework::StandardFramework; use serenity::model::guild::Member; use serenity::model::id::ChannelId; -use serenity::model::prelude::{GuildId, Message, UserId}; +use serenity::model::prelude::{Message, UserId}; use serenity::prelude::Context; use serenity::utils::MessageBuilder; use crate::discord::helper::{add_user_to_game, parse_duration_arg}; use crate::error::{Result, WoxlfError}; use crate::game::global_data::GlobalData; -use crate::game::message_router::{dispatch_message, MessageDest, MessageSource}; +use crate::game::message_router::{ + dispatch_message, Median, MessageDest, MessageSource, WoxlfMessage, +}; use crate::game::player_data::PlayerData; use crate::game::Phase; use crate::messages::DiscordUser; @@ -161,23 +163,20 @@ async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult { #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] -async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult { - let mut data = ctx.data.write().await; - let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).unwrap(); +async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { + let data = ctx.data.read().await; + let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; + let global_data = global_data.lock().await; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Host, - MessageDest::Broadcast, - args.rest(), - None, - ) - .await?; + let msg = WoxlfMessage::default() + .source(MessageSource::Host) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(args.rest()) + .clone(); + + dispatch_message(ctx, &global_data, msg).await?; Ok(()) } @@ -185,27 +184,24 @@ async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult { #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] -async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult { - let mut data = ctx.data.write().await; - let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).unwrap(); +async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { + let data = ctx.data.read().await; + let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; + let global_data = global_data.lock().await; - let msg = global_data + let broadcast = global_data .templates()? .build_announcement(&global_data, args.rest())?; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Automated, - MessageDest::Broadcast, - &msg, - None, - ) - .await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Automated) + .dest(MessageDest::Broadcast) + .content(&broadcast) + .median(Median::Webhook) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; Ok(()) } @@ -234,16 +230,14 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu .templates()? .build_announcement(&global_data, &broadcast)?; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Automated, - MessageDest::Broadcast, - &broadcast, - None, - ) - .await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Automated) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(&broadcast) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; if global_data.game_state_mut()?.current_phase == Phase::Day { let vote_channel = guild @@ -311,7 +305,6 @@ async fn kill(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -335,16 +328,14 @@ async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult .templates()? .build_announcement(&global_data, &broadcast)?; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Automated, - MessageDest::Broadcast, - &broadcast, - None, - ) - .await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Automated) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(&broadcast) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; msg.reply(&ctx.http, "Phase has been updated") .await @@ -651,13 +642,9 @@ async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult msg.reply(&ctx.http, "Need a recipient and message!") .await?; } else { - let data = ctx.data.write().await; + let data = ctx.data.read().await; let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; - - let guild = GuildId::from(global_data.cfg.discord_config.guild_id) - .to_guild_cached(&ctx.cache) - .unwrap(); + let global_data = global_data.lock().await; let target = args.single::()?; let pm = args.rest(); @@ -680,9 +667,14 @@ async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult return Ok(()); } - let msg_src = MessageSource::Player(Box::new(src_player.clone())); - let msg_dest = MessageDest::PlayerDm(Box::new(target_player.clone())); - dispatch_message(ctx, &guild, &mut global_data, msg_src, msg_dest, pm, None).await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Player(Box::new(src_player.clone()))) + .dest(MessageDest::Player(Box::new(target_player.clone()))) + .median(Median::DirectMessage) + .content(pm) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; } else { msg.reply( &ctx.http, diff --git a/src/discord/event_handler.rs b/src/discord/event_handler.rs index e58357c..7f294f3 100644 --- a/src/discord/event_handler.rs +++ b/src/discord/event_handler.rs @@ -6,8 +6,8 @@ use serenity::model::prelude::AttachmentType; use serenity::utils::parse_emoji; use crate::game::global_data::GlobalData; -use crate::game::message_router::MessageSource; use crate::game::message_router::{dispatch_message, MessageDest}; +use crate::game::message_router::{Median, MessageSource, WoxlfMessage}; pub struct Handler {} @@ -26,7 +26,7 @@ impl EventHandler for Handler { let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; + let global_data = global_data.lock().await; if global_data.game_state.is_none() { // no game in progress @@ -43,7 +43,6 @@ impl EventHandler for Handler { return; } - let guild = msg.guild(&ctx.cache).unwrap(); let user_msg = msg.content.clone(); let re = regex::Regex::new(r"").unwrap(); @@ -68,19 +67,17 @@ impl EventHandler for Handler { .map(|a| AttachmentType::Image((a.url).parse().unwrap())) .collect(); - let msg_source = MessageSource::Player(Box::new(player_data.clone())); + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Player(Box::new(player_data.clone()))) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(&user_msg) + .attachments(attachments) + .clone(); - dispatch_message( - &ctx, - &guild, - &mut global_data, - msg_source, - MessageDest::Broadcast, - &user_msg, - Some(attachments), - ) - .await - .expect("Unable to send message to players"); + dispatch_message(&ctx, &global_data, woxlf_msg) + .await + .expect("Unable to send message to players"); } } diff --git a/src/game/message_router.rs b/src/game/message_router.rs index eebc477..0892f7c 100644 --- a/src/game/message_router.rs +++ b/src/game/message_router.rs @@ -1,11 +1,13 @@ use crate::error; +use crate::error::WoxlfError; use crate::game::global_data::GlobalData; use crate::game::player_data::PlayerData; +use bitflags::bitflags; use serenity::client::Context; -use serenity::http::Http; +use serenity::http::{CacheHttp, Http}; use serenity::model::guild::Guild; -use serenity::model::id::UserId; -use serenity::model::prelude::AttachmentType; +use serenity::model::id::{ChannelId, UserId}; +use serenity::model::prelude::{AttachmentType, GuildId, WebhookId}; use serenity::utils::MessageBuilder; #[derive(Debug, Clone)] @@ -15,13 +17,111 @@ pub enum MessageSource { Automated, } +impl Default for MessageSource { + fn default() -> Self { + Self::Automated + } +} + #[derive(Debug, Clone)] +#[allow(dead_code)] pub enum MessageDest { - PlayerChannel(Box), - PlayerDm(Box), + Player(Box), + Host, Broadcast, } +impl Default for MessageDest { + fn default() -> Self { + Self::Broadcast + } +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub enum Median { + DirectMessage, + Webhook, + StandardMessage, +} + +impl Default for Median { + fn default() -> Self { + Self::Webhook + } +} + +bitflags! { + #[derive(Default)] + pub struct MsgFlags: u32 { + const PIN_MSG = 0b00000001; + } +} + +#[derive(Debug, Clone, Default)] +pub struct WoxlfMessage<'a> { + pub source: MessageSource, + pub dest: MessageDest, + pub median: Median, + pub content: String, + pub attachments: Option>>, + pub flags: MsgFlags, +} + +#[allow(dead_code)] +impl<'a> WoxlfMessage<'a> { + pub fn get_profile_pic(&self, global_data: &GlobalData) -> Result { + Ok(match &self.source { + MessageSource::Player(p) => p.profile_pic_url.clone(), + MessageSource::Host | MessageSource::Automated => { + global_data.game_cfg()?.bot_profile_pic.clone() + } + }) + } + + pub fn get_message_username(&self, global_data: &GlobalData) -> Result { + Ok(match &self.source { + MessageSource::Player(p) => p.codename.clone(), + MessageSource::Host => global_data.game_cfg()?.bot_name.clone(), + MessageSource::Automated => "Woxlf System Message".to_string(), + }) + } + + pub fn source(mut self, source: MessageSource) -> Self { + self.source = source; + self + } + + pub fn dest(mut self, dest: MessageDest) -> Self { + self.dest = dest; + self + } + + pub fn median(mut self, median: Median) -> Self { + self.median = median; + self + } + + pub fn content(mut self, content: &str) -> Self { + self.content = content.to_string(); + self + } + + pub fn attachments(mut self, attachments: Vec>) -> Self { + self.attachments = Some(attachments); + self + } + + pub fn flags(mut self, flags: MsgFlags) -> Self { + self.flags = flags; + self + } + + pub fn pin(mut self) -> Self { + self.flags |= MsgFlags::PIN_MSG; + self + } +} fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool { if let MessageSource::Player(source_player) = &msg_source { @@ -34,13 +134,13 @@ fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) async fn send_webhook_msg( http: &Http, - webhook_id: u64, + webhook_id: WebhookId, username: &str, profile_pic_url: Option, msg: &str, attachments: &Option>>, ) -> error::Result<()> { - let webhook = http.get_webhook(webhook_id).await?; + let webhook = http.get_webhook(webhook_id.0).await?; webhook .execute(http, false, move |w| { @@ -93,15 +193,10 @@ async fn send_private_message( async fn send_to_host_channel( http: &Http, guild: &Guild, - global_data: &mut GlobalData, - msg_username: &str, - msg_source: MessageSource, - msg_dest: MessageDest, - profile_pic_url: Option, - msg: &str, - attachments: &Option>>, + global_data: &GlobalData, + msg: WoxlfMessage<'_>, ) -> error::Result<()> { - let source = match &msg_source { + let source = match &msg.source { MessageSource::Player(player_data) => { let name = guild .members @@ -115,113 +210,106 @@ async fn send_to_host_channel( MessageSource::Automated => "Automated".to_string(), }; - let dest = match &msg_dest { - MessageDest::PlayerChannel(p) | MessageDest::PlayerDm(p) => { - let name = guild - .members - .get(&UserId::from(p.discord_id)) - .unwrap() - .display_name(); - - format!(" to {} ({})", p.codename, name) + let dest = match &msg.median { + Median::DirectMessage => { + if let MessageDest::Player(dest_player) = &msg.dest { + let name = guild + .members + .get(&UserId::from(dest_player.discord_id)) + .unwrap() + .display_name(); + format!(" to {} ({})", dest_player.codename, name) + } else { + "".to_string() + } } - MessageDest::Broadcast => "".to_string(), + _ => "".to_string(), }; - let host_channel_username = format!("{} ({}){}", msg_username, source, dest); + let host_channel_username = format!( + "{} ({}){}", + msg.get_message_username(global_data)?, + source, + dest + ); send_webhook_msg( http, - global_data.cfg.discord_config.host_webhook_id, + WebhookId::from(global_data.cfg.discord_config.host_webhook_id), &host_channel_username, - profile_pic_url, - msg, - attachments, + Some(msg.get_profile_pic(global_data)?), + &msg.content, + &msg.attachments, ) .await?; Ok(()) } +pub async fn send_message( + ctx: &Context, + global_data: &GlobalData, + msg: &WoxlfMessage<'_>, + dest_player: &PlayerData, +) -> Result<(), WoxlfError> { + match &msg.median { + Median::Webhook => { + send_webhook_msg( + &ctx.http, + WebhookId::from(dest_player.channel_webhook_id), + &msg.get_message_username(global_data)?, + Some(msg.get_profile_pic(global_data)?), + &msg.content, + &msg.attachments, + ) + .await?; + } + Median::DirectMessage => { + send_private_message( + &ctx.http, + &msg.get_message_username(global_data)?, + UserId(dest_player.discord_id), + &msg.content, + &msg.attachments, + ) + .await?; + } + Median::StandardMessage => { + let channel = ChannelId::from(dest_player.channel); + channel.say(&ctx.http(), &msg.content).await?; + } + }; + + Ok(()) +} + pub async fn dispatch_message( ctx: &Context, - guild: &Guild, - global_data: &mut GlobalData, - msg_source: MessageSource, - msg_dest: MessageDest, - msg: &str, - attachments: Option>>, + global_data: &GlobalData, + msg: WoxlfMessage<'_>, ) -> error::Result<()> { - let msg_username = match &msg_source { - MessageSource::Player(p) => p.codename.clone(), - MessageSource::Host => "Woxlf".to_string(), - MessageSource::Automated => "Woxlf System Message".to_string(), - }; + let guild_id = global_data.cfg.discord_config.guild_id; + let guild = GuildId::from(guild_id).to_guild_cached(&ctx.cache).unwrap(); - let profile_pic = match &msg_source { - MessageSource::Player(p) => Some(p.profile_pic_url.clone()), - MessageSource::Host | MessageSource::Automated => { - Some(global_data.game_cfg()?.bot_profile_pic.clone()) - } - }; - - let msg_tasks: Vec<&PlayerData> = global_data - .game_state_mut()? + let msg_tasks = global_data + .game_state()? .player_data .iter() - .filter(|player| filter_source_channel(player, &msg_source)) + .filter(|player| filter_source_channel(player, &msg.source)) + .filter(|player| match &msg.dest { + MessageDest::Player(dest_user) => dest_user.discord_id == player.discord_id, + MessageDest::Host => false, + MessageDest::Broadcast => true, + }) + .map(|p| send_message(ctx, global_data, &msg, p)); + + let results: Result<(), WoxlfError> = futures::future::join_all(msg_tasks) + .await + .into_iter() .collect(); - for player_data in msg_tasks { - match &msg_dest { - MessageDest::PlayerChannel(dest_player) => { - if dest_player.discord_id == player_data.discord_id { - send_webhook_msg( - &ctx.http, - player_data.channel_webhook_id, - &msg_username, - profile_pic.clone(), - msg, - &attachments, - ) - .await?; - } - } - MessageDest::PlayerDm(dest_player) => { - send_private_message( - &ctx.http, - &msg_username, - UserId(dest_player.discord_id), - msg, - &attachments, - ) - .await? - } - MessageDest::Broadcast => { - send_webhook_msg( - &ctx.http, - player_data.channel_webhook_id, - &msg_username, - profile_pic.clone(), - msg, - &attachments, - ) - .await? - } - } - } + results?; - send_to_host_channel( - &ctx.http, - guild, - global_data, - &msg_username, - msg_source, - msg_dest, - profile_pic, - msg, - &attachments, - ) - .await?; + send_to_host_channel(&ctx.http, &guild, global_data, msg).await?; Ok(()) } -- 2.41.0 From a5e1a275e846038789eed31a600c904c61eec79a Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Thu, 5 Jan 2023 22:04:25 -0700 Subject: [PATCH 3/3] Message router refactor + Created the WoxlfMessage struct to streamline interface + Message tasks are now joined at once instead of sequentially + Clippy + fmt --- Cargo.lock | 1 + Cargo.toml | 1 + src/discord/commands.rs | 118 +++++++------- src/discord/event_handler.rs | 27 ++-- src/game/message_router.rs | 290 +++++++++++++++++++++++------------ src/messages/mod.rs | 4 +- 6 files changed, 259 insertions(+), 182 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53f2f94..711dd40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2304,6 +2304,7 @@ dependencies = [ name = "woxlf" version = "0.2.0" dependencies = [ + "bitflags", "chrono", "config", "futures", diff --git a/Cargo.toml b/Cargo.toml index f106dc9..0057d95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ regex = "1.5.5" futures = "0.3.21" reqwest = "0.11.10" tera = "1.15.0" +bitflags = "1.3.2" [dependencies.serenity] version = "0.11.5" diff --git a/src/discord/commands.rs b/src/discord/commands.rs index b015090..702a672 100644 --- a/src/discord/commands.rs +++ b/src/discord/commands.rs @@ -10,14 +10,16 @@ use serenity::framework::standard::{ use serenity::framework::StandardFramework; use serenity::model::guild::Member; use serenity::model::id::ChannelId; -use serenity::model::prelude::{GuildId, Message, UserId}; +use serenity::model::prelude::{Message, UserId}; use serenity::prelude::Context; use serenity::utils::MessageBuilder; use crate::discord::helper::{add_user_to_game, parse_duration_arg}; use crate::error::{Result, WoxlfError}; use crate::game::global_data::GlobalData; -use crate::game::message_router::{dispatch_message, MessageDest, MessageSource}; +use crate::game::message_router::{ + dispatch_message, Median, MessageDest, MessageSource, WoxlfMessage, +}; use crate::game::player_data::PlayerData; use crate::game::Phase; use crate::messages::DiscordUser; @@ -161,23 +163,20 @@ async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult { #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] -async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult { - let mut data = ctx.data.write().await; - let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).unwrap(); +async fn say(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { + let data = ctx.data.read().await; + let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; + let global_data = global_data.lock().await; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Host, - MessageDest::Broadcast, - args.rest(), - None, - ) - .await?; + let msg = WoxlfMessage::default() + .source(MessageSource::Host) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(args.rest()) + .clone(); + + dispatch_message(ctx, &global_data, msg).await?; Ok(()) } @@ -185,27 +184,24 @@ async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult { #[command] #[only_in(guilds)] #[allowed_roles("wolfx host")] -async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult { - let mut data = ctx.data.write().await; - let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).unwrap(); +async fn broadcast(ctx: &Context, _msg: &Message, args: Args) -> CommandResult { + let data = ctx.data.read().await; + let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; + let global_data = global_data.lock().await; - let msg = global_data + let broadcast = global_data .templates()? .build_announcement(&global_data, args.rest())?; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Automated, - MessageDest::Broadcast, - &msg, - None, - ) - .await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Automated) + .dest(MessageDest::Broadcast) + .content(&broadcast) + .median(Median::Webhook) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; Ok(()) } @@ -234,16 +230,14 @@ async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu .templates()? .build_announcement(&global_data, &broadcast)?; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Automated, - MessageDest::Broadcast, - &broadcast, - None, - ) - .await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Automated) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(&broadcast) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; if global_data.game_state_mut()?.current_phase == Phase::Day { let vote_channel = guild @@ -311,7 +305,6 @@ async fn kill(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global_data = data.get_mut::().unwrap(); - let guild = msg.guild(&ctx.cache).unwrap(); let mut global_data = global_data.lock().await; @@ -335,16 +328,14 @@ async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult .templates()? .build_announcement(&global_data, &broadcast)?; - dispatch_message( - ctx, - &guild, - &mut global_data, - MessageSource::Automated, - MessageDest::Broadcast, - &broadcast, - None, - ) - .await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Automated) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(&broadcast) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; msg.reply(&ctx.http, "Phase has been updated") .await @@ -651,13 +642,9 @@ async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult msg.reply(&ctx.http, "Need a recipient and message!") .await?; } else { - let data = ctx.data.write().await; + let data = ctx.data.read().await; let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; - - let guild = GuildId::from(global_data.cfg.discord_config.guild_id) - .to_guild_cached(&ctx.cache) - .unwrap(); + let global_data = global_data.lock().await; let target = args.single::()?; let pm = args.rest(); @@ -680,9 +667,14 @@ async fn whisper(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult return Ok(()); } - let msg_src = MessageSource::Player(Box::new(src_player.clone())); - let msg_dest = MessageDest::PlayerDm(Box::new(target_player.clone())); - dispatch_message(ctx, &guild, &mut global_data, msg_src, msg_dest, pm, None).await?; + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Player(Box::new(src_player.clone()))) + .dest(MessageDest::Player(Box::new(target_player.clone()))) + .median(Median::DirectMessage) + .content(pm) + .clone(); + + dispatch_message(ctx, &global_data, woxlf_msg).await?; } else { msg.reply( &ctx.http, diff --git a/src/discord/event_handler.rs b/src/discord/event_handler.rs index e58357c..7f294f3 100644 --- a/src/discord/event_handler.rs +++ b/src/discord/event_handler.rs @@ -6,8 +6,8 @@ use serenity::model::prelude::AttachmentType; use serenity::utils::parse_emoji; use crate::game::global_data::GlobalData; -use crate::game::message_router::MessageSource; use crate::game::message_router::{dispatch_message, MessageDest}; +use crate::game::message_router::{Median, MessageSource, WoxlfMessage}; pub struct Handler {} @@ -26,7 +26,7 @@ impl EventHandler for Handler { let global_data = data.get::().unwrap(); - let mut global_data = global_data.lock().await; + let global_data = global_data.lock().await; if global_data.game_state.is_none() { // no game in progress @@ -43,7 +43,6 @@ impl EventHandler for Handler { return; } - let guild = msg.guild(&ctx.cache).unwrap(); let user_msg = msg.content.clone(); let re = regex::Regex::new(r"").unwrap(); @@ -68,19 +67,17 @@ impl EventHandler for Handler { .map(|a| AttachmentType::Image((a.url).parse().unwrap())) .collect(); - let msg_source = MessageSource::Player(Box::new(player_data.clone())); + let woxlf_msg = WoxlfMessage::default() + .source(MessageSource::Player(Box::new(player_data.clone()))) + .dest(MessageDest::Broadcast) + .median(Median::Webhook) + .content(&user_msg) + .attachments(attachments) + .clone(); - dispatch_message( - &ctx, - &guild, - &mut global_data, - msg_source, - MessageDest::Broadcast, - &user_msg, - Some(attachments), - ) - .await - .expect("Unable to send message to players"); + dispatch_message(&ctx, &global_data, woxlf_msg) + .await + .expect("Unable to send message to players"); } } diff --git a/src/game/message_router.rs b/src/game/message_router.rs index eebc477..0892f7c 100644 --- a/src/game/message_router.rs +++ b/src/game/message_router.rs @@ -1,11 +1,13 @@ use crate::error; +use crate::error::WoxlfError; use crate::game::global_data::GlobalData; use crate::game::player_data::PlayerData; +use bitflags::bitflags; use serenity::client::Context; -use serenity::http::Http; +use serenity::http::{CacheHttp, Http}; use serenity::model::guild::Guild; -use serenity::model::id::UserId; -use serenity::model::prelude::AttachmentType; +use serenity::model::id::{ChannelId, UserId}; +use serenity::model::prelude::{AttachmentType, GuildId, WebhookId}; use serenity::utils::MessageBuilder; #[derive(Debug, Clone)] @@ -15,13 +17,111 @@ pub enum MessageSource { Automated, } +impl Default for MessageSource { + fn default() -> Self { + Self::Automated + } +} + #[derive(Debug, Clone)] +#[allow(dead_code)] pub enum MessageDest { - PlayerChannel(Box), - PlayerDm(Box), + Player(Box), + Host, Broadcast, } +impl Default for MessageDest { + fn default() -> Self { + Self::Broadcast + } +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub enum Median { + DirectMessage, + Webhook, + StandardMessage, +} + +impl Default for Median { + fn default() -> Self { + Self::Webhook + } +} + +bitflags! { + #[derive(Default)] + pub struct MsgFlags: u32 { + const PIN_MSG = 0b00000001; + } +} + +#[derive(Debug, Clone, Default)] +pub struct WoxlfMessage<'a> { + pub source: MessageSource, + pub dest: MessageDest, + pub median: Median, + pub content: String, + pub attachments: Option>>, + pub flags: MsgFlags, +} + +#[allow(dead_code)] +impl<'a> WoxlfMessage<'a> { + pub fn get_profile_pic(&self, global_data: &GlobalData) -> Result { + Ok(match &self.source { + MessageSource::Player(p) => p.profile_pic_url.clone(), + MessageSource::Host | MessageSource::Automated => { + global_data.game_cfg()?.bot_profile_pic.clone() + } + }) + } + + pub fn get_message_username(&self, global_data: &GlobalData) -> Result { + Ok(match &self.source { + MessageSource::Player(p) => p.codename.clone(), + MessageSource::Host => global_data.game_cfg()?.bot_name.clone(), + MessageSource::Automated => "Woxlf System Message".to_string(), + }) + } + + pub fn source(mut self, source: MessageSource) -> Self { + self.source = source; + self + } + + pub fn dest(mut self, dest: MessageDest) -> Self { + self.dest = dest; + self + } + + pub fn median(mut self, median: Median) -> Self { + self.median = median; + self + } + + pub fn content(mut self, content: &str) -> Self { + self.content = content.to_string(); + self + } + + pub fn attachments(mut self, attachments: Vec>) -> Self { + self.attachments = Some(attachments); + self + } + + pub fn flags(mut self, flags: MsgFlags) -> Self { + self.flags = flags; + self + } + + pub fn pin(mut self) -> Self { + self.flags |= MsgFlags::PIN_MSG; + self + } +} fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) -> bool { if let MessageSource::Player(source_player) = &msg_source { @@ -34,13 +134,13 @@ fn filter_source_channel(player_data: &&PlayerData, msg_source: &MessageSource) async fn send_webhook_msg( http: &Http, - webhook_id: u64, + webhook_id: WebhookId, username: &str, profile_pic_url: Option, msg: &str, attachments: &Option>>, ) -> error::Result<()> { - let webhook = http.get_webhook(webhook_id).await?; + let webhook = http.get_webhook(webhook_id.0).await?; webhook .execute(http, false, move |w| { @@ -93,15 +193,10 @@ async fn send_private_message( async fn send_to_host_channel( http: &Http, guild: &Guild, - global_data: &mut GlobalData, - msg_username: &str, - msg_source: MessageSource, - msg_dest: MessageDest, - profile_pic_url: Option, - msg: &str, - attachments: &Option>>, + global_data: &GlobalData, + msg: WoxlfMessage<'_>, ) -> error::Result<()> { - let source = match &msg_source { + let source = match &msg.source { MessageSource::Player(player_data) => { let name = guild .members @@ -115,113 +210,106 @@ async fn send_to_host_channel( MessageSource::Automated => "Automated".to_string(), }; - let dest = match &msg_dest { - MessageDest::PlayerChannel(p) | MessageDest::PlayerDm(p) => { - let name = guild - .members - .get(&UserId::from(p.discord_id)) - .unwrap() - .display_name(); - - format!(" to {} ({})", p.codename, name) + let dest = match &msg.median { + Median::DirectMessage => { + if let MessageDest::Player(dest_player) = &msg.dest { + let name = guild + .members + .get(&UserId::from(dest_player.discord_id)) + .unwrap() + .display_name(); + format!(" to {} ({})", dest_player.codename, name) + } else { + "".to_string() + } } - MessageDest::Broadcast => "".to_string(), + _ => "".to_string(), }; - let host_channel_username = format!("{} ({}){}", msg_username, source, dest); + let host_channel_username = format!( + "{} ({}){}", + msg.get_message_username(global_data)?, + source, + dest + ); send_webhook_msg( http, - global_data.cfg.discord_config.host_webhook_id, + WebhookId::from(global_data.cfg.discord_config.host_webhook_id), &host_channel_username, - profile_pic_url, - msg, - attachments, + Some(msg.get_profile_pic(global_data)?), + &msg.content, + &msg.attachments, ) .await?; Ok(()) } +pub async fn send_message( + ctx: &Context, + global_data: &GlobalData, + msg: &WoxlfMessage<'_>, + dest_player: &PlayerData, +) -> Result<(), WoxlfError> { + match &msg.median { + Median::Webhook => { + send_webhook_msg( + &ctx.http, + WebhookId::from(dest_player.channel_webhook_id), + &msg.get_message_username(global_data)?, + Some(msg.get_profile_pic(global_data)?), + &msg.content, + &msg.attachments, + ) + .await?; + } + Median::DirectMessage => { + send_private_message( + &ctx.http, + &msg.get_message_username(global_data)?, + UserId(dest_player.discord_id), + &msg.content, + &msg.attachments, + ) + .await?; + } + Median::StandardMessage => { + let channel = ChannelId::from(dest_player.channel); + channel.say(&ctx.http(), &msg.content).await?; + } + }; + + Ok(()) +} + pub async fn dispatch_message( ctx: &Context, - guild: &Guild, - global_data: &mut GlobalData, - msg_source: MessageSource, - msg_dest: MessageDest, - msg: &str, - attachments: Option>>, + global_data: &GlobalData, + msg: WoxlfMessage<'_>, ) -> error::Result<()> { - let msg_username = match &msg_source { - MessageSource::Player(p) => p.codename.clone(), - MessageSource::Host => "Woxlf".to_string(), - MessageSource::Automated => "Woxlf System Message".to_string(), - }; + let guild_id = global_data.cfg.discord_config.guild_id; + let guild = GuildId::from(guild_id).to_guild_cached(&ctx.cache).unwrap(); - let profile_pic = match &msg_source { - MessageSource::Player(p) => Some(p.profile_pic_url.clone()), - MessageSource::Host | MessageSource::Automated => { - Some(global_data.game_cfg()?.bot_profile_pic.clone()) - } - }; - - let msg_tasks: Vec<&PlayerData> = global_data - .game_state_mut()? + let msg_tasks = global_data + .game_state()? .player_data .iter() - .filter(|player| filter_source_channel(player, &msg_source)) + .filter(|player| filter_source_channel(player, &msg.source)) + .filter(|player| match &msg.dest { + MessageDest::Player(dest_user) => dest_user.discord_id == player.discord_id, + MessageDest::Host => false, + MessageDest::Broadcast => true, + }) + .map(|p| send_message(ctx, global_data, &msg, p)); + + let results: Result<(), WoxlfError> = futures::future::join_all(msg_tasks) + .await + .into_iter() .collect(); - for player_data in msg_tasks { - match &msg_dest { - MessageDest::PlayerChannel(dest_player) => { - if dest_player.discord_id == player_data.discord_id { - send_webhook_msg( - &ctx.http, - player_data.channel_webhook_id, - &msg_username, - profile_pic.clone(), - msg, - &attachments, - ) - .await?; - } - } - MessageDest::PlayerDm(dest_player) => { - send_private_message( - &ctx.http, - &msg_username, - UserId(dest_player.discord_id), - msg, - &attachments, - ) - .await? - } - MessageDest::Broadcast => { - send_webhook_msg( - &ctx.http, - player_data.channel_webhook_id, - &msg_username, - profile_pic.clone(), - msg, - &attachments, - ) - .await? - } - } - } + results?; - send_to_host_channel( - &ctx.http, - guild, - global_data, - &msg_username, - msg_source, - msg_dest, - profile_pic, - msg, - &attachments, - ) - .await?; + send_to_host_channel(&ctx.http, &guild, global_data, msg).await?; Ok(()) } diff --git a/src/messages/mod.rs b/src/messages/mod.rs index a1898db..b77c45a 100644 --- a/src/messages/mod.rs +++ b/src/messages/mod.rs @@ -10,9 +10,7 @@ use tera::{Tera, Value}; type TeraFnRet = Box) -> tera::Result + Send + Sync>; -fn time_to_discord_time( - time_flag: &str, -) -> TeraFnRet { +fn time_to_discord_time(time_flag: &str) -> TeraFnRet { let time_flag = time_flag.to_string(); Box::new( -- 2.41.0