2022-12-24 21:16:23 +11:00
|
|
|
use super::ListenerSession;
|
2022-12-24 13:43:28 +11:00
|
|
|
use crate::DResult;
|
2022-12-31 00:59:14 +11:00
|
|
|
use crate::db::{DBTrans, DBPool, ItemSearchParams};
|
2022-12-28 20:00:55 +11:00
|
|
|
use ansi::ansi;
|
2022-12-24 21:16:23 +11:00
|
|
|
use phf::phf_map;
|
|
|
|
use async_trait::async_trait;
|
2022-12-31 00:59:14 +11:00
|
|
|
use crate::models::{session::Session, user::User, item::Item};
|
2022-12-25 12:42:03 +11:00
|
|
|
use log::warn;
|
2022-12-31 00:59:14 +11:00
|
|
|
use once_cell::sync::OnceCell;
|
|
|
|
use std::sync::Arc;
|
2022-12-24 13:43:28 +11:00
|
|
|
|
2022-12-28 20:00:55 +11:00
|
|
|
mod agree;
|
2022-12-24 21:16:23 +11:00
|
|
|
mod help;
|
2022-12-28 20:00:55 +11:00
|
|
|
mod ignore;
|
2022-12-26 01:30:59 +11:00
|
|
|
mod less_explicit_mode;
|
2022-12-27 16:08:27 +11:00
|
|
|
mod login;
|
2022-12-28 20:00:55 +11:00
|
|
|
mod look;
|
2022-12-31 00:59:14 +11:00
|
|
|
pub mod parsing;
|
2022-12-28 20:00:55 +11:00
|
|
|
mod quit;
|
|
|
|
mod register;
|
2022-12-31 00:59:14 +11:00
|
|
|
mod whisper;
|
2022-12-24 21:16:23 +11:00
|
|
|
|
|
|
|
pub struct VerbContext<'l> {
|
2022-12-31 00:59:14 +11:00
|
|
|
pub session: &'l ListenerSession,
|
|
|
|
pub session_dat: &'l mut Session,
|
|
|
|
pub user_dat: &'l mut Option<User>,
|
|
|
|
pub trans: &'l DBTrans
|
2022-12-24 21:16:23 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum CommandHandlingError {
|
|
|
|
UserError(String),
|
|
|
|
SystemError(Box<dyn std::error::Error + Send + Sync>)
|
|
|
|
}
|
|
|
|
use CommandHandlingError::*;
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
pub trait UserVerb {
|
2022-12-26 01:30:59 +11:00
|
|
|
async fn handle(self: &Self, ctx: &mut VerbContext, verb: &str, remaining: &str) -> UResult<()>;
|
2022-12-24 21:16:23 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
pub type UResult<A> = Result<A, CommandHandlingError>;
|
|
|
|
|
2022-12-27 00:20:09 +11:00
|
|
|
|
2022-12-27 16:08:27 +11:00
|
|
|
impl<T> From<T> for CommandHandlingError where T: Into<Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
fn from(input: T) -> CommandHandlingError {
|
|
|
|
SystemError(input.into())
|
2022-12-24 21:16:23 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn user_error<A>(msg: String) -> UResult<A> {
|
|
|
|
Err(UserError(msg))
|
|
|
|
}
|
|
|
|
|
2022-12-26 01:30:59 +11:00
|
|
|
|
|
|
|
/* Verb registries list types of commands available in different circumstances. */
|
|
|
|
pub type UserVerbRef = &'static (dyn UserVerb + Sync + Send);
|
2022-12-24 21:16:23 +11:00
|
|
|
type UserVerbRegistry = phf::Map<&'static str, UserVerbRef>;
|
|
|
|
|
|
|
|
static ALWAYS_AVAILABLE_COMMANDS: UserVerbRegistry = phf_map! {
|
|
|
|
"" => ignore::VERB,
|
2022-12-25 12:42:03 +11:00
|
|
|
"help" => help::VERB,
|
|
|
|
"quit" => quit::VERB,
|
2022-12-24 21:16:23 +11:00
|
|
|
};
|
|
|
|
|
2022-12-26 01:30:59 +11:00
|
|
|
static UNREGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
2022-12-28 20:00:55 +11:00
|
|
|
"agree" => agree::VERB,
|
|
|
|
"connect" => login::VERB,
|
2022-12-26 01:30:59 +11:00
|
|
|
"less_explicit_mode" => less_explicit_mode::VERB,
|
2022-12-27 16:08:27 +11:00
|
|
|
"login" => login::VERB,
|
2022-12-28 20:00:55 +11:00
|
|
|
"register" => register::VERB,
|
2022-12-27 00:20:09 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
2022-12-28 20:00:55 +11:00
|
|
|
"l" => look::VERB,
|
|
|
|
"look" => look::VERB,
|
2022-12-31 00:59:14 +11:00
|
|
|
"-" => whisper::VERB,
|
|
|
|
"whisper" => whisper::VERB,
|
2022-12-26 01:30:59 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> {
|
|
|
|
let mut result = ALWAYS_AVAILABLE_COMMANDS.get(cmd);
|
|
|
|
|
2022-12-27 00:20:09 +11:00
|
|
|
match &ctx.user_dat {
|
|
|
|
None => {
|
|
|
|
result = result.or_else(|| UNREGISTERED_COMMANDS.get(cmd));
|
|
|
|
}
|
|
|
|
Some(user_dat) => {
|
|
|
|
if user_dat.terms.terms_complete {
|
|
|
|
result = result.or_else(|| REGISTERED_COMMANDS.get(cmd));
|
|
|
|
} else if cmd == "agree" {
|
|
|
|
result = Some(&agree::VERB);
|
|
|
|
}
|
|
|
|
}
|
2022-12-26 01:30:59 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2022-12-24 21:16:23 +11:00
|
|
|
pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DResult<()> {
|
|
|
|
let (cmd, params) = parsing::parse_command_name(msg);
|
2022-12-25 00:25:52 +11:00
|
|
|
let trans = pool.start_transaction().await?;
|
2022-12-26 01:30:59 +11:00
|
|
|
let (mut session_dat, mut user_dat) = match trans.get_session_user_model(session).await? {
|
2022-12-25 12:42:03 +11:00
|
|
|
None => {
|
|
|
|
// If the session has been cleaned up from the database, there is
|
|
|
|
// nowhere to go from here, so just ignore it.
|
|
|
|
warn!("Got command from session not in database: {}", session.session);
|
|
|
|
return Ok(());
|
|
|
|
}
|
2022-12-25 01:42:51 +11:00
|
|
|
Some(v) => v
|
|
|
|
};
|
2022-12-26 01:30:59 +11:00
|
|
|
|
|
|
|
let mut ctx = VerbContext { session, trans: &trans, session_dat: &mut session_dat,
|
|
|
|
user_dat: &mut user_dat };
|
|
|
|
let handler_opt = resolve_handler(&ctx, cmd);
|
2022-12-25 01:42:51 +11:00
|
|
|
|
2022-12-24 21:16:23 +11:00
|
|
|
match handler_opt {
|
|
|
|
None => {
|
2022-12-25 00:25:52 +11:00
|
|
|
trans.queue_for_session(session,
|
2022-12-25 12:42:03 +11:00
|
|
|
Some(ansi!(
|
2022-12-25 00:25:52 +11:00
|
|
|
"That's not a command I know. Try <bold>help<reset>\r\n"
|
2022-12-25 12:42:03 +11:00
|
|
|
))
|
2022-12-24 21:16:23 +11:00
|
|
|
).await?;
|
2022-12-27 00:20:09 +11:00
|
|
|
trans.commit().await?;
|
2022-12-24 21:16:23 +11:00
|
|
|
}
|
|
|
|
Some(handler) => {
|
2022-12-26 01:30:59 +11:00
|
|
|
match handler.handle(&mut ctx, cmd, params).await {
|
2022-12-27 00:20:09 +11:00
|
|
|
Ok(()) => {
|
|
|
|
trans.commit().await?;
|
|
|
|
}
|
2022-12-24 21:16:23 +11:00
|
|
|
Err(UserError(err_msg)) => {
|
2022-12-27 00:20:09 +11:00
|
|
|
pool.queue_for_session(session, Some(&(err_msg + "\r\n"))).await?;
|
2022-12-24 21:16:23 +11:00
|
|
|
}
|
|
|
|
Err(SystemError(e)) => Err(e)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-24 13:43:28 +11:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-12-31 00:59:14 +11:00
|
|
|
|
|
|
|
pub fn is_likely_explicit(msg: &str) -> bool {
|
|
|
|
static EXPLICIT_MARKER_WORDS: OnceCell<Vec<&'static str>> =
|
|
|
|
OnceCell::new();
|
|
|
|
let markers = EXPLICIT_MARKER_WORDS.get_or_init(||
|
|
|
|
vec!("fuck", "sex", "cock", "cunt", "dick", "pussy", "whore",
|
|
|
|
"orgasm", "erection", "nipple", "boob", "tit"));
|
|
|
|
for word in markers {
|
|
|
|
if msg.contains(word) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_user_or_fail<'l>(ctx: &'l VerbContext) -> UResult<&'l User> {
|
|
|
|
ctx.user_dat.as_ref()
|
|
|
|
.ok_or_else(|| UserError("Not logged in".to_owned()))
|
|
|
|
}
|
|
|
|
|
2022-12-31 18:42:13 +11:00
|
|
|
pub fn get_user_or_fail_mut<'l>(ctx: &'l mut VerbContext) -> UResult<&'l mut User> {
|
|
|
|
ctx.user_dat.as_mut()
|
|
|
|
.ok_or_else(|| UserError("Not logged in".to_owned()))
|
|
|
|
}
|
|
|
|
|
2022-12-31 00:59:14 +11:00
|
|
|
pub async fn get_player_item_or_fail(ctx: &VerbContext<'_>) -> UResult<Arc<Item>> {
|
|
|
|
Ok(ctx.trans.find_item_by_type_code(
|
|
|
|
"player", &get_user_or_fail(ctx)?.username.to_lowercase()).await?
|
|
|
|
.ok_or_else(|| UserError("Your player is gone, you'll need to re-register or ask an admin".to_owned()))?)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn search_item_for_user<'l>(ctx: &'l VerbContext<'l>, search: &'l ItemSearchParams<'l>) ->
|
|
|
|
UResult<Arc<Item>> {
|
|
|
|
Ok(match &ctx.trans.resolve_items_by_display_name_for_player(search).await?[..] {
|
|
|
|
[] => user_error("Sorry, I couldn't find anything matching.".to_owned())?,
|
|
|
|
[match_it] => match_it.clone(),
|
|
|
|
[item1, ..] =>
|
|
|
|
item1.clone(),
|
|
|
|
})
|
|
|
|
}
|