use std::fmt::Debug; use std::future::Future; use std::pin::Pin; use async_trait::async_trait; use serde::de::DeserializeOwned; use serde::Serialize; use serenity::builder::CreateApplicationCommand; use serenity::model::interactions::application_command::ApplicationCommandInteraction; use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam}; use geoffrey_models::models::player::UserID; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use crate::api::run_api_query; use crate::context::GeoffreyContext; use crate::error::BotError; pub mod add_item; pub mod add_location; pub mod delete; pub mod edit_name; pub mod edit_pos; pub mod find; pub mod info; pub mod register; pub mod remove_item; pub mod report_out_of_stock; pub mod restock; pub mod selling; pub mod set_portal; pub type GeoffreyCommandFn = Box< fn( GeoffreyContext, UserID, ApplicationCommandInteraction, ) -> Pin + Send>>, >; #[async_trait] pub trait BotCommand: Send + 'static { type ApiParams: GeoffreyParam; type ApiResp: Serialize + DeserializeOwned + Send + Debug; fn command_name() -> String; fn endpoint() -> String { format!("command/{}/", Self::command_name()) } fn request_type() -> reqwest::Method; fn get_err_resp(err: BotError) -> String { if let Some(resp) = Self::custom_err_resp(&err) { resp } else { match err { BotError::GeoffreyApi(GeoffreyAPIError::PlayerNotRegistered) => { "You need to register before using this command!".to_string() } BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { "Couldn't find that, maybe look for something that exists?".to_string() } BotError::GeoffreyApi(GeoffreyAPIError::PermissionInsufficient) => { "Looks like you don't have permission for that.".to_string() } BotError::GeoffreyApi(GeoffreyAPIError::EntryNotUnique) => { "Slow down, I already know that thing. Try a new name.".to_string() } BotError::GeoffreyApi(GeoffreyAPIError::MultipleLocationsMatch) => { "I couldn't match a single location, narrow down your search".to_string() } BotError::GeoffreyApi(GeoffreyAPIError::ParameterInvalid(err)) => { format!( "Welp, you some how messed up the {} parameter, great job", err ) } _ => { log::warn!("GeoffreyBot got an unhandled error: {}", err); format!("OOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The admins at our \ headquarters are working VEWY HAWD to fix this! (Error in command {})", Self::command_name()) } } } } fn custom_err_resp(_: &BotError) -> Option { None } fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand; async fn process_arguments( command_interaction: ApplicationCommandInteraction, ) -> Result; async fn run_command( ctx: GeoffreyContext, user_id: UserID, command_interact: ApplicationCommandInteraction, ) -> Result { let args = Self::process_arguments(command_interact).await?; log::info!( "Running command {}, with args {:?}", Self::command_name(), args ); let request = CommandRequest { token: ctx.cfg.api.token.clone(), user_id: Some(user_id), params: args.clone(), }; let resp = run_api_query(&ctx, &request, Self::request_type(), &Self::endpoint()).await?; Ok(Self::build_response(&ctx, resp, args)) } async fn command( ctx: GeoffreyContext, user_id: UserID, command_interact: ApplicationCommandInteraction, ) -> String { match Self::run_command(ctx, user_id, command_interact).await { Ok(msg) => msg, Err(e) => Self::get_err_resp(e), } } fn build_response(ctx: &GeoffreyContext, resp: Self::ApiResp, req: Self::ApiParams) -> String; }