From a655146c818000eb7de91ef3aa7d7ae0935c4bb3 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Fri, 3 Dec 2021 20:42:57 -0700 Subject: [PATCH] Bot code cleanup + Added functions for parsing Application Command options into different types + Added formatters for common message formats + Clippy + Fmt --- geoffrey_bot/src/bot/arg_parse.rs | 72 +++++++++++++++++ geoffrey_bot/src/bot/commands/add_item.rs | 66 ++++------------ geoffrey_bot/src/bot/commands/add_location.rs | 79 +++---------------- geoffrey_bot/src/bot/commands/find.rs | 19 ++--- geoffrey_bot/src/bot/commands/mod.rs | 5 +- geoffrey_bot/src/bot/commands/selling.rs | 41 ++-------- geoffrey_bot/src/bot/formatters.rs | 38 +++++++++ geoffrey_bot/src/bot/mod.rs | 4 +- geoffrey_bot/src/main.rs | 10 +-- 9 files changed, 161 insertions(+), 173 deletions(-) create mode 100644 geoffrey_bot/src/bot/arg_parse.rs create mode 100644 geoffrey_bot/src/bot/formatters.rs diff --git a/geoffrey_bot/src/bot/arg_parse.rs b/geoffrey_bot/src/bot/arg_parse.rs new file mode 100644 index 0000000..ae90fd8 --- /dev/null +++ b/geoffrey_bot/src/bot/arg_parse.rs @@ -0,0 +1,72 @@ +use crate::bot::commands::CommandError; +use geoffrey_models::models::locations::LocationType; +use geoffrey_models::models::parameters::selling_params::{ItemSort, Order}; +use geoffrey_models::models::Dimension; +use serenity::model::prelude::application_command::{ + ApplicationCommandInteractionDataOption, ApplicationCommandInteractionDataOptionValue, +}; +use std::str::FromStr; + +pub fn option_to_i64( + option: Option<&ApplicationCommandInteractionDataOption>, + field: &str, +) -> Result { + if let Some(option) = option { + let option = option.resolved.as_ref(); + + if let Some(ApplicationCommandInteractionDataOptionValue::Integer(i)) = option { + return Ok(*i); + } + } + Err(CommandError::ArgumentParse(field.to_string())) +} + +pub fn option_to_string( + option: Option<&ApplicationCommandInteractionDataOption>, + field: &str, +) -> Result { + if let Some(option) = option { + let option = option.resolved.as_ref(); + + if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { + return Ok(s.clone()); + } + } + Err(CommandError::ArgumentParse(field.to_string())) +} + +pub fn option_to_loc_type( + option: Option<&ApplicationCommandInteractionDataOption>, + field: &str, +) -> Result { + let loc_type = option_to_string(option, field)?; + + LocationType::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) +} + +pub fn option_to_dim( + option: Option<&ApplicationCommandInteractionDataOption>, + field: &str, +) -> Result { + let loc_type = option_to_string(option, field)?; + + Dimension::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) +} + +pub fn option_to_sort( + option: Option<&ApplicationCommandInteractionDataOption>, + field: &str, +) -> Result { + let loc_type = option_to_string(option, field)?; + + ItemSort::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) +} + +pub fn option_to_order( + option: Option<&ApplicationCommandInteractionDataOption>, + field: &str, +) -> Result { + let loc_type = option_to_string(option, field)?; + + Order::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) +} diff --git a/geoffrey_bot/src/bot/commands/add_item.rs b/geoffrey_bot/src/bot/commands/add_item.rs index 73787fd..8f2d8be 100644 --- a/geoffrey_bot/src/bot/commands/add_item.rs +++ b/geoffrey_bot/src/bot/commands/add_item.rs @@ -4,12 +4,13 @@ use serenity::client::Context; use serenity::model::interactions::application_command::{ ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType, }; -use serenity::model::prelude::application_command::ApplicationCommandInteractionDataOptionValue; use geoffrey_models::models::locations::Location; use geoffrey_models::models::parameters::add_item_params::AddItemParams; +use crate::bot::arg_parse::{option_to_i64, option_to_string}; use crate::bot::commands::{BotCommand, CommandError}; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; pub struct AddItemCommand; @@ -72,60 +73,25 @@ impl BotCommand for AddItemCommand { ) -> Result { let options = command_interaction.data.options; - let mut item_name = String::default(); - let mut price = 0; - let mut quanity = 0; - let mut shop = String::default(); - - if let Some(option) = options.get(0) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - item_name = s.clone(); - } else { - return Err(CommandError::ArgumentParse("item_name".to_string())); - } - } - - if let Some(option) = options.get(1) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::Integer(p)) = option { - price = *p; - } else { - return Err(CommandError::ArgumentParse("price".to_string())); - } - } - - if let Some(option) = options.get(2) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::Integer(q)) = option { - quanity = *q; - } else { - return Err(CommandError::ArgumentParse("quantity".to_string())); - } - } - - if let Some(option) = options.get(3) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - shop = s.clone(); - } else { - return Err(CommandError::ArgumentParse("shop".to_string())); - } - } - Ok(Self::ApiParams::new( - item_name, - price as u32, - quanity as u32, - shop, + option_to_string(options.get(0), "item_name")?, + option_to_i64(options.get(1), "price")? as u32, + option_to_i64(options.get(2), "quantity")? as u32, + option_to_string(options.get(3), "shop")?, )) } fn build_response(resp: Self::ApiResp) -> String { format!("{} has been updated", resp.name) } + + fn custom_err_resp(e: &CommandError) -> Option { + if let CommandError::GeoffreyApi(err) = e { + if matches!(err, GeoffreyAPIError::EntryNotFound) { + return Some("You don't have a shop by that name ding dong!".to_string()); + } + } + + None + } } diff --git a/geoffrey_bot/src/bot/commands/add_location.rs b/geoffrey_bot/src/bot/commands/add_location.rs index eda9c30..0e90c0d 100644 --- a/geoffrey_bot/src/bot/commands/add_location.rs +++ b/geoffrey_bot/src/bot/commands/add_location.rs @@ -1,32 +1,18 @@ -use std::str::FromStr; - use async_trait::async_trait; +use geoffrey_models::models::locations::{Location, LocationType}; +use geoffrey_models::models::parameters::add_location_params::AddLocationParams; +use geoffrey_models::models::{Dimension, Position}; use reqwest::Method; use serenity::client::Context; use serenity::model::interactions::application_command::{ - ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandInteractionDataOption, - ApplicationCommandOptionType, + ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType, }; -use serenity::model::prelude::application_command::ApplicationCommandInteractionDataOptionValue; - -use geoffrey_models::models::{Dimension, Position}; -use geoffrey_models::models::locations::{Location, LocationType}; -use geoffrey_models::models::parameters::add_location_params::AddLocationParams; +use crate::bot::arg_parse::{option_to_dim, option_to_i64, option_to_loc_type, option_to_string}; use crate::bot::commands::{BotCommand, CommandError}; pub struct AddLocationCommand; -fn option_to_i64(option: &ApplicationCommandInteractionDataOption) -> Option { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::Integer(s)) = option { - Some(*s) - } else { - None - } -} - #[async_trait] impl BotCommand for AddLocationCommand { type ApiParams = AddLocationParams; @@ -120,55 +106,16 @@ impl BotCommand for AddLocationCommand { command_interaction: ApplicationCommandInteraction, ) -> Result { let options = command_interaction.data.options; - let mut name = String::new(); - let mut loc_type = LocationType::Base; - let x; - let _y; - let z; - let dimension; + let name = option_to_string(options.get(0), "name")?; + let loc_type = option_to_loc_type(options.get(1), "loc_type")?; + let x = option_to_i64(options.get(2), "x")?; + let _y = option_to_i64(options.get(3), "x")?; + let z = option_to_i64(options.get(4), "x")?; + let dim = option_to_dim(options.get(5), "dimension").unwrap_or(Dimension::Overworld); - if let Some(option) = options.get(0) { - let option = option.resolved.as_ref(); + let position = Position::new(x as i32, z as i32, dim); - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - loc_type = LocationType::from_str(s).unwrap(); - } else { - return Err(CommandError::ArgumentParse("loc_type".to_string())); - } - } - - if let Some(option) = options.get(1) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - name = s.clone(); - } else { - return Err(CommandError::ArgumentParse("name".to_string())); - } - } - - x = option_to_i64(&options[2]).unwrap() as i32; - _y = option_to_i64(&options[3]).unwrap() as i32; - z = option_to_i64(&options[4]).unwrap() as i32; - - if let Some(option) = options.get(5) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - dimension = Dimension::from_str(s.as_str()).unwrap(); - } else { - return Err(CommandError::ArgumentParse("dimension".to_string())); - } - } else { - dimension = Dimension::default(); - } - - Ok(Self::ApiParams::new( - name, - Position::new(x, z, dimension), - loc_type, - None, - )) + Ok(Self::ApiParams::new(name, position, loc_type, None)) } fn build_response(resp: Self::ApiResp) -> String { diff --git a/geoffrey_bot/src/bot/commands/find.rs b/geoffrey_bot/src/bot/commands/find.rs index e4d5179..d1e3e69 100644 --- a/geoffrey_bot/src/bot/commands/find.rs +++ b/geoffrey_bot/src/bot/commands/find.rs @@ -1,17 +1,17 @@ -use std::fmt::Write; - use async_trait::async_trait; use reqwest::Method; use serenity::client::Context; use serenity::model::interactions::application_command::{ ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType, }; -use serenity::model::prelude::application_command::ApplicationCommandInteractionDataOptionValue; +use std::fmt::Write; use geoffrey_models::models::locations::Location; use geoffrey_models::models::parameters::find_params::FindParams; +use crate::bot::arg_parse::option_to_string; use crate::bot::commands::{BotCommand, CommandError}; +use crate::bot::formatters::display_loc; pub struct FindCommand; @@ -50,16 +50,9 @@ impl BotCommand for FindCommand { command_interaction: ApplicationCommandInteraction, ) -> Result { let options = command_interaction.data.options; + let query = option_to_string(options.get(0), "query")?; - if let Some(option) = options.get(0) { - let query = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = query { - return Ok(FindParams::new(s.clone())); - } - } - - Err(CommandError::ArgumentParse("query".to_string())) + Ok(FindParams::new(query)) } fn build_response(resp: Self::ApiResp) -> String { @@ -69,7 +62,7 @@ impl BotCommand for FindCommand { let mut resp_str = String::new(); writeln!(resp_str, "The following locations match:").unwrap(); for loc in resp { - writeln!(resp_str, "**{}**, {}", loc.name, loc.position).unwrap(); + writeln!(display_loc(loc)).unwrap(); } resp_str } diff --git a/geoffrey_bot/src/bot/commands/mod.rs b/geoffrey_bot/src/bot/commands/mod.rs index 259b4a7..a353d57 100644 --- a/geoffrey_bot/src/bot/commands/mod.rs +++ b/geoffrey_bot/src/bot/commands/mod.rs @@ -5,8 +5,10 @@ use reqwest::Error; use serde::de::DeserializeOwned; use serde::Serialize; use serenity::client::Context; +use serenity::model::interactions::application_command::{ + ApplicationCommand, ApplicationCommandInteraction, +}; use serenity::Error as SerenityError; -use serenity::model::interactions::application_command::{ApplicationCommand, ApplicationCommandInteraction}; use geoffrey_models::models::parameters::CommandRequest; use geoffrey_models::models::player::UserID; @@ -17,7 +19,6 @@ use crate::context::GeoffreyContext; pub mod add_item; pub mod add_location; -mod arg_parse; pub mod find; pub mod selling; diff --git a/geoffrey_bot/src/bot/commands/selling.rs b/geoffrey_bot/src/bot/commands/selling.rs index 8051f3d..5f5cdbc 100644 --- a/geoffrey_bot/src/bot/commands/selling.rs +++ b/geoffrey_bot/src/bot/commands/selling.rs @@ -1,5 +1,4 @@ use std::fmt::Write; -use std::str::FromStr; use async_trait::async_trait; use reqwest::Method; @@ -7,11 +6,11 @@ use serenity::client::Context; use serenity::model::interactions::application_command::{ ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType, }; -use serenity::model::prelude::application_command::ApplicationCommandInteractionDataOptionValue; use geoffrey_models::models::parameters::selling_params::{ItemSort, Order, SellingParams}; use geoffrey_models::models::response::selling_listing::SellingListing; +use crate::bot::arg_parse::{option_to_order, option_to_sort, option_to_string}; use crate::bot::commands::{BotCommand, CommandError}; pub struct SellingCommand; @@ -69,39 +68,9 @@ impl BotCommand for SellingCommand { command_interaction: ApplicationCommandInteraction, ) -> Result { let options = command_interaction.data.options; - let mut query = String::new(); - let mut sort = None; - let mut order = None; - - if let Some(option) = options.get(0) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - query = s.clone(); - } else { - return Err(CommandError::ArgumentParse("query".to_string())); - } - } - - if let Some(option) = options.get(1) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - sort = Some(ItemSort::from_str(s).unwrap()); - } else { - return Err(CommandError::ArgumentParse("sort".to_string())); - } - } - - if let Some(option) = options.get(2) { - let option = option.resolved.as_ref(); - - if let Some(ApplicationCommandInteractionDataOptionValue::String(s)) = option { - order = Some(Order::from_str(s).unwrap()); - } else { - return Err(CommandError::ArgumentParse("order".to_string())); - } - } + let query = option_to_string(options.get(0), "query")?; + let sort = option_to_sort(options.get(1), "sort").ok(); + let order = option_to_order(options.get(2), "order").ok(); Ok(SellingParams::new(query, sort, order)) } @@ -111,7 +80,7 @@ impl BotCommand for SellingCommand { "No shops were found selling that, maybe I should start selling it...".to_string() } else { let mut resp_str = String::new(); - writeln!(resp_str, "The items match:").unwrap(); + writeln!(resp_str, "The following items match:").unwrap(); for item in resp { writeln!( resp_str, diff --git a/geoffrey_bot/src/bot/formatters.rs b/geoffrey_bot/src/bot/formatters.rs new file mode 100644 index 0000000..b6c2564 --- /dev/null +++ b/geoffrey_bot/src/bot/formatters.rs @@ -0,0 +1,38 @@ +use geoffrey_models::models::locations::Location; +use geoffrey_models::models::player::Player; + +pub fn display_owners(owners: Vec, limit: usize) -> String { + let mut plural = ""; + let mut ellipses = ""; + + if owners.len() > 1 { + plural = "s" + } + + let range = if owners.len() > limit { + ellipses = "..."; + limit + } else { + owners.len() + }; + + format!( + "Owner{}: {}{}", + plural, + owners[0..range] + .iter() + .map(|owner| owner.name.clone()) + .collect::>() + .join(", "), + ellipses + ) +} + +pub fn display_loc(loc: Location) -> String { + format!( + "**{}**, {}, Owner(s): **{}**", + loc.name, + loc.position, + display_owners(loc.owners, 3) + ) +} diff --git a/geoffrey_bot/src/bot/mod.rs b/geoffrey_bot/src/bot/mod.rs index ac86e30..291bdb7 100644 --- a/geoffrey_bot/src/bot/mod.rs +++ b/geoffrey_bot/src/bot/mod.rs @@ -1,13 +1,15 @@ use serenity::model::interactions::application_command::ApplicationCommand; use serenity::prelude::*; -use commands::{BotCommand, CommandError}; use commands::add_item::AddItemCommand; use commands::add_location::AddLocationCommand; use commands::find::FindCommand; use commands::selling::SellingCommand; +use commands::{BotCommand, CommandError}; +pub mod arg_parse; pub mod commands; +pub mod formatters; pub async fn create_commands(ctx: &Context) -> Result, CommandError> { let mut commands: Vec = Vec::new(); diff --git a/geoffrey_bot/src/main.rs b/geoffrey_bot/src/main.rs index f45682b..863c0f0 100644 --- a/geoffrey_bot/src/main.rs +++ b/geoffrey_bot/src/main.rs @@ -2,14 +2,14 @@ mod bot; mod configs; mod context; -use bot::commands::add_item::AddItemCommand; -use bot::commands::add_location::AddLocationCommand; -use bot::commands::BotCommand; use crate::bot::create_commands; -use bot::commands::find::FindCommand; -use bot::commands::selling::SellingCommand; use crate::configs::GeoffreyBotConfig; use crate::context::GeoffreyContext; +use bot::commands::add_item::AddItemCommand; +use bot::commands::add_location::AddLocationCommand; +use bot::commands::find::FindCommand; +use bot::commands::selling::SellingCommand; +use bot::commands::BotCommand; use geoffrey_models::models::player::UserID; use serenity::utils::{content_safe, ContentSafeOptions}; use serenity::{