blastmud/blastmud_game/src/message_handler/user_commands/register.rs
2023-10-01 16:34:25 +11:00

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;