diff --git a/geoffrey_api/src/main.rs b/geoffrey_api/src/main.rs index 32b29d0..25cf9fb 100644 --- a/geoffrey_api/src/main.rs +++ b/geoffrey_api/src/main.rs @@ -11,8 +11,8 @@ use geoffrey_models::models::parameters::add_token_params::AddTokenParams; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::token::Permissions; -use crate::commands::{Command, command_filter}; use crate::commands::add_token::AddToken; +use crate::commands::{command_filter, Command}; use crate::config::GeoffreyAPIConfig; use crate::context::Context; use crate::logging::init_logging; diff --git a/geoffrey_bot/src/bot/commands/add_item.rs b/geoffrey_bot/src/bot/commands/add_item.rs index 8f2d8be..c861732 100644 --- a/geoffrey_bot/src/bot/commands/add_item.rs +++ b/geoffrey_bot/src/bot/commands/add_item.rs @@ -1,8 +1,7 @@ use async_trait::async_trait; use reqwest::Method; -use serenity::client::Context; use serenity::model::interactions::application_command::{ - ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType, + ApplicationCommandInteraction, ApplicationCommandOptionType, }; use geoffrey_models::models::locations::Location; @@ -11,6 +10,7 @@ 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; +use serenity::builder::CreateApplicationCommand; pub struct AddItemCommand; @@ -27,45 +27,50 @@ impl BotCommand for AddItemCommand { Method::POST } - async fn create_app_command(ctx: &Context) -> Result { - let command = ApplicationCommand::create_global_application_command(&ctx.http, |command| { - command - .name(Self::command_name()) - .description("Add a item to a shop.") - .create_option(|option| { - option - .name("item_name") - .description("Name of the item to sell.") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - .create_option(|option| { - option - .name("price") - .description("Price to list them item at.") - .kind(ApplicationCommandOptionType::Integer) - .required(true) - .min_int_value(0) - }) - .create_option(|option| { - option - .name("quantity") - .description("Number of items to sell for price") - .kind(ApplicationCommandOptionType::Integer) - .required(true) - .min_int_value(1) - }) - .create_option(|option| { - option - .name("shop") - .description("Shop to list the item at") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - }) - .await?; + 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()); + } + } - Ok(command) + None + } + + fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { + command + .name(Self::command_name()) + .description("Add a item to a shop.") + .create_option(|option| { + option + .name("item_name") + .description("Name of the item to sell.") + .kind(ApplicationCommandOptionType::String) + .required(true) + }) + .create_option(|option| { + option + .name("price") + .description("Price to list them item at.") + .kind(ApplicationCommandOptionType::Integer) + .required(true) + .min_int_value(0) + }) + .create_option(|option| { + option + .name("quantity") + .description("Number of items to sell for price") + .kind(ApplicationCommandOptionType::Integer) + .required(true) + .min_int_value(1) + }) + .create_option(|option| { + option + .name("shop") + .description("Shop to list the item at") + .kind(ApplicationCommandOptionType::String) + .required(true) + }) } async fn process_arguments( @@ -84,14 +89,4 @@ impl BotCommand for AddItemCommand { 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 8eba808..47da7ea 100644 --- a/geoffrey_bot/src/bot/commands/add_location.rs +++ b/geoffrey_bot/src/bot/commands/add_location.rs @@ -3,13 +3,13 @@ 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, ApplicationCommandOptionType, + ApplicationCommandInteraction, ApplicationCommandOptionType, }; use crate::bot::arg_parse::{option_to_dim, option_to_i64, option_to_loc_type, option_to_string}; use crate::bot::commands::{BotCommand, CommandError}; +use serenity::builder::CreateApplicationCommand; pub struct AddLocationCommand; @@ -26,72 +26,67 @@ impl BotCommand for AddLocationCommand { Method::POST } - async fn create_app_command(ctx: &Context) -> Result { - let command = ApplicationCommand::create_global_application_command(&ctx.http, |command| { - command - .name(Self::command_name()) - .description("Add a location to Geoffrey.") - .create_option(|option| { - option - .name("type") - .description("Location type") - .kind(ApplicationCommandOptionType::String) - .required(true) - .add_string_choice(LocationType::Base, LocationType::Base) - .add_string_choice(LocationType::Shop, LocationType::Shop) - .add_string_choice(LocationType::Attraction, LocationType::Attraction) - .add_string_choice(LocationType::Town, LocationType::Town) - .add_string_choice(LocationType::Farm, LocationType::Farm) - .add_string_choice(LocationType::Market, LocationType::Market) - }) - .create_option(|option| { - option - .name("name") - .description("Name of the location") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - .create_option(|option| { - option - .name("x") - .description("X coordinate of the location") - .kind(ApplicationCommandOptionType::Integer) - .max_int_value(i32::MAX) - .min_int_value(i32::MIN) - .required(true) - }) - .create_option(|option| { - option - .name("y") - .description("Y coordinate of the location") - .kind(ApplicationCommandOptionType::Integer) - .max_int_value(i32::MAX) - .min_int_value(i32::MIN) - .required(true) - }) - .create_option(|option| { - option - .name("z") - .description("Z coordinate of the location") - .kind(ApplicationCommandOptionType::Integer) - .max_int_value(i32::MAX) - .min_int_value(i32::MIN) - .required(true) - }) - .create_option(|option| { - option - .name("dimension") - .description("Dimension of the shop, default is Overworld") - .kind(ApplicationCommandOptionType::String) - .add_string_choice(Dimension::Overworld, Dimension::Overworld) - .add_string_choice(Dimension::Nether, Dimension::Nether) - .add_string_choice(Dimension::TheEnd, Dimension::TheEnd) - .required(false) - }) - }) - .await?; - - Ok(command) + fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { + command + .name(Self::command_name()) + .description("Add a location to Geoffrey.") + .create_option(|option| { + option + .name("type") + .description("Location type") + .kind(ApplicationCommandOptionType::String) + .required(true) + .add_string_choice(LocationType::Base, LocationType::Base) + .add_string_choice(LocationType::Shop, LocationType::Shop) + .add_string_choice(LocationType::Attraction, LocationType::Attraction) + .add_string_choice(LocationType::Town, LocationType::Town) + .add_string_choice(LocationType::Farm, LocationType::Farm) + .add_string_choice(LocationType::Market, LocationType::Market) + }) + .create_option(|option| { + option + .name("name") + .description("Name of the location") + .kind(ApplicationCommandOptionType::String) + .required(true) + }) + .create_option(|option| { + option + .name("x") + .description("X coordinate of the location") + .kind(ApplicationCommandOptionType::Integer) + .max_int_value(i32::MAX) + .min_int_value(i32::MIN) + .required(true) + }) + .create_option(|option| { + option + .name("y") + .description("Y coordinate of the location") + .kind(ApplicationCommandOptionType::Integer) + .max_int_value(i32::MAX) + .min_int_value(i32::MIN) + .required(true) + }) + .create_option(|option| { + option + .name("z") + .description("Z coordinate of the location") + .kind(ApplicationCommandOptionType::Integer) + .max_int_value(i32::MAX) + .min_int_value(i32::MIN) + .required(true) + }) + .create_option(|option| { + option + .name("dimension") + .description("Dimension of the shop, default is Overworld") + .kind(ApplicationCommandOptionType::String) + .add_string_choice(Dimension::Overworld, Dimension::Overworld) + .add_string_choice(Dimension::Nether, Dimension::Nether) + .add_string_choice(Dimension::TheEnd, Dimension::TheEnd) + .required(false) + }) } async fn process_arguments( diff --git a/geoffrey_bot/src/bot/commands/find.rs b/geoffrey_bot/src/bot/commands/find.rs index a87d403..682d938 100644 --- a/geoffrey_bot/src/bot/commands/find.rs +++ b/geoffrey_bot/src/bot/commands/find.rs @@ -1,8 +1,7 @@ use async_trait::async_trait; use reqwest::Method; -use serenity::client::Context; use serenity::model::interactions::application_command::{ - ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType, + ApplicationCommandInteraction, ApplicationCommandOptionType, }; use std::fmt::Write; @@ -12,6 +11,7 @@ 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; +use serenity::builder::CreateApplicationCommand; pub struct FindCommand; @@ -28,22 +28,17 @@ impl BotCommand for FindCommand { Method::GET } - async fn create_app_command(ctx: &Context) -> Result { - let command = ApplicationCommand::create_global_application_command(&ctx.http, |command| { - command - .name(Self::command_name()) - .description("Find a location in Geoffrey.") - .create_option(|option| { - option - .name("query") - .description("The location name or player to lookup") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - }) - .await?; - - Ok(command) + fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { + command + .name(Self::command_name()) + .description("Find a location in Geoffrey.") + .create_option(|option| { + option + .name("query") + .description("The location name or player to lookup") + .kind(ApplicationCommandOptionType::String) + .required(true) + }) } async fn process_arguments( diff --git a/geoffrey_bot/src/bot/commands/mod.rs b/geoffrey_bot/src/bot/commands/mod.rs index 51775e7..75fd988 100644 --- a/geoffrey_bot/src/bot/commands/mod.rs +++ b/geoffrey_bot/src/bot/commands/mod.rs @@ -4,10 +4,7 @@ use async_trait::async_trait; use reqwest::Error; use serde::de::DeserializeOwned; use serde::Serialize; -use serenity::client::Context; -use serenity::model::interactions::application_command::{ - ApplicationCommand, ApplicationCommandInteraction, -}; +use serenity::model::interactions::application_command::ApplicationCommandInteraction; use serenity::Error as SerenityError; use geoffrey_models::models::parameters::CommandRequest; @@ -16,6 +13,9 @@ use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::APIResponse; use crate::context::GeoffreyContext; +use serenity::builder::CreateApplicationCommand; +use std::future::Future; +use std::pin::Pin; pub mod add_item; pub mod add_location; @@ -23,12 +23,21 @@ pub mod find; pub mod selling; pub mod set_portal; +pub type GeoffreyCommandFn = Box< + fn( + GeoffreyContext, + UserID, + ApplicationCommandInteraction, + ) -> Pin + Send>>, +>; + #[derive(Debug)] pub enum CommandError { ArgumentParse(String), GeoffreyApi(GeoffreyAPIError), Serenity(serenity::Error), Reqwest(reqwest::Error), + CommandNotFound(String), } impl Display for CommandError { @@ -38,6 +47,7 @@ impl Display for CommandError { CommandError::GeoffreyApi(err) => format!("Got error from GeoffreyAPI: {}", err), CommandError::Serenity(err) => format!("Serenity Error: {}", err), CommandError::Reqwest(err) => format!("Reqwest Error: {}", err), + CommandError::CommandNotFound(err) => format!("'{}' not found!", err), }; write!(f, "{}", s) @@ -63,7 +73,7 @@ impl From for CommandError { } #[async_trait] -pub trait BotCommand { +pub trait BotCommand: Send + 'static { type ApiParams: CommandRequest; type ApiResp: Serialize + DeserializeOwned + Send; @@ -78,7 +88,7 @@ pub trait BotCommand { } async fn run_api_query( - ctx: &GeoffreyContext, + ctx: GeoffreyContext, params: Self::ApiParams, ) -> Result, CommandError> { let command_url = Self::command_url(&ctx.cfg.api.base_url); @@ -103,7 +113,7 @@ pub trait BotCommand { "You need to register before using this command!".to_string() } CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { - "Couldn't find that, maybe look for something that exists?".to_string() + "Couldn't find that, maybe look for something that exists?".to_string() } CommandError::GeoffreyApi(GeoffreyAPIError::PermissionInsufficient) => { "Looks like you don't have permission for that.".to_string() @@ -133,20 +143,24 @@ pub trait BotCommand { None } - async fn create_app_command(ctx: &Context) -> Result; + fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand; async fn process_arguments( command_interaction: ApplicationCommandInteraction, ) -> Result; async fn run_command( - ctx: &GeoffreyContext, + ctx: GeoffreyContext, user_id: UserID, command_interact: ApplicationCommandInteraction, ) -> Result { let mut args = Self::process_arguments(command_interact).await?; - log::info!("Running command {}, with args {:?}", Self::command_name(), args); + log::info!( + "Running command {}, with args {:?}", + Self::command_name(), + args + ); args.set_token(ctx.cfg.api.token.clone()); args.set_user_id(user_id); @@ -160,7 +174,7 @@ pub trait BotCommand { } async fn command( - ctx: &GeoffreyContext, + ctx: GeoffreyContext, user_id: UserID, command_interact: ApplicationCommandInteraction, ) -> String { diff --git a/geoffrey_bot/src/bot/commands/selling.rs b/geoffrey_bot/src/bot/commands/selling.rs index 5f5cdbc..5380872 100644 --- a/geoffrey_bot/src/bot/commands/selling.rs +++ b/geoffrey_bot/src/bot/commands/selling.rs @@ -2,9 +2,8 @@ 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, + ApplicationCommandInteraction, ApplicationCommandOptionType, }; use geoffrey_models::models::parameters::selling_params::{ItemSort, Order, SellingParams}; @@ -12,6 +11,7 @@ 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}; +use serenity::builder::CreateApplicationCommand; pub struct SellingCommand; @@ -28,40 +28,35 @@ impl BotCommand for SellingCommand { Method::GET } - async fn create_app_command(ctx: &Context) -> Result { - let command = ApplicationCommand::create_global_application_command(&ctx.http, |command| { - command - .name(Self::command_name()) - .description("Find items for sale.") - .create_option(|option| { - option - .name("query") - .description("Item to find") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - .create_option(|option| { - option - .name("sort") - .description("How to sort items") - .kind(ApplicationCommandOptionType::String) - .add_string_choice(ItemSort::Price, ItemSort::Price) - .add_string_choice(ItemSort::Restock, ItemSort::Restock) - .required(false) - }) - .create_option(|option| { - option - .name("order") - .description("Order of the item Search") - .kind(ApplicationCommandOptionType::String) - .add_string_choice(Order::Low, Order::Low) - .add_string_choice(Order::High, Order::High) - .required(false) - }) - }) - .await?; - - Ok(command) + fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { + command + .name(Self::command_name()) + .description("Find items for sale.") + .create_option(|option| { + option + .name("query") + .description("Item to find") + .kind(ApplicationCommandOptionType::String) + .required(true) + }) + .create_option(|option| { + option + .name("sort") + .description("How to sort items") + .kind(ApplicationCommandOptionType::String) + .add_string_choice(ItemSort::Price, ItemSort::Price) + .add_string_choice(ItemSort::Restock, ItemSort::Restock) + .required(false) + }) + .create_option(|option| { + option + .name("order") + .description("Order of the item Search") + .kind(ApplicationCommandOptionType::String) + .add_string_choice(Order::Low, Order::Low) + .add_string_choice(Order::High, Order::High) + .required(false) + }) } async fn process_arguments( diff --git a/geoffrey_bot/src/bot/commands/set_portal.rs b/geoffrey_bot/src/bot/commands/set_portal.rs index f18bf72..0265e08 100644 --- a/geoffrey_bot/src/bot/commands/set_portal.rs +++ b/geoffrey_bot/src/bot/commands/set_portal.rs @@ -2,14 +2,14 @@ use async_trait::async_trait; use geoffrey_models::models::locations::Location; use geoffrey_models::models::Portal; use reqwest::Method; -use serenity::client::Context; use serenity::model::interactions::application_command::{ - ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType, + ApplicationCommandInteraction, ApplicationCommandOptionType, }; use crate::bot::arg_parse::{option_to_i64, option_to_string}; use crate::bot::commands::{BotCommand, CommandError}; use geoffrey_models::models::parameters::set_portal_params::SetPortalParams; +use serenity::builder::CreateApplicationCommand; pub struct SetPortalCommand; @@ -26,40 +26,35 @@ impl BotCommand for SetPortalCommand { Method::POST } - async fn create_app_command(ctx: &Context) -> Result { - let command = ApplicationCommand::create_global_application_command(&ctx.http, |command| { - command - .name(Self::command_name()) - .description("Set a portal for a location") - .create_option(|option| { - option - .name("loc_name") - .description("Name of the location") - .kind(ApplicationCommandOptionType::String) - .required(true) - }) - .create_option(|option| { - option - .name("x_portal") - .description("X coordinate of the portal in the nether") - .kind(ApplicationCommandOptionType::Integer) - .max_int_value(i32::MAX) - .min_int_value(i32::MIN) - .required(true) - }) - .create_option(|option| { - option - .name("z_portal") - .description("Z coordinate of the portal in the nether") - .kind(ApplicationCommandOptionType::Integer) - .max_int_value(i32::MAX) - .min_int_value(i32::MIN) - .required(true) - }) - }) - .await?; - - Ok(command) + fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { + command + .name(Self::command_name()) + .description("Set a portal for a location") + .create_option(|option| { + option + .name("loc_name") + .description("Name of the location") + .kind(ApplicationCommandOptionType::String) + .required(true) + }) + .create_option(|option| { + option + .name("x_portal") + .description("X coordinate of the portal in the nether") + .kind(ApplicationCommandOptionType::Integer) + .max_int_value(i32::MAX) + .min_int_value(i32::MIN) + .required(true) + }) + .create_option(|option| { + option + .name("z_portal") + .description("Z coordinate of the portal in the nether") + .kind(ApplicationCommandOptionType::Integer) + .max_int_value(i32::MAX) + .min_int_value(i32::MIN) + .required(true) + }) } async fn process_arguments( @@ -78,4 +73,4 @@ impl BotCommand for SetPortalCommand { fn build_response(resp: Self::ApiResp) -> String { format!("{} has been been updated.", resp.name) } -} \ No newline at end of file +} diff --git a/geoffrey_bot/src/bot/formatters.rs b/geoffrey_bot/src/bot/formatters.rs index ee79ad2..74d50ec 100644 --- a/geoffrey_bot/src/bot/formatters.rs +++ b/geoffrey_bot/src/bot/formatters.rs @@ -30,13 +30,19 @@ pub fn display_owners(owners: Vec, limit: usize) -> String { } pub fn display_portal(portal: Portal) -> String { - format!("Portal: {} {} (x={}, z={})", portal.direction(), portal.tunnel_addr(), portal.x, portal.z) + format!( + "Portal: {} {} (x={}, z={})", + portal.direction(), + portal.tunnel_addr(), + portal.x, + portal.z + ) } pub fn display_loc(loc: Location) -> String { let portal_str = match loc.portal { None => "".to_string(), - Some(p) => format!("{}, ", display_portal(p)) + Some(p) => format!("{}, ", display_portal(p)), }; format!( diff --git a/geoffrey_bot/src/bot/mod.rs b/geoffrey_bot/src/bot/mod.rs index 6efdc8f..509e467 100644 --- a/geoffrey_bot/src/bot/mod.rs +++ b/geoffrey_bot/src/bot/mod.rs @@ -1,25 +1,86 @@ use serenity::model::interactions::application_command::ApplicationCommand; use serenity::prelude::*; +use crate::bot::commands::GeoffreyCommandFn; +use crate::context::GeoffreyContext; use commands::add_item::AddItemCommand; use commands::add_location::AddLocationCommand; use commands::find::FindCommand; use commands::selling::SellingCommand; use commands::set_portal::SetPortalCommand; use commands::{BotCommand, CommandError}; +use geoffrey_models::models::player::UserID; +use serenity::model::prelude::application_command::ApplicationCommandInteraction; +use std::collections::HashMap; 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(); - - commands.push(FindCommand::create_app_command(ctx).await?); - commands.push(SellingCommand::create_app_command(ctx).await?); - commands.push(AddLocationCommand::create_app_command(ctx).await?); - commands.push(AddItemCommand::create_app_command(ctx).await?); - commands.push( SetPortalCommand::create_app_command(ctx).await?); - - Ok(commands) +#[derive(Default)] +pub struct CommandRunner { + commands: HashMap, +} + +impl CommandRunner { + async fn register_app_command(ctx: &Context) -> Result<(), CommandError> { + ApplicationCommand::create_global_application_command(&ctx.http, |command| { + T::create_app_command(command) + }) + .await?; + + Ok(()) + } + + fn add_command_to_lookup(&mut self) { + self.commands + .insert(T::command_name(), Box::new(T::command)); + } + + pub async fn add_command( + &mut self, + ctx: &Context, + ) -> Result<&mut Self, CommandError> { + self.add_command_to_lookup::(); + Self::register_app_command::(ctx).await?; + Ok(self) + } + + pub async fn run_command<'r>( + &self, + command_name: &str, + geoffrey_ctx: GeoffreyContext, + user_id: UserID, + interaction: ApplicationCommandInteraction, + ) -> Result { + let command_fn = self + .commands + .get(command_name) + .ok_or_else(|| CommandError::CommandNotFound(command_name.to_string()))?; + + Ok(command_fn(geoffrey_ctx, user_id, interaction).await) + } +} + +pub async fn build_commands( + ctx: &Context, + command_runner: &mut CommandRunner, +) -> Result<(), CommandError> { + command_runner + .add_command::(ctx) + .await? + .add_command::(ctx) + .await? + .add_command::(ctx) + .await? + .add_command::(ctx) + .await? + .add_command::(ctx) + .await?; + + Ok(()) +} + +impl TypeMapKey for CommandRunner { + type Value = CommandRunner; } diff --git a/geoffrey_bot/src/logging.rs b/geoffrey_bot/src/logging.rs index ebe03e6..a1e8ef3 100644 --- a/geoffrey_bot/src/logging.rs +++ b/geoffrey_bot/src/logging.rs @@ -8,4 +8,4 @@ pub fn init_logging(log_level: LogLevel) -> Result<(), SetLoggerError> { .with_level(LevelFilter::Warn) .with_module_level("geoffrey_bot", log_level.into()) .init() -} \ No newline at end of file +} diff --git a/geoffrey_bot/src/main.rs b/geoffrey_bot/src/main.rs index d391888..b38f683 100644 --- a/geoffrey_bot/src/main.rs +++ b/geoffrey_bot/src/main.rs @@ -3,14 +3,11 @@ mod configs; mod context; mod logging; -use crate::bot::create_commands; +use crate::bot::{build_commands, CommandRunner}; 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 crate::logging::init_logging; +use geoffrey_models::logging::LogLevel; use geoffrey_models::models::player::UserID; use serenity::utils::{content_safe, ContentSafeOptions}; use serenity::{ @@ -23,9 +20,6 @@ use serenity::{ }; use std::path::PathBuf; use structopt::StructOpt; -use geoffrey_models::logging::LogLevel; -use crate::logging::init_logging; -use crate::bot::commands::set_portal::SetPortalCommand; #[derive(Debug, StructOpt, Clone)] #[structopt(name = "GeoffreyBot", about = "Geoffrey Discord Bot")] @@ -33,11 +27,11 @@ struct Args { #[structopt(env = "GEOFFREY_BOT_CONFIG", parse(from_os_str))] config: PathBuf, #[structopt( - short, - long, - env = "GEOFFREY_LOG_LEVEL", - parse(from_str), - default_value = "Info" + short, + long, + env = "GEOFFREY_LOG_LEVEL", + parse(from_str), + default_value = "Info" )] log_level: LogLevel, } @@ -54,36 +48,47 @@ struct Handler; impl EventHandler for Handler { async fn ready(&self, ctx: Context, ready: Ready) { log::info!("{} is connected!", ready.user.name); - let commands = create_commands(&ctx).await.unwrap(); - log::debug!("The following bot have been registered:"); + let mut data = ctx.data.write().await; - for command in commands { - log::debug!( - "{}: {} - {:?}", - command.name, command.description, command.options - ); + let command_runner = data + .get_mut::() + .expect("Unable to open command runner!"); + + match build_commands(&ctx, command_runner).await { + Ok(_) => {} + Err(e) => { + log::warn!("Error registering commands: {:?}", e) + } } } async fn interaction_create(&self, ctx: Context, interaction: Interaction) { let data = ctx.data.read().await; let geoffrey_ctx = data.get::().unwrap(); + let command_runner = data.get::().unwrap(); if let Interaction::ApplicationCommand(command) = interaction { let user_id = UserID::DiscordUUID { discord_uuid: command.user.id.0, }; - let msg = match command.data.name.as_str() { - "find" => FindCommand::command(geoffrey_ctx, user_id, command.clone()).await, - "selling" => SellingCommand::command(geoffrey_ctx, user_id, command.clone()).await, - "add_location" => { - AddLocationCommand::command(geoffrey_ctx, user_id, command.clone()).await + let command_name = command.data.name.clone(); + + let msg = match command_runner + .run_command( + &command_name, + geoffrey_ctx.clone(), + user_id, + command.clone(), + ) + .await + { + Ok(msg) => msg, + Err(e) => { + log::warn!("Error running command '{}': {:?}", command_name, e); + return; } - "add_item" => AddItemCommand::command(geoffrey_ctx, user_id, command.clone()).await, - "set_portal" => SetPortalCommand::command(geoffrey_ctx, user_id, command.clone()).await, - _ => "not implemented :(".to_string(), }; let msg = content_safe(&ctx.cache, &msg, &ContentSafeOptions::default()).await; @@ -143,7 +148,9 @@ async fn main() { data.insert::(GeoffreyContext { http_client: reqwest::Client::new(), cfg, - }) + }); + + data.insert::(CommandRunner::default()); } if let Err(e) = client.start().await { diff --git a/geoffrey_models/src/lib.rs b/geoffrey_models/src/lib.rs index 3a5cad0..15c0b43 100644 --- a/geoffrey_models/src/lib.rs +++ b/geoffrey_models/src/lib.rs @@ -3,8 +3,8 @@ use serde::de::DeserializeOwned; use serde::Serialize; use std::fmt::Debug; -pub mod models; pub mod logging; +pub mod models; pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned + Debug { fn id(&self) -> Option; diff --git a/geoffrey_models/src/logging/mod.rs b/geoffrey_models/src/logging/mod.rs index de0d30f..4373de0 100644 --- a/geoffrey_models/src/logging/mod.rs +++ b/geoffrey_models/src/logging/mod.rs @@ -1,5 +1,5 @@ use log::LevelFilter; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug)] pub enum LogLevel { diff --git a/geoffrey_models/src/models/mod.rs b/geoffrey_models/src/models/mod.rs index d4f52e4..a6798f1 100644 --- a/geoffrey_models/src/models/mod.rs +++ b/geoffrey_models/src/models/mod.rs @@ -122,7 +122,7 @@ impl Portal { pub fn tunnel_addr(&self) -> i32 { match self.direction() { Direction::North | Direction::South => self.z.abs(), - Direction::East | Direction::West => self.x.abs() + Direction::East | Direction::West => self.x.abs(), } } }