Added link command
continuous-integration/woodpecker the build was successful
Details
continuous-integration/woodpecker the build was successful
Details
+ Link provides a link code that a user can use to link other accounts + This places the main auth source into MC and the plugin + Refactored register to accept a link code + Clippy + fmtmain
parent
37117ce5a9
commit
48be50dd67
|
@ -421,6 +421,7 @@ dependencies = [
|
|||
name = "geoffrey_api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"geoffrey_db",
|
||||
"geoffrey_models",
|
||||
|
|
|
@ -18,4 +18,5 @@ structopt = "0.3.21"
|
|||
log = "0.4.14"
|
||||
simple_logger = "1.13.0"
|
||||
rand = "0.8.4"
|
||||
regex = "1.5.4"
|
||||
regex = "1.5.4"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
use crate::commands::{Command, RequestType};
|
||||
use crate::context::Context;
|
||||
use crate::Result;
|
||||
use chrono::{Duration, Utc};
|
||||
use geoffrey_models::models::link::Link;
|
||||
use geoffrey_models::models::parameters::link_params::LinkParameters;
|
||||
use geoffrey_models::models::player::Player;
|
||||
use geoffrey_models::models::CommandLevel;
|
||||
use geoffrey_models::GeoffreyDatabaseModel;
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::Rng;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct LinkCommand {}
|
||||
|
||||
impl Command for LinkCommand {
|
||||
type Req = LinkParameters;
|
||||
type Resp = Link;
|
||||
|
||||
fn command_name() -> String {
|
||||
"link".to_string()
|
||||
}
|
||||
|
||||
fn request_type() -> RequestType {
|
||||
RequestType::POST
|
||||
}
|
||||
|
||||
fn command_level() -> CommandLevel {
|
||||
CommandLevel::REGISTERED
|
||||
}
|
||||
|
||||
fn run_command(
|
||||
ctx: Arc<Context>,
|
||||
_req: &Self::Req,
|
||||
player: Option<Player>,
|
||||
) -> Result<Self::Resp> {
|
||||
let player = player.unwrap();
|
||||
|
||||
let links: Vec<Link> = ctx
|
||||
.db
|
||||
.filter(|_, link: &Link| player.id().unwrap() == link.player_id)?
|
||||
.collect();
|
||||
|
||||
for link in links {
|
||||
ctx.db.remove::<Link>(link.id().unwrap())?;
|
||||
}
|
||||
|
||||
let link_code: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(10)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
|
||||
let expire_time = Utc::now() + Duration::days(1);
|
||||
|
||||
let link = Link::new(player.id.unwrap(), link_code, expire_time);
|
||||
|
||||
let link = ctx.db.insert(link)?;
|
||||
|
||||
Ok(link)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ use crate::commands::add_item::AddItem;
|
|||
use crate::commands::add_location::AddLocation;
|
||||
use crate::commands::delete::Delete;
|
||||
use crate::commands::find::FindCommand;
|
||||
use crate::commands::link::LinkCommand;
|
||||
use crate::commands::register::Register;
|
||||
use crate::commands::selling::Selling;
|
||||
use crate::commands::set_portal::SetPortal;
|
||||
|
@ -26,6 +27,7 @@ pub mod add_location;
|
|||
pub mod add_token;
|
||||
pub mod delete;
|
||||
pub mod find;
|
||||
pub mod link;
|
||||
pub mod register;
|
||||
pub mod selling;
|
||||
pub mod set_portal;
|
||||
|
@ -131,6 +133,7 @@ pub fn command_filter(
|
|||
.or(create_command_filter::<Selling>(ctx.clone()))
|
||||
.or(create_command_filter::<AddItem>(ctx.clone()))
|
||||
.or(create_command_filter::<Delete>(ctx.clone()))
|
||||
.or(create_command_filter::<LinkCommand>(ctx.clone()))
|
||||
.or(create_command_filter::<SetPortal>(ctx)),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use crate::commands::{Command, RequestType};
|
||||
use crate::context::Context;
|
||||
use crate::Result;
|
||||
use geoffrey_models::models::link::Link;
|
||||
use geoffrey_models::models::parameters::register_params::RegisterParameters;
|
||||
use geoffrey_models::models::player::Player;
|
||||
use geoffrey_models::models::player::{Player, UserID};
|
||||
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
|
||||
use geoffrey_models::models::CommandLevel;
|
||||
use geoffrey_models::GeoffreyDatabaseModel;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Register {}
|
||||
|
@ -24,11 +27,30 @@ impl Command for Register {
|
|||
CommandLevel::ALL
|
||||
}
|
||||
|
||||
fn run_command(
|
||||
ctx: Arc<Context>,
|
||||
req: &Self::Req,
|
||||
_: Option<Player>,
|
||||
) -> crate::Result<Self::Resp> {
|
||||
fn run_command(ctx: Arc<Context>, req: &Self::Req, _: Option<Player>) -> Result<Self::Resp> {
|
||||
if let Some(link_code) = &req.link_code {
|
||||
let link: Option<Link> = ctx
|
||||
.db
|
||||
.filter(|_, link: &Link| link.is_valid(link_code.clone()))?
|
||||
.next();
|
||||
|
||||
return if let Some(link) = link {
|
||||
ctx.db.remove::<Link>(link.id().unwrap())?;
|
||||
|
||||
let mut player = ctx.db.get::<Player>(link.player_id)?;
|
||||
|
||||
player.user_ids.insert(req.user_id.clone());
|
||||
|
||||
ctx.db.insert(player).map_err(GeoffreyAPIError::from)
|
||||
} else {
|
||||
Err(GeoffreyAPIError::AccountLinkInvalid)
|
||||
};
|
||||
}
|
||||
|
||||
if !matches!(&req.user_id, UserID::MinecraftUUID { mc_uuid: _ }) {
|
||||
return Err(GeoffreyAPIError::PlayerRegistrationWithoutMCUUID);
|
||||
}
|
||||
|
||||
let player = Player::new(req.username.as_str(), req.user_id.clone());
|
||||
|
||||
ctx.db.insert(player).map_err(GeoffreyAPIError::from)
|
||||
|
|
|
@ -21,6 +21,7 @@ pub mod add_item;
|
|||
pub mod add_location;
|
||||
pub mod delete;
|
||||
pub mod find;
|
||||
pub mod register;
|
||||
pub mod selling;
|
||||
pub mod set_portal;
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
use async_trait::async_trait;
|
||||
use reqwest::Method;
|
||||
use serenity::model::interactions::application_command::{
|
||||
ApplicationCommandInteraction, ApplicationCommandOptionType,
|
||||
};
|
||||
|
||||
use crate::bot::arg_parse::option_to_string;
|
||||
use crate::bot::commands::{BotCommand, CommandError};
|
||||
use geoffrey_models::models::parameters::register_params::RegisterParameters;
|
||||
use geoffrey_models::models::player::{Player, UserID};
|
||||
use serenity::builder::CreateApplicationCommand;
|
||||
|
||||
pub struct RegisterCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl BotCommand for RegisterCommand {
|
||||
type ApiParams = RegisterParameters;
|
||||
type ApiResp = Player;
|
||||
|
||||
fn command_name() -> String {
|
||||
"register".to_string()
|
||||
}
|
||||
|
||||
fn request_type() -> Method {
|
||||
Method::POST
|
||||
}
|
||||
|
||||
fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
||||
command
|
||||
.name(Self::command_name())
|
||||
.description("Link your discord account to a geoffrey account")
|
||||
.create_option(|option| {
|
||||
option
|
||||
.name("link_code")
|
||||
.description("Link code give to you by Geoffrey in-game")
|
||||
.kind(ApplicationCommandOptionType::String)
|
||||
.required(true)
|
||||
})
|
||||
}
|
||||
|
||||
async fn process_arguments(
|
||||
command_interaction: ApplicationCommandInteraction,
|
||||
) -> Result<Self::ApiParams, CommandError> {
|
||||
let options = command_interaction.data.options;
|
||||
let link_code = option_to_string(options.get(0), "link_code")?;
|
||||
|
||||
let discord_user_id = UserID::DiscordUUID {
|
||||
discord_uuid: command_interaction.user.id.0,
|
||||
};
|
||||
|
||||
let register = RegisterParameters::new(
|
||||
command_interaction.user.name,
|
||||
discord_user_id,
|
||||
Some(link_code),
|
||||
);
|
||||
|
||||
Ok(register)
|
||||
}
|
||||
|
||||
fn build_response(resp: Self::ApiResp) -> String {
|
||||
format!(
|
||||
"**{}**, you have been registered for the Geoffrey bot!",
|
||||
resp.name
|
||||
)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ use serenity::model::interactions::application_command::ApplicationCommand;
|
|||
use serenity::prelude::*;
|
||||
|
||||
use crate::bot::commands::delete::DeleteCommand;
|
||||
use crate::bot::commands::register::RegisterCommand;
|
||||
use crate::bot::commands::GeoffreyCommandFn;
|
||||
use crate::context::GeoffreyContext;
|
||||
use commands::add_item::AddItemCommand;
|
||||
|
@ -80,6 +81,8 @@ pub async fn build_commands(
|
|||
.add_command::<SetPortalCommand>(ctx)
|
||||
.await?
|
||||
.add_command::<DeleteCommand>(ctx)
|
||||
.await?
|
||||
.add_command::<RegisterCommand>(ctx)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
use crate::GeoffreyDatabaseModel;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Link {
|
||||
id: Option<u64>,
|
||||
pub player_id: u64,
|
||||
pub link_code: String,
|
||||
pub expires: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Link {
|
||||
pub fn new(player_id: u64, link_code: String, expires: DateTime<Utc>) -> Self {
|
||||
Self {
|
||||
id: None,
|
||||
player_id,
|
||||
link_code,
|
||||
expires,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self, link_code: String) -> bool {
|
||||
self.expires > Utc::now() && self.link_code == link_code
|
||||
}
|
||||
}
|
||||
|
||||
impl GeoffreyDatabaseModel for Link {
|
||||
fn id(&self) -> Option<u64> {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: u64) {
|
||||
self.id = Some(id);
|
||||
}
|
||||
|
||||
fn tree() -> String {
|
||||
"link".to_string()
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ use std::str::FromStr;
|
|||
|
||||
pub mod db_metadata;
|
||||
pub mod item;
|
||||
pub mod link;
|
||||
pub mod locations;
|
||||
pub mod meta;
|
||||
pub mod parameters;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
use crate::models::parameters::GeoffreyParam;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct LinkParameters {}
|
||||
|
||||
impl LinkParameters {}
|
||||
|
||||
impl GeoffreyParam for LinkParameters {}
|
|
@ -3,6 +3,7 @@ pub mod add_location_params;
|
|||
pub mod add_token_params;
|
||||
pub mod delete_params;
|
||||
pub mod find_params;
|
||||
pub mod link_params;
|
||||
pub mod register_params;
|
||||
pub mod selling_params;
|
||||
pub mod set_portal_params;
|
||||
|
|
|
@ -6,11 +6,16 @@ use serde::{Deserialize, Serialize};
|
|||
pub struct RegisterParameters {
|
||||
pub username: String,
|
||||
pub user_id: UserID,
|
||||
pub link_code: Option<String>,
|
||||
}
|
||||
|
||||
impl RegisterParameters {
|
||||
pub fn new(username: String, user_id: UserID) -> Self {
|
||||
RegisterParameters { username, user_id }
|
||||
pub fn new(username: String, user_id: UserID, link_code: Option<String>) -> Self {
|
||||
RegisterParameters {
|
||||
username,
|
||||
user_id,
|
||||
link_code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::models::CommandLevel;
|
||||
use crate::GeoffreyDatabaseModel;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub enum UserID {
|
||||
|
@ -15,20 +16,22 @@ impl Default for UserID {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Player {
|
||||
pub id: Option<u64>,
|
||||
pub name: String,
|
||||
pub user_ids: Vec<UserID>,
|
||||
pub user_ids: HashSet<UserID>,
|
||||
pub auth_level: CommandLevel,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(name: &str, user_id: UserID) -> Self {
|
||||
let mut user_ids = HashSet::new();
|
||||
user_ids.insert(user_id);
|
||||
Self {
|
||||
id: None,
|
||||
name: name.to_string(),
|
||||
user_ids: vec![user_id],
|
||||
user_ids,
|
||||
auth_level: CommandLevel::REGISTERED,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ pub enum GeoffreyAPIError {
|
|||
TokenNotAuthorized,
|
||||
MultipleLocationsMatch,
|
||||
ParameterInvalid(String),
|
||||
PlayerRegistrationWithoutMCUUID,
|
||||
AccountLinkInvalid,
|
||||
}
|
||||
|
||||
impl Display for GeoffreyAPIError {
|
||||
|
@ -34,6 +36,12 @@ impl Display for GeoffreyAPIError {
|
|||
GeoffreyAPIError::ParameterInvalid(param) => {
|
||||
format!("Parameter \"{}\" is invalid", param)
|
||||
}
|
||||
GeoffreyAPIError::PlayerRegistrationWithoutMCUUID => {
|
||||
"Players can only registered with a MC UUID".to_string()
|
||||
}
|
||||
GeoffreyAPIError::AccountLinkInvalid => {
|
||||
"The supplied account link code is invalid".to_string()
|
||||
}
|
||||
};
|
||||
write!(f, "{}", string)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue