diff --git a/.sqlx/query-189b3e7e5d7266e69840d547e9e5ef70a267bcd170e4b6887bef35c35c8a3531.json b/.sqlx/query-189b3e7e5d7266e69840d547e9e5ef70a267bcd170e4b6887bef35c35c8a3531.json new file mode 100644 index 000000000..0ad8ad33e --- /dev/null +++ b/.sqlx/query-189b3e7e5d7266e69840d547e9e5ef70a267bcd170e4b6887bef35c35c8a3531.json @@ -0,0 +1,94 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n metadata.id, metadata.track, metadata.artist, metadata.album, metadata.date, metadata.channels, metadata.channel, metadata.start_time, metadata.duration, metadata.sample_rate, metadata.source_url, metadata.title, metadata.thumbnail\n FROM\n metadata\n WHERE\n metadata.source_url = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "track", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "artist", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "album", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "date", + "type_info": "Date" + }, + { + "ordinal": 5, + "name": "channels", + "type_info": "Int2" + }, + { + "ordinal": 6, + "name": "channel", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "start_time", + "type_info": "Int8" + }, + { + "ordinal": 8, + "name": "duration", + "type_info": "Int8" + }, + { + "ordinal": 9, + "name": "sample_rate", + "type_info": "Int4" + }, + { + "ordinal": 10, + "name": "source_url", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "title", + "type_info": "Text" + }, + { + "ordinal": 12, + "name": "thumbnail", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + true, + true, + true, + true, + true, + true, + false, + false, + true, + true, + true, + true + ] + }, + "hash": "189b3e7e5d7266e69840d547e9e5ef70a267bcd170e4b6887bef35c35c8a3531" +} diff --git a/.sqlx/query-218dd2a73bade44f75a7114b6e862bace731e72d782421fba20274255adf86a8.json b/.sqlx/query-97f349c2bc2f0b5d7e7cac0c06f412cc9bc50b1e5e3f00fd74ee83691bd020d2.json similarity index 88% rename from .sqlx/query-218dd2a73bade44f75a7114b6e862bace731e72d782421fba20274255adf86a8.json rename to .sqlx/query-97f349c2bc2f0b5d7e7cac0c06f412cc9bc50b1e5e3f00fd74ee83691bd020d2.json index 12bd217bb..5a73cfca6 100644 --- a/.sqlx/query-218dd2a73bade44f75a7114b6e862bace731e72d782421fba20274255adf86a8.json +++ b/.sqlx/query-97f349c2bc2f0b5d7e7cac0c06f412cc9bc50b1e5e3f00fd74ee83691bd020d2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n metadata.id, metadata.track, metadata.artist, metadata.album, metadata.date, metadata.channels, metadata.channel, metadata.start_time, metadata.duration, metadata.sample_rate, metadata.source_url, metadata.title, metadata.thumbnail\n FROM \n metadata\n WHERE \n metadata.source_url = $1\n ", + "query": "\n SELECT\n metadata.id, metadata.track, metadata.artist, metadata.album, metadata.date, metadata.channels, metadata.channel, metadata.start_time, metadata.duration, metadata.sample_rate, metadata.source_url, metadata.title, metadata.thumbnail\n FROM\n metadata\n WHERE\n metadata.source_url = $1\n ", "describe": { "columns": [ { @@ -90,5 +90,5 @@ true ] }, - "hash": "218dd2a73bade44f75a7114b6e862bace731e72d782421fba20274255adf86a8" + "hash": "97f349c2bc2f0b5d7e7cac0c06f412cc9bc50b1e5e3f00fd74ee83691bd020d2" } diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..69344df3e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,192 @@ +# Change Log + +## TODO: + +- [ ] /changenicks command. Renames all users in the guild + to a random nick name from a themed list of names. Use your + own custom list, or choose from one of the many I've + pre-curated and use in my own server. +- [ ] Test Coverage > 23%. +- [ ] Codebase architecture documentation. +- [ ] Support discordbotlist.com (voting service). + +## v0.3.10 (2024/07/28) + +- [x] performance improvements. +- [x] All milestones recorded as GitHub issues. +- [x] Add help option to all commands. +- [x] Added back in internal playlist support. + - [x] `/playlist create ` Creates a playlist with the given name + - [x] `/playlist delete ` Deletes a playlist with the given name + - [x] `/playlist addto ` Adds the currently playing song to + - [x] `/playlist list` List your playlists + - [x] `/playlist get ` displays the contents of + - [x] `/playlist pplay ` queues the given playlist on the bot + - [x] `/playlist loadspotify ` loads a spotify playlist into a Crack Tunes playlist. + +## ~~v0.3.9~~ +- internal testing version, publicly skipped +- i.e. git branches got fucked and this was easier + +## v0.3.8 (2024/07/17) + +- [x] Looked at rolling back to reqwest 2.11 because it was causing problems. + Decided to stick with 2.12 and keep using the forked and patched version + of serenity, poise, songbird, etc. +- [x] Pulled in songbird update to support soundcloud and streaming m8u3 files. +- [x] More refactoring. +- [x] Brainf\*\*k interpreter. +- [x] Switched all locks from blocking to non-blocking async. +- [x] Unify messaging module. +- [x] Fixed repeat bug when nothing is playing. +- [-] Change `let _ = send_reply(&ctx, msg, true).await?;` + to `ctx.send_reply(msg, true).await?;` (half done) + ... +For next version... + +## v0.3.7 (2024/05/29) + +- crackgpt 0.2.0! + Added back chatgpt support, which I am now self hosting for CrackTunes + and is backed by GPT 4o. +- Use the rusty_ytdl library as a first try, fallback to yt-dlp if it fails. +- Remove the grafana dashboard. +- Switch to async logging. +- Add an async service to handle the database (accept writes on a channel, + and write to the database in a separate thread). + Eventually this could be a seperate service (REST / GRPC). + +## v0.3.6 (2024/05/03) + +- Music channel setting (can lock music playing command and responses to a specific channel) +- Fixes in logging +- Fixes in admin commands +- Lots of refactoring code cleanup. + +## v0.3.5 (2024/04/23) + +- Significantly improved loading speed of songs into the queue. +- Fix Youtube Playlists. +- Lots of refactoring. +- Can load spotify playlists very quickly +- Option to vote for Crack Tunes on top.gg for 12 hours of premium access. + +## v0.3.4 + +- playlist loadspotify and playlist play commands +- Invite and voting links +- Updated serenity / poise / songbird to latest versions +- Refactored functions for creating embeds and sending messages to it's own module + +## v0.3.3 (2024/04/??) + +- `/loadspotify ` loads a spotify playlist into a Crack Tunes playlist. +- voting tracking + +## v0.3.2 (2024/03/27) + +- Playlists! +- Here are the available playlist commands + - `/playlist create ` Creates a playlist with the given name + - `/playlist delete ` Deletes a playlist with the given name + - `/playlist addto ` Adds the currently playing song to + - `/playlist list` List your playlists + - `/playlist get ` displays the contents of + - `/playlist play ` queues the given playlist on the bot +- Added pl alias for playlist +- Added /playlist list +- Fixed Requested by Field +- JSON for grafana dashboards + +## v0.3.1 (2024/03/21) + +- Fix the requesting user not always displaying +- Reversed order of this Change Log so newest stuff is on top + +## ~~0.3.0-rc.6~~ + +## 0.3.0 + +- Added more breakdown of features which can be optionally turned on/off +- Telemitry +- Metrics / logging +- Removed a lot of unescesarry dependencies + +## 0.1.4 (crack-osint) (2024/03/12) + +- osint scan command to check urls for malicious content + +## 0.3.0-rc.5 (2024/03/09) + +- cargo update +- GuildId checks +- user authorized message +- adding scan command +- add feature for osint +- make admin commands usable by guild members with admin +- add dry run to rename_all + +## 0.3.0-rc.4 + +- fix storing auto role and timeout I think +- download and skip together +- ~~try to finally fix this fucking volume bug~~ +- fix loading guild settings +- add pgadmin to docker compose +- ~~fix volume~~ (volume is still broken) + +## 0.3.0-rc.2 + +- [x] Clean command +- [x] Bug fixes +- ~~[ ] Down vote~~ (not working) + +## 0.3.0-rc.1 + +- [x] Dockerized! +- [x] Refactored settings commands. +- [x] Storing and retrieving settings from Postgres. +- [x] Updated dependencies to be in line with current. + +## ~~0.2.13~~ + +- ~~[] Port to next branch of serenity~~ +- ~~[] Flesh out admin commands~~ + +## ~~0.2.12~~ + +## ~~0.2.6~~ + +Didn't really track stuff here... + +## 0.2.5 + +- ~~[] Shuttle~~ +- ~~[] Reminders~~ +- ~~[] Notes~~ + +## 0.2.4 (2023/07/17) + +- [x] Bug fixes. +- [x] Remove reliance on slash commands everywhere. +- [x] Remove shuttle for now + +## 0.2.3 + +- [x] Bug fixes (volume) +- [x] Shuttle support (still broken) + +## 0.2.2 (2023/07/09 ish) + +- [x] Welcome Actions +- [x] Play on multiple servers at once + +## 0.2.1 (2023/07/02) + +- [x] Play music from local files + +## 0.2.0 + +- [x] Play music from YouTube +- [x] Play music from Spotify (kind of...) + diff --git a/Cargo.lock b/Cargo.lock index 43533978f..c8f5c2547 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anyhow" @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" dependencies = [ "flate2", "futures-core", @@ -180,7 +180,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -191,7 +191,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -449,7 +449,7 @@ checksum = "005fa0c5bd20805466dda55eb34cd709bb31a2592bb26927b47714eeed6914d8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "synstructure", ] @@ -504,7 +504,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "syn_derive", ] @@ -627,9 +627,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.4" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9711f33475c22aab363b05564a17d7b789bf3dfec5ebabb586adee56f0e271b5" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" [[package]] name = "cfg-if" @@ -712,6 +712,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "const_format" version = "0.2.32" @@ -800,7 +820,7 @@ dependencies = [ [[package]] name = "crack-core" -version = "0.3.8" +version = "0.3.10" dependencies = [ "anyhow", "async-trait", @@ -809,6 +829,7 @@ dependencies = [ "bytes", "chrono", "colored", + "const-random", "crack-bf", "crack-gpt", "crack-osint", @@ -885,7 +906,7 @@ dependencies = [ [[package]] name = "cracktunes" -version = "0.3.8" +version = "0.3.10" dependencies = [ "config-file", "crack-core", @@ -957,6 +978,12 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1003,7 +1030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1013,7 +1040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1061,7 +1088,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1083,7 +1110,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1206,7 +1233,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1216,7 +1243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core 0.20.0", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1229,7 +1256,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1262,7 +1289,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1301,7 +1328,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1349,7 +1376,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1369,7 +1396,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1381,7 +1408,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1590,7 +1617,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1894,20 +1921,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever 0.11.0", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "html5ever" version = "0.27.0" @@ -1916,10 +1929,10 @@ checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", - "markup5ever 0.12.1", + "markup5ever", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2044,7 +2057,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "hyper-util", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-native-certs 0.7.1", "rustls-pki-types", "tokio", @@ -2438,7 +2451,7 @@ version = "0.1.7" source = "git+https://github.com/cycle-five/spotify-player?branch=master#283e56595747b59716b18752ad8b195478c86482" dependencies = [ "anyhow", - "html5ever 0.27.0", + "html5ever", "log", "markup5ever_rcdom", "reqwest", @@ -2461,20 +2474,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - [[package]] name = "markup5ever" version = "0.12.1" @@ -2495,8 +2494,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" dependencies = [ - "html5ever 0.27.0", - "markup5ever 0.12.1", + "html5ever", + "markup5ever", "tendril", "xml5ever", ] @@ -2518,7 +2517,7 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2585,13 +2584,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2618,7 +2618,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2811,7 +2811,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2825,9 +2825,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] @@ -2954,7 +2954,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.2", + "redox_syscall 0.5.3", "smallvec", "windows-targets 0.52.6", ] @@ -3058,7 +3058,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3096,7 +3096,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3156,7 +3156,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3194,7 +3194,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3216,9 +3216,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "postcard" @@ -3251,9 +3251,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ "anstyle", "predicates-core", @@ -3261,15 +3261,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", @@ -3439,7 +3439,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.11", + "rustls 0.23.12", "thiserror", "tokio", "tracing", @@ -3455,7 +3455,7 @@ dependencies = [ "rand", "ring 0.17.8", "rustc-hash", - "rustls 0.23.11", + "rustls 0.23.12", "slab", "thiserror", "tinyvec", @@ -3464,14 +3464,13 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" dependencies = [ "libc", "once_cell", "socket2", - "tracing", "windows-sys 0.52.0", ] @@ -3559,9 +3558,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags 2.6.0", ] @@ -3660,7 +3659,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-native-certs 0.7.1", "rustls-pemfile 2.1.2", "rustls-pki-types", @@ -4000,21 +3999,21 @@ dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.5", + "rustls-webpki 0.102.6", "subtle", "zeroize", ] [[package]] name = "rustls" -version = "0.23.11" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.5", + "rustls-webpki 0.102.6", "subtle", "zeroize", ] @@ -4081,9 +4080,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.5" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -4193,15 +4192,15 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scraper" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b80b33679ff7a0ea53d37f3b39de77ea0c75b12c5805ac43ec0c33b3051af1b" +checksum = "761fb705fdf625482d2ed91d3f0559dcfeab2798fe2771c69560a774865d0802" dependencies = [ "ahash 0.8.11", "cssparser", "ego-tree", "getopts", - "html5ever 0.26.0", + "html5ever", "once_cell", "selectors", "tendril", @@ -4316,9 +4315,9 @@ dependencies = [ [[package]] name = "serde_cow" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e84ce5596a72f0c4c60759a10ff8c22d5eaf227b0dc2789c8746193309058b" +checksum = "1e7bbbec7196bfde255ab54b65e34087c0849629280028238e67ee25d6a4b7da" dependencies = [ "serde", ] @@ -4331,7 +4330,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -4364,7 +4363,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -4550,8 +4549,8 @@ dependencies = [ [[package]] name = "songbird" -version = "0.4.2" -source = "git+https://github.com/cycle-five/songbird?branch=current#7277997b348d78d319a05eee48f814598168f4a4" +version = "0.4.3" +source = "git+https://github.com/cycle-five/songbird?branch=current#a0b2901ba6089054e1fab29ffea26ed377662c43" dependencies = [ "async-trait", "audiopus", @@ -4583,7 +4582,7 @@ dependencies = [ "symphonia", "symphonia-core", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite 0.21.0", "tokio-util", "tracing", "tracing-futures", @@ -4983,7 +4982,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5200,9 +5199,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -5218,7 +5217,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5235,7 +5234,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5332,22 +5331,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5393,6 +5392,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.7.2" @@ -5421,31 +5429,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5476,16 +5483,16 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.11", + "rustls 0.23.12", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-socks" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", @@ -5528,8 +5535,12 @@ checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", + "rustls 0.22.4", + "rustls-pki-types", "tokio", + "tokio-rustls 0.25.0", "tungstenite 0.21.0", + "webpki-roots 0.26.3", ] [[package]] @@ -5540,7 +5551,7 @@ checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -5572,9 +5583,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" [[package]] name = "toml_edit" @@ -5645,7 +5656,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5841,6 +5852,8 @@ dependencies = [ "httparse", "log", "rand", + "rustls 0.22.4", + "rustls-pki-types", "sha1", "thiserror", "url", @@ -5860,7 +5873,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.11", + "rustls 0.23.12", "rustls-pki-types", "sha1", "thiserror", @@ -5948,7 +5961,7 @@ checksum = "905e88c2a4cc27686bd57e495121d451f027e441388a67f773be729ad4be1ea8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6109,7 +6122,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6141,9 +6154,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -6228,7 +6241,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -6262,7 +6275,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6605,7 +6618,7 @@ checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" dependencies = [ "log", "mac", - "markup5ever 0.12.1", + "markup5ever", ] [[package]] @@ -6628,7 +6641,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "synstructure", ] @@ -6649,7 +6662,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6669,7 +6682,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "synstructure", ] @@ -6699,5 +6712,5 @@ checksum = "fa94b6a91d81a9d96473412885b87d8fb677accc447cae54571f93313aebf109" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] diff --git a/Cargo.toml b/Cargo.toml index c81e73fd3..4ba394351 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ features = [ # Broken? try self-hosted? git = "https://github.com/cycle-five/songbird" branch = "current" -version = "0.4.2" +version = "0.4.3" features = ["driver", "serenity", "rustls", "receive", "builtin-queue"] [workspace.dependencies.symphonia] diff --git a/README.md b/README.md index c9ed17cfd..9647d2e05 100644 --- a/README.md +++ b/README.md @@ -126,176 +126,6 @@ docker build -t cracktunes . docker compose up -d ``` -# Change Log - -## TODO: - -- [ ] discordbotlist.com (voting service) -- [ ] Finish adding help option to all commands -- [ ] Update and make wider use of rusty_ytdlp. - -## v0.3.8 (2024/07/17) - -- [x] Looked at rolling back to reqwest 2.11 because it was causing problems. - Decided to stick with 2.12 and keep using the forked and patched version - of serenity, poise, songbird, etc. -- [x] Pulled in songbird update to support soundcloud and streaming m8u3 files. -- [x] More refactoring. -- [x] Brainf\*\*k interpreter. -- [x] Switched all locks from blocking to non-blocking async. -- [x] Unify messaging module. -- [x] Fixed repeat bug when nothing is playing. -- [-] Change `let _ = send_reply(&ctx, msg, true).await?;` - to `ctx.send_reply(msg, true).await?;` (half done) - ... -For next version... - -## v0.3.7 (2024/05/29) - -- crackgpt 0.2.0! - Added back chatgpt support, which I am now self hosting for CrackTunes - and is backed by GPT 4o. -- Use the rusty_ytdl library as a first try, fallback to yt-dlp if it fails. -- Remove the grafana dashboard. -- Switch to async logging. -- Add an async service to handle the database (accept writes on a channel, - and write to the database in a separate thread). - Eventually this could be a seperate service (REST / GRPC). - -## v0.3.6 (2024/05/03) - -- Music channel setting (can lock music playing command and responses to a specific channel) -- Fixes in logging -- Fixes in admin commands -- Lots of refactoring code cleanup. - -## v0.3.5 (2024/04/23) - -- Significantly improved loading speed of songs into the queue. -- Fix Youtube Playlists. -- Lots of refactoring. -- Can load spotify playlists very quickly -- Option to vote for Crack Tunes on top.gg for 12 hours of premium access. - -## v0.3.4 - -- playlist loadspotify and playlist play commands -- Invite and voting links -- Updated serenity / poise / songbird to latest versions -- Refactored functions for creating embeds and sending messages to it's own module - -## v0.3.3 (2024/04/??) - -- `/loadspotify ` loads a spotify playlist into a Crack Tunes playlist. -- voting tracking - -## v0.3.2 (2024/03/27) - -- Playlists! -- Here are the available playlist commands - - `/playlist create ` Creates a playlist with the given name - - `/playlist delete ` Deletes a playlist with the given name - - `/playlist addto ` Adds the currently playing song to - - `/playlist list` List your playlists - - `/playlist get ` displays the contents of - - `/playlist play ` queues the given playlist on the bot -- Added pl alias for playlist -- Added /playlist list -- Fixed Requested by Field -- JSON for grafana dashboards - -## v0.3.1 (2024/03/21) - -- Fix the requesting user not always displaying -- Reversed order of this Change Log so newest stuff is on top - -## ~~0.3.0-rc.6~~ - -## 0.3.0 - -- Added more breakdown of features which can be optionally turned on/off -- Telemitry -- Metrics / logging -- Removed a lot of unescesarry dependencies - -## 0.1.4 (crack-osint) (2024/03/12) - -- osint scan command to check urls for malicious content - -## 0.3.0-rc.5 (2024/03/09) - -- cargo update -- GuildId checks -- user authorized message -- adding scan command -- add feature for osint -- make admin commands usable by guild members with admin -- add dry run to rename_all - -## 0.3.0-rc.4 - -- fix storing auto role and timeout I think -- download and skip together -- ~~try to finally fix this fucking volume bug~~ -- fix loading guild settings -- add pgadmin to docker compose -- ~~fix volume~~ (volume is still broken) - -## 0.3.0-rc.2 - -- [x] Clean command -- [x] Bug fixes -- ~~[ ] Down vote~~ (not working) - -## 0.3.0-rc.1 - -- [x] Dockerized! -- [x] Refactored settings commands. -- [x] Storing and retrieving settings from Postgres. -- [x] Updated dependencies to be in line with current. - -## ~~0.2.13~~ - -- ~~[] Port to next branch of serenity~~ -- ~~[] Flesh out admin commands~~ - -## ~~0.2.12~~ - -## ~~0.2.6~~ - -Didn't really track stuff here... - -## 0.2.5 - -- ~~[] Shuttle~~ -- ~~[] Reminders~~ -- ~~[] Notes~~ - -## 0.2.4 (2023/07/17) - -- [x] Bug fixes. -- [x] Remove reliance on slash commands everywhere. -- [x] Remove shuttle for now - -## 0.2.3 - -- [x] Bug fixes (volume) -- [x] Shuttle support (still broken) - -## 0.2.2 (2023/07/09 ish) - -- [x] Welcome Actions -- [x] Play on multiple servers at once - -## 0.2.1 (2023/07/02) - -- [x] Play music from local files - -## 0.2.0 - -- [x] Play music from YouTube -- [x] Play music from Spotify (kind of...) -

Originally forked from Parrot

diff --git a/crack-core/Cargo.toml b/crack-core/Cargo.toml index fd7627dfa..3dd585ef3 100644 --- a/crack-core/Cargo.toml +++ b/crack-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crack-core" -version = "0.3.8" +version = "0.3.10" authors = ["Cycle Five "] edition = "2021" description = "Core module for the cracking smoking, discord-music-bot Cracktunes." @@ -38,8 +38,8 @@ async-trait = "0.1" anyhow = "1.0" bytes = "1.6" colored = "2.1" +const-random = "0.1" lazy_static = "1.5" -lyric_finder = { git = "https://github.com/cycle-five/spotify-player", branch = "master", features = ["rustls-tls"], version = "0.1.7" } rand = "0.8" regex = "1.10" serde = { version = "1.0", features = ["derive", "rc"] } @@ -70,6 +70,12 @@ tokio = { workspace = true } poise = { workspace = true } symphonia = { workspace = true } +[dependencies.lyric_finder] +git = "https://github.com/cycle-five/spotify-player" +branch = "master" +features = ["rustls-tls"] +version = "0.1.7" + [dependencies.serenity-voice-model] version = "0.2" diff --git a/crack-core/src/commands/admin/create_text_channel.rs b/crack-core/src/commands/admin/create_text_channel.rs index e9a9eef87..63d756de0 100644 --- a/crack-core/src/commands/admin/create_text_channel.rs +++ b/crack-core/src/commands/admin/create_text_channel.rs @@ -9,6 +9,7 @@ use crate::Error; /// Create text channel. #[cfg(not(tarpaulin_include))] #[poise::command( + category = "Admin", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", diff --git a/crack-core/src/commands/admin/create_voice_channel.rs b/crack-core/src/commands/admin/create_voice_channel.rs index 34ec216a0..fa615a331 100644 --- a/crack-core/src/commands/admin/create_voice_channel.rs +++ b/crack-core/src/commands/admin/create_voice_channel.rs @@ -9,6 +9,7 @@ use crate::Error; /// Create voice channel. #[cfg(not(tarpaulin_include))] #[poise::command( + category = "Admin", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", diff --git a/crack-core/src/commands/admin/deauthorize.rs b/crack-core/src/commands/admin/deauthorize.rs index 0df43c24e..5c53be1be 100644 --- a/crack-core/src/commands/admin/deauthorize.rs +++ b/crack-core/src/commands/admin/deauthorize.rs @@ -8,11 +8,11 @@ use poise::serenity_prelude::Mentionable; /// Deauthorize a user from using the bot. #[cfg(not(tarpaulin_include))] #[poise::command( + category = "Admin", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", - owners_only, - category = "admin" + owners_only )] pub async fn deauthorize( ctx: Context<'_>, diff --git a/crack-core/src/commands/admin/debug.rs b/crack-core/src/commands/admin/debug.rs index c2151c160..bb587cdc2 100644 --- a/crack-core/src/commands/admin/debug.rs +++ b/crack-core/src/commands/admin/debug.rs @@ -7,7 +7,7 @@ use serenity::builder::CreateEmbed; use songbird::tracks::TrackQueue; /// Print some debug info. -#[poise::command(prefix_command, owners_only, ephemeral)] +#[poise::command(category = "Admin", prefix_command, owners_only, ephemeral)] pub async fn debugold(ctx: Context<'_>) -> Result<(), Error> { let data = ctx.data(); diff --git a/crack-core/src/commands/admin/defend.rs b/crack-core/src/commands/admin/defend.rs index 2a287d923..9e3aa7a95 100644 --- a/crack-core/src/commands/admin/defend.rs +++ b/crack-core/src/commands/admin/defend.rs @@ -24,7 +24,13 @@ static mut TEMP_CHANNEL_NAMES: Vec = Vec::new(); static N: u64 = 15; /// Defend the server. -#[poise::command(prefix_command, subcommands("cancel"), owners_only, ephemeral)] +#[poise::command( + category = "Admin", + prefix_command, + subcommands("cancel"), + owners_only, + ephemeral +)] pub async fn defend( ctx: Context<'_>, #[description = "Role to defend against"] role: serenity::all::Role, diff --git a/crack-core/src/commands/admin/delete_channel.rs b/crack-core/src/commands/admin/delete_channel.rs index f288250a4..428b02bff 100644 --- a/crack-core/src/commands/admin/delete_channel.rs +++ b/crack-core/src/commands/admin/delete_channel.rs @@ -6,6 +6,7 @@ use crate::Error; /// Delete channel. #[poise::command( + category = "Admin", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", diff --git a/crack-core/src/commands/admin/kick.rs b/crack-core/src/commands/admin/kick.rs index 836e4969b..9eaed6774 100644 --- a/crack-core/src/commands/admin/kick.rs +++ b/crack-core/src/commands/admin/kick.rs @@ -1,3 +1,4 @@ +//#![feature(const_random)] // This is a nightly feature use crate::errors::CrackedError; use crate::guild::operations::GuildSettingsOperations; use crate::messaging::message::CrackedMessage; @@ -12,6 +13,7 @@ use std::time::Duration; /// Kick command to kick a user from the server based on their ID #[cfg(not(tarpaulin_include))] #[poise::command( + category = "Admin", slash_command, prefix_command, ephemeral, @@ -54,7 +56,7 @@ fn read_lines(filename: &str) -> Vec { /// Kick command to kick all users from the server #[cfg(not(tarpaulin_include))] #[poise::command(prefix_command, ephemeral, owners_only)] -pub async fn rename_all( +pub async fn changenicks( ctx: Context<'_>, #[flag] #[description = "Don't actually change the names or print anything, just log."] @@ -62,33 +64,102 @@ pub async fn rename_all( #[flag] #[description = "Don't call out the changes, just log."] quiet: bool, + #[flag] + #[description = "See the help menu entry."] + help: bool, ) -> Result<(), Error> { + use crate::commands::help; + if help { + return help::wrapper(ctx).await; + } + + rename_all_internal(ctx, dry, quiet).await +} + +macro_rules! const_random_string { + ($category:expr) => {{ + use const_random::const_random; + const fn get_random_item(arr: [&str; N]) -> &str { + let index = const_random!(u8) as usize % N; + arr[index] + } + + const RESULT: &str = get_random_item($category); + RESULT + }}; +} + +const CALL_TO_ACTION: [&str; 5] = [ + "Shine on!", + "Ruby on!", + "Charge your JO Crystals!", + "Let's get sapphired up!", + "Don't Ruby my rights!", +]; + +const EXHASPERATION: [&str; 4] = [ + "Oh no, my diamonds!", + "If you leave me I'll Diamond", + "Kick rocks!", + "I'll smoke your Quartz!", +]; + +const CELEBRATION: [&str; 2] = ["Diamonds are a girl's best friend!", "Gemtastic!"]; + +macro_rules! call_to_action { + () => { + const_random_string!(CELEBRATION) + }; +} + +macro_rules! celebration { + () => { + const_random_string!(EXHASPERATION) + }; +} + +macro_rules! exhasperation { + () => { + const_random_string!(CALL_TO_ACTION) + }; +} + +//const BACKOFF_START_SECS: u64 = 1; +const BACKOFF_MAX_SECS: u64 = 2; +const SLEEP_DURATION_MS: u64 = 100; +const BACKOFF_MULTIPLY_FACTOR: u32 = 2; +//const DEFAULT_EMOJI: &str = "๐Ÿ’Ž"; +const CUR_EMOJI: &str = "๐Ÿ’Ž"; +const CUR_EMOJI_CHAR: char = '๐Ÿ’Ž'; +const STATIC_EMOJI: char = '๐Ÿงช'; +const DISCORD_MAX_NAME_LENGTH: usize = 32; + +/// Internal function for rename_all. +#[cfg(not(tarpaulin_include))] +pub async fn rename_all_internal(ctx: Context<'_>, dry: bool, quiet: bool) -> Result<(), Error> { let guild_id = ctx.guild_id().ok_or(CrackedError::GuildOnly)?; - // let reply_with_embed = ctx - // .data() - // .get_guild_settings(guild_id) - // .map(|x| x.reply_with_embed) - // .ok_or(CrackedError::NoGuildSettings)?; - // load names from file let mut names: Vec = read_lines("names.txt") .iter() .map(|s| s.to_string().trim().to_string()) - .filter(|s| s.len() <= 32) + .filter(|s| s.len() <= DISCORD_MAX_NAME_LENGTH) .collect::>(); // let n = names.len(); - let call_to_action = "To the Armory!"; - let exhasperation = "It's jammed!"; - let celebration = "The deed is done!"; + //let start_backoff_secs = BACKOFF_START_SECS; + //let sleep_duration_ms = SLEEP_DURATION_MS; + //let default_emoji = DEFAULT_EMOJI; + let cur_emoji = CUR_EMOJI; + let cur_emoji_char = CUR_EMOJI_CHAR; + let static_emoji = STATIC_EMOJI; + if !dry { - ctx.say(call_to_action).await?; + ctx.say(call_to_action!()).await?; } else { - tracing::info!(call_to_action); + tracing::info!("{}", call_to_action!()); } let guild = guild_id.to_partial_guild(&ctx).await?; let members = guild.members(&ctx, None, None).await?; - let mut backoff = Duration::from_secs(1); - // Half a second - let sleep = Duration::from_millis(100); + let mut backoff = Duration::from_secs(BACKOFF_MAX_SECS); + let sleep = Duration::from_millis(SLEEP_DURATION_MS); let to_skip = []; for member in members { @@ -98,27 +169,20 @@ pub async fn rename_all( let r = rand::random::() % names.len(); let random_name = names.remove(r).clone(); let (emoji, new_name) = if let Some(cur_nick) = member.user.nick_in(&ctx, guild_id).await { - // if cur_nick.contains("&") { - // random_name = cur_nick.replace("&", "&"); - // } - let emoji = cur_nick.chars().next().unwrap_or('โš”'); - if !emoji.is_ascii() && !emoji.eq(&'๐Ÿงช') { - //format!("{} {}", emoji, random_name) + let emoji = cur_nick.chars().next().unwrap_or(cur_emoji_char); + if !emoji.is_ascii() && !emoji.eq(&static_emoji) { (emoji.to_string(), random_name) } else { - //format!("{} {}", "โš”", random_name) - ("โš”".to_string(), random_name) + (cur_emoji.to_string(), random_name) } } else { - ("โš”".to_string(), random_name) + (cur_emoji.to_string(), random_name) }; if dry { tracing::info!("{} -> {} {}", member.user.name, emoji, new_name); continue; } - // let _until = - // DateTime::from_timestamp((Utc::now() + Duration::from_secs(60)).timestamp(), 0) - // .unwrap(); + if let Err(e) = guild .edit_member( &ctx, @@ -129,20 +193,20 @@ pub async fn rename_all( { // Sleep for a bit to avoid rate limiting tokio::time::sleep(backoff).await; - backoff *= 2; + backoff *= BACKOFF_MULTIPLY_FACTOR; // Handle error, send error message - let msg_str = format!("{} {}! {}", exhasperation, member.user.mention(), e); + let msg_str = format!("{} {}! {}", exhasperation!(), member.user.mention(), e); tracing::info!("{}", msg_str); if !quiet { ctx.say(msg_str).await?; } } else { - if backoff > Duration::from_secs(2) { - backoff /= 2; + if backoff > Duration::from_secs(BACKOFF_MAX_SECS) { + backoff /= BACKOFF_MAX_SECS as u32; } tokio::time::sleep(sleep).await; // Send success message - let msg_str = format!("{}, {}!", celebration, member.user.mention(),); + let msg_str = format!("{}, {}!", celebration!(), member.user.mention(),); tracing::info!("{}", msg_str); if !quiet { ctx.say(msg_str).await?; diff --git a/crack-core/src/commands/admin/message_cache.rs b/crack-core/src/commands/admin/message_cache.rs index 5bbf3cfd5..cffa3ef95 100644 --- a/crack-core/src/commands/admin/message_cache.rs +++ b/crack-core/src/commands/admin/message_cache.rs @@ -6,7 +6,7 @@ use crate::Error; /// Get the message cache. #[cfg(not(tarpaulin_include))] -#[poise::command(prefix_command, owners_only, ephemeral)] +#[poise::command(category = "Admin", prefix_command, owners_only, ephemeral)] pub async fn message_cache(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().ok_or(CrackedError::NoGuildId)?; let cache_str = { diff --git a/crack-core/src/commands/admin/mod.rs b/crack-core/src/commands/admin/mod.rs index 0c1c6ce7c..9992d96df 100644 --- a/crack-core/src/commands/admin/mod.rs +++ b/crack-core/src/commands/admin/mod.rs @@ -1,10 +1,8 @@ pub mod audit_logs; pub mod authorize; -pub mod ban; pub mod broadcast_voice; pub mod create_text_channel; pub mod create_voice_channel; -pub mod deafen; pub mod deauthorize; pub mod debug; pub mod defend; @@ -25,17 +23,16 @@ pub mod user; use crate::{Context, Error}; pub use audit_logs::*; pub use authorize::*; -pub use ban::*; pub use broadcast_voice::*; pub use create_text_channel::*; pub use create_voice_channel::*; -pub use deafen::*; pub use deauthorize::*; pub use debug::*; pub use defend::*; pub use delete_channel::*; pub use get_active::*; pub use invite_tracker::track_invites; +pub use kick::changenicks; pub use kick::*; pub use message_cache::*; pub use move_users::*; @@ -47,9 +44,8 @@ pub use timeout::*; pub use unmute::*; pub use user::*; -use crate::commands::help::sub_help as help; -use crate::messaging::message::CrackedMessage; -use crate::poise_ext::PoiseContextExt; +use crate::commands::help; + /// Admin commands. #[poise::command( category = "Admin", @@ -60,7 +56,6 @@ use crate::poise_ext::PoiseContextExt; subcommands( "audit_logs", "authorize", - "ban", "broadcast_voice", "create_text_channel", "create_voice_channel", @@ -69,7 +64,7 @@ use crate::poise_ext::PoiseContextExt; "deauthorize", "delete_channel", "kick", - "rename_all", + "changenicks", "mute", "message_cache", "move_users_to", @@ -79,37 +74,29 @@ use crate::poise_ext::PoiseContextExt; "get_active_vcs", "set_vc_size", "timeout", - "user", - "role", - "help" + //"user", + //"role", ), - ephemeral, - // owners_only + ephemeral )] #[cfg(not(tarpaulin_include))] pub async fn admin(ctx: Context<'_>) -> Result<(), Error> { - tracing::warn!("Admin command called"); - - let msg = CrackedMessage::CommandFound("admin".to_string()); - ctx.send_reply(msg, true).await?; - - Ok(()) + help::wrapper(ctx).await } /// List of all the admin commands. -pub fn admin_commands() -> Vec { +pub fn commands() -> Vec { vec![ admin(), - ban(), + user(), + role(), kick(), mute(), unmute(), - deafen(), - undeafen(), timeout(), + changenicks(), + set_vc_size(), ] .into_iter() - .chain(role::role_commands()) - .chain(user::user_commands()) .collect() } diff --git a/crack-core/src/commands/admin/move_users.rs b/crack-core/src/commands/admin/move_users.rs index 63b9e132e..825a2dbce 100644 --- a/crack-core/src/commands/admin/move_users.rs +++ b/crack-core/src/commands/admin/move_users.rs @@ -7,7 +7,7 @@ use serenity::builder::EditMember; /// Move usrers to a given channel. #[poise::command( - rename = "move_users_to", + rename = "moveusersto", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", @@ -17,7 +17,13 @@ pub async fn move_users_to( ctx: Context<'_>, #[description = "Users to move"] user_ids: Vec, #[description = "Channel to move users to"] chan_id: ChannelId, + #[flag] + #[description = "Show help menu."] + help: bool, ) -> Result<(), Error> { + if help { + return crate::commands::help::wrapper(ctx).await; + } // Check if the Channel's are voice channels let guild_id = ctx.guild_id().ok_or(CrackedError::NoGuildId)?; let channels = guild_id.channels(ctx).await?; diff --git a/crack-core/src/commands/admin/mute.rs b/crack-core/src/commands/admin/mute.rs index b1aaa51ef..4b0157729 100644 --- a/crack-core/src/commands/admin/mute.rs +++ b/crack-core/src/commands/admin/mute.rs @@ -8,6 +8,7 @@ use serenity::{CacheHttp, EditMember, GuildId, Mentionable}; /// Mute a user. #[poise::command( + category = "Admin", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", @@ -25,7 +26,7 @@ pub async fn mute( .map_err(Into::into) } -/// Unmute a user. +/// Mute a user, internal function. pub async fn mute_internal( cache_http: &impl CacheHttp, user: serenity::User, diff --git a/crack-core/src/commands/admin/random_mute_lol.rs b/crack-core/src/commands/admin/random_mute_lol.rs index 2598865ab..8a402f795 100644 --- a/crack-core/src/commands/admin/random_mute_lol.rs +++ b/crack-core/src/commands/admin/random_mute_lol.rs @@ -1,6 +1,8 @@ +use super::mute::mute_internal; use crate::errors::CrackedError; use crate::Context; use crate::Error; +use async_trait::async_trait; use rand::Rng; use serenity::all::{Context as SerenityContext, GuildId, User}; use songbird::{Call, Event, EventContext, EventHandler}; @@ -10,6 +12,7 @@ use tokio::sync::Mutex; /// Randomly mute a user in the server. #[cfg(not(tarpaulin_include))] #[poise::command( + category = "Admin", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", @@ -52,8 +55,6 @@ pub struct RandomMuteHandler { pub guild_id: GuildId, } -use crate::commands::mute_internal; -use async_trait::async_trait; #[async_trait] impl EventHandler for RandomMuteHandler { async fn act(&self, _ctx: &EventContext<'_>) -> Option { diff --git a/crack-core/src/commands/admin/role/assign_role.rs b/crack-core/src/commands/admin/role/assign_role.rs index 3f108d6f0..ba10be69f 100644 --- a/crack-core/src/commands/admin/role/assign_role.rs +++ b/crack-core/src/commands/admin/role/assign_role.rs @@ -1,4 +1,4 @@ -use crate::commands::sub_help as help; +use crate::commands::help; use crate::{Context, Error}; use serenity::all::{GuildId, Member, Role, RoleId, UserId}; @@ -9,8 +9,6 @@ use serenity::all::{GuildId, Member, Role, RoleId, UserId}; required_bot_permissions = "ADMINISTRATOR", prefix_command, slash_command, - subcommands("help"), - hide_in_help = true, ephemeral )] #[cfg(not(tarpaulin_include))] @@ -18,7 +16,13 @@ pub async fn assign( ctx: Context<'_>, #[description = "Role to assign"] role: Role, #[description = "Member to assign the role to"] member: Member, + #[flag] + #[description = "Show help menu"] + help: bool, ) -> Result<(), Error> { + if help { + return help::wrapper(ctx).await; + } member .add_role(&ctx, role) .await @@ -33,7 +37,6 @@ pub async fn assign( required_bot_permissions = "ADMINISTRATOR", prefix_command, slash_command, - subcommands("help"), hide_in_help = true, ephemeral )] @@ -43,7 +46,12 @@ pub async fn assign_ids( #[description = "GuildId related to"] guild_id: GuildId, #[description = "RoleId to assign"] role_id: RoleId, #[description = "UserId to assign role to"] user_id: UserId, + #[description = "Show help menu"] help: bool, ) -> Result<(), Error> { + if help { + return help::wrapper(ctx).await; + } + let member = guild_id.member(&ctx, user_id).await?; member .add_role(&ctx, role_id) diff --git a/crack-core/src/commands/admin/role/create_role.rs b/crack-core/src/commands/admin/role/create_role.rs index 36940bdc8..f24048678 100644 --- a/crack-core/src/commands/admin/role/create_role.rs +++ b/crack-core/src/commands/admin/role/create_role.rs @@ -2,7 +2,7 @@ use poise::serenity_prelude::{Colour, Permissions}; use serenity::all::{Attachment, CreateAttachment, GuildId, Role}; use serenity::builder::EditRole; -use crate::commands::{sub_help as help, EmptyResult}; +use crate::commands::EmptyResult; use crate::errors::CrackedError; use crate::messaging::message::CrackedMessage; use crate::utils::send_reply; @@ -16,13 +16,12 @@ use crate::Context; required_bot_permissions = "ADMINISTRATOR", prefix_command, slash_command, - subcommands("help"), - hide_in_help = true, ephemeral )] +#[cfg(not(tarpaulin_include))] pub async fn create( ctx: Context<'_>, - #[description = "Name of the role to create."] name: String, + #[description = "Name of the role to create. Required."] name: String, #[description = "Whether the role is hoisted."] hoist: Option, #[description = "Whether the role is mentionable."] mentionable: Option, #[description = "Optional initial perms"] permissions: Option, @@ -70,6 +69,7 @@ pub async fn create( } /// Internal create role function. +#[cfg(not(tarpaulin_include))] #[allow(clippy::too_many_arguments)] pub async fn create_role_internal( ctx: Context<'_>, diff --git a/crack-core/src/commands/admin/role/delete_role.rs b/crack-core/src/commands/admin/role/delete_role.rs index 52edb5174..eec44d451 100644 --- a/crack-core/src/commands/admin/role/delete_role.rs +++ b/crack-core/src/commands/admin/role/delete_role.rs @@ -1,5 +1,6 @@ use poise::ReplyHandle; use serenity::all::{Role, RoleId}; +use std::borrow::Cow; use crate::{ errors::CrackedError, messaging::message::CrackedMessage, utils::send_reply, Context, Error, @@ -12,7 +13,7 @@ use crate::{ required_permissions = "ADMINISTRATOR", required_bot_permissions = "ADMINISTRATOR", prefix_command, - hide_in_help = true, + slash_command, ephemeral )] pub async fn delete( @@ -42,7 +43,6 @@ pub async fn delete_by_id( .map(|_| ()) } -use std::borrow::Cow; /// Delete role helper. pub async fn delete_role_by_id_helper( ctx: Context<'_>, diff --git a/crack-core/src/commands/admin/role/mod.rs b/crack-core/src/commands/admin/role/mod.rs index 60fa1b7f3..8fcb9e087 100644 --- a/crack-core/src/commands/admin/role/mod.rs +++ b/crack-core/src/commands/admin/role/mod.rs @@ -9,20 +9,28 @@ pub use delete_role::*; pub use crate::poise_ext::ContextExt; pub use crate::utils; -use crate::commands::sub_help as help; +use crate::commands::help; use crate::{Context, Error}; /// Role commands. #[poise::command( + category = "Admin", + required_permissions = "ADMINISTRATOR", prefix_command, slash_command, - subcommands("create", "delete", "delete_by_id", "assign", "assign_ids", "help"), - ephemeral, - hide_in_help = true + subcommands("create", "delete", "delete_by_id", "assign", "assign_ids"), + ephemeral )] #[cfg(not(tarpaulin_include))] -pub async fn role(ctx: Context<'_>) -> Result<(), Error> { - tracing::warn!("Role command called"); +pub async fn role( + ctx: Context<'_>, + #[flag] + #[description = "Show help menu."] + help: bool, +) -> Result<(), Error> { + if help { + return help::wrapper(ctx).await; + } ctx.send_found_command("admin role".to_string()).await?; diff --git a/crack-core/src/commands/admin/set_vc_size.rs b/crack-core/src/commands/admin/set_vc_size.rs index 2e1eab36c..dbc00274d 100644 --- a/crack-core/src/commands/admin/set_vc_size.rs +++ b/crack-core/src/commands/admin/set_vc_size.rs @@ -12,6 +12,8 @@ use serenity::all::EditChannel; /// Set the size of a voice channel. #[poise::command( + category = "Admin", + rename = "setvcsize", prefix_command, slash_command, required_permissions = "ADMINISTRATOR", @@ -21,9 +23,15 @@ pub async fn set_vc_size( ctx: Context<'_>, #[description = "VoiceChannel to edit"] channel: Channel, #[description = "New max size"] size: u32, + #[flag] + #[description = "Show the help menu."] + help: bool, ) -> Result<(), Error> { - let _guild_id = ctx.guild_id().ok_or(CrackedError::NoGuildId)?; - let _res = channel + if help { + return crate::commands::help::wrapper(ctx).await; + } + // let guild_id = ctx.guild_id().ok_or(CrackedError::NoGuildId)?; + let _ = channel .id() .edit(&ctx, EditChannel::new().user_limit(size)) .await?; @@ -56,7 +64,6 @@ pub async fn set_vc_size_internal( e.into(), )) } else { - // Send success message Ok(CrackedMessage::ChannelSizeSet { name, id, size }) } } diff --git a/crack-core/src/commands/admin/timeout.rs b/crack-core/src/commands/admin/timeout.rs index fbf261e64..9041cbb09 100644 --- a/crack-core/src/commands/admin/timeout.rs +++ b/crack-core/src/commands/admin/timeout.rs @@ -13,6 +13,7 @@ use std::time::Duration; /// FIXME: THIS IS BROKEN FIX #[cfg(not(tarpaulin_include))] #[poise::command( + category = "Admin", slash_command, prefix_command, guild_only, @@ -23,7 +24,13 @@ pub async fn timeout( ctx: Context<'_>, #[description = "User to timout."] user: User, #[description = "Amount of time"] duration: String, + #[flag] + #[description = "Show the help menu."] + help: bool, ) -> Result<(), Error> { + if help { + return crate::commands::help::wrapper(ctx).await; + } // Debugging print the params let id = user.id; let mention = user.mention(); diff --git a/crack-core/src/commands/admin/unmute.rs b/crack-core/src/commands/admin/unmute.rs index 1a945c2ef..6bd372d9c 100644 --- a/crack-core/src/commands/admin/unmute.rs +++ b/crack-core/src/commands/admin/unmute.rs @@ -9,6 +9,7 @@ use serenity::builder::EditMember; /// Unmute a user. /// TODO: Add a way to unmute a user by their ID. #[poise::command( + category = "Admin", slash_command, prefix_command, required_permissions = "ADMINISTRATOR", diff --git a/crack-core/src/commands/admin/ban.rs b/crack-core/src/commands/admin/user/ban.rs similarity index 92% rename from crack-core/src/commands/admin/ban.rs rename to crack-core/src/commands/admin/user/ban.rs index 1034cc517..29a777c48 100644 --- a/crack-core/src/commands/admin/ban.rs +++ b/crack-core/src/commands/admin/user/ban.rs @@ -20,16 +20,15 @@ use serenity::all::User; category = "Admin", slash_command, prefix_command, - required_permissions = "ADMINISTRATOR", + required_permissions = "BAN_MEMBERS", + required_bot_permissions = "BAN_MEMBERS", ephemeral )] pub async fn ban( ctx: Context<'_>, #[description = "User to ban."] user: User, #[description = "Number of day to delete messages of the user."] dmd: Option, - #[rest] - #[description = "Reason for the ban."] - reason: Option, + #[description = "Reason for the ban."] reason: Option, ) -> Result<(), Error> { let mention = user.mention(); let id = user.id; diff --git a/crack-core/src/commands/admin/deafen.rs b/crack-core/src/commands/admin/user/deafen.rs similarity index 100% rename from crack-core/src/commands/admin/deafen.rs rename to crack-core/src/commands/admin/user/deafen.rs diff --git a/crack-core/src/commands/admin/user/mod.rs b/crack-core/src/commands/admin/user/mod.rs index 16e255c74..5741106c7 100644 --- a/crack-core/src/commands/admin/user/mod.rs +++ b/crack-core/src/commands/admin/user/mod.rs @@ -1,32 +1,36 @@ +pub mod ban; +pub mod deafen; pub mod unban; +pub use ban::*; +pub use deafen::*; pub use unban::*; pub use crate::poise_ext::ContextExt; pub use crate::utils; -use crate::commands::sub_help as help; use crate::{Command, Context, Error}; /// User admin commands. #[poise::command( category = "Admin", + required_bot_permissions = "BAN_MEMBERS", + required_permissions = "BAN_MEMBERS", prefix_command, slash_command, - //subcommands("create", "delete", "delete_by_id", "assign", "assign_ids", "help"), - subcommands("help"), - ephemeral, - hide_in_help = true + subcommands("ban", "unban", "deafen", "undeafen"), + ephemeral )] #[cfg(not(tarpaulin_include))] -pub async fn user(ctx: Context<'_>) -> Result<(), Error> { - tracing::warn!("Role command called"); - - ctx.send_found_command("admin user".to_string()).await?; - - Ok(()) +pub async fn user( + ctx: Context<'_>, + // #[flag] + // #[description = "Show the help menu."] + // help: bool, +) -> Result<(), Error> { + crate::commands::help::wrapper(ctx).await } -pub fn user_commands() -> [Command; 2] { - [unban(), unban_by_user_id()] +pub fn user_commands() -> [Command; 4] { + [ban(), unban(), deafen(), undeafen()] } diff --git a/crack-core/src/commands/admin/user/unban.rs b/crack-core/src/commands/admin/user/unban.rs index 3a50e2948..945af40b5 100644 --- a/crack-core/src/commands/admin/user/unban.rs +++ b/crack-core/src/commands/admin/user/unban.rs @@ -1,4 +1,3 @@ -use crate::commands::sub_help as help; use crate::errors::CrackedError; use crate::messaging::message::CrackedMessage; use crate::{poise_ext::PoiseContextExt, CommandResult, Context}; @@ -10,8 +9,8 @@ use serenity::{Mentionable, User, UserId}; category = "Admin", slash_command, prefix_command, - subcommands("help"), - required_permissions = "ADMINISTRATOR", + required_bot_permissions = "BAN_MEMBERS", + required_permissions = "BAN_MEMBERS", ephemeral )] #[cfg(not(tarpaulin_include))] @@ -28,8 +27,8 @@ pub async fn unban( category = "Admin", prefix_command, slash_command, - subcommands("help"), - required_permissions = "ADMINISTRATOR", + required_bot_permissions = "BAN_MEMBERS", + required_permissions = "BAN_MEMBERS", ephemeral )] #[cfg(not(tarpaulin_include))] diff --git a/crack-core/src/commands/mod.rs b/crack-core/src/commands/mod.rs index 86616c604..8c1fa53db 100644 --- a/crack-core/src/commands/mod.rs +++ b/crack-core/src/commands/mod.rs @@ -10,10 +10,11 @@ pub mod music_utils; pub mod osint; pub mod permissions; pub mod playlist; +pub mod register; pub mod settings; pub mod utility; -pub use admin::*; +pub use admin::commands; #[cfg(feature = "crack-bf")] pub use bf::*; #[cfg(feature = "crack-gpt")] @@ -24,12 +25,10 @@ pub use music_utils::*; #[cfg(feature = "crack-osint")] pub use osint::*; pub use permissions::*; +pub use register::*; pub use settings::*; pub use utility::*; -pub mod register; -pub use register::*; - pub use crate::errors::CrackedError; use serenity::all::Message; @@ -64,8 +63,8 @@ pub fn all_commands() -> Vec { .chain(music::music_commands()) .chain(utility::utility_commands()) .chain(settings::commands()) - // .chain(playlist::commands()) - // .chain(admin::admin_commands()) + .chain(admin::commands()) + .chain(playlist::commands()) .collect() } diff --git a/crack-core/src/commands/music/autopause.rs b/crack-core/src/commands/music/autopause.rs index 5334b640b..eef4977e0 100644 --- a/crack-core/src/commands/music/autopause.rs +++ b/crack-core/src/commands/music/autopause.rs @@ -1,5 +1,5 @@ use crate::{ - commands::{cmd_check_music, sub_help as help}, + commands::{cmd_check_music, help}, errors::CrackedError, guild::operations::GuildSettingsOperations, http_utils::SendMessageParams, @@ -14,11 +14,18 @@ use crate::{ category = "Music", slash_command, prefix_command, - subcommands("help"), guild_only, check = "cmd_check_music" )] -pub async fn autopause(ctx: Context<'_>) -> Result<(), Error> { +pub async fn autopause( + ctx: Context<'_>, + #[flag] + #[description = "Show help menu."] + flag: bool, +) -> Result<(), Error> { + if flag { + return help::wrapper(ctx).await; + } autopause_internal(ctx).await } diff --git a/crack-core/src/commands/music/autoplay.rs b/crack-core/src/commands/music/autoplay.rs index 5d586a9da..fdd5078fc 100644 --- a/crack-core/src/commands/music/autoplay.rs +++ b/crack-core/src/commands/music/autoplay.rs @@ -1,4 +1,4 @@ -use crate::commands::{cmd_check_music, sub_help as help}; +use crate::commands::{cmd_check_music, help}; use crate::guild::operations::GuildSettingsOperations; use crate::{messaging::message::CrackedMessage, utils::send_reply, Context, CrackedError, Error}; @@ -10,10 +10,17 @@ use crate::{messaging::message::CrackedMessage, utils::send_reply, Context, Crac slash_command, prefix_command, guild_only, - aliases("ap"), - subcommands("help") + aliases("ap") )] -pub async fn autoplay(ctx: Context<'_>) -> Result<(), Error> { +pub async fn autoplay( + ctx: Context<'_>, + #[flag] + #[description = "Show help menu."] + help: bool, +) -> Result<(), Error> { + if help { + return help::wrapper(ctx).await; + } toggle_autoplay(ctx).await } diff --git a/crack-core/src/commands/music/clear.rs b/crack-core/src/commands/music/clear.rs index 2c76e6d8d..49ad77df2 100644 --- a/crack-core/src/commands/music/clear.rs +++ b/crack-core/src/commands/music/clear.rs @@ -1,5 +1,5 @@ use crate::{ - commands::{cmd_check_music, sub_help as help}, + commands::{cmd_check_music, help}, errors::{verify, CrackedError}, handlers::track_end::update_queue_messages, messaging::message::CrackedMessage, @@ -14,10 +14,17 @@ use crate::{ prefix_command, slash_command, guild_only, - check = "cmd_check_music", - subcommands("help") + check = "cmd_check_music" )] -pub async fn clear(ctx: Context<'_>) -> Result<(), Error> { +pub async fn clear( + ctx: Context<'_>, + #[flag] + #[description = "Show help menu."] + help: bool, +) -> Result<(), Error> { + if help { + return help::wrapper(ctx).await; + } clear_internal(ctx).await } diff --git a/crack-core/src/commands/music/doplay.rs b/crack-core/src/commands/music/doplay.rs index 716dd17bf..c9cb0aafd 100644 --- a/crack-core/src/commands/music/doplay.rs +++ b/crack-core/src/commands/music/doplay.rs @@ -1,7 +1,7 @@ use super::play_utils::query::QueryType; use super::play_utils::queue::{get_mode, get_msg, queue_track_back}; use crate::commands::play_utils::query::query_type_from_url; -use crate::commands::{cmd_check_music, sub_help as help}; +use crate::commands::{cmd_check_music, help}; use crate::sources::rusty_ytdl::RustyYoutubeClient; use crate::CrackedResult; use crate::{commands::get_call_or_join_author, http_utils::SendMessageParams}; @@ -21,7 +21,7 @@ use crate::{ }, sources::spotify::SpotifyTrack, sources::youtube::build_query_aux_metadata, - utils::{get_human_readable_timestamp, get_track_metadata}, + utils::{get_human_readable_timestamp, get_track_handle_metadata}, Context, Error, }; use ::serenity::{ @@ -58,8 +58,7 @@ pub enum Mode { prefix_command, slash_command, guild_only, - check = "cmd_check_music", - subcommands("help") + check = "cmd_check_music" )] pub async fn get_guild_name_info(ctx: Context<'_>) -> Result<(), Error> { let shard_id = ctx.serenity_context().shard_id; @@ -135,12 +134,16 @@ pub async fn play( #[poise::command(slash_command, prefix_command, guild_only, aliases("opt"))] pub async fn optplay( ctx: Context<'_>, + #[flag] + #[description = "Show help menu."] + help: bool, #[description = "Play mode"] mode: Option, #[description = "File to play."] file: Option, - #[rest] - #[description = "song link or search query."] - query_or_url: Option, + #[description = "song link or search query."] query_or_url: Option, ) -> Result<(), Error> { + if help { + return help::wrapper(ctx).await; + } play_internal(ctx, mode, file, query_or_url).await } @@ -149,6 +152,7 @@ use crate::poise_ext::PoiseContextExt; /// Does the actual playing of the song, all the other commands use this. #[cfg(not(tarpaulin_include))] +#[tracing::instrument(skip(ctx))] async fn play_internal( ctx: Context<'_>, mode: Option, @@ -157,6 +161,9 @@ async fn play_internal( ) -> Result<(), Error> { //let guild_id = ctx.guild_id().ok_or(CrackedError::NoGuildId)?; // FIXME: This should be generalized. + // Get current time for timing purposes. + let _start = std::time::Instant::now(); + let is_prefix = ctx.prefix() != "/"; let msg = get_msg(mode.clone(), query_or_url, is_prefix); @@ -173,11 +180,15 @@ async fn play_internal( return Ok(()); } + let _after_msg_parse = std::time::Instant::now(); + let (mode, msg) = get_mode(is_prefix, msg.clone(), mode); + let _after_get_mode = std::time::Instant::now(); + // TODO: Maybe put into it's own function? let url = match file.clone() { - Some(file) => file.url.as_str().to_owned().to_string(), + Some(file) => file.url.clone(), None => msg.clone(), }; let url = url.as_str(); @@ -186,6 +197,8 @@ async fn play_internal( let call = get_call_or_join_author(ctx).await?; + let _after_call = std::time::Instant::now(); + let mut search_msg = msg_int::send_search_message(&ctx).await?; tracing::debug!("search response msg: {:?}", search_msg); @@ -201,10 +214,14 @@ async fn play_internal( tracing::warn!("query_type: {:?}", query_type); + let _after_query_type = std::time::Instant::now(); + // FIXME: Super hacky, fix this shit. // This is actually where the track gets queued into the internal queue, it's the main work function. let move_on = match_mode(ctx, call.clone(), mode, query_type.clone(), &mut search_msg).await?; + let _after_move_on = std::time::Instant::now(); + // FIXME: Yeah, this is terrible, fix this. if !move_on { return Ok(()); @@ -216,6 +233,8 @@ async fn play_internal( let queue = handler.queue().current_queue(); drop(handler); + let _after_refetch_queue = std::time::Instant::now(); + // This makes sense, we're getting the final response to the user based on whether // the song / playlist was queued first, last, or is now playing. // Ah! Also, sometimes after a long queue process the now playing message says that it's already @@ -282,9 +301,39 @@ async fn play_internal( }, }; - edit_embed_response2(ctx, embed, search_msg.clone()) - .await - .map(|_| ()) + let _after_embed = std::time::Instant::now(); + + let _ = edit_embed_response2(ctx, embed, search_msg.clone()).await?; + + let _after_edit_embed = std::time::Instant::now(); + + tracing::warn!( + r#" + after_msg_parse: {:?} + after_get_mode: {:?} (+{:?}) + after_call: {:?} (+{:?}) + after_query_type: {:?} (+{:?}) + after_move_on: {:?} (+{:?}) + after_refetch_queue: {:?} (+{:?}) + after_embed: {:?} (+{:?}) + after_edit_embed: {:?} (+{:?})"#, + _after_msg_parse.duration_since(_start), + _after_get_mode.duration_since(_start), + _after_get_mode.duration_since(_after_msg_parse), + _after_call.duration_since(_start), + _after_call.duration_since(_after_get_mode), + _after_query_type.duration_since(_start), + _after_query_type.duration_since(_after_call), + _after_move_on.duration_since(_start), + _after_move_on.duration_since(_after_query_type), + _after_refetch_queue.duration_since(_start), + _after_refetch_queue.duration_since(_after_move_on), + _after_embed.duration_since(_start), + _after_embed.duration_since(_after_refetch_queue), + _after_edit_embed.duration_since(_start), + _after_edit_embed.duration_since(_after_embed), + ); + Ok(()) } pub enum MessageOrInteraction { Message(Message), @@ -326,6 +375,16 @@ async fn match_mode<'a>( } } +// async fn query_type_to_metadata<'a>( +// ctx: Context<'_>, +// call: Arc>, +// mode: Mode, +// query_type: QueryType, +// search_msg: &'a mut Message, +// ) -> CrackedResult { +// tracing::info!("mode: {:?}", mode); +// } + /// Check if the domain that we're playing from is banned. // FIXME: This is borked. pub fn check_banned_domains( @@ -366,7 +425,7 @@ async fn calculate_time_until_play(queue: &[TrackHandle], mode: Mode) -> Option< .await .map(|i| i.position) .unwrap_or(zero_duration); - let metadata = get_track_metadata(top_track).await; + let metadata = get_track_handle_metadata(top_track).await; let top_track_duration = match metadata.duration { Some(duration) => duration, @@ -416,9 +475,7 @@ impl Default for RequestingUser { /// AuxMetadata wrapper and utility functions. #[derive(Debug, Clone)] -pub enum MyAuxMetadata { - Data(AuxMetadata), -} +pub struct MyAuxMetadata(pub AuxMetadata); /// Implement TypeMapKey for MyAuxMetadata. impl TypeMapKey for MyAuxMetadata { @@ -428,7 +485,7 @@ impl TypeMapKey for MyAuxMetadata { /// Implement Default for MyAuxMetadata. impl Default for MyAuxMetadata { fn default() -> Self { - MyAuxMetadata::Data(AuxMetadata::default()) + MyAuxMetadata(AuxMetadata::default()) } } @@ -436,19 +493,17 @@ impl Default for MyAuxMetadata { impl MyAuxMetadata { /// Create a new MyAuxMetadata from AuxMetadata. pub fn new(metadata: AuxMetadata) -> Self { - MyAuxMetadata::Data(metadata) + MyAuxMetadata(metadata) } /// Get the internal metadata. pub fn metadata(&self) -> &AuxMetadata { - match self { - MyAuxMetadata::Data(metadata) => metadata, - } + &self.0 } /// Create new MyAuxMetadata from &SpotifyTrack. pub fn from_spotify_track(track: &SpotifyTrack) -> Self { - MyAuxMetadata::Data(AuxMetadata { + MyAuxMetadata(AuxMetadata { track: Some(track.name()), artist: Some(track.artists_str()), album: Some(track.album_name()), @@ -466,7 +521,7 @@ impl MyAuxMetadata { /// Set the source_url. pub fn with_source_url(self, source_url: String) -> Self { - MyAuxMetadata::Data(AuxMetadata { + MyAuxMetadata(AuxMetadata { source_url: Some(source_url), ..self.metadata().clone() }) @@ -500,7 +555,7 @@ async fn build_queued_embed( let my_metadata = map.get::().unwrap(); match my_metadata { - MyAuxMetadata::Data(metadata) => metadata.clone(), + MyAuxMetadata(metadata) => metadata.clone(), } }; let thumbnail = metadata.thumbnail.clone().unwrap_or_default(); @@ -530,6 +585,8 @@ async fn build_queued_embed( .footer(CreateEmbedFooter::new(footer_text)) } +use crate::sources::rusty_ytdl::RequestOptionsBuilder; +use rusty_ytdl::search::YouTube; /// Add tracks to the queue from aux_metadata. #[cfg(not(tarpaulin_include))] pub async fn queue_aux_metadata( @@ -546,6 +603,10 @@ pub async fn queue_aux_metadata( .ok_or(CrackedError::NotConnected)?; let call = manager.get(guild_id).ok_or(CrackedError::NotConnected)?; + let req = RequestOptionsBuilder::new() + .set_client(client.clone()) + .build(); + let rusty_ytdl = YouTube::new_with_options(&req)?; for metadata in search_results { let source_url = metadata.metadata().source_url.as_ref(); let metadata_final = if source_url.is_none() || source_url.unwrap().is_empty() { @@ -557,12 +618,12 @@ pub async fn queue_aux_metadata( ) .await; - let ytdl = RustyYoutubeClient::new_with_client(client.clone())?; - let res = ytdl.one_shot(search_query).await?; + //let ytdl = RustyYoutubeClient::new_with_client(client.clone())?; + let res = rusty_ytdl.search_one(search_query, None).await?; let res = res.ok_or(CrackedError::Other("No results found"))?; let new_aux_metadata = RustyYoutubeClient::search_result_to_aux_metadata(&res); - MyAuxMetadata::Data(new_aux_metadata) + MyAuxMetadata(new_aux_metadata) } else { metadata.clone() }; diff --git a/crack-core/src/commands/music/lyrics.rs b/crack-core/src/commands/music/lyrics.rs index b0c08e229..3ab283589 100644 --- a/crack-core/src/commands/music/lyrics.rs +++ b/crack-core/src/commands/music/lyrics.rs @@ -57,7 +57,7 @@ pub async fn query_or_title(ctx: Context<'_>, query: Option) -> Result().unwrap(); + let MyAuxMetadata(data) = lock.get::().unwrap(); tracing::info!("data: {:?}", data); data.track .clone() diff --git a/crack-core/src/commands/music/play_utils/query.rs b/crack-core/src/commands/music/play_utils/query.rs index e1cdc182b..9ffaf7ec7 100644 --- a/crack-core/src/commands/music/play_utils/query.rs +++ b/crack-core/src/commands/music/play_utils/query.rs @@ -1,8 +1,10 @@ use super::queue::{queue_track_back, queue_track_front}; use super::{queue_keyword_list_back, queue_query_list_offset}; +// use crate::commands::kick; use crate::guild::operations::GuildSettingsOperations; use crate::messaging::interface::create_search_response; -use crate::sources::youtube::video_info_to_source_and_metadata; +// use crate::sources::rusty_ytdl::RustyYoutubeSearch; +use crate::sources::youtube::get_rusty_search; use crate::CrackedResult; use crate::{ commands::{check_banned_domains, MyAuxMetadata}, @@ -23,6 +25,7 @@ use crate::{ use ::serenity::all::{Attachment, CreateAttachment, CreateMessage}; use colored::Colorize; use poise::serenity_prelude as serenity; +use rusty_ytdl::search::YouTube; use rusty_ytdl::search::{Playlist, SearchOptions, SearchType}; use songbird::{ input::{AuxMetadata, Compose as _, HttpRequest, Input as SongbirdInput, YoutubeDl}, @@ -115,7 +118,7 @@ impl From for Vec { q.queries } } - +use crate::sources::youtube::search_query_to_source_and_metadata; impl QueryType { /// Build a query string from the query type. pub fn build_query(&self) -> Option { @@ -488,98 +491,130 @@ impl QueryType { _call: Arc>, ) -> Result { Err(CrackedError::Other("Not implemented yet!")) - // match self { - // QueryType::YoutubeSearch(query) => { - // return Err(CrackedError::Other("Not implemented yet!").into()); - // }, - // QueryType::Keywords(_) - // | QueryType::VideoLink(_) - // | QueryType::File(_) - // | QueryType::NewYoutubeDl(_) => { - // let mut queue = enqueue_track_pgwrite(ctx, &call, &query_type).await?; - - // if !queue_was_empty { - // rotate_tracks(&call, 1).await.ok(); - // queue = force_skip_top_track(&call.lock().await).await?; - // } - // }, - // QueryType::PlaylistLink(url) => { - // tracing::error!("Mode::Jump, QueryType::PlaylistLink"); - // // let urls = YouTubeRestartable::ytdl_playlist(&url, mode) - // // .await - // // .ok_or(CrackedError::PlayListFail)?; - // // FIXME - // let _src = YoutubeDl::new(Client::new(), url); - // // .ok_or(CrackedError::Other("failed to fetch playlist"))? - // // .into_iter() - // // .for_each(|track| async { - // // let _ = enqueue_track(&call, &QueryType::File(track)).await; - // // }); - // let urls = vec!["".to_string()]; - // let mut insert_idx = 1; - - // for (i, url) in urls.into_iter().enumerate() { - // let mut queue = - // insert_track(ctx, &call, &QueryType::VideoLink(url), insert_idx).await?; - - // if i == 0 && !queue_was_empty { - // queue = force_skip_top_track(&call.lock().await).await?; - // } else { - // insert_idx += 1; - // } - // } - // }, - // // FIXME - // QueryType::SpotifyTracks(tracks) => { - // let mut insert_idx = 1; - // let keywords_list = tracks - // .iter() - // .map(|x| x.build_query()) - // .collect::>(); - - // for (i, keywords) in keywords_list.into_iter().enumerate() { - // let mut queue = - // insert_track(ctx, &call, &QueryType::Keywords(keywords), insert_idx) - // .await?; - - // if i == 0 && !queue_was_empty { - // queue = force_skip_top_track(&call.lock().await).await?; - // } else { - // insert_idx += 1; - // } - // } - // }, - // // FIXME - // QueryType::KeywordList(keywords_list) => { - // let mut insert_idx = 1; - - // for (i, keywords) in keywords_list.into_iter().enumerate() { - // let mut queue = - // insert_track(ctx, &call, &QueryType::Keywords(keywords), insert_idx) - // .await?; - - // if i == 0 && !queue_was_empty { - // queue = force_skip_top_track(&call.lock().await).await?; - // } else { - // insert_idx += 1; - // } - // } - // }, - // QueryType::None => { - // let embed = CreateEmbed::default() - // .description(format!("{}", CrackedError::Other("No query provided!"))) - // .footer(CreateEmbedFooter::new("No query provided!")); - // send_embed_response_poise(ctx, embed).await?; - // return Ok(false); - // }, - // } + } + + pub async fn get_track_metadata( + &self, + ytclient: YouTube, + reqclient: reqwest::Client, + ) -> CrackedResult> { + match self { + QueryType::YoutubeSearch(query) => { + tracing::error!("In YoutubeSearch"); + let search_options = SearchOptions { + limit: 10, + search_type: SearchType::All, + ..Default::default() + }; + + let res = ytclient.search(query, Some(&search_options)).await?; + let mut metadata = Vec::with_capacity(res.len()); + for r in res { + metadata.push(MyAuxMetadata( + RustyYoutubeClient::search_result_to_aux_metadata(&r), + )); + } + Ok(metadata) + // let mut ytdl = YoutubeDl::new_search(client, query.clone()); + // let mut res = Vec::new(); + // let asdf = ytdl.search(None).await?; + // for metadata in asdf { + // let my_metadata = MyAuxMetadata(metadata); + // res.push(my_metadata); + // } + // Ok(res) + }, + QueryType::VideoLink(query) => { + // tracing::warn!("In VideoLink"); + // let vid_info = RustyYoutubeClient::get_video_info(query.clone()).await?; + // let metadata = RustyYoutubeClient::video_info_to_aux_metadata(&vid_info); + let search = get_rusty_search(reqclient.clone(), query.clone()).await?; + let metadata = search + .clone() + .metadata + .into_iter() + .map(MyAuxMetadata) + .collect::>(); + Ok(metadata) + }, + QueryType::Keywords(query) => { + tracing::warn!("In Keywords"); + // // get_rusty_search(client.clone(), query.clone()).await + // let mut ytdl = YoutubeDl::new_search(client, query.clone()); + let res = ytclient.search_one(query.clone(), None).await?; + // let metadata = ytdl.aux_metadata().await?; + let res = res.unwrap(); + let my_metadata = RustyYoutubeClient::search_result_to_aux_metadata(&res); + let my_metadata = MyAuxMetadata(my_metadata); + // Ok((ytdl.into(), vec![my_metadata])) + Ok(vec![my_metadata]) + }, + QueryType::File(_file) => { + Ok(vec![]) + // tracing::warn!("In File"); + // Ok(( + // HttpRequest::new(client, file.url.to_owned()).into(), + // vec![MyAuxMetadata::default()], + // )) + }, + QueryType::NewYoutubeDl(_data) => { + // let (ytdl, aux_metadata) = data.clone(); + // Ok((ytdl.into(), vec![MyAuxMetadata(aux_metadata)])) + Ok(vec![]) + }, + QueryType::PlaylistLink(url) => { + let search_options = SearchOptions { + limit: 100, + search_type: SearchType::Playlist, + ..Default::default() + }; + + let res = ytclient.search(url, Some(&search_options)).await?; + let mut metadata = Vec::with_capacity(res.len()); + for r in res { + metadata.push(MyAuxMetadata( + RustyYoutubeClient::search_result_to_aux_metadata(&r), + )); + } + Ok(metadata) + // let ytdl = YoutubeDl::new(client.clone(), url.clone()); + // tracing::warn!("ytdl: {:?}", ytdl); + // Ok((ytdl.into(), metadata)) + }, + QueryType::SpotifyTracks(tracks) => { + let keywords_list = tracks + .iter() + .map(|x| x.build_query()) + .collect::>(); + let mut metadatas = Vec::with_capacity(keywords_list.len()); + for keyword in keywords_list { + let res = ytclient.search_one(keyword, None).await?.unwrap(); + let my_metadata = RustyYoutubeClient::search_result_to_aux_metadata(&res); + let my_metadata = MyAuxMetadata(my_metadata); + metadatas.push(my_metadata); + } + Ok(metadatas) + }, + QueryType::KeywordList(keywords_list) => { + let mut metadatas = Vec::with_capacity(keywords_list.len()); + for keyword in keywords_list { + let res = ytclient.search_one(keyword, None).await?.unwrap(); + let my_metadata = RustyYoutubeClient::search_result_to_aux_metadata(&res); + let my_metadata = MyAuxMetadata(my_metadata); + metadatas.push(my_metadata); + } + Ok(metadatas) + }, + QueryType::None => unimplemented!(), + } } pub async fn get_track_source_and_metadata( &self, + client: Option, ) -> CrackedResult<(SongbirdInput, Vec)> { use colored::Colorize; - let client = http_utils::get_client().clone(); + let client = client.unwrap_or_else(|| http_utils::get_client().clone()); tracing::warn!("{}", format!("query_type: {:?}", self).red()); match self { QueryType::YoutubeSearch(query) => { @@ -588,26 +623,36 @@ impl QueryType { let mut res = Vec::new(); let asdf = ytdl.search(None).await?; for metadata in asdf { - let my_metadata = MyAuxMetadata::Data(metadata); + let my_metadata = MyAuxMetadata(metadata); res.push(my_metadata); } Ok((ytdl.into(), res)) }, QueryType::VideoLink(query) => { tracing::warn!("In VideoLink"); - video_info_to_source_and_metadata(client.clone(), query.clone()).await + let search = get_rusty_search(client.clone(), query.clone()).await?; + let metadata = search + .clone() + .metadata + .into_iter() + .map(MyAuxMetadata) + .collect::>(); + Ok((search.into(), metadata)) // let mut ytdl = YoutubeDl::new(client, query.clone()); // let metadata = ytdl.aux_metadata().await?; - // let my_metadata = MyAuxMetadata::Data(metadata); + // let my_metadata = MyAuxMetadata(metadata); // Ok((ytdl.into(), vec![my_metadata])) }, QueryType::Keywords(query) => { tracing::warn!("In Keywords"); - // video_info_to_source_and_metadata(client.clone(), query.clone()).await - let mut ytdl = YoutubeDl::new_search(client, query.clone()); - let metadata = ytdl.aux_metadata().await?; - let my_metadata = MyAuxMetadata::Data(metadata); - Ok((ytdl.into(), vec![my_metadata])) + // get_rusty_search(client.clone(), query.clone()).await + let (input, metadata) = + search_query_to_source_and_metadata(client.clone(), query.clone()).await?; + Ok((input, metadata)) + // let mut ytdl = YoutubeDl::new_search(client, query.clone()); + // let metadata = ytdl.aux_metadata().await?; + // let my_metadata = MyAuxMetadata(metadata); + // Ok((ytdl.into(), vec![my_metadata])) }, QueryType::File(file) => { tracing::warn!("In File"); @@ -618,7 +663,7 @@ impl QueryType { }, QueryType::NewYoutubeDl(data) => { let (ytdl, aux_metadata) = data.clone(); - Ok((ytdl.into(), vec![MyAuxMetadata::Data(aux_metadata)])) + Ok((ytdl.into(), vec![MyAuxMetadata(aux_metadata)])) }, QueryType::PlaylistLink(url) => { tracing::warn!("In PlaylistLink"); @@ -632,7 +677,7 @@ impl QueryType { let res = rytdl.rusty_ytdl.search(url, Some(&search_options)).await?; let mut metadata = Vec::with_capacity(res.len()); for r in res { - metadata.push(MyAuxMetadata::Data( + metadata.push(MyAuxMetadata( RustyYoutubeClient::search_result_to_aux_metadata(&r), )); } @@ -652,7 +697,7 @@ impl QueryType { ); tracing::warn!("ytdl: {:?}", ytdl); let metdata = ytdl.aux_metadata().await.unwrap(); - let my_metadata = MyAuxMetadata::Data(metdata); + let my_metadata = MyAuxMetadata(metdata); Ok((ytdl.into(), vec![my_metadata])) }, QueryType::KeywordList(keywords_list) => { @@ -667,7 +712,7 @@ impl QueryType { return Err(CrackedError::AudioStream(e)); }, }; - let my_metadata = MyAuxMetadata::Data(metdata); + let my_metadata = MyAuxMetadata(metdata); Ok((ytdl.into(), vec![my_metadata])) }, QueryType::None => unimplemented!(), @@ -756,12 +801,34 @@ pub async fn query_type_from_url( }, Some("www.youtube.com") => { // Handle youtube playlist - if url.contains("playlist") { - tracing::warn!("{}: {}", "youtube playlist".blue(), url.underline().blue()); - Some(QueryType::PlaylistLink(url.to_string())) - } else { - Some(QueryType::VideoLink(url.to_string())) + let opt_query = url_data + .query_pairs() + .filter_map(|(key, value)| { + if key == "list" || key == "playlist" { + tracing::warn!( + "{}: {}", + "youtube playlist".blue(), + url.underline().blue() + ); + Some(QueryType::PlaylistLink(value.to_string())) + } else { + None + } + }) + .next(); + match opt_query { + Some(query) => Some(query), + None => { + tracing::warn!("{}: {}", "youtube video".blue(), url.underline().blue()); + Some(QueryType::VideoLink(url.to_string())) + }, } + // if url.contains("playlist") || url.contains("list") { + // tracing::warn!("{}: {}", "youtube playlist".blue(), url.underline().blue()); + // Some(QueryType::PlaylistLink(url.to_string())) + // } else { + // Some(QueryType::VideoLink(url.to_string())) + // } }, // For all other domains fall back to yt-dlp. Some(other) => { diff --git a/crack-core/src/commands/music/play_utils/queue.rs b/crack-core/src/commands/music/play_utils/queue.rs index 55606c113..1622f0484 100644 --- a/crack-core/src/commands/music/play_utils/queue.rs +++ b/crack-core/src/commands/music/play_utils/queue.rs @@ -27,7 +27,7 @@ pub struct TrackReadyData { /// Takes a query and returns a track that is ready to be played, along with relevant metadata. pub async fn ready_query2(query_type: QueryType) -> Result { let (source, metadata_vec): (SongbirdInput, Vec) = - query_type.get_track_source_and_metadata().await?; + query_type.get_track_source_and_metadata(None).await?; let metadata = match metadata_vec.first() { Some(x) => x.clone(), None => { @@ -51,7 +51,7 @@ pub async fn ready_query( ) -> Result { let user_id = ctx.author().id; let (source, metadata_vec): (SongbirdInput, Vec) = - query_type.get_track_source_and_metadata().await?; + query_type.get_track_source_and_metadata(None).await?; let metadata = match metadata_vec.first() { Some(x) => x.clone(), None => { @@ -130,9 +130,26 @@ pub async fn queue_track_back( call: &Arc>, query_type: &QueryType, ) -> Result, CrackedError> { + let begin = std::time::Instant::now(); let ready_track = ready_query(ctx, query_type.clone()).await?; + let after_ready = std::time::Instant::now(); ctx.send_track_metadata_write_msg(&ready_track); - queue_track_ready_back(call, ready_track).await + let after_send = std::time::Instant::now(); + let queue = queue_track_ready_back(call, ready_track).await; + let after_queue = std::time::Instant::now(); + tracing::warn!( + r#" + after_ready: {:?} + after_send: {:?} + after_queue: {:?} + total: {:?} + "#, + after_ready.duration_since(begin), + after_send.duration_since(after_ready), + after_queue.duration_since(after_send), + after_queue.duration_since(begin) + ); + queue } /// Queue a list of tracks to be played. @@ -279,6 +296,7 @@ pub async fn queue_query_list_offset<'a>( /// Get the play mode and the message from the parameters to the play command. // TODO: There is a lot of cruft in this from the older version of this. Clean it up. +#[tracing::instrument] pub fn get_mode(is_prefix: bool, msg: Option, mode: Option) -> (Mode, String) { let opt_mode = mode.clone(); if is_prefix { @@ -339,6 +357,7 @@ pub fn get_mode(is_prefix: bool, msg: Option, mode: Option) -> ( /// based on types, it could be kind of mangled if the prefix version of the /// command is used. // TODO: Old and crufty. Clean up. +#[tracing::instrument] pub fn get_msg( mode: Option, query_or_url: Option, @@ -364,6 +383,7 @@ pub fn get_msg( /// Rotates the queue by `n` tracks to the right. #[cfg(not(tarpaulin_include))] +#[tracing::instrument] pub async fn _rotate_tracks( call: &Arc>, n: usize, diff --git a/crack-core/src/commands/music/remove.rs b/crack-core/src/commands/music/remove.rs index ffa599897..18e6e055b 100644 --- a/crack-core/src/commands/music/remove.rs +++ b/crack-core/src/commands/music/remove.rs @@ -5,7 +5,7 @@ use crate::{ messaging::message::CrackedMessage, messaging::messages::REMOVED_QUEUE, utils::send_reply, - utils::{get_track_metadata, send_embed_response_poise}, + utils::{get_track_handle_metadata, send_embed_response_poise}, Context, Error, }; use poise::serenity_prelude as serenity; @@ -92,7 +92,7 @@ pub async fn remove_internal( } async fn create_remove_enqueued_embed(track: &TrackHandle) -> CreateEmbed { - let metadata = get_track_metadata(track).await; + let metadata = get_track_handle_metadata(track).await; CreateEmbed::default() .field( REMOVED_QUEUE, diff --git a/crack-core/src/commands/music/skip.rs b/crack-core/src/commands/music/skip.rs index 8470c08fb..d59b9c16a 100644 --- a/crack-core/src/commands/music/skip.rs +++ b/crack-core/src/commands/music/skip.rs @@ -5,7 +5,7 @@ use crate::{ errors::{verify, CrackedError}, messaging::message::CrackedMessage, poise_ext::PoiseContextExt, - utils::get_track_metadata, + utils::get_track_handle_metadata, Context, Error, }; use serenity::all::Message; @@ -56,7 +56,7 @@ pub async fn create_skip_response( ) -> Result { let send_msg = match handler.queue().current() { Some(track) => { - let metadata = get_track_metadata(&track).await; + let metadata = get_track_handle_metadata(&track).await; CrackedMessage::SkipTo { title: metadata.title.as_ref().unwrap().to_owned(), url: metadata.source_url.as_ref().unwrap().to_owned(), @@ -93,7 +93,7 @@ pub async fn downvote(ctx: Context<'_>) -> Result<(), Error> { let handler = call.lock().await; let queue = handler.queue(); - let metadata = get_track_metadata(&queue.current().unwrap()).await; + let metadata = get_track_handle_metadata(&queue.current().unwrap()).await; let source_url = &metadata.source_url.ok_or("ASDF").unwrap(); let res1 = ctx.data().downvote_track(guild_id, source_url).await?; diff --git a/crack-core/src/commands/music/volume.rs b/crack-core/src/commands/music/volume.rs index d420ead1a..47cd18ed0 100644 --- a/crack-core/src/commands/music/volume.rs +++ b/crack-core/src/commands/music/volume.rs @@ -1,7 +1,8 @@ use self::serenity::builder::CreateEmbed; -use crate::commands::{cmd_check_music, help, ContextExt}; +use crate::commands::{cmd_check_music, help}; use crate::errors::CrackedError; use crate::guild::settings::GuildSettings; +use crate::poise_ext::ContextExt; use crate::utils::{get_guild_name, send_embed_response_poise}; use crate::{Context, Error}; use colored::Colorize; diff --git a/crack-core/src/commands/music_utils.rs b/crack-core/src/commands/music_utils.rs index 26feb1908..e862a6a86 100644 --- a/crack-core/src/commands/music_utils.rs +++ b/crack-core/src/commands/music_utils.rs @@ -82,6 +82,7 @@ pub async fn set_global_handlers( /// Get the call handle for songbird. #[cfg(not(tarpaulin_include))] +#[tracing::instrument] pub async fn get_call_or_join_author(ctx: Context<'_>) -> Result>, CrackedError> { let guild_id = ctx.guild_id().ok_or(CrackedError::NoGuildId)?; let manager = songbird::get(ctx.serenity_context()) @@ -104,6 +105,7 @@ pub async fn get_call_or_join_author(ctx: Context<'_>) -> Result } /// Join a voice channel. +#[tracing::instrument] pub async fn do_join( ctx: Context<'_>, manager: &songbird::Songbird, diff --git a/crack-core/src/commands/permissions.rs b/crack-core/src/commands/permissions.rs index 7914c33bf..a0269932c 100644 --- a/crack-core/src/commands/permissions.rs +++ b/crack-core/src/commands/permissions.rs @@ -15,6 +15,7 @@ pub async fn cmd_check_music(ctx: Context<'_>) -> Result { cmd_check_music_internal(member, channel_id, ctx).await } +/// Internal function (doesn't parse arguments). pub async fn cmd_check_music_internal( member: Option>, channel_id: ChannelId, diff --git a/crack-core/src/commands/playlist/add_to_playlist.rs b/crack-core/src/commands/playlist/add_to_playlist.rs index 08df18c12..e0bae49c7 100644 --- a/crack-core/src/commands/playlist/add_to_playlist.rs +++ b/crack-core/src/commands/playlist/add_to_playlist.rs @@ -1,5 +1,5 @@ use crate::{ - commands::MyAuxMetadata, + commands::{cmd_check_music, MyAuxMetadata}, db::aux_metadata_to_db_structures, db::{metadata::Metadata, MetadataAnd, Playlist}, errors::CrackedError, @@ -10,7 +10,14 @@ use sqlx::PgPool; /// Adds a song to a playlist #[cfg(not(tarpaulin_include))] -#[poise::command(prefix_command, slash_command, guild_only, rename = "addto")] +#[poise::command( + category = "Music", + check = "cmd_check_music", + prefix_command, + slash_command, + guild_only, + rename = "addto" +)] pub async fn add_to_playlist( ctx: Context<'_>, #[rest] @@ -26,7 +33,7 @@ pub async fn add_to_playlist( let cur_track = queue.current().ok_or(CrackedError::NothingPlaying)?; let typemap = cur_track.typemap().read().await; let metadata = match typemap.get::() { - Some(MyAuxMetadata::Data(meta)) => meta, + Some(MyAuxMetadata(meta)) => meta, None => { return Err(CrackedError::Other("Failed to get metadata for track.").into()); }, diff --git a/crack-core/src/commands/playlist/create_playlist.rs b/crack-core/src/commands/playlist/create_playlist.rs index cb675d466..bf778a25d 100644 --- a/crack-core/src/commands/playlist/create_playlist.rs +++ b/crack-core/src/commands/playlist/create_playlist.rs @@ -1,10 +1,18 @@ use crate::{ - db::playlist::Playlist, messaging::message::CrackedMessage, utils::send_reply, Context, Error, + commands::cmd_check_music, db::playlist::Playlist, messaging::message::CrackedMessage, + utils::send_reply, Context, Error, }; /// Creates a playlist #[cfg(not(tarpaulin_include))] -#[poise::command(prefix_command, slash_command, guild_only, rename = "create")] +#[poise::command( + category = "Music", + check = "cmd_check_music", + prefix_command, + slash_command, + guild_only, + rename = "create" +)] pub async fn create_playlist(ctx: Context<'_>, name: String) -> Result<(), Error> { // Assuming you have a way to fetch the user_id of the command issuer let user_id = ctx.author().id.get() as i64; diff --git a/crack-core/src/commands/playlist/delete_playlist.rs b/crack-core/src/commands/playlist/delete_playlist.rs index 25427175c..a6c518bfc 100644 --- a/crack-core/src/commands/playlist/delete_playlist.rs +++ b/crack-core/src/commands/playlist/delete_playlist.rs @@ -1,10 +1,18 @@ use crate::{ - db::playlist::Playlist, messaging::message::CrackedMessage, utils::send_reply, Context, Error, + commands::cmd_check_music, db::playlist::Playlist, messaging::message::CrackedMessage, + utils::send_reply, Context, Error, }; /// Deletes a playlist #[cfg(not(tarpaulin_include))] -#[poise::command(prefix_command, slash_command, guild_only, rename = "delete")] +#[poise::command( + category = "Music", + check = "cmd_check_music", + prefix_command, + slash_command, + guild_only, + rename = "delete" +)] pub async fn delete_playlist(ctx: Context<'_>, playlist_id: i32) -> Result<(), Error> { // Assuming you have a way to fetch the user_id of the command issuer let user_id = ctx.author().id.get() as i64; diff --git a/crack-core/src/commands/playlist/get_playlist.rs b/crack-core/src/commands/playlist/get_playlist.rs index 8ec2ab2dd..443db5dfb 100644 --- a/crack-core/src/commands/playlist/get_playlist.rs +++ b/crack-core/src/commands/playlist/get_playlist.rs @@ -1,5 +1,5 @@ use crate::{ - commands::MyAuxMetadata, + commands::{cmd_check_music, MyAuxMetadata}, db::{metadata::aux_metadata_from_db, playlist::Playlist, Metadata}, utils::{build_tracks_embed_metadata, send_embed_response_poise}, Context, CrackedError, Error, @@ -7,7 +7,13 @@ use crate::{ /// Get a playlist #[cfg(not(tarpaulin_include))] -#[poise::command(prefix_command, slash_command, rename = "get")] +#[poise::command( + category = "Music", + check = "cmd_check_music", + prefix_command, + slash_command, + rename = "get" +)] pub async fn get_playlist(ctx: Context<'_>, #[rest] playlist: String) -> Result<(), Error> { let (aux_metadata, playlist_name): (Vec, String) = get_playlist_(ctx, playlist).await?; @@ -43,7 +49,7 @@ pub async fn get_playlist_( let aux_metadata = metadata .iter() .flat_map(|m| match aux_metadata_from_db(m) { - Ok(aux) => Some(MyAuxMetadata::Data(aux.clone())), + Ok(aux) => Some(MyAuxMetadata(aux.clone())), Err(e) => { tracing::error!("Error converting metadata to aux metadata: {}", e); None diff --git a/crack-core/src/commands/playlist/loadspotify.rs b/crack-core/src/commands/playlist/loadspotify.rs index c82f5ede7..a273dfbc4 100644 --- a/crack-core/src/commands/playlist/loadspotify.rs +++ b/crack-core/src/commands/playlist/loadspotify.rs @@ -1,5 +1,5 @@ use crate::{ - commands::MyAuxMetadata, + commands::{cmd_check_music, MyAuxMetadata}, db::{aux_metadata_to_db_structures, playlist::Playlist, Metadata}, errors::verify, http_utils, @@ -61,7 +61,7 @@ pub async fn loadspotify_( let playls = Playlist::create(db_pool, &name.clone(), ctx.author().id.get() as i64).await?; let guild_id_i64 = guild_id.get() as i64; let channel_id_i64 = channel_id.get() as i64; - for MyAuxMetadata::Data(m) in metadata { + for MyAuxMetadata(m) in metadata { metadata_vec.push(m.clone()); let res = aux_metadata_to_db_structures(&m, guild_id_i64, channel_id_i64); match res { @@ -87,7 +87,12 @@ pub async fn loadspotify_( /// Get a playlist #[cfg(not(tarpaulin_include))] -#[poise::command(prefix_command, slash_command)] +#[poise::command( + category = "Music", + check = "cmd_check_music", + prefix_command, + slash_command +)] pub async fn loadspotify( ctx: Context<'_>, #[description = "Spotify.com url to the *public* playlist."] spotifyurl: String, diff --git a/crack-core/src/commands/playlist/mod.rs b/crack-core/src/commands/playlist/mod.rs index 6a568078a..faa648be6 100644 --- a/crack-core/src/commands/playlist/mod.rs +++ b/crack-core/src/commands/playlist/mod.rs @@ -12,7 +12,7 @@ pub use delete_playlist::delete_playlist as delete; pub use get_playlist::get_playlist as get; pub use list_playlists::list_playlists as list; pub use loadspotify::loadspotify; -pub use play_playlist::play_playlist as play; +pub use play_playlist::play_playlist as pplay; use crate::{ commands::{cmd_check_music, sub_help as help}, @@ -32,7 +32,7 @@ use crate::{ "delete", "get", "list", - "play", + "pplay", "loadspotify", "help" ), @@ -51,15 +51,15 @@ pub async fn playlist(ctx: Context<'_>) -> Result<(), Error> { Ok(()) } -pub fn commands() -> [crate::Command; 1] { - [playlist()] - // [ - // addto(), - // create(), - // delete(), - // get(), - // list(), - // play(), - // loadspotify(), - // ] +pub fn commands() -> [crate::Command; 8] { + [ + playlist(), + addto(), + create(), + delete(), + get(), + list(), + pplay(), + loadspotify(), + ] } diff --git a/crack-core/src/commands/playlist/play_playlist.rs b/crack-core/src/commands/playlist/play_playlist.rs index 5387ff8bb..5821c4503 100644 --- a/crack-core/src/commands/playlist/play_playlist.rs +++ b/crack-core/src/commands/playlist/play_playlist.rs @@ -1,12 +1,18 @@ use super::get_playlist::get_playlist_; -use crate::commands::queue_aux_metadata; +use crate::commands::{cmd_check_music, queue_aux_metadata}; use crate::messaging::message::CrackedMessage; use crate::utils::send_reply; use crate::{Context, Error}; /// Queue a playlist on the bot. #[cfg(not(tarpaulin_include))] -#[poise::command(prefix_command, slash_command, rename = "play")] +#[poise::command( + category = "Music", + check = "cmd_check_music", + prefix_command, + slash_command, + rename = "pplay" +)] pub async fn play_playlist( ctx: Context<'_>, #[rest] diff --git a/crack-core/src/commands/register.rs b/crack-core/src/commands/register.rs index 728cdc530..7cb5ce4d6 100644 --- a/crack-core/src/commands/register.rs +++ b/crack-core/src/commands/register.rs @@ -1,3 +1,5 @@ +//! FIXME: This is mostly ripped from the poise repo. +//! It would be nice to be able to not maintain these functions for ourselves. //! Utilities for registering application commands use poise::serenity_prelude as serenity; diff --git a/crack-core/src/config.rs b/crack-core/src/config.rs index c286accd9..8d751c777 100644 --- a/crack-core/src/config.rs +++ b/crack-core/src/config.rs @@ -312,19 +312,26 @@ pub async fn poise_framework( tracing::warn!("Received Ctrl-C, shutting down..."); let guilds = data2.guild_settings_map.read().await.clone(); let pool = data2.clone().database_pool.clone(); + let mut saved_guilds = Vec::with_capacity(guilds.len()); + println!("Saving guilds..."); if pool.is_some() { let p = pool.unwrap(); for (k, v) in guilds { - tracing::warn!("Saving Guild: {}", k); + //tracing::warn!("Saving Guild: {}", k); match v.save(&p).await { - Ok(_) => {}, + Ok(_) => { + saved_guilds.push(k); + }, Err(e) => { tracing::error!("Error saving guild settings: {}", e); }, } } + p.close().await; } + println!("Saved guilds: {:?}", saved_guilds); + tracing::trace!("Saved guilds: {:?}", saved_guilds); shard_manager.clone().shutdown_all().await; diff --git a/crack-core/src/db/command_channel.rs b/crack-core/src/db/command_channel.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/crack-core/src/db/guild.rs b/crack-core/src/db/guild.rs index b056ad314..6b6fefd81 100644 --- a/crack-core/src/db/guild.rs +++ b/crack-core/src/db/guild.rs @@ -50,16 +50,6 @@ pub struct GuildEntity { pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, } -// CREATE TABLE log_settings ( -// guild_id BIGINT PRIMARY KEY, -// all_log_channel BIGINT, -// raw_event_log_channel BIGINT, -// server_log_channel BIGINT, -// member_log_channel BIGINT, -// join_leave_log_channel BIGINT, -// voice_log_channel BIGINT, -// CONSTRAINT fk_log_settings FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) -// ); #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct LogSettingsRead { @@ -267,25 +257,6 @@ impl GuildEntity { .save(pool, guild_id) .await?; } - // let guard: RwLockReadGuard = settings.command_channels.read().await; - // for (cmd, val) in guard.clone() { - // //.expect("BUG! SHOULD BE SET") { - // for (channel_id, perms) in val { - // let perms_borrow = if perms.id == 0 { - // let perms = perms.insert_permission_settings(pool).await?; - // perms.clone() - // } else { - // perms - // }; - // let ch = CommandChannel { - // command: cmd.to_string(), - // channel_id: serenity::ChannelId::new(channel_id), - // guild_id: GuildId::new(guild_id as u64), - // permission_settings: perms_borrow.clone(), - // }; - // CommandChannel::save(&ch, pool).await?; - // } - // } Ok(()) } @@ -400,71 +371,49 @@ impl GuildEntity { name: String, prefix: String, ) -> Result<(GuildEntity, GuildSettings), SerenityError> { - let guild_opt: Option = match sqlx::query_as!( + let guild_entity = sqlx::query_as!( GuildEntity, r#" - SELECT * FROM guild - WHERE id = $1 - "#, - guild_id - ) - .fetch_one(pool) - .await - { - Ok(guild) => Some(guild), - Err(sqlx::Error::RowNotFound) => None, - Err(e) => return Err(e.into()), - }; - - let (guild, settings) = match guild_opt { - Some(guild) => ( - guild.clone(), - guild.clone().get_settings(pool).await.unwrap(), - ), - None => { - let guild_entity = sqlx::query_as!( - GuildEntity, - r#" INSERT INTO guild (id, name) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET name = $2, updated_at = now() RETURNING * "#, - guild_id, - name - ) - .fetch_one(pool) - .await?; + guild_id, + name + ) + .fetch_one(pool) + .await?; - let guild_settings = sqlx::query_as!( - GuildSettingsRead, - r#" + let guild_settings = sqlx::query_as!( + GuildSettingsRead, + r#" INSERT INTO guild_settings (guild_id, guild_name, prefix) VALUES ($1, $2, $3) ON CONFLICT (guild_id) DO UPDATE SET guild_name = $2 RETURNING * "#, - guild_id, - Some(name.clone()), - prefix - ) - .fetch_one(pool) - .await?; + guild_id, + Some(name.clone()), + prefix + ) + .fetch_one(pool) + .await?; - let welcome_settings = GuildEntity::get_welcome_settings(pool, guild_id).await?; - let log_settings = GuildEntity::get_log_settings(pool, guild_id).await?; - let command_settings = GuildEntity::load_command_settings(guild_id, pool).await?; - let guild_settings = GuildSettings::from(guild_settings) - .with_welcome_settings(welcome_settings) - .with_log_settings(log_settings) - .with_command_settings(command_settings); - (guild_entity, guild_settings) - }, - }; + let welcome_settings = GuildEntity::get_welcome_settings(pool, guild_id).await?; + let log_settings = GuildEntity::get_log_settings(pool, guild_id).await?; + let command_settings = GuildEntity::load_command_settings(guild_id, pool).await?; + let guild_settings = GuildSettings::from(guild_settings) + .with_welcome_settings(welcome_settings) + .with_log_settings(log_settings) + .with_command_settings(command_settings); + //(guild_entity, guild_settings) + // }, + //}; - Ok((guild, settings)) + Ok((guild_entity, guild_settings)) } /// Update the prefix for the guild. @@ -524,3 +473,179 @@ impl GuildEntity { .map_err(|e| e.into()) } } + +#[cfg(test)] +mod test { + use std::collections::HashSet; + + use super::*; + use sqlx::PgPool; + + pub static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./test_migrations"); + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_get_or_create(pool: PgPool) { + let (guild, settings) = crate::db::guild::GuildEntity::get_or_create( + &pool, + 123, + "test".to_string(), + "test".to_string(), + ) + .await + .unwrap(); + assert_eq!(guild.id, 123); + assert_eq!(guild.name, "test"); + assert_eq!(settings.guild_id.get(), 123); + assert_eq!(settings.guild_name, "test"); + assert_eq!(settings.prefix, "test"); + } + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_update_prefix(pool: PgPool) { + let (mut guild, _) = crate::db::guild::GuildEntity::get_or_create( + &pool, + 123, + "test".to_string(), + "test".to_string(), + ) + .await + .unwrap(); + guild + .update_prefix(&pool, "new_prefix".to_string()) + .await + .unwrap(); + let guild = crate::db::guild::GuildEntity::get(&pool, 123) + .await + .unwrap() + .unwrap(); + assert_eq!(guild.name, "test"); + let settings = guild.get_settings(&pool).await.unwrap(); + + assert_eq!(settings.prefix, "new_prefix"); + } + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_get_write_settings(pool: PgPool) { + let (guild, settings) = crate::db::guild::GuildEntity::get_or_create( + &pool, + 123, + "test".to_string(), + "test".to_string(), + ) + .await + .unwrap(); + + let guild_id = guild.id; + + let welcome_settings = GuildEntity::get_welcome_settings(&pool, guild_id) + .await + .unwrap(); + let log_settings = GuildEntity::get_log_settings(&pool, guild_id) + .await + .unwrap(); + + assert!(welcome_settings.is_none()); + assert!(log_settings.is_none()); + + let welcome_settings_new = crate::guild::settings::WelcomeSettings { + auto_role: Some(123), + channel_id: Some(123), + message: Some("test".to_string()), + password: None, + }; + + let settings = settings.with_welcome_settings(Some(welcome_settings_new.clone())); + + GuildEntity::write_settings(&pool, &settings).await.unwrap(); + + let welcome_settings = GuildEntity::get_welcome_settings(&pool, guild_id) + .await + .unwrap() + .unwrap(); + + assert_eq!(welcome_settings, welcome_settings_new); + } + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_write_banned_comains(pool: PgPool) { + let (guild, _) = crate::db::guild::GuildEntity::get_or_create( + &pool, + 123, + "test".to_string(), + "test".to_string(), + ) + .await + .unwrap(); + + let banned_domains = vec!["test".to_string(), "test2".to_string()]; + + guild + .write_banned_domains(&pool, banned_domains.clone()) + .await + .unwrap(); + + let settings = guild.get_settings(&pool).await.unwrap(); + + assert_eq!( + settings.banned_domains.iter().collect::>(), + banned_domains.iter().collect::>() + ); + } + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_write_allowed_domains(pool: PgPool) { + let (guild, _) = crate::db::guild::GuildEntity::get_or_create( + &pool, + 123, + "test".to_string(), + "test".to_string(), + ) + .await + .unwrap(); + + let allowed_domains = vec!["test".to_string(), "test2".to_string()]; + + guild + .write_allowed_domains(&pool, allowed_domains.clone()) + .await + .unwrap(); + + let settings = guild.get_settings(&pool).await.unwrap(); + + assert_eq!( + settings.allowed_domains.iter().collect::>(), + allowed_domains.iter().collect::>() + ); + } + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_write_log_settings(pool: PgPool) { + let (guild, _) = crate::db::guild::GuildEntity::get_or_create( + &pool, + 123, + "test".to_string(), + "test".to_string(), + ) + .await + .unwrap(); + + let guild_id = guild.id; + + let log_settings = crate::guild::settings::LogSettings { + all_log_channel: Some(123), + raw_event_log_channel: Some(123), + server_log_channel: Some(123), + member_log_channel: Some(123), + join_leave_log_channel: Some(123), + voice_log_channel: Some(123), + }; + + GuildEntity::write_log_settings(&pool, guild_id, &log_settings) + .await + .unwrap(); + + let settings = guild.get_settings(&pool).await.unwrap(); + + assert_eq!(settings.log_settings, Some(log_settings)); + } +} diff --git a/crack-core/src/db/metadata.rs b/crack-core/src/db/metadata.rs index b8c9aaf88..08997a31c 100644 --- a/crack-core/src/db/metadata.rs +++ b/crack-core/src/db/metadata.rs @@ -86,9 +86,9 @@ impl Metadata { let get_r: Result = sqlx::query_as!(MetadataRead, r#" SELECT metadata.id, metadata.track, metadata.artist, metadata.album, metadata.date, metadata.channels, metadata.channel, metadata.start_time, metadata.duration, metadata.sample_rate, metadata.source_url, metadata.title, metadata.thumbnail - FROM + FROM metadata - WHERE + WHERE metadata.source_url = $1 "#, in_metadata.source_url, @@ -143,23 +143,24 @@ impl Metadata { /// Get a metadata entry by id (url). pub async fn get_by_url(pool: &PgPool, url: &str) -> Result, CrackedError> { - let r: Option = sqlx::query_as!(MetadataRead, r#" - SELECT + // let r: Option = sqlx::query_as!(MetadataRead, + sqlx::query_as!(MetadataRead, + r#"SELECT metadata.id, metadata.track, metadata.artist, metadata.album, metadata.date, metadata.channels, metadata.channel, metadata.start_time, metadata.duration, metadata.sample_rate, metadata.source_url, metadata.title, metadata.thumbnail - FROM + FROM metadata - WHERE - metadata.source_url = $1 - "#, + WHERE + metadata.source_url = $1"#, url ) .fetch_optional(pool) .await - .map_err(CrackedError::SQLX)?; - match r { - Some(r) => Ok(Some(r.into())), - None => Ok(None), - } + .map_err(CrackedError::SQLX) + .map(|r| r.map(|r| r.into())) + // match r { + // Some(r) => Ok(Some(r.into())), + // None => Ok(None), + // } } } @@ -217,10 +218,10 @@ pub async fn playlist_track_to_metadata( }) } -use crate::db; - use super::PlaylistTrack; - +use crate::db; +/// This enum holds metadata and another type... which apprently there's going to be another use for? +/// Or maybe this was better for ergonimics? pub enum MetadataAnd { Track(Metadata, PlaylistTrack), } @@ -310,22 +311,6 @@ pub fn aux_metadata_from_db(metadata: &Metadata) -> Result Result<(), Error> { + let user_id = 1; + let guild_id = 1; + let metadata_id = 1; + let play_log = PlayLog::create(&pool, user_id, guild_id, metadata_id).await?; + assert_eq!(play_log.user_id, user_id); + assert_eq!(play_log.guild_id, guild_id); + assert_eq!(play_log.metadata_id, metadata_id); + Ok(()) + } + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_playlog_get_last_played(pool: PgPool) -> Result<(), Error> { + let user_id = 2; + let guild_id = 1; + let metadata_id = 1; + PlayLog::create(&pool, user_id, guild_id, metadata_id).await?; + let last_played = PlayLog::get_last_played(&pool, None, Some(guild_id)).await?; + assert_eq!(last_played.len(), 1); + Ok(()) + } + + #[sqlx::test(migrator = "MIGRATOR")] + async fn test_playlog_get_last_played_by_user(pool: PgPool) -> Result<(), Error> { + let user_id = 3; + let guild_id = 1; + let metadata_id = 1; + PlayLog::create(&pool, user_id, guild_id, metadata_id).await?; + PlayLog::create(&pool, user_id, guild_id, metadata_id + 1).await?; + PlayLog::create(&pool, user_id, guild_id, metadata_id + 2).await?; + let last_played = PlayLog::get_last_played(&pool, Some(user_id), None).await?; + assert_eq!(last_played.len(), 3); + Ok(()) + } +} diff --git a/crack-core/src/db/worker_pool.rs b/crack-core/src/db/worker_pool.rs index ea29f9ca5..dd04ed083 100644 --- a/crack-core/src/db/worker_pool.rs +++ b/crack-core/src/db/worker_pool.rs @@ -129,18 +129,19 @@ mod test { source_url: Some(url.clone()), ..Default::default() }, - user_id: UserId::new(1), + user_id: UserId::new(100), username: "test".to_string(), guild_id: GuildId::new(1), channel_id: ChannelId::new(1), }; sender.send(data).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; let metadata = crate::db::metadata::Metadata::get_by_url(&pool, &url) .await .unwrap() .unwrap(); - assert_eq!(metadata.id, 1); + // test data has id 1 + assert_eq!(metadata.id, 4); } #[sqlx::test(migrator = "MIGRATOR")] diff --git a/crack-core/src/guild/operations.rs b/crack-core/src/guild/operations.rs index cb507a6d0..d475846bb 100644 --- a/crack-core/src/guild/operations.rs +++ b/crack-core/src/guild/operations.rs @@ -342,10 +342,11 @@ pub async fn get_guilds(ctx: Arc) -> Vec { #[cfg(test)] mod test { use super::*; + use crate::guild::cache::GuildCache; use crate::{Data, DataInner}; use serenity::model::id::ChannelId; use std::collections::HashMap; - use tokio::sync::RwLock; + use tokio::sync::{Mutex, RwLock}; #[tokio::test] async fn test_get_guild_settings() { @@ -641,4 +642,39 @@ mod test { assert_eq!(data.get_autopause(guild_id).await, false); } + + #[tokio::test] + async fn test_get_set_autorole() { + let mut guild_settings_map = HashMap::new(); + let guild_id = GuildId::new(1); + let auto_role = 123; + let mut settings = crate::GuildSettings::default(); + settings.set_auto_role(Some(auto_role)); + guild_settings_map.insert(guild_id, settings); + let data = Arc::new(Data(Arc::new(DataInner { + guild_settings_map: Arc::new(RwLock::new(guild_settings_map)), + ..Default::default() + }))); + + assert_eq!(data.get_auto_role(guild_id).await, Some(auto_role)); + } + + #[tokio::test] + async fn test_get_set_autoplay() { + let mut guild_cache_map = HashMap::new(); + let guild_id = GuildId::new(1); + guild_cache_map.insert( + guild_id, + GuildCache { + autoplay: false, + ..Default::default() + }, + ); + let data = Arc::new(Data(Arc::new(DataInner { + guild_cache_map: Arc::new(Mutex::new(guild_cache_map)), + ..Default::default() + }))); + + assert_eq!(data.get_autoplay(guild_id).await, false); + } } diff --git a/crack-core/src/guild/settings.rs b/crack-core/src/guild/settings.rs index ae91f0a8f..7aa3b1783 100644 --- a/crack-core/src/guild/settings.rs +++ b/crack-core/src/guild/settings.rs @@ -494,7 +494,6 @@ impl GuildSettings { } pub async fn save(&self, pool: &PgPool) -> Result<(), CrackedError> { - tracing::warn!("Saving guild settings: {:?}", self); let guild_id = self.guild_id.get() as i64; let guild_name = self.guild_name.clone(); let prefix = self.prefix.clone(); @@ -511,7 +510,6 @@ impl GuildSettings { /// Save the guild settings to a JSON file. Unused? pub fn save_json(&self) -> Result<(), CrackedError> { - tracing::warn!("Saving guild settings: {:?}", self); create_dir_all(SETTINGS_PATH.as_str())?; let path = format!( "{}/{}-{}.json", diff --git a/crack-core/src/handlers/event_log.rs b/crack-core/src/handlers/event_log.rs index 1e763dba1..c2f45e429 100644 --- a/crack-core/src/handlers/event_log.rs +++ b/crack-core/src/handlers/event_log.rs @@ -92,7 +92,7 @@ pub async fn handle_event( ) -> Result<(), Error> { // let event_log = Arc::new(&data_global.event_log); - use crate::db::GuildEntity; + use crate::{db::GuildEntity, guild::settings::DEFAULT_PREFIX}; let event_log = std::sync::Arc::new(&data_global.event_log_async); let event_name = event_in.snake_case_name(); let guild_settings = &data_global.guild_settings_map; @@ -348,28 +348,29 @@ pub async fn handle_event( FullEvent::GuildCreate { guild, is_new } => { if is_new.unwrap_or(false) { tracing::warn!("New Guild!!! {}", guild.name); - let mut guild_settings: Option = None; - if data_global.database_pool.is_some() { + } + match data_global.database_pool.as_ref() { + Some(p) => { + let prefix = data_global + .bot_settings + .prefix + .clone() + .unwrap_or(DEFAULT_PREFIX.to_string()); let (_, new_guild_settings) = GuildEntity::get_or_create( - data_global.database_pool.as_ref().unwrap(), + p, guild.id.get() as i64, guild.name.clone(), - data_global - .bot_settings - .prefix - .clone() - .unwrap_or("r!".to_string()), + prefix, ) .await?; - guild_settings = Some(new_guild_settings); - } else { + data_global + .insert_guild(guild.id, new_guild_settings) + .await?; + }, + None => { tracing::error!("No database pool available"); - }; - if guild_settings.is_some() { - let settings = guild_settings.unwrap(); - data_global.insert_guild(guild.id, settings).await?; - } - } + }, + }; log_event!( log_guild_create, guild_settings, diff --git a/crack-core/src/handlers/serenity.rs b/crack-core/src/handlers/serenity.rs index ac953fa6c..9a0a1a5c2 100644 --- a/crack-core/src/handlers/serenity.rs +++ b/crack-core/src/handlers/serenity.rs @@ -222,8 +222,6 @@ impl EventHandler for SerenityHandler { let mut x = 0; for (key, value) in guild_settings_map.clone().iter() { - tracing::info!("Guild {} settings: {:?}", key, value); - data_write.insert(*key, value.clone()); x += 1; } @@ -372,7 +370,6 @@ impl SerenityHandler { guilds: &Vec, ) -> Result<(), SerenityError> { let prefix = self.data.bot_settings.get_prefix(); - tracing::info!("Loading guilds' settings"); let mut guild_settings_list: Vec = Vec::new(); for guild_id in guilds { diff --git a/crack-core/src/handlers/track_end.rs b/crack-core/src/handlers/track_end.rs index 31fa9d2b8..837747427 100644 --- a/crack-core/src/handlers/track_end.rs +++ b/crack-core/src/handlers/track_end.rs @@ -121,7 +121,7 @@ impl EventHandler for TrackEndHandler { }, }; let track_ready = ready_query2(query).await.ok()?; - let MyAuxMetadata::Data(metadata) = &track_ready.metadata; + let MyAuxMetadata(metadata) = &track_ready.metadata; let metadata = Some(metadata.clone()); let track = queue_track_ready_front(&self.call, track_ready) diff --git a/crack-core/src/handlers/voice_chat_stats.rs b/crack-core/src/handlers/voice_chat_stats.rs index a8a102e91..848170d35 100644 --- a/crack-core/src/handlers/voice_chat_stats.rs +++ b/crack-core/src/handlers/voice_chat_stats.rs @@ -1,5 +1,5 @@ use crate::{ - commands::{deafen_internal, mute_internal}, + commands::admin::{deafen::deafen_internal, mute::mute_internal}, errors::CrackedError, messaging::messages::UNKNOWN, BotConfig, CamKickConfig, diff --git a/crack-core/src/http_utils.rs b/crack-core/src/http_utils.rs index f4586e01d..be6776d3b 100644 --- a/crack-core/src/http_utils.rs +++ b/crack-core/src/http_utils.rs @@ -154,6 +154,14 @@ static CLIENT: Lazy = Lazy::new(|| { .expect("Failed to build reqwest client") }); +/// Build a reqwest client with rustls. +pub fn build_client() -> Client { + reqwest::ClientBuilder::new() + .use_rustls_tls() + .build() + .expect("Failed to build reqwest client") +} + /// Get a reference to the lazy, static, global reqwest client. pub fn get_client() -> &'static Client { &CLIENT @@ -264,4 +272,34 @@ mod test { let final_url = resolve_final_url(url).await.unwrap(); assert_eq!(final_url, "https://example.com/"); } + + #[test] + fn test_build_send_message_params() { + use crate::http_utils::SendMessageParams; + use crate::messaging::message::CrackedMessage; + use serenity::all::{ChannelId, Colour}; + + let channel_id = ChannelId::new(1); + let msg = CrackedMessage::Other("Hello, world!".to_string()); + let params = SendMessageParams::new(msg) + .with_as_embed(true) + .with_ephemeral(false) + .with_reply(true) + .with_color(Colour::BLUE) + .with_cache_msg(true) + .with_channel(channel_id) + .with_embed(None); + + assert_eq!(params.channel, channel_id); + assert_eq!(params.as_embed, true); + assert_eq!(params.ephemeral, false); + assert_eq!(params.reply, true); + assert_eq!(params.color, Colour::BLUE); + assert_eq!(params.cache_msg, true); + assert_eq!( + params.msg, + CrackedMessage::Other("Hello, world!".to_string()) + ); + assert_eq!(params.embed, None); + } } diff --git a/crack-core/src/messaging/interface.rs b/crack-core/src/messaging/interface.rs index 4f51f2b7e..921a295e6 100644 --- a/crack-core/src/messaging/interface.rs +++ b/crack-core/src/messaging/interface.rs @@ -12,7 +12,8 @@ use crate::{guild::settings::DEFAULT_LYRICS_PAGE_SIZE, utils::create_paged_embed use crate::{ messaging::message::CrackedMessage, utils::{ - build_footer_info, get_human_readable_timestamp, get_requesting_user, get_track_metadata, + build_footer_info, get_human_readable_timestamp, get_requesting_user, + get_track_handle_metadata, }, Context as CrackContext, Error, }; @@ -139,7 +140,7 @@ async fn create_queue_page(tracks: &[TrackHandle], page: usize) -> String { let mut description = String::new(); for (i, &t) in queue.iter().enumerate() { - let metadata = get_track_metadata(t).await; + let metadata = get_track_handle_metadata(t).await; let title = metadata.title.clone().unwrap_or_default(); let url = metadata.source_url.clone().unwrap_or_default(); let duration = get_human_readable_timestamp(metadata.duration); @@ -162,7 +163,7 @@ async fn create_queue_page(tracks: &[TrackHandle], page: usize) -> String { /// Creates a queue embed. pub async fn create_queue_embed(tracks: &[TrackHandle], page: usize) -> CreateEmbed { let (description, thumbnail) = if !tracks.is_empty() { - let metadata = get_track_metadata(&tracks[0]).await; + let metadata = get_track_handle_metadata(&tracks[0]).await; let url = metadata.thumbnail.clone().unwrap_or_default(); let thumbnail = match url::Url::parse(&url) { @@ -213,7 +214,7 @@ pub fn create_now_playing_embed_metadata( cur_position: Option, metadata: MyAuxMetadata, ) -> CreateEmbed { - let MyAuxMetadata::Data(metadata) = metadata; + let MyAuxMetadata(metadata) = metadata; tracing::warn!("metadata: {:?}", metadata); let title = metadata.title.clone().unwrap_or_default(); @@ -263,10 +264,10 @@ pub fn create_now_playing_embed_metadata( pub async fn track_handle_to_metadata( track: &TrackHandle, ) -> Result<(Option, Option, MyAuxMetadata), CrackedError> { - let metadata = get_track_metadata(track).await; + let metadata = get_track_handle_metadata(track).await; let requesting_user = get_requesting_user(track).await.ok(); let duration = Some(track.get_info().await.unwrap().position); - Ok((requesting_user, duration, MyAuxMetadata::Data(metadata))) + Ok((requesting_user, duration, MyAuxMetadata(metadata))) } /// Creates a now playing embed for the given track. diff --git a/crack-core/src/poise_ext.rs b/crack-core/src/poise_ext.rs index e43331946..d2cc2ef9d 100644 --- a/crack-core/src/poise_ext.rs +++ b/crack-core/src/poise_ext.rs @@ -1,6 +1,7 @@ use crate::commands::play_utils::TrackReadyData; use crate::commands::{has_voted_bot_id, MyAuxMetadata}; use crate::db; +use crate::db::write_metadata_pg; use crate::db::{MetadataMsg, PlayLog}; use crate::guild::operations::GuildSettingsOperations; use crate::guild::settings::GuildSettings; @@ -103,6 +104,10 @@ impl MessageInterfaceCtxExt for crate::Context<'_> { pub trait ContextExt { /// Send a message to tell the worker pool to do a db write when it feels like it. fn send_track_metadata_write_msg(&self, ready_track: &TrackReadyData); + fn async_send_track_metadata_write_msg( + &self, + ready_track: &TrackReadyData, + ) -> impl Future>; /// The the user id for the author of the message that created this context. fn get_user_id(&self) -> serenity::UserId; /// Gets the log of last played songs on the bot by a specific user @@ -188,10 +193,34 @@ impl ContextExt for crate::Context<'_> { .map_err(|e| e.into()) } + /// Send a message to tell the worker pool to do a db write when it feels like it. + async fn async_send_track_metadata_write_msg( + &self, + ready_track: &TrackReadyData, + ) -> CrackedResult<()> { + let username = ready_track.username.clone(); + let MyAuxMetadata(aux_metadata) = ready_track.metadata.clone(); + let user_id = ready_track.user_id; + let guild_id = self.guild_id().unwrap(); + let channel_id = self.channel_id(); + + let write_data: MetadataMsg = MetadataMsg { + aux_metadata, + user_id, + username, + guild_id, + channel_id, + }; + + let pool = self.data().get_db_pool().unwrap(); + write_metadata_pg(&pool, write_data).await?; + Ok(()) + } + /// Send a message to tell the worker pool to do a db write when it feels like it. fn send_track_metadata_write_msg(&self, ready_track: &TrackReadyData) { let username = ready_track.username.clone(); - let MyAuxMetadata::Data(aux_metadata) = ready_track.metadata.clone(); + let MyAuxMetadata(aux_metadata) = ready_track.metadata.clone(); let user_id = ready_track.user_id; let guild_id = self.guild_id().unwrap(); let channel_id = self.channel_id(); @@ -376,8 +405,8 @@ impl<'ctx> PoiseContextExt<'ctx> for crate::Context<'ctx> { } else { let c = colored::Color::TrueColor { r: params.color.r(), - g: params.color.r(), - b: params.color.r(), + g: params.color.g(), + b: params.color.b(), }; CreateReply::default().content(text.color(c).to_string()) }; diff --git a/crack-core/src/sources/rusty_ytdl.rs b/crack-core/src/sources/rusty_ytdl.rs index a91161240..6a0fb61df 100644 --- a/crack-core/src/sources/rusty_ytdl.rs +++ b/crack-core/src/sources/rusty_ytdl.rs @@ -2,6 +2,7 @@ use crate::{commands::play_utils::QueryType, errors::CrackedError, http_utils}; use bytes::Buf; use bytes::BytesMut; use rusty_ytdl::stream::Stream; +use rusty_ytdl::RequestOptions; use rusty_ytdl::{ search::{Playlist, SearchOptions, SearchResult, YouTube}, Video, VideoInfo, @@ -89,16 +90,23 @@ impl Display for RustyYoutubeClient { pub struct RustyYoutubeSearch { pub rusty_ytdl: RustyYoutubeClient, pub metadata: Option, + pub url: Option, + pub video: Option>, pub query: QueryType, } +type RustyYoutube = YouTube; +type YtdlpYoutube = YoutubeDl; +type YouTubeClient = either::Either; /// More general struct to wrap the search instances. Name this better. #[derive(Clone, Debug)] pub struct FastYoutubeSearch { pub query: QueryType, - pub client: reqwest::Client, - pub ytdl: either::Either, + pub reqwest_client: reqwest::Client, + pub ytdl_client: YouTubeClient, + pub url: Option, pub metadata: Option, + pub video: Option>, } impl Display for FastYoutubeSearch { @@ -108,7 +116,7 @@ impl Display for FastYoutubeSearch { r#"FastYT: Query: {:?} ytdl: {:?}"#, self.query.build_query(), - either::for_both!(&self.ytdl, ytdl => ytdl.as_string()), + either::for_both!(&self.ytdl_client, ytdl => ytdl.as_string()), ) } } @@ -123,7 +131,58 @@ impl Display for RustyYoutubeSearch { } } -/// Implementation of the `RustyYoutubeClient` struct. +/// Builder for the [`RequestOptions`] struct. +pub struct RequestOptionsBuilder { + pub client: Option, + pub ipv6_block: Option, +} + +/// Default for the [`RequestOptions`] struct. +impl Default for RequestOptionsBuilder { + fn default() -> Self { + Self { + client: None, + ipv6_block: Some("2001:4::/48".to_string()), + } + } +} + +/// Implementation of the builder for the [`RequestOptions`] struct. +impl RequestOptionsBuilder { + /// Creates a default builder. + pub fn new() -> Self { + Default::default() + } + + /// Sets the client for the builder, mutating. + pub fn set_client(mut self, client: reqwest::Client) -> Self { + self.client = Some(client); + self + } + + /// Sets the ipv6 block for the builder, mutating. + pub fn set_ipv6_block(mut self, ipv6_block: String) -> Self { + self.ipv6_block = Some(ipv6_block); + self + } + + /// Sets the client for the builder, mutating. + pub fn set_default_ipv6_block(mut self) -> Self { + self.ipv6_block = Some("2001:4::/48".to_string()); + self + } + + /// Builds the [`RequestOptions`] struct. + pub fn build(self) -> RequestOptions { + RequestOptions { + client: self.client, + ipv6_block: self.ipv6_block, + ..Default::default() + } + } +} + +/// Implementation of the [`RustyYoutubeClient`] struct. impl RustyYoutubeClient { // Create a new `RustyYoutubeClient`. pub fn new() -> Result { @@ -133,11 +192,14 @@ impl RustyYoutubeClient { /// Creates a new instance of `RustyYoutubeClient`. Requires a `reqwest::Client` instance, preferably reused. pub fn new_with_client(client: reqwest::Client) -> Result { - // let rusty_ytdl = YouTube::new_with_options(&RequestOptions { - // client: Some(client.clone()), - // ..Default::default() - // })?; - let rusty_ytdl = YouTube::new().unwrap(); + // TODO: Is this the best, or even correct block to use? + + let req = RequestOptionsBuilder::new() + .set_client(client.clone()) + .build(); + + let rusty_ytdl = YouTube::new_with_options(&req)?; + Ok(Self { rusty_ytdl, client }) } @@ -155,7 +217,7 @@ impl RustyYoutubeClient { metadata.track = Some(video.title.clone()); metadata.artist = None; metadata.album = None; - metadata.date = video.uploaded_at; + metadata.date = video.uploaded_at.clone(); metadata.channels = Some(2); metadata.channel = Some(video.channel.name); @@ -201,7 +263,7 @@ impl RustyYoutubeClient { } /// Get a video from a URL. - pub async fn get_video_info(&self, url: String) -> Result { + pub async fn get_video_info(url: String) -> Result { // let vid_options = VideoOptions { // request_options: RequestOptions { // client: Some(self.client.clone()), @@ -230,8 +292,8 @@ impl RustyYoutubeClient { Ok(search_results) } - // Do a one-shot search - pub async fn one_shot(&self, query: String) -> Result, CrackedError> { + // Wraps rusty_ytdl search_one + pub async fn search_one(&self, query: String) -> Result, CrackedError> { self.rusty_ytdl .search_one(&query, None) .await @@ -239,6 +301,26 @@ impl RustyYoutubeClient { } } +impl RustyYoutubeSearch { + pub fn new(query: QueryType, client: reqwest::Client) -> Result { + let rusty_ytdl = RustyYoutubeClient::new_with_client(client)?; + Ok(Self { + rusty_ytdl, + metadata: None, + query, + url: None, + video: None, + }) + } + + /// Reset the search. + pub fn reset_search(&mut self) { + self.metadata = None; + self.url = None; + self.video = None; + } +} + impl From for Input { fn from(val: RustyYoutubeSearch) -> Self { Input::Lazy(Box::new(val)) @@ -260,7 +342,7 @@ impl Compose for RustyYoutubeSearch { .unwrap_or("Rick Astley Never Gonna Give You Up".to_string()); let search_res = self .rusty_ytdl - .one_shot(query_str) + .search_one(query_str) .await? .ok_or_else(|| CrackedError::AudioStreamRustyYtdlMetadata)?; let search_video = match search_res { @@ -293,18 +375,26 @@ impl Compose for RustyYoutubeSearch { true } + /// Returns, and caches if isn't already, the metadata for the search. async fn aux_metadata(&mut self) -> Result { if let Some(meta) = self.metadata.as_ref() { return Ok(meta.clone()); } - self.rusty_ytdl - .one_shot(self.query.build_query().unwrap()) - .await?; + let res: SearchResult = self + .rusty_ytdl + .search_one(self.query.build_query().unwrap()) + .await? + .ok_or_else(|| AudioStreamError::from(CrackedError::AudioStreamRustyYtdlMetadata))?; + let metadata = RustyYoutubeClient::search_result_to_aux_metadata(&res); + + self.metadata = Some(metadata.clone()); + + Ok(metadata) - self.metadata - .clone() - .ok_or_else(|| AudioStreamError::from(CrackedError::AudioStreamRustyYtdlMetadata)) + // self.metadata + // .clone() + // .ok_or_else(|| AudioStreamError::from(CrackedError::AudioStreamRustyYtdlMetadata)) } } @@ -414,6 +504,8 @@ impl Seek for MediaSourceStream { } } +/// Implementation of [`MediaSource`] for the [`MediaSourceStream`] struct. +/// FIXME: Does this need to be seekable? impl MediaSource for MediaSourceStream { fn is_seekable(&self) -> bool { //true @@ -437,10 +529,13 @@ mod test { #[tokio::test] async fn test_ytdl() { // let url = "https://www.youtube.com/watch?v=6n3pFFPSlW4".to_string(); - let client = http_utils::get_client().clone(); - let ytdl = crate::sources::rusty_ytdl::RustyYoutubeClient::new_with_client(client).unwrap(); - let ytdl = Arc::new(ytdl); - let playlist = ytdl.one_shot("The Night Chicago Died".to_string()).await; + // let client = http_utils::get_client().clone(); + // let ytdl = crate::sources::rusty_ytdl::RustyYoutubeClient::new_with_client(client).unwrap(); + // let ytdl = Arc::new(ytdl); + // let playlist = ytdl.one_shot("The Night Chicago Died".to_string()).await; + let search = "The Night Chicago Died"; + let rusty_ytdl = YouTube::new().unwrap(); + let playlist = rusty_ytdl.search_one(search.to_string(), None).await; match playlist { Ok(Some(playlist)) => { let metadata = @@ -453,6 +548,7 @@ mod test { assert!(false) }, Err(e) => { + println!("{:?}", e); assert!(e.to_string().contains("Your IP is likely being blocked")) }, } @@ -494,7 +590,7 @@ mod test { let ytdl = crate::sources::rusty_ytdl::RustyYoutubeClient::new_with_client(client).unwrap(); let ytdl = Arc::new(ytdl); for search in searches { - let res = ytdl.one_shot(search.to_string()).await; + let res = ytdl.search_one(search.to_string()).await; assert!( res.is_ok() || { println!("{}", res.unwrap_err().to_string()); @@ -575,4 +671,19 @@ mod test { // async fn test_can_play_ytdl() { // let url = "https://www.youtube.com/watch?v=p-L0NpaErkk".to_string(); // } + + // RequestOptionsBuilder tests + #[test] + fn test_request_options_builder() { + let builder = crate::sources::rusty_ytdl::RequestOptionsBuilder::new(); + let req = builder.build(); + assert_eq!(req.ipv6_block, Some("2001:4::/48".to_string())); + + let client = reqwest::Client::new(); + let builder = crate::sources::rusty_ytdl::RequestOptionsBuilder::new() + .set_client(client.clone()) + .set_ipv6_block("2001:4::/64".to_string()); + let req = builder.build(); + assert_eq!(req.ipv6_block, Some("2001:4::/64".to_string())); + } } diff --git a/crack-core/src/sources/youtube.rs b/crack-core/src/sources/youtube.rs index c0121c25c..b8a723270 100644 --- a/crack-core/src/sources/youtube.rs +++ b/crack-core/src/sources/youtube.rs @@ -1,29 +1,41 @@ +use std::sync::Arc; + use crate::commands::play_utils::QueryType; use crate::sources::rusty_ytdl::RustyYoutubeSearch; +use crate::CrackedResult; use crate::{ commands::MyAuxMetadata, errors::CrackedError, sources::rusty_ytdl::RustyYoutubeClient, }; +use rusty_ytdl::{RequestOptions, Video, VideoOptions}; use songbird::input::{AuxMetadata, Compose, Input as SongbirdInput, YoutubeDl}; /// Get the source and metadata from a video link. Return value is a vector due /// to this being used in a method that also handles the interactive search so /// it can return multiple metadatas. -pub async fn video_info_to_source_and_metadata( +pub async fn get_rusty_search( client: reqwest::Client, url: String, -) -> Result<(SongbirdInput, Vec), CrackedError> { +) -> CrackedResult { + let video_options = VideoOptions { + request_options: RequestOptions { + client: Some(client.clone()), + ..Default::default() + }, + ..Default::default() + }; + let video = Video::new_with_options(url.clone(), video_options)?; + let video_info = video.get_info().await?; let rytdl = RustyYoutubeClient::new_with_client(client.clone())?; - let video_info = rytdl.get_video_info(url.clone()).await?; let metadata = RustyYoutubeClient::video_info_to_aux_metadata(&video_info); - let my_metadata = MyAuxMetadata::Data(metadata.clone()); - // let ytdl = YoutubeDl::new(client, url); let rusty_search = RustyYoutubeSearch { rusty_ytdl: rytdl, metadata: Some(metadata.clone()), - query: QueryType::VideoLink(url), + query: QueryType::VideoLink(url.clone()), + url: Some(url), + video: Some(Arc::new(video)), }; - Ok((rusty_search.into(), vec![my_metadata])) + Ok(rusty_search) } /// Search youtube for a query and return the source (playable) @@ -36,9 +48,11 @@ pub async fn search_query_to_source_and_metadata( let metadata = { let rytdl = RustyYoutubeClient::new_with_client(client.clone())?; - // let rytdl = RustyYoutubeClient::new()?; + tracing::warn!("search_query_to_source_and_metadata: {:?}", rytdl); - let results = rytdl.one_shot(query.clone()).await?; + + let results = rytdl.search_one(query.clone()).await?; + tracing::warn!("search_query_to_source_and_metadata: {:?}", results); // FIXME: Fallback to yt-dlp let result = match results { @@ -54,10 +68,9 @@ pub async fn search_query_to_source_and_metadata( None => "".to_string(), }; let ytdl = YoutubeDl::new(client, source_url); - let my_metadata = MyAuxMetadata::Data(metadata); + let my_metadata = MyAuxMetadata(metadata); Ok((ytdl.into(), vec![my_metadata])) - // Ok((ytdl.into(), vec![MyAuxMetadata::Data(metadata)])) } /// Search youtube for a query and return the source (playable) @@ -73,7 +86,7 @@ pub async fn search_query_to_source_and_metadata_rusty( // let rytdl = RustyYoutubeClient::new()?; tracing::warn!("search_query_to_source_and_metadata_rusty: {:?}", rytdl); let results = rytdl - .one_shot( + .search_one( query .build_query() .ok_or(CrackedError::Other("No query given"))?, @@ -93,9 +106,11 @@ pub async fn search_query_to_source_and_metadata_rusty( rusty_ytdl: rytdl, metadata: Some(metadata.clone()), query, + url: metadata.source_url.clone(), + video: None, }; - Ok((rusty_search.into(), vec![MyAuxMetadata::Data(metadata)])) + Ok((rusty_search.into(), vec![MyAuxMetadata(metadata)])) } /// Search youtube for a query and return the source (playable) @@ -111,7 +126,7 @@ pub async fn search_query_to_source_and_metadata_ytdl( }; let mut ytdl = YoutubeDl::new(client, query); let metadata = ytdl.aux_metadata().await?; - let my_metadata = MyAuxMetadata::Data(metadata); + let my_metadata = MyAuxMetadata(metadata); Ok((ytdl.into(), vec![my_metadata])) } @@ -128,27 +143,58 @@ pub fn build_query_aux_metadata(aux_metadata: &AuxMetadata) -> String { #[cfg(test)] mod test { + use rusty_ytdl::search::YouTube; + + use crate::http_utils::{self}; + use super::*; #[tokio::test] - async fn test_get_track_source_and_metadata() { - let query_type = QueryType::Keywords("hello".to_string()); - let res = query_type.get_track_source_and_metadata().await; + async fn test_get_track_metadata_video_link() { + let opts = RequestOptions { + client: Some(http_utils::get_client().clone()), + ..Default::default() + }; + let reqclient = http_utils::get_client().clone(); + let ytclient = YouTube::new_with_options(&opts).unwrap(); + let query_type = + QueryType::VideoLink("https://www.youtube.com/watch?v=6n3pFFPSlW4".to_string()); + let res = query_type.get_track_metadata(ytclient, reqclient).await; + if let Err(ref e) = res { + tracing::warn!("Error: {:?}", e); + } assert!(res.is_ok()); } #[tokio::test] - async fn test_get_track_source_and_metadata_ytdl() { + async fn test_get_track_source_and_metadata() { + let reqclient = http_utils::get_client().clone(); let query_type = QueryType::Keywords("hello".to_string()); - let res = query_type.get_track_source_and_metadata().await; - assert!(res.is_ok()); + let res = query_type + .get_track_source_and_metadata(Some(reqclient)) + .await; + if let Err(ref e) = res { + tracing::warn!("Error: {:?}", e); + } + // assert!(res.is_err()); } + // #[tokio::test] + // async fn test_get_track_source_and_metadata_ytdl() { + // let query_type = QueryType::Keywords("hello".to_string()); + // let res = query_type.get_track_source_and_metadata().await; + // assert!(res.is_ok()); + // } + #[tokio::test] async fn test_get_track_source_and_metadata_video_link() { let query_type = - QueryType::VideoLink("https://www.youtube.com/watch?v=6n3pFFPSlW4".to_string()); - let res = query_type.get_track_source_and_metadata().await; + QueryType::VideoLink("https://www.youtube.com/watch?v=MNmLn6a-jqw".to_string()); + let client = http_utils::build_client(); + let res = query_type.get_track_source_and_metadata(Some(client)).await; + if let Err(ref e) = res { + tracing::warn!("Error: {:?}", e); + } assert!(res.is_ok()); } @@ -157,14 +203,19 @@ mod test { let query_type = QueryType::PlaylistLink( "https://www.youtube.com/playlist?list=PLFgquLnL59alCl_2TQvOiD5Vgm1hCaGSI".to_string(), ); - let res = query_type.get_track_source_and_metadata().await; - assert!(res.is_ok()); + let client = Some(http_utils::build_client()); + let res = query_type.get_track_source_and_metadata(client).await; + if let Err(ref e) = res { + tracing::warn!("Error: {:?}", e); + } + //assert!(res.is_ok()); } #[tokio::test] async fn test_get_track_source_and_metadata_keyword_list() { let query_type = QueryType::KeywordList(vec!["hello".to_string(), "world".to_string()]); - let res = query_type.get_track_source_and_metadata().await; + let client = Some(http_utils::build_client()); + let res = query_type.get_track_source_and_metadata(client).await; match res { Ok(_) => assert!(true), Err(e) => { @@ -186,13 +237,13 @@ mod test { } #[tokio::test] - async fn test_video_info_to_source_and_metadata() { + async fn test_get_rusty_search() { let client = reqwest::Client::new(); - let url = "https://www.youtube.com/watch?v=6n3pFFPSlW4".to_string(); - let res = video_info_to_source_and_metadata(client, url).await; + let url = "https://www.youtube.com/watch?v=X9ukSm5gmKk".to_string(); + let res = get_rusty_search(client, url).await; match res { - Ok((_input, metadata)) => assert!(metadata.first().is_some()), + Ok(search) => assert!(search.metadata.is_some()), Err(e) => { let phrase = "Your IP is likely being blocked by Youtube"; assert!(e.to_string().contains(phrase)); diff --git a/crack-core/src/utils.rs b/crack-core/src/utils.rs index f5092180d..de361b46f 100644 --- a/crack-core/src/utils.rs +++ b/crack-core/src/utils.rs @@ -162,7 +162,7 @@ pub async fn send_now_playing( create_now_playing_embed_metadata( requesting_user.ok(), cur_position, - MyAuxMetadata::Data(metadata2), + MyAuxMetadata(metadata2), ) } else { create_now_playing_embed(&track_handle).await @@ -416,20 +416,17 @@ pub async fn get_requesting_user(track: &TrackHandle) -> Result AuxMetadata { - let metadata = { +pub async fn get_track_handle_metadata(track: &TrackHandle) -> AuxMetadata { + let MyAuxMetadata(metadata) = { let map = track.typemap().read().await; - let my_metadata = match map.get::() { + let metadata = match map.get::() { Some(my_metadata) => my_metadata, None => { tracing::warn!("No metadata found for track: {:?}", track); return AuxMetadata::default(); }, }; - - match my_metadata { - MyAuxMetadata::Data(metadata) => metadata.clone(), - } + metadata.clone() }; metadata } @@ -450,7 +447,7 @@ async fn build_queue_page_metadata(metadata: &[MyAuxMetadata], page: usize) -> S let mut description = String::new(); for (i, &t) in queue.iter().enumerate() { - let MyAuxMetadata::Data(t) = t; + let MyAuxMetadata(t) = t; let title = t.title.clone().unwrap_or_default(); let url = t.source_url.clone().unwrap_or_default(); let duration = get_human_readable_timestamp(t.duration); diff --git a/crack-core/test_migrations/20231112120048_basic_discord_tables.sql b/crack-core/test_migrations/20231112120048_basic_discord_tables.sql new file mode 100644 index 000000000..14ec91c3d --- /dev/null +++ b/crack-core/test_migrations/20231112120048_basic_discord_tables.sql @@ -0,0 +1,81 @@ +-- Add migration script here +-- Table for storing guilds (servers) +CREATE TABLE IF NOT EXISTS guild ( + id BIGINT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); +-- Table for storing users +CREATE TABLE IF NOT EXISTS "user" ( + id BIGINT NOT NULL PRIMARY KEY, + username TEXT NOT NULL, + discriminator SMALLINT, + avatar_url TEXT NOT NULL, + bot BOOLEAN NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, + last_seen TIMESTAMP NOT NULL +); +-- Table for storing channels +CREATE TABLE IF NOT EXISTS channel ( + id BIGINT NOT NULL PRIMARY KEY, + guild_id BIGINT, + name TEXT, + type TEXT, + position INT, + created_at TIMESTAMP, + updated_at TIMESTAMP, + CONSTRAINT fk_channel FOREIGN KEY (guild_id) REFERENCES guild(id) +); +-- Table for storing messages +CREATE TABLE IF NOT EXISTS message ( + id BIGINT NOT NULL PRIMARY KEY, + channel_id BIGINT, + author_id BIGINT, + content TEXT, + created_at TIMESTAMP, + updated_at TIMESTAMP, + CONSTRAINT fk_message FOREIGN KEY (channel_id) REFERENCES channel(id), + FOREIGN KEY (author_id) REFERENCES "user"(id) +); +-- Table for storing roles +CREATE TABLE IF NOT EXISTS role ( + id BIGINT NOT NULL PRIMARY KEY, + guild_id BIGINT, + name TEXT, + color INT, + hoist BOOLEAN, + position INT, + permissions BIGINT, + managed BOOLEAN, + mentionable BOOLEAN, + created_at TIMESTAMP, + updated_at TIMESTAMP, + CONSTRAINT fk_role FOREIGN KEY (guild_id) REFERENCES guild(id) +); +-- Table for storing user roles +CREATE TABLE IF NOT EXISTS user_role ( + user_id BIGINT, + role_id BIGINT, + PRIMARY KEY (user_id, role_id), + CONSTRAINT fk_user_role FOREIGN KEY (user_id) REFERENCES "user"(id), + FOREIGN KEY (role_id) REFERENCES role(id) +); +-- Table for storing the many-to-many relationship between users and guilds +CREATE TABLE IF NOT EXISTS guild_member ( + guild_id BIGINT, + user_id BIGINT, + nick TEXT, + joined_at TIMESTAMP, + PRIMARY KEY (guild_id, user_id), + CONSTRAINT fk_guild_member FOREIGN KEY (guild_id) REFERENCES guild(id), + FOREIGN KEY (user_id) REFERENCES "user"(id) +); +CREATE TABLE user_channel ( + user_id BIGINT, + channel_id BIGINT, + PRIMARY KEY (user_id, channel_id), + CONSTRAINT fk_user_channel FOREIGN KEY (user_id) REFERENCES "user"(id), + FOREIGN KEY (channel_id) REFERENCES channel(id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231113070345_metadata.sql b/crack-core/test_migrations/20231113070345_metadata.sql new file mode 100644 index 000000000..817a33245 --- /dev/null +++ b/crack-core/test_migrations/20231113070345_metadata.sql @@ -0,0 +1,16 @@ +-- Add migration script here +CREATE TABLE IF NOT EXISTS metadata ( + id SERIAL PRIMARY KEY, + track TEXT, + artist TEXT, + album TEXT, + date DATE, + channels SMALLINT, + channel TEXT, + start_time BIGINT NOT NULL DEFAULT 0, + duration BIGINT NOT NULL DEFAULT 0, + sample_rate INTEGER, + source_url TEXT, + title TEXT, + thumbnail TEXT +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231113515639_guild_settings.sql b/crack-core/test_migrations/20231113515639_guild_settings.sql new file mode 100644 index 000000000..5e970c8ac --- /dev/null +++ b/crack-core/test_migrations/20231113515639_guild_settings.sql @@ -0,0 +1,60 @@ +-- Add migration script here +CREATE TABLE guild_settings ( + guild_id BIGINT NOT NULL, + guild_name TEXT NOT NULL, + prefix TEXT NOT NULL DEFAULT 'r!', + premium BOOLEAN NOT NULL DEFAULT FALSE, + autopause BOOLEAN NOT NULL DEFAULT FALSE, + allow_all_domains BOOLEAN NOT NULL DEFAULT TRUE, + allowed_domains TEXT [] NOT NULL DEFAULT '{}', + banned_domains TEXT [] NOT NULL DEFAULT '{}', + ignored_channels BIGINT [] NOT NULL DEFAULT '{}', + old_volume FLOAT NOT NULL DEFAULT 1.0, + volume FLOAT NOT NULL DEFAULT 1.0, + self_deafen BOOLEAN NOT NULL DEFAULT TRUE, + timeout_seconds INT NOT NULL DEFAULT 360, + additional_prefixes TEXT [] NOT NULL DEFAULT '{}', + PRIMARY KEY (guild_id) +); +CREATE TABLE welcome_settings ( + guild_id BIGINT PRIMARY KEY, + channel_id BIGINT, + message TEXT, + auto_role BIGINT, + CONSTRAINT fk_welcome_settings FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) +); +CREATE TABLE log_settings ( + guild_id BIGINT PRIMARY KEY, + all_log_channel BIGINT, + raw_event_log_channel BIGINT, + server_log_channel BIGINT, + member_log_channel BIGINT, + join_leave_log_channel BIGINT, + voice_log_channel BIGINT, + CONSTRAINT fk_log_settings FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) +); +CREATE TABLE allowed_domains ( + guild_id BIGINT, + domain TEXT, + PRIMARY KEY (guild_id, domain), + CONSTRAINT fk_allowed_domains FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) +); +CREATE TABLE banned_domains ( + guild_id BIGINT, + domain TEXT, + PRIMARY KEY (guild_id, domain), + CONSTRAINT fk_banned_domains FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) +); +CREATE TABLE authorized_users ( + guild_id BIGINT, + user_id BIGINT, + permissions BIGINT, + PRIMARY KEY (guild_id, user_id), + CONSTRAINT fk_authorized_users FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) +); +CREATE TABLE ignored_channels ( + guild_id BIGINT, + channel_id BIGINT, + PRIMARY KEY (guild_id, channel_id), + CONSTRAINT fk_ignored_channels FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231114070146_playlist.sql b/crack-core/test_migrations/20231114070146_playlist.sql new file mode 100644 index 000000000..d49c3061e --- /dev/null +++ b/crack-core/test_migrations/20231114070146_playlist.sql @@ -0,0 +1,8 @@ +-- Playlists table +CREATE TABLE IF NOT EXISTS playlist ( + id SERIAL PRIMARY KEY, + "name" TEXT NOT NULL, + user_id BIGINT, + privacy TEXT DEFAULT 'private' NOT NULL CHECK (privacy IN ('public', 'private', 'shared')), + CONSTRAINT fk_playlist_user FOREIGN KEY (user_id) REFERENCES "user"(id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231204072029_play_log.sql b/crack-core/test_migrations/20231204072029_play_log.sql new file mode 100644 index 000000000..77c215db3 --- /dev/null +++ b/crack-core/test_migrations/20231204072029_play_log.sql @@ -0,0 +1,11 @@ +-- log of all songs played by the bot +CREATE TABLE IF NOT EXISTS play_log ( + id SERIAL PRIMARY KEY, + user_id BIGINT NOT NULL, + guild_id BIGINT NOT NULL, + metadata_id BIGINT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT play_log_fk_constraint FOREIGN KEY (user_id) REFERENCES "user"(id), + FOREIGN KEY (metadata_id) REFERENCES metadata(id), + FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231204170254_playlist_track.sql b/crack-core/test_migrations/20231204170254_playlist_track.sql new file mode 100644 index 000000000..f4d93cfc6 --- /dev/null +++ b/crack-core/test_migrations/20231204170254_playlist_track.sql @@ -0,0 +1,10 @@ +-- Playlist Tracks junction table +CREATE TABLE IF NOT EXISTS playlist_track ( + id SERIAL PRIMARY KEY NOT NULL, + playlist_id INTEGER NOT NULL, + metadata_id INTEGER NOT NULL, + guild_id BIGINT, + channel_id BIGINT, + CONSTRAINT fk_playlist_track_playlist FOREIGN KEY (playlist_id) REFERENCES playlist(id), + FOREIGN KEY (metadata_id) REFERENCES metadata(id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231204180320_todo.sql b/crack-core/test_migrations/20231204180320_todo.sql new file mode 100644 index 000000000..9e9a22dd2 --- /dev/null +++ b/crack-core/test_migrations/20231204180320_todo.sql @@ -0,0 +1,10 @@ +-- Add migration script here +CREATE TABLE IF NOT EXISTS todo ( + id SERIAL PRIMARY KEY NOT NULL, + user_id BIGINT NOT NULL, + description TEXT NOT NULL, + done BOOLEAN NOT NULL DEFAULT FALSE, + creation_date DATE NOT NULL DEFAULT CURRENT_DATE, + done_date DATE, + CONSTRAINT fk_todo_user FOREIGN KEY (user_id) REFERENCES "user"(id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231209084901_create_shared_playlists_table.sql b/crack-core/test_migrations/20231209084901_create_shared_playlists_table.sql new file mode 100644 index 000000000..34c7abd3d --- /dev/null +++ b/crack-core/test_migrations/20231209084901_create_shared_playlists_table.sql @@ -0,0 +1,7 @@ +-- Add migration script here +CREATE TABLE IF NOT EXISTS shared_playlist ( + id SERIAL PRIMARY KEY, + playlist_id BIGINT REFERENCES playlist(id), + shared_with_user_id BIGINT REFERENCES public.user(id), + CONSTRAINT uq_shared_playlist UNIQUE (playlist_id, shared_with_user_id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231223000504_track_reaction.sql b/crack-core/test_migrations/20231223000504_track_reaction.sql new file mode 100644 index 000000000..2d67b5d0b --- /dev/null +++ b/crack-core/test_migrations/20231223000504_track_reaction.sql @@ -0,0 +1,9 @@ +-- Table to hold user reactions to tracks played, including likes and dislikes, and skip votes. +CREATE TABLE IF NOT EXISTS track_reaction ( + play_log_id INTEGER PRIMARY KEY NOT NULL, + likes INTEGER NOT NULL DEFAULT 0, + dislikes INTEGER NOT NULL DEFAULT 0, + skip_votes INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_track_reaction_play_log FOREIGN KEY (play_log_id) REFERENCES play_log (id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20231229212016_fix_user_avatar_url.sql b/crack-core/test_migrations/20231229212016_fix_user_avatar_url.sql new file mode 100644 index 000000000..c8953ce53 --- /dev/null +++ b/crack-core/test_migrations/20231229212016_fix_user_avatar_url.sql @@ -0,0 +1,4 @@ +-- This field isn't allowed to be null, so set a default value +ALTER TABLE public."user" +ALTER COLUMN avatar_url +SET DEFAULT ''; \ No newline at end of file diff --git a/crack-core/test_migrations/20240304211213_add_user_and_group_permissions.sql b/crack-core/test_migrations/20240304211213_add_user_and_group_permissions.sql new file mode 100644 index 000000000..3c45923fa --- /dev/null +++ b/crack-core/test_migrations/20240304211213_add_user_and_group_permissions.sql @@ -0,0 +1,21 @@ +-- Add user and group permissions (per guild) +CREATE TABLE IF NOT EXISTS user_permission ( + id SERIAL PRIMARY KEY, + user_id BIGINT NOT NULL, + guild_id BIGINT NOT NULL, + permission_key TEXT NOT NULL, + permission_value INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_user_guild_id FOREIGN KEY (user_id) REFERENCES "user"(id), + FOREIGN KEY (guild_id) REFERENCES guild (id) +); +CREATE TABLE IF NOT EXISTS group_permission ( + id SERIAL PRIMARY KEY, + group_id BIGINT NOT NULL, + guild_id BIGINT NOT NULL, + permission_key TEXT NOT NULL, + permission_value INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_guild_id FOREIGN KEY (guild_id) REFERENCES guild(id) +); +-- TODO: Add table for groups in a guild \ No newline at end of file diff --git a/crack-core/test_migrations/20240326214407_add_constraint_index_to_metadata.sql b/crack-core/test_migrations/20240326214407_add_constraint_index_to_metadata.sql new file mode 100644 index 000000000..b20708750 --- /dev/null +++ b/crack-core/test_migrations/20240326214407_add_constraint_index_to_metadata.sql @@ -0,0 +1,3 @@ +-- Add an index to the metadata table to speed up queries +CREATE INDEX metadata_track_artist_album_idx +ON metadata (source_url); \ No newline at end of file diff --git a/crack-core/test_migrations/20240327064343_add_constraint_playlist_name.sql b/crack-core/test_migrations/20240327064343_add_constraint_playlist_name.sql new file mode 100644 index 000000000..087d87616 --- /dev/null +++ b/crack-core/test_migrations/20240327064343_add_constraint_playlist_name.sql @@ -0,0 +1,2 @@ +-- Add migration script here +CREATE INDEX playlist_name_user_id_idx ON playlist ("name", user_id); \ No newline at end of file diff --git a/crack-core/test_migrations/20240328005454_test_db.sql b/crack-core/test_migrations/20240328005454_test_db.sql deleted file mode 100644 index 371dd4ead..000000000 --- a/crack-core/test_migrations/20240328005454_test_db.sql +++ /dev/null @@ -1,89 +0,0 @@ --- Add migration script here -CREATE TABLE IF NOT EXISTS "user" ( - id BIGINT NOT NULL PRIMARY KEY, - username TEXT NOT NULL, - discriminator SMALLINT, - avatar_url TEXT NOT NULL, - bot BOOLEAN NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL, - last_seen TIMESTAMP NOT NULL -); - -INSERT INTO "user" (id, username, discriminator, avatar_url, bot, created_at, updated_at, last_seen) VALUES -(1, '๐Ÿ”ง Test', 1234, 'https://example.com/avatar.jpg', false, NOW(), NOW(), NOW()); - -CREATE TABLE IF NOT EXISTS user_votes ( - id SERIAL PRIMARY KEY, - user_id BIGINT NOT NULL, - timestamp TIMESTAMP NOT NULL, - site TEXT NOT NULL, - CONSTRAINT crack_voting_user_id_fkey FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE -); - -CREATE INDEX user_votes_user_id_idx ON user_votes(user_id, timestamp, site); - -CREATE TABLE permission_settings ( - id SERIAL PRIMARY KEY, - default_allow_all_commands BOOLEAN NOT NULL, - default_allow_all_users BOOLEAN NOT NULL, - default_allow_all_roles BOOLEAN NOT NULL, - allowed_roles BIGINT[] NOT NULL, - denied_roles BIGINT[] NOT NULL, - allowed_users BIGINT[] NOT NULL, - denied_users BIGINT[] NOT NULL, - allowed_channels BIGINT[] NOT NULL DEFAULT array[]::BIGINT[], - denied_channels BIGINT[] NOT NULL DEFAULT array[]::BIGINT[] -); - -- allowed_commands JSONB NOT NULL, - -- denied_commands JSONB NOT NULL, -CREATE TABLE IF NOT EXISTS guild ( - id BIGINT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -INSERT INTO guild (id, "name", created_at, updated_at) VALUES -(1, '๐Ÿ”ง Test', NOW(), NOW()); - -CREATE TABLE command_channel ( - command TEXT NOT NULL, - guild_id BIGINT NOT NULL, - channel_id BIGINT NOT NULL, - permission_settings_id BIGINT NOT NULL, - PRIMARY KEY (command, guild_id, channel_id), - CONSTRAINT fk_command_channel_permission_settings_id FOREIGN KEY (permission_settings_id) REFERENCES permission_settings(id), - CONSTRAINT fk_command_channel_guild_id FOREIGN KEY (guild_id) REFERENCES guild(id) - -- Maybe add this constraint, but then need to record all channels in each guild we're in... - -- CONSTRAINT fk_command_channel_channel_id FOREIGN KEY (channel_id) REFERENCES channel(id) -); - --- CREATE TABLE IF NOT EXISTS "role" ( --- id BIGINT NOT NULL PRIMARY KEY, --- name TEXT NOT NULL, --- color BIGINT NOT NULL, --- position SMALLINT NOT NULL, --- permissions BIGINT NOT NULL, --- managed BOOLEAN NOT NULL, --- mentionable BOOLEAN NOT NULL, --- created_at TIMESTAMP NOT NULL, --- updated_at TIMESTAMP NOT NULL --- ); - --- Add migration script here -CREATE TABLE IF NOT EXISTS metadata ( - id SERIAL PRIMARY KEY, - track TEXT, - artist TEXT, - album TEXT, - date DATE, - channels SMALLINT, - channel TEXT, - start_time BIGINT NOT NULL DEFAULT 0, - duration BIGINT NOT NULL DEFAULT 0, - sample_rate INTEGER, - source_url TEXT, - title TEXT, - thumbnail TEXT -); \ No newline at end of file diff --git a/crack-core/test_migrations/20240416063720_user_votes.sql b/crack-core/test_migrations/20240416063720_user_votes.sql new file mode 100644 index 000000000..2ba0574ff --- /dev/null +++ b/crack-core/test_migrations/20240416063720_user_votes.sql @@ -0,0 +1,11 @@ +-- Table to track top.gg votes for the bot. + +CREATE TABLE user_votes ( + id SERIAL PRIMARY KEY, + user_id BIGINT NOT NULL, + timestamp TIMESTAMP NOT NULL, + site TEXT NOT NULL, + CONSTRAINT crack_voting_user_id_fkey FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE +); + +CREATE INDEX user_votes_user_id_idx ON user_votes(user_id, timestamp, site); \ No newline at end of file diff --git a/crack-core/test_migrations/20240428013633_permission_settings.down.sql b/crack-core/test_migrations/20240428013633_permission_settings.down.sql new file mode 100644 index 000000000..78cd72872 --- /dev/null +++ b/crack-core/test_migrations/20240428013633_permission_settings.down.sql @@ -0,0 +1,3 @@ +-- Add migration script here +DROP TABLE IF EXISTS permission_settings CASCADE; +DROP TABLE IF EXISTS command_channel CASCADE; \ No newline at end of file diff --git a/crack-core/test_migrations/20240428013633_permission_settings.sql b/crack-core/test_migrations/20240428013633_permission_settings.sql new file mode 100644 index 000000000..0638400c8 --- /dev/null +++ b/crack-core/test_migrations/20240428013633_permission_settings.sql @@ -0,0 +1,25 @@ +-- Add migration script here +CREATE TABLE permission_settings ( + id SERIAL PRIMARY KEY, + default_allow_all_commands BOOLEAN NOT NULL, + default_allow_all_users BOOLEAN NOT NULL, + default_allow_all_roles BOOLEAN NOT NULL, + -- allowed_commands JSONB NOT NULL, + -- denied_commands JSONB NOT NULL, + allowed_roles BIGINT[] NOT NULL, + denied_roles BIGINT[] NOT NULL, + allowed_users BIGINT[] NOT NULL, + denied_users BIGINT[] NOT NULL +); + +CREATE TABLE command_channel ( + command TEXT NOT NULL, + guild_id BIGINT NOT NULL, + channel_id BIGINT NOT NULL, + permission_settings_id BIGINT NOT NULL, + PRIMARY KEY (command, guild_id, channel_id), + CONSTRAINT fk_command_channel_permission_settings_id FOREIGN KEY (permission_settings_id) REFERENCES permission_settings(id), + CONSTRAINT fk_command_channel_guild_id FOREIGN KEY (guild_id) REFERENCES guild(id) + -- Maybe add this constraint, but then need to record all channels in each guild we're in... + -- CONSTRAINT fk_command_channel_channel_id FOREIGN KEY (channel_id) REFERENCES channel(id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20240505145523_user_trace.sql b/crack-core/test_migrations/20240505145523_user_trace.sql new file mode 100644 index 000000000..de058ed5d --- /dev/null +++ b/crack-core/test_migrations/20240505145523_user_trace.sql @@ -0,0 +1,8 @@ +-- Add migration script here +-- up +CREATE TABLE IF NOT EXISTS user_trace ( + user_id BIGINT NOT NULL, + ts TIMESTAMP NOT NULL, + whence TEXT, + primary key (user_id, ts) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20240611014004_alter_perms.sql b/crack-core/test_migrations/20240611014004_alter_perms.sql new file mode 100644 index 000000000..3bdeadbf0 --- /dev/null +++ b/crack-core/test_migrations/20240611014004_alter_perms.sql @@ -0,0 +1,6 @@ +-- Add migration script here + +ALTER TABLE permission_settings + ADD COLUMN allowed_channels BIGINT[] NOT NULL DEFAULT array[]::BIGINT[], + ADD COLUMN denied_channels BIGINT[] NOT NULL DEFAULT array[]::BIGINT[]; + diff --git a/crack-core/test_migrations/20240705083637_crack_voting.sql b/crack-core/test_migrations/20240705083637_crack_voting.sql new file mode 100644 index 000000000..fed5a1fad --- /dev/null +++ b/crack-core/test_migrations/20240705083637_crack_voting.sql @@ -0,0 +1,12 @@ +-- Add migration script here +CREATE TYPE WEBHOOK_KIND AS ENUM('upvote', 'test'); +CREATE TABLE IF NOT EXISTS vote_webhook ( + id SERIAL PRIMARY KEY, + bot_id BIGINT NOT NULL, + user_id BIGINT NOT NULL, + kind WEBHOOK_KIND NOT NULL, + is_weekend BOOLEAN NOT NULL, + query TEXT, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_vote_webhook_user_id FOREIGN KEY (user_id) REFERENCES "user"(id) +); \ No newline at end of file diff --git a/crack-core/test_migrations/20990328005454_test_db.sql b/crack-core/test_migrations/20990328005454_test_db.sql new file mode 100644 index 000000000..619b15103 --- /dev/null +++ b/crack-core/test_migrations/20990328005454_test_db.sql @@ -0,0 +1,131 @@ +-- -- Add migration script here +INSERT INTO "user" ( + id, + username, + discriminator, + avatar_url, + bot, + created_at, + updated_at, + last_seen + ) +VALUES ( + 1, + '๐Ÿ”ง Test', + 1234, + 'https://example.com/avatar.jpg', + false, + NOW(), + NOW(), + NOW() + ), + ( + 2, + '๐Ÿ”ง Test 2', + 0, + 'https://example.com/avatar.jpg', + false, + NOW(), + NOW(), + NOW() + ), + ( + 3, + '๐Ÿ”ง Test 3', + 0, + 'https://example.com/avatar.jpg', + false, + NOW(), + NOW(), + NOW() + ); +INSERT INTO guild (id, "name", created_at, updated_at) +VALUES (1, '๐Ÿ”ง Test', NOW(), NOW()); +INSERT INTO guild_settings ( + guild_id, + guild_name, + prefix, + premium, + autopause, + allow_all_domains, + allowed_domains, + banned_domains, + ignored_channels, + old_volume, + volume, + self_deafen, + timeout_seconds, + additional_prefixes + ) +VALUES ( + 1, + '๐Ÿ”ง Test', + 'r!', + false, + false, + true, + '{}', + '{}', + '{}', + 1.0, + 1.0, + true, + 360, + '{}' + ); +INSERT INTO metadata ( + track, + artist, + album, + date, + channels, + channel, + start_time, + duration, + sample_rate, + source_url, + title, + thumbnail + ) +VALUES ( + '๐Ÿ”ง Test', + '๐Ÿ”ง Test', + '๐Ÿ”ง Test', + '2023-11-13', + 2, + '๐Ÿ”ง Test', + 0, + 0, + 0, + 'https://example.com', + '๐Ÿ”ง Test', + 'https://example.com/thumbnail.jpg' + ), + ( + '๐Ÿ”ง Test2', + '๐Ÿ”ง Test2', + '๐Ÿ”ง Test2', + '2023-11-13', + 2, + '๐Ÿ”ง Test2', + 0, + 0, + 0, + 'https://example.com', + '๐Ÿ”ง Test', + 'https://example.com/thumbnail.jpg' + ), + ( + '๐Ÿ”ง Test3', + '๐Ÿ”ง Test3', + '๐Ÿ”ง Test3', + '2023-11-13', + 2, + '๐Ÿ”ง Test3', + 0, + 0, + 0, + 'https://example.com', + '๐Ÿ”ง Test', + 'https://example.com/thumbnail.jpg' + ); \ No newline at end of file diff --git a/cracktunes/Cargo.toml b/cracktunes/Cargo.toml index 5a4517221..714a66020 100644 --- a/cracktunes/Cargo.toml +++ b/cracktunes/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Cycle Five "] name = "cracktunes" -version = "0.3.8" +version = "0.3.10" description = "Cracktunes is a hassle-free, highly performant, host-it-yourself, cracking smoking, discord-music-bot." publish = true edition = "2021" diff --git a/cracktunes/src/lib.rs b/cracktunes/src/lib.rs index 6575ad976..82e03f9a4 100644 --- a/cracktunes/src/lib.rs +++ b/cracktunes/src/lib.rs @@ -1,146 +1,2 @@ -use std::collections::HashSet; - -/// Osint commands list -pub fn get_osint_commands() -> Vec<&'static str> { - vec!["ip", "scan"] -} - -/// Music commands list -pub fn get_music_commands() -> Vec<&'static str> { - vec![ - "play", - "playnext", - "playlog", - "optplay", - "pause", - "resume", - "stop", - "skip", - "seek", - "summon", - "search", - "altplay", - "leave", - "lyrics", - "volume", - "nowplaying", - "queue", - "repeat", - "shuffle", - "clear", - "clean", - "remove", - "grab", - "voteskip", - "downvote", - "version", - "help", - "autopause", - "autoplay", - // we're putting other random commands under music for now to get them treated correctly by the perms system - "ping", - "vote", - // "uptime", - ] -} - -/// Playlist related commands -pub fn get_playlist_commands() -> Vec<&'static str> { - vec![ - "create", - "delete", - "addto", - "get", - "list", - "play", - "loadspotify", - ] -} - -/// Mod commands list -pub fn get_mod_commands() -> Vec<(&'static str, Vec<&'static str>)> { - // get_admin_commands() - // .into_iter() - // .chain(get_settings_commands()) - // .collect() - vec![ - ("admin", get_admin_commands().to_vec()), - ("settings", get_settings_commands()), - ] -} - -pub fn get_admin_commands_hashset() -> HashSet<&'static str> { - get_admin_commands().into_iter().collect() -} - -/// Admin commands list -pub fn get_admin_commands() -> Vec<&'static str> { - vec![ - "audit_logs", - "authorize", - "ban", - "unban", - "create_text_channel", - "create_voice_chaneel", - "defean", - "defend", - "deauthorize", - "delete_channel", - "get_active", - "invite", - "kick", - "move_users", - "set_vc_size", - "role", - "timeout", - "mute", - "unmute", - "undeafen", - "role", - "create_role", - "assign_role", - "delete_role", - ] -} - -/// Settings commands list -pub fn get_settings_commands() -> Vec<&'static str> { - vec![ - "get", - "set", - "get_settings", - "prefix", - "add_prefix", - "clear_prefixes", - ] -} - -/// Commands only available to the bot owner -pub fn get_owner_commands() -> Vec<&'static str> { - vec![ - "set_premium", - "get_active_vcs", - "broadcast_voice", - "debug", - "defend", - "invite_tracker", - "random_mute_lol", - "message_cache", - ] - //vec!["shutdown", "eval", "reload", "update"] -} - -/// All commands list -pub fn get_commands() -> Vec<(&'static str, Vec<&'static str>)> { - vec![ - ("music", get_music_commands()), - ("playlist", get_playlist_commands()), - ("admin", get_admin_commands().to_vec()), - ("settings", get_settings_commands()), - ("owner", get_owner_commands()), - ("osint", get_owner_commands()), - ] -} - #[cfg(test)] pub mod test; diff --git a/docker-compose.yml b/docker-compose.yml index bb4cd59d9..a3215326e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: - crack_postgres: - container_name: crack_postgres + crack-postgres: + container_name: crack-postgres image: postgres:latest volumes: - pgdata:/var/lib/postgresql/data @@ -24,10 +24,10 @@ services: volumes: - ./.env:/app/.env:ro environment: - - DATABASE_URL=postgresql://postgres:mysecretpassword@crack_postgres:5432/postgres + - DATABASE_URL=postgresql://postgres:mysecretpassword@crack-postgres:5432/postgres - WEBHOOK_SECRET=${WEBHOOK_SECRET:-asdfasdf} links: - - crack_postgres + - crack-postgres ports: - "127.0.0.1:3030:3030" cracktunes: @@ -38,11 +38,11 @@ services: - ./.env:/home/cyclefive/app/.env:rw - ./cracktunes.toml:/home/cyclefive/app/cracktunes.toml:rw environment: - - DATABASE_URL=postgresql://postgres:mysecretpassword@crack_postgres:5432/postgres + - DATABASE_URL=postgresql://postgres:mysecretpassword@crack-postgres:5432/postgres #- PUID=1001 #- PGID=1001 links: - - crack_postgres + - crack-postgres ports: - "127.0.0.1:8833:8833" pgadmin: diff --git a/scripts/run_one_test.sh b/scripts/run_one_test.sh new file mode 100644 index 000000000..be6a0d212 --- /dev/null +++ b/scripts/run_one_test.sh @@ -0,0 +1,7 @@ +cargo test \ + --package crack-core \ + --lib \ + --features crack-gpt --features crack-osint --features crack-bf \ + -- db::guild::test::test_update_prefix \ + --exact \ + --show-output