Added global settings
ci/woodpecker/push/woodpecker Pipeline was successful Details

+ Usedful for the impls to retrieve settings without having to redefine them in each project
+ Start of models api
+ Bunch of small tweaks
+ clippy + fmt
main
Joey Hines 2022-01-08 12:35:55 -07:00
parent 99cb470e6e
commit a8f5f5d87b
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
34 changed files with 433 additions and 285 deletions

View File

@ -1,5 +1,4 @@
use crate::commands::{Command, RequestType}; use crate::commands::{Command, RequestType};
use crate::config::GeoffreyAPIConfig;
use crate::context::Context; use crate::context::Context;
use crate::helper::validate_string_parameter; use crate::helper::validate_string_parameter;
use crate::Result; use crate::Result;
@ -10,6 +9,7 @@ use geoffrey_models::models::locations::{Location, LocationDataDb, LocationDb, L
use geoffrey_models::models::parameters::add_item_params::AddItemParams; use geoffrey_models::models::parameters::add_item_params::AddItemParams;
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::settings::GeoffreySettings;
use geoffrey_models::models::CommandLevel; use geoffrey_models::models::CommandLevel;
use std::sync::Arc; use std::sync::Arc;
@ -60,7 +60,7 @@ impl Command for AddItem {
} }
} }
fn validate_parameters(req: &Self::Req, cfg: &GeoffreyAPIConfig) -> Result<()> { fn validate_parameters(req: &Self::Req, cfg: &GeoffreySettings) -> Result<()> {
if req.quantity == 0 { if req.quantity == 0 {
return Err(GeoffreyAPIError::ParameterInvalid("quantity".to_string())); return Err(GeoffreyAPIError::ParameterInvalid("quantity".to_string()));
} }

View File

@ -1,5 +1,4 @@
use crate::commands::{Command, RequestType}; use crate::commands::{Command, RequestType};
use crate::config::GeoffreyAPIConfig;
use crate::context::Context; use crate::context::Context;
use crate::helper::validate_string_parameter; use crate::helper::validate_string_parameter;
use crate::Result; use crate::Result;
@ -7,6 +6,7 @@ use geoffrey_db::helper::load_location;
use geoffrey_models::models::locations::{Location, LocationDb}; use geoffrey_models::models::locations::{Location, LocationDb};
use geoffrey_models::models::parameters::add_location_params::AddLocationParams; use geoffrey_models::models::parameters::add_location_params::AddLocationParams;
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
use geoffrey_models::models::settings::GeoffreySettings;
use geoffrey_models::models::CommandLevel; use geoffrey_models::models::CommandLevel;
use std::sync::Arc; use std::sync::Arc;
@ -44,7 +44,7 @@ impl Command for AddLocation {
load_location(&ctx.db, &location).map_err(|err| err.into()) load_location(&ctx.db, &location).map_err(|err| err.into())
} }
fn validate_parameters(req: &Self::Req, cfg: &GeoffreyAPIConfig) -> Result<()> { fn validate_parameters(req: &Self::Req, cfg: &GeoffreySettings) -> Result<()> {
validate_string_parameter("name", &req.name, cfg.max_str_len) validate_string_parameter("name", &req.name, cfg.max_str_len)
} }
} }

View File

@ -11,7 +11,7 @@ use crate::commands::report_out_of_stock::ReportOutOfStock;
use crate::commands::restock::Restock; use crate::commands::restock::Restock;
use crate::commands::selling::Selling; use crate::commands::selling::Selling;
use crate::commands::set_portal::SetPortal; use crate::commands::set_portal::SetPortal;
use crate::config::GeoffreyAPIConfig; use crate::commands::settings::Settings;
use crate::context::Context; use crate::context::Context;
use crate::helper::{get_player_from_req, get_token_from_req}; use crate::helper::{get_player_from_req, get_token_from_req};
use crate::Result; use crate::Result;
@ -19,6 +19,7 @@ use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam};
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::response::APIResponse; use geoffrey_models::models::response::APIResponse;
use geoffrey_models::models::settings::GeoffreySettings;
use geoffrey_models::models::token::{Permissions, Token}; use geoffrey_models::models::token::{Permissions, Token};
use geoffrey_models::models::CommandLevel; use geoffrey_models::models::CommandLevel;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
@ -42,6 +43,7 @@ pub mod report_out_of_stock;
pub mod restock; pub mod restock;
pub mod selling; pub mod selling;
pub mod set_portal; pub mod set_portal;
pub mod settings;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
@ -59,7 +61,7 @@ pub trait Command {
fn command_level() -> CommandLevel; fn command_level() -> CommandLevel;
fn run_command(ctx: Arc<Context>, req: &Self::Req, user: Option<Player>) -> Result<Self::Resp>; fn run_command(ctx: Arc<Context>, req: &Self::Req, user: Option<Player>) -> Result<Self::Resp>;
fn validate_parameters(_: &Self::Req, _: &GeoffreyAPIConfig) -> Result<()> { fn validate_parameters(_: &Self::Req, _: &GeoffreySettings) -> Result<()> {
Ok(()) Ok(())
} }
@ -102,7 +104,7 @@ pub fn handle_command<T: Command>(
match T::user_is_authorized(&token, &user) { match T::user_is_authorized(&token, &user) {
Ok(_) => { Ok(_) => {
T::validate_parameters(&req.params, &ctx.cfg)?; T::validate_parameters(&req.params, &ctx.cfg.geoffrey_settings)?;
T::run_command(ctx, &req.params, user) T::run_command(ctx, &req.params, user)
} }
Err(e) => Err(e), Err(e) => Err(e),
@ -153,3 +155,9 @@ pub fn command_filter(
.or(create_command_filter::<SetPortal>(ctx)), .or(create_command_filter::<SetPortal>(ctx)),
) )
} }
pub fn model_filter(
ctx: Arc<Context>,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::path("model").and(create_command_filter::<Settings>(ctx))
}

View File

@ -101,22 +101,25 @@ impl Command for Selling {
mod test { mod test {
use crate::commands::selling::Selling; use crate::commands::selling::Selling;
use crate::commands::Command; use crate::commands::Command;
use crate::config::GeoffreyAPIConfig; use crate::config::{GeoffreyAPIConfig, ServerConfig};
use crate::context::Context; use crate::context::Context;
use crate::Args; use crate::Args;
use geoffrey_models::models::item::ItemListing; use geoffrey_models::models::item::ItemListing;
use geoffrey_models::models::locations::shop::Shop; use geoffrey_models::models::locations::shop::Shop;
use geoffrey_models::models::locations::{LocationDataDb, LocationDb}; use geoffrey_models::models::locations::{LocationDataDb, LocationDb};
use geoffrey_models::models::parameters::selling_params::SellingParams; use geoffrey_models::models::parameters::selling_params::SellingParams;
use geoffrey_models::models::settings::GeoffreySettings;
use geoffrey_models::models::Position; use geoffrey_models::models::Position;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
fn test_selling_lookup_speed(iter: i32, shop_count: i32, items_for_sale: i32) -> f32 { fn test_selling_lookup_speed(iter: i32, shop_count: i32, items_for_sale: i32) -> f32 {
let config = GeoffreyAPIConfig { let config = GeoffreyAPIConfig {
db_path: PathBuf::from("test_db/"), server_config: ServerConfig {
host: "".to_string(), db_path: PathBuf::from("test_db/"),
max_str_len: 64, host: "".to_string(),
},
geoffrey_settings: GeoffreySettings::default(),
}; };
let ctx = Context::new(config.clone(), Args::default()).unwrap(); let ctx = Context::new(config.clone(), Args::default()).unwrap();

View File

@ -0,0 +1,31 @@
use crate::commands::{Command, RequestType};
use crate::context::Context;
use crate::Result;
use geoffrey_models::models::parameters::EmptyRequest;
use geoffrey_models::models::player::Player;
use geoffrey_models::models::settings::GeoffreySettings;
use geoffrey_models::models::CommandLevel;
use std::sync::Arc;
pub struct Settings {}
impl Command for Settings {
type Req = EmptyRequest;
type Resp = GeoffreySettings;
fn command_name() -> String {
"settings".to_string()
}
fn request_type() -> RequestType {
RequestType::GET
}
fn command_level() -> CommandLevel {
CommandLevel::ALL
}
fn run_command(ctx: Arc<Context>, _: &Self::Req, _: Option<Player>) -> Result<Self::Resp> {
Ok(ctx.cfg.geoffrey_settings.clone())
}
}

View File

@ -1,12 +1,18 @@
use config::{Config, ConfigError, File}; use config::{Config, ConfigError, File};
use geoffrey_models::models::settings::GeoffreySettings;
use serde::Deserialize; use serde::Deserialize;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct GeoffreyAPIConfig { pub struct GeoffreyAPIConfig {
pub server_config: ServerConfig,
pub geoffrey_settings: GeoffreySettings,
}
#[derive(Debug, Deserialize, Clone)]
pub struct ServerConfig {
pub db_path: PathBuf, pub db_path: PathBuf,
pub host: String, pub host: String,
pub max_str_len: usize,
} }
impl GeoffreyAPIConfig { impl GeoffreyAPIConfig {

View File

@ -11,7 +11,7 @@ pub struct Context {
impl Context { impl Context {
pub fn new(cfg: GeoffreyAPIConfig, args: Args) -> Result<Arc<Self>> { pub fn new(cfg: GeoffreyAPIConfig, args: Args) -> Result<Arc<Self>> {
let ctx = Self { let ctx = Self {
db: Database::new(cfg.db_path.as_path(), args.force_migration).unwrap(), db: Database::new(cfg.server_config.db_path.as_path(), args.force_migration).unwrap(),
cfg, cfg,
}; };

View File

@ -4,6 +4,8 @@ use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use warp::Filter;
use structopt::StructOpt; use structopt::StructOpt;
use geoffrey_models::logging::LogLevel; use geoffrey_models::logging::LogLevel;
@ -12,7 +14,7 @@ use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::token::Permissions; use geoffrey_models::models::token::Permissions;
use crate::commands::add_token::AddToken; use crate::commands::add_token::AddToken;
use crate::commands::{command_filter, Command}; use crate::commands::{command_filter, model_filter, Command};
use crate::config::GeoffreyAPIConfig; use crate::config::GeoffreyAPIConfig;
use crate::context::Context; use crate::context::Context;
use crate::logging::init_logging; use crate::logging::init_logging;
@ -65,15 +67,19 @@ pub struct CreateTokenCommand {
} }
async fn run_server(ctx: Arc<Context>) { async fn run_server(ctx: Arc<Context>) {
let socket_addr = match SocketAddr::from_str(ctx.cfg.host.as_str()) { let socket_addr = match SocketAddr::from_str(ctx.cfg.server_config.host.as_str()) {
Ok(socket_addr) => socket_addr, Ok(socket_addr) => socket_addr,
Err(e) => { Err(e) => {
log::warn!("Error parsing {} as address: {}", ctx.cfg.host, e); log::warn!(
"Error parsing {} as address: {}",
ctx.cfg.server_config.host,
e
);
return; return;
} }
}; };
let api = command_filter(ctx.clone()); let api = command_filter(ctx.clone()).or(model_filter(ctx.clone()));
warp::serve(api).run(socket_addr).await; warp::serve(api).run(socket_addr).await;
} }

View File

@ -0,0 +1,33 @@
use crate::context::GeoffreyContext;
use crate::error::BotError;
use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam};
use geoffrey_models::models::response::APIResponse;
use reqwest::Method;
use serde::de::DeserializeOwned;
pub fn build_url(base_str: &str, end_point: &str) -> String {
let slash = if !base_str.ends_with('/') { "/" } else { "" };
format!("{}{}{}", base_str, slash, end_point)
}
pub async fn run_api_query<T: GeoffreyParam, U: DeserializeOwned>(
ctx: &GeoffreyContext,
param: &CommandRequest<T>,
method: Method,
endpoint: &str,
) -> Result<U, BotError> {
let resp: APIResponse<U> = ctx
.http_client
.request(method, build_url(&ctx.cfg.api.base_url, endpoint))
.json(param)
.send()
.await?
.json()
.await?;
match resp {
APIResponse::Response(resp) => Ok(resp),
APIResponse::Error { error: e, .. } => Err(e.into()),
}
}

View File

@ -1,4 +1,4 @@
use crate::bot::commands::CommandError; use crate::error::BotError;
use geoffrey_models::models::locations::LocationType; use geoffrey_models::models::locations::LocationType;
use geoffrey_models::models::parameters::selling_params::{ItemSort, Order}; use geoffrey_models::models::parameters::selling_params::{ItemSort, Order};
use geoffrey_models::models::Dimension; use geoffrey_models::models::Dimension;
@ -12,7 +12,7 @@ use std::str::FromStr;
pub fn option_to_i64( pub fn option_to_i64(
option: Option<&ApplicationCommandInteractionDataOption>, option: Option<&ApplicationCommandInteractionDataOption>,
field: &str, field: &str,
) -> Result<i64, CommandError> { ) -> Result<i64, BotError> {
if let Some(option) = option { if let Some(option) = option {
let option = option.resolved.as_ref(); let option = option.resolved.as_ref();
@ -20,13 +20,13 @@ pub fn option_to_i64(
return Ok(*i); return Ok(*i);
} }
} }
Err(CommandError::ArgumentParse(field.to_string())) Err(BotError::ArgumentParse(field.to_string()))
} }
pub fn option_to_string( pub fn option_to_string(
option: Option<&ApplicationCommandInteractionDataOption>, option: Option<&ApplicationCommandInteractionDataOption>,
field: &str, field: &str,
) -> Result<String, CommandError> { ) -> Result<String, BotError> {
if let Some(option) = option { if let Some(option) = option {
let option = option.resolved.as_ref(); let option = option.resolved.as_ref();
@ -34,43 +34,43 @@ pub fn option_to_string(
return Ok(s.clone()); return Ok(s.clone());
} }
} }
Err(CommandError::ArgumentParse(field.to_string())) Err(BotError::ArgumentParse(field.to_string()))
} }
pub fn option_to_loc_type( pub fn option_to_loc_type(
option: Option<&ApplicationCommandInteractionDataOption>, option: Option<&ApplicationCommandInteractionDataOption>,
field: &str, field: &str,
) -> Result<LocationType, CommandError> { ) -> Result<LocationType, BotError> {
let loc_type = option_to_string(option, field)?; let loc_type = option_to_string(option, field)?;
LocationType::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) LocationType::from_str(&loc_type).map_err(|_| BotError::ArgumentParse(field.to_string()))
} }
pub fn option_to_dim( pub fn option_to_dim(
option: Option<&ApplicationCommandInteractionDataOption>, option: Option<&ApplicationCommandInteractionDataOption>,
field: &str, field: &str,
) -> Result<Dimension, CommandError> { ) -> Result<Dimension, BotError> {
let loc_type = option_to_string(option, field)?; let loc_type = option_to_string(option, field)?;
Dimension::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) Dimension::from_str(&loc_type).map_err(|_| BotError::ArgumentParse(field.to_string()))
} }
pub fn option_to_sort( pub fn option_to_sort(
option: Option<&ApplicationCommandInteractionDataOption>, option: Option<&ApplicationCommandInteractionDataOption>,
field: &str, field: &str,
) -> Result<ItemSort, CommandError> { ) -> Result<ItemSort, BotError> {
let loc_type = option_to_string(option, field)?; let loc_type = option_to_string(option, field)?;
ItemSort::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) ItemSort::from_str(&loc_type).map_err(|_| BotError::ArgumentParse(field.to_string()))
} }
pub fn option_to_order( pub fn option_to_order(
option: Option<&ApplicationCommandInteractionDataOption>, option: Option<&ApplicationCommandInteractionDataOption>,
field: &str, field: &str,
) -> Result<Order, CommandError> { ) -> Result<Order, BotError> {
let loc_type = option_to_string(option, field)?; let loc_type = option_to_string(option, field)?;
Order::from_str(&loc_type).map_err(|_| CommandError::ArgumentParse(field.to_string())) Order::from_str(&loc_type).map_err(|_| BotError::ArgumentParse(field.to_string()))
} }
pub fn add_x_position_argument( pub fn add_x_position_argument(

View File

@ -1,18 +1,20 @@
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use geoffrey_models::models::locations::Location; use geoffrey_models::models::locations::Location;
use geoffrey_models::models::parameters::add_item_params::AddItemParams; use geoffrey_models::models::parameters::add_item_params::AddItemParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use crate::bot::arg_parse::{option_to_i64, option_to_string}; use crate::bot::arg_parse::{option_to_i64, option_to_string};
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::BotCommand;
use crate::bot::formatters::display_loc_full; use crate::bot::formatters::display_loc_full;
use crate::bot::lang::{PLAYER_ALREADY_SELLS_ITEM, PLAYER_DOES_NOT_HAVE_MATCHING_SHOP}; use crate::bot::lang::{PLAYER_ALREADY_SELLS_ITEM, PLAYER_DOES_NOT_HAVE_MATCHING_SHOP};
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use crate::context::GeoffreyContext;
use serenity::builder::CreateApplicationCommand; use crate::error::BotError;
pub struct AddItemCommand; pub struct AddItemCommand;
@ -29,12 +31,12 @@ impl BotCommand for AddItemCommand {
Method::POST Method::POST
} }
fn custom_err_resp(e: &CommandError) -> Option<String> { fn custom_err_resp(e: &BotError) -> Option<String> {
match e { match e {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(PLAYER_DOES_NOT_HAVE_MATCHING_SHOP.to_string()) Some(PLAYER_DOES_NOT_HAVE_MATCHING_SHOP.to_string())
} }
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotUnique) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotUnique) => {
Some(PLAYER_ALREADY_SELLS_ITEM.to_string()) Some(PLAYER_ALREADY_SELLS_ITEM.to_string())
} }
_ => None, _ => None,
@ -79,7 +81,7 @@ impl BotCommand for AddItemCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
Ok(Self::ApiParams::new( Ok(Self::ApiParams::new(
@ -90,12 +92,12 @@ impl BotCommand for AddItemCommand {
)) ))
} }
fn build_response(resp: Self::ApiResp, req: Self::ApiParams) -> String { fn build_response(ctx: &GeoffreyContext, resp: Self::ApiResp, req: Self::ApiParams) -> String {
format!( format!(
"**{}** has been added to {} :\n{}", "**{}** has been added to {} :\n{}",
req.item_name, req.item_name,
resp.name, resp.name,
display_loc_full(&resp) display_loc_full(&resp, &ctx.settings)
) )
} }
} }

View File

@ -1,19 +1,22 @@
use async_trait::async_trait; 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 reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use geoffrey_models::models::locations::{Location, LocationType};
use geoffrey_models::models::parameters::add_location_params::AddLocationParams;
use geoffrey_models::models::{Dimension, Position};
use crate::bot::arg_parse::{ use crate::bot::arg_parse::{
add_dimension_argument, add_x_position_argument, add_y_position_argument, add_dimension_argument, add_x_position_argument, add_y_position_argument,
add_z_position_argument, option_to_dim, option_to_i64, option_to_loc_type, option_to_string, add_z_position_argument, option_to_dim, option_to_i64, option_to_loc_type, option_to_string,
}; };
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::BotCommand;
use crate::bot::formatters::display_loc_full; use crate::bot::formatters::display_loc_full;
use serenity::builder::CreateApplicationCommand; use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct AddLocationCommand; pub struct AddLocationCommand;
@ -62,7 +65,7 @@ impl BotCommand for AddLocationCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let loc_type = option_to_loc_type(options.get(0), "loc_type")?; let loc_type = option_to_loc_type(options.get(0), "loc_type")?;
let name = option_to_string(options.get(1), "name")?; let name = option_to_string(options.get(1), "name")?;
@ -76,11 +79,11 @@ impl BotCommand for AddLocationCommand {
Ok(Self::ApiParams::new(name, position, loc_type, None)) Ok(Self::ApiParams::new(name, position, loc_type, None))
} }
fn build_response(resp: Self::ApiResp, _: Self::ApiParams) -> String { fn build_response(ctx: &GeoffreyContext, resp: Self::ApiResp, _: Self::ApiParams) -> String {
format!( format!(
"**{}** has been added to Geoffrey:\n{}", "**{}** has been added to Geoffrey:\n{}",
resp.name, resp.name,
display_loc_full(&resp) display_loc_full(&resp, &ctx.settings)
) )
} }
} }

View File

@ -1,16 +1,19 @@
use async_trait::async_trait; use async_trait::async_trait;
use geoffrey_models::models::locations::Location;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use crate::bot::arg_parse::option_to_string; use geoffrey_models::models::locations::Location;
use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_LOC;
use geoffrey_models::models::parameters::delete_params::DeleteParams; use geoffrey_models::models::parameters::delete_params::DeleteParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::BotCommand;
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_LOC;
use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct DeleteCommand; pub struct DeleteCommand;
@ -27,9 +30,9 @@ impl BotCommand for DeleteCommand {
Method::POST Method::POST
} }
fn custom_err_resp(err: &CommandError) -> Option<String> { fn custom_err_resp(err: &BotError) -> Option<String> {
match err { match err {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(PLAYER_DOES_NOT_HAVE_MATCHING_LOC.to_string()) Some(PLAYER_DOES_NOT_HAVE_MATCHING_LOC.to_string())
} }
_ => None, _ => None,
@ -51,14 +54,14 @@ impl BotCommand for DeleteCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let loc_name = option_to_string(options.get(0), "loc_name")?; let loc_name = option_to_string(options.get(0), "loc_name")?;
Ok(Self::ApiParams::new(loc_name)) Ok(Self::ApiParams::new(loc_name))
} }
fn build_response(resp: Self::ApiResp, _: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, _: Self::ApiParams) -> String {
format!( format!(
"**{}** has been been removed from Geoffrey, good riddance!", "**{}** has been been removed from Geoffrey, good riddance!",
resp.name resp.name

View File

@ -1,14 +1,17 @@
use async_trait::async_trait; use async_trait::async_trait;
use geoffrey_models::models::locations::Location;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use crate::bot::arg_parse::option_to_string; use geoffrey_models::models::locations::Location;
use crate::bot::commands::{BotCommand, CommandError};
use geoffrey_models::models::parameters::edit_params::EditParams; use geoffrey_models::models::parameters::edit_params::EditParams;
use serenity::builder::CreateApplicationCommand;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::BotCommand;
use crate::context::GeoffreyContext;
use crate::error::BotError;
//TODO: Combine edit commands into one class once I figure out why subcommand are not working //TODO: Combine edit commands into one class once I figure out why subcommand are not working
pub struct EditNameCommand; pub struct EditNameCommand;
@ -22,7 +25,7 @@ impl BotCommand for EditNameCommand {
"edit_name".to_string() "edit_name".to_string()
} }
fn endpoint_name() -> String { fn endpoint() -> String {
"edit".to_string() "edit".to_string()
} }
@ -52,14 +55,14 @@ impl BotCommand for EditNameCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let name = option_to_string(options.get(0), "loc_name")?; let name = option_to_string(options.get(0), "loc_name")?;
let new_name = option_to_string(options.get(1), "new_name")?; let new_name = option_to_string(options.get(1), "new_name")?;
Ok(Self::ApiParams::new(name, None, Some(new_name))) Ok(Self::ApiParams::new(name, None, Some(new_name)))
} }
fn build_response(resp: Self::ApiResp, args: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, args: Self::ApiParams) -> String {
format!("**{}** has been renamed to {}", args.loc_name, resp.name,) format!("**{}** has been renamed to {}", args.loc_name, resp.name,)
} }
} }

View File

@ -1,18 +1,21 @@
use async_trait::async_trait; use async_trait::async_trait;
use geoffrey_models::models::locations::Location;
use geoffrey_models::models::{Dimension, Position};
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use geoffrey_models::models::locations::Location;
use geoffrey_models::models::parameters::edit_params::EditParams;
use geoffrey_models::models::{Dimension, Position};
use crate::bot::arg_parse::{ use crate::bot::arg_parse::{
add_dimension_argument, add_x_position_argument, add_y_position_argument, add_dimension_argument, add_x_position_argument, add_y_position_argument,
add_z_position_argument, option_to_dim, option_to_i64, option_to_string, add_z_position_argument, option_to_dim, option_to_i64, option_to_string,
}; };
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::BotCommand;
use geoffrey_models::models::parameters::edit_params::EditParams; use crate::context::GeoffreyContext;
use serenity::builder::CreateApplicationCommand; use crate::error::BotError;
//TODO: Combine edit commands into one class once I figure out why subcommand are not working //TODO: Combine edit commands into one class once I figure out why subcommand are not working
pub struct EditPosCommand; pub struct EditPosCommand;
@ -26,7 +29,7 @@ impl BotCommand for EditPosCommand {
"edit_pos".to_string() "edit_pos".to_string()
} }
fn endpoint_name() -> String { fn endpoint() -> String {
"edit".to_string() "edit".to_string()
} }
@ -53,7 +56,7 @@ impl BotCommand for EditPosCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let name = option_to_string(options.get(0), "loc_name")?; let name = option_to_string(options.get(0), "loc_name")?;
@ -66,7 +69,7 @@ impl BotCommand for EditPosCommand {
Ok(Self::ApiParams::new(name, Some(position), None)) Ok(Self::ApiParams::new(name, Some(position), None))
} }
fn build_response(resp: Self::ApiResp, _: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, _: Self::ApiParams) -> String {
format!("**{}** has been moved to {}", resp.name, resp.position) format!("**{}** has been moved to {}", resp.name, resp.position)
} }
} }

View File

@ -1,17 +1,20 @@
use std::fmt::Write;
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use std::fmt::Write;
use geoffrey_models::models::locations::Location; use geoffrey_models::models::locations::Location;
use geoffrey_models::models::parameters::find_params::FindParams; use geoffrey_models::models::parameters::find_params::FindParams;
use crate::bot::arg_parse::option_to_string; use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::BotCommand;
use crate::bot::formatters::display_loc; use crate::bot::formatters::display_loc;
use serenity::builder::CreateApplicationCommand; use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct FindCommand; pub struct FindCommand;
@ -43,21 +46,21 @@ impl BotCommand for FindCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let query = option_to_string(options.get(0), "query")?; let query = option_to_string(options.get(0), "query")?;
Ok(FindParams::new(query)) Ok(FindParams::new(query))
} }
fn build_response(resp: Self::ApiResp, _: Self::ApiParams) -> String { fn build_response(ctx: &GeoffreyContext, resp: Self::ApiResp, _: Self::ApiParams) -> String {
if resp.is_empty() { if resp.is_empty() {
"No locations match that query, try better next time ding dong".to_string() "No locations match that query, try better next time ding dong".to_string()
} else { } else {
let mut resp_str = String::new(); let mut resp_str = String::new();
writeln!(resp_str, "The following locations match:").unwrap(); writeln!(resp_str, "The following locations match:").unwrap();
for loc in resp { for loc in resp {
writeln!(resp_str, "{}", display_loc(&loc)).unwrap(); writeln!(resp_str, "{}", display_loc(&loc, &ctx.settings)).unwrap();
} }
resp_str resp_str
} }

View File

@ -1,18 +1,20 @@
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use geoffrey_models::models::locations::Location; use geoffrey_models::models::locations::Location;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::formatters::display_loc_full;
use crate::bot::lang::NO_LOCATION_FOUND;
use geoffrey_models::models::parameters::info_params::InfoParams; use geoffrey_models::models::parameters::info_params::InfoParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::BotCommand;
use crate::bot::formatters::display_loc_full;
use crate::bot::lang::NO_LOCATION_FOUND;
use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct InfoCommand; pub struct InfoCommand;
@ -29,9 +31,9 @@ impl BotCommand for InfoCommand {
Method::GET Method::GET
} }
fn custom_err_resp(err: &CommandError) -> Option<String> { fn custom_err_resp(err: &BotError) -> Option<String> {
match err { match err {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(NO_LOCATION_FOUND.to_string()) Some(NO_LOCATION_FOUND.to_string())
} }
_ => None, _ => None,
@ -53,14 +55,14 @@ impl BotCommand for InfoCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let location_name = option_to_string(options.get(0), "loc_name")?; let location_name = option_to_string(options.get(0), "loc_name")?;
Ok(Self::ApiParams::new(location_name)) Ok(Self::ApiParams::new(location_name))
} }
fn build_response(resp: Self::ApiResp, _: Self::ApiParams) -> String { fn build_response(ctx: &GeoffreyContext, resp: Self::ApiResp, _: Self::ApiParams) -> String {
display_loc_full(&resp) display_loc_full(&resp, &ctx.settings)
} }
} }

View File

@ -1,21 +1,20 @@
use std::fmt::{Debug, Display, Formatter}; use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Error;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::ApplicationCommandInteraction; use serenity::model::interactions::application_command::ApplicationCommandInteraction;
use serenity::Error as SerenityError;
use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam}; use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam};
use geoffrey_models::models::player::UserID; use geoffrey_models::models::player::UserID;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::response::APIResponse;
use crate::api::run_api_query;
use crate::context::GeoffreyContext; use crate::context::GeoffreyContext;
use serenity::builder::CreateApplicationCommand; use crate::error::BotError;
use std::future::Future;
use std::pin::Pin;
pub mod add_item; pub mod add_item;
pub mod add_location; pub mod add_location;
@ -39,47 +38,6 @@ pub type GeoffreyCommandFn = Box<
) -> Pin<Box<dyn Future<Output = String> + Send>>, ) -> Pin<Box<dyn Future<Output = String> + Send>>,
>; >;
#[derive(Debug)]
pub enum CommandError {
ArgumentParse(String),
GeoffreyApi(GeoffreyAPIError),
Serenity(serenity::Error),
Reqwest(reqwest::Error),
CommandNotFound(String),
}
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),
CommandError::CommandNotFound(err) => format!("'{}' not found!", 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] #[async_trait]
pub trait BotCommand: Send + 'static { pub trait BotCommand: Send + 'static {
type ApiParams: GeoffreyParam; type ApiParams: GeoffreyParam;
@ -87,56 +45,33 @@ pub trait BotCommand: Send + 'static {
fn command_name() -> String; fn command_name() -> String;
fn endpoint_name() -> String { fn endpoint() -> String {
Self::command_name() format!("command/{}/", Self::command_name())
} }
fn request_type() -> reqwest::Method; fn request_type() -> reqwest::Method;
fn command_url(base_string: &str) -> String { fn get_err_resp(err: BotError) -> String {
let slash = if !base_string.ends_with('/') { "/" } else { "" };
format!("{}{}command/{}/", base_string, slash, Self::endpoint_name())
}
async fn run_api_query(
ctx: GeoffreyContext,
params: CommandRequest<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) { if let Some(resp) = Self::custom_err_resp(&err) {
resp resp
} else { } else {
match err { match err {
CommandError::GeoffreyApi(GeoffreyAPIError::PlayerNotRegistered) => { BotError::GeoffreyApi(GeoffreyAPIError::PlayerNotRegistered) => {
"You need to register before using this command!".to_string() "You need to register before using this command!".to_string()
} }
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::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) => { BotError::GeoffreyApi(GeoffreyAPIError::PermissionInsufficient) => {
"Looks like you don't have permission for that.".to_string() "Looks like you don't have permission for that.".to_string()
} }
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotUnique) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotUnique) => {
"Slow down, I already know that thing. Try a new name.".to_string() "Slow down, I already know that thing. Try a new name.".to_string()
} }
CommandError::GeoffreyApi(GeoffreyAPIError::MultipleLocationsMatch) => { BotError::GeoffreyApi(GeoffreyAPIError::MultipleLocationsMatch) => {
"I couldn't match a single location, narrow down your search".to_string() "I couldn't match a single location, narrow down your search".to_string()
} }
CommandError::GeoffreyApi(GeoffreyAPIError::ParameterInvalid(err)) => { BotError::GeoffreyApi(GeoffreyAPIError::ParameterInvalid(err)) => {
format!( format!(
"Welp, you some how messed up the {} parameter, great job", "Welp, you some how messed up the {} parameter, great job",
err err
@ -151,7 +86,7 @@ pub trait BotCommand: Send + 'static {
} }
} }
fn custom_err_resp(_: &CommandError) -> Option<String> { fn custom_err_resp(_: &BotError) -> Option<String> {
None None
} }
@ -159,13 +94,13 @@ pub trait BotCommand: Send + 'static {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError>; ) -> Result<Self::ApiParams, BotError>;
async fn run_command( async fn run_command(
ctx: GeoffreyContext, ctx: GeoffreyContext,
user_id: UserID, user_id: UserID,
command_interact: ApplicationCommandInteraction, command_interact: ApplicationCommandInteraction,
) -> Result<String, CommandError> { ) -> Result<String, BotError> {
let args = Self::process_arguments(command_interact).await?; let args = Self::process_arguments(command_interact).await?;
log::info!( log::info!(
@ -180,12 +115,9 @@ pub trait BotCommand: Send + 'static {
params: args.clone(), params: args.clone(),
}; };
let resp = Self::run_api_query(ctx, request).await?; let resp = run_api_query(&ctx, &request, Self::request_type(), &Self::endpoint()).await?;
match resp { Ok(Self::build_response(&ctx, resp, args))
APIResponse::Response(resp) => Ok(Self::build_response(resp, args)),
APIResponse::Error { error: err, .. } => Err(CommandError::GeoffreyApi(err)),
}
} }
async fn command( async fn command(
@ -199,5 +131,5 @@ pub trait BotCommand: Send + 'static {
} }
} }
fn build_response(resp: Self::ApiResp, req: Self::ApiParams) -> String; fn build_response(ctx: &GeoffreyContext, resp: Self::ApiResp, req: Self::ApiParams) -> String;
} }

View File

@ -1,16 +1,19 @@
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::lang::ACCOUNT_LINK_INVALID;
use geoffrey_models::models::parameters::register_params::RegisterParameters; use geoffrey_models::models::parameters::register_params::RegisterParameters;
use geoffrey_models::models::player::{Player, UserID}; use geoffrey_models::models::player::{Player, UserID};
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::BotCommand;
use crate::bot::lang::ACCOUNT_LINK_INVALID;
use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct RegisterCommand; pub struct RegisterCommand;
@ -27,9 +30,9 @@ impl BotCommand for RegisterCommand {
Method::POST Method::POST
} }
fn custom_err_resp(err: &CommandError) -> Option<String> { fn custom_err_resp(err: &BotError) -> Option<String> {
match err { match err {
CommandError::GeoffreyApi(GeoffreyAPIError::AccountLinkInvalid) => { BotError::GeoffreyApi(GeoffreyAPIError::AccountLinkInvalid) => {
Some(ACCOUNT_LINK_INVALID.to_string()) Some(ACCOUNT_LINK_INVALID.to_string())
} }
_ => None, _ => None,
@ -51,7 +54,7 @@ impl BotCommand for RegisterCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let link_code = option_to_string(options.get(0), "link_code")?; let link_code = option_to_string(options.get(0), "link_code")?;
@ -68,7 +71,7 @@ impl BotCommand for RegisterCommand {
Ok(register) Ok(register)
} }
fn build_response(resp: Self::ApiResp, _: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, _: Self::ApiParams) -> String {
format!( format!(
"**{}**, you have been registered for the Geoffrey bot!", "**{}**, you have been registered for the Geoffrey bot!",
resp.name resp.name

View File

@ -1,17 +1,19 @@
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use geoffrey_models::models::locations::Location; use geoffrey_models::models::locations::Location;
use geoffrey_models::models::parameters::item_command_params::ItemCommandParams; use geoffrey_models::models::parameters::item_command_params::ItemCommandParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use crate::bot::arg_parse::option_to_string; use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::BotCommand;
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_SHOP; use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_SHOP;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use crate::context::GeoffreyContext;
use serenity::builder::CreateApplicationCommand; use crate::error::BotError;
pub struct RemoveItemCommand; pub struct RemoveItemCommand;
@ -28,9 +30,9 @@ impl BotCommand for RemoveItemCommand {
Method::POST Method::POST
} }
fn custom_err_resp(e: &CommandError) -> Option<String> { fn custom_err_resp(e: &BotError) -> Option<String> {
match e { match e {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(PLAYER_DOES_NOT_HAVE_MATCHING_SHOP.to_string()) Some(PLAYER_DOES_NOT_HAVE_MATCHING_SHOP.to_string())
} }
_ => None, _ => None,
@ -59,7 +61,7 @@ impl BotCommand for RemoveItemCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
Ok(Self::ApiParams::new( Ok(Self::ApiParams::new(
@ -68,7 +70,7 @@ impl BotCommand for RemoveItemCommand {
)) ))
} }
fn build_response(resp: Self::ApiResp, args: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, args: Self::ApiParams) -> String {
format!( format!(
"**{}** has been removed from **{}**", "**{}** has been removed from **{}**",
args.item_name, resp.name args.item_name, resp.name

View File

@ -1,17 +1,19 @@
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use geoffrey_models::models::locations::Location; use geoffrey_models::models::locations::Location;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::lang::NO_LOCATION_FOUND;
use geoffrey_models::models::parameters::item_command_params::ItemCommandParams; use geoffrey_models::models::parameters::item_command_params::ItemCommandParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::BotCommand;
use crate::bot::lang::NO_LOCATION_FOUND;
use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct ReportOutOfStockCommand; pub struct ReportOutOfStockCommand;
@ -28,9 +30,9 @@ impl BotCommand for ReportOutOfStockCommand {
Method::POST Method::POST
} }
fn custom_err_resp(e: &CommandError) -> Option<String> { fn custom_err_resp(e: &BotError) -> Option<String> {
match e { match e {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(NO_LOCATION_FOUND.to_string()) Some(NO_LOCATION_FOUND.to_string())
} }
_ => None, _ => None,
@ -59,7 +61,7 @@ impl BotCommand for ReportOutOfStockCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
Ok(Self::ApiParams::new( Ok(Self::ApiParams::new(
@ -68,7 +70,7 @@ impl BotCommand for ReportOutOfStockCommand {
)) ))
} }
fn build_response(resp: Self::ApiResp, args: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, args: Self::ApiParams) -> String {
format!( format!(
"**{}** has been reported out of stock at {}", "**{}** has been reported out of stock at {}",
args.item_name, resp.name args.item_name, resp.name

View File

@ -1,17 +1,19 @@
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use geoffrey_models::models::locations::Location; use geoffrey_models::models::locations::Location;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_SHOP;
use geoffrey_models::models::parameters::item_command_params::ItemCommandParams; use geoffrey_models::models::parameters::item_command_params::ItemCommandParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::BotCommand;
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_SHOP;
use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct RestockCommand; pub struct RestockCommand;
@ -28,9 +30,9 @@ impl BotCommand for RestockCommand {
Method::POST Method::POST
} }
fn custom_err_resp(e: &CommandError) -> Option<String> { fn custom_err_resp(e: &BotError) -> Option<String> {
match e { match e {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(PLAYER_DOES_NOT_HAVE_MATCHING_SHOP.to_string()) Some(PLAYER_DOES_NOT_HAVE_MATCHING_SHOP.to_string())
} }
_ => None, _ => None,
@ -59,7 +61,7 @@ impl BotCommand for RestockCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
Ok(Self::ApiParams::new( Ok(Self::ApiParams::new(
@ -68,7 +70,7 @@ impl BotCommand for RestockCommand {
)) ))
} }
fn build_response(resp: Self::ApiResp, args: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, args: Self::ApiParams) -> String {
format!( format!(
"**{}** has been restocked at **{}**", "**{}** has been restocked at **{}**",
args.item_name, resp.name args.item_name, resp.name

View File

@ -2,6 +2,7 @@ use std::fmt::Write;
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
@ -10,9 +11,10 @@ use geoffrey_models::models::parameters::selling_params::{ItemSort, Order, Selli
use geoffrey_models::models::response::selling_listing::SellingListing; 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::arg_parse::{option_to_order, option_to_sort, option_to_string};
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::BotCommand;
use crate::bot::formatters::display_item_listing; use crate::bot::formatters::display_item_listing;
use serenity::builder::CreateApplicationCommand; use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct SellingCommand; pub struct SellingCommand;
@ -62,7 +64,7 @@ impl BotCommand for SellingCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let query = option_to_string(options.get(0), "query")?; let query = option_to_string(options.get(0), "query")?;
let sort = option_to_sort(options.get(1), "sort").ok(); let sort = option_to_sort(options.get(1), "sort").ok();
@ -71,7 +73,7 @@ impl BotCommand for SellingCommand {
Ok(SellingParams::new(query, sort, order)) Ok(SellingParams::new(query, sort, order))
} }
fn build_response(resp: Self::ApiResp, args: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, args: Self::ApiParams) -> String {
if resp.is_empty() { if resp.is_empty() {
"No shops were found selling that, maybe I should start selling it...".to_string() "No shops were found selling that, maybe I should start selling it...".to_string()
} else { } else {

View File

@ -1,17 +1,20 @@
use async_trait::async_trait; use async_trait::async_trait;
use geoffrey_models::models::locations::Location;
use geoffrey_models::models::Portal;
use reqwest::Method; use reqwest::Method;
use serenity::builder::CreateApplicationCommand;
use serenity::model::interactions::application_command::{ use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType, ApplicationCommandInteraction, ApplicationCommandOptionType,
}; };
use crate::bot::arg_parse::{option_to_i64, option_to_string}; use geoffrey_models::models::locations::Location;
use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_LOC;
use geoffrey_models::models::parameters::set_portal_params::SetPortalParams; use geoffrey_models::models::parameters::set_portal_params::SetPortalParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand; use geoffrey_models::models::Portal;
use crate::bot::arg_parse::{option_to_i64, option_to_string};
use crate::bot::commands::BotCommand;
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_LOC;
use crate::context::GeoffreyContext;
use crate::error::BotError;
pub struct SetPortalCommand; pub struct SetPortalCommand;
@ -28,9 +31,9 @@ impl BotCommand for SetPortalCommand {
Method::POST Method::POST
} }
fn custom_err_resp(err: &CommandError) -> Option<String> { fn custom_err_resp(err: &BotError) -> Option<String> {
match err { match err {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => { BotError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(PLAYER_DOES_NOT_HAVE_MATCHING_LOC.to_string()) Some(PLAYER_DOES_NOT_HAVE_MATCHING_LOC.to_string())
} }
_ => None, _ => None,
@ -70,7 +73,7 @@ impl BotCommand for SetPortalCommand {
async fn process_arguments( async fn process_arguments(
command_interaction: ApplicationCommandInteraction, command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> { ) -> Result<Self::ApiParams, BotError> {
let options = command_interaction.data.options; let options = command_interaction.data.options;
let loc_name = option_to_string(options.get(0), "name")?; let loc_name = option_to_string(options.get(0), "name")?;
let x_portal = option_to_i64(options.get(1), "x_portal")?; let x_portal = option_to_i64(options.get(1), "x_portal")?;
@ -81,7 +84,7 @@ impl BotCommand for SetPortalCommand {
Ok(Self::ApiParams::new(loc_name, portal)) Ok(Self::ApiParams::new(loc_name, portal))
} }
fn build_response(resp: Self::ApiResp, _: Self::ApiParams) -> String { fn build_response(_: &GeoffreyContext, resp: Self::ApiResp, _: Self::ApiParams) -> String {
let portal = match resp.portal { let portal = match resp.portal {
None => return "Portal could not be set, try again!".to_string(), None => return "Portal could not be set, try again!".to_string(),
Some(p) => p, Some(p) => p,

View File

@ -2,9 +2,10 @@ use chrono::{Duration, Utc};
use geoffrey_models::models::item::ItemListing; use geoffrey_models::models::item::ItemListing;
use geoffrey_models::models::locations::{Location, LocationData}; use geoffrey_models::models::locations::{Location, LocationData};
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
use geoffrey_models::models::settings::GeoffreySettings;
use geoffrey_models::models::Portal; use geoffrey_models::models::Portal;
pub fn display_owners(owners: &[Player], limit: usize) -> String { pub fn display_owners(owners: &[Player], settings: &GeoffreySettings) -> String {
let mut plural = ""; let mut plural = "";
let mut ellipses = ""; let mut ellipses = "";
@ -12,9 +13,9 @@ pub fn display_owners(owners: &[Player], limit: usize) -> String {
plural = "s" plural = "s"
} }
let range = if owners.len() > limit { let range = if owners.len() > settings.max_owners_to_display as usize {
ellipses = "..."; ellipses = "...";
limit settings.max_owners_to_display as usize
} else { } else {
owners.len() owners.len()
}; };
@ -41,7 +42,7 @@ pub fn display_portal(portal: &Portal) -> String {
) )
} }
pub fn display_loc(loc: &Location) -> String { pub fn display_loc(loc: &Location, settings: &GeoffreySettings) -> String {
let portal_str = match &loc.portal { let portal_str = match &loc.portal {
None => "".to_string(), None => "".to_string(),
Some(p) => format!("{}, ", display_portal(p)), Some(p) => format!("{}, ", display_portal(p)),
@ -52,7 +53,7 @@ pub fn display_loc(loc: &Location) -> String {
loc.name, loc.name,
loc.position, loc.position,
portal_str, portal_str,
display_owners(&loc.owners, 3), display_owners(&loc.owners, settings),
) )
} }
@ -79,7 +80,7 @@ pub fn display_item_listing(listing: &ItemListing) -> String {
} }
} }
pub fn display_loc_full(loc: &Location) -> String { pub fn display_loc_full(loc: &Location, settings: &GeoffreySettings) -> String {
let info = match &loc.loc_data { let info = match &loc.loc_data {
LocationData::Shop(shop) => { LocationData::Shop(shop) => {
if !shop.item_listings.is_empty() { if !shop.item_listings.is_empty() {
@ -98,5 +99,5 @@ pub fn display_loc_full(loc: &Location) -> String {
_ => "".to_string(), _ => "".to_string(),
}; };
format!("{}\n{}", display_loc(loc), info) format!("{}\n{}", display_loc(loc, settings), info)
} }

View File

@ -1,6 +1,17 @@
use std::collections::HashMap;
use serenity::model::interactions::application_command::ApplicationCommand; use serenity::model::interactions::application_command::ApplicationCommand;
use serenity::model::prelude::application_command::ApplicationCommandInteraction;
use serenity::prelude::*; use serenity::prelude::*;
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;
use geoffrey_models::models::player::UserID;
use crate::bot::commands::delete::DeleteCommand; use crate::bot::commands::delete::DeleteCommand;
use crate::bot::commands::edit_name::EditNameCommand; use crate::bot::commands::edit_name::EditNameCommand;
use crate::bot::commands::edit_pos::EditPosCommand; use crate::bot::commands::edit_pos::EditPosCommand;
@ -11,15 +22,7 @@ use crate::bot::commands::report_out_of_stock::ReportOutOfStockCommand;
use crate::bot::commands::restock::RestockCommand; use crate::bot::commands::restock::RestockCommand;
use crate::bot::commands::GeoffreyCommandFn; use crate::bot::commands::GeoffreyCommandFn;
use crate::context::GeoffreyContext; use crate::context::GeoffreyContext;
use commands::add_item::AddItemCommand; use crate::error::BotError;
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 arg_parse;
pub mod commands; pub mod commands;
@ -33,10 +36,7 @@ pub struct CommandRunner {
} }
impl CommandRunner { impl CommandRunner {
async fn register_app_command<T: BotCommand>( async fn register_app_command<T: BotCommand>(&mut self, ctx: &Context) -> Result<(), BotError> {
&mut self,
ctx: &Context,
) -> Result<(), CommandError> {
let command = ApplicationCommand::create_global_application_command(&ctx.http, |command| { let command = ApplicationCommand::create_global_application_command(&ctx.http, |command| {
T::create_app_command(command) T::create_app_command(command)
}) })
@ -55,7 +55,7 @@ impl CommandRunner {
pub async fn add_command<T: BotCommand>( pub async fn add_command<T: BotCommand>(
&mut self, &mut self,
ctx: &Context, ctx: &Context,
) -> Result<&mut Self, CommandError> { ) -> Result<&mut Self, BotError> {
self.add_command_to_lookup::<T>(); self.add_command_to_lookup::<T>();
self.register_app_command::<T>(ctx).await?; self.register_app_command::<T>(ctx).await?;
Ok(self) Ok(self)
@ -67,11 +67,11 @@ impl CommandRunner {
geoffrey_ctx: GeoffreyContext, geoffrey_ctx: GeoffreyContext,
user_id: UserID, user_id: UserID,
interaction: ApplicationCommandInteraction, interaction: ApplicationCommandInteraction,
) -> Result<String, CommandError> { ) -> Result<String, BotError> {
let command_fn = self let command_fn = self
.commands .commands
.get(command_name) .get(command_name)
.ok_or_else(|| CommandError::CommandNotFound(command_name.to_string()))?; .ok_or_else(|| BotError::CommandNotFound(command_name.to_string()))?;
Ok(command_fn(geoffrey_ctx, user_id, interaction).await) Ok(command_fn(geoffrey_ctx, user_id, interaction).await)
} }
@ -80,7 +80,7 @@ impl CommandRunner {
pub async fn build_commands( pub async fn build_commands(
ctx: &Context, ctx: &Context,
command_runner: &mut CommandRunner, command_runner: &mut CommandRunner,
) -> Result<(), CommandError> { ) -> Result<(), BotError> {
command_runner command_runner
.add_command::<AddItemCommand>(ctx) .add_command::<AddItemCommand>(ctx)
.await? .await?

View File

@ -1,10 +1,12 @@
use crate::configs::GeoffreyBotConfig; use crate::configs::GeoffreyBotConfig;
use geoffrey_models::models::settings::GeoffreySettings;
use serenity::prelude::TypeMapKey; use serenity::prelude::TypeMapKey;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GeoffreyContext { pub struct GeoffreyContext {
pub http_client: reqwest::Client, pub http_client: reqwest::Client,
pub cfg: GeoffreyBotConfig, pub cfg: GeoffreyBotConfig,
pub settings: GeoffreySettings,
} }
impl TypeMapKey for GeoffreyContext { impl TypeMapKey for GeoffreyContext {

View File

@ -0,0 +1,47 @@
use std::fmt::{Display, Formatter};
use reqwest::Error;
use serenity::Error as SerenityError;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
#[derive(Debug)]
pub enum BotError {
ArgumentParse(String),
GeoffreyApi(GeoffreyAPIError),
Serenity(serenity::Error),
Reqwest(reqwest::Error),
CommandNotFound(String),
}
impl Display for BotError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = match self {
BotError::ArgumentParse(s) => format!("Unable to parse argument '{}'", s),
BotError::GeoffreyApi(err) => format!("Got error from GeoffreyAPI: {}", err),
BotError::Serenity(err) => format!("Serenity Error: {}", err),
BotError::Reqwest(err) => format!("Reqwest Error: {}", err),
BotError::CommandNotFound(err) => format!("'{}' not found!", err),
};
write!(f, "{}", s)
}
}
impl From<GeoffreyAPIError> for BotError {
fn from(err: GeoffreyAPIError) -> Self {
Self::GeoffreyApi(err)
}
}
impl From<SerenityError> for BotError {
fn from(err: SerenityError) -> Self {
Self::Serenity(err)
}
}
impl From<reqwest::Error> for BotError {
fn from(err: Error) -> Self {
Self::Reqwest(err)
}
}

View File

@ -1,14 +1,6 @@
mod bot; use std::path::PathBuf;
mod configs;
mod context;
mod logging;
use crate::bot::{build_commands, CommandRunner}; use reqwest::Method;
use crate::configs::GeoffreyBotConfig;
use crate::context::GeoffreyContext;
use crate::logging::init_logging;
use geoffrey_models::logging::LogLevel;
use geoffrey_models::models::player::UserID;
use serenity::utils::{content_safe, ContentSafeOptions}; use serenity::utils::{content_safe, ContentSafeOptions};
use serenity::{ use serenity::{
async_trait, async_trait,
@ -18,9 +10,26 @@ use serenity::{
}, },
prelude::*, prelude::*,
}; };
use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
use geoffrey_models::logging::LogLevel;
use geoffrey_models::models::parameters::{CommandRequest, EmptyRequest};
use geoffrey_models::models::player::UserID;
use geoffrey_models::models::settings::GeoffreySettings;
use crate::api::run_api_query;
use crate::bot::{build_commands, CommandRunner};
use crate::configs::GeoffreyBotConfig;
use crate::context::GeoffreyContext;
use crate::logging::init_logging;
mod api;
mod bot;
mod configs;
mod context;
mod error;
mod logging;
#[derive(Debug, StructOpt, Clone)] #[derive(Debug, StructOpt, Clone)]
#[structopt(name = "GeoffreyBot", about = "Geoffrey Discord Bot")] #[structopt(name = "GeoffreyBot", about = "Geoffrey Discord Bot")]
struct Args { struct Args {
@ -42,6 +51,12 @@ impl TypeMapKey for HttpClient {
type Value = serenity::client::Client; type Value = serenity::client::Client;
} }
struct Settings;
impl TypeMapKey for Settings {
type Value = GeoffreySettings;
}
struct Handler; struct Handler;
#[async_trait] #[async_trait]
@ -51,6 +66,29 @@ impl EventHandler for Handler {
let mut data = ctx.data.write().await; let mut data = ctx.data.write().await;
let mut geoffrey_ctx = data.get_mut::<GeoffreyContext>().expect("Unable");
match run_api_query::<EmptyRequest, GeoffreySettings>(
geoffrey_ctx,
&CommandRequest {
token: geoffrey_ctx.cfg.api.token.clone(),
user_id: None,
params: EmptyRequest {},
},
Method::GET,
"model/settings/",
)
.await
{
Ok(settings) => geoffrey_ctx.settings = settings,
Err(err) => {
log::warn!(
"Unable to retrieve Geoffrey global settings, using defaults. Error: {}",
err
)
}
}
let command_runner = data let command_runner = data
.get_mut::<CommandRunner>() .get_mut::<CommandRunner>()
.expect("Unable to open command runner!"); .expect("Unable to open command runner!");
@ -154,6 +192,7 @@ async fn main() {
data.insert::<GeoffreyContext>(GeoffreyContext { data.insert::<GeoffreyContext>(GeoffreyContext {
http_client: reqwest::Client::new(), http_client: reqwest::Client::new(),
cfg, cfg,
settings: GeoffreySettings::default(),
}); });
data.insert::<CommandRunner>(CommandRunner::default()); data.insert::<CommandRunner>(CommandRunner::default());

View File

@ -1,7 +1,9 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::fmt::Debug;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use std::fmt::Debug;
pub mod logging; pub mod logging;
pub mod models; pub mod models;

View File

@ -1,23 +0,0 @@
use crate::GeoffreyDatabaseModel;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Meta {
id: Option<u64>,
version: u64,
database_version: u64,
}
impl GeoffreyDatabaseModel for Meta {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id);
}
fn tree() -> String {
"meta".to_string()
}
}

View File

@ -1,15 +1,16 @@
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize};
pub mod db_metadata; pub mod db_metadata;
pub mod item; pub mod item;
pub mod link; pub mod link;
pub mod locations; pub mod locations;
pub mod meta;
pub mod parameters; pub mod parameters;
pub mod player; pub mod player;
pub mod response; pub mod response;
pub mod settings;
pub mod token; pub mod token;
#[derive(Serialize, Deserialize, Debug, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, Copy, Clone)]

View File

@ -24,3 +24,8 @@ pub struct CommandRequest<T> {
pub user_id: Option<UserID>, pub user_id: Option<UserID>,
pub params: T, pub params: T,
} }
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EmptyRequest {}
impl GeoffreyParam for EmptyRequest {}

View File

@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct GeoffreySettings {
pub min_out_of_stock_votes: u32,
pub max_owners_to_display: u32,
pub max_item_listings_to_display: u32,
pub max_str_len: usize,
pub dynmap_base_link: Option<String>,
}
impl Default for GeoffreySettings {
fn default() -> Self {
Self {
min_out_of_stock_votes: 1,
max_owners_to_display: 3,
max_item_listings_to_display: 10,
max_str_len: 25,
dynmap_base_link: None,
}
}
}