diff --git a/Cargo.lock b/Cargo.lock index 479390c..f97e5c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,15 @@ version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + [[package]] name = "async-trait" version = "0.1.83" @@ -107,22 +116,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "async-tungstenite" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb" -dependencies = [ - "futures-io", - "futures-util", - "log", - "pin-project-lite", - "tokio", - "tokio-rustls 0.23.4", - "tungstenite", - "webpki-roots 0.22.6", -] - [[package]] name = "autocfg" version = "1.4.0" @@ -156,6 +149,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "1.3.2" @@ -395,6 +394,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "deranged" version = "0.3.11" @@ -643,7 +648,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap 2.6.0", "slab", "tokio", @@ -686,6 +691,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -693,7 +709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] @@ -720,7 +736,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", "httpdate", @@ -740,7 +756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", + "http 0.2.12", "hyper", "rustls 0.21.12", "tokio", @@ -1155,15 +1171,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - [[package]] name = "overload" version = "0.1.1" @@ -1345,7 +1352,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-rustls", @@ -1380,21 +1387,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.8" @@ -1405,8 +1397,8 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin 0.9.8", - "untrusted 0.9.0", + "spin", + "untrusted", "windows-sys 0.52.0", ] @@ -1431,26 +1423,28 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.9" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.16.20", + "ring", + "rustls-webpki 0.101.7", "sct", - "webpki", ] [[package]] name = "rustls" -version = "0.21.12" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.8", - "rustls-webpki", - "sct", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", ] [[package]] @@ -1462,21 +1456,32 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] -name = "rustversion" -version = "1.0.18" +name = "rustls-webpki" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] [[package]] name = "ryu" @@ -1499,8 +1504,18 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", ] [[package]] @@ -1536,12 +1551,11 @@ dependencies = [ ] [[package]] -name = "serde-value" -version = "0.7.0" +name = "serde_cow" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +checksum = "1e7bbbec7196bfde255ab54b65e34087c0849629280028238e67ee25d6a4b7da" dependencies = [ - "ordered-float", "serde", ] @@ -1594,38 +1608,37 @@ dependencies = [ [[package]] name = "serenity" -version = "0.11.7" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a7a89cef23483fc9d4caf2df41e6d3928e18aada84c56abd237439d929622c6" +checksum = "880a04106592d0a8f5bdacb1d935889bfbccb4a14f7074984d9cd857235d34ac" dependencies = [ + "arrayvec", "async-trait", - "async-tungstenite", - "base64 0.21.7", - "bitflags 1.3.2", + "base64 0.22.1", + "bitflags 2.6.0", "bytes", - "cfg-if", "flate2", "futures", - "mime", "mime_guess", "percent-encoding", "reqwest", - "rustversion", + "secrecy", "serde", - "serde-value", + "serde_cow", "serde_json", "time", "tokio", + "tokio-tungstenite", "tracing", "typemap_rev", "url", ] [[package]] -name = "sha-1" -version = "0.10.1" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -1678,12 +1691,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -1708,6 +1715,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1891,25 +1904,41 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.20.9", + "rustls 0.21.12", "tokio", - "webpki", ] [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.21.12", + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "rustls 0.22.4", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tungstenite", + "webpki-roots 0.26.6", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -1995,30 +2024,30 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ - "base64 0.13.1", "byteorder", "bytes", - "http", + "data-encoding", + "http 1.1.0", "httparse", "log", "rand", - "rustls 0.20.9", - "sha-1", + "rustls 0.22.4", + "rustls-pki-types", + "sha1", "thiserror", "url", "utf-8", - "webpki", ] [[package]] name = "typemap_rev" -version = "0.1.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5b74f0a24b5454580a79abb6994393b09adf0ab8070f15827cb666255de155" +checksum = "74b08b0c1257381af16a5c3605254d529d3e7e109f3c62befc5d168968192998" [[package]] name = "typenum" @@ -2038,12 +2067,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -2210,30 +2233,20 @@ dependencies = [ ] [[package]] -name = "webpki" -version = "0.22.4" +name = "webpki-roots" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ - "webpki", + "rustls-pki-types", ] -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - [[package]] name = "winapi" version = "0.3.9" @@ -2510,6 +2523,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerovec" version = "0.10.4" diff --git a/Cargo.toml b/Cargo.toml index e2e04c8..c4933d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ serde = "1.0.131" serde_derive = "1.0.131" serde_json = "1.0.108" serde_yaml = "0.8.21" -serenity = { version = "0.11.6", default-features = false, features = ["client", "collector", "gateway", "rustls_backend", "model", "unstable_discord_api"] } +serenity = { version = "0.12.2", default-features = false, features = ["client", "collector", "gateway", "rustls_backend", "model", "unstable_discord_api"] } thiserror = "1.0.30" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } tracing = "0.1.30" diff --git a/src/bot/commands/archive.rs b/src/bot/commands/archive.rs index 0110188..309322d 100644 --- a/src/bot/commands/archive.rs +++ b/src/bot/commands/archive.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use serenity::builder::CreateApplicationCommand; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; +use serenity::all::CreateCommand; +use serenity::all::EditInteractionResponse; use serenity::model::prelude::*; use crate::bot::helpers::HelperError; @@ -18,25 +18,21 @@ enum ArchiveCommandError { type ArchiveCommandResult = std::result::Result; impl Bot { - pub fn create_archive_command( - command: &mut CreateApplicationCommand, - ) -> &mut CreateApplicationCommand { - command - .name("archive") - .description("運営への質問スレッドを終了します") + pub fn create_archive_command() -> CreateCommand { + CreateCommand::new("archive").description("運営への質問スレッドを終了します") } - pub async fn handle_archive_command( - &self, - interaction: &ApplicationCommandInteraction, - ) -> Result<()> { + pub async fn handle_archive_command(&self, interaction: &CommandInteraction) -> Result<()> { tracing::debug!("send acknowledgement"); self.defer_response(interaction).await?; if let Err(err) = self.do_archive_command(interaction).await { tracing::error!(?err, "failed to do archive command"); - self.edit_response(interaction, |data| data.content(err.to_string())) - .await?; + self.edit_response( + interaction, + EditInteractionResponse::new().content(err.to_string()), + ) + .await?; } Ok(()) } @@ -44,7 +40,7 @@ impl Bot { #[tracing::instrument(skip_all)] async fn do_archive_command( &self, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, ) -> ArchiveCommandResult<()> { let channel_id = interaction.channel_id; let channel = self.get_channel(channel_id).await?; @@ -60,9 +56,10 @@ impl Bot { self.archive_thread(&mut guild_channel).await?; - self.edit_response(interaction, |response| { - response.content("質問スレッドを終了しました。") - }) + self.edit_response( + interaction, + EditInteractionResponse::new().content("質問スレッドを終了しました。"), + ) .await?; Ok(()) diff --git a/src/bot/commands/ask.rs b/src/bot/commands/ask.rs index e412f68..a8da897 100644 --- a/src/bot/commands/ask.rs +++ b/src/bot/commands/ask.rs @@ -1,7 +1,9 @@ use anyhow::Result; -use serenity::builder::CreateApplicationCommand; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; -use serenity::model::prelude::command::*; +use serenity::all::CreateCommand; +use serenity::all::CreateCommandOption; +use serenity::all::CreateInteractionResponseMessage; +use serenity::all::CreateMessage; +use serenity::all::EditInteractionResponse; use serenity::model::prelude::*; use crate::bot::helpers::HelperError; @@ -26,32 +28,30 @@ enum AskCommandError { type AskCommandResult = std::result::Result; impl Bot { - pub fn create_ask_command( - command: &mut CreateApplicationCommand, - ) -> &mut CreateApplicationCommand { - command - .name("ask") + pub fn create_ask_command() -> CreateCommand { + CreateCommand::new("ask") .description("運営への質問スレッドを開始します") - .create_option(|option| { - option - .name("title") - .description("質問タイトル(50文字以内)") - .kind(CommandOptionType::String) - .required(true) - }) + .add_option( + CreateCommandOption::new( + CommandOptionType::String, + "title", + "質問タイトル(50文字以内)", + ) + .required(true), + ) } #[tracing::instrument(skip_all)] - pub async fn handle_ask_command( - &self, - interaction: &ApplicationCommandInteraction, - ) -> Result<()> { + pub async fn handle_ask_command(&self, interaction: &CommandInteraction) -> Result<()> { let (guild_channel, title) = match self.validate_ask_command(interaction).await { Ok(v) => v, Err(err) => { - self.respond(interaction, |data| { - data.ephemeral(true).content(err.to_string()) - }) + self.respond( + interaction, + CreateInteractionResponseMessage::new() + .ephemeral(true) + .content(err.to_string()), + ) .await?; return Ok(()); }, @@ -65,8 +65,11 @@ impl Bot { .await { tracing::error!(?err, "failed to do ask command"); - self.edit_response(interaction, |data| data.content(err.to_string())) - .await?; + self.edit_response( + interaction, + EditInteractionResponse::new().content(err.to_string()), + ) + .await?; } Ok(()) @@ -74,7 +77,7 @@ impl Bot { async fn validate_ask_command<'t>( &self, - interaction: &'t ApplicationCommandInteraction, + interaction: &'t CommandInteraction, ) -> AskCommandResult<(GuildChannel, &'t str)> { let channel = self.get_channel(interaction.channel_id).await?; @@ -103,7 +106,7 @@ impl Bot { async fn do_ask_command( &self, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, guild_channel: &GuildChannel, title: &str, ) -> AskCommandResult<()> { @@ -117,9 +120,11 @@ impl Bot { .map(|role| Mention::from(role.id).to_string()) .collect(); - self.edit_response(interaction, |data| { - data.content(format!("{} 質問内容を入力してください。", sender_mention)) - }) + self.edit_response( + interaction, + EditInteractionResponse::new() + .content(format!("{} 質問スレッドを開始します。", sender_mention)), + ) .await?; let message = self.get_response(interaction).await?; @@ -130,9 +135,13 @@ impl Bot { // TODO: 直接メッセージを送信するな!!! channel - .send_message(&self.discord_client, |data| { - data.content(format!("{}", staff_mentions.join(" "))) - }) + .send_message( + &self.discord_client, + CreateMessage::new().content(format!( + "{} 質問スレッドを開始します。", + staff_mentions.join(" ") + )), + ) .await?; Ok(()) diff --git a/src/bot/commands/join.rs b/src/bot/commands/join.rs index 4bfe924..6ff9930 100644 --- a/src/bot/commands/join.rs +++ b/src/bot/commands/join.rs @@ -1,9 +1,12 @@ use std::collections::HashSet; use anyhow::Result; -use serenity::builder::CreateApplicationCommand; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; -use serenity::model::prelude::command::*; +use serenity::all::CommandInteraction; +use serenity::all::CommandOptionType; +use serenity::all::CreateCommand; +use serenity::all::CreateCommandOption; +use serenity::all::CreateInteractionResponseMessage; +use serenity::all::EditInteractionResponse; use crate::bot::helpers::HelperError; use crate::bot::roles; @@ -25,32 +28,30 @@ enum JoinCommandError<'a> { type JoinCommandResult<'t, T> = std::result::Result>; impl Bot { - pub fn create_join_command( - command: &mut CreateApplicationCommand, - ) -> &mut CreateApplicationCommand { - command - .name("join") + pub fn create_join_command() -> CreateCommand { + CreateCommand::new("join") .description("チームに参加します。") - .create_option(|option| { - option - .name("invitation_code") - .description("招待コード") - .kind(CommandOptionType::String) - .required(true) - }) + .add_option( + CreateCommandOption::new( + CommandOptionType::String, + "invitation_code", + "招待コード", + ) + .required(true), + ) } #[tracing::instrument(skip_all)] - pub async fn handle_join_command( - &self, - interaction: &ApplicationCommandInteraction, - ) -> Result<()> { + pub async fn handle_join_command(&self, interaction: &CommandInteraction) -> Result<()> { let role_name = match self.validate_join_command(interaction) { Ok(role_name) => role_name, Err(err) => { - self.respond(interaction, |data| { - data.ephemeral(true).content(err.to_string()) - }) + self.respond( + interaction, + CreateInteractionResponseMessage::new() + .ephemeral(true) + .content(err.to_string()), + ) .await?; return Ok(()); }, @@ -61,8 +62,11 @@ impl Bot { if let Err(err) = self.do_join_command(interaction, role_name).await { tracing::error!(?err, "failed to do join command"); - self.edit_response(interaction, |data| data.content(err.to_string())) - .await?; + self.edit_response( + interaction, + EditInteractionResponse::new().content(err.to_string()), + ) + .await?; } Ok(()) @@ -70,7 +74,7 @@ impl Bot { fn validate_join_command<'t>( &self, - interaction: &'t ApplicationCommandInteraction, + interaction: &'t CommandInteraction, ) -> JoinCommandResult<'t, &str> { // joinコマンドはGlobalCommandなので、どこからでも呼び出すことは可能である。 // だが、間違ってrandomチャンネル等で呼び出されてしまうことを防ぐため、DM以外からの呼び出しはエラーとする。 @@ -106,7 +110,7 @@ impl Bot { async fn do_join_command( &self, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, role_name: &str, ) -> JoinCommandResult<()> { // DMの送信元が、ICTSC Discordチャンネルに参加しているかをチェックする。 @@ -141,9 +145,11 @@ impl Bot { self.revoke_roles(&mut sender_member, role_ids_revoked) .await?; - self.edit_response(interaction, |data| { - data.content(format!("チーム `{}` に参加しました。", role_name)) - }) + self.edit_response( + interaction, + EditInteractionResponse::new() + .content(format!("チーム `{}` に参加しました。", role_name)), + ) .await?; Ok(()) diff --git a/src/bot/commands/mod.rs b/src/bot/commands/mod.rs index 79c0f11..162809f 100644 --- a/src/bot/commands/mod.rs +++ b/src/bot/commands/mod.rs @@ -6,20 +6,16 @@ mod redeploy; use anyhow::Result; use serenity::client::Context; -use serenity::model::application::command::Command; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; use crate::bot::*; impl Bot { pub async fn sync_global_application_commands(&self) -> Result<()> { tracing::debug!("Syncing ping command"); - Command::create_global_application_command(&self.discord_client, Bot::create_ping_command) - .await?; + Command::create_global_command(&self.discord_client, Bot::create_ping_command()).await?; tracing::debug!("Syncing join command"); - Command::create_global_application_command(&self.discord_client, Bot::create_join_command) - .await?; + Command::create_global_command(&self.discord_client, Bot::create_join_command()).await?; Ok(()) } @@ -27,17 +23,17 @@ impl Bot { pub async fn sync_guild_application_commands(&self) -> Result<()> { tracing::debug!("sync archive command"); self.guild_id - .create_application_command(&self.discord_client, Bot::create_archive_command) + .create_command(&self.discord_client, Bot::create_archive_command()) .await?; tracing::debug!("sync ask command"); self.guild_id - .create_application_command(&self.discord_client, Bot::create_ask_command) + .create_command(&self.discord_client, Bot::create_ask_command()) .await?; tracing::debug!("sync redeploy command"); self.guild_id - .create_application_command(&self.discord_client, Bot::create_redeploy_command) + .create_command(&self.discord_client, Bot::create_redeploy_command()) .await?; Ok(()) @@ -46,28 +42,25 @@ impl Bot { #[tracing::instrument(skip_all)] pub async fn delete_commands(&self) -> Result<()> { tracing::info!("delete global application commands"); - let commands = self - .discord_client - .get_global_application_commands() - .await?; + let commands = self.discord_client.get_global_commands().await?; for command in commands { tracing::debug!(?command, "delete global application command"); self.discord_client - .delete_global_application_command(command.id.0) + .delete_global_command(command.id) .await?; } tracing::info!("delete guild application commands"); let commands = self .discord_client - .get_guild_application_commands(self.guild_id.0) + .get_guild_commands(self.guild_id) .await?; for command in commands { tracing::debug!(?command, "delete guild application command"); self.discord_client - .delete_guild_application_command(self.guild_id.0, command.id.0) + .delete_guild_command(self.guild_id, command.id) .await?; } @@ -86,7 +79,7 @@ impl Bot { pub async fn handle_application_command( &self, ctx: &Context, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, ) { let name = interaction.data.name.as_str(); diff --git a/src/bot/commands/ping.rs b/src/bot/commands/ping.rs index 1ce26d1..603aee5 100644 --- a/src/bot/commands/ping.rs +++ b/src/bot/commands/ping.rs @@ -1,24 +1,23 @@ use anyhow::Result; -use serenity::builder::CreateApplicationCommand; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; +use serenity::all::CommandInteraction; +use serenity::all::CreateInteractionResponseMessage; +use serenity::builder::CreateCommand; use crate::bot::Bot; impl Bot { - pub fn create_ping_command( - command: &mut CreateApplicationCommand, - ) -> &mut CreateApplicationCommand { - command.name("ping").description("botの生存確認をします。") + pub fn create_ping_command() -> CreateCommand { + CreateCommand::new("ping").description("botの生存確認をします。") } #[tracing::instrument(skip_all)] - pub async fn handle_ping_command( - &self, - interaction: &ApplicationCommandInteraction, - ) -> Result<()> { + pub async fn handle_ping_command(&self, interaction: &CommandInteraction) -> Result<()> { tracing::trace!("send acknowledgement"); - self.respond(interaction, |data| data.content("pong!")) - .await?; + self.respond( + interaction, + CreateInteractionResponseMessage::new().content("pong!"), + ) + .await?; Ok(()) } } diff --git a/src/bot/commands/redeploy.rs b/src/bot/commands/redeploy.rs index a7e298d..ec89813 100644 --- a/src/bot/commands/redeploy.rs +++ b/src/bot/commands/redeploy.rs @@ -1,13 +1,19 @@ use std::time::Duration; use anyhow::Result; -use serenity::builder::CreateApplicationCommand; -use serenity::builder::CreateComponents; -use serenity::model::application::interaction::application_command::CommandDataOption; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; -use serenity::model::prelude::command::CommandOptionType; -use serenity::model::prelude::component::ButtonStyle; -use serenity::model::prelude::component::ComponentType; +use serenity::all::ButtonStyle; +use serenity::all::CommandDataOption; +use serenity::all::CommandDataOptionValue; +use serenity::all::CommandInteraction; +use serenity::all::CommandOptionType; +use serenity::all::ComponentInteractionDataKind; +use serenity::all::CreateActionRow; +use serenity::all::CreateButton; +use serenity::all::CreateCommand; +use serenity::all::CreateCommandOption; +use serenity::all::CreateEmbed; +use serenity::all::CreateInteractionResponseMessage; +use serenity::all::EditInteractionResponse; use serenity::model::user::User; use serenity::prelude::*; @@ -46,62 +52,58 @@ enum RedeployCommandError<'a> { type RedeployCommandResult<'t, T> = std::result::Result>; -fn create_buttons(c: &mut CreateComponents, disabled: bool) -> &mut CreateComponents { - c.create_action_row(|r| { - r.create_button(|b| { - b.style(ButtonStyle::Primary) - .label("OK") - .custom_id(CUSTOM_ID_REDEPLOY_CONFIRM) - .disabled(disabled) - }) - .create_button(|b| { - b.style(ButtonStyle::Secondary) - .label("キャンセル") - .custom_id(CUSTOM_ID_REDEPLOY_CANCELED) - .disabled(disabled) - }) - }) +fn create_buttons(disabled: bool) -> Vec { + let ok = CreateButton::new(CUSTOM_ID_REDEPLOY_CONFIRM) + .label("OK") + .style(ButtonStyle::Primary) + .disabled(disabled); + + let cancel = CreateButton::new(CUSTOM_ID_REDEPLOY_CANCELED) + .label("キャンセル") + .style(ButtonStyle::Secondary) + .disabled(disabled); + + vec![CreateActionRow::Buttons(vec![ok, cancel])] } impl Bot { - pub fn create_redeploy_command( - command: &mut CreateApplicationCommand, - ) -> &mut CreateApplicationCommand { - command - .name("redeploy") + pub fn create_redeploy_command() -> CreateCommand { + CreateCommand::new("redeploy") .description("問題環境の再展開に関するコマンド") - .create_option(|option| { - option - .name("start") - .description("問題環境を再展開します。") - .kind(CommandOptionType::SubCommand) - .create_sub_option(|option| { - option - .name("problem_code") - .description("問題コード") - .kind(CommandOptionType::String) - .required(true) - }) - }) - .create_option(|option| { - option - .name("status") - .description("現在の再展開状況を表示します。") - .kind(CommandOptionType::SubCommand) - }) + .add_option( + CreateCommandOption::new( + CommandOptionType::SubCommand, + "start", + "問題環境を再展開します。", + ) + .add_sub_option( + CreateCommandOption::new( + CommandOptionType::String, + "problem_code", + "問題コード", + ) + .required(true), + ), + ) + .add_option(CreateCommandOption::new( + CommandOptionType::SubCommand, + "status", + "現在の再展開状況を表示します。", + )) } #[tracing::instrument(skip_all)] pub async fn handle_redeploy_command( &self, ctx: &Context, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, ) -> Result<()> { if let Err(err) = self._handle_redeploy_command(ctx, interaction).await { tracing::error!(?err, "failed to handle redeploy command"); - self.edit_response(interaction, |data| { - data.content(err.to_string()).components(|c| c) - }) + self.edit_response( + interaction, + EditInteractionResponse::new().content(err.to_string()), + ) .await?; } @@ -111,7 +113,7 @@ impl Bot { async fn _handle_redeploy_command( &self, ctx: &Context, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, ) -> RedeployCommandResult<()> { let subcommand = interaction .data @@ -119,18 +121,19 @@ impl Bot { .first() .ok_or(RedeployCommandError::InconsistentCommandDefinitionError)?; - if subcommand.kind != CommandOptionType::SubCommand { - return Err(RedeployCommandError::InconsistentCommandDefinitionError); - } - - Ok(match subcommand.name.as_str() { - "start" => { - self.handle_redeploy_start_subcommand(ctx, interaction, subcommand) - .await? + match &subcommand.value { + CommandDataOptionValue::SubCommand(options) => match subcommand.name.as_str() { + "start" => { + self.handle_redeploy_start_subcommand(ctx, interaction, options) + .await? + }, + "status" => self.handle_redeploy_status_subcommand(interaction).await?, + _ => return Err(RedeployCommandError::InconsistentCommandDefinitionError), }, - "status" => self.handle_redeploy_status_subcommand(interaction).await?, _ => return Err(RedeployCommandError::InconsistentCommandDefinitionError), - }) + } + + Ok(()) } async fn get_team_for(&self, user: &User) -> RedeployCommandResult { @@ -159,15 +162,18 @@ impl Bot { async fn handle_redeploy_start_subcommand( &self, ctx: &Context, - interaction: &ApplicationCommandInteraction, - option: &CommandDataOption, + interaction: &CommandInteraction, + options: &[CommandDataOption], ) -> RedeployCommandResult<()> { - let problem = match self.validate_redeploy_start_subcommand(option) { + let problem = match self.validate_redeploy_start_subcommand(options) { Ok(problem) => problem, Err(err) => { - self.respond(interaction, |data| { - data.ephemeral(true).content(err.to_string()) - }) + self.respond( + interaction, + CreateInteractionResponseMessage::new() + .ephemeral(true) + .content(err.to_string()), + ) .await?; return Ok(()); }, @@ -188,11 +194,9 @@ impl Bot { fn validate_redeploy_start_subcommand<'t>( &self, - option: &'t CommandDataOption, + options: &'t [CommandDataOption], ) -> RedeployCommandResult<'t, &Problem> { - let problem_code = self - .get_option_as_str(&option.options, "problem_code") - .unwrap(); + let problem_code = self.get_option_as_str(options, "problem_code").unwrap(); // スコアサーバーとの互換性のため、ここで大文字に正規化する let normalized_problem_code = problem_code.to_uppercase(); @@ -208,7 +212,7 @@ impl Bot { async fn do_redeploy_start_subcommand( &self, ctx: &Context, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, problem: &Problem, ) -> RedeployCommandResult<()> { let sender = &interaction.user; @@ -228,14 +232,15 @@ impl Bot { )); } - self.edit_response(interaction, |data| { - // TODO: チーム名にする - data.content(format!( - "チーム `{}` の問題 `{}` を再展開しますか?", - sender_team.role_name, problem.name - )) - .components(|c| create_buttons(c, false)) - }) + self.edit_response( + interaction, + EditInteractionResponse::new() + .content(format!( + "チーム `{}` の問題 `{}` を再展開しますか?", + sender_team.role_name, problem.name + )) + .components(create_buttons(false)), + ) .await?; let message = self.get_response(interaction).await?; @@ -243,37 +248,45 @@ impl Bot { let component_interaction = message .await_component_interaction(ctx) .author_id(sender.id) - .filter(|component_interaction| { - component_interaction.data.component_type == ComponentType::Button - && (component_interaction.data.custom_id == CUSTOM_ID_REDEPLOY_CONFIRM - || component_interaction.data.custom_id == CUSTOM_ID_REDEPLOY_CANCELED) - }) + .filter( + |component_interaction| match &component_interaction.data.kind { + ComponentInteractionDataKind::Button => { + component_interaction.data.custom_id == CUSTOM_ID_REDEPLOY_CONFIRM + || component_interaction.data.custom_id == CUSTOM_ID_REDEPLOY_CANCELED + }, + _ => false, + }, + ) .timeout(Duration::from_secs(60)) .await; - self.edit_response(interaction, |response| { - response.components(|c| create_buttons(c, true)) - }) - .await?; - - let (component_interaction, should_recreate) = match component_interaction { - Some(component_interaction) => { - let should_recreate = - component_interaction.data.custom_id == CUSTOM_ID_REDEPLOY_CONFIRM; - (component_interaction, should_recreate) - }, + let component_interaction = match component_interaction { + Some(component_interaction) => component_interaction, None => { + self.edit_response( + interaction, + EditInteractionResponse::new() + .content("タイムアウトしました。再度、再作成リクエストを投稿してください。") + .components(create_buttons(true)), + ) + .await?; return Ok(()); }, }; - let component_interaction = component_interaction.as_ref(); - self.defer_response(component_interaction).await?; + self.edit_response( + interaction, + EditInteractionResponse::new().components(create_buttons(true)), + ) + .await?; + self.defer_response(&component_interaction).await?; + let should_recreate = component_interaction.data.custom_id == CUSTOM_ID_REDEPLOY_CONFIRM; if !should_recreate { - self.edit_response(component_interaction, |response| { - response.content("再展開を中止しました。") - }) + self.edit_response( + &component_interaction, + EditInteractionResponse::new().content("再展開を中止しました。"), + ) .await?; return Ok(()); } @@ -286,27 +299,29 @@ impl Bot { match &result { Ok(_) => { - self.edit_response(component_interaction, |response| { - response.content("再展開を開始しました。") - }) + self.edit_response( + &component_interaction, + EditInteractionResponse::new().content("再展開を開始しました。"), + ) .await?; }, Err(err) => match err { RedeployError::AnotherJobInQueue(_) => { - self.edit_response(component_interaction, |response| { - response.content( - "この問題は既に再展開リクエストが投げられています。再展開が完了してから再度お試しください。", - ) - }) + self.edit_response( + &component_interaction, + EditInteractionResponse::new() .content("この問題は既に再展開リクエストが投げられています。再展開が完了してから再度お試しください。") + ) .await?; }, _ => { tracing::error!(?err, "failed to redeploy"); - self.edit_response(component_interaction, |response| { - response - .content("再展開中にエラーが発生しました。運営にお問い合わせください。") - }) + self.edit_response( + &component_interaction, + EditInteractionResponse::new().content( + "再展開中にエラーが発生しました。運営にお問い合わせください。", + ), + ) .await?; }, }, @@ -324,7 +339,7 @@ impl Bot { #[tracing::instrument(skip_all)] async fn handle_redeploy_status_subcommand( &self, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, ) -> RedeployCommandResult<()> { self.defer_response(interaction).await?; @@ -338,7 +353,7 @@ impl Bot { async fn do_redeploy_status_subcommand( &self, - interaction: &ApplicationCommandInteraction, + interaction: &CommandInteraction, ) -> RedeployCommandResult<()> { let sender = &interaction.user; let sender_team = self.get_team_for(sender).await?; @@ -354,54 +369,51 @@ impl Bot { .all(|status| status.last_redeploy_started_at.is_none()); if no_deploys { - self.edit_response(interaction, |data| { - data.content("まだ再展開は実行されていません。") - }) + self.edit_response( + interaction, + EditInteractionResponse::new().content("まだ再展開は実行されていません。"), + ) .await?; + return Ok(()); } - self.edit_response(interaction, |data| { - data.embed(|e| { - e.title("再展開状況"); - for status in &statuses { - let started_at = match status.last_redeploy_started_at { - Some(started_at) => started_at, - None => continue, - }; - - let name = status.problem_code.clone(); - let problem_name = self - .problems - .iter() - .find(|problem| problem.code == status.problem_code) - .map(|problem| format!("{}: {}", name, problem.name)) - .unwrap_or_else(|| name); - - let value = match status.last_redeploy_completed_at { - Some(completed_at) => { - let completed_at_local = - completed_at.with_timezone(&chrono_tz::Asia::Tokyo); - format!( - "🎉 再展開完了(完了時刻:{})", - completed_at_local.format("%Y/%m/%d %H:%M:%S") - ) - }, - None => { - let started_at_local = - started_at.with_timezone(&chrono_tz::Asia::Tokyo); - format!( - "⚙️ 再展開中(開始時刻:{})", - started_at_local.format("%Y/%m/%d %H:%M:%S") - ) - }, - }; - - e.field(problem_name, value, false); - } - e - }) - }) - .await?; + let mut embed = CreateEmbed::new().title("再展開状況"); + for status in &statuses { + let started_at = match status.last_redeploy_started_at { + Some(started_at) => started_at, + None => continue, + }; + + let name = status.problem_code.clone(); + let problem_name = self + .problems + .iter() + .find(|problem| problem.code == status.problem_code) + .map(|problem| format!("{}: {}", name, problem.name)) + .unwrap_or_else(|| name); + + let value = match status.last_redeploy_completed_at { + Some(completed_at) => { + let completed_at_local = completed_at.with_timezone(&chrono_tz::Asia::Tokyo); + format!( + "🎉 再展開完了(完了時刻:{})", + completed_at_local.format("%Y/%m/%d %H:%M:%S") + ) + }, + None => { + let started_at_local = started_at.with_timezone(&chrono_tz::Asia::Tokyo); + format!( + "⚙️ 再展開中(開始時刻:{})", + started_at_local.format("%Y/%m/%d %H:%M:%S") + ) + }, + }; + + embed = embed.field(problem_name, value, false); + } + + self.edit_response(interaction, EditInteractionResponse::new().add_embed(embed)) + .await?; Ok(()) } diff --git a/src/bot/helpers/channels.rs b/src/bot/helpers/channels.rs index a62ec54..e8352bc 100644 --- a/src/bot/helpers/channels.rs +++ b/src/bot/helpers/channels.rs @@ -1,3 +1,7 @@ +use serenity::all::CreateChannel; +use serenity::all::CreateThread; +use serenity::all::EditChannel; +use serenity::all::EditThread; use serenity::model::prelude::*; use super::HelperResult; @@ -25,31 +29,31 @@ impl Bot { ) -> HelperResult { tracing::trace!("Create channel"); let definition = definition.clone(); + + let mut create_channel = CreateChannel::new(definition.name) + .kind(definition.kind) + .permissions(definition.permissions); + + create_channel = match definition.topic { + Some(topic) => create_channel.topic(topic), + None => create_channel, + }; + + create_channel = match definition.category { + Some(category_id) => create_channel.category(category_id), + None => create_channel, + }; + Ok(self .guild_id - .create_channel(&self.discord_client, |channel| { - channel - .name(definition.name) - .kind(definition.kind) - .permissions(definition.permissions); - - match definition.topic { - Some(topic) => channel.topic(topic), - None => channel, - }; - - match definition.category { - Some(category_id) => channel.category(category_id), - None => channel, - } - }) + .create_channel(&self.discord_client, create_channel) .await?) } #[tracing::instrument(skip_all)] pub async fn get_channel(&self, id: ChannelId) -> HelperResult { tracing::trace!("Get channel"); - Ok(self.discord_client.get_channel(id.0).await?) + Ok(self.discord_client.get_channel(id).await?) } #[tracing::instrument(skip_all)] @@ -80,33 +84,33 @@ impl Bot { if channel.kind != definition.kind { return Err(HelperError::InvalidChannelKindError); } - Ok(channel - .edit(&self.discord_client, |edit| { - edit.name(&definition.name) - .category(definition.category) - .permissions(definition.permissions.clone()); - - match &definition.topic { - Some(topic) => edit.topic(topic), - None => edit, - } - }) - .await?) + + let mut edit_channel = EditChannel::new() + .name(&definition.name) + .category(definition.category) + .permissions(definition.permissions.clone()); + + edit_channel = match &definition.topic { + Some(topic) => edit_channel.topic(topic), + None => edit_channel, + }; + + Ok(channel.edit(&self.discord_client, edit_channel).await?) } #[tracing::instrument(skip_all, fields(channel = ?channel))] - pub async fn archive_thread(&self, channel: &mut GuildChannel) -> HelperResult { + pub async fn archive_thread(&self, channel: &mut GuildChannel) -> HelperResult<()> { tracing::trace!("Edit channel"); if channel.kind != ChannelType::PublicThread && channel.kind != ChannelType::PrivateThread { return Err(HelperError::InvalidChannelKindError); } Ok(channel - .edit_thread(&self.discord_client, |thread| thread.archived(true)) + .edit_thread(&self.discord_client, EditThread::new().archived(true)) .await?) } #[tracing::instrument(skip_all, fields(channel = ?channel))] - pub async fn delete_channel(&self, channel: &mut GuildChannel) -> HelperResult { + pub async fn delete_channel(&self, channel: &mut GuildChannel) -> HelperResult { tracing::trace!("Delete channel"); Ok(channel.delete(&self.discord_client).await?) } @@ -120,7 +124,7 @@ impl Bot { ) -> HelperResult { tracing::trace!("Create public thread"); Ok(channel - .create_public_thread(&self.discord_client, message, |thread| thread.name(title)) + .create_thread_from_message(&self.discord_client, message, CreateThread::new(title)) .await?) } } diff --git a/src/bot/helpers/interactions.rs b/src/bot/helpers/interactions.rs index bf9eac0..1999446 100644 --- a/src/bot/helpers/interactions.rs +++ b/src/bot/helpers/interactions.rs @@ -1,27 +1,28 @@ -use serenity::builder::CreateInteractionResponseData; +use serenity::all::CommandDataOption; +use serenity::all::CommandInteraction; +use serenity::all::ComponentInteraction; +use serenity::all::CreateInteractionResponse; +use serenity::all::CreateInteractionResponseMessage; +use serenity::all::Message; use serenity::builder::EditInteractionResponse; -use serenity::model::application::interaction::application_command::CommandDataOption; -use serenity::model::prelude::application_command::ApplicationCommandInteraction; -use serenity::model::prelude::message_component::MessageComponentInteraction; -use serenity::model::prelude::*; use super::HelperResult; use crate::bot::Bot; pub enum Interaction<'a> { - ApplicationCommandInteraction(&'a ApplicationCommandInteraction), - MessageComponentInteraction(&'a MessageComponentInteraction), + CommandInteraction(&'a CommandInteraction), + ComponentInteraction(&'a ComponentInteraction), } -impl<'a> From<&'a ApplicationCommandInteraction> for Interaction<'a> { - fn from(interaction: &'a ApplicationCommandInteraction) -> Self { - Interaction::ApplicationCommandInteraction(interaction) +impl<'a> From<&'a CommandInteraction> for Interaction<'a> { + fn from(interaction: &'a CommandInteraction) -> Self { + Interaction::CommandInteraction(interaction) } } -impl<'a> From<&'a MessageComponentInteraction> for Interaction<'a> { - fn from(interaction: &'a MessageComponentInteraction) -> Self { - Interaction::MessageComponentInteraction(interaction) +impl<'a> From<&'a ComponentInteraction> for Interaction<'a> { + fn from(interaction: &'a ComponentInteraction) -> Self { + Interaction::ComponentInteraction(interaction) } } @@ -29,29 +30,30 @@ impl<'a> From<&'a MessageComponentInteraction> for Interaction<'a> { impl Bot { // ユーザからのinteractionに即時応答するメソッド #[tracing::instrument(skip_all)] - pub async fn respond<'a, I, F>(&self, interaction: I, f: F) -> HelperResult<()> + pub async fn respond<'a, I>( + &self, + interaction: I, + message: CreateInteractionResponseMessage, + ) -> HelperResult<()> where I: Into>, - for<'b, 'c> F: FnOnce( - &'b mut CreateInteractionResponseData<'c>, - ) -> &'b mut CreateInteractionResponseData<'c>, { tracing::trace!("Respond"); Ok(match interaction.into() { - Interaction::ApplicationCommandInteraction(interaction) => { + Interaction::CommandInteraction(interaction) => { interaction - .create_interaction_response(&self.discord_client, |response| { - response.kind(InteractionResponseType::ChannelMessageWithSource); - response.interaction_response_data(f) - }) + .create_response( + &self.discord_client, + CreateInteractionResponse::Message(message), + ) .await? }, - Interaction::MessageComponentInteraction(interaction) => { + Interaction::ComponentInteraction(interaction) => { interaction - .create_interaction_response(&self.discord_client, |response| { - response.kind(InteractionResponseType::UpdateMessage); - response.interaction_response_data(f) - }) + .create_response( + &self.discord_client, + CreateInteractionResponse::Message(message), + ) .await? }, }) @@ -65,18 +67,20 @@ impl Bot { { tracing::trace!("Defer response"); Ok(match interaction.into() { - Interaction::ApplicationCommandInteraction(interaction) => { + Interaction::CommandInteraction(interaction) => { interaction - .create_interaction_response(&self.discord_client, |f| { - f.kind(InteractionResponseType::DeferredChannelMessageWithSource) - }) + .create_response( + &self.discord_client, + CreateInteractionResponse::Defer(CreateInteractionResponseMessage::new()), + ) .await? }, - Interaction::MessageComponentInteraction(interaction) => { + Interaction::ComponentInteraction(interaction) => { interaction - .create_interaction_response(&self.discord_client, |f| { - f.kind(InteractionResponseType::DeferredChannelMessageWithSource) - }) + .create_response( + &self.discord_client, + CreateInteractionResponse::Defer(CreateInteractionResponseMessage::new()), + ) .await? }, }) @@ -84,21 +88,24 @@ impl Bot { // ユーザからのinteractionの応答を編集するメソッド #[tracing::instrument(skip_all)] - pub async fn edit_response<'a, I, F>(&self, interaction: I, f: F) -> HelperResult + pub async fn edit_response<'a, I>( + &self, + interaction: I, + message: EditInteractionResponse, + ) -> HelperResult where I: Into>, - F: FnOnce(&mut EditInteractionResponse) -> &mut EditInteractionResponse, { tracing::trace!("Edit response"); Ok(match interaction.into() { - Interaction::ApplicationCommandInteraction(interaction) => { + Interaction::CommandInteraction(interaction) => { interaction - .edit_original_interaction_response(&self.discord_client, f) + .edit_response(&self.discord_client, message) .await? }, - Interaction::MessageComponentInteraction(interaction) => { + Interaction::ComponentInteraction(interaction) => { interaction - .edit_original_interaction_response(&self.discord_client, f) + .edit_response(&self.discord_client, message) .await? }, }) @@ -112,15 +119,11 @@ impl Bot { { tracing::trace!("Get response"); Ok(match interaction.into() { - Interaction::ApplicationCommandInteraction(interaction) => { - interaction - .get_interaction_response(&self.discord_client) - .await? + Interaction::CommandInteraction(interaction) => { + interaction.get_response(&self.discord_client).await? }, - Interaction::MessageComponentInteraction(interaction) => { - interaction - .get_interaction_response(&self.discord_client) - .await? + Interaction::ComponentInteraction(interaction) => { + interaction.get_response(&self.discord_client).await? }, }) } @@ -132,7 +135,7 @@ impl Bot { ) -> Option<&'t str> { for option in options { if option.name == name { - return option.value.as_ref().and_then(|v| v.as_str()); + return option.value.as_str(); } } None diff --git a/src/bot/helpers/member.rs b/src/bot/helpers/member.rs index 092c3a5..80580ad 100644 --- a/src/bot/helpers/member.rs +++ b/src/bot/helpers/member.rs @@ -12,11 +12,7 @@ impl Bot { } #[tracing::instrument(skip_all)] - pub async fn grant_roles( - &self, - member: &mut Member, - role_ids: T, - ) -> HelperResult> + pub async fn grant_roles(&self, member: &mut Member, role_ids: T) -> HelperResult<()> where T: AsRef<[RoleId]>, { @@ -28,11 +24,7 @@ impl Bot { } #[tracing::instrument(skip_all)] - pub async fn revoke_roles( - &self, - member: &mut Member, - role_ids: T, - ) -> HelperResult> + pub async fn revoke_roles(&self, member: &mut Member, role_ids: T) -> HelperResult<()> where T: AsRef<[RoleId]>, { diff --git a/src/bot/helpers/roles.rs b/src/bot/helpers/roles.rs index cd27251..f00b796 100644 --- a/src/bot/helpers/roles.rs +++ b/src/bot/helpers/roles.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use serenity::all::EditRole; use serenity::model::prelude::Role; use serenity::model::prelude::RoleId; use serenity::model::Permissions; @@ -30,13 +31,15 @@ impl Bot { let definition = definition.clone(); Ok(self .guild_id - .create_role(&self.discord_client, |edit| { - edit.name(definition.name) + .create_role( + &self.discord_client, + EditRole::new() + .name(definition.name) .permissions(definition.permissions) - .colour(definition.colour as u64) + .colour(definition.colour) .hoist(definition.hoist) - .mentionable(definition.mentionable) - }) + .mentionable(definition.mentionable), + ) .await?) } @@ -60,13 +63,16 @@ impl Bot { let definition = definition.clone(); Ok(self .guild_id - .edit_role(&self.discord_client, role.id.0, |edit| { - edit.name(definition.name) + .edit_role( + &self.discord_client, + role.id, + EditRole::new() + .name(definition.name) .permissions(definition.permissions) - .colour(definition.colour as u64) + .colour(definition.colour) .hoist(definition.hoist) - .mentionable(definition.mentionable) - }) + .mentionable(definition.mentionable), + ) .await?) } @@ -75,7 +81,7 @@ impl Bot { tracing::trace!("Delete role called"); Ok(self .guild_id - .delete_role(&self.discord_client, role.id.0) + .delete_role(&self.discord_client, role.id) .await?) } } diff --git a/src/bot/mod.rs b/src/bot/mod.rs index 6deb270..a4201f7 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -46,9 +46,10 @@ impl Bot { redeploy_notifiers: Vec>, configure_channel_topics: bool, ) -> Self { - let application_id = ApplicationId(application_id); - let guild_id = GuildId(guild_id); - let discord_client = Http::new_with_application_id(&token, application_id.0); + let application_id = ApplicationId::new(application_id); + let guild_id = GuildId::new(guild_id); + let discord_client = Http::new(&token); + discord_client.set_application_id(application_id); Bot { token, application_id, @@ -74,7 +75,7 @@ impl Bot { | GatewayIntents::DIRECT_MESSAGES; let mut client = Client::builder(token, intents) - .application_id(application_id.0) + .application_id(application_id) .event_handler(self) .await?; @@ -89,7 +90,7 @@ impl EventHandler for Bot { name = ?guild.name, owner_id = ?guild.owner_id, ))] - async fn guild_create(&self, _: Context, guild: Guild) { + async fn guild_create(&self, _: Context, guild: Guild, _: Option) { if guild.id != self.guild_id { tracing::info!("Target guild is not for contest, skipping"); return; @@ -122,7 +123,7 @@ impl EventHandler for Bot { #[tracing::instrument(skip_all)] async fn interaction_create(&self, ctx: Context, interaction: Interaction) { match interaction { - Interaction::ApplicationCommand(interaction) => { + Interaction::Command(interaction) => { self.handle_application_command(&ctx, &interaction).await }, _ => {}, diff --git a/src/bot/permissions.rs b/src/bot/permissions.rs index d3c3803..140ece6 100644 --- a/src/bot/permissions.rs +++ b/src/bot/permissions.rs @@ -46,7 +46,7 @@ impl Bot { | Permissions::MUTE_MEMBERS | Permissions::DEAFEN_MEMBERS | Permissions::USE_VAD - | Permissions::USE_SLASH_COMMANDS + | Permissions::USE_APPLICATION_COMMANDS | Permissions::SEND_MESSAGES_IN_THREADS } diff --git a/src/services/redeploy.rs b/src/services/redeploy.rs index b23feda..7e41af4 100644 --- a/src/services/redeploy.rs +++ b/src/services/redeploy.rs @@ -7,10 +7,11 @@ use reqwest::ClientBuilder; use reqwest::StatusCode; use serde::Deserialize; use serde::Serialize; +use serenity::all::Colour; +use serenity::all::CreateEmbed; +use serenity::all::ExecuteWebhook; use serenity::http::Http; -use serenity::model::prelude::Embed; use serenity::model::webhook::Webhook; -use serenity::utils::Colour; use crate::models::Problem; @@ -280,26 +281,26 @@ impl DiscordRedeployNotifier { target: &RedeployTarget, result: &RedeployResult, ) -> Result<()> { - let embed = match result { - Ok(job) => Embed::fake(|e| { - e.title("再展開開始通知") - .colour(Colour::from_rgb(40, 167, 65)) + let notification = match result { + Ok(job) => ExecuteWebhook::new().embed( + CreateEmbed::new() + .color(Colour::from_rgb(40, 167, 65)) .field("チームID", &target.team_id, true) .field("問題コード", &target.problem_id, true) - .field("再展開Job ID", &job.id, true) - }), - Err(err) => Embed::fake(|e| { - e.title("再展開失敗通知") - .colour(Colour::from_rgb(236, 76, 82)) + .field("再展開Job ID", &job.id, true), + ), + Err(err) => ExecuteWebhook::new().embed( + CreateEmbed::new() + .color(Colour::from_rgb(236, 76, 82)) .field("チームID", &target.team_id, true) .field("問題コード", &target.problem_id, true) - .field("エラー", err, true) - }), + .field("エラー", err.to_string(), true), + ), }; let result = self .webhook - .execute(&self.discord_client, false, |w| w.embeds(vec![embed])) + .execute(&self.discord_client, false, notification) .await?; if let Some(message) = result {