138 lines
4.5 KiB
Rust
138 lines
4.5 KiB
Rust
use super::{parsing::parse_username, user_error};
|
|
use super::{UResult, UserVerb, UserVerbRef, VerbContext};
|
|
use crate::models::{
|
|
item::{Item, Pronouns},
|
|
user::User,
|
|
};
|
|
use ansi::ansi;
|
|
use async_trait::async_trait;
|
|
use chrono::Utc;
|
|
use once_cell::sync::OnceCell;
|
|
use std::collections::HashSet;
|
|
use tokio::time;
|
|
|
|
pub fn is_invalid_username(name: &str) -> bool {
|
|
static INVALID_PREFIXES: OnceCell<Vec<&'static str>> = OnceCell::new();
|
|
static INVALID_SUFFIXES: OnceCell<Vec<&'static str>> = OnceCell::new();
|
|
static INVALID_WORDS: OnceCell<HashSet<&'static str>> = OnceCell::new();
|
|
let invalid_prefixes =
|
|
INVALID_PREFIXES.get_or_init(|| vec!["admin", "god", "helper", "npc", "corpse", "dead"]);
|
|
let invalid_suffixes = INVALID_SUFFIXES.get_or_init(|| vec!["bot"]);
|
|
let invalid_words = INVALID_WORDS.get_or_init(|| {
|
|
HashSet::from([
|
|
"corp",
|
|
"to",
|
|
"from",
|
|
"dog",
|
|
"bot",
|
|
"for",
|
|
"against",
|
|
"on",
|
|
"privileges",
|
|
"as",
|
|
"treasury",
|
|
])
|
|
});
|
|
if invalid_words.contains(name) {
|
|
return true;
|
|
}
|
|
for pfx in invalid_prefixes.iter() {
|
|
if name.starts_with(pfx) {
|
|
return true;
|
|
}
|
|
}
|
|
for sfx in invalid_suffixes.iter() {
|
|
if name.ends_with(sfx) {
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
pub struct Verb;
|
|
#[async_trait]
|
|
impl UserVerb for Verb {
|
|
async fn handle(
|
|
self: &Self,
|
|
ctx: &mut VerbContext,
|
|
_verb: &str,
|
|
remaining: &str,
|
|
) -> UResult<()> {
|
|
let (username, password, email) = match parse_username(remaining) {
|
|
Err(e) => user_error("Invalid username: ".to_owned() + e)?,
|
|
Ok((username, rest)) => match rest.split_whitespace().collect::<Vec<&str>>()[..] {
|
|
[password, email] => (username, password, email),
|
|
[] | [_] => user_error(
|
|
"Too few options to register - supply username, password, and email".to_owned(),
|
|
)?,
|
|
_ => user_error(
|
|
"Too many options to register - supply username, password, and email"
|
|
.to_owned(),
|
|
)?,
|
|
},
|
|
};
|
|
|
|
if is_invalid_username(&username.to_lowercase()) {
|
|
user_error("Sorry, that username isn't allowed. Try another".to_owned())?;
|
|
}
|
|
|
|
if ctx.trans.find_by_username(username).await?.is_some() {
|
|
user_error("Username already exists".to_owned())?;
|
|
}
|
|
if ctx.trans.find_corp_by_name(username).await?.is_some() {
|
|
user_error("Username clashes with existing corp name".to_owned())?;
|
|
}
|
|
if password.len() < 6 {
|
|
user_error("Password must be 6 characters long or longer".to_owned())?;
|
|
} else if !validator::validate_email(email) {
|
|
user_error(
|
|
"Please supply a valid email in case you need to reset your password.".to_owned(),
|
|
)?;
|
|
}
|
|
|
|
let player_item_id = ctx
|
|
.trans
|
|
.create_item(&Item {
|
|
item_type: "player".to_owned(),
|
|
item_code: username.to_lowercase(),
|
|
display: username.to_owned(),
|
|
details: Some("A non-descript individual".to_owned()),
|
|
location: "room/repro_xv_chargen".to_owned(),
|
|
pronouns: Pronouns::default_animate(),
|
|
..Item::default()
|
|
})
|
|
.await?;
|
|
|
|
// Force a wait to protect against abuse.
|
|
time::sleep(time::Duration::from_secs(5)).await;
|
|
let password_hash = bcrypt::hash(password, 10).expect("hash not to fail");
|
|
let user_dat = User {
|
|
username: username.to_owned(),
|
|
password_hash: password_hash.to_owned(),
|
|
email: email.to_owned(),
|
|
player_item_id,
|
|
registered_at: Some(Utc::now()),
|
|
..User::default()
|
|
};
|
|
*ctx.user_dat = Some(user_dat);
|
|
|
|
ctx.trans
|
|
.queue_for_session(
|
|
ctx.session,
|
|
Some(&format!(
|
|
ansi!("Welcome <bold>{}<reset>, you are now officially registered.\r\n"),
|
|
&username
|
|
)),
|
|
)
|
|
.await?;
|
|
super::agree::check_and_notify_accepts(ctx).await?;
|
|
ctx.trans
|
|
.create_user(ctx.session, ctx.user_dat.as_ref().unwrap())
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
static VERB_INT: Verb = Verb;
|
|
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|