2025-04-14 21:17:41 -04:00
|
|
|
pub struct ChatSystemConfig {
|
|
|
|
|
command_channel: String,
|
|
|
|
|
commands: Vec<ChatCommand>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
#[derive(Clone, Copy)]
|
2025-05-23 16:46:12 -04:00
|
|
|
struct CommandContext<'a, 'b> {
|
2025-04-14 21:17:41 -04:00
|
|
|
chat_system: &'a super::ChatSystem,
|
2025-09-09 18:15:54 -04:00
|
|
|
user: &'b dyn oj_rc_core::persist::user::ChatUser,
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ChatSystemConfig {
|
2025-06-03 21:09:03 -04:00
|
|
|
pub fn from_persist(config: oj_rc_core::persist::config::ChatSystemConfig) -> std::io::Result<Self> {
|
2025-04-14 21:17:41 -04:00
|
|
|
let mut compiled_commands = Vec::with_capacity(config.commands.len());
|
|
|
|
|
for (i, cmd) in config.commands.into_iter().enumerate() {
|
|
|
|
|
let compiled_command = ChatCommand::compile_command(cmd).map_err(|e| {
|
|
|
|
|
log::error!("Failed to load command {}: {}", i, e);
|
|
|
|
|
std::io::Error::new(std::io::ErrorKind::InvalidInput, e)
|
|
|
|
|
})?;
|
|
|
|
|
compiled_commands.push(compiled_command);
|
|
|
|
|
}
|
|
|
|
|
Ok(Self {
|
|
|
|
|
command_channel: config.command_channel,
|
|
|
|
|
commands: compiled_commands,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-09 18:15:54 -04:00
|
|
|
pub async fn perform_command(&self, text: &str, chat_system: &super::ChatSystem, user: &dyn oj_rc_core::persist::user::ChatUser,) -> String {
|
2025-04-14 21:17:41 -04:00
|
|
|
let ctx = CommandContext {
|
|
|
|
|
chat_system,
|
|
|
|
|
user,
|
|
|
|
|
};
|
|
|
|
|
for cmd in self.commands.iter() {
|
2025-09-09 18:15:54 -04:00
|
|
|
if let Some(result) = cmd.perform_if_match(text, ctx).await {
|
2025-04-14 21:17:41 -04:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-03 22:33:26 -04:00
|
|
|
"Invalid command".to_owned()
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_command_channel(&self, channel: &str) -> bool {
|
2025-12-21 10:24:22 -05:00
|
|
|
self.command_channel.to_lowercase() == channel.to_lowercase()
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_command_user(&self, username: &str) -> bool {
|
|
|
|
|
self.command_channel == username
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn command_username(&self) -> &'_ str {
|
|
|
|
|
&self.command_channel
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct ChatCommand {
|
|
|
|
|
regex: regex::Regex,
|
|
|
|
|
op: ChatOperation,
|
2025-10-31 21:02:10 -04:00
|
|
|
perms: ExecutePermission,
|
2026-01-04 17:13:31 -05:00
|
|
|
is_hidden: bool,
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ChatCommand {
|
2025-06-03 21:09:03 -04:00
|
|
|
fn compile_command(command: oj_rc_core::persist::ChatCommand) -> Result<Self, regex::Error> {
|
2025-04-14 21:17:41 -04:00
|
|
|
Ok(Self {
|
|
|
|
|
regex: regex::RegexBuilder::new(&command.regex).build()?,
|
2025-10-31 21:02:10 -04:00
|
|
|
op: ChatOperation::from_persist(command.op),
|
|
|
|
|
perms: ExecutePermission::from_persist(command.permission),
|
2026-01-04 17:13:31 -05:00
|
|
|
is_hidden: command.hidden,
|
2025-04-14 21:17:41 -04:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-09 18:15:54 -04:00
|
|
|
async fn perform_if_match<'b, 'c>(&self, text: &str, ctx: CommandContext<'b, 'c>) -> Option<String> {
|
|
|
|
|
if let Some(cap) = self.regex.captures(text) {
|
2025-10-31 21:02:10 -04:00
|
|
|
if self.perms.has_perms(ctx.user) {
|
|
|
|
|
Some(self.op.perform_command(text, cap, ctx).await)
|
|
|
|
|
} else {
|
|
|
|
|
log::warn!("User {} tried to run command {} without sufficient permissions", ctx.user.public_id(), self.regex);
|
|
|
|
|
None
|
|
|
|
|
}
|
2025-09-09 18:15:54 -04:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ChatOperation {
|
|
|
|
|
BuiltIn(BuiltIn),
|
|
|
|
|
Custom,
|
|
|
|
|
Nop,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ChatOperation {
|
2025-06-03 21:09:03 -04:00
|
|
|
fn from_persist(op: oj_rc_core::persist::ChatOperation) -> Self {
|
2025-04-14 21:17:41 -04:00
|
|
|
match op {
|
2025-06-03 21:09:03 -04:00
|
|
|
oj_rc_core::persist::ChatOperation::BuiltIn(b_in) => Self::BuiltIn(BuiltIn::from_persist(b_in)),
|
|
|
|
|
oj_rc_core::persist::ChatOperation::Custom => Self::Custom,
|
|
|
|
|
oj_rc_core::persist::ChatOperation::Nop => Self::Nop,
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-26 15:43:51 -04:00
|
|
|
async fn perform_command<'a, 'b, 'c>(&self, text: &str, _captures: regex::Captures<'a>, ctx: CommandContext<'b, 'c>) -> String {
|
2025-04-14 21:17:41 -04:00
|
|
|
match self {
|
2025-10-26 15:43:51 -04:00
|
|
|
Self::BuiltIn(b_in) => b_in.do_command(text, ctx).await,
|
2025-04-14 21:17:41 -04:00
|
|
|
Self::Custom => "{not implemented}".to_owned(),
|
|
|
|
|
Self::Nop => "{no op}".to_owned(),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-09 18:15:54 -04:00
|
|
|
|
|
|
|
|
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(),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum BuiltIn {
|
2025-10-26 15:43:51 -04:00
|
|
|
Intercom(Intercom),
|
2025-12-26 20:46:05 -05:00
|
|
|
System(System),
|
2025-04-14 21:17:41 -04:00
|
|
|
OnlineUsers,
|
|
|
|
|
TotalUsers,
|
2025-10-31 22:41:33 -04:00
|
|
|
Stats,
|
2025-09-14 12:23:37 -04:00
|
|
|
Version,
|
2025-09-09 18:15:54 -04:00
|
|
|
Help,
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BuiltIn {
|
2025-06-03 21:09:03 -04:00
|
|
|
fn from_persist(b_in: oj_rc_core::persist::BuiltInChatOperation) -> Self {
|
2025-04-14 21:17:41 -04:00
|
|
|
match b_in {
|
2025-10-26 15:43:51 -04:00
|
|
|
oj_rc_core::persist::BuiltInChatOperation::Intercom(com) => Self::Intercom(Intercom::from_persist(com)),
|
2025-12-26 20:46:05 -05:00
|
|
|
oj_rc_core::persist::BuiltInChatOperation::System(sys) => Self::System(System::from_persist(sys)),
|
2025-06-03 21:09:03 -04:00
|
|
|
oj_rc_core::persist::BuiltInChatOperation::OnlineUsers => Self::OnlineUsers,
|
|
|
|
|
oj_rc_core::persist::BuiltInChatOperation::TotalUsers => Self::TotalUsers,
|
2025-10-31 22:41:33 -04:00
|
|
|
oj_rc_core::persist::BuiltInChatOperation::Stats => Self::Stats,
|
2025-09-14 12:23:37 -04:00
|
|
|
oj_rc_core::persist::BuiltInChatOperation::Version => Self::Version,
|
2025-09-09 18:15:54 -04:00
|
|
|
oj_rc_core::persist::BuiltInChatOperation::Help => Self::Help,
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 19:40:51 -04:00
|
|
|
fn prettify_re(regex: &str) -> &str {
|
2025-09-09 18:15:54 -04:00
|
|
|
regex.trim_start_matches("\\")
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-26 15:43:51 -04:00
|
|
|
async fn do_command<'b, 'c>(&self, text: &str, ctx: CommandContext<'b, 'c>) -> String {
|
2025-04-14 21:17:41 -04:00
|
|
|
match self {
|
2025-10-26 15:43:51 -04:00
|
|
|
Self::Intercom(intercom) => intercom.do_command(text, ctx).await,
|
2025-12-26 20:46:05 -05:00
|
|
|
Self::System(sys) => sys.do_command(text, ctx).await,
|
2025-04-14 21:17:41 -04:00
|
|
|
Self::OnlineUsers => {
|
|
|
|
|
let online_count = ctx.chat_system.user_count();
|
|
|
|
|
if online_count == 1 {
|
|
|
|
|
"1 user online".to_owned()
|
|
|
|
|
} else {
|
|
|
|
|
format!("{} users online", online_count)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Self::TotalUsers => {
|
2025-09-09 18:15:54 -04:00
|
|
|
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()),
|
|
|
|
|
}
|
2025-04-14 21:17:41 -04:00
|
|
|
},
|
2025-10-31 22:41:33 -04:00
|
|
|
Self::Stats => {
|
|
|
|
|
let mut stats = Vec::new();
|
|
|
|
|
for variant in text.trim().split(' ').skip(1) {
|
|
|
|
|
match variant {
|
|
|
|
|
"db" | "database" => {
|
|
|
|
|
let db_stats = format!("(chat db) {}", ctx.user.db_metrics().await);
|
|
|
|
|
stats.push(db_stats);
|
2026-01-05 20:34:53 -05:00
|
|
|
let counters = ctx.user.db_counters().await;
|
|
|
|
|
for (key, val) in counters {
|
|
|
|
|
stats.push(format!("(db) {}: {}", key, val));
|
|
|
|
|
}
|
2025-10-31 22:41:33 -04:00
|
|
|
},
|
|
|
|
|
"perms" | "permission" | "permissions" => {
|
2026-01-05 20:34:53 -05:00
|
|
|
let com_stats = format!("(perms {}) mod:{} admin:{} dev:{} banned:{} r:{}",
|
2025-10-31 22:41:33 -04:00
|
|
|
ctx.user.public_id(),
|
|
|
|
|
ctx.user.is_mod(),
|
|
|
|
|
ctx.user.is_admin(),
|
|
|
|
|
ctx.user.is_dev(),
|
|
|
|
|
ctx.user.is_banned(),
|
|
|
|
|
ctx.user.is_royal(),
|
|
|
|
|
);
|
|
|
|
|
stats.push(com_stats);
|
|
|
|
|
},
|
|
|
|
|
"up" | "uptime" => {
|
|
|
|
|
let now = chrono::Utc::now().timestamp();
|
|
|
|
|
let startup_timestamp = crate::START_TIMESTAMP_S.load(std::sync::atomic::Ordering::Relaxed);
|
|
|
|
|
let uptime_delta = now - startup_timestamp;
|
|
|
|
|
let uptime_str = if uptime_delta <= 0 {
|
|
|
|
|
"0?".to_owned()
|
|
|
|
|
} else if uptime_delta < 60 {
|
|
|
|
|
format!("{}s", uptime_delta)
|
|
|
|
|
} else if uptime_delta < 24 * 60 * 60 {
|
|
|
|
|
format!("{}:{:02}", uptime_delta / (60 * 60), (uptime_delta % (60 * 60)) / 60)
|
|
|
|
|
} else {
|
|
|
|
|
format!("{} days {}:{:02}", uptime_delta / (24 * 60 * 60), (uptime_delta % (24 * 60 * 60)) / (60 * 60), ((uptime_delta % (24 * 60 * 60)) % (60 * 60)) / 60)
|
|
|
|
|
};
|
|
|
|
|
let ready_ns = crate::READY_DURATION_NS.load(std::sync::atomic::Ordering::Relaxed);
|
|
|
|
|
let uptime_stats = format!("(uptime) {}, startup in {}ns", uptime_str, ready_ns);
|
|
|
|
|
stats.push(uptime_stats);
|
|
|
|
|
},
|
|
|
|
|
idk => {
|
|
|
|
|
let stat = format!("(unknown stat) {}", idk);
|
|
|
|
|
stats.push(stat);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if stats.is_empty() {
|
2025-12-26 16:16:23 -05:00
|
|
|
stats.push("TODO: general stats (try db)".to_owned());
|
2025-10-31 22:41:33 -04:00
|
|
|
}
|
|
|
|
|
stats.join("\n")
|
|
|
|
|
},
|
2025-09-14 12:23:37 -04:00
|
|
|
Self::Version => {
|
|
|
|
|
let name = env!("CARGO_PKG_NAME");
|
|
|
|
|
let version = env!("CARGO_PKG_VERSION");
|
|
|
|
|
let git_version = git_version::git_version!(args = ["--always", "--dirty=+"]);
|
|
|
|
|
let authors = env!("CARGO_PKG_AUTHORS");
|
|
|
|
|
let license = env!("CARGO_PKG_LICENSE");
|
|
|
|
|
let repo = env!("CARGO_PKG_REPOSITORY");
|
|
|
|
|
format!("{} {}:{}\n[{}]\n{} {}", name, version, git_version, authors, license, repo)
|
|
|
|
|
}
|
2025-09-09 18:15:54 -04:00
|
|
|
Self::Help => {
|
|
|
|
|
use core::fmt::Write;
|
2026-01-04 17:13:31 -05:00
|
|
|
let force_all = text.trim().split(' ').any(|word| word == "all");
|
2025-09-09 18:15:54 -04:00
|
|
|
let mut msg = String::new();
|
|
|
|
|
for command in ctx.chat_system.chat_config().commands.iter() {
|
2026-01-04 17:13:31 -05:00
|
|
|
if command.is_hidden { continue; }
|
2025-10-31 21:02:10 -04:00
|
|
|
if command.perms.has_perms(ctx.user) || force_all {
|
|
|
|
|
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);
|
|
|
|
|
}
|
2025-09-09 18:15:54 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
msg
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn do_help(&self) -> String {
|
|
|
|
|
match self {
|
2025-10-26 15:43:51 -04:00
|
|
|
Self::Intercom(i) => i.do_help(),
|
2025-12-26 20:46:05 -05:00
|
|
|
Self::System(s) => s.do_help(),
|
2025-09-09 18:15:54 -04:00
|
|
|
Self::OnlineUsers => "Show total users online".to_owned(),
|
|
|
|
|
Self::TotalUsers => "Show total users registered".to_owned(),
|
2026-01-05 20:34:53 -05:00
|
|
|
Self::Stats => "Show server metrics (db|perms|uptime|counts)".to_owned(),
|
2025-09-14 12:23:37 -04:00
|
|
|
Self::Version => "Show chat server version information".to_owned(),
|
2025-09-09 18:15:54 -04:00
|
|
|
Self::Help => "Display this message".to_owned(),
|
2025-04-14 21:17:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-26 15:43:51 -04:00
|
|
|
|
|
|
|
|
enum Intercom {
|
|
|
|
|
DevMessage,
|
2025-10-31 21:02:10 -04:00
|
|
|
DevBroadcast,
|
|
|
|
|
Maintenance,
|
2025-10-26 15:43:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Intercom {
|
|
|
|
|
fn from_persist(intercom: oj_rc_core::persist::IntercomChatOperation) -> Self {
|
|
|
|
|
match intercom {
|
|
|
|
|
oj_rc_core::persist::IntercomChatOperation::DevMessage => Self::DevMessage,
|
2025-10-31 21:02:10 -04:00
|
|
|
oj_rc_core::persist::IntercomChatOperation::DevBroadcast => Self::DevBroadcast,
|
|
|
|
|
oj_rc_core::persist::IntercomChatOperation::Maintenance => Self::Maintenance,
|
2025-10-26 15:43:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn do_command<'b, 'c>(&self, text: &str, ctx: CommandContext<'b, 'c>) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
Self::DevMessage => {
|
|
|
|
|
let pub_id = ctx.user.public_id();
|
|
|
|
|
ctx.user.show_dev_message(
|
|
|
|
|
oj_rc_core::persist::user::intercom::IntercomDevMessage {
|
|
|
|
|
message: text.trim().split_once(' ').map(|x| x.1.to_owned()).unwrap_or_else(|| "???".to_owned()),
|
|
|
|
|
duration: 10,
|
|
|
|
|
},
|
|
|
|
|
vec![pub_id.to_owned()],
|
|
|
|
|
).await;
|
|
|
|
|
format!("Sent dev message to {}", pub_id)
|
2025-10-31 21:02:10 -04:00
|
|
|
},
|
|
|
|
|
Self::DevBroadcast => {
|
|
|
|
|
if let Some(message) = text.trim().split_once(' ').map(|x| x.1.to_owned()) {
|
|
|
|
|
ctx.user.show_dev_message(
|
|
|
|
|
oj_rc_core::persist::user::intercom::IntercomDevMessage {
|
|
|
|
|
message,
|
|
|
|
|
duration: 60,
|
|
|
|
|
},
|
|
|
|
|
vec![],
|
|
|
|
|
).await;
|
2025-12-26 16:16:23 -05:00
|
|
|
"Sent dev broadcast to everyone".to_owned()
|
2025-10-31 21:02:10 -04:00
|
|
|
} else {
|
2025-12-26 16:16:23 -05:00
|
|
|
"Missing dev message, did not send".to_owned()
|
2025-10-31 21:02:10 -04:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Self::Maintenance => {
|
|
|
|
|
if let Some(message) = text.trim().split_once(' ').map(|x| x.1.to_owned()) {
|
|
|
|
|
ctx.user.enter_maintenance(
|
|
|
|
|
oj_rc_core::persist::user::intercom::IntercomMaintenanceMessage { message },
|
|
|
|
|
vec![],
|
|
|
|
|
).await;
|
2025-12-26 16:16:23 -05:00
|
|
|
"Sent maintenance message".to_owned()
|
2025-10-31 21:02:10 -04:00
|
|
|
} else {
|
2025-12-26 16:16:23 -05:00
|
|
|
"Missing maintenance message, did not send".to_owned()
|
2025-10-31 21:02:10 -04:00
|
|
|
}
|
|
|
|
|
|
2025-10-26 15:43:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn do_help(&self) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
Self::DevMessage => "Show dev message to yourself".to_owned(),
|
2025-10-31 21:02:10 -04:00
|
|
|
Self::DevBroadcast => "Show dev message to everyone".to_owned(),
|
|
|
|
|
Self::Maintenance => "Broadcast maintenance mode to everyone".to_owned(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-26 20:46:05 -05:00
|
|
|
enum System {
|
|
|
|
|
Permissions,
|
2026-06-20 19:27:53 -04:00
|
|
|
ClearGarageFactoryFlag,
|
2025-12-26 20:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum SystemPermission {
|
|
|
|
|
Mod,
|
|
|
|
|
NotMod,
|
|
|
|
|
Admin,
|
|
|
|
|
NotAdmin,
|
|
|
|
|
Dev,
|
|
|
|
|
NotDev,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SystemPermission {
|
|
|
|
|
fn from_str(s: &str) -> Option<Self> {
|
|
|
|
|
match &s.to_lowercase() as &str {
|
|
|
|
|
"mod" | "moderator" => Some(Self::Mod),
|
|
|
|
|
"!mod" | "!moderator" | "unmod" | "notmod" => Some(Self::NotMod),
|
|
|
|
|
"admin" | "administrator" => Some(Self::Admin),
|
|
|
|
|
"!admin" | "!administrator" | "nadmin" | "notadmin" => Some(Self::NotAdmin),
|
|
|
|
|
"dev" | "developer" => Some(Self::Dev),
|
|
|
|
|
"!dev" | "!developer" | "notdev" => Some(Self::NotDev),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn display(&self) -> &'static str {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Mod => "Moderator",
|
|
|
|
|
Self::NotMod => "!Moderator",
|
|
|
|
|
Self::Admin => "Administrator",
|
|
|
|
|
Self::NotAdmin => "!Administrator",
|
|
|
|
|
Self::Dev => "Developer",
|
|
|
|
|
Self::NotDev => "!Developer",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn role(&self) -> oj_rc_core::persist::user::UserRole {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Mod => oj_rc_core::persist::user::UserRole::Moderator,
|
|
|
|
|
Self::NotMod => oj_rc_core::persist::user::UserRole::Moderator,
|
|
|
|
|
Self::Admin => oj_rc_core::persist::user::UserRole::Administrator,
|
|
|
|
|
Self::NotAdmin => oj_rc_core::persist::user::UserRole::Administrator,
|
|
|
|
|
Self::Dev => oj_rc_core::persist::user::UserRole::Developer,
|
|
|
|
|
Self::NotDev => oj_rc_core::persist::user::UserRole::Developer,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn value(&self) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Mod | Self::Admin | Self::Dev => true,
|
|
|
|
|
Self::NotMod | Self::NotAdmin | Self::NotDev => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl System {
|
|
|
|
|
fn from_persist(sys: oj_rc_core::persist::SystemChatOperation) -> Self {
|
|
|
|
|
match sys {
|
|
|
|
|
oj_rc_core::persist::SystemChatOperation::Permissions => Self::Permissions,
|
2026-06-20 19:27:53 -04:00
|
|
|
oj_rc_core::persist::SystemChatOperation::ClearGarageFactoryFlag => Self::ClearGarageFactoryFlag,
|
2025-12-26 20:46:05 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn do_command<'b, 'c>(&self, text: &str, ctx: CommandContext<'b, 'c>) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Permissions => {
|
|
|
|
|
let params: Vec<_> = text.trim().split(' ').collect();
|
|
|
|
|
if params.len() < 3 {
|
|
|
|
|
return "Not enough arguments\nusage: [command] [permission] [username]".to_owned();
|
|
|
|
|
} else if params.len() > 3 {
|
|
|
|
|
return "Too many arguments parameters\nusage: [command] [permission] [username]".to_owned();
|
|
|
|
|
}
|
|
|
|
|
let perm = SystemPermission::from_str(params[1]);
|
|
|
|
|
if perm.is_none() {
|
|
|
|
|
return format!("Unrecognised permission \"{}\" (try dev, admin, or mod)", ¶ms[1]);
|
|
|
|
|
}
|
|
|
|
|
let perm = perm.unwrap();
|
|
|
|
|
if let Err(e) = ctx.user.set_permission(params[2].to_owned(), perm.role(), perm.value()).await {
|
|
|
|
|
if let Some(msg) = e.error_msg() {
|
|
|
|
|
format!("Failed to grant permission: {}", msg)
|
|
|
|
|
} else {
|
|
|
|
|
format!("Failed to grant permission (code {})", e.error_code())
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
format!("Granted {} to {} (they should re-log)", perm.display(), ¶ms[2])
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-06-20 19:27:53 -04:00
|
|
|
Self::ClearGarageFactoryFlag => {
|
|
|
|
|
match ctx.user.clear_factory_flag().await {
|
|
|
|
|
Err(e) => {
|
|
|
|
|
if let Some(msg) = e.error_msg() {
|
|
|
|
|
format!("Failed to clear flag: {}", msg)
|
|
|
|
|
} else {
|
|
|
|
|
format!("Failed to clear flag (code {})", e.error_code())
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Ok(is_changed) => {
|
|
|
|
|
if is_changed {
|
|
|
|
|
"Cleared flag from selected garage".to_owned()
|
|
|
|
|
} else {
|
|
|
|
|
"Cleared flag from selected garage (but it already was?)".to_owned()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-26 20:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn do_help(&self) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Permissions => "Grant permissions to an account".to_owned(),
|
2026-06-20 19:27:53 -04:00
|
|
|
Self::ClearGarageFactoryFlag => "Clear crf_id on currently-selected garage".to_owned(),
|
2025-12-26 20:46:05 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 21:02:10 -04:00
|
|
|
enum ExecutePermission {
|
|
|
|
|
Player,
|
|
|
|
|
Moderator,
|
|
|
|
|
Administrator,
|
|
|
|
|
Developer,
|
|
|
|
|
Royal,
|
|
|
|
|
None,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ExecutePermission {
|
|
|
|
|
fn from_persist(perm: oj_rc_core::persist::ChatPermission) -> Self {
|
|
|
|
|
match perm {
|
|
|
|
|
oj_rc_core::persist::ChatPermission::Player => Self::Player,
|
|
|
|
|
oj_rc_core::persist::ChatPermission::Moderator => Self::Moderator,
|
|
|
|
|
oj_rc_core::persist::ChatPermission::Administrator => Self::Administrator,
|
|
|
|
|
oj_rc_core::persist::ChatPermission::Developer => Self::Developer,
|
|
|
|
|
oj_rc_core::persist::ChatPermission::Royal => Self::Royal,
|
|
|
|
|
oj_rc_core::persist::ChatPermission::None => Self::None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn has_perms(&self, user: &dyn oj_rc_core::persist::user::CommonUser) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
Self::Player => true,
|
|
|
|
|
Self::Moderator => user.is_mod() || user.is_admin() || user.is_dev() || user.is_royal(),
|
|
|
|
|
Self::Administrator => user.is_admin() || user.is_dev() || user.is_royal(),
|
|
|
|
|
Self::Developer => user.is_dev() || user.is_royal(),
|
|
|
|
|
Self::Royal => user.is_royal(),
|
|
|
|
|
Self::None => false,
|
2025-10-26 15:43:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|