From 4ebb10690bb3dfcd0597f5410c2d26a933badc5b Mon Sep 17 00:00:00 2001 From: Niko Date: Sun, 10 Dec 2023 19:25:27 -0700 Subject: [PATCH] Update serenity and migrate STT service --- ...6e59161d686e4e179afc92321e61050fd11b.json} | 10 +- ...3dc3b4c5a19076907a323070d568c545036a.json} | 16 +- ...b17ca90067b1fdc085602e205292f09731457.json | 15 + ...36ce6c8b8dea6a77e50ee347fee338592eb5d.json | 28 + Cargo.lock | 574 +++++++++++++----- Cargo.toml | 2 +- .../20231211021825_translate_to_english.sql | 2 + scripty_audio_handler/Cargo.toml | 2 +- scripty_audio_handler/src/audio_handler.rs | 8 +- .../src/events/voice_tick.rs | 163 ++--- scripty_bot/src/lib.rs | 20 +- .../background_tasks/tasks/bot_list_poster.rs | 2 +- .../background_tasks/tasks/status_update.rs | 4 +- scripty_bot_utils/src/dm_support.rs | 7 +- scripty_bot_utils/src/error/handler.rs | 20 +- scripty_bot_utils/src/error/message.rs | 2 +- scripty_bot_utils/src/extern_utils.rs | 11 +- .../src/generic_audio_message.rs | 61 +- scripty_bot_utils/src/voice_message.rs | 13 +- scripty_botlists/src/common.rs | 2 +- .../src/lists/botlist_me/models.rs | 2 +- .../src/lists/discord_bots_gg/models.rs | 2 +- .../lists/discordextremelist_xyz/models.rs | 2 +- .../src/lists/discordservices_net/models.rs | 2 +- .../src/lists/infinitybots_gg/models.rs | 2 +- .../src/lists/radarcord_net/models.rs | 2 +- scripty_botlists/src/lists/top_gg/models.rs | 2 +- .../src/lists/topcord_xyz/models.rs | 2 +- .../src/lists/voidbots_net/models.rs | 2 +- scripty_commands/Cargo.toml | 4 + scripty_commands/src/cmds/admin/cache_info.rs | 63 ++ .../src/cmds/admin/guild_check.rs | 14 +- scripty_commands/src/cmds/admin/mod.rs | 2 + scripty_commands/src/cmds/config/mod.rs | 2 +- .../src/cmds/config/transcribe_only_role.rs | 2 +- scripty_commands/src/cmds/terms_of_service.rs | 12 +- scripty_commands/src/i18n.rs | 9 +- scripty_commands/src/lib.rs | 6 +- scripty_stt/Cargo.toml | 7 +- scripty_stt/src/lib.rs | 4 +- scripty_stt/src/load_balancer.rs | 506 ++++++++++++--- scripty_stt/src/models.rs | 361 +++++------ scripty_utils/src/latency/ws.rs | 2 +- .../src/endpoints/bot_stats/advanced.rs | 4 +- .../src/endpoints/bot_stats/mod.rs | 2 +- 45 files changed, 1334 insertions(+), 646 deletions(-) rename .sqlx/{query-32f9c865e2021e170d477a028d097ada327d5e6d19a2099df3ff24ee93da2708.json => query-58a67995b6f90c0a75dfb93454e46e59161d686e4e179afc92321e61050fd11b.json} (70%) rename .sqlx/{query-11c8a239a12caf99448c5a65ac17360f9d6a1377f8f5777555555b9ddcd637cb.json => query-7393c23910431ddaa5b67614df6b3dc3b4c5a19076907a323070d568c545036a.json} (53%) create mode 100644 .sqlx/query-a2abf8a0f150945b1b627443345b17ca90067b1fdc085602e205292f09731457.json create mode 100644 .sqlx/query-b9265a1f8483e4c649b1c43c08436ce6c8b8dea6a77e50ee347fee338592eb5d.json create mode 100644 migrations/20231211021825_translate_to_english.sql create mode 100644 scripty_commands/src/cmds/admin/cache_info.rs diff --git a/.sqlx/query-32f9c865e2021e170d477a028d097ada327d5e6d19a2099df3ff24ee93da2708.json b/.sqlx/query-58a67995b6f90c0a75dfb93454e46e59161d686e4e179afc92321e61050fd11b.json similarity index 70% rename from .sqlx/query-32f9c865e2021e170d477a028d097ada327d5e6d19a2099df3ff24ee93da2708.json rename to .sqlx/query-58a67995b6f90c0a75dfb93454e46e59161d686e4e179afc92321e61050fd11b.json index 305b4beb..bf4ec1ff 100644 --- a/.sqlx/query-32f9c865e2021e170d477a028d097ada327d5e6d19a2099df3ff24ee93da2708.json +++ b/.sqlx/query-58a67995b6f90c0a75dfb93454e46e59161d686e4e179afc92321e61050fd11b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT language, transcribe_audio_files, transcribe_video_files FROM guilds WHERE guild_id = $1", + "query": "SELECT language, transcribe_audio_files, transcribe_video_files, translate FROM guilds WHERE guild_id = $1", "describe": { "columns": [ { @@ -17,6 +17,11 @@ "ordinal": 2, "name": "transcribe_video_files", "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "translate", + "type_info": "Bool" } ], "parameters": { @@ -25,10 +30,11 @@ ] }, "nullable": [ + false, false, false, false ] }, - "hash": "32f9c865e2021e170d477a028d097ada327d5e6d19a2099df3ff24ee93da2708" + "hash": "58a67995b6f90c0a75dfb93454e46e59161d686e4e179afc92321e61050fd11b" } diff --git a/.sqlx/query-11c8a239a12caf99448c5a65ac17360f9d6a1377f8f5777555555b9ddcd637cb.json b/.sqlx/query-7393c23910431ddaa5b67614df6b3dc3b4c5a19076907a323070d568c545036a.json similarity index 53% rename from .sqlx/query-11c8a239a12caf99448c5a65ac17360f9d6a1377f8f5777555555b9ddcd637cb.json rename to .sqlx/query-7393c23910431ddaa5b67614df6b3dc3b4c5a19076907a323070d568c545036a.json index 82ed5c2a..e62a6cd3 100644 --- a/.sqlx/query-11c8a239a12caf99448c5a65ac17360f9d6a1377f8f5777555555b9ddcd637cb.json +++ b/.sqlx/query-7393c23910431ddaa5b67614df6b3dc3b4c5a19076907a323070d568c545036a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT be_verbose, language, auto_detect_lang FROM guilds WHERE guild_id = $1", + "query": "SELECT be_verbose, language, auto_detect_lang, transcript_only_role, translate FROM guilds WHERE guild_id = $1", "describe": { "columns": [ { @@ -17,6 +17,16 @@ "ordinal": 2, "name": "auto_detect_lang", "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "transcript_only_role", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "translate", + "type_info": "Bool" } ], "parameters": { @@ -27,8 +37,10 @@ "nullable": [ false, false, + false, + true, false ] }, - "hash": "11c8a239a12caf99448c5a65ac17360f9d6a1377f8f5777555555b9ddcd637cb" + "hash": "7393c23910431ddaa5b67614df6b3dc3b4c5a19076907a323070d568c545036a" } diff --git a/.sqlx/query-a2abf8a0f150945b1b627443345b17ca90067b1fdc085602e205292f09731457.json b/.sqlx/query-a2abf8a0f150945b1b627443345b17ca90067b1fdc085602e205292f09731457.json new file mode 100644 index 00000000..f36317b0 --- /dev/null +++ b/.sqlx/query-a2abf8a0f150945b1b627443345b17ca90067b1fdc085602e205292f09731457.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO guilds (guild_id, transcript_only_role) VALUES ($1, $2) ON CONFLICT (guild_id) DO UPDATE SET transcript_only_role = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "a2abf8a0f150945b1b627443345b17ca90067b1fdc085602e205292f09731457" +} diff --git a/.sqlx/query-b9265a1f8483e4c649b1c43c08436ce6c8b8dea6a77e50ee347fee338592eb5d.json b/.sqlx/query-b9265a1f8483e4c649b1c43c08436ce6c8b8dea6a77e50ee347fee338592eb5d.json new file mode 100644 index 00000000..8ae2a14c --- /dev/null +++ b/.sqlx/query-b9265a1f8483e4c649b1c43c08436ce6c8b8dea6a77e50ee347fee338592eb5d.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT language, translate FROM guilds WHERE guild_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "language", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "translate", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "b9265a1f8483e4c649b1c43c08436ce6c8b8dea6a77e50ee347fee338592eb5d" +} diff --git a/Cargo.lock b/Cargo.lock index 1956c990..6b0b3238 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -322,6 +322,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "bytemuck" version = "1.14.0" @@ -355,6 +361,37 @@ dependencies = [ "byteorder", ] +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.0.83" @@ -432,8 +469,8 @@ dependencies = [ [[package]] name = "command_attr" -version = "0.5.0" -source = "git+https://github.com/serenity-rs/serenity?branch=next#fdf9f3761fa9831691efaf67e92d7bfe93bd3ab9" +version = "0.5.1" +source = "git+https://github.com/serenity-rs/serenity?branch=next#9da2a4422685ce152df79d40e8d4b27ea14bba31" dependencies = [ "proc-macro2", "quote", @@ -448,9 +485,9 @@ checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -458,9 +495,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" @@ -582,9 +619,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -592,27 +629,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn 2.0.39", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 2.0.39", ] [[package]] @@ -622,7 +659,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "lock_api", "once_cell", "parking_lot_core", @@ -722,9 +759,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "deadpool" @@ -770,9 +807,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" dependencies = [ "powerfmt", "serde", @@ -854,12 +891,21 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", ] [[package]] @@ -982,19 +1028,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "flume" -version = "0.10.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" -dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "pin-project", - "spin 0.9.8", -] - [[package]] name = "flume" version = "0.11.0" @@ -1015,9 +1048,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1180,9 +1213,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" @@ -1211,9 +1250,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -1225,7 +1264,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -1295,9 +1334,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1355,7 +1394,7 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.9", + "rustls 0.21.10", "tokio", "tokio-rustls 0.24.1", ] @@ -1391,9 +1430,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1416,7 +1455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -1471,30 +1510,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.25", + "rustix 0.38.28", "windows-sys 0.48.0", ] [[package]] name = "itertools" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -1516,9 +1555,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libm" @@ -1545,9 +1584,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -1641,6 +1680,21 @@ dependencies = [ "unicase", ] +[[package]] +name = "mini-moka" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e0b72e7c9042467008b10279fc732326bd605459ae03bda88825909dd19b56" +dependencies = [ + "crossbeam-channel", + "crossbeam-utils", + "dashmap", + "skeptic", + "smallvec", + "tagptr", + "triomphe", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1658,9 +1712,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -1853,9 +1907,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -1948,9 +2002,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" @@ -2043,31 +2097,29 @@ dependencies = [ [[package]] name = "poise" -version = "0.5.5" -source = "git+https://github.com/serenity-rs/poise?branch=serenity-next#7844c03365bcf7024671c078a254a2159fd8b556" +version = "0.6.0-rc1" +source = "git+https://github.com/serenity-rs/poise?branch=serenity-next#3689947a2c61c3c6f54e0ee0efb17c6ffd48297c" dependencies = [ "async-trait", "derivative", - "futures-core", "futures-util", - "log", - "once_cell", "parking_lot", "poise_macros", "regex", "serenity", "tokio", + "tracing", ] [[package]] name = "poise_macros" -version = "0.5.5" -source = "git+https://github.com/serenity-rs/poise?branch=serenity-next#7844c03365bcf7024671c078a254a2159fd8b556" +version = "0.6.0-rc1" +source = "git+https://github.com/serenity-rs/poise?branch=serenity-next#3689947a2c61c3c6f54e0ee0efb17c6ffd48297c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.39", ] [[package]] @@ -2140,9 +2192,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -2195,6 +2247,17 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + [[package]] name = "quote" version = "1.0.33" @@ -2341,7 +2404,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.9", + "rustls 0.21.10", "rustls-pemfile", "serde", "serde_json", @@ -2377,9 +2440,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom", @@ -2407,11 +2470,33 @@ dependencies = [ "libc", ] +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rsa" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ "const-oid", "digest", @@ -2491,15 +2576,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.11", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", ] [[package]] @@ -2516,12 +2601,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.9" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.5", + "ring 0.17.7", "rustls-webpki", "sct", ] @@ -2553,7 +2638,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.5", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -2578,9 +2663,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "salsa20" @@ -2591,6 +2676,15 @@ dependencies = [ "cipher", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.22" @@ -2612,6 +2706,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scripty-common" +version = "0.1.0" +source = "git+https://github.com/scripty-bot/scripty-common#1106f29b62bb395a874095710c35edca489c5345" +dependencies = [ + "serde", + "uuid", +] + [[package]] name = "scripty_audio_handler" version = "1.0.0" @@ -2716,7 +2819,9 @@ dependencies = [ "hex", "humantime", "indexmap 1.9.3", + "num-format", "poise", + "rand", "scripty_audio_handler", "scripty_automod", "scripty_bot_utils", @@ -2730,6 +2835,7 @@ dependencies = [ "sqlx", "tokio", "tracing", + "typesize", ] [[package]] @@ -2871,15 +2977,20 @@ dependencies = [ "dashmap", "dasp_interpolate", "dasp_signal", - "flume 0.10.14", + "flume", + "futures", "magnum", "once_cell", + "parking_lot", + "rmp-serde", + "scripty-common", "scripty_config", "scripty_metrics", "serde", "serde_json", "tokio", "tracing", + "uuid", ] [[package]] @@ -2944,7 +3055,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.5", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -3001,21 +3112,24 @@ name = "semver" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +dependencies = [ + "serde", +] [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde-aux" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3dfe1b7eb6f9dcf011bd6fad169cdeaae75eda0d61b1a99a3f015b41b0cae39" +checksum = "184eba62ebddb71658697c8b08822edee89970bf318c5362189f0de27f85b498" dependencies = [ "chrono", "serde", @@ -3034,9 +3148,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -3089,8 +3203,8 @@ dependencies = [ [[package]] name = "serenity" -version = "0.12.0-rc" -source = "git+https://github.com/serenity-rs/serenity?branch=next#fdf9f3761fa9831691efaf67e92d7bfe93bd3ab9" +version = "0.12.0" +source = "git+https://github.com/serenity-rs/serenity?branch=next#9da2a4422685ce152df79d40e8d4b27ea14bba31" dependencies = [ "arrayvec", "async-trait", @@ -3111,20 +3225,23 @@ dependencies = [ "secrecy", "serde", "serde_json", + "small-fixed-array", "static_assertions", "time", "tokio", "tokio-tungstenite 0.20.1", "tracing", "typemap_rev", + "typesize", "url", "uwl", ] [[package]] name = "serenity-voice-model" -version = "0.1.1" -source = "git+https://github.com/serenity-rs/serenity?branch=next#fdf9f3761fa9831691efaf67e92d7bfe93bd3ab9" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "593682f6155d07c8b331b3d1060f5aab7e6796caca9f2f66bd9e6855c880e06b" dependencies = [ "bitflags 2.4.1", "num-traits", @@ -3189,6 +3306,21 @@ dependencies = [ "rand_core", ] +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "slab" version = "0.4.9" @@ -3198,6 +3330,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "small-fixed-array" +version = "0.1.0" +source = "git+https://github.com/GnomedDev/small-fixed-array#295791a491435a660a5a51164ab4215b605dd49c" +dependencies = [ + "serde", + "tracing", + "typesize", +] + [[package]] name = "smallvec" version = "1.11.2" @@ -3226,8 +3368,8 @@ dependencies = [ [[package]] name = "songbird" -version = "0.3.2" -source = "git+https://github.com/serenity-rs/songbird?branch=next#c8b2dab0f01f245bd64ab3f7db796d41c40c4832" +version = "0.4.0" +source = "git+https://github.com/tazz4843/songbird?branch=serenity-next#14f87c9ff42eef7d92e67665bf7335d3b1db6a78" dependencies = [ "async-trait", "audiopus 0.3.0-rc.0", @@ -3237,10 +3379,9 @@ dependencies = [ "dashmap", "derivative", "discortp", - "flume 0.11.0", + "flume", "futures", "nohash-hasher", - "once_cell", "parking_lot", "pin-project", "rand", @@ -3285,9 +3426,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -3295,9 +3436,9 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" dependencies = [ "itertools", "nom", @@ -3345,7 +3486,7 @@ dependencies = [ "once_cell", "paste", "percent-encoding", - "rustls 0.21.9", + "rustls 0.21.10", "rustls-pemfile", "serde", "serde_json", @@ -3491,7 +3632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" dependencies = [ "atoi", - "flume 0.11.0", + "flume", "futures-channel", "futures-core", "futures-executor", @@ -3659,6 +3800,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tempfile" version = "3.8.1" @@ -3668,7 +3815,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix 0.38.25", + "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -3763,9 +3910,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -3820,7 +3967,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.9", + "rustls 0.21.10", "tokio", ] @@ -3859,7 +4006,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls 0.21.9", + "rustls 0.21.10", "tokio", "tokio-rustls 0.24.1", "tungstenite 0.20.1", @@ -3999,11 +4146,17 @@ dependencies = [ "strength_reduce", ] +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" + [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" @@ -4039,7 +4192,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.21.9", + "rustls 0.21.10", "sha1", "thiserror", "url", @@ -4110,20 +4263,49 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "typesize" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d112ab96abcb66808bb508fce8a9143fe2cee32b167d240d5d792c326db5a4" +dependencies = [ + "chrono", + "dashmap", + "hashbrown 0.14.3", + "mini-moka", + "parking_lot", + "secrecy", + "serde_json", + "time", + "typesize-derive", + "url", +] + +[[package]] +name = "typesize-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b122284365ba8497be951b9a21491f70c9688eb6fddc582931a0703f6a00ece" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "unic-langid" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +checksum = "887622f8e7b723780c5e64b04dcc0c9b8f426ada7cca6790cd3ea3bf0f08037a" dependencies = [ "unic-langid-impl", ] [[package]] name = "unic-langid-impl" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +checksum = "5adeb847e35eed4efbffd9fb2e4d078b91ece56e4d6a3c0d2df55b3a1dac07d5" dependencies = [ "tinystr", ] @@ -4139,9 +4321,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -4194,9 +4376,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" +checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" dependencies = [ "base64 0.21.5", "log", @@ -4206,9 +4388,9 @@ dependencies = [ [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -4230,12 +4412,13 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom", "rand", + "serde", ] [[package]] @@ -4262,6 +4445,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -4279,9 +4472,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4289,9 +4482,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -4304,9 +4497,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -4316,9 +4509,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4326,9 +4519,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -4339,9 +4532,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-streams" @@ -4358,9 +4551,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -4372,15 +4565,15 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.5", + "ring 0.17.7", "untrusted 0.9.0", ] [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "whoami" @@ -4404,6 +4597,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -4446,6 +4648,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -4476,6 +4687,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -4488,6 +4714,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -4500,6 +4732,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -4512,6 +4750,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -4524,6 +4768,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -4536,6 +4786,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -4548,6 +4804,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -4560,6 +4822,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winreg" version = "0.50.0" @@ -4572,18 +4840,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.26" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.26" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 734befb6..0d1bd397 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,4 +43,4 @@ tracing = { version = "0.1", features = ["release_max_level_info"] } [patch.crates-io] serenity = { git = "https://github.com/serenity-rs/serenity", branch = "next" } -songbird = { git = "https://github.com/serenity-rs/songbird", branch = "next" } +songbird = { git = "https://github.com/tazz4843/songbird", branch = "serenity-next" } diff --git a/migrations/20231211021825_translate_to_english.sql b/migrations/20231211021825_translate_to_english.sql new file mode 100644 index 00000000..f1beae71 --- /dev/null +++ b/migrations/20231211021825_translate_to_english.sql @@ -0,0 +1,2 @@ +-- Add migration script here +ALTER TABLE guilds ADD COLUMN translate BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/scripty_audio_handler/Cargo.toml b/scripty_audio_handler/Cargo.toml index 961306e4..c88b86b6 100644 --- a/scripty_audio_handler/Cargo.toml +++ b/scripty_audio_handler/Cargo.toml @@ -23,7 +23,7 @@ scripty_metrics = { path = "../scripty_metrics" } scripty_premium = { path = "../scripty_premium" } tokio = { version = "1", features = ["parking_lot"] } scripty_data_storage = { path = "../scripty_data_storage" } -songbird = { git = "https://github.com/serenity-rs/songbird", branch = "next", features = [ +songbird = { git = "https://github.com/tazz4843/songbird", branch = "serenity-next", features = [ "receive", "rustls", ] } diff --git a/scripty_audio_handler/src/audio_handler.rs b/scripty_audio_handler/src/audio_handler.rs index 516742f3..885edec9 100644 --- a/scripty_audio_handler/src/audio_handler.rs +++ b/scripty_audio_handler/src/audio_handler.rs @@ -65,6 +65,7 @@ pub struct AudioHandler { automod_server_cfg: Arc, auto_detect_lang: Arc, transcribe_only_role: Arc>>, + translate: Arc, } impl AudioHandler { @@ -106,6 +107,7 @@ impl AudioHandler { automod_server_cfg: Arc::new(automod_server_cfg), auto_detect_lang: Arc::new(AtomicBool::new(false)), transcribe_only_role: Arc::new(RwLock::new(None)), + translate: Arc::new(AtomicBool::new(false)), }; this.reload_config().await?; @@ -132,8 +134,8 @@ impl AudioHandler { pub async fn reload_config(&self) -> Result<(), sqlx::Error> { let db = scripty_db::get_db(); let mut guild_res = sqlx::query!( - "SELECT be_verbose, language, auto_detect_lang, transcript_only_role FROM guilds \ - WHERE guild_id = $1", + "SELECT be_verbose, language, auto_detect_lang, transcript_only_role, translate FROM \ + guilds WHERE guild_id = $1", self.guild_id.get() as i64 ) .fetch_one(db) @@ -149,6 +151,7 @@ impl AudioHandler { self.premium_level.store(0, Ordering::Relaxed); self.auto_detect_lang.store(false, Ordering::Relaxed); } + self.translate.store(guild_res.translate, Ordering::Relaxed); std::mem::swap(&mut *self.language.write(), &mut guild_res.language); std::mem::swap( &mut *self.transcribe_only_role.write(), @@ -185,6 +188,7 @@ impl EventHandler for AudioHandler { self.transcript_results.clone(), Arc::clone(&self.automod_server_cfg), Arc::clone(&self.auto_detect_lang), + Arc::clone(&self.translate), )), EventContext::ClientDisconnect(client_disconnect_data) => { tokio::spawn(client_disconnect( diff --git a/scripty_audio_handler/src/events/voice_tick.rs b/scripty_audio_handler/src/events/voice_tick.rs index 0795ae8d..321e8816 100644 --- a/scripty_audio_handler/src/events/voice_tick.rs +++ b/scripty_audio_handler/src/events/voice_tick.rs @@ -37,6 +37,7 @@ pub async fn voice_tick( transcript_results: Option>>>, automod_server_cfg: Arc, auto_detect_lang: Arc, + translate: Arc, ) { let metrics = scripty_metrics::get_metrics(); let tick_start_time = Instant::now(); @@ -49,14 +50,7 @@ pub async fn voice_tick( last_tick_speakers.retain(|s| voice_data.silent.contains(s)); // handle those speaking this tick - handle_speakers( - Arc::clone(&ssrc_state), - Arc::clone(&metrics), - voice_data, - Arc::clone(&language), - verbose.clone(), - ) - .await; + handle_speakers(Arc::clone(&ssrc_state), Arc::clone(&metrics), voice_data).await; let hooks = handle_silent_speakers(SilentSpeakersContext { ssrc_state: Arc::clone(&ssrc_state), @@ -69,6 +63,7 @@ pub async fn voice_tick( transcript_results: transcript_results.clone(), ctx: &ctx, auto_detect_lang, + translate, }) .await; @@ -100,6 +95,7 @@ struct SilentSpeakersContext<'a> { transcript_results: TranscriptResults, ctx: &'a Context, auto_detect_lang: Arc, + translate: Arc, } async fn handle_silent_speakers( SilentSpeakersContext { @@ -113,6 +109,7 @@ async fn handle_silent_speakers( transcript_results, ctx, auto_detect_lang, + translate, }: SilentSpeakersContext<'_>, ) -> Vec<(ExecuteWebhook, u32)> { // batch up webhooks to send @@ -120,34 +117,37 @@ async fn handle_silent_speakers( for ssrc in last_tick_speakers { // make a new stream for the next time they speak and remove their old one - let lang = language.read().to_owned(); - let new_stream = match scripty_stt::get_stream( - if auto_detect_lang.load(Ordering::Relaxed) { - "auto" - } else { - &lang - }, - verbose.load(Ordering::Relaxed), - ) - .await - { - Ok(s) => s, + let maybe_old_stream = match scripty_stt::get_stream().await { + Ok(s) => ssrc_state.ssrc_stream_map.insert(ssrc, s), Err(e) => { error!(?ssrc, "failed to create new stream: {}", e); - continue; + ssrc_state.ssrc_stream_map.remove(&ssrc).map(|x| x.1) // take what we have } }; - let Some(old_stream) = ssrc_state.ssrc_stream_map.insert(ssrc, new_stream) else { + let old_stream = if let Some(old_stream) = maybe_old_stream { + old_stream + } else { + warn!(%ssrc, "no stream found for ssrc"); + hooks.push(( + ExecuteWebhook::new().content(format!( + "no stream found for user (likely a bug): SSRC {}", + ssrc + )), + ssrc, + )); continue; }; // finalize the stream + let lang = language.read().clone(); let (final_result, hook) = finalize_stream( old_stream, ssrc_state.ssrc_user_data_map.clone(), thread_id, ssrc, - verbose.load(Ordering::Relaxed), + lang, + &verbose, + &translate, ) .await; @@ -256,13 +256,7 @@ async fn handle_silent_speakers( hooks } -async fn handle_speakers( - ssrc_state: Arc, - metrics: Arc, - voice_data: VoiceTick, - language: Arc>, - verbose: Arc, -) { +async fn handle_speakers(ssrc_state: Arc, metrics: Arc, voice_data: VoiceTick) { for (ssrc, data) in voice_data.speaking { let st = Instant::now(); @@ -354,16 +348,13 @@ async fn handle_speakers( trace!(?ssrc, "done processing pkt"); } else { warn!(?ssrc, "no stream found for ssrc"); - // cold path so we can afford to do this - let lang = language.read().to_owned(); - let new_stream = - match scripty_stt::get_stream(&lang, verbose.load(Ordering::Relaxed)).await { - Ok(s) => s, - Err(e) => { - error!(?ssrc, "failed to create new stream: {}", e); - continue; - } - }; + let new_stream = match scripty_stt::get_stream().await { + Ok(s) => s, + Err(e) => { + error!(?ssrc, "failed to create new stream: {}", e); + continue; + } + }; ssrc_state.ssrc_stream_map.insert(ssrc, new_stream); } } else { @@ -381,51 +372,31 @@ async fn finalize_stream( user_data_map: SsrcUserDataMap, thread_id: Option, ssrc: u32, - verbose: bool, + language: String, + verbose: &Arc, + translate: &Arc, ) -> (Option, Option) { let mut final_transcript = None; debug!(%ssrc, "finalizing stream"); - let mut webhook_executor = if verbose { - let res = stream.get_result_verbose().await; - match res { - Ok(res) => { - if res.num_transcripts == 0 { - return (None, None); - } - let mut embed = - CreateEmbed::new().title(format!("Transcript 1/{}", res.num_transcripts)); - if let Some(transcript) = res.main_transcript { - if transcript.is_empty() { - return (None, None); - } - embed = embed.field("Transcription", &transcript, false); - final_transcript = Some(transcript); - } else { - return (None, None); - } - if let Some(confidence) = res.main_confidence { - embed = embed.field("Confidence", format!("{:.2}%", confidence), false) - } else { - embed = embed.field("Confidence", "unknown", false) - } - ExecuteWebhook::new().embed(embed) - } - Err(error) => handle_error(error, ssrc), - } - } else { - let res = stream.get_result().await; - match res { - Ok(res) if !res.result.is_empty() => { - let webhook_executor = ExecuteWebhook::new().content(&res.result); - final_transcript = Some(res.result); - webhook_executor - } - Ok(_) => return (None, None), - Err(error) => handle_error(error, ssrc), + let res = stream + .get_result( + language, + verbose.load(Ordering::Relaxed), + translate.load(Ordering::Relaxed), + ) + .await; + let mut webhook_executor = match res { + Ok(res) if !res.is_empty() => { + let webhook_executor = ExecuteWebhook::new().content(&res); + final_transcript = Some(res); + webhook_executor } + Ok(_) => return (None, None), + Err(error) => handle_error(error, ssrc), }; + debug!(%ssrc, "got stream results"); let Some(user_details) = user_data_map.get(&ssrc) else { @@ -462,6 +433,42 @@ fn handle_error(error: ModelError, ssrc: u32) -> ExecuteWebhook { error!(%ssrc, "STTS error: no available servers"); format!("no available STT servers (SSRC {})", ssrc) } + ModelError::MessagePackDecode(e) => { + error!(%ssrc, "STTS error: failed to decode messagepack: {}", e); + format!("internal STT service error (SSRC {})", ssrc) + } + ModelError::MessagePackEncode(e) => { + error!(%ssrc, "STTS error: failed to encode messagepack: {}", e); + format!("internal STT service error (SSRC {})", ssrc) + } + ModelError::InvalidMagicBytes(e) => { + error!(%ssrc, "STTS error: invalid magic bytes: {:?}", e); + format!("internal STT service error (SSRC {})", ssrc) + } + ModelError::PayloadOutOfOrder => { + error!(%ssrc, "STTS error: payload received out of order"); + format!("internal STT service error (SSRC {})", ssrc) + } + ModelError::InvalidPayload { expected, got } => { + error!(%ssrc, "STTS error: invalid payload: expected {:?}, got {:?}", expected, got); + format!("internal STT service error (SSRC {})", ssrc) + } + ModelError::OverloadedRemote => { + error!(%ssrc, "STTS error: remote overloaded"); + format!("STT service overloaded (SSRC {})", ssrc) + } + ModelError::InitializationTimedOut => { + error!(%ssrc, "STTS error: initialization timed out"); + format!("STT service initialization timed out (SSRC {})", ssrc) + } + ModelError::RemoteDisconnected => { + error!(%ssrc, "STTS error: remote disconnected"); + format!("STT service disconnected (SSRC {})", ssrc) + } + ModelError::TimedOutWaitingForResult => { + error!(%ssrc, "STTS error: timed out waiting for result"); + format!("STT service timed out (SSRC {})", ssrc) + } }; ExecuteWebhook::new().content(user_error) } diff --git a/scripty_bot/src/lib.rs b/scripty_bot/src/lib.rs index 77b503b8..7fc8f1fe 100644 --- a/scripty_bot/src/lib.rs +++ b/scripty_bot/src/lib.rs @@ -26,13 +26,7 @@ pub async fn entrypoint() { .expect("failed to init blocked entities"); // initialize the framework - let client = FrameworkBuilder::default() - .token(&cfg.token) - .client_settings(|b| { - b.event_handler(handler::BotEventHandler) - .raw_event_handler(handler::RawEventHandler) - .register_songbird_from_config(scripty_audio_handler::get_songbird()) - }) + let framework = FrameworkBuilder::default() .setup(move |ctx, _, c| { Box::pin(async move { set_cache_http(ctx.http.clone(), ctx.cache.clone()); @@ -60,7 +54,15 @@ pub async fn entrypoint() { }) }) .options(framework_opts::get_framework_opts()) - .intents(framework_opts::get_gateway_intents()); + .build(); + + let mut client = serenity::Client::builder(&cfg.token, framework_opts::get_gateway_intents()) + .framework(framework) + .event_handler(handler::BotEventHandler) + .raw_event_handler(handler::RawEventHandler) + .register_songbird_from_config(scripty_audio_handler::get_songbird()) + .await + .expect("failed to create serenity client"); - client.run_autosharded().await.expect("failed to run bot"); + client.start_autosharded().await.expect("failed to run bot"); } diff --git a/scripty_bot_utils/src/background_tasks/tasks/bot_list_poster.rs b/scripty_bot_utils/src/background_tasks/tasks/bot_list_poster.rs index 917e5934..500e8aab 100644 --- a/scripty_bot_utils/src/background_tasks/tasks/bot_list_poster.rs +++ b/scripty_bot_utils/src/background_tasks/tasks/bot_list_poster.rs @@ -36,7 +36,7 @@ impl BackgroundTask for BotListUpdater { async fn run(&mut self) { let stats = PostStats { server_count: self.ctx.cache.guild_count(), - shard_count: self.ctx.cache.shard_count(), + shard_count: self.ctx.cache.shard_count().get(), }; for list in self.bot_lists.iter() { diff --git a/scripty_bot_utils/src/background_tasks/tasks/status_update.rs b/scripty_bot_utils/src/background_tasks/tasks/status_update.rs index 8bd326ee..cdc82ae7 100644 --- a/scripty_bot_utils/src/background_tasks/tasks/status_update.rs +++ b/scripty_bot_utils/src/background_tasks/tasks/status_update.rs @@ -65,9 +65,9 @@ impl BackgroundTask for StatusUpdater { // create activity let activity = ActivityData { - name: "UwU~".to_string(), + name: "UwU~".to_string().into(), kind: ActivityType::Custom, - state: Some(shard_status), + state: Some(shard_status.into()), url: None, }; diff --git a/scripty_bot_utils/src/dm_support.rs b/scripty_bot_utils/src/dm_support.rs index 566a7af8..57539aeb 100644 --- a/scripty_bot_utils/src/dm_support.rs +++ b/scripty_bot_utils/src/dm_support.rs @@ -127,14 +127,15 @@ impl DmSupportStatus { .first() .expect("asserted one element already exists"); if message_channel.is_nsfw() { - embed_builder = embed_builder.field("Attached", &attachment.url, true); + embed_builder = embed_builder.field("Attached", attachment.url.as_str(), true); } else { - embed_builder = embed_builder.image(&attachment.url); + embed_builder = embed_builder.image(attachment.url.as_str()); } } Ordering::Greater => { for attached_file in message.attachments.iter() { - embed_builder = embed_builder.field("Attached", &attached_file.url, true); + embed_builder = + embed_builder.field("Attached", attached_file.url.as_str(), true); } } } diff --git a/scripty_bot_utils/src/error/handler.rs b/scripty_bot_utils/src/error/handler.rs index 9f044a39..b228861d 100644 --- a/scripty_bot_utils/src/error/handler.rs +++ b/scripty_bot_utils/src/error/handler.rs @@ -106,19 +106,11 @@ pub async fn on_error(error: FrameworkError<'_, Data, Error>) { let mut args = String::new(); for param in &ctx.command.parameters { if param.required { - write!( - &mut args, - "<{}> ", - param.name.as_ref().map_or("arg", |s| s.as_str()) - ) - .expect("failed to format string: this is a bug"); + write!(&mut args, "<{}> ", param.name) + .expect("failed to format string: this is a bug"); } else { - write!( - &mut args, - "[{}] ", - param.name.as_ref().map_or("arg", |s| s.as_str()) - ) - .expect("failed to format string: this is a bug"); + write!(&mut args, "[{}] ", param.name) + .expect("failed to format string: this is a bug"); } } @@ -140,14 +132,14 @@ pub async fn on_error(error: FrameworkError<'_, Data, Error>) { let response = ctx .interaction - .channel_id() + .channel_id .send_message(&ctx.serenity_context(), msg.clone()) .await; if let Err(e) = response { warn!("failed to send message while handling error: {}", e); let response = ctx .interaction - .user() + .user .direct_message(ctx.serenity_context(), msg) .await; if let Err(e) = response { diff --git a/scripty_bot_utils/src/error/message.rs b/scripty_bot_utils/src/error/message.rs index 598fc77c..f17da091 100644 --- a/scripty_bot_utils/src/error/message.rs +++ b/scripty_bot_utils/src/error/message.rs @@ -65,7 +65,7 @@ pub async fn log_error_message( let (guild_id, guild_name) = if let Some(guild_id) = ctx.guild_id() { let guild_name = cache .guild(guild_id) - .map_or_else(|| "unknown guild".to_string(), |g| g.name.clone()); + .map_or_else(|| "unknown guild".to_string(), |g| g.name.to_string()); e = e.field("Guild ID", guild_id.to_string(), false); e = e.field("Guild Name", &guild_name, true); diff --git a/scripty_bot_utils/src/extern_utils.rs b/scripty_bot_utils/src/extern_utils.rs index c44fe74b..527fa7f3 100644 --- a/scripty_bot_utils/src/extern_utils.rs +++ b/scripty_bot_utils/src/extern_utils.rs @@ -103,11 +103,12 @@ pub fn get_voice_channel_count() -> Result { Ok(count) } -pub fn get_shard_count() -> Result { +pub fn get_shard_count() -> Result { Ok(CLIENT_CACHE .get() .ok_or(CacheNotInitializedError)? - .shard_count()) + .shard_count() + .get()) } pub struct ShardInfo { @@ -126,9 +127,9 @@ pub struct ShardInfo { pub guild_count: usize, } -static CACHED_SHARD_GUILD_COUNT: OnceCell, Instant)>> = OnceCell::new(); +static CACHED_SHARD_GUILD_COUNT: OnceCell, Instant)>> = OnceCell::new(); -pub async fn get_shard_info() -> Result, CacheNotInitializedError> { +pub async fn get_shard_info() -> Result, CacheNotInitializedError> { let data = CLIENT_DATA.get().ok_or(CacheNotInitializedError)?; let cache = CLIENT_CACHE.get().ok_or(CacheNotInitializedError)?; @@ -147,7 +148,7 @@ pub async fn get_shard_info() -> Result, CacheNotInitial let mut shard_guild_count = HashMap::new(); for guild in cache.guilds() { - let guild_shard_id = ((guild.get() >> 22) % shard_count) as u32; + let guild_shard_id = ((guild.get() >> 22) % shard_count) as u16; if let Some(id) = shard_guild_count.get_mut(&guild_shard_id) { *id += 1; } else { diff --git a/scripty_bot_utils/src/generic_audio_message.rs b/scripty_bot_utils/src/generic_audio_message.rs index 2a2d385e..68cbbf86 100644 --- a/scripty_bot_utils/src/generic_audio_message.rs +++ b/scripty_bot_utils/src/generic_audio_message.rs @@ -82,6 +82,7 @@ pub async fn handle_message(ctx: Context, msg: Message) -> Result<(), GenericMes if msg.flags.map_or(false, |flags| { flags.contains(MessageFlags::IS_VOICE_MESSAGE) }) { + debug!(%msg.id, "message is a voice message, ignoring"); return Ok(()); } @@ -91,11 +92,11 @@ pub async fn handle_message(ctx: Context, msg: Message) -> Result<(), GenericMes for attachment in msg.attachments.iter() { if let Some(ext) = attachment.filename.split('.').last() { if AUDIO_EXTENSIONS.contains(&ext) { - debug!("found audio file"); + debug!(%msg.id, "found audio file"); attached_files.push(attachment); audio_files_found = true; } else if VIDEO_EXTENSIONS.contains(&ext) { - debug!("found video file"); + debug!(%msg.id, "found video file"); attached_files.push(attachment); video_files_found = true; } @@ -107,30 +108,34 @@ pub async fn handle_message(ctx: Context, msg: Message) -> Result<(), GenericMes } // does the guild even have it enabled? - let (language, audio_enabled, video_enabled) = sqlx::query!( - "SELECT language, transcribe_audio_files, transcribe_video_files FROM guilds WHERE \ - guild_id = $1", + let (language, audio_enabled, video_enabled, translate) = sqlx::query!( + "SELECT language, transcribe_audio_files, transcribe_video_files, translate FROM guilds \ + WHERE guild_id = $1", guild_id.get() as i64 ) .fetch_optional(scripty_db::get_db()) .await? .map_or_else( - || (String::new(), false, false), + || (String::new(), false, false, false), |row| { ( row.language, row.transcribe_audio_files, row.transcribe_video_files, + row.translate, ) }, ); if !(audio_enabled || video_enabled) { + debug!(%msg.id, "neither audio nor video enabled"); return Ok(()); } if audio_files_found && !audio_enabled { + debug!(%msg.id, "audio files found but audio not enabled"); return Ok(()); } if video_files_found && !video_enabled { + debug!(%msg.id, "video files found but video not enabled"); return Ok(()); } @@ -139,9 +144,11 @@ pub async fn handle_message(ctx: Context, msg: Message) -> Result<(), GenericMes .await .unwrap_or(PremiumTierList::None); if premium_tier == PremiumTierList::None { + debug!(%msg.id, "no premium tier, ignoring"); return Ok(()); } if video_files_found && premium_tier < PremiumTierList::Tier2 { + debug!(%msg.id, "video files found but not enough premium"); return Ok(()); } @@ -160,18 +167,19 @@ pub async fn handle_message(ctx: Context, msg: Message) -> Result<(), GenericMes }; // and then transcribe it - let transcripts = match handle_transcripts(attached_files, &language, premium_tier).await { - Ok(transcripts) => transcripts, - Err(e) => { - new_msg - .edit( - ctx, - EditMessage::new().content(format!("Failed to transcribe files: {:?}", e)), - ) - .await?; - return Err(e); - } - }; + let transcripts = + match handle_transcripts(attached_files, language, premium_tier, translate).await { + Ok(transcripts) => transcripts, + Err(e) => { + new_msg + .edit( + ctx, + EditMessage::new().content(format!("Failed to transcribe files: {:?}", e)), + ) + .await?; + return Err(e); + } + }; // sometimes to prevent spam we get no transcripts returned // (ie premium tier is too low for one file) @@ -361,8 +369,9 @@ enum TranscriptResult { async fn handle_transcripts( files: Vec<&Attachment>, - language: &str, + language: String, premium_tier: PremiumTierList, + translate: bool, ) -> Result, GenericMessageError> { let mut output = Vec::with_capacity(files.len()); for file in files { @@ -417,7 +426,7 @@ async fn handle_transcripts( output.push(TranscriptResult::VideoTooLong { max_video_length, video_length: file_length, - file_name: file.filename.clone(), + file_name: file.filename.to_string(), }); continue; } @@ -427,7 +436,7 @@ async fn handle_transcripts( output.push(TranscriptResult::AudioTooLong { max_audio_length, audio_length: file_length, - file_name: file.filename.clone(), + file_name: file.filename.to_string(), }); continue; } @@ -438,20 +447,22 @@ async fn handle_transcripts( let i16_audio = convert_to_pcm(path).await?; // fetch a stream, feed the audio, get the result, send it - let stream = scripty_stt::get_stream(language, false).await?; + let stream = scripty_stt::get_stream().await?; stream.feed_audio(i16_audio)?; - let transcript = stream.get_result().await?.result; + let transcript = stream + .get_result(language.clone(), false, translate) + .await?; let transcript = transcript.trim(); if transcript.is_empty() { output.push(TranscriptResult::EmptyTranscript { - file_name: file.filename.clone(), + file_name: file.filename.to_string(), }); continue; } else { output.push(TranscriptResult::Success { transcript: transcript.to_owned(), - file_name: file.filename.clone(), + file_name: file.filename.to_string(), }); } } diff --git a/scripty_bot_utils/src/voice_message.rs b/scripty_bot_utils/src/voice_message.rs index a50c9314..4bbce755 100644 --- a/scripty_bot_utils/src/voice_message.rs +++ b/scripty_bot_utils/src/voice_message.rs @@ -53,17 +53,18 @@ async fn internal_handle_message( debug!(%msg.id, "decoded voice message, feeding to speech-to-text"); // fetch guild language let db = scripty_db::get_db(); - let lang = sqlx::query!( - "SELECT language FROM guilds WHERE guild_id = $1", + let res = sqlx::query!( + "SELECT language, translate FROM guilds WHERE guild_id = $1", msg.guild_id.ok_or_else(crate::Error::expected_guild)?.get() as i64 ) .fetch_one(db) - .await? - .language; + .await?; + let lang = res.language; + let translate = res.translate; - let stream = scripty_stt::get_stream(&lang, false).await?; + let stream = scripty_stt::get_stream().await?; stream.feed_audio(output)?; - let transcript = stream.get_result().await?.result; + let transcript = stream.get_result(lang, false, translate).await?; let transcript = transcript.trim(); let mut msg_builder = EditMessage::new(); diff --git a/scripty_botlists/src/common.rs b/scripty_botlists/src/common.rs index f42bf313..00a7f1f7 100644 --- a/scripty_botlists/src/common.rs +++ b/scripty_botlists/src/common.rs @@ -10,7 +10,7 @@ pub trait StatPoster { #[derive(Debug, Copy, Clone)] pub struct PostStats { pub server_count: usize, - pub shard_count: u32, + pub shard_count: u16, } #[derive(Deserialize, Serialize, Debug, Copy, Clone)] diff --git a/scripty_botlists/src/lists/botlist_me/models.rs b/scripty_botlists/src/lists/botlist_me/models.rs index 298f6285..6b1162a8 100644 --- a/scripty_botlists/src/lists/botlist_me/models.rs +++ b/scripty_botlists/src/lists/botlist_me/models.rs @@ -1,7 +1,7 @@ #[derive(Debug, Serialize)] pub struct PostStats { pub server_count: usize, - pub shard_count: u32, + pub shard_count: u16, } #[derive(Debug, Deserialize, Copy, Clone)] diff --git a/scripty_botlists/src/lists/discord_bots_gg/models.rs b/scripty_botlists/src/lists/discord_bots_gg/models.rs index fafec73d..4a00f4fd 100644 --- a/scripty_botlists/src/lists/discord_bots_gg/models.rs +++ b/scripty_botlists/src/lists/discord_bots_gg/models.rs @@ -2,7 +2,7 @@ #[serde(rename_all = "camelCase")] pub struct PostStats { pub server_count: usize, - pub shard_count: u32, + pub shard_count: u16, } #[derive(Deserialize, Debug, Clone)] diff --git a/scripty_botlists/src/lists/discordextremelist_xyz/models.rs b/scripty_botlists/src/lists/discordextremelist_xyz/models.rs index 52deb10e..7662d1a8 100644 --- a/scripty_botlists/src/lists/discordextremelist_xyz/models.rs +++ b/scripty_botlists/src/lists/discordextremelist_xyz/models.rs @@ -2,7 +2,7 @@ #[serde(rename_all = "camelCase")] pub struct PostStats { pub server_count: usize, - pub shard_count: u32, + pub shard_count: u16, } #[derive(Deserialize, Debug, Clone)] diff --git a/scripty_botlists/src/lists/discordservices_net/models.rs b/scripty_botlists/src/lists/discordservices_net/models.rs index 12e7c670..ed7920f6 100644 --- a/scripty_botlists/src/lists/discordservices_net/models.rs +++ b/scripty_botlists/src/lists/discordservices_net/models.rs @@ -1,7 +1,7 @@ #[derive(Serialize, Debug, Copy, Clone)] pub struct PostStats { pub servers: usize, - pub shards: u32, + pub shards: u16, } #[derive(Deserialize, Debug, Clone)] diff --git a/scripty_botlists/src/lists/infinitybots_gg/models.rs b/scripty_botlists/src/lists/infinitybots_gg/models.rs index 06c18cb4..f34cdb42 100644 --- a/scripty_botlists/src/lists/infinitybots_gg/models.rs +++ b/scripty_botlists/src/lists/infinitybots_gg/models.rs @@ -1,5 +1,5 @@ #[derive(Serialize, Debug, Copy, Clone)] pub struct PostStats { pub servers: usize, - pub shards: u32, + pub shards: u16, } diff --git a/scripty_botlists/src/lists/radarcord_net/models.rs b/scripty_botlists/src/lists/radarcord_net/models.rs index 515516f4..53c37e04 100644 --- a/scripty_botlists/src/lists/radarcord_net/models.rs +++ b/scripty_botlists/src/lists/radarcord_net/models.rs @@ -1,7 +1,7 @@ #[derive(Serialize, Debug, Copy, Clone)] pub struct PostStats { pub servers: usize, - pub shards: u32, + pub shards: u16, } #[derive(Deserialize, Debug, Clone)] diff --git a/scripty_botlists/src/lists/top_gg/models.rs b/scripty_botlists/src/lists/top_gg/models.rs index 9908e8ea..92ce25bb 100644 --- a/scripty_botlists/src/lists/top_gg/models.rs +++ b/scripty_botlists/src/lists/top_gg/models.rs @@ -17,5 +17,5 @@ pub enum VoteWebhookType { #[derive(Debug, Serialize)] pub struct PostStats { pub server_count: usize, - pub shard_count: u32, + pub shard_count: u16, } diff --git a/scripty_botlists/src/lists/topcord_xyz/models.rs b/scripty_botlists/src/lists/topcord_xyz/models.rs index 515516f4..53c37e04 100644 --- a/scripty_botlists/src/lists/topcord_xyz/models.rs +++ b/scripty_botlists/src/lists/topcord_xyz/models.rs @@ -1,7 +1,7 @@ #[derive(Serialize, Debug, Copy, Clone)] pub struct PostStats { pub servers: usize, - pub shards: u32, + pub shards: u16, } #[derive(Deserialize, Debug, Clone)] diff --git a/scripty_botlists/src/lists/voidbots_net/models.rs b/scripty_botlists/src/lists/voidbots_net/models.rs index 3fe85879..853f5206 100644 --- a/scripty_botlists/src/lists/voidbots_net/models.rs +++ b/scripty_botlists/src/lists/voidbots_net/models.rs @@ -1,5 +1,5 @@ #[derive(Debug, Serialize)] pub struct PostStats { pub server_count: usize, - pub shard_count: u32, + pub shard_count: u16, } diff --git a/scripty_commands/Cargo.toml b/scripty_commands/Cargo.toml index 506bc129..62da8328 100644 --- a/scripty_commands/Cargo.toml +++ b/scripty_commands/Cargo.toml @@ -11,6 +11,8 @@ hex = "0.4" tracing = "0.1" indexmap = "1" humantime = "2" +typesize = "0.1" +num-format = "0.4" scripty_db = { path = "../scripty_db" } scripty_i18n = { path = "../scripty_i18n" } scripty_utils = { path = "../scripty_utils" } @@ -33,6 +35,8 @@ serenity = { git = "https://github.com/serenity-rs/serenity", branch = "next", f "builder", "collector", "utils", + "typesize", ] } sqlx = { version = "0.7", features = ["postgres", "macros", "migrate", "runtime-tokio-rustls", "time"] } poise = { git = "https://github.com/serenity-rs/poise", branch = "serenity-next", features = ["cache", "collector"] } +rand = "0.8.5" diff --git a/scripty_commands/src/cmds/admin/cache_info.rs b/scripty_commands/src/cmds/admin/cache_info.rs new file mode 100644 index 00000000..7f61fe46 --- /dev/null +++ b/scripty_commands/src/cmds/admin/cache_info.rs @@ -0,0 +1,63 @@ +use std::borrow::Cow; + +use num_format::{Locale, ToFormattedString}; +use poise::CreateReply; +use serenity::builder::CreateEmbed; +use typesize::TypeSize; + +use crate::{Context, Error}; + +#[poise::command(prefix_command, owners_only, hide_in_help)] +pub async fn cache_info(ctx: Context<'_>) -> Result<(), Error> { + struct Field { + name: String, + size: usize, + value: String, + is_collection: bool, + } + + let serenity_cache = ctx.cache(); + let cache_stats = serenity_cache.get_size_details(); + + let mut fields = Vec::new(); + for field in cache_stats { + let name = format!("`{}`", field.name); + let size = field.size.to_formatted_string(&Locale::en); + if let Some(count) = field.collection_items { + let (count, size_per) = if count == 0 { + (Cow::Borrowed("0"), Cow::Borrowed("N/A")) + } else { + let count_fmt = count.to_formatted_string(&Locale::en); + let mut size_per = (field.size / count).to_formatted_string(&Locale::en); + size_per.push('b'); + + (Cow::Owned(count_fmt), Cow::Owned(size_per)) + }; + + fields.push(Field { + name, + size: field.size, + is_collection: true, + value: format!("Size: `{size}b`\nCount: `{count}`\nSize per model: `{size_per}`"), + }); + } else { + fields.push(Field { + name, + size: field.size, + is_collection: false, + value: format!("Size: `{size}b`"), + }); + }; + } + + fields.sort_by_key(|field| field.size); + fields.sort_by_key(|field| field.is_collection); + fields.reverse(); + + let embed = CreateEmbed::default() + .title("Cache Statistics") + .fields(fields.into_iter().map(|f| (f.name, f.value, true))); + + ctx.send(CreateReply::default().embed(embed)).await?; + Ok(()) +} diff --git a/scripty_commands/src/cmds/admin/guild_check.rs b/scripty_commands/src/cmds/admin/guild_check.rs index 62de8c14..9de75871 100644 --- a/scripty_commands/src/cmds/admin/guild_check.rs +++ b/scripty_commands/src/cmds/admin/guild_check.rs @@ -15,8 +15,7 @@ pub async fn check_guilds(ctx: Context<'_>, specified_ratio: f64) -> Result<(), let mut error_count: u32 = 0; // guilds should be categorized by the number of members in them - // groups: 0-2, 2-5, 5-10, 10-25, 25-50, 50-100, 100-250, 250-500, 500-1000, 1000-2500, 2500-5000, 5000-10000, 10000+ - const GROUPS: [Range; 13] = [ + const GROUPS: [Range; 18] = [ 0..2, 2..5, 5..10, @@ -29,7 +28,12 @@ pub async fn check_guilds(ctx: Context<'_>, specified_ratio: f64) -> Result<(), 1000..2500, 2500..5000, 5000..10000, - 10000..u64::MAX, + 10000..24999, + 25000..49999, + 50000..99999, + 100000..250000, + 250000..500000, + 1000000..u64::MAX, ]; // map of bounds to the number of servers in that range @@ -43,7 +47,7 @@ pub async fn check_guilds(ctx: Context<'_>, specified_ratio: f64) -> Result<(), .expect("client data not initialized") .shard_manager .clone(); - let shard_count = shard_manager.runners.lock().await.len() as u32; + let shard_count = shard_manager.runners.lock().await.len() as u16; for guild in ctx.serenity_context().cache.guilds() { let g = match guild.to_guild_cached(&ctx) { @@ -109,7 +113,7 @@ pub async fn check_guilds(ctx: Context<'_>, specified_ratio: f64) -> Result<(), } let ratio = bot_count as f64 / user_count as f64; if ratio > specified_ratio { - guild_warnings.push((g.name.clone(), g.id.get(), ratio)); + guild_warnings.push((g.name.to_string(), g.id.get(), ratio)); } } diff --git a/scripty_commands/src/cmds/admin/mod.rs b/scripty_commands/src/cmds/admin/mod.rs index e7fd2794..c3783e18 100644 --- a/scripty_commands/src/cmds/admin/mod.rs +++ b/scripty_commands/src/cmds/admin/mod.rs @@ -1,9 +1,11 @@ use crate::{Context, Error}; +mod cache_info; mod guild_check; mod hash_user_id; mod shutdown; +pub use cache_info::cache_info; pub use guild_check::*; pub use hash_user_id::hash_user_id; diff --git a/scripty_commands/src/cmds/config/mod.rs b/scripty_commands/src/cmds/config/mod.rs index d2029d8e..0f909dec 100644 --- a/scripty_commands/src/cmds/config/mod.rs +++ b/scripty_commands/src/cmds/config/mod.rs @@ -31,7 +31,7 @@ pub async fn config_root(ctx: Context<'_>) -> Result<(), Error> { .await; ctx.send( - CreateReply::new().ephemeral(true).embed( + CreateReply::default().ephemeral(true).embed( CreateEmbed::new() .title(format_message!( resolved_language, diff --git a/scripty_commands/src/cmds/config/transcribe_only_role.rs b/scripty_commands/src/cmds/config/transcribe_only_role.rs index ca3d58a2..da581e52 100644 --- a/scripty_commands/src/cmds/config/transcribe_only_role.rs +++ b/scripty_commands/src/cmds/config/transcribe_only_role.rs @@ -31,7 +31,7 @@ pub async fn config_transcribe_only_role( .await?; ctx.send( - CreateReply::new() + CreateReply::default() .allowed_mentions(CreateAllowedMentions::new().empty_roles()) .content(format_message!( resolved_language, diff --git a/scripty_commands/src/cmds/terms_of_service.rs b/scripty_commands/src/cmds/terms_of_service.rs index bd81f6f2..00de5ee8 100644 --- a/scripty_commands/src/cmds/terms_of_service.rs +++ b/scripty_commands/src/cmds/terms_of_service.rs @@ -50,7 +50,7 @@ pub async fn terms_of_service(ctx: Context<'_>) -> Result<(), Error> { // send a message with the terms of service and privacy policy let m = ctx .send( - CreateReply::new() + CreateReply::default() .content(format_message!(resolved_language, "agreeing-to-tos")) .components(vec![CreateActionRow::Buttons(vec![ CreateButton::new("tos_agree") @@ -69,7 +69,13 @@ pub async fn terms_of_service(ctx: Context<'_>) -> Result<(), Error> { .timeout(std::time::Duration::from_secs(60)) .author_id(ctx.author().id) .message_id(m.message().await?.id) - .custom_ids(vec!["tos_agree".to_string(), "tos_disagree".to_string()]) + .custom_ids( + vec![ + "tos_agree".to_string().into(), + "tos_disagree".to_string().into(), + ] + .into(), + ) .await; if let Some(interaction) = maybe_interaction { @@ -101,7 +107,7 @@ pub async fn terms_of_service(ctx: Context<'_>) -> Result<(), Error> { } else { m.edit( ctx, - CreateReply::new() + CreateReply::default() .content(format_message!(resolved_language, "tos-agree-timed-out")) .components(vec![]), ) diff --git a/scripty_commands/src/i18n.rs b/scripty_commands/src/i18n.rs index 4978ba61..7c45e6a8 100644 --- a/scripty_commands/src/i18n.rs +++ b/scripty_commands/src/i18n.rs @@ -46,11 +46,8 @@ pub fn localize_commands(cmds: &mut Vec>) { } for parameter in cmd.parameters.iter_mut() { - let Some(ref parameter_name) = parameter.name else { - continue; - }; let Some(formatted_parameter_name) = - get_fmt_msg(language, &key, Some(parameter_name), command_name, true) + get_fmt_msg(language, &key, Some(&*parameter.name), command_name, true) else { continue; }; @@ -61,7 +58,7 @@ pub fn localize_commands(cmds: &mut Vec>) { let Some(formatted_parameter_description) = get_fmt_msg( language, &key, - Some(&format!("{}-description", ¶meter_name)), + Some(&format!("{}-description", ¶meter.name)), command_name, false, ) else { @@ -77,7 +74,7 @@ pub fn localize_commands(cmds: &mut Vec>) { let Some(formatted_choice_name) = get_fmt_msg( language, &key, - Some(&format!("{}-choice-{}", parameter_name, choice.name)), + Some(&format!("{}-choice-{}", parameter.name, choice.name)), command_name, false, ) else { diff --git a/scripty_commands/src/lib.rs b/scripty_commands/src/lib.rs index 832ef853..93191407 100644 --- a/scripty_commands/src/lib.rs +++ b/scripty_commands/src/lib.rs @@ -24,7 +24,11 @@ pub fn build_commands() -> Vec> { ..cmds::block() }, poise::Command { - subcommands: vec![cmds::check_guilds(), cmds::hash_user_id()], + subcommands: vec![ + cmds::check_guilds(), + cmds::hash_user_id(), + cmds::cache_info(), + ], ..cmds::admin() }, poise::Command { diff --git a/scripty_stt/Cargo.toml b/scripty_stt/Cargo.toml index d5c055aa..1c21700e 100644 --- a/scripty_stt/Cargo.toml +++ b/scripty_stt/Cargo.toml @@ -7,16 +7,21 @@ license = "EUPL-1.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -flume = "0.10" +flume = "0.11" magnum = "1" dashmap = "5" +futures = "0.3" tracing = "0.1" byteorder = "1" +rmp-serde = "1" once_cell = "1" serde_json = "1" dasp_signal = "0.11" +parking_lot = "0.12" +uuid = { version = "1", features = ["v4"] } serde = { version = "1", features = ["derive"] } tokio = { version = "1", features = ["sync"] } scripty_config = { path = "../scripty_config" } scripty_metrics = { path = "../scripty_metrics" } dasp_interpolate = { version = "0.11", features = ["linear"] } +scripty-common = { git = "https://github.com/scripty-bot/scripty-common" } diff --git a/scripty_stt/src/lib.rs b/scripty_stt/src/lib.rs index 4e038eb9..b0087404 100644 --- a/scripty_stt/src/lib.rs +++ b/scripty_stt/src/lib.rs @@ -34,10 +34,10 @@ pub fn get_model_languages() -> Vec { } /// Get a new stream. -pub async fn get_stream(language: &str, verbose: bool) -> Result { +pub async fn get_stream() -> Result { load_balancer::LOAD_BALANCER .get() .expect("initialize load balancer before trying to get stream") - .get_stream(language, verbose) + .get_stream() .await } diff --git a/scripty_stt/src/load_balancer.rs b/scripty_stt/src/load_balancer.rs index 185f154b..7c00dcc6 100644 --- a/scripty_stt/src/load_balancer.rs +++ b/scripty_stt/src/load_balancer.rs @@ -1,4 +1,5 @@ use std::{ + collections::VecDeque, net::SocketAddr, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, @@ -7,32 +8,63 @@ use std::{ time::Duration, }; +use byteorder::NetworkEndian; use dashmap::DashMap; use once_cell::sync::OnceCell; +use parking_lot::Mutex; +use scripty_common::stt_transport_models::{ + ClientToServerMessage, + ServerToClientMessage, + StatusConnectionData, + StatusConnectionOpen, +}; use scripty_config::SttServiceDefinition; use tokio::{ - io, io::{AsyncReadExt, AsyncWriteExt}, - net::lookup_host, + net::{ + lookup_host, + tcp::{OwnedReadHalf, OwnedWriteHalf}, + TcpStream, + }, + sync::broadcast::{Receiver, Sender}, }; use crate::{ModelError, Stream, NUM_STT_SERVICE_TRIES}; +/// Maximum number of workers to queue up. +/// +/// Takes roughly 60ms after TCP RTT to establish a connection to a server. +/// Scripty's analytics put all time peak usage at 11 simultaneous streams. +/// +/// 11 concurrent streams * 60ms TCP RTT = 660ms / 20ms packet length = 33 queue slots, +/// rounded down to 32. +const MAXIMUM_QUEUE_SIZE: usize = 32; + pub static LOAD_BALANCER: OnceCell = OnceCell::new(); /// Round-robin load balancer that equally loads all tasks, /// until one notes that it is overloaded, at which point it is removed from the pool. /// /// If it notifies the master that it is no longer overloaded, it is re-added. +#[derive(Clone)] pub struct LoadBalancer { /// The current worker index. - current_index: AtomicUsize, + current_index: Arc, /// A list of all workers. - workers: DashMap, + workers: Arc>, + /// Queued-up workers ready for use. + /// + /// This is used to prevent dropping a few hundred milliseconds of audio at the very start of a stream. + /// If a worker is queued up, it is ready to be used immediately. + queued_workers: Arc>>, + /// Channel to request a new worker be queued up. + /// + /// Allows avoiding busy waiting in the background task. + new_worker_tx: flume::Sender<()>, } impl LoadBalancer { - pub async fn new() -> io::Result { + pub async fn new() -> Result { let stt_services = scripty_config::get_config().stt_services.clone(); let mut peer_addresses: Vec = Vec::new(); for service in stt_services { @@ -50,14 +82,20 @@ impl LoadBalancer { } } - let workers = DashMap::new(); + let workers = Arc::new(DashMap::new()); for (n, addr) in peer_addresses.into_iter().enumerate() { workers.insert(n, LoadBalancedStream::new(addr).await?); } - Ok(Self { - current_index: AtomicUsize::new(0), + let (new_worker_tx, new_worker_rx) = flume::unbounded(); + let this = Self { + current_index: Arc::new(AtomicUsize::new(0)), workers, - }) + queued_workers: Arc::new(Mutex::new(VecDeque::with_capacity(MAXIMUM_QUEUE_SIZE))), + new_worker_tx, + }; + let t2 = this.clone(); + tokio::spawn(t2.new_worker_background_task(new_worker_rx)); + Ok(this) } fn get_next_worker_idx(&self) -> usize { @@ -83,8 +121,8 @@ impl LoadBalancer { if (allow_overload && worker.can_overload) || !worker.is_overloaded() && !worker.is_in_error() { - // usually this is going to be the fast path and it will immediately return this worker - // if it isn't, this is still decently fast, an O(2n) operation worst case + // usually this is going to be the fast path, and it will immediately return this worker. + // if it isn't, this is still decently fast, an O(2n) operation worst case. // given there's very likely never going to be more than 255 workers, this is fine return Ok(idx); } @@ -94,8 +132,8 @@ impl LoadBalancer { // are we back at the start? if !allow_overload && iter_count > self.workers.len() { - // we've looped through all workers and none are available: - // try again, but this time allow overloading + // we've looped through all workers, and none are available: + // try again, but this time, allow overloading allow_overload = true; } @@ -116,12 +154,12 @@ impl LoadBalancer { } } - pub async fn get_stream(&self, language: &str, verbose: bool) -> Result { + async fn spawn_new_stream(&self) -> Result { let worker_id = self.find_worker()?; let worker = self.workers.get(&worker_id).expect("worker should exist"); let metrics = scripty_metrics::get_metrics(); - match worker.open_connection(language, verbose).await { + match worker.open_connection().await { Ok(s) => { metrics.stt_server_fetch_success.inc_by(1); Ok(s) @@ -132,13 +170,72 @@ impl LoadBalancer { } } } + + async fn new_worker_background_task(self, new_worker_rx: flume::Receiver<()>) { + loop { + { + // check if we have reached the maximum queue size + if self.queued_workers.lock().len() >= MAXIMUM_QUEUE_SIZE { + // wait for a new worker to be requested + if new_worker_rx.recv_async().await.is_err() { + error!("all clients disconnected (should never happen)"); + return; + }; + } + debug!( + "got request for new worker, {} queued", + self.queued_workers.lock().len() + ); + } + + // spawn a new worker + let new_worker = match self.spawn_new_stream().await { + Ok(s) => s, + Err(e) => { + error!("failed to spawn new worker: {}", e); + continue; + } + }; + self.queued_workers.lock().push_back(new_worker); + } + } + + pub async fn get_stream(&self) -> Result { + // check if we have any queued workers + { + let mut queued_workers = self.queued_workers.lock(); + if let Some(worker) = queued_workers.pop_front() { + // request a new worker to be queued up + let new_worker_queue = self.new_worker_tx.clone(); + tokio::spawn(async move { new_worker_queue.send_async(()).await }); + + // return the one we got + return Ok(worker); + } + } + + // spawn a new worker + let new_worker = match self.spawn_new_stream().await { + Ok(s) => s, + Err(e) => { + error!("failed to spawn new worker: {}", e); + return Err(e); + } + }; + Ok(new_worker) + } } pub struct LoadBalancedStream { - peer_address: SocketAddr, - is_overloaded: Arc, - can_overload: bool, - is_in_error: Arc, + peer_address: SocketAddr, + is_overloaded: Arc, + can_overload: bool, + waiting_for_new_stream: Arc, + + msg_tx: Sender, + msg_rx_transmit_handle: Sender, + // keep this field that way there's always one receiver + _msg_rx: Receiver, } impl LoadBalancedStream { @@ -149,116 +246,271 @@ impl LoadBalancedStream { #[inline] pub fn is_in_error(&self) -> bool { - self.is_in_error.load(Ordering::Relaxed) + self.waiting_for_new_stream.load(Ordering::Relaxed) } - pub(crate) async fn open_connection( - &self, - language: &str, - verbose: bool, - ) -> Result { + pub async fn open_connection(&self) -> Result { if !self.can_overload && self.is_overloaded() { - return Err(ModelError::Io(io::Error::new( - io::ErrorKind::Other, - "remote is overloaded", - ))); + return Err(ModelError::OverloadedRemote); } - let res = Stream::new(language, verbose, self.peer_address).await; - self.is_in_error.store(res.is_err(), Ordering::Relaxed); - res + Stream::new( + self.peer_address, + self.msg_tx.clone(), + self.msg_rx_transmit_handle.subscribe(), + ) + .await } - pub async fn new(peer_address: SocketAddr) -> io::Result { + pub async fn new(peer_address: SocketAddr) -> Result { // open a connection to the remote - let mut peer_stream = tokio::net::TcpStream::connect(peer_address).await?; - - // convert this connection into a data-only connection (send 0x04) - peer_stream.write_u8(0x04).await?; - - // wait for a response of 0x06 (status connection open, fields max_utilization: f64, can_overload: bool) - if peer_stream.read_u8().await? != 0x06 { - return Err(io::Error::new( - io::ErrorKind::Other, - "unexpected response from server", - )); - } + info!("trying to connect to STT service at {}", peer_address); + let peer_stream = TcpStream::connect(peer_address).await?; + let (mut stream_read, stream_write) = peer_stream.into_split(); - // read the fields - let max_utilization = peer_stream.read_f64().await?; - let can_overload = peer_stream.read_u8().await? == 1; + // wait for the server to send a StatusConnectionOpen message + info!(%peer_address, "waiting for initialization"); + let ServerToClientMessage::StatusConnectionOpen(StatusConnectionOpen { + max_utilization, + can_overload, + }) = read_socket_message(&mut stream_read).await? + else { + // got something other than a StatusConnectionOpen message + // should never happen + return Err(ModelError::PayloadOutOfOrder); + }; - debug!( + info!( ?max_utilization, ?can_overload, ?peer_address, "got data for new stream" ); - let is_overloaded = Arc::new(AtomicBool::new(false)); - let iso2 = Arc::clone(&is_overloaded); - let is_in_error = Arc::new(AtomicBool::new(false)); - let iie2 = Arc::clone(&is_in_error); + // spawn background tx and rx tasks + let (client_to_server_tx, client_to_server_rx) = tokio::sync::broadcast::channel(16384); + let (server_to_client_tx, server_to_client_rx) = tokio::sync::broadcast::channel(16384); + // error handling queue + let (stream_error_tx, mut stream_error_rx) = tokio::sync::mpsc::channel(2); + let (new_read_stream_tx, new_read_stream_rx) = tokio::sync::mpsc::channel(1); + let (new_write_stream_tx, new_write_stream_rx) = tokio::sync::mpsc::channel(1); - // spawn a background task that will monitor the connection, and if it reports being overloaded, sets the overloaded flag + // read stream task + struct ReadStreamTask { + stream_read: OwnedReadHalf, + server_to_client_tx: tokio::sync::broadcast::Sender, + stream_error_tx: tokio::sync::mpsc::Sender, + new_read_stream_rx: tokio::sync::mpsc::Receiver, + } + let mut read_stream_task = ReadStreamTask { + stream_read, + server_to_client_tx: server_to_client_tx.clone(), + stream_error_tx: stream_error_tx.clone(), + new_read_stream_rx, + }; tokio::spawn(async move { - let metrics = scripty_metrics::get_metrics(); - let mut peer_stream = peer_stream; - loop { - let data: u8 = tokio::select! { - data_type = peer_stream.read_u8() => { - match data_type { - Ok(d) => d, - Err(e) => { - error!(?peer_address, "error reading from peer: {}", e); - // try to reconnect - peer_stream = match tokio::net::TcpStream::connect(peer_address).await { - Ok(s) => s, - Err(e) => { - error!(?peer_address, "error reconnecting to peer: {}", e); - iie2.store(true, Ordering::Relaxed); - metrics.stt_server_fetch_failure.inc_by(1); - const ONE_SECOND: Duration = Duration::from_secs(1); - tokio::time::sleep(ONE_SECOND).await; - continue; - } - }; - continue; + 'outer: loop { + let error = 'inner: loop { + let message = tokio::select! { + biased; + new_handle = read_stream_task.new_read_stream_rx.recv() => { + // always swap handles before trying to send anything new + match new_handle { + Some(stream) => { + read_stream_task.stream_read = stream; + continue 'inner; + } + None => { + error!(%peer_address, + "error receiving new stream from error queue: error task exited early" + ); + break 'outer; + } } } - }, - _ = tokio::signal::ctrl_c() => { - break + message = read_socket_message(&mut read_stream_task.stream_read) => { + message + } + }; + match message { + Ok(message) => { + debug!("got message: {:?}", message); + if let Err(e) = read_stream_task.server_to_client_tx.send(message) { + error!(%peer_address, + "error sending message to client: no remaining receivers: {}", + e + ); + break 'outer; // no remaining receivers, thus we are done + } + } + Err(e) => { + error!(%peer_address,"error reading message from server: {}", e); + break 'inner e; + } } }; - iie2.store(false, Ordering::Relaxed); - if data != 0x07 { - error!(?peer_address, "unexpected data type from peer: {}", data); - // toss the error to the handler which will retry - continue; + // error with stream, send our stream to the error queue and wait for a new one back + if let Err(e) = read_stream_task.stream_error_tx.send(error).await { + error!( + %peer_address, + "error sending error to error queue: error task exited early: {}", + e + ); + break 'outer; } - metrics.stt_server_fetch_success.inc_by(1); + // wait for a new stream to be sent back + match read_stream_task.new_read_stream_rx.recv().await { + Some(stream) => read_stream_task.stream_read = stream, + None => { + error!( + %peer_address, + "error receiving new stream from error queue: error task exited early" + ); + break 'outer; + } + } + } + }); - // read payload (utilization: f64) - let utilization = match peer_stream.read_f64().await { - Ok(u) => u, - Err(e) => { - error!(?peer_address, "error reading from peer: {}", e); - // toss the error to the handler which will try to reconnect or exit - continue; + // write stream task + struct WriteStreamTask { + stream_write: OwnedWriteHalf, + client_to_server_rx: tokio::sync::broadcast::Receiver, + stream_error_tx: tokio::sync::mpsc::Sender, + new_write_stream_rx: tokio::sync::mpsc::Receiver, + } + let mut write_stream_task = WriteStreamTask { + stream_write, + client_to_server_rx, + stream_error_tx, + new_write_stream_rx, + }; + tokio::spawn(async move { + 'outer: loop { + let error = 'inner: loop { + let message = tokio::select! { + biased; + new_handle = write_stream_task.new_write_stream_rx.recv() => { + // always swap handles before trying to send anything new + match new_handle { + Some(stream) => { + write_stream_task.stream_write = stream; + continue 'inner; + } + None => { + error!(%peer_address, + "error receiving new stream from error queue: error task exited early" + ); + break 'outer; + } + } + } + message = write_stream_task.client_to_server_rx.recv() => { + message + }, + }; + match message { + Ok(message) => { + debug!("sending message: {:?}", message); + if let Err(e) = + write_socket_message(&mut write_stream_task.stream_write, &message) + .await + { + error!(%peer_address, "error sending message to server: {}", e); + break 'inner e; + } + } + Err(e) => { + error!( + %peer_address, + "error reading message from client: no remaining transmitters: {}", + e + ); + break 'outer; // no remaining transmitters, thus we are done + } } }; - // if the utilization is above the threshold, set the overloaded flag - iso2.store(utilization > max_utilization, Ordering::Relaxed); + // error with stream, send our stream to the error queue and wait for a new one back + if let Err(e) = write_stream_task.stream_error_tx.send(error).await { + error!(%peer_address, + "error sending error to error queue: error task exited early: {}", + e + ); + break 'outer; + } + + // wait for a new stream to be sent back + match write_stream_task.new_write_stream_rx.recv().await { + Some(stream) => write_stream_task.stream_write = stream, + None => { + error!( + %peer_address, + "error receiving new stream from error queue: error task exited early", + ); + break 'outer; + } + } } - // write 0x03 to the stream to close the connection - if let Err(e) = peer_stream.write_u8(0x03).await { - error!( - ?peer_address, - "error closing connection to {}: {}", peer_address, e - ); + }); + + let waiting_for_new_stream = Arc::new(AtomicBool::new(false)); + let wfns2 = Arc::clone(&waiting_for_new_stream); + // error handling task + tokio::spawn(async move { + loop { + let _error = stream_error_rx.recv().await; + warn!("got error from stream pair"); + wfns2.store(true, Ordering::Relaxed); + + // start a new connection to the server + let mut peer_stream = None; + for n in 0..=12 { + // try 12 times to connect to the server with exponential backoff + let maybe_stream = TcpStream::connect(peer_address).await; + match maybe_stream { + Ok(stream) => { + peer_stream = Some(stream); + break; + } + Err(e) => { + error!(%peer_address, "error connecting to server: {}", e); + tokio::time::sleep(Duration::from_secs(2_u64.pow(n))).await; + } + } + } + let peer_stream = match peer_stream { + Some(stream) => stream, + None => { + error!(%peer_address, "failed to connect to server"); + break; + } + }; + let (stream_read, stream_write) = peer_stream.into_split(); + // send the new streams to the read and write tasks + let _ = new_read_stream_tx.send(stream_read).await; + let _ = new_write_stream_tx.send(stream_write).await; + wfns2.store(false, Ordering::Relaxed); + } + }); + + let is_overloaded = Arc::new(AtomicBool::new(false)); + let iso2 = Arc::clone(&is_overloaded); + let mut server_to_client_rx2 = server_to_client_tx.subscribe(); + // monitoring task + tokio::spawn(async move { + loop { + let Ok(res) = server_to_client_rx2.recv().await else { + // error happened, we are never going to get any more messages + break; + }; + if let ServerToClientMessage::StatusConnectionData(StatusConnectionData { + utilization, + }) = res + { + iso2.store(utilization > max_utilization, Ordering::Relaxed); + } } }); @@ -266,7 +518,59 @@ impl LoadBalancedStream { peer_address, is_overloaded, can_overload, - is_in_error, + waiting_for_new_stream, + msg_tx: client_to_server_tx, + msg_rx_transmit_handle: server_to_client_tx, + _msg_rx: server_to_client_rx, }) } } + +async fn read_socket_message( + socket: &mut OwnedReadHalf, +) -> Result { + // read the magic bytes + let mut magic = [0; 4]; + socket.read_exact(&mut magic).await?; + if magic != scripty_common::MAGIC_BYTES { + return Err(ModelError::InvalidMagicBytes(magic)); + } + + // read the data length + let mut data_length_bytes = [0; 8]; + socket.read_exact(&mut data_length_bytes).await?; + let data_length = { + use byteorder::ByteOrder; + NetworkEndian::read_u64(&data_length_bytes) + }; + + // read the data + let mut data = vec![0; data_length as usize]; + socket.read_exact(&mut data).await?; + + // deserialize the data + Ok(rmp_serde::from_slice(&data)?) +} + +async fn write_socket_message( + socket: &mut OwnedWriteHalf, + message: &ClientToServerMessage, +) -> Result<(), ModelError> { + // serialize the message + let mut data = Vec::new(); + rmp_serde::encode::write(&mut data, message)?; + + // write the magic bytes + socket.write_all(&scripty_common::MAGIC_BYTES).await?; + + // write the data length + socket.write_u64(data.len() as u64).await?; + + // write the data + socket.write_all(&data).await?; + + // flush the socket + socket.flush().await?; + + Ok(()) +} diff --git a/scripty_stt/src/models.rs b/scripty_stt/src/models.rs index 5dee67e7..33b2e44c 100644 --- a/scripty_stt/src/models.rs +++ b/scripty_stt/src/models.rs @@ -1,234 +1,152 @@ use std::{net::SocketAddr, time::Duration}; -use byteorder::{ByteOrder, NetworkEndian}; -use flume::{Receiver, Sender}; +use scripty_common::stt_transport_models::{ + AudioData, + ClientToServerMessage, + FinalizeStreaming, + InitializationComplete, + InitializeStreaming, + ServerToClientMessage, + SttError, + SttSuccess, +}; use tokio::{ io, - io::{AsyncReadExt, AsyncWriteExt}, - net::TcpStream, + sync::broadcast::{Receiver, Sender}, }; +use uuid::Uuid; use crate::NUM_STT_SERVICE_TRIES; pub struct Stream { - comm: Sender>, - final_comm: Sender, - err_comm: Receiver, -} - -enum FinalizeVariant { - Normal(Sender>), - Verbose(Sender>), + tx: Sender, + rx: Receiver, + peer_address: SocketAddr, + session_id: Uuid, } impl Stream { pub(crate) async fn new( - language: &str, - verbose: bool, - remote: SocketAddr, + peer_address: SocketAddr, + tx: Sender, + mut rx: Receiver, ) -> Result { - let mut socket = TcpStream::connect(remote).await?; - - // handshake with server - // 0x00: Initialize Streaming - socket.write_u8(0x00).await?; - // field 0: verbose: bool - socket.write_u8(verbose as u8).await?; - // field 1: language: String - socket.write_u64(language.len() as u64).await?; - socket.write_all(language.as_ref()).await?; - socket.flush().await?; - - // wait for response - match socket.read_u8().await? { - 0x00 => {} - _ => return Err(ModelError::SttsServer(2147483653)), - }; - - let (comm_tx, comm_rx) = flume::unbounded::>(); - let (final_tx, final_rx) = flume::bounded(0); - let (err_tx, err_rx) = flume::bounded(1); - - tokio::spawn(async move { - loop { - tokio::select! { - val = comm_rx.recv_async() => { - match val { - Ok(data) => { - if let Err(e) = Self::feed_audio_wrapper(&mut socket, data.as_ref()).await { - error!("error sending audio to stts: {}", e); - let _ = err_tx.send_async(e).await; - } - } - Err(_) => { - let _ = socket.write_u8(0x03).await; - break; - }, - } - } - val = final_rx.recv_async() => { - match val { - Ok(FinalizeVariant::Normal(r)) => { - if verbose { - panic!("when verbose, use get_result_verbose()"); - } - // this might fail to send, but at this point the stream is already dead so no cleanup is needed - let _ = r.send_async(Self::get_result_wrapper(&mut socket).await).await; - } - Ok(FinalizeVariant::Verbose(r)) => { - if !verbose { - panic!("when not verbose, use get_result()"); - } - // this also might fail to send, but at this point the stream is already dead so no cleanup is needed - let _ =r.send_async(Self::get_result_verbose_wrapper(&mut socket).await).await; - } - Err(_) => { - let _ = socket.write_u8(0x03).await; - } - } - break; + let session_id = Uuid::new_v4(); + debug!(%session_id, %peer_address, "initializing stts stream to peer"); + + tx.send(ClientToServerMessage::InitializeStreaming( + InitializeStreaming { id: session_id }, + ))?; + + // wait for server to acknowledge initialization + let stream_fut = async { + while let Ok(next) = rx.recv().await { + if let ServerToClientMessage::InitializationComplete(InitializationComplete { + id, + }) = next + { + if id == session_id { + return true; } } } - }); - Ok(Self { - comm: comm_tx, - final_comm: final_tx, - err_comm: err_rx, - }) - } - - pub fn feed_audio(&self, data: Vec) -> Result<(), ModelError> { - debug!("feeding audio to stts"); - if self.comm.send(data).is_err() { - Err(self - .err_comm - .recv_timeout(Duration::from_micros(10)) - .expect("error was not sent in time after erroring")) - } else { - debug!("audio sent to stts"); - Ok(()) - } - } - - async fn feed_audio_wrapper(socket: &mut TcpStream, audio: &[i16]) -> Result<(), ModelError> { - // 0x01: Feed Audio - socket.write_u8(0x01).await?; - - let bytes = std::mem::size_of_val(audio); - - // field 0: data_len: u32 - socket.write_u32(bytes as u32).await?; - - // field 1: data: Vec - let mut dst = vec![0; bytes]; - NetworkEndian::write_i16_into(audio, &mut dst); - socket.write_all(&dst).await?; - - // flush the socket, waiting at most 1 millisecond - match tokio::time::timeout(Duration::from_millis(1), socket.flush()).await { - Ok(Err(e)) => return Err(e.into()), - Err(_) => warn!("failed to flush socket before timeout"), - _ => {} + false }; - Ok(()) - } - - pub async fn get_result(self) -> Result { - let (tx, rx) = flume::bounded(0); - self.final_comm - .send_async(FinalizeVariant::Normal(tx)) - .await - .expect("failed to send to a channel that should still be open?"); - rx.recv_async() - .await - .expect("failed to receive from a open channel?") - } - - async fn get_result_wrapper(socket: &mut TcpStream) -> Result { - debug!("got result request"); - - // 0x02: Get Result - socket.write_u8(0x02).await?; - socket.flush().await?; - - // wait for response - debug!("waiting for response"); - let resp = match socket.read_u8().await { - Ok(0x02) => { - // read transcript - Ok(Transcript { - result: read_string(socket).await?, + match tokio::time::timeout(Duration::from_secs(5), stream_fut).await { + Ok(true) => { + debug!(%session_id, %peer_address, "stts stream initialized"); + Ok(Self { + tx, + rx, + peer_address, + session_id, }) } - Ok(0x04) => { - // read error code - Err(ModelError::SttsServer(socket.read_i64().await?)) + Ok(false) => { + warn!(%session_id, %peer_address, "remote stream died"); + Err(ModelError::RemoteDisconnected) } - Err(e) => Err(e.into()), - _ => Err(ModelError::SttsServer(2147483653)), - }; - debug!("got response"); - resp + Err(_) => { + warn!(%session_id, %peer_address, "timed out waiting for server to acknowledge initialization"); + Err(ModelError::InitializationTimedOut) + } + } } - pub async fn get_result_verbose(self) -> Result { - let (tx, rx) = flume::bounded(0); - self.final_comm - .send_async(FinalizeVariant::Verbose(tx)) - .await - .expect("failed to send to a channel that should still be open?"); - rx.recv_async() - .await - .expect("failed to receive from an open channel?") + pub fn feed_audio(&self, data: Vec) -> Result<(), ModelError> { + debug!(%self.session_id, "feeding audio to stts"); + self.tx + .send(ClientToServerMessage::AudioData(AudioData { + data, + id: self.session_id, + })) + .map_or(Err(ModelError::RemoteDisconnected), |_| Ok(())) } - async fn get_result_verbose_wrapper( - socket: &mut TcpStream, - ) -> Result { - debug!("got verbose result request"); - - // 0x02: Get Result - socket.write_u8(0x02).await?; - socket.flush().await?; - - // wait for response - debug!("waiting for response"); - let resp = match socket.read_u8().await { - Ok(0x03) => { - // read verbose transcript - let num_transcripts = socket.read_u32().await?; - let mut main_transcript = None; - let mut main_confidence = None; - if num_transcripts != 0 { - main_transcript = Some(read_string(socket).await?); - main_confidence = Some(socket.read_f64().await?); + pub async fn get_result( + mut self, + language: String, + verbose: bool, + translate: bool, + ) -> Result { + debug!(%self.session_id, "getting result from stts"); + // send the finalize message + self.tx + .send(ClientToServerMessage::FinalizeStreaming( + FinalizeStreaming { + verbose, + language, + translate, + id: self.session_id, + }, + )) + .map_err(|_| ModelError::RemoteDisconnected)?; + let stream_fut = async { + while let Ok(next) = self.rx.recv().await { + if let ServerToClientMessage::SttResult(SttSuccess { id, result }) = next { + if id == self.session_id { + debug!(%self.session_id, "got result from stts"); + return Ok(result); + } + } else if let ServerToClientMessage::SttError(SttError { id, error }) = next { + if id == self.session_id { + debug!(%self.session_id, "got error from stts"); + return Err(ModelError::SttsServer(error)); + } } - - Ok(VerboseTranscript { - num_transcripts, - main_transcript, - main_confidence, - }) - } - Ok(0x04) => { - // read error code - Err(ModelError::SttsServer(socket.read_i64().await?)) } - Err(e) => Err(e.into()), - _ => Err(ModelError::SttsServer(2147483653)), + Err(ModelError::RemoteDisconnected) }; - debug!("got response"); - resp + match tokio::time::timeout(Duration::from_secs(30), stream_fut).await { + Ok(Ok(res)) => Ok(res), + Ok(Err(e)) => Err(e), + Err(_) => { + warn!(%self.session_id, "timed out waiting for result"); + Err(ModelError::TimedOutWaitingForResult) + } + } } } #[derive(Debug)] pub enum ModelError { Io(io::Error), - SttsServer(i64), + MessagePackDecode(rmp_serde::decode::Error), + MessagePackEncode(rmp_serde::encode::Error), + SttsServer(String), NoAvailableServers, + InvalidMagicBytes([u8; 4]), + /// The server sent a payload that was not valid for the current state + PayloadOutOfOrder, + OverloadedRemote, + InitializationTimedOut, + TimedOutWaitingForResult, + RemoteDisconnected, + InvalidPayload { + expected: Vec, + got: Vec, + }, } impl std::error::Error for ModelError {} @@ -239,9 +157,21 @@ impl From for ModelError { } } -impl From for ModelError { - fn from(err: i64) -> Self { - ModelError::SttsServer(err) +impl From for ModelError { + fn from(err: rmp_serde::decode::Error) -> Self { + ModelError::MessagePackDecode(err) + } +} + +impl From for ModelError { + fn from(err: rmp_serde::encode::Error) -> Self { + ModelError::MessagePackEncode(err) + } +} + +impl From> for ModelError { + fn from(_: tokio::sync::broadcast::error::SendError) -> Self { + ModelError::RemoteDisconnected } } @@ -257,6 +187,33 @@ impl std::fmt::Display for ModelError { NUM_STT_SERVICE_TRIES ) } + ModelError::MessagePackDecode(e) => { + write!(f, "failed to decode MsgPack: {}", e) + } + ModelError::MessagePackEncode(e) => { + write!(f, "failed to encode MsgPack: {}", e) + } + ModelError::InvalidMagicBytes(bytes) => { + write!(f, "invalid magic bytes: {:?}", bytes) + } + ModelError::PayloadOutOfOrder => { + write!(f, "payload received out of order") + } + ModelError::InvalidPayload { got, expected } => { + write!(f, "invalid payload: expected {:?}, got {:?}", expected, got) + } + ModelError::OverloadedRemote => { + write!(f, "remote is overloaded") + } + ModelError::InitializationTimedOut => { + write!(f, "timed out waiting for initialization") + } + ModelError::RemoteDisconnected => { + write!(f, "remote disconnected") + } + ModelError::TimedOutWaitingForResult => { + write!(f, "timed out waiting for result") + } } } } @@ -270,11 +227,3 @@ pub struct VerboseTranscript { pub main_transcript: Option, pub main_confidence: Option, } - -async fn read_string(stream: &mut TcpStream) -> io::Result { - // strings are encoded as a u64 length followed by the string bytes - let len = stream.read_u64().await?; - let mut buf = vec![0u8; len as usize]; - stream.read_exact(&mut buf).await?; - Ok(String::from_utf8_lossy(&buf).to_string()) -} diff --git a/scripty_utils/src/latency/ws.rs b/scripty_utils/src/latency/ws.rs index 1f2fdeb1..053ac62c 100644 --- a/scripty_utils/src/latency/ws.rs +++ b/scripty_utils/src/latency/ws.rs @@ -4,7 +4,7 @@ use crate::{ShardManagerWrapper, TypeMapKey}; pub async fn get_ws_latency( shard_manager: &::Value, - shard_id: u32, + shard_id: u16, ) -> Option { let runners = shard_manager.runners.lock().await; runners diff --git a/scripty_webserver/src/endpoints/bot_stats/advanced.rs b/scripty_webserver/src/endpoints/bot_stats/advanced.rs index a1167454..106b4931 100644 --- a/scripty_webserver/src/endpoints/bot_stats/advanced.rs +++ b/scripty_webserver/src/endpoints/bot_stats/advanced.rs @@ -24,10 +24,10 @@ pub struct AdvancedBotStats { pub user_count: usize, pub channel_count: usize, pub voice_channel_count: usize, - pub shard_count: u32, + pub shard_count: u16, /// Per shard information. /// The key is the shard ID. - pub shard_info: HashMap, + pub shard_info: HashMap, } #[derive(Serialize, Deserialize, Debug)] diff --git a/scripty_webserver/src/endpoints/bot_stats/mod.rs b/scripty_webserver/src/endpoints/bot_stats/mod.rs index 0ae53f71..539369a6 100644 --- a/scripty_webserver/src/endpoints/bot_stats/mod.rs +++ b/scripty_webserver/src/endpoints/bot_stats/mod.rs @@ -19,7 +19,7 @@ pub struct BotStats { pub guild_count: usize, pub user_count: usize, pub channel_count: usize, - pub shard_count: u32, + pub shard_count: u16, } pub async fn get_bot_stats() -> Result, WebServerError> {