1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Fix total users chat command, add help command

This commit is contained in:
NG (Graham)
2025-09-09 18:15:54 -04:00
parent 31585513dd
commit a694ee5f5f
10 changed files with 100 additions and 36 deletions

View File

@@ -1,18 +0,0 @@
{
"commands": [
{
"regex": "\\?online",
"op": {
"type": "BuiltIn",
"built_in": "OnlineUsers"
}
},
{
"regex": "\\?users",
"op": {
"type": "BuiltIn",
"built_in": "TotalUsers"
}
}
]
}

View File

@@ -14462,6 +14462,13 @@
"type": "BuiltIn", "type": "BuiltIn",
"built_in": "TotalUsers" "built_in": "TotalUsers"
} }
},
{
"regex": "\\?help",
"op": {
"type": "BuiltIn",
"built_in": "Help"
}
} }
] ]
}, },
@@ -14539,4 +14546,4 @@
} }
] ]
} }
} }

View File

@@ -76,7 +76,7 @@ impl <C: Send + 'static> SimpleOperation<C> for PrivateMessageSender {
}; };
let chat_system = self.chat.system().await; let chat_system = self.chat.system().await;
log::debug!("Got message `{}` from user {} (@ {} to {})", message_text.string, user.public_id(), chat_loc, username.string); log::debug!("Got message `{}` from user {} (@ {} to {})", message_text.string, user.public_id(), chat_loc, username.string);
chat_system.handle_private_message(user.as_ref().as_ref(), message_text.string, username.string); chat_system.handle_private_message(user.as_ref().as_ref(), message_text.string, username.string).await;
} }
} }
Ok(params.into()) Ok(params.into())

View File

@@ -90,7 +90,7 @@ impl ChatSystem {
pub async fn handle_public_message(&self, user: &(dyn oj_rc_core::persist::user::User<()> + Send + Sync), text: String, channel: String, channel_ty: crate::data::channel::ChatChannelType) { pub async fn handle_public_message(&self, user: &(dyn oj_rc_core::persist::user::User<()> + Send + Sync), text: String, channel: String, channel_ty: crate::data::channel::ChatChannelType) {
if self.config.is_command_channel(&channel) { if self.config.is_command_channel(&channel) {
if let Some(user_handle) = self.online_users.get(user.public_id()) { if let Some(user_handle) = self.online_users.get(user.public_id()) {
self.handle_public_command(user, text, user_handle, channel, channel_ty); self.handle_public_command(user, text, user_handle, channel, channel_ty).await;
} }
} else if let Some(room) = self.chats.get(&channel) { } else if let Some(room) = self.chats.get(&channel) {
let event_params = crate::events::chat_message::PublicMessage { let event_params = crate::events::chat_message::PublicMessage {
@@ -109,11 +109,11 @@ impl ChatSystem {
} }
} }
fn handle_public_command(&self, user: &dyn oj_rc_core::persist::user::User<()>, text: String, handle: &super::UserHandle, channel: String, channel_ty: crate::data::channel::ChatChannelType) { async fn handle_public_command(&self, user: &dyn oj_rc_core::persist::user::User<()>, text: String, handle: &super::UserHandle, channel: String, channel_ty: crate::data::channel::ChatChannelType) {
let event_params = crate::events::chat_message::PublicMessage { let event_params = crate::events::chat_message::PublicMessage {
sender_name: self.config.command_username().to_owned(), sender_name: self.config.command_username().to_owned(),
sender_display_name: self.config.command_username().to_owned(), sender_display_name: self.config.command_username().to_owned(),
text: self.config.perform_command(&text, self, user), text: self.config.perform_command(&text, self, user).await,
is_dev: false, is_dev: false,
is_mod: false, is_mod: false,
is_admin: false, is_admin: false,
@@ -132,10 +132,10 @@ impl ChatSystem {
handle.send(polariton_server::ToSend::Data { data: polariton::packet::Data::Event(event), encrypt: true, channel: 0, reliable: true }); handle.send(polariton_server::ToSend::Data { data: polariton::packet::Data::Event(event), encrypt: true, channel: 0, reliable: true });
} }
pub fn handle_private_message(&self, user: &dyn oj_rc_core::persist::user::User<()>, text: String, recipient: String) { pub async fn handle_private_message(&self, user: &dyn oj_rc_core::persist::user::User<()>, text: String, recipient: String) {
if self.config.is_command_user(&recipient) { if self.config.is_command_user(&recipient) {
if let Some(user_handle) = self.online_users.get(user.public_id()) { if let Some(user_handle) = self.online_users.get(user.public_id()) {
self.handle_private_command(user, text, user_handle); self.handle_private_command(user, text, user_handle).await;
} }
} else if let Some(recipient_handle) = self.online_users.get(&recipient) { } else if let Some(recipient_handle) = self.online_users.get(&recipient) {
let private_msg = crate::events::chat_message::PrivateMessage { let private_msg = crate::events::chat_message::PrivateMessage {
@@ -150,11 +150,11 @@ impl ChatSystem {
} }
} }
fn handle_private_command(&self, user: &dyn oj_rc_core::persist::user::User<()>, text: String, handle: &super::UserHandle) { async fn handle_private_command(&self, user: &dyn oj_rc_core::persist::user::User<()>, text: String, handle: &super::UserHandle) {
let event_params = crate::events::chat_message::PrivateMessage { let event_params = crate::events::chat_message::PrivateMessage {
sender_name: self.config.command_username().to_owned(), sender_name: self.config.command_username().to_owned(),
sender_display_name: self.config.command_username().to_owned(), sender_display_name: self.config.command_username().to_owned(),
text: self.config.perform_command(&text, self, user), text: self.config.perform_command(&text, self, user).await,
is_dev: false, is_dev: false,
is_mod: false, is_mod: false,
is_admin: false, is_admin: false,
@@ -182,4 +182,8 @@ impl ChatSystem {
pub fn is_user_online(&self, display_name: &str) -> bool { pub fn is_user_online(&self, display_name: &str) -> bool {
self.config.is_command_user(display_name) || self.online_users.get(display_name).map(|x| x.is_online()).unwrap_or(false) self.config.is_command_user(display_name) || self.online_users.get(display_name).map(|x| x.is_online()).unwrap_or(false)
} }
pub fn chat_config(&self) -> &'_ super::ChatSystemConfig {
&self.config
}
} }

View File

@@ -7,7 +7,7 @@ pub struct ChatSystemConfig {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct CommandContext<'a, 'b> { struct CommandContext<'a, 'b> {
chat_system: &'a super::ChatSystem, chat_system: &'a super::ChatSystem,
user: &'b dyn oj_rc_core::persist::user::User<()>, user: &'b dyn oj_rc_core::persist::user::ChatUser,
} }
impl ChatSystemConfig { impl ChatSystemConfig {
@@ -26,13 +26,13 @@ impl ChatSystemConfig {
}) })
} }
pub fn perform_command(&self, text: &str, chat_system: &super::ChatSystem, user: &dyn oj_rc_core::persist::user::User<()>,) -> String { pub async fn perform_command(&self, text: &str, chat_system: &super::ChatSystem, user: &dyn oj_rc_core::persist::user::ChatUser,) -> String {
let ctx = CommandContext { let ctx = CommandContext {
chat_system, chat_system,
user, user,
}; };
for cmd in self.commands.iter() { for cmd in self.commands.iter() {
if let Some(result) = cmd.perform_if_match(text, ctx) { if let Some(result) = cmd.perform_if_match(text, ctx).await {
return result; return result;
} }
} }
@@ -65,8 +65,12 @@ impl ChatCommand {
}) })
} }
fn perform_if_match(&self, text: &str, ctx: CommandContext) -> Option<String> { async fn perform_if_match<'b, 'c>(&self, text: &str, ctx: CommandContext<'b, 'c>) -> Option<String> {
self.regex.captures(text).map(|cap| self.op.perform_command(cap, ctx)) if let Some(cap) = self.regex.captures(text) {
Some(self.op.perform_command(cap, ctx).await)
} else {
None
}
} }
} }
@@ -85,18 +89,27 @@ impl ChatOperation {
} }
} }
fn perform_command<'a>(&self, _captures: regex::Captures<'a>, ctx: CommandContext) -> String { async fn perform_command<'a, 'b, 'c>(&self, _captures: regex::Captures<'a>, ctx: CommandContext<'b, 'c>) -> String {
match self { match self {
Self::BuiltIn(b_in) => b_in.do_command(ctx), Self::BuiltIn(b_in) => b_in.do_command(ctx).await,
Self::Custom => "{not implemented}".to_owned(), Self::Custom => "{not implemented}".to_owned(),
Self::Nop => "{no op}".to_owned(), Self::Nop => "{no op}".to_owned(),
} }
} }
fn help_str(&self) -> String {
match self {
Self::BuiltIn(b_in) => b_in.do_help(),
Self::Custom => "{not implemented}".to_owned(),
Self::Nop => "does nothing".to_owned(),
}
}
} }
enum BuiltIn { enum BuiltIn {
OnlineUsers, OnlineUsers,
TotalUsers, TotalUsers,
Help,
} }
impl BuiltIn { impl BuiltIn {
@@ -104,10 +117,15 @@ impl BuiltIn {
match b_in { match b_in {
oj_rc_core::persist::BuiltInChatOperation::OnlineUsers => Self::OnlineUsers, oj_rc_core::persist::BuiltInChatOperation::OnlineUsers => Self::OnlineUsers,
oj_rc_core::persist::BuiltInChatOperation::TotalUsers => Self::TotalUsers, oj_rc_core::persist::BuiltInChatOperation::TotalUsers => Self::TotalUsers,
oj_rc_core::persist::BuiltInChatOperation::Help => Self::Help,
} }
} }
fn do_command(&self, ctx: CommandContext) -> String { fn prettify_re<'a>(regex: &'a str) -> &'a str {
regex.trim_start_matches("\\")
}
async fn do_command<'b, 'c>(&self, ctx: CommandContext<'b, 'c>) -> String {
match self { match self {
Self::OnlineUsers => { Self::OnlineUsers => {
let online_count = ctx.chat_system.user_count(); let online_count = ctx.chat_system.user_count();
@@ -118,8 +136,35 @@ impl BuiltIn {
} }
}, },
Self::TotalUsers => { Self::TotalUsers => {
"User count is not supported".to_string() match ctx.user.get_total_registered_users().await {
Ok(count) => if count == 1 {
"1 user registered".to_owned()
} else {
format!("{} users registered", count)
},
Err(e) => e.error_msg().map(|x| x.to_owned()).unwrap_or_else(|| "Failed to retrieve registered users".to_owned()),
}
}, },
Self::Help => {
use core::fmt::Write;
let mut msg = String::new();
for command in ctx.chat_system.chat_config().commands.iter() {
let raw_re = command.regex.to_string();
let pretty_name = Self::prettify_re(&raw_re);
if let Err(e) = write!(msg, "\n{}: {}", pretty_name, command.op.help_str()) {
log::warn!("Failed to construct help for command `{}`: {}", pretty_name, e);
}
}
msg
}
}
}
fn do_help(&self) -> String {
match self {
Self::OnlineUsers => "Show total users online".to_owned(),
Self::TotalUsers => "Show total users registered".to_owned(),
Self::Help => "Display this message".to_owned(),
} }
} }
} }

View File

@@ -33,6 +33,7 @@ pub enum ChatOperation {
pub enum BuiltInChatOperation { pub enum BuiltInChatOperation {
OnlineUsers, OnlineUsers,
TotalUsers, TotalUsers,
Help,
} }

View File

@@ -1337,4 +1337,15 @@ impl super::ChatUser for UserData {
Err(crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16) Err(crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16)
} }
} }
async fn get_total_registered_users(&self) -> Result<u64, polariton_server::operations::SimpleOpError> {
self.db.user_count().await
.map_err(|e| {
log::error!("Failed to retrieve total user count for {}: {}", self.account.id, e);
polariton_server::operations::SimpleOpError::with_message(
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16,
format!("Failed to retrieve total user count: {}", e),
)
})
}
} }

View File

@@ -211,6 +211,7 @@ pub trait ChatUser: CommonUser {
//async fn has_pending_sanctions(&self) -> Result<bool, i16>; //async fn has_pending_sanctions(&self) -> Result<bool, i16>;
async fn get_sanctions(&self, username: String) -> Result<polariton::operation::Typed<()>, i16>; async fn get_sanctions(&self, username: String) -> Result<polariton::operation::Typed<()>, i16>;
async fn set_sanction(&self, sanction: SetSanction) -> Result<(), i16>; async fn set_sanction(&self, sanction: SetSanction) -> Result<(), i16>;
async fn get_total_registered_users(&self) -> Result<u64, polariton_server::operations::SimpleOpError>;
} }
pub struct SetSanction { pub struct SetSanction {

View File

@@ -15,6 +15,12 @@ impl Database {
}) })
} }
pub async fn user_count(&self) -> Result<u64, sea_orm::DbErr> {
crate::schema::user::Entity::find()
.count(&self.orm)
.await
}
pub async fn user_by_display_name(&self, public_id: String) -> Result<Option<crate::schema::user::Model>, sea_orm::DbErr> { pub async fn user_by_display_name(&self, public_id: String) -> Result<Option<crate::schema::user::Model>, sea_orm::DbErr> {
crate::schema::user::Entity::find() crate::schema::user::Entity::find()
.filter(crate::schema::user::Column::DisplayName.eq(public_id)) .filter(crate::schema::user::Column::DisplayName.eq(public_id))

View File

@@ -472,6 +472,13 @@ def main(asset_in, cubes=None, weapons=None, movement=None):
"type": "BuiltIn", "type": "BuiltIn",
"built_in": "TotalUsers" "built_in": "TotalUsers"
} }
},
{
"regex": "\\?help",
"op": {
"type": "BuiltIn",
"built_in": "Help"
}
} }
] ]
}, },