diff --git a/Cargo.lock b/Cargo.lock index 4af4d4b..ad61a83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -595,6 +595,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -3418,6 +3419,7 @@ dependencies = [ name = "zmp-bot" version = "0.1.0" dependencies = [ + "futures", "poise", "reqwest 0.12.9", "serde", diff --git a/Cargo.toml b/Cargo.toml index f6bd318..034ea7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,5 @@ serde = { version = "1.0.216", features = ["derive"] } reqwest = { version = "0.12.9", features = ["json"] } tokio = { version = "1.42.0", features = ["rt-multi-thread"] } tracing = { version = "0.1.41" } -sqlx = { version = "0.8.2", features = ["sqlite", "sqlx-sqlite", "runtime-tokio"]} \ No newline at end of file +sqlx = { version = "0.8.2", features = ["sqlite", "sqlx-sqlite", "runtime-tokio"]} +futures = "0.3.31" diff --git a/src/commands/accountv2.rs b/src/commands/accountv2.rs index 2ff3115..53d99d5 100644 --- a/src/commands/accountv2.rs +++ b/src/commands/accountv2.rs @@ -71,13 +71,11 @@ impl Uuid { let matches = deserialized .player .social_media - .map(|sm| sm.links) - .flatten() - .map(|l| l.discord) - .flatten() - .ok_or(Other(format!( - "The Hypixel profile has no Discord account linked. Please follow the steps in <#1256219552568840263>", - )))? + .and_then(|sm| sm.links) + .and_then(|l| l.discord) + .ok_or(Other( + "The Hypixel profile has no Discord account linked. Please follow the steps in <#1256219552568840263>".to_string(), + ))? == user.name; Ok(matches) } @@ -124,20 +122,20 @@ impl Link { Ok(self) } } -#[poise::command(slash_command, subcommands("add", "list"))] +#[poise::command( + slash_command, + subcommands("add", "list"), + install_context = "User|Guild", + interaction_context = "Guild|BotDm|PrivateChannel", +)] pub(crate) async fn account(_ctx: Context<'_>) -> Result<(), Error> { // root of slash-commands is not invokable. unreachable!() } -#[poise::command( - slash_command, - install_context = "User|Guild", - interaction_context = "Guild|BotDm|PrivateChannel", - ephemeral = "false" -)] +#[poise::command(slash_command, ephemeral = "false")] /// Verify a Minecraft account on the Zombies MultiPlayer Discord. -pub(crate) async fn add<'a>( +pub(crate) async fn add( ctx: Context<'_>, #[description = "Minecraft username"] #[min_length = 2] @@ -238,13 +236,7 @@ pub(crate) async fn add<'a>( } } -#[poise::command( - slash_command, - install_context = "User|Guild", - interaction_context = "Guild|BotDm|PrivateChannel", - ephemeral = "true", - context_menu_command = "Account list" -)] +#[poise::command(slash_command, ephemeral = "true", context_menu_command = "Account list")] /// List a users linked minecraft Accounts. pub(crate) async fn list(ctx: Context<'_>, user: User) -> Result<(), Error> { ctx.defer().await?; diff --git a/src/commands/bots.rs b/src/commands/bots.rs index 51ec5e5..0b294a6 100644 --- a/src/commands/bots.rs +++ b/src/commands/bots.rs @@ -2,15 +2,15 @@ use std::string::String; use poise::CreateReply; -use crate::Context; use crate::error::Error; +use crate::Context; #[poise::command( slash_command, owners_only, install_context = "User", interaction_context = "Guild|BotDm|PrivateChannel", - ephemeral = "false", + ephemeral = "false" )] /// Change how many helpstart bots are online, to limit usage of helpstart pings. pub(crate) async fn bots( diff --git a/src/commands/command_helper.rs b/src/commands/command_helper.rs index adf37ce..856c18b 100644 --- a/src/commands/command_helper.rs +++ b/src/commands/command_helper.rs @@ -1,7 +1,7 @@ use std::time::Duration; -use crate::Context; use crate::error::Error; +use crate::Context; pub(crate) fn cooldown(ctx: &Context, user: u64, guild: u64) -> Result<(), Error> { let mut cooldown_tracker = ctx.command().cooldowns.lock().unwrap(); diff --git a/src/commands/helpstart.rs b/src/commands/helpstart.rs index bb22d20..09364a1 100644 --- a/src/commands/helpstart.rs +++ b/src/commands/helpstart.rs @@ -2,15 +2,10 @@ use poise::CreateReply; use serenity::all::CreateAllowedMentions; use crate::commands::command_helper; -use crate::Context; use crate::error::Error; +use crate::Context; -#[poise::command( - slash_command, - install_context = "Guild", - interaction_context = "Guild", - ephemeral = "false", -)] +#[poise::command(slash_command, install_context = "Guild", interaction_context = "Guild", ephemeral = "false")] /// Ping the @helpstart to fill a queue. pub(crate) async fn helpstart( ctx: Context<'_>, diff --git a/src/commands/lfg.rs b/src/commands/lfg.rs index 0fbdcb1..f5ae501 100644 --- a/src/commands/lfg.rs +++ b/src/commands/lfg.rs @@ -7,8 +7,8 @@ use crate::commands::command_helper::cooldown; use crate::commands::lfg::Difficulty::Normal; use crate::commands::lfg::Map::*; use crate::commands::lfg::Mode::*; -use crate::Context; use crate::error::Error; +use crate::Context; #[derive(Debug, poise::ChoiceParameter, PartialEq)] pub enum Map { @@ -43,12 +43,7 @@ pub enum Difficulty { #[name = "R.I.P."] Rip, } -#[poise::command( - slash_command, - install_context = "Guild", - interaction_context = "Guild", - ephemeral = "false", -)] +#[poise::command(slash_command, install_context = "Guild", interaction_context = "Guild", ephemeral = "false")] /// Find a team for Hypixel Zombies. pub(crate) async fn lfg( ctx: Context<'_>, @@ -102,7 +97,7 @@ pub(crate) async fn lfg( AlienArcadium => Normal, }; - let mut reply_content: String = format!("## <@&{ping}> {current}/{desired} {map_name}", ); + let mut reply_content: String = format!("## <@&{ping}> {current}/{desired} {map_name}",); match difficulty { Normal => {} Difficulty::Hard | Difficulty::Rip => { diff --git a/src/commands/xd.rs b/src/commands/xd.rs index f33de18..df353ab 100644 --- a/src/commands/xd.rs +++ b/src/commands/xd.rs @@ -1,21 +1,21 @@ -use crate::Context; use crate::error::Error; +use crate::Context; -const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n\ - ⣿⣿⣿⣧⣄⡀⠀⠀⠀⢀⣠⣼⣿⣿⣿⣿⣧⣄⡀⠀⠀⠀⣀⣤⣼⣷⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣦⣄⡀⠀⠀⠙⢿⣿⣿⣿⣿\n\ - ⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⠋⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⡿⠁⠀⠀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⢸⣿⣿⣿\n\ - ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡗⠀⠀⠀⠐⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⠀⠀⣿⣿⣿\n\ - ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⣠⡀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⠀⣰⣿⣷⡄⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⣾⣿⣿⣿\n\ - ⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⢀⣼⣿⣿⣿⣿⣆⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⢀⣼⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⠋⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⠿⠛⠁⠀⠀⣠⣾⣿⣿⣿⣿⣿\n\ - ⣿⣿⣿⠿⠛⠁⠀⠀⠀⠙⠻⣿⣿⣿⣿⣿⡿⠟⠋⠀⠀⠀⠈⠛⠻⡿⠟⠛⠁⠀⠀⠈⠉⠉⠉⠉⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣶⣶⣶⣶⣶⣶⣶⣶⣿⣿⣿⣿⣿⣷⣶⣶⣶⣶⣶⣶⣶⣶⣷⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n\ - ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n"; +const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\\ + n⣿⣿⣿⣧⣄⡀⠀⠀⠀⢀⣠⣼⣿⣿⣿⣿⣧⣄⡀⠀⠀⠀⣀⣤⣼⣷⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠻⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣼⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣶⣦⣄⡀⠀⠀⠙⢿⣿⣿⣿⣿\\ + n⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⠋⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠈⢿⡿⠁⠀⠀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⢸⣿⣿⣿\\ + n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡗⠀⠀⠀⠐⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⠀⠀⣿⣿⣿\\ + n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⣠⡀⠀⠀⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⠀⣰⣿⣷⡄⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⣾⣿⣿⣿\\ + n⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⢀⣼⣿⣿⣿⣿⣆⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⢀⣼⣿⣿⣿⣿\n⣿⣿⣿⣿⣿⣿⠋⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠙⣿⣿⣿⣿⣿⣿⡇⠀⠀⢸⣿⣿⣿⣿⣿⣿⠿⠛⠁⠀⠀⣠⣾⣿⣿⣿⣿⣿\\ + n⣿⣿⣿⠿⠛⠁⠀⠀⠀⠙⠻⣿⣿⣿⣿⣿⡿⠟⠋⠀⠀⠀⠈⠛⠻⡿⠟⠛⠁⠀⠀⠈⠉⠉⠉⠉⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⣶⣶⣶⣶⣶⣶⣶⣶⣿⣿⣿⣿⣿⣷⣶⣶⣶⣶⣶⣶⣶⣶⣷⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\\ + n⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n"; #[poise::command( slash_command, owners_only, install_context = "User|Guild", interaction_context = "Guild|BotDm|PrivateChannel", - ephemeral = "false", + ephemeral = "false" )] /// Useless command to check if the bot is online. pub(crate) async fn xd(ctx: Context<'_>) -> Result<(), Error> { diff --git a/src/error.rs b/src/error.rs index 65553d6..807420b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ -use std::fmt::{Display, Formatter, Result as FmtResult}; use poise::{CreateReply, FrameworkError}; +use std::fmt::{Display, Formatter, Result as FmtResult}; use crate::Data; @@ -54,7 +54,7 @@ pub(crate) async fn handle_error<'a>(error: FrameworkError<'a, Data, Error>) { match error { FrameworkError::Command { error, ctx, .. } => { reply_fail_handler!(ctx.send(CreateReply::default().content(error.to_string()).ephemeral(true))) - }, + } FrameworkError::CommandStructureMismatch { description, ctx, .. } => { reply_fail_handler!(ctx.send( CreateReply::default() diff --git a/src/handlers/bot_interaction.rs b/src/handlers/bot_interaction.rs index e46c48e..cf12e6a 100644 --- a/src/handlers/bot_interaction.rs +++ b/src/handlers/bot_interaction.rs @@ -1,5 +1,4 @@ use serenity::all::ButtonStyle::Success; -use serenity::all::ComponentInteractionDataKind; use serenity::all::Context; use serenity::all::CreateActionRow; use serenity::all::CreateButton; @@ -12,6 +11,7 @@ use serenity::all::Interaction; use serenity::all::ReactionType; use serenity::all::RoleId; use serenity::all::{ButtonStyle, ComponentInteraction}; +use serenity::all::{ComponentInteractionDataKind, CreateInteractionResponse}; use crate::error::Error; use crate::Data; @@ -25,23 +25,20 @@ pub(crate) async fn component(ctx: &Context, interaction: &Interaction, data: &D } async fn button(ctx: &Context, mut interaction: ComponentInteraction, data: &Data) -> Result<(), Error> { - let m = &interaction.message; - let u = m.mentions.first().expect("Message did not mention a user."); + let u = interaction.message.mentions.first().expect("Message did not mention a user.").id; match interaction.data.custom_id.as_str() { "accept_verification" => { - let member = m + let member = interaction + .message .guild_id .unwrap_or(GuildId::new(1256217633959841853_u64)) - .member(ctx, u.id) + .member(ctx, u) .await?; - member.add_role(ctx, RoleId::new(1256218805911425066_u64)).await?; - member.remove_role(ctx, RoleId::new(1256253358701023232_u64)).await?; - let _dm = u - .direct_message(ctx, CreateMessage::new().content("Your verified minecraft account was approved.")) - .await?; - interaction - .message - .edit( + let (_, _, _dm, _) = futures::try_join!( + member.add_role(ctx, RoleId::new(1256218805911425066_u64)), + member.remove_role(ctx, RoleId::new(1256253358701023232_u64)), + u.direct_message(ctx, CreateMessage::new().content("Your verified minecraft account was approved.")), + interaction.message.edit( ctx, EditMessage::new().components(vec![CreateActionRow::Buttons(vec![ CreateButton::new("accept_verification") @@ -57,16 +54,14 @@ async fn button(ctx: &Context, mut interaction: ComponentInteraction, data: &Dat .style(ButtonStyle::Primary), ])]), ) - .await?; + )?; + interaction.create_response(ctx, CreateInteractionResponse::Acknowledge).await?; Ok(()) } "deny_verification" => { - let _dm = u - .direct_message(ctx, CreateMessage::new().content("Your verified minecraft account was denied.")) - .await?; - interaction - .message - .edit( + let (_dm, _) = futures::try_join!( + u.direct_message(ctx, CreateMessage::new().content("Your verified minecraft account was denied.")), + interaction.message.edit( ctx, EditMessage::new().components(vec![CreateActionRow::Buttons(vec![ CreateButton::new("accept_verification") @@ -82,8 +77,8 @@ async fn button(ctx: &Context, mut interaction: ComponentInteraction, data: &Dat .style(ButtonStyle::Primary), ])]), ) - .await?; - + )?; + interaction.create_response(ctx, CreateInteractionResponse::Acknowledge).await?; Ok(()) } "list_accounts" => { diff --git a/src/main.rs b/src/main.rs index 6b65eb9..fc845a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,9 @@ use std::sync::Arc; use std::time::Duration; use poise::serenity_prelude as serenity; -use serenity::{FullEvent, model::id::UserId}; use serenity::all::{ActivityData, InteractionType, RoleId}; use serenity::prelude::GatewayIntents; +use serenity::{model::id::UserId, FullEvent}; use sqlx::Sqlite; use tokio::sync::RwLock; @@ -96,7 +96,7 @@ async fn event_handler( match event { FullEvent::Ready { data_about_bot, .. } => { println!("Logged in as '{}'!", data_about_bot.user.name); - }, + } FullEvent::GuildMemberAddition { new_member } => { if new_member.guild_id.get() == 1256217633959841853_u64 { new_member.add_role(ctx, RoleId::new(1256253358701023232_u64)).await?; @@ -106,13 +106,13 @@ async fn event_handler( if interaction.application_id().get() == 1165594074473037824 && interaction.kind() == InteractionType::Component { handlers::bot_interaction::component(ctx, interaction, data).await?; } - }, + } FullEvent::Message { new_message } => { handlers::message::on_create(ctx, new_message).await?; - }, + } FullEvent::ThreadCreate { thread } => { handlers::thread::on_create(ctx, thread).await?; - }, + } _ => {} } Ok(())