diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..027755f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,82 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'youmubot-osu'", + "cargo": { + "args": [ + "build", + "--bin=youmubot-osu", + "--package=youmubot-osu" + ], + "filter": { + "name": "youmubot-osu", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'youmubot-osu'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=youmubot-osu", + "--package=youmubot-osu" + ], + "filter": { + "name": "youmubot-osu", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'youmubot'", + "cargo": { + "args": [ + "build", + "--bin=youmubot", + "--package=youmubot" + ], + "filter": { + "name": "youmubot", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'youmubot'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=youmubot", + "--package=youmubot" + ], + "filter": { + "name": "youmubot", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index dba3942..1897b68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,6 +170,20 @@ dependencies = [ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "crc32fast" version = "1.2.0" @@ -315,6 +329,19 @@ name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -470,6 +497,18 @@ dependencies = [ "webpki-roots 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hyper-tls" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.5" @@ -641,6 +680,23 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "native-tls" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "net2" version = "0.2.33" @@ -691,6 +747,36 @@ name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "openssl" +version = "0.10.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.9.0" @@ -725,6 +811,11 @@ name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ppv-lite86" version = "0.2.6" @@ -917,9 +1008,17 @@ name = "regex-syntax" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "reqwest" -version = "0.9.22" +version = "0.9.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -932,9 +1031,11 @@ dependencies = [ "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1006,6 +1107,15 @@ name = "ryu" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "schannel" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "1.0.0" @@ -1020,6 +1130,25 @@ dependencies = [ "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "security-framework" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.9.0" @@ -1095,7 +1224,7 @@ dependencies = [ "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1181,6 +1310,19 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "threadpool" version = "1.7.1" @@ -1469,6 +1611,11 @@ name = "uwl" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vcpkg" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.5" @@ -1656,6 +1803,7 @@ dependencies = [ "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "rustbreak 2.0.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serenity 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1693,6 +1841,8 @@ dependencies = [ "checksum command_attr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5e331e10b98950feec95fd4e44c0dd240ddb91097d0cc0bd4d128502cd2d6d5d" "checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" "checksum cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" "checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" @@ -1711,6 +1861,8 @@ dependencies = [ "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" @@ -1726,6 +1878,7 @@ dependencies = [ "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" "checksum hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "719d85c7df4a7f309a77d145340a063ea929dcb2e025bae46a80345cffec2952" +"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" @@ -1748,16 +1901,21 @@ dependencies = [ "checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" "checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" @@ -1780,15 +1938,19 @@ dependencies = [ "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" -"checksum reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "2c2064233e442ce85c77231ebd67d9eca395207dec2127fe0bbedde4bd29a650" +"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +"checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" "checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac" "checksum rustbreak 2.0.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c185a2ede13fcb28feb6864ee9412a20f57bd83b4be18dc81fde4d6e786982" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" +"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" @@ -1807,6 +1969,7 @@ dependencies = [ "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" @@ -1838,6 +2001,7 @@ dependencies = [ "checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum uwl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dfabe4f248cd69106fdbea55ddd1a1d7aa7abd1a2b167ad62245b7a9306fb1f1" +"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" diff --git a/youmubot/Cargo.toml b/youmubot/Cargo.toml index c050d17..69b46f0 100644 --- a/youmubot/Cargo.toml +++ b/youmubot/Cargo.toml @@ -13,6 +13,7 @@ serde = { version = "1.0", features = ["derive"] } chrono = "0.4.9" rand = "0.7.2" static_assertions = "1.1.0" +reqwest = "0.9.24" [dependencies.rustbreak] version = "2.0.0-rc3" diff --git a/youmubot/src/commands/admin/mod.rs b/youmubot/src/commands/admin/mod.rs index 420b505..af92d32 100644 --- a/youmubot/src/commands/admin/mod.rs +++ b/youmubot/src/commands/admin/mod.rs @@ -4,7 +4,10 @@ use serenity::{ macros::{command, group}, Args, CommandResult, }, - model::{channel::Message, id::UserId}, + model::{ + channel::{Channel, Message}, + id::UserId, + }, }; use soft_ban::{SOFT_BAN_COMMAND, SOFT_BAN_INIT_COMMAND}; use std::{thread::sleep, time::Duration}; @@ -15,7 +18,6 @@ pub use soft_ban::watch_soft_bans; group!({ name: "admin", options: { - only_in: "guilds", description: "Administrative commands for the server.", }, commands: [clean, ban, kick, soft_ban, soft_ban_init], @@ -24,7 +26,7 @@ group!({ #[command] #[aliases("cleanall")] #[required_permissions(MANAGE_MESSAGES)] -#[description = "Clean at most X latest messages from the current channel. Defaults to 10."] +#[description = "Clean at most X latest messages from the current channel (only clean Youmu's messages in DMs). Defaults to 10."] #[usage = "clean 50"] #[min_args(0)] #[max_args(1)] @@ -33,11 +35,25 @@ fn clean(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { let messages = msg .channel_id .messages(&ctx.http, |b| b.before(msg.id).limit(limit))?; - msg.channel_id.delete_messages(&ctx.http, messages.iter())?; + let channel = msg.channel_id.to_channel(&ctx)?; + match &channel { + Channel::Private(_) | Channel::Group(_) => { + let self_id = ctx.http.get_current_application_info()?.id; + messages + .into_iter() + .filter(|v| v.author.id == self_id) + .try_for_each(|m| m.delete(&ctx))?; + } + _ => { + msg.channel_id + .delete_messages(&ctx.http, messages.into_iter())?; + } + }; msg.react(&ctx, "šŸŒ‹")?; - - sleep(Duration::from_secs(2)); - msg.delete(&ctx)?; + if let Channel::Guild(_) = &channel { + sleep(Duration::from_secs(2)); + msg.delete(&ctx)?; + } Ok(()) } @@ -48,6 +64,7 @@ fn clean(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { #[usage = "ban user#1234 spam"] #[min_args(1)] #[max_args(2)] +#[only_in("guilds")] fn ban(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { let user = args.single::()?.to_user(&ctx)?; let reason = args @@ -73,6 +90,7 @@ fn ban(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { #[usage = "kick user#1234 spam"] #[min_args(1)] #[max_args(2)] +#[only_in("guilds")] fn kick(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { let user = args.single::()?.to_user(&ctx)?; let reason = args diff --git a/youmubot/src/commands/admin/soft_ban.rs b/youmubot/src/commands/admin/soft_ban.rs index d997dea..ef613fa 100644 --- a/youmubot/src/commands/admin/soft_ban.rs +++ b/youmubot/src/commands/admin/soft_ban.rs @@ -20,6 +20,7 @@ use std::cmp::max; #[example = "user#1234 5s"] #[min_args(1)] #[max_args(2)] +#[only_in("guilds")] pub fn soft_ban(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { let user = args.single::()?.to_user(&ctx)?; let duration = if args.is_empty() { @@ -84,6 +85,7 @@ pub fn soft_ban(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResu #[description = "Sets up the soft-ban command. This command can only be run once.\nThe soft-ban command assigns a role, temporarily, to a user."] #[usage = "{soft_ban_role_id}"] #[num_args(1)] +#[only_in("guilds")] pub fn soft_ban_init(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { let role_id = args.single::()?; let guild = msg.guild(&ctx).ok_or(Error::from("Guild-only command"))?; diff --git a/youmubot/src/commands/community/mod.rs b/youmubot/src/commands/community/mod.rs new file mode 100644 index 0000000..f82b39f --- /dev/null +++ b/youmubot/src/commands/community/mod.rs @@ -0,0 +1,102 @@ +use rand::{ + distributions::{Distribution, Uniform}, + thread_rng, +}; +use serenity::prelude::*; +use serenity::{ + framework::standard::{ + macros::{command, group}, + Args, CommandError as Error, CommandResult, + }, + model::{ + channel::{Channel, Message}, + user::OnlineStatus, + }, + utils::MessageBuilder, +}; + +mod votes; + +use votes::VOTE_COMMAND; + +group!({ + name: "community", + options: { + only_in: "guilds", + description: "Community related commands. Usually comes with some sort of delays, since it involves pinging", + }, + commands: [choose, vote], +}); + +#[command] +#[description = r"šŸ‘‘ Randomly choose an active member and mention them! +Note that only online/idle users in the channel are chosen from."] +#[usage = "[title = the chosen one]"] +#[example = "the strongest in Gensokyo"] +#[bucket = "community"] +#[max_args(1)] +pub fn choose(ctx: &mut Context, m: &Message, mut args: Args) -> CommandResult { + let title = if args.is_empty() { + "the chosen one".to_owned() + } else { + args.single::()? + }; + + let users: Result, Error> = { + let guild = m.guild(&ctx).unwrap(); + let guild = guild.read(); + let presences = &guild.presences; + let channel = m.channel_id.to_channel(&ctx)?; + if let Channel::Guild(channel) = channel { + let channel = channel.read(); + Ok(channel + .members(&ctx)? + .into_iter() + .filter(|v| !v.user.read().bot) + .map(|v| v.user_id()) + .filter(|v| { + presences + .get(v) + .map(|presence| { + presence.status == OnlineStatus::Online + || presence.status == OnlineStatus::Idle + }) + .unwrap_or(false) + }) + .collect()) + } else { + panic!() + } + }; + let users = users?; + + if users.len() < 2 { + m.reply( + &ctx, + "šŸ° Have this cake for yourself because no-one is here for the gods to pick.", + )?; + return Ok(()); + } + + let winner = { + let uniform = Uniform::from(0..users.len()); + let mut rng = thread_rng(); + &users[uniform.sample(&mut rng)] + }; + + m.channel_id.send_message(&ctx, |c| { + c.content( + MessageBuilder::new() + .push("šŸ‘‘ The Gensokyo gods have gathered around and decided, out of ") + .push_bold(format!("{}", users.len())) + .push(" potential prayers, ") + .push(winner.mention()) + .push(" will be ") + .push_bold_safe(title) + .push(". Congrats! šŸŽ‰ šŸŽŠ šŸ„³") + .build(), + ) + })?; + + Ok(()) +} diff --git a/youmubot/src/commands/fun/votes.rs b/youmubot/src/commands/community/votes.rs similarity index 81% rename from youmubot/src/commands/fun/votes.rs rename to youmubot/src/commands/community/votes.rs index 4610b9e..b324736 100644 --- a/youmubot/src/commands/fun/votes.rs +++ b/youmubot/src/commands/community/votes.rs @@ -30,11 +30,8 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { return Ok(()); } let question = args.single::()?; - let (choices, reactions) = if args.is_empty() { - ( - vec!["Yes! šŸ˜".to_owned(), "No! šŸ¤¢".to_owned()], - vec!["šŸ˜", "šŸ¤¢"], - ) + let choices = if args.is_empty() { + vec![("šŸ˜", "Yes! šŸ˜".to_owned()), ("šŸ¤¢", "No! šŸ¤¢".to_owned())] } else { let choices: Vec<_> = args.iter().map(|v| v.unwrap()).collect(); if choices.len() < 2 { @@ -57,14 +54,15 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { return Ok(()); } - let reactions = pick_n_reactions(choices.len())?; - (choices, reactions) + pick_n_reactions(choices.len())? + .into_iter() + .zip(choices.into_iter()) + .collect() }; let fields: Vec<_> = { choices .iter() - .zip(reactions.iter()) .map(|(choice, reaction)| { ( MessageBuilder::new().push_bold_safe(choice).build(), @@ -92,12 +90,14 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { })?; msg.delete(&ctx)?; // React on all the choices - reactions.iter().try_for_each(|v| panel.react(&ctx, *v))?; + choices + .iter() + .try_for_each(|(v, _)| panel.react(&ctx, *v))?; // Start sleeping thread::sleep(duration.to_std()?); - let result = collect_reactions(ctx, panel, &reactions, &choices)?; + let result = collect_reactions(ctx, &panel, &choices)?; if result.len() == 0 { msg.reply( &ctx, @@ -136,7 +136,7 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { }) })?; } - msg.delete(&ctx)?; + panel.delete(&ctx)?; Ok(()) // unimplemented!(); @@ -145,41 +145,33 @@ pub fn vote(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { // Collect reactions and store them as a map from choice to fn collect_reactions<'a>( ctx: &mut Context, - msg: Message, - reaction_emojis: &[&'static str], - choices: &'a [String], + msg: &Message, + choices: &'a [(&'static str, String)], ) -> Result)>, Error> { // Get a brand new version of the Message let reactions = msg.channel_id.message(&ctx, msg.id)?.reactions; - let reaction_to_choice: Map<_, _> = reaction_emojis + let reaction_to_choice: Map<_, _> = choices.into_iter().map(|r| (r.0, &r.1)).collect(); + let mut vec: Vec<(&str, Vec)> = Vec::new(); + reactions .into_iter() - .zip(choices.into_iter()) - .collect(); - let result: Result, Error> = { - let mut vec: Vec<(&str, Vec)> = Vec::new(); - reactions - .into_iter() - .filter_map(|r| { - if let ReactionType::Unicode(ref v) = r.reaction_type { - reaction_to_choice - .get(&&v[..]) - .cloned() - .filter(|_| r.count > 1) - .map(|choice| (r.clone(), choice)) - } else { - None - } - }) - .try_for_each(|(r, choice)| -> Result<_, Error> { - let users = collect_reaction_users(ctx, &msg, &r)?; - vec.push((choice, users)); - Ok(()) - })?; - vec.sort_by(|(_, b): &(_, Vec<_>), (_, d)| d.len().cmp(&b.len())); - Ok(vec) - }; - let result = result?; - Ok(result) + .filter_map(|r| { + if let ReactionType::Unicode(ref v) = r.reaction_type { + reaction_to_choice + .get(&&v[..]) + .cloned() + .filter(|_| r.count > 1) + .map(|choice| (r.clone(), choice)) + } else { + None + } + }) + .try_for_each(|(r, choice)| -> Result<_, Error> { + let users = collect_reaction_users(ctx, &msg, &r)?; + vec.push((choice, users)); + Ok(()) + })?; + vec.sort_by(|(_, b): &(_, Vec<_>), (_, d)| d.len().cmp(&b.len())); + Ok(vec) } fn collect_reaction_users( diff --git a/youmubot/src/commands/fun/images.rs b/youmubot/src/commands/fun/images.rs new file mode 100644 index 0000000..bfd2229 --- /dev/null +++ b/youmubot/src/commands/fun/images.rs @@ -0,0 +1,101 @@ +use crate::http::HTTP; +use reqwest::Client as HTTPClient; +use serde::Deserialize; +use serenity::framework::standard::CommandError as Error; +use serenity::prelude::*; +use serenity::{ + framework::standard::{ + macros::{check, command}, + Args, CheckResult, CommandOptions, CommandResult, Reason, + }, + model::channel::{Channel, Message}, +}; +use std::string::ToString; + +#[command] +#[checks(nsfw)] +#[description = "šŸ–¼ļø Find an image with a given tag on Danbooru[nsfw]!"] +#[min_args(1)] +#[bucket("images")] +pub fn nsfw(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult { + message_command(ctx, msg, args, Rating::Explicit) +} + +#[command] +#[description = "šŸ–¼ļø Find an image with a given tag on Danbooru[safe]!"] +#[min_args(1)] +#[bucket("images")] +pub fn image(ctx: &mut Context, msg: &Message, args: Args) -> CommandResult { + message_command(ctx, msg, args, Rating::Safe) +} + +#[check] +#[name = "nsfw"] +fn nsfw_check(ctx: &mut Context, msg: &Message, _: &mut Args, _: &CommandOptions) -> CheckResult { + let channel = msg.channel_id.to_channel(&ctx).unwrap(); + if !(match channel { + Channel::Guild(guild_channel) => guild_channel.read().nsfw, + _ => true, + }) { + CheckResult::Failure(Reason::User("šŸ˜£ YOU FREAKING PERVERT!!!".to_owned())) + } else { + CheckResult::Success + } +} + +fn message_command(ctx: &mut Context, msg: &Message, args: Args, rating: Rating) -> CommandResult { + let tags = args.remains().unwrap_or("touhou"); + let http = ctx.data.read(); + let http = http.get::().unwrap(); + let image = get_image(http, rating, tags)?; + match image { + None => msg.reply(&ctx, "šŸ–¼ļø No image found...\nšŸ’” Tip: In danbooru, character names follow Japanese standards (last name before first name), so **Hakurei Reimu** might give you an image while **Reimu Hakurei** won't."), + Some(url) => msg.reply( + &ctx, + format!("šŸ–¼ļø Here's the image you requested!\n\n{}", url), + ), + }?; + Ok(()) +} + +// Gets an image URL. +fn get_image(client: &HTTPClient, rating: Rating, tags: &str) -> Result, Error> { + // Fix the tags: change whitespaces to + + let tags = tags.split_whitespace().collect::>().join("_"); + let req = client + .get(&format!( + "https://danbooru.donmai.us/posts.json?tags=rating:{}+{}", + rating.to_string(), + tags + )) + .query(&[("limit", "1"), ("random", "true")]) + .build()?; + println!("{:?}", req.url()); + let response: Vec = client.execute(req)?.json()?; + Ok(response + .into_iter() + .next() + .map(|v| format!("https://danbooru.donmai.us/posts/{}", v.id))) +} + +#[derive(Deserialize, Debug)] +struct PostResponse { + id: u64, +} + +#[derive(Copy, Clone, Debug)] +enum Rating { + Explicit, + Safe, +} + +impl ToString for Rating { + fn to_string(&self) -> String { + use Rating::*; + match self { + Explicit => "explicit", + Safe => "safe", + } + .to_owned() + } +} diff --git a/youmubot/src/commands/fun/mod.rs b/youmubot/src/commands/fun/mod.rs index f1645b5..7e80a3d 100644 --- a/youmubot/src/commands/fun/mod.rs +++ b/youmubot/src/commands/fun/mod.rs @@ -12,17 +12,17 @@ use serenity::{ utils::MessageBuilder, }; +mod images; mod names; -mod votes; -use votes::VOTE_COMMAND; +use images::*; group!({ name: "fun", options: { description: "Random commands", }, - commands: [roll, pick, name, vote], + commands: [roll, pick, name, image, nsfw], }); #[command] diff --git a/youmubot/src/commands/mod.rs b/youmubot/src/commands/mod.rs index e14bd5f..b323cbb 100644 --- a/youmubot/src/commands/mod.rs +++ b/youmubot/src/commands/mod.rs @@ -10,9 +10,11 @@ use std::collections::HashSet; mod args; pub mod admin; +pub mod community; pub mod fun; pub use admin::ADMIN_GROUP; +pub use community::COMMUNITY_GROUP; pub use fun::FUN_GROUP; // A help command diff --git a/youmubot/src/http.rs b/youmubot/src/http.rs new file mode 100644 index 0000000..b065ab5 --- /dev/null +++ b/youmubot/src/http.rs @@ -0,0 +1,7 @@ +use serenity::prelude::TypeMapKey; + +pub(crate) struct HTTP; + +impl TypeMapKey for HTTP { + type Value = reqwest::Client; +} diff --git a/youmubot/src/main.rs b/youmubot/src/main.rs index b15df35..b122331 100644 --- a/youmubot/src/main.rs +++ b/youmubot/src/main.rs @@ -1,5 +1,6 @@ use dotenv; use dotenv::var; +use reqwest; use serenity::{ framework::standard::{DispatchError, StandardFramework}, model::gateway, @@ -8,6 +9,7 @@ use serenity::{ mod commands; mod db; +mod http; struct Handler; @@ -33,6 +35,11 @@ fn main() { // Setup initial data db::setup_db(&mut client).expect("Setup db should succeed"); + // Setup shared instances of things + { + let mut data = client.data.write(); + data.insert::(reqwest::Client::new()); + } // Create handler threads std::thread::spawn(commands::admin::watch_soft_bans(&mut client)); @@ -107,9 +114,14 @@ fn setup_framework(mut client: Client) -> Client { .bucket("voting", |c| { c.delay(120 /* 2 minutes */).time_span(120).limit(1) }) + .bucket("images", |c| c.time_span(60).limit(2)) + .bucket("community", |c| { + c.delay(30).time_span(30).limit(1) + }) // groups here .group(&commands::ADMIN_GROUP) - .group(&commands::FUN_GROUP), + .group(&commands::FUN_GROUP) + .group(&commands::COMMUNITY_GROUP), ); client }