From 6df6577967835156bce77b74e3bbcab269380f36 Mon Sep 17 00:00:00 2001 From: Elfein Landers Date: Mon, 24 Jan 2022 10:33:09 -0800 Subject: [PATCH] Initial commit. --- .gitignore | 1 + Cargo.lock | 1302 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 11 + README.md | 18 + src/commands.rs | 466 +++++++++++++++++ src/handler.rs | 380 ++++++++++++++ src/main.rs | 72 +++ src/track.rs | 52 ++ 8 files changed, 2302 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/commands.rs create mode 100644 src/handler.rs create mode 100644 src/main.rs create mode 100644 src/track.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c385c76 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1302 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "async-trait" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-tungstenite" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7cc5408453d37e2b1c6f01d8078af1da58b6cfa6a80fa2ede3bd2b9a6ada9c4" +dependencies = [ + "futures-io", + "futures-util", + "log", + "pin-project", + "tokio", + "tokio-rustls 0.22.0", + "tungstenite", + "webpki-roots 0.20.0", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "byteorder" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "winapi", +] + +[[package]] +name = "command_attr" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e74a021f9d0b577821bf9f5b1a2be88677e95ee05713e5d44fb4236cfa1744c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" + +[[package]] +name = "futures-io" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" + +[[package]] +name = "futures-sink" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" + +[[package]] +name = "futures-task" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" + +[[package]] +name = "futures-util" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "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", +] + +[[package]] +name = "h2" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" +dependencies = [ + "bytes 1.1.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes 1.1.0", + "fnv", + "itoa 1.0.1", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes 1.1.0", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +dependencies = [ + "bytes 1.1.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.8", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls 0.20.2", + "tokio", + "tokio-rustls 0.23.2", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[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 = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "mpsc_bot" +version = "0.1.0" +dependencies = [ + "serde_json", + "serenity", + "tokio", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "reqwest" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" +dependencies = [ + "base64 0.13.0", + "bytes 1.1.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "rustls 0.20.2", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls 0.23.2", + "tokio-util", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.22.2", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.0", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf9235533494ea2ddcdb794665461814781c53f19d87b76e571a1c35acbad2b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dcde03d87d4c973c04be249e7d8f0b35db1c848c487bd43032808e59dd8328d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serenity" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde86919535c6047e055d512641c5241320c01cb8fee54f1e5ba77c939a0ec23" +dependencies = [ + "async-trait", + "async-tungstenite", + "base64 0.13.0", + "bitflags", + "bytes 1.1.0", + "chrono", + "command_attr", + "flate2", + "futures", + "percent-encoding", + "reqwest", + "serde", + "serde_json", + "static_assertions", + "tokio", + "tracing", + "typemap_rev", + "url", + "uwl", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "socket2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f82496b90c36d70af5fcd482edaa2e0bd16fade569de1330405fecbbdac736b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[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.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +dependencies = [ + "bytes 1.1.0", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.2", + "tokio", + "webpki 0.22.0", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes 1.1.0", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" +dependencies = [ + "base64 0.12.3", + "byteorder", + "bytes 0.5.6", + "http", + "httparse", + "input_buffer", + "log", + "rand", + "sha-1", + "url", + "utf-8", +] + +[[package]] +name = "typemap_rev" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5b74f0a24b5454580a79abb6994393b09adf0ab8070f15827cb666255de155" + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "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 = "wasm-bindgen" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "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", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..edea6ed --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "mpsc_bot" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serenity = "0.10" +tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } +serde_json = "*" diff --git a/README.md b/README.md new file mode 100644 index 0000000..078b3f2 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +*A bot for copying a channel's messages, optionally including those in threads, to another channel.* +**Commands:** +Gives information on how to use this bot's commands. +`help` +Sets which channels to use as source or target. +`set (source | s | target | t) [(source | s | target | t) ]` +Tells the bot whether or not to copy from threads. Default is true. +`weave (false | f | true | t)` +Sets source and target to `None`. +`reset` +Deletes info messages sent by this bot and command messages issued by users. +`clean` +Deletes all messages sent by this bot. +`flush` +Stops the bot. +`stop` +Begins the bot. +`begin` \ No newline at end of file diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..976e814 --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,466 @@ +use std::{str::FromStr, sync::atomic::Ordering}; + +use serenity::{ + client::Context, + framework::standard::{ + macros::{command, group}, + Args, CommandResult, + }, + model::{channel::Message, id::ChannelId, misc::ChannelIdParseError}, + utils::MessageBuilder, +}; + +use crate::track::{Active, InfoSent, Sent, SourceChannel, TargetChannel, Weave}; + +use crate::err_strs::*; + +#[group] +#[commands(set, stop, begin, clean, flush, help, reset, weave)] +pub struct General; + +const SET_USAGE: &str = + "Usage: `set (source | target) [(source | target) ]`"; + +#[derive(Clone, Copy)] +pub enum SetArg { + Source, + Target, + Id(ChannelId), + Unset, +} + +impl SetArg { + pub fn get(self) -> Option { + match self { + Self::Id(id) => Some(id), + _ => None, + } + } +} + +impl FromStr for SetArg { + type Err = ChannelIdParseError; + + fn from_str(s: &str) -> Result { + if s == "source" || s == "s" { + Ok(Self::Source) + } else if s == "target" || s == "t" { + Ok(Self::Target) + } else { + Ok(Self::Id(ChannelId::from_str(s)?)) + } + } +} + +#[command] +async fn help(ctx: &Context, msg: &Message) -> CommandResult { + let data = ctx.data.read().await; + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + (*info_sent.lock().await).push((msg.channel_id, msg.id)); + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply( + ctx, + "*A bot for copying a channel's messages, optionally including those in threads, to another channel.* +**Commands:** +Gives information on how to use this bot's commands. +`help` +Sets which channels to use as source or target. +`set (source | s | target | t) [(source | s | target | t) ]` +Tells the bot whether or not to copy from threads. Default is true. +`weave (false | f | true | t)` +Sets source and target to `None`. +`reset` +Deletes info messages sent by this bot and command messages issued by users. +`clean` +Deletes all messages sent by this bot. +`flush` +Stops the bot. +`stop` +Begins the bot. +`begin`", + ) + .await? + .id, + )); + Ok(()) +} + +#[command] +#[only_in(guilds)] +#[owners_only] +#[usage(SET_USAGE)] +/// Sets the channel that MPSC Bot will clone messages from. +/// If a channel id is given, will compile all thread messages into the target channel. +async fn set(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { + let data = ctx.data.read().await; + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + (*info_sent.lock().await).push((msg.channel_id, msg.id)); + if args.len() > 4 { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "Too many arguments.").await?.id, + )); + return Ok(()); + } + if args.len() % 2 == 1 { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "Not enough arguments.").await?.id, + )); + return Ok(()); + } + use SetArg::*; + let mut arg_frame: [SetArg; 4] = [Unset, Unset, Unset, Unset]; + + for (n, arg) in args.iter::().enumerate() { + if let Ok(arg) = arg { + arg_frame[n] = arg; + } else { + (*info_sent.lock().await).push((msg.channel_id, msg.reply(ctx, SET_USAGE).await?.id)); + return Ok(()); + } + } + + match arg_frame { + [Source, source @ Id(_), Unset | Target, target] + | [Target, target @ Id(_), Unset | Source, source] => { + if let Some(source) = source.get() { + let ch = data.get::().expect(EXPECTED_SOURCE_CHANNEL); + *ch.lock().await = Some(source); + } + if let Some(target) = target.get() { + let ch = data.get::().expect(EXPECTED_TARGET_CHANNEL); + *ch.lock().await = Some(target); + } + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply( + ctx, + match ( + *data + .get::() + .expect(EXPECTED_SOURCE_CHANNEL) + .lock() + .await, + *data + .get::() + .expect(EXPECTED_SOURCE_CHANNEL) + .lock() + .await, + ) { + (None, None) => MessageBuilder::new() + .push("Source channel: None\nTarget Channel: None") + .build(), + (None, Some(target)) => MessageBuilder::new() + .push("Source channel: None\nTarget channel: ") + .mention(&target) + .build(), + (Some(source), None) => MessageBuilder::new() + .push("Source channel: ") + .mention(&source) + .push("\nTarget channel: None") + .build(), + (Some(source), Some(target)) => MessageBuilder::new() + .push("Source channel: ") + .mention(&source) + .push("\nTarget channel: ") + .mention(&target) + .build(), + }, + ) + .await? + .id, + )); + } + [Id(_), ..] => { + (*info_sent.lock().await).push((msg.channel_id, msg.reply( + ctx, + "Please specify if source or target channel: `/set source ` or `/set target `", + ) + .await?.id)); + } + [Source, Target, Unset, Unset] | [Target, Source, Unset, Unset] => { + (*info_sent.lock().await).push((msg.channel_id, msg.reply(ctx, "? what?").await?.id)); + } + [Source, Target, ..] | [Target, Source, ..] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "Please specify a channel.").await?.id, + )); + } + [Target, Id(_), Target, Id(_)] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply( + ctx, + "Uhh, you feelin' okay there, bud? You specified target *twice*.", + ) + .await? + .id, + )); + } + [Target, Target, Target, Target] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "You really need to stop. You're wasting CPU cycles.") + .await? + .id, + )); + } + [Target, Target, ..] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "This just makes no sense. Why would you do this?") + .await? + .id, + )); + } + [Source, Id(_), Source, Id(_)] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply( + ctx, + "Uhh, you feelin' okay there, bud? You specified source twice.", + ) + .await? + .id, + )); + } + [Source, Source, Source, Source] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "Okay what's going on here?").await?.id, + )); + } + [Source, Source, ..] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "Seriously, why would you do this?") + .await? + .id, + )); + } + [Source, Unset, Unset, Unset] | [Target, Unset, Unset, Unset] => { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "Please specify a channel.").await?.id, + )); + } + [Source, Unset, Target, _] + | [Source, Unset, Source, _] + | [Source, Unset, Id(_), _] + | [Source, Unset, _, Target] + | [Source, Unset, _, Source] + | [Source, Unset, _, Id(_)] + | [Target, Unset, Target, _] + | [Target, Unset, Source, _] + | [Target, Unset, Id(_), _] + | [Target, Unset, _, Target] + | [Target, Unset, _, Source] + | [Target, Unset, _, Id(_)] => unreachable![], + _ => { + (*info_sent.lock().await).push((msg.channel_id, msg.reply(ctx, SET_USAGE).await?.id)); + } + } + Ok(()) +} + +#[command] +#[only_in(guilds)] +#[owners_only] +#[usage("reset")] +/// Sets source and target channels to `None`. +async fn reset(ctx: &Context, msg: &Message) -> CommandResult { + let data = ctx.data.read().await; + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + (*info_sent.lock().await).push((msg.channel_id, msg.id)); + { + let ch = data.get::().expect(EXPECTED_SOURCE_CHANNEL); + *ch.lock().await = None; + } + { + let ch = data.get::().expect(EXPECTED_TARGET_CHANNEL); + *ch.lock().await = None; + } + { + let active = data.get::().expect(EXPECTED_TARGET_CHANNEL); + active.store(false, Ordering::Relaxed); + } + Ok(()) +} + +#[command] +#[only_in(guilds)] +#[owners_only] +#[usage("clean")] +/// Deletes all messages sent by this bot. +async fn clean(ctx: &Context, msg: &Message) -> CommandResult { + let data = ctx.data.read().await; + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + (*info_sent.lock().await).push((msg.channel_id, msg.id)); + for (channel_id, message_id) in info_sent.lock().await.iter() { + ctx.http + .delete_message((*channel_id).into(), (*message_id).into()) + .await?; + } + (*info_sent.lock().await).clear(); + Ok(()) +} + +#[command] +#[only_in(guilds)] +#[owners_only] +#[usage("flush")] +/// Deletes all messages sent by this bot. +async fn flush(ctx: &Context, msg: &Message) -> CommandResult { + let data = ctx.data.read().await; + { + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + (*info_sent.lock().await).push((msg.channel_id, msg.id)); + for (channel_id, message_id) in info_sent.lock().await.iter() { + ctx.http + .delete_message((*channel_id).into(), (*message_id).into()) + .await?; + } + (*info_sent.lock().await).clear(); + } + { + let sent = data.get::().expect("Expected Sent in TypeMap."); + for (_, (channel_id, message_id)) in sent.lock().await.iter() { + ctx.http + .delete_message((*channel_id).into(), (*message_id).into()) + .await?; + } + (*sent.lock().await).clear(); + } + Ok(()) +} + +#[command] +#[only_in(guilds)] +#[owners_only] +#[num_args(0)] +#[usage("stop")] +/// Stops the bot. +async fn stop(ctx: &Context, msg: &Message) -> CommandResult { + let data = ctx.data.read().await; + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + (*info_sent.lock().await).push((msg.channel_id, msg.id)); + let active = data.get::().expect(EXPECTED_ACTIVE); + + if !active.load(Ordering::Relaxed) { + (*info_sent.lock().await).push((msg.channel_id, msg.reply(ctx, "Not started.").await?.id)); + return Ok(()); + } + + (*info_sent.lock().await).push((msg.channel_id, msg.reply(ctx, "Stopping...").await?.id)); + + active.store(false, Ordering::Relaxed); + + Ok(()) +} + +#[command] +#[only_in(guilds)] +#[owners_only] +#[num_args(0)] +#[usage("begin")] +/// Begins the bot. +async fn begin(ctx: &Context, msg: &Message) -> CommandResult { + let data = ctx.data.read().await; + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + (*info_sent.lock().await).push((msg.channel_id, msg.id)); + if let Some(target) = data.get::() { + if (*target.lock().await).is_none() { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply( + ctx, + "Target channel must be specified first. Try specifying one using the `~set` command.", + ) + .await? + .id, + )); + return Ok(()); + } + } + if let Some(source) = data.get::() { + if (*source.lock().await).is_none() { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply( + ctx, + "Source channel must be specified first. Try specifying one using the `~set` command.", + ) + .await? + .id, + )); + return Ok(()); + } + } + + (*info_sent.lock().await).push((msg.channel_id, msg.reply(ctx, "Starting...").await?.id)); + + let active = data.get::().expect(EXPECTED_ACTIVE); + + active.store(true, Ordering::Relaxed); + + for (channel_id, message_id) in info_sent.lock().await.iter() { + ctx.http + .delete_message((*channel_id).into(), (*message_id).into()) + .await?; + } + (*info_sent.lock().await).clear(); + + Ok(()) +} + +#[command] +#[only_in(guilds)] +#[owners_only] +#[num_args(1)] +#[usage("weave (false | f | true | t)")] +/// Tells the bot whether or not to copy from threads. Default is true. +async fn weave(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { + let data = ctx.data.read().await; + let info_sent = data + .get::() + .expect("Expected InfoSent in TypeMap."); + for arg in args.iter::() { + let arg = arg.expect("Failed to read argument."); + if arg == "false" || arg == "f" { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "I will not copy from threads.").await?.id, + )); + let weave = data.get::().expect(EXPECTED_ACTIVE); + weave.store(false, Ordering::Relaxed); + } else if arg == "true" || arg == "t" { + (*info_sent.lock().await).push(( + msg.channel_id, + msg.reply(ctx, "I will weave the threads into a beautiful tapestry.") + .await? + .id, + )); + let weave = data.get::().expect(EXPECTED_ACTIVE); + weave.store(true, Ordering::Relaxed); + } + } + + Ok(()) +} diff --git a/src/handler.rs b/src/handler.rs new file mode 100644 index 0000000..cb31b09 --- /dev/null +++ b/src/handler.rs @@ -0,0 +1,380 @@ +use std::{str::FromStr, sync::atomic::Ordering}; + +use serde_json::Value; +use serenity::{ + async_trait, + client::{Context, EventHandler}, + http::{ + request::{Request, RequestBuilder}, + routing::RouteInfo, + }, + model::{ + channel::{Channel, ChannelType, Message}, + id::ChannelId, + id::GuildId, + }, +}; + +use crate::track::{err_strs::*, Active, Sent, SourceChannel, TargetChannel, Weave}; + +macro_rules! create_embed { + ($embed:expr, $message:expr, $channel_name:expr) => {{ + $embed.author(|author| { + author + .icon_url($message.author.face()) + .name(format!["in #{}\n", $channel_name]) + .url(format![ + "https://discord.com/channels/{}/{}", + $message + .guild_id + .expect("Unsure how we got here, but we did"), + $message.channel_id + ]); + author + }); + if let Some(replied_to) = $message.referenced_message { + $embed + .title(format![ + "@{}: {}", + replied_to.author.name, + if replied_to.content.chars().count() > 48 { + format![ + "{}...", + replied_to.content.chars().take(64).collect::() + ] + } else { + replied_to.content.clone() + } + ]) + .url(format![ + "https://discord.com/channels/{}/{}/{}", + replied_to + .guild_id + .expect("Really unsure how we got here, but we're here."), + replied_to.channel_id, + replied_to.id + ]); + } + #[allow(clippy::invisible_characters)] + if !$message.content.is_empty() { + $embed + .description(format![ + "<@{}> [➤➤➤](https://discord.com/channels/{}/{}/{})\n{}", + $message.author.id, + $message + .guild_id + .expect("Unsure how we got here, but we did"), + $message.channel_id, + $message.id, + $message.timestamp.to_string() + ]) + .field( + "​", + $message.content.chars().take(1024).collect::(), + true, + ); + if $message.content.chars().count() > 1024 { + #[allow(clippy::invisible_characters)] + $embed.field( + "​", + $message + .content + .chars() + .skip(1024) + .take(1024) + .collect::(), + true, + ); + } + } + if !$message.attachments.is_empty() { + let mut s = String::new(); + for attachment in $message.attachments { + s = format!["{}\n{}", s, attachment.url]; + } + let mut size = 0usize; + let mut stopped_at = None; + $embed.field( + "__Attachments__", + s.lines() + .enumerate() + .map(|(n, line)| { + if stopped_at.is_none() { + if (size + line.chars().count()) > 1024 { + stopped_at = Some(n); + "" + } else { + size += line.chars().count(); + line + } + } else { + "" + } + }) + .collect::(), + false, + ); + if let Some(n) = stopped_at { + #[allow(clippy::invisible_characters)] + $embed.field("​", s.lines().skip(n + 1).collect::(), true); + } + } + $embed + }}; +} + +#[derive(Default)] +pub struct Handler; + +#[async_trait] +impl EventHandler for Handler { + fn message<'life0, 'async_trait>( + &'life0 self, + ctx: Context, + new_message: Message, + ) -> core::pin::Pin + core::marker::Send + 'async_trait>> + where + 'life0: 'async_trait, + Self: 'async_trait, + { + Box::pin(async move { + let data = ctx.data.read().await; + if data + .get::() + .expect(EXPECTED_ACTIVE) + .load(Ordering::Relaxed) + { + let source = if let Some(source) = *data + .get::() + .expect(EXPECTED_SOURCE_CHANNEL) + .lock() + .await + { + source + } else { + return; + }; + let weave = data + .get::() + .expect(EXPECTED_ACTIVE) + .load(Ordering::Relaxed); + + if source != new_message.channel_id { + if weave { + let request = RequestBuilder::new(RouteInfo::GetChannel { + channel_id: new_message.channel_id.into(), + }); + let request = Request::new(request); + if let Ok(response) = ctx.http.request(request).await { + if let Ok(response) = response.json::().await { + if let Some(parent_id) = response.get("parent_id") { + if let Ok(parent_id) = ChannelId::from_str(&format![ + "<#{}>", + parent_id.as_str().unwrap_or("") + ]) { + if source != parent_id { + return; + } + } else { + return; + }; + } else { + return; + }; + } else { + #[cfg(debug)] + println!["Failed to deserialize response"]; + return; + }; + } else { + #[cfg(debug)] + println!["Failed to get channel information"]; + return; + }; + } else { + return; + } + } + + let target = if let Some(target) = *data + .get::() + .expect(EXPECTED_SOURCE_CHANNEL) + .lock() + .await + { + target + } else { + return; + }; + let channel_name = + if let Ok(Channel::Guild(gc)) = new_message.channel_id.to_channel(&ctx.http).await { + if gc.kind == ChannelType::PrivateThread { + return; + } + gc.name + } else { + return; + }; + let msg = target + .send_message(&ctx.http, |m| { + m.embed(|e| { + let e = create_embed![e, new_message, channel_name]; + e + }); + m + }) + .await + .expect("Failed to send message"); + let sent = data.get::().expect("Expected Sent in TypeMap"); + (*sent.lock().await).insert((source, new_message.id), (target, msg.id)); + } + }) + } + + fn message_delete<'life0, 'async_trait>( + &'life0 self, + ctx: Context, + event_channel_id: ChannelId, + deleted_message_id: serenity::model::id::MessageId, + _guild_id: Option, + ) -> core::pin::Pin + core::marker::Send + 'async_trait>> + where + 'life0: 'async_trait, + Self: 'async_trait, + { + Box::pin(async move { + let data = ctx.data.read().await; + let sent = data + .get::() + .expect("Expected Sent in TypeMap") + .clone(); + if let Some((channel_id, message_id)) = + (*sent.lock().await).remove(&(event_channel_id, deleted_message_id)) + { + ctx.http + .delete_message(channel_id.into(), message_id.into()) + .await + .expect("Failed to delete message"); + }; + }) + } + + fn message_delete_bulk<'life0, 'async_trait>( + &'life0 self, + ctx: Context, + event_channel_id: ChannelId, + multiple_deleted_messages_ids: Vec, + _guild_id: Option, + ) -> core::pin::Pin + core::marker::Send + 'async_trait>> + where + 'life0: 'async_trait, + Self: 'async_trait, + { + Box::pin(async move { + let data = ctx.data.read().await; + let sent = data.get::().expect("Expected Sent in TypeMap"); + for deleted_message_id in multiple_deleted_messages_ids { + if let Some((channel_id, message_id)) = + (*sent.lock().await).remove(&(event_channel_id, deleted_message_id)) + { + ctx.http + .delete_message(channel_id.into(), message_id.into()) + .await + .expect("Failed to delete message"); + } + } + }) + } + + fn message_update<'life0, 'async_trait>( + &'life0 self, + ctx: Context, + _old_if_available: Option, + new: Option, + event: serenity::model::event::MessageUpdateEvent, + ) -> core::pin::Pin + core::marker::Send + 'async_trait>> + where + 'life0: 'async_trait, + Self: 'async_trait, + { + Box::pin(async move { + let data = ctx.data.read().await; + let sent = data + .get::() + .expect("Expected Sent in TypeMap") + .clone(); + let channel_name = if let Some(nym) = event.channel_id.name(&ctx).await { + nym + } else { + return; + }; + if let Some(new) = new { + if let Some((channel_id, message_id)) = + (*sent.lock().await).get(&(event.channel_id, event.id)) + { + channel_id + .edit_message(ctx.http, *message_id, |m| { + m.embed(|e| { + let e = create_embed![e, new, channel_name]; + e + }); + m + }) + .await + .expect("Failed to delete message"); + } + } + }) + } + + // fn ready<'life0, 'async_trait>( + // &'life0 self, + // _ctx: Context, + // _data_about_bot: serenity::model::prelude::Ready, + // ) -> core::pin::Pin + core::marker::Send + 'async_trait>> + // where + // 'life0: 'async_trait, + // Self: 'async_trait, + // { + // Box::pin(async move { + // let __self = self; + // let _ctx = _ctx; + // let _data_about_bot = _data_about_bot; + // let _: () = {}; + // }) + // } + + // fn thread_create<'life0, 'async_trait>( + // &'life0 self, + // _ctx: Context, + // _thread: serenity::model::channel::GuildChannel, + // ) -> core::pin::Pin + core::marker::Send + 'async_trait>> + // where + // 'life0: 'async_trait, + // Self: 'async_trait, + // { + // Box::pin(async move { + // let __self = self; + // let _ctx = _ctx; + // let _thread = _thread; + // let _: () = {}; + // }) + // } + + // fn thread_delete<'life0, 'async_trait>( + // &'life0 self, + // _ctx: Context, + // _thread: serenity::model::channel::PartialGuildChannel, + // ) -> core::pin::Pin + core::marker::Send + 'async_trait>> + // where + // 'life0: 'async_trait, + // Self: 'async_trait, + // { + // Box::pin(async move { + // let __self = self; + // let _ctx = _ctx; + // let _thread = _thread; + // let _: () = {}; + // }) + // } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2e13c4e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,72 @@ +#![feature(in_band_lifetimes)] +#![feature(async_closure)] +use serenity::client::Client; +use serenity::framework::standard::StandardFramework; +use serenity::futures::lock::Mutex; +use serenity::http::Http; + +use std::collections::{HashMap, HashSet}; +use std::env; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; + +mod commands; +mod handler; +mod track; + +use commands::*; +use handler::*; +use track::{err_strs, Active, InfoSent, Sent, SourceChannel, TargetChannel, Weave}; + +#[tokio::main] +async fn main() { + let token = env::var("MPSC_BOT_TOKEN").expect("token"); + + let http = Http::new_with_token(&token); + + let (owners, _) = match http.get_current_application_info().await { + Ok(info) => { + let mut owners = HashSet::new(); + if let Some(team) = info.team { + owners.insert(team.owner_user_id); + } else { + owners.insert(info.owner.id); + } + match http.get_current_user().await { + Ok(bot_id) => (owners, bot_id.id), + Err(why) => panic!("Could not access the bot id: {:?}", why), + } + } + Err(why) => panic!("Could not access application info: {:?}", why), + }; + + let framework = StandardFramework::new() + .configure(|c| { + c.with_whitespace(true) + .prefix("~") + .owners(owners) + .ignore_bots(true) + }) + .group(&GENERAL_GROUP); + + let mut client = Client::builder(token) + .event_handler(Handler::default()) + .framework(framework) + .await + .expect("Error creating client"); + + { + let mut data = client.data.write().await; + data.insert::(Arc::new(AtomicBool::new(false))); + data.insert::(Arc::new(Mutex::new(Vec::new()))); + data.insert::(Arc::new(Mutex::new(HashMap::new()))); + data.insert::(Arc::new(Mutex::new(None))); + data.insert::(Arc::new(Mutex::new(None))); + data.insert::(Arc::new(AtomicBool::new(true))); + } + + // start listening for events by starting a single shard + if let Err(why) = client.start().await { + println!("An error occurred while running the client: {:?}", why); + } +} diff --git a/src/track.rs b/src/track.rs new file mode 100644 index 0000000..89b8410 --- /dev/null +++ b/src/track.rs @@ -0,0 +1,52 @@ +use std::{ + collections::HashMap, + sync::{atomic::AtomicBool, Arc}, +}; + +use serenity::{ + futures::lock::Mutex, + model::id::{ChannelId, MessageId}, + prelude::TypeMapKey, +}; + +pub mod err_strs { + pub const EXPECTED_SOURCE_CHANNEL: &str = "Expected SourceChannel in TypeMap"; + pub const EXPECTED_TARGET_CHANNEL: &str = "Expected TargetChannel in TypeMap"; + pub const EXPECTED_ACTIVE: &str = "Expected Active in TypeMap"; +} + +pub struct Active; + +impl TypeMapKey for Active { + type Value = Arc; +} + +pub struct InfoSent; + +impl TypeMapKey for InfoSent { + type Value = Arc>>; +} + +pub struct Sent; + +impl TypeMapKey for Sent { + type Value = Arc>>; +} + +pub struct SourceChannel; + +impl TypeMapKey for SourceChannel { + type Value = Arc>>; +} + +pub struct TargetChannel; + +impl TypeMapKey for TargetChannel { + type Value = Arc>>; +} + +pub struct Weave; + +impl TypeMapKey for Weave { + type Value = Arc; +}