Geoffrey-rs/geoffrey_bot/src/commands/bot_command.rs

167 lines
5.6 KiB
Rust

use crate::context::GeoffreyContext;
use async_trait::async_trait;
use geoffrey_models::models::parameters::CommandRequest;
use geoffrey_models::models::player::UserID;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::response::APIResponse;
use reqwest::Error;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serenity::model::prelude::application_command::{
ApplicationCommand, ApplicationCommandInteraction,
};
use serenity::prelude::{Context, SerenityError};
use std::fmt::{Display, Formatter};
#[derive(Debug)]
pub enum CommandError {
ArgumentParse(String),
GeoffreyApi(GeoffreyAPIError),
Serenity(serenity::Error),
Reqwest(reqwest::Error),
}
impl Display for CommandError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = match self {
CommandError::ArgumentParse(s) => format!("Unable to parse argument '{}'", s),
CommandError::GeoffreyApi(err) => format!("Got error from GeoffreyAPI: {}", err),
CommandError::Serenity(err) => format!("Serenity Error: {}", err),
CommandError::Reqwest(err) => format!("Reqwest Error: {}", err),
};
write!(f, "{}", s)
}
}
impl From<GeoffreyAPIError> for CommandError {
fn from(err: GeoffreyAPIError) -> Self {
Self::GeoffreyApi(err)
}
}
impl From<SerenityError> for CommandError {
fn from(err: SerenityError) -> Self {
Self::Serenity(err)
}
}
impl From<reqwest::Error> for CommandError {
fn from(err: Error) -> Self {
Self::Reqwest(err)
}
}
#[async_trait]
pub trait BotCommand {
type ApiParams: CommandRequest;
type ApiResp: Serialize + DeserializeOwned + Send;
fn command_name() -> String;
fn request_type() -> reqwest::Method;
fn command_url(base_string: &str) -> String {
let slash = if !base_string.ends_with('/') { "/" } else { "" };
format!("{}{}command/{}/", base_string, slash, Self::command_name())
}
async fn run_api_query(
ctx: &GeoffreyContext,
params: Self::ApiParams,
) -> Result<APIResponse<Self::ApiResp>, CommandError> {
let command_url = Self::command_url(&ctx.cfg.api.base_url);
let resp: APIResponse<Self::ApiResp> = ctx
.http_client
.request(Self::request_type(), command_url)
.json(&params)
.send()
.await?
.json()
.await?;
Ok(resp)
}
fn get_err_resp(err: CommandError) -> String {
if let Some(resp) = Self::custom_err_resp(&err) {
resp
} else {
match err {
CommandError::GeoffreyApi(err) => match err {
GeoffreyAPIError::PlayerNotRegistered => {
"You need to register before using this command!".to_string()
}
GeoffreyAPIError::EntryNotFound => {
"Couldn't find that, maybe look for something that exists?".to_string()
}
GeoffreyAPIError::PermissionInsufficient => {
"Looks like you don't have permission for that.".to_string()
}
GeoffreyAPIError::EntryNotUnique => {
"Slow down, I already know that thing. Try a new name.".to_string()
}
GeoffreyAPIError::DatabaseError(_) => "How the heck u mess that up".to_string(),
GeoffreyAPIError::TokenNotAuthorized => "WHO ARE YOU????".to_string(),
GeoffreyAPIError::MultipleLocationsMatch => {
"I couldn't match a single location, narrow down your search".to_string()
}
GeoffreyAPIError::ParameterInvalid(err) => {
format!(
"Welp, you some how messed up the {} parameter, great job",
err
)
}
},
_ => {
println!("GeoffreyBot 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(_: &CommandError) -> Option<String> {
None
}
async fn create_app_command(ctx: &Context) -> Result<ApplicationCommand, CommandError>;
async fn process_arguments(
command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError>;
async fn run_command(
ctx: &GeoffreyContext,
user_id: UserID,
command_interact: ApplicationCommandInteraction,
) -> Result<String, CommandError> {
let mut args = Self::process_arguments(command_interact).await?;
args.set_token(ctx.cfg.api.token.clone());
args.set_user_id(user_id);
let resp = Self::run_api_query(ctx, args).await?;
match resp {
APIResponse::Response(resp) => Ok(Self::build_response(resp)),
APIResponse::Error { error: err, .. } => Err(CommandError::GeoffreyApi(err)),
}
}
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(resp: Self::ApiResp) -> String;
}