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"
|
name = "geoffrey_api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"config",
|
"config",
|
||||||
"geoffrey_db",
|
"geoffrey_db",
|
||||||
"geoffrey_models",
|
"geoffrey_models",
|
||||||
|
|
|
@ -18,4 +18,5 @@ structopt = "0.3.21"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
simple_logger = "1.13.0"
|
simple_logger = "1.13.0"
|
||||||
rand = "0.8.4"
|
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::add_location::AddLocation;
|
||||||
use crate::commands::delete::Delete;
|
use crate::commands::delete::Delete;
|
||||||
use crate::commands::find::FindCommand;
|
use crate::commands::find::FindCommand;
|
||||||
|
use crate::commands::link::LinkCommand;
|
||||||
use crate::commands::register::Register;
|
use crate::commands::register::Register;
|
||||||
use crate::commands::selling::Selling;
|
use crate::commands::selling::Selling;
|
||||||
use crate::commands::set_portal::SetPortal;
|
use crate::commands::set_portal::SetPortal;
|
||||||
|
@ -26,6 +27,7 @@ pub mod add_location;
|
||||||
pub mod add_token;
|
pub mod add_token;
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod find;
|
pub mod find;
|
||||||
|
pub mod link;
|
||||||
pub mod register;
|
pub mod register;
|
||||||
pub mod selling;
|
pub mod selling;
|
||||||
pub mod set_portal;
|
pub mod set_portal;
|
||||||
|
@ -131,6 +133,7 @@ pub fn command_filter(
|
||||||
.or(create_command_filter::<Selling>(ctx.clone()))
|
.or(create_command_filter::<Selling>(ctx.clone()))
|
||||||
.or(create_command_filter::<AddItem>(ctx.clone()))
|
.or(create_command_filter::<AddItem>(ctx.clone()))
|
||||||
.or(create_command_filter::<Delete>(ctx.clone()))
|
.or(create_command_filter::<Delete>(ctx.clone()))
|
||||||
|
.or(create_command_filter::<LinkCommand>(ctx.clone()))
|
||||||
.or(create_command_filter::<SetPortal>(ctx)),
|
.or(create_command_filter::<SetPortal>(ctx)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
use crate::commands::{Command, RequestType};
|
use crate::commands::{Command, RequestType};
|
||||||
use crate::context::Context;
|
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::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::response::api_error::GeoffreyAPIError;
|
||||||
use geoffrey_models::models::CommandLevel;
|
use geoffrey_models::models::CommandLevel;
|
||||||
|
use geoffrey_models::GeoffreyDatabaseModel;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct Register {}
|
pub struct Register {}
|
||||||
|
@ -24,11 +27,30 @@ impl Command for Register {
|
||||||
CommandLevel::ALL
|
CommandLevel::ALL
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_command(
|
fn run_command(ctx: Arc<Context>, req: &Self::Req, _: Option<Player>) -> Result<Self::Resp> {
|
||||||
ctx: Arc<Context>,
|
if let Some(link_code) = &req.link_code {
|
||||||
req: &Self::Req,
|
let link: Option<Link> = ctx
|
||||||
_: Option<Player>,
|
.db
|
||||||
) -> crate::Result<Self::Resp> {
|
.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());
|
let player = Player::new(req.username.as_str(), req.user_id.clone());
|
||||||
|
|
||||||
ctx.db.insert(player).map_err(GeoffreyAPIError::from)
|
ctx.db.insert(player).map_err(GeoffreyAPIError::from)
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub mod add_item;
|
||||||
pub mod add_location;
|
pub mod add_location;
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod find;
|
pub mod find;
|
||||||
|
pub mod register;
|
||||||
pub mod selling;
|
pub mod selling;
|
||||||
pub mod set_portal;
|
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 serenity::prelude::*;
|
||||||
|
|
||||||
use crate::bot::commands::delete::DeleteCommand;
|
use crate::bot::commands::delete::DeleteCommand;
|
||||||
|
use crate::bot::commands::register::RegisterCommand;
|
||||||
use crate::bot::commands::GeoffreyCommandFn;
|
use crate::bot::commands::GeoffreyCommandFn;
|
||||||
use crate::context::GeoffreyContext;
|
use crate::context::GeoffreyContext;
|
||||||
use commands::add_item::AddItemCommand;
|
use commands::add_item::AddItemCommand;
|
||||||
|
@ -80,6 +81,8 @@ pub async fn build_commands(
|
||||||
.add_command::<SetPortalCommand>(ctx)
|
.add_command::<SetPortalCommand>(ctx)
|
||||||
.await?
|
.await?
|
||||||
.add_command::<DeleteCommand>(ctx)
|
.add_command::<DeleteCommand>(ctx)
|
||||||
|
.await?
|
||||||
|
.add_command::<RegisterCommand>(ctx)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
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 db_metadata;
|
||||||
pub mod item;
|
pub mod item;
|
||||||
|
pub mod link;
|
||||||
pub mod locations;
|
pub mod locations;
|
||||||
pub mod meta;
|
pub mod meta;
|
||||||
pub mod parameters;
|
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 add_token_params;
|
||||||
pub mod delete_params;
|
pub mod delete_params;
|
||||||
pub mod find_params;
|
pub mod find_params;
|
||||||
|
pub mod link_params;
|
||||||
pub mod register_params;
|
pub mod register_params;
|
||||||
pub mod selling_params;
|
pub mod selling_params;
|
||||||
pub mod set_portal_params;
|
pub mod set_portal_params;
|
||||||
|
|
|
@ -6,11 +6,16 @@ use serde::{Deserialize, Serialize};
|
||||||
pub struct RegisterParameters {
|
pub struct RegisterParameters {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub user_id: UserID,
|
pub user_id: UserID,
|
||||||
|
pub link_code: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegisterParameters {
|
impl RegisterParameters {
|
||||||
pub fn new(username: String, user_id: UserID) -> Self {
|
pub fn new(username: String, user_id: UserID, link_code: Option<String>) -> Self {
|
||||||
RegisterParameters { username, user_id }
|
RegisterParameters {
|
||||||
|
username,
|
||||||
|
user_id,
|
||||||
|
link_code,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::models::CommandLevel;
|
use crate::models::CommandLevel;
|
||||||
use crate::GeoffreyDatabaseModel;
|
use crate::GeoffreyDatabaseModel;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub enum UserID {
|
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 struct Player {
|
||||||
pub id: Option<u64>,
|
pub id: Option<u64>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub user_ids: Vec<UserID>,
|
pub user_ids: HashSet<UserID>,
|
||||||
pub auth_level: CommandLevel,
|
pub auth_level: CommandLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn new(name: &str, user_id: UserID) -> Self {
|
pub fn new(name: &str, user_id: UserID) -> Self {
|
||||||
|
let mut user_ids = HashSet::new();
|
||||||
|
user_ids.insert(user_id);
|
||||||
Self {
|
Self {
|
||||||
id: None,
|
id: None,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
user_ids: vec![user_id],
|
user_ids,
|
||||||
auth_level: CommandLevel::REGISTERED,
|
auth_level: CommandLevel::REGISTERED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ pub enum GeoffreyAPIError {
|
||||||
TokenNotAuthorized,
|
TokenNotAuthorized,
|
||||||
MultipleLocationsMatch,
|
MultipleLocationsMatch,
|
||||||
ParameterInvalid(String),
|
ParameterInvalid(String),
|
||||||
|
PlayerRegistrationWithoutMCUUID,
|
||||||
|
AccountLinkInvalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for GeoffreyAPIError {
|
impl Display for GeoffreyAPIError {
|
||||||
|
@ -34,6 +36,12 @@ impl Display for GeoffreyAPIError {
|
||||||
GeoffreyAPIError::ParameterInvalid(param) => {
|
GeoffreyAPIError::ParameterInvalid(param) => {
|
||||||
format!("Parameter \"{}\" is invalid", 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)
|
write!(f, "{}", string)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue