Implement messaging to corps.

This commit is contained in:
Condorra 2023-03-26 22:29:39 +11:00
parent 6c86599103
commit d35bbbad53
6 changed files with 197 additions and 3 deletions

View File

@ -894,6 +894,24 @@ impl DBTrans {
.collect())
}
pub async fn get_default_corp_for_user(&self, username: &str) -> DResult<Option<(CorpId, Corp)>> {
Ok(match self.pg_trans()?
.query_opt("SELECT m.corp_id, c.details FROM corp_membership m \
JOIN corps c ON c.corp_id = m.corp_id WHERE m.member_username = $1 \
AND m.details->>'joined_at' IS NOT NULL \
ORDER BY (m.details->>'priority')::int ASC NULLS LAST, \
(m.details->>'joined_at') :: TIMESTAMPTZ ASC LIMIT 1",
&[&username.to_lowercase()])
.await? {
None => None,
Some(row) => match serde_json::from_value(row.get(1)) {
Err(_) => None,
Ok(j) =>
Some((CorpId(row.get(0)), j))
}
})
}
pub async fn broadcast_to_corp<'a>(self: &'a Self,
corp_id: &'a CorpId,
comm_type: &'a CorpCommType,

View File

@ -15,6 +15,7 @@ mod agree;
mod allow;
pub mod attack;
mod buy;
mod c;
mod corp;
pub mod drop;
pub mod get;
@ -119,6 +120,7 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
"attack" => attack::VERB,
"buy" => buy::VERB,
"c" => c::VERB,
"corp" => corp::VERB,
"drop" => drop::VERB,
"get" => get::VERB,

View File

@ -0,0 +1,55 @@
use super::{VerbContext, UserVerb, UserVerbRef, UResult,
get_user_or_fail, get_player_item_or_fail, user_error};
use async_trait::async_trait;
use crate::{
models::corp::CorpCommType
};
use ansi::{ansi, ignore_special_characters};
pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> {
let user = get_user_or_fail(ctx)?;
let (corp_id, corp, msg) = if remaining.starts_with("@") {
match remaining[1..].split_once(" ") {
None => user_error("Usage: c message (lowest ordered corp) or c @corpname message"
.to_owned())?,
Some((corpname, msg)) => {
let (corp_id, corp, _) =
match ctx.trans.match_user_corp_by_name(corpname.trim(), &user.username).await? {
None => user_error("You don't seem to belong to a matching corp!".to_owned())?,
Some(c) => c
};
(corp_id, corp, msg.trim())
}
}
} else {
let (corp_id, corp) =
match ctx.trans.get_default_corp_for_user(&user.username).await? {
None => user_error("You're not a member of any corps.".to_owned())?,
Some(v) => v
};
(corp_id, corp, remaining.trim())
};
let msg = ignore_special_characters(msg);
if msg.trim() == "" {
user_error("Message required.".to_owned())?;
}
let player = get_player_item_or_fail(ctx).await?;
ctx.trans.broadcast_to_corp(&corp_id, &CorpCommType::Chat,
Some(&user.username),
&format!(ansi!("<yellow>{} (to {}): <reset><bold>\"{}\"<reset>\n"),
&player.display_for_sentence(false, 1, true),
&corp.name,
&msg)).await?;
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;

View File

@ -576,6 +576,91 @@ async fn corp_config(ctx: &mut VerbContext<'_>, remaining: &str) -> UResult<()>
Ok(())
}
async fn corp_subscribe(ctx: &mut VerbContext<'_>, remaining: &str) -> UResult<()> {
let user = get_user_or_fail(ctx)?;
let (subs, remaining) = match remaining.split_once(" from ") {
None => {
let (_, corp, mem) =
match ctx.trans.match_user_corp_by_name(remaining.trim(), &user.username).await? {
None => user_error("You don't seem to belong to a matching corp!".to_owned())?,
Some(c) => c
};
ctx.trans.queue_for_session(
&ctx.session,
Some(&format!("Subscriptions for {}: {}\n",
&corp.name,
&mem.comms_on.iter().map(|m| m.display()).join(" ")))
).await?;
return Ok(());
}
Some(v) => v
};
let mut subs_add = BTreeSet::<CorpCommType>::new();
for sub in subs.trim().split(" ") {
let sub = ignore_special_characters(&sub.trim());
match CorpCommType::parse(&sub) {
None => user_error(format!("Invalid commtype: {}", sub))?,
Some(t) => subs_add.insert(t)
};
}
let (corp_id, _, mut mem) =
match ctx.trans.match_user_corp_by_name(remaining.trim(), &user.username).await? {
None => user_error("You don't seem to belong to a matching corp!".to_owned())?,
Some(c) => c
};
mem.comms_on = (&mem.comms_on.into_iter().collect::<BTreeSet<CorpCommType>>() | &subs_add).into_iter().collect();
ctx.trans.upsert_corp_membership(&corp_id, &user.username, &mem).await?;
ctx.trans.queue_for_session(&ctx.session, Some("Subscriptions updated.\n")).await?;
Ok(())
}
async fn corp_unsubscribe(ctx: &mut VerbContext<'_>, remaining: &str) -> UResult<()> {
let (subs, remaining) = match remaining.split_once(" from ") {
None => user_error("Usage: corp unsubscribe commtype commtype ... from corpname\n\
commtypes: chat notice connect reward death".to_owned())?,
Some(v) => v
};
let mut subs_del = BTreeSet::<CorpCommType>::new();
for sub in subs.trim().split(" ") {
let sub = ignore_special_characters(&sub.trim());
match CorpCommType::parse(&sub) {
None => user_error(format!("Invalid commtype: {}", sub))?,
Some(t) => subs_del.insert(t)
};
}
let user = get_user_or_fail(ctx)?;
let (corp_id, _, mut mem) =
match ctx.trans.match_user_corp_by_name(remaining.trim(), &user.username).await? {
None => user_error("You don't seem to belong to a matching corp!".to_owned())?,
Some(c) => c
};
mem.comms_on = (&mem.comms_on.into_iter().collect::<BTreeSet<CorpCommType>>() - &subs_del).into_iter().collect();
ctx.trans.upsert_corp_membership(&corp_id, &user.username, &mem).await?;
ctx.trans.queue_for_session(&ctx.session, Some("Subscriptions updated.\n")).await?;
Ok(())
}
async fn corp_order(ctx: &mut VerbContext<'_>, remaining: &str) -> UResult<()> {
let (corpname, number_s) = match remaining.split_once(" as ") {
None => user_error("Usage: corp order corpname as number".to_owned())?,
Some(v) => v
};
let number = match nom::character::complete::i64::<&str, ()>(number_s) {
Ok((rest, num)) if rest.trim() == "" => num,
_ => user_error("Usage: corp order corpname as number".to_owned())?
};
let user = get_user_or_fail(ctx)?;
let (corp_id, _, mut mem) =
match ctx.trans.match_user_corp_by_name(corpname.trim(), &user.username).await? {
None => user_error("You don't seem to belong to a matching corp!".to_owned())?,
Some(c) => c
};
mem.priority = number;
ctx.trans.upsert_corp_membership(&corp_id, &user.username, &mem).await?;
ctx.trans.queue_for_session(&ctx.session, Some("Updated!\n")).await?;
Ok(())
}
pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
@ -607,7 +692,16 @@ impl UserVerb for Verb {
},
"configure" | "config" => {
corp_config(ctx, remaining).await?;
}
},
"sub" | "subscribe" => {
corp_subscribe(ctx, remaining).await?;
},
"unsub" | "unsubscribe" => {
corp_unsubscribe(ctx, remaining).await?;
},
"order" => {
corp_order(ctx, remaining).await?;
},
_ => user_error("Unknown command".to_owned())?
}
Ok(())

View File

@ -19,7 +19,8 @@ pub fn is_invalid_username(name: &str) -> bool {
"bot"
));
let invalid_words = INVALID_WORDS.get_or_init(|| HashSet::from(
["corp", "to", "from", "dog", "bot"]
["corp", "to", "from", "dog", "bot", "for", "against", "on",
"privileges", "as"]
));
if invalid_words.contains(name) {
return true;

View File

@ -40,7 +40,7 @@ impl CorpPermission {
}
#[derive(Serialize, Deserialize, PartialEq)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Clone)]
pub enum CorpCommType {
Chat,
Notice,
@ -49,6 +49,30 @@ pub enum CorpCommType {
Death,
}
impl CorpCommType {
pub fn parse(s: &str) -> Option<CorpCommType> {
use CorpCommType::*;
match s {
"chat" => Some(Chat),
"notice" => Some(Notice),
"connect" => Some(Connect),
"reward" => Some(Reward),
"death" => Some(Death),
_ => None
}
}
pub fn display(&self) -> &'static str {
use CorpCommType::*;
match self {
Chat => "chat",
Notice => "notice",
Connect => "connect",
Reward => "reward",
Death => "death",
}
}
}
pub struct CorpId(pub i64);
#[derive(Serialize, Deserialize)]