diff --git a/Cargo.lock b/Cargo.lock index 19a3531..344de05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,7 +203,6 @@ dependencies = [ "color-eyre", "dotenv", "mongodb", - "nougat", "poise", "serde", "serde_json", @@ -785,22 +784,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "macro_rules_attribute" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422a6e082b3d33461030d68a243af2482eefded078eaaf8ac401777d21cae32b" -dependencies = [ - "macro_rules_attribute-proc_macro", - "paste", -] - -[[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4091d2e7f515b3a7ae8dda39417cd2b81d5c8814f68361c3e15b24e2d40b4a" - [[package]] name = "match_cfg" version = "0.1.0" @@ -920,27 +903,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "nougat" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dafa3a476b709b3930ca280fe980fa583a9904735b76045ca328f2c7382ca10" -dependencies = [ - "macro_rules_attribute", - "nougat-proc_macros", -] - -[[package]] -name = "nougat-proc_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aad0b7693b89a294713bf27d8664b86a9fee198e44bd683533215e256dc3176" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -1042,12 +1004,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "paste" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" - [[package]] name = "pbkdf2" version = "0.10.1" diff --git a/src/handler.rs b/src/handler.rs index c72c753..fc4d5fa 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,4 +1,4 @@ -use std::fmt::{Debug, Formatter}; +use std::fmt::{Debug, Display, Formatter}; use color_eyre::eyre::{eyre, ContextCompat}; use mongodb::bson::Uuid; @@ -20,136 +20,17 @@ use crate::{ }; fn slash_commands(commands: &mut CreateApplicationCommands) -> &mut CreateApplicationCommands { - commands - .create_application_command(|command| { - command - .name("lawsuit") - .description("Einen Gerichtsprozess starten") - .create_option(|option| { - option - .name("create") - .description("Einen neuen Gerichtsprozess anfangen") - .kind(ApplicationCommandOptionType::SubCommand) - .create_sub_option(|option| { - option - .name("plaintiff") - .description("Der Kläger") - .kind(ApplicationCommandOptionType::User) - .required(true) - }) - .create_sub_option(|option| { - option - .name("accused") - .description("Der Angeklagte") - .kind(ApplicationCommandOptionType::User) - .required(true) - }) - .create_sub_option(|option| { - option - .name("judge") - .description("Der Richter") - .kind(ApplicationCommandOptionType::User) - .required(true) - }) - .create_sub_option(|option| { - option - .name("reason") - .description("Der Grund für die Klage") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - .create_sub_option(|option| { - option - .name("plaintiff_lawyer") - .description("Der Anwalt des Klägers") - .kind(ApplicationCommandOptionType::User) - .required(false) - }) - .create_sub_option(|option| { - option - .name("accused_lawyer") - .description("Der Anwalt des Angeklagten") - .kind(ApplicationCommandOptionType::User) - .required(false) - }) - }) - .create_option(|option| { - option - .name("set_category") - .description("Die Gerichtskategorie setzen") - .kind(ApplicationCommandOptionType::SubCommand) - .create_sub_option(|option| { - option - .name("category") - .description("Die Kategorie") - .kind(ApplicationCommandOptionType::Channel) - .required(true) - }) - }) - .create_option(|option| { - option - .name("close") - .description("Den Prozess abschliessen") - .kind(ApplicationCommandOptionType::SubCommand) - .create_sub_option(|option| { - option - .name("verdict") - .description("Das Urteil") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - }) - .create_option(|option| { - option - .name("clear") - .description("Alle Rechtsprozessdaten löschen") - .kind(ApplicationCommandOptionType::SubCommand) - }) - }) - .create_application_command(|command| { - command - .name("prison") - .description("Leute im Gefängnis einsperren") - .create_option(|option| { - option - .name("arrest") - .description("Jemanden einsperren") - .kind(ApplicationCommandOptionType::SubCommand) - .create_sub_option(|option| { - option - .name("user") - .description("Die Person zum einsperren") - .kind(ApplicationCommandOptionType::User) - .required(true) - }) - }) - .create_option(|option| { - option - .name("release") - .description("Jemanden freilassen") - .kind(ApplicationCommandOptionType::SubCommand) - .create_sub_option(|option| { - option - .name("user") - .description("Die Person zum freilassen") - .kind(ApplicationCommandOptionType::User) - .required(true) - }) - }) - .create_option(|option| { - option - .name("set_role") - .description("Die Rolle für Gefangene setzen") - .kind(ApplicationCommandOptionType::SubCommand) - .create_sub_option(|option| { - option - .name("role") - .description("Die Rolle") - .kind(ApplicationCommandOptionType::Role) - .required(true) - }) - }) - }) + commands.create_application_command(|command| { + command + .name("lawsuit") + .description("Einen Gerichtsprozess starten") + .create_option(|option| { + option + .name("clear") + .description("Alle Rechtsprozessdaten löschen") + .kind(ApplicationCommandOptionType::SubCommand) + }) + }) } pub struct Handler { @@ -170,6 +51,16 @@ pub enum Response { NoPermissions, } +impl Display for Response { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::EphemeralStr(str) => f.write_str(str), + Self::Ephemeral(str) => f.write_str(str), + Self::NoPermissions => f.write_str("du häsch kei recht für da!"), + } + } +} + #[async_trait] impl EventHandler for Handler { async fn guild_member_addition(&self, ctx: Context, new_member: Member) { @@ -201,42 +92,9 @@ impl EventHandler for Handler { } } } - - async fn interaction_create(&self, ctx: Context, interaction: Interaction) { - if let Interaction::ApplicationCommand(command) = interaction { - if let Err(err) = self.handle_interaction(ctx, command).await { - error!(?err, "An error occurred in interaction_create handler"); - } - } - } } + impl Handler { - async fn handle_interaction( - &self, - ctx: Context, - command: ApplicationCommandInteraction, - ) -> color_eyre::Result<()> { - debug!(name = %command.data.name, "Received command interaction"); - - let response = match command.data.name.as_str() { - "lawsuit" => lawsuit_command_handler(&command, &ctx, &self.mongo).await, - _ => Ok(Response::EphemeralStr("not implemented :(")), - }; - - match response { - Ok(response) => self.send_response(ctx, command, response).await, - Err(err) => { - error!(?err, "Error during command execution"); - self.send_response( - ctx, - command, - Response::EphemeralStr("An internal error occurred"), - ) - .await - } - } - } - async fn handle_guild_member_join( &self, ctx: Context, @@ -266,196 +124,78 @@ impl Handler { Ok(()) } - - async fn send_response( - &self, - ctx: Context, - command: ApplicationCommandInteraction, - response: Response, - ) -> color_eyre::Result<()> { - command - .create_interaction_response(&ctx.http, |res| match response { - Response::EphemeralStr(content) => res - .kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|message| { - message - .content(content) - .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) - }), - Response::Ephemeral(content) => res - .kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|message| { - message - .content(content) - .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) - }), - Response::NoPermissions => res - .kind(InteractionResponseType::ChannelMessageWithSource) - .interaction_response_data(|message| { - message - .content("du häsch kei recht für da!") - .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL) - }), - }) - .await - .wrap_err("sending response")?; - Ok(()) - } } -async fn lawsuit_command_handler( - command: &ApplicationCommandInteraction, - ctx: &Context, - mongo_client: &Mongo, -) -> color_eyre::Result { - let options = &command.data.options; - let subcommand = options.get(0).wrap_err("needs subcommand")?; +#[poise::command( + slash_command, + subcommands("prison_set_role", "prison_arrest", "prison_release") +)] +async fn lawsuit(_: crate::Context<'_>) -> color_eyre::Result<()> { + unreachable!() +} - let options = &subcommand.options; - let guild_id = command.guild_id.wrap_err("guild_id not found")?; +/// Einen neuen Gerichtsprozess erstellen +#[poise::command(slash_command, required_permissions = "MANAGE_GUILD")] +async fn lawsuit_create( + ctx: crate::Context<'_>, + #[description = "Der Kläger"] plaintiff: User, + #[description = "Der Angeklagte"] accused: User, + #[description = "Der Richter"] judge: User, + #[description = "Der Grund für die Klage"] reason: String, + #[description = "Der Anwalt des Klägers"] plaintiff_lawyer: Option, + #[description = "Der Anwalt des Angeklagten"] accused_lawyer: Option, +) -> color_eyre::Result<()> { + lawsuit_create_impl( + ctx, + plaintiff, + accused, + judge, + reason, + plaintiff_lawyer, + accused_lawyer, + ) + .await + .wrap_err("lawsuit_create") +} - let member = command - .member - .as_ref() - .wrap_err("command must be used my member")?; - let permissions = member.permissions.wrap_err("must be in interaction")?; +/// Die Rolle für Gefangene setzen +#[poise::command( + slash_command, + required_permissions = "MANAGE_GUILD", + on_error = "error_handler" +)] +async fn lawsuit_set_category( + ctx: crate::Context<'_>, + #[description = "Die Kategorie"] category: Channel, +) -> color_eyre::Result<()> { + lawsuit_set_category_impl(ctx, category) + .await + .wrap_err("lawsuit_set_category") +} - match subcommand.name.as_str() { - "create" => { - if !permissions.contains(Permissions::MANAGE_GUILD) { - return Ok(Response::NoPermissions); - } +/// Den Gerichtsprozess abschliessen und ein Urteil fällen +#[poise::command( + slash_command, + required_permissions = "MANAGE_GUILD", + on_error = "error_handler" +)] +async fn lawsuit_close( + ctx: crate::Context<'_>, + #[description = "Das Urteil"] verdict: String, +) -> color_eyre::Result<()> { + lawsuit_close_impl(ctx, verdict) + .await + .wrap_err("lawsuit_close") +} - let plaintiff = UserOption::get(options.get(0)).wrap_err("plaintiff")?; - let accused = UserOption::get(options.get(1)).wrap_err("accused")?; - let judge = UserOption::get(options.get(2)).wrap_err("judge")?; - let reason = StringOption::get(options.get(3)).wrap_err("reason")?; - let plaintiff_layer = - UserOption::get_optional(options.get(4)).wrap_err("plaintiff_layer")?; - let accused_layer = - UserOption::get_optional(options.get(5)).wrap_err("accused_layer")?; - - let lawsuit = Lawsuit { - id: Uuid::new(), - plaintiff: plaintiff.0.id.into(), - accused: accused.0.id.into(), - judge: judge.0.id.into(), - plaintiff_lawyer: plaintiff_layer.map(|user| user.0.id.into()), - accused_lawyer: accused_layer.map(|user| user.0.id.into()), - reason: reason.to_owned(), - verdict: None, - court_room: SnowflakeId(0), - }; - - let lawsuit_ctx = LawsuitCtx { - lawsuit, - mongo_client: mongo_client.clone(), - http: ctx.http.clone(), - guild_id, - }; - - let response = lawsuit_ctx - .initialize() - .await - .wrap_err("initialize lawsuit")?; - - Ok(response) - } - "set_category" => { - if !permissions.contains(Permissions::MANAGE_GUILD) { - return Ok(Response::NoPermissions); - } - - let channel = ChannelOption::get(options.get(0))?; - - let channel = channel - .id - .to_channel(&ctx.http) - .await - .wrap_err("fetch category for set_category")?; - match channel.category() { - Some(category) => { - let id = category.id; - mongo_client - .set_court_category(guild_id.into(), id.into()) - .await?; - } - None => return Ok(Response::EphemeralStr("Das ist keine Kategorie!")), - } - - Ok(Response::EphemeralStr("isch gsetzt")) - } - "close" => { - let permission_override = permissions.contains(Permissions::MANAGE_GUILD); - - let verdict = StringOption::get(options.get(0))?; - - let room_id = command.channel_id; - - let state = mongo_client - .find_or_insert_state(guild_id.into()) - .await - .wrap_err("find guild for verdict")?; - - let lawsuit = state - .lawsuits - .iter() - .find(|l| l.court_room == room_id.into() && l.verdict.is_none()); - - let lawsuit = match lawsuit { - Some(lawsuit) => lawsuit.clone(), - None => { - return Ok(Response::EphemeralStr( - "i dem channel lauft kein aktive prozess!", - )) - } - }; - - let room = state - .court_rooms - .iter() - .find(|r| r.channel_id == room_id.into()); - let room = match room { - Some(room) => room.clone(), - None => { - return Ok(Response::EphemeralStr( - "i dem channel lauft kein aktive prozess!", - )) - } - }; - - let mut lawsuit_ctx = LawsuitCtx { - lawsuit, - mongo_client: mongo_client.clone(), - http: ctx.http.clone(), - guild_id, - }; - - let response = lawsuit_ctx - .rule_verdict( - permission_override, - member.user.id, - verdict.to_string(), - room, - ) - .await?; - - if let Err(response) = response { - return Ok(response); - } - - Ok(Response::EphemeralStr("ich han en dir abschlosse")) - } - "clear" => { - if !permissions.contains(Permissions::MANAGE_GUILD) { - return Ok(Response::NoPermissions); - } - - mongo_client.delete_guild(guild_id.into()).await?; - Ok(Response::EphemeralStr("alles weg")) - } - _ => Err(eyre!("Unknown subcommand")), - } +/// Alle Rechtsprozessdaten löschen +#[poise::command( + slash_command, + required_permissions = "MANAGE_GUILD", + on_error = "error_handler" +)] +async fn lawsuit_clear(ctx: crate::Context<'_>) -> color_eyre::Result<()> { + lawsuit_clear_impl(ctx).await.wrap_err("lawsuit_clear") } #[poise::command( @@ -474,13 +214,179 @@ async fn prison(_: crate::Context<'_>) -> color_eyre::Result<()> { )] async fn prison_set_role( ctx: crate::Context<'_>, - #[description = "Die rolle"] role: Role, + #[description = "Die Rolle"] role: Role, ) -> color_eyre::Result<()> { prison_set_role_impl(ctx, role) .await .wrap_err("prison_set_role") } +/// Jemanden einsperren +#[poise::command(slash_command, required_permissions = "MANAGE_GUILD")] +async fn prison_arrest( + ctx: crate::Context<'_>, + #[description = "Die Person zum einsperren"] user: User, +) -> color_eyre::Result<()> { + prison_arrest_impl(ctx, user) + .await + .wrap_err("prison_arrest") +} + +/// Einen Gefangenen freilassen +#[poise::command(slash_command, required_permissions = "MANAGE_GUILD")] +async fn prison_release( + ctx: crate::Context<'_>, + #[description = "Die Person zum freilassen"] user: User, +) -> color_eyre::Result<()> { + prison_release_impl(ctx, user) + .await + .wrap_err("prison_release") +} + +async fn lawsuit_create_impl( + ctx: crate::Context<'_>, + plaintiff: User, + accused: User, + judge: User, + reason: String, + plaintiff_lawyer: Option, + accused_lawyer: Option, +) -> color_eyre::Result<()> { + let guild_id = ctx.guild_id().wrap_err("guild_id not found")?; + + let lawsuit = Lawsuit { + id: Uuid::new(), + plaintiff: plaintiff.id.into(), + accused: accused.id.into(), + judge: judge.id.into(), + plaintiff_lawyer: plaintiff_lawyer.map(|user| user.id.into()), + accused_lawyer: accused_lawyer.map(|user| user.id.into()), + reason: reason.to_owned(), + verdict: None, + court_room: SnowflakeId(0), + }; + + let lawsuit_ctx = LawsuitCtx { + lawsuit, + mongo_client: ctx.data().mongo.clone(), + http: ctx.discord().http.clone(), + guild_id, + }; + + let response = lawsuit_ctx + .initialize() + .await + .wrap_err("initialize lawsuit")?; + + ctx.say(response.to_string()).await?; + + Ok(()) +} + +async fn lawsuit_set_category_impl( + ctx: crate::Context<'_>, + category: Channel, +) -> color_eyre::Result<()> { + let guild_id = ctx.guild_id().wrap_err("guild_id not found")?; + + //let channel = channel + // .id + // .to_channel(&ctx.http) + // .await + // .wrap_err("fetch category for set_category")?; + match category.category() { + Some(category) => { + let id = category.id; + ctx.data() + .mongo + .set_court_category(guild_id.into(), id.into()) + .await?; + ctx.say("isch gsetzt").await?; + } + None => { + ctx.say("Das ist keine Kategorie!").await?; + } + } + + Ok(()) +} + +async fn lawsuit_close_impl(ctx: crate::Context<'_>, verdict: String) -> color_eyre::Result<()> { + let guild_id = ctx.guild_id().wrap_err("guild_id not found")?; + + let member = ctx.author_member().await.wrap_err("member not found")?; + let permission_override = member + .permissions + .wrap_err("permissions not found")? + .contains(Permissions::MANAGE_GUILD); + + let room_id = ctx.channel_id(); + let mongo_client = &ctx.data().mongo; + + let state = mongo_client + .find_or_insert_state(guild_id.into()) + .await + .wrap_err("find guild for verdict")?; + + let lawsuit = state + .lawsuits + .iter() + .find(|l| l.court_room == room_id.into() && l.verdict.is_none()); + + let lawsuit = match lawsuit { + Some(lawsuit) => lawsuit.clone(), + None => { + ctx.say("i dem channel lauft kein aktive prozess!").await?; + return Ok(()); + } + }; + + let room = state + .court_rooms + .iter() + .find(|r| r.channel_id == room_id.into()); + let room = match room { + Some(room) => room.clone(), + None => { + ctx.say("i dem channel lauft kein aktive prozess!").await?; + return Ok(()); + } + }; + + let mut lawsuit_ctx = LawsuitCtx { + lawsuit, + mongo_client: mongo_client.clone(), + http: ctx.discord().http.clone(), + guild_id, + }; + + let response = lawsuit_ctx + .rule_verdict( + permission_override, + member.user.id, + verdict.to_string(), + room, + ) + .await?; + + if let Err(response) = response { + ctx.say(response.to_string()).await?; + return Ok(()); + } + + ctx.say("ich han en dir abschlosse").await?; + + Ok(()) +} + +async fn lawsuit_clear_impl(ctx: crate::Context<'_>) -> color_eyre::Result<()> { + let guild_id = ctx.guild_id().wrap_err("guild_id not found")?; + + ctx.data().mongo.delete_guild(guild_id.into()).await?; + ctx.say("alles weg").await?; + Ok(()) +} + async fn prison_set_role_impl(ctx: crate::Context<'_>, role: Role) -> color_eyre::Result<()> { ctx.data() .mongo @@ -495,17 +401,6 @@ async fn prison_set_role_impl(ctx: crate::Context<'_>, role: Role) -> color_eyre Ok(()) } -/// Jemanden einsperren -#[poise::command(slash_command, required_permissions = "MANAGE_GUILD")] -async fn prison_arrest( - ctx: crate::Context<'_>, - #[description = "Die Person zum einsperren"] user: User, -) -> color_eyre::Result<()> { - prison_arrest_impl(ctx, user) - .await - .wrap_err("prison_arrest") -} - async fn prison_arrest_impl(ctx: crate::Context<'_>, user: User) -> color_eyre::Result<()> { let mongo_client = &ctx.data().mongo; let guild_id = ctx.guild_id().wrap_err("guild_id not found")?; @@ -517,7 +412,8 @@ async fn prison_arrest_impl(ctx: crate::Context<'_>, user: User) -> color_eyre:: let role = match role { Some(role) => role, None => { - ctx.say("du mosch zerst e rolle setze mit /prison set_role").await?; + ctx.say("du mosch zerst e rolle setze mit /prison set_role") + .await?; return Ok(()); } }; @@ -536,17 +432,6 @@ async fn prison_arrest_impl(ctx: crate::Context<'_>, user: User) -> color_eyre:: Ok(()) } -/// Einen Gefangenen freilassen -#[poise::command(slash_command, required_permissions = "MANAGE_GUILD")] -async fn prison_release( - ctx: crate::Context<'_>, - #[description = "Die Person zum freilassen"] user: User, -) -> color_eyre::Result<()> { - prison_release_impl(ctx, user) - .await - .wrap_err("prison_release") -} - async fn prison_release_impl(ctx: crate::Context<'_>, user: User) -> color_eyre::Result<()> { let mongo_client = &ctx.data().mongo; let guild_id = ctx.guild_id().wrap_err("guild_id not found")?;