mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add server-side chat command to grant account permissions in-game
This commit is contained in:
@@ -14508,6 +14508,24 @@
|
|||||||
},
|
},
|
||||||
"permission": "Developer"
|
"permission": "Developer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"regex": "\\?grant",
|
||||||
|
"op": {
|
||||||
|
"type": "BuiltIn",
|
||||||
|
"built_in": "System",
|
||||||
|
"system": "Permissions"
|
||||||
|
},
|
||||||
|
"permission": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": "\\?perms",
|
||||||
|
"op": {
|
||||||
|
"type": "BuiltIn",
|
||||||
|
"built_in": "System",
|
||||||
|
"system": "CheckPermissions"
|
||||||
|
},
|
||||||
|
"permission": "Player"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"regex": "\\?help",
|
"regex": "\\?help",
|
||||||
"op": {
|
"op": {
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ impl ChatOperation {
|
|||||||
|
|
||||||
enum BuiltIn {
|
enum BuiltIn {
|
||||||
Intercom(Intercom),
|
Intercom(Intercom),
|
||||||
|
System(System),
|
||||||
OnlineUsers,
|
OnlineUsers,
|
||||||
TotalUsers,
|
TotalUsers,
|
||||||
Stats,
|
Stats,
|
||||||
@@ -126,6 +127,7 @@ impl BuiltIn {
|
|||||||
fn from_persist(b_in: oj_rc_core::persist::BuiltInChatOperation) -> Self {
|
fn from_persist(b_in: oj_rc_core::persist::BuiltInChatOperation) -> Self {
|
||||||
match b_in {
|
match b_in {
|
||||||
oj_rc_core::persist::BuiltInChatOperation::Intercom(com) => Self::Intercom(Intercom::from_persist(com)),
|
oj_rc_core::persist::BuiltInChatOperation::Intercom(com) => Self::Intercom(Intercom::from_persist(com)),
|
||||||
|
oj_rc_core::persist::BuiltInChatOperation::System(sys) => Self::System(System::from_persist(sys)),
|
||||||
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::Stats => Self::Stats,
|
oj_rc_core::persist::BuiltInChatOperation::Stats => Self::Stats,
|
||||||
@@ -141,6 +143,7 @@ impl BuiltIn {
|
|||||||
async fn do_command<'b, 'c>(&self, text: &str, ctx: CommandContext<'b, 'c>) -> String {
|
async fn do_command<'b, 'c>(&self, text: &str, ctx: CommandContext<'b, 'c>) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Intercom(intercom) => intercom.do_command(text, ctx).await,
|
Self::Intercom(intercom) => intercom.do_command(text, ctx).await,
|
||||||
|
Self::System(sys) => sys.do_command(text, ctx).await,
|
||||||
Self::OnlineUsers => {
|
Self::OnlineUsers => {
|
||||||
let online_count = ctx.chat_system.user_count();
|
let online_count = ctx.chat_system.user_count();
|
||||||
if online_count == 1 {
|
if online_count == 1 {
|
||||||
@@ -236,6 +239,7 @@ impl BuiltIn {
|
|||||||
fn do_help(&self) -> String {
|
fn do_help(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Intercom(i) => i.do_help(),
|
Self::Intercom(i) => i.do_help(),
|
||||||
|
Self::System(s) => s.do_help(),
|
||||||
Self::OnlineUsers => "Show total users online".to_owned(),
|
Self::OnlineUsers => "Show total users online".to_owned(),
|
||||||
Self::TotalUsers => "Show total users registered".to_owned(),
|
Self::TotalUsers => "Show total users registered".to_owned(),
|
||||||
Self::Stats => "Show server metrics (db|perms)".to_owned(),
|
Self::Stats => "Show server metrics (db|perms)".to_owned(),
|
||||||
@@ -312,6 +316,114 @@ impl Intercom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum System {
|
||||||
|
Permissions,
|
||||||
|
CheckPermissions,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
oj_rc_core::persist::SystemChatOperation::CheckPermissions => Self::CheckPermissions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::CheckPermissions => {
|
||||||
|
let is_royal = ctx.user.is_royal();
|
||||||
|
let is_dev = ctx.user.is_dev();
|
||||||
|
let is_admin = ctx.user.is_dev();
|
||||||
|
let is_mod = ctx.user.is_mod();
|
||||||
|
format!("r:{} dev:{} adm:{} mod:{}", is_royal as u8, is_dev as u8, is_admin as u8, is_mod as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_help(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Permissions => "Grant permissions to an account".to_owned(),
|
||||||
|
Self::CheckPermissions => "Display permissions for current account".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ExecutePermission {
|
enum ExecutePermission {
|
||||||
Player,
|
Player,
|
||||||
Moderator,
|
Moderator,
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ impl super::config::SelfValidator for ChatCommand {
|
|||||||
self.op,
|
self.op,
|
||||||
ChatOperation::BuiltIn(BuiltInChatOperation::Intercom(IntercomChatOperation::DevBroadcast))
|
ChatOperation::BuiltIn(BuiltInChatOperation::Intercom(IntercomChatOperation::DevBroadcast))
|
||||||
| ChatOperation::BuiltIn(BuiltInChatOperation::Intercom(IntercomChatOperation::Maintenance))
|
| ChatOperation::BuiltIn(BuiltInChatOperation::Intercom(IntercomChatOperation::Maintenance))
|
||||||
|
| ChatOperation::BuiltIn(BuiltInChatOperation::System(SystemChatOperation::Permissions))
|
||||||
) {
|
) {
|
||||||
if !matches!(self.permission, ChatPermission::Administrator | ChatPermission::Developer | ChatPermission::Royal) {
|
if !matches!(self.permission, ChatPermission::Administrator | ChatPermission::Developer | ChatPermission::Royal) {
|
||||||
info.warn(crate::persist::config::ValidationMessage {
|
info.warn(crate::persist::config::ValidationMessage {
|
||||||
@@ -115,6 +116,7 @@ pub enum ChatOperation {
|
|||||||
#[serde(tag = "built_in")]
|
#[serde(tag = "built_in")]
|
||||||
pub enum BuiltInChatOperation {
|
pub enum BuiltInChatOperation {
|
||||||
Intercom(IntercomChatOperation),
|
Intercom(IntercomChatOperation),
|
||||||
|
System(SystemChatOperation),
|
||||||
OnlineUsers,
|
OnlineUsers,
|
||||||
TotalUsers,
|
TotalUsers,
|
||||||
Stats,
|
Stats,
|
||||||
@@ -130,6 +132,13 @@ pub enum IntercomChatOperation {
|
|||||||
Maintenance,
|
Maintenance,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(tag = "system")]
|
||||||
|
pub enum SystemChatOperation {
|
||||||
|
Permissions,
|
||||||
|
CheckPermissions,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn default_pub_channs() -> Vec<String> {
|
fn default_pub_channs() -> Vec<String> {
|
||||||
vec![
|
vec![
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ mod settings;
|
|||||||
pub use settings::{Settings, QueueMode};
|
pub use settings::{Settings, QueueMode};
|
||||||
|
|
||||||
mod chat;
|
mod chat;
|
||||||
pub use chat::{ChatConfig, ChatCommand, ChatOperation, BuiltInChatOperation, IntercomChatOperation, ChatPermission};
|
pub use chat::{ChatConfig, ChatCommand, ChatOperation, BuiltInChatOperation, IntercomChatOperation, ChatPermission, SystemChatOperation};
|
||||||
|
|
||||||
mod vehicle_factory;
|
mod vehicle_factory;
|
||||||
pub use vehicle_factory::{FactoryConfig, AdapterSettings, ArcFactorySettings};
|
pub use vehicle_factory::{FactoryConfig, AdapterSettings, ArcFactorySettings};
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ impl UserData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_perms_to_exec(&self, ty: &super::SanctionType) -> Result<(), i16> {
|
pub(super) fn check_perms_to_exec(&self, ty: &super::SanctionType) -> Result<(), i16> {
|
||||||
match ty {
|
match ty {
|
||||||
super::SanctionType::Warn
|
super::SanctionType::Warn
|
||||||
| super::SanctionType::Mute
|
| super::SanctionType::Mute
|
||||||
@@ -1186,177 +1186,3 @@ impl super::GameEventSetter for GameEventSetterImpl {
|
|||||||
self.select_event_now(oj_rc_database::schema::game_event::EventVariant::Singleplayer).await
|
self.select_event_now(oj_rc_database::schema::game_event::EventVariant::Singleplayer).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl super::ChatUser for UserData {
|
|
||||||
async fn subscribed_channels(&self) -> Result<polariton::operation::Typed<()>, i16> {
|
|
||||||
let channels = self.subscribed_channels_strings().await?;
|
|
||||||
log::info!("User is subscribed to channels {:?}", channels);
|
|
||||||
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
|
||||||
ty: polariton::serdes::TypePrefix::HashMap, // hashtable
|
|
||||||
items: channels.into_iter().map(|name| crate::data::channel::ChatChannelInfo {
|
|
||||||
channel_name: name,
|
|
||||||
members: vec![
|
|
||||||
crate::data::channel::ChatChannelMember {
|
|
||||||
name: self.account.display_name.clone(),
|
|
||||||
use_custom_avatar: false,
|
|
||||||
state: crate::data::channel::ChatPlayerState::Idk0,
|
|
||||||
custom_avatar: Vec::default(),
|
|
||||||
avatar_id: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
channel_ty: crate::data::channel::ChatChannelType::Public,
|
|
||||||
}.as_transmissible()).collect()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16> {
|
|
||||||
let channels = self.db.user_aux_by_user_id_and_descriptor(self.account.id, oj_rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| {
|
|
||||||
log::error!("Failed to retrieve SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?.ok_or_else(|| {
|
|
||||||
log::error!("Failed to find SubscribedChannels (user_aux) for user_id {}", self.account.id);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
let channels = serde_json::from_str::<Vec<String>>(&channels.data).map_err(|e| {
|
|
||||||
log::error!("Failed to parse SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
Ok(channels)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<polariton::operation::Typed<()>, i16> {
|
|
||||||
if let crate::data::channel::ChatChannelType::Public = channel_ty {
|
|
||||||
let mut sub_channels = self.subscribed_channels_strings().await?;
|
|
||||||
sub_channels.push(channel.clone());
|
|
||||||
let new_data = serde_json::to_string(&sub_channels).map_err(|e| {
|
|
||||||
log::error!("Failed to convert to JSON SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
self.db.update_user_aux_by_user_id_and_descriptor(oj_rc_database::schema::user_aux::ActiveModel {
|
|
||||||
data: oj_rc_database::sea_orm::ActiveValue::Set(new_data),
|
|
||||||
..Default::default()
|
|
||||||
}, self.account.id, oj_rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| {
|
|
||||||
log::error!("Failed to update SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(crate::data::channel::ChatChannelInfo {
|
|
||||||
channel_name: channel,
|
|
||||||
members: Vec::default(),
|
|
||||||
channel_ty,
|
|
||||||
}.as_transmissible())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<(), i16> {
|
|
||||||
if let crate::data::channel::ChatChannelType::Public = channel_ty {
|
|
||||||
let mut sub_channels = self.subscribed_channels_strings().await?;
|
|
||||||
if let Some(index) = sub_channels.iter().position(|chann| chann == &channel) {
|
|
||||||
sub_channels.swap_remove(index);
|
|
||||||
let new_data = serde_json::to_string(&sub_channels).map_err(|e| {
|
|
||||||
log::error!("Failed to convert to JSON SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
self.db.update_user_aux_by_user_id_and_descriptor(oj_rc_database::schema::user_aux::ActiveModel {
|
|
||||||
data: oj_rc_database::sea_orm::ActiveValue::Set(new_data),
|
|
||||||
..Default::default()
|
|
||||||
}, self.account.id, oj_rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| {
|
|
||||||
log::error!("Failed to update SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/*async fn has_pending_sanctions(&self) -> Result<bool, i16> {
|
|
||||||
let count = self.db.count_sanctions_to_ack_by_user_id_and_descriptor(self.account.id, oj_rc_database::schema::sanction::Descriptor::Warn).await.map_err(|e| {
|
|
||||||
log::error!("Failed to count pending sanctions for user_id {}: {}", self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
Ok(count != 0)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
async fn get_sanctions(&self, username: String) -> Result<polariton::operation::Typed<()>, i16> {
|
|
||||||
let user_opt = self.db.user_by_display_name(username.clone()).await.map_err(|e| {
|
|
||||||
log::error!("Failed to retrieve user by username {} for user_id {}: {}", username, self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
if let Some(user) = user_opt {
|
|
||||||
let sanctions = self.db.sanctions_by_user_id(user.id).await.map_err(|e| {
|
|
||||||
log::error!("Failed to retrieve sanctions by username {} for user_id {}: {}", username, self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
|
||||||
ty: polariton::serdes::TypePrefix::Str,
|
|
||||||
items: sanctions.into_iter().map(|x| {
|
|
||||||
let data = crate::data::sanction::SanctionJson {
|
|
||||||
type_: crate::data::sanction::SanctionType::from_db(x.descriptor),
|
|
||||||
reason: x.reason,
|
|
||||||
reporter: x.issuer_name,
|
|
||||||
issued: chrono::DateTime::from_timestamp(x.creation_time, 0).unwrap(),
|
|
||||||
};
|
|
||||||
polariton::operation::Typed::Str(data.as_json().into())
|
|
||||||
}).collect(),
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
Err(crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_sanction(&self, sanction: super::SetSanction) -> Result<(), i16> {
|
|
||||||
self.check_perms_to_exec(&sanction.type_)?;
|
|
||||||
let user_opt = self.db.user_by_display_name(sanction.username.clone()).await.map_err(|e| {
|
|
||||||
log::error!("Failed to retrieve user by username {} for user_id {}: {}", sanction.username, self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
if let Some(user) = user_opt {
|
|
||||||
if sanction.is_adding {
|
|
||||||
let now = chrono::Utc::now().timestamp();
|
|
||||||
let sanction_ty = crate::data::sanction::SanctionType::from_persist(sanction.type_).to_db();
|
|
||||||
let to_add = oj_rc_database::schema::sanction::ActiveModel {
|
|
||||||
user_id: oj_rc_database::sea_orm::ActiveValue::Set(user.id),
|
|
||||||
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
|
|
||||||
issuer_id: oj_rc_database::sea_orm::ActiveValue::Set(self.account.id),
|
|
||||||
issuer_name: oj_rc_database::sea_orm::ActiveValue::Set(self.account.display_name.clone()),
|
|
||||||
descriptor: oj_rc_database::sea_orm::ActiveValue::Set(sanction_ty.clone()),
|
|
||||||
reason: oj_rc_database::sea_orm::ActiveValue::Set(sanction.reason),
|
|
||||||
duration: oj_rc_database::sea_orm::ActiveValue::Set(if sanction.duration <= 0 { None } else { Some(sanction.duration as i64) }),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
if matches!(sanction_ty, oj_rc_database::schema::sanction::Descriptor::Ban) {
|
|
||||||
self.db.update_perms_by_user_id(oj_rc_database::schema::permissions::ActiveModel {
|
|
||||||
banned: oj_rc_database::sea_orm::ActiveValue::Set(true),
|
|
||||||
..Default::default()
|
|
||||||
}, user.id).await.map_err(|e| {
|
|
||||||
log::error!("Failed to update permissions (to ban) for user_id {} by user_id {}: {}", user.id, self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
self.db.insert_sanction(to_add).await.map_err(|e| {
|
|
||||||
log::error!("Failed to insert sanction for user_id {} by user_id {}: {}", user.id, self.account.id, e);
|
|
||||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
// FIXME
|
|
||||||
log::error!("Modifying sanctions is not currently supported");
|
|
||||||
Err(crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
217
rc_core/src/persist/user/chat.rs
Normal file
217
rc_core/src/persist/user/chat.rs
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
use super::account_json::UserData;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl super::ChatUser for UserData {
|
||||||
|
async fn subscribed_channels(&self) -> Result<polariton::operation::Typed<()>, i16> {
|
||||||
|
let channels = self.subscribed_channels_strings().await?;
|
||||||
|
log::info!("User is subscribed to channels {:?}", channels);
|
||||||
|
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||||
|
ty: polariton::serdes::TypePrefix::HashMap, // hashtable
|
||||||
|
items: channels.into_iter().map(|name| crate::data::channel::ChatChannelInfo {
|
||||||
|
channel_name: name,
|
||||||
|
members: vec![
|
||||||
|
crate::data::channel::ChatChannelMember {
|
||||||
|
name: self.account.display_name.clone(),
|
||||||
|
use_custom_avatar: false,
|
||||||
|
state: crate::data::channel::ChatPlayerState::Idk0,
|
||||||
|
custom_avatar: Vec::default(),
|
||||||
|
avatar_id: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
channel_ty: crate::data::channel::ChatChannelType::Public,
|
||||||
|
}.as_transmissible()).collect()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16> {
|
||||||
|
let channels = self.db.user_aux_by_user_id_and_descriptor(self.account.id, oj_rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?.ok_or_else(|| {
|
||||||
|
log::error!("Failed to find SubscribedChannels (user_aux) for user_id {}", self.account.id);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
let channels = serde_json::from_str::<Vec<String>>(&channels.data).map_err(|e| {
|
||||||
|
log::error!("Failed to parse SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
Ok(channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<polariton::operation::Typed<()>, i16> {
|
||||||
|
if let crate::data::channel::ChatChannelType::Public = channel_ty {
|
||||||
|
let mut sub_channels = self.subscribed_channels_strings().await?;
|
||||||
|
sub_channels.push(channel.clone());
|
||||||
|
let new_data = serde_json::to_string(&sub_channels).map_err(|e| {
|
||||||
|
log::error!("Failed to convert to JSON SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
self.db.update_user_aux_by_user_id_and_descriptor(oj_rc_database::schema::user_aux::ActiveModel {
|
||||||
|
data: oj_rc_database::sea_orm::ActiveValue::Set(new_data),
|
||||||
|
..Default::default()
|
||||||
|
}, self.account.id, oj_rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| {
|
||||||
|
log::error!("Failed to update SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(crate::data::channel::ChatChannelInfo {
|
||||||
|
channel_name: channel,
|
||||||
|
members: Vec::default(),
|
||||||
|
channel_ty,
|
||||||
|
}.as_transmissible())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<(), i16> {
|
||||||
|
if let crate::data::channel::ChatChannelType::Public = channel_ty {
|
||||||
|
let mut sub_channels = self.subscribed_channels_strings().await?;
|
||||||
|
if let Some(index) = sub_channels.iter().position(|chann| chann == &channel) {
|
||||||
|
sub_channels.swap_remove(index);
|
||||||
|
let new_data = serde_json::to_string(&sub_channels).map_err(|e| {
|
||||||
|
log::error!("Failed to convert to JSON SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
self.db.update_user_aux_by_user_id_and_descriptor(oj_rc_database::schema::user_aux::ActiveModel {
|
||||||
|
data: oj_rc_database::sea_orm::ActiveValue::Set(new_data),
|
||||||
|
..Default::default()
|
||||||
|
}, self.account.id, oj_rc_database::schema::user_aux::Descriptor::SubscribedChannels).await.map_err(|e| {
|
||||||
|
log::error!("Failed to update SubscribedChannels (user_aux) for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*async fn has_pending_sanctions(&self) -> Result<bool, i16> {
|
||||||
|
let count = self.db.count_sanctions_to_ack_by_user_id_and_descriptor(self.account.id, oj_rc_database::schema::sanction::Descriptor::Warn).await.map_err(|e| {
|
||||||
|
log::error!("Failed to count pending sanctions for user_id {}: {}", self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
Ok(count != 0)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
async fn get_sanctions(&self, username: String) -> Result<polariton::operation::Typed<()>, i16> {
|
||||||
|
let user_opt = self.db.user_by_display_name(username.clone()).await.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve user by username {} for user_id {}: {}", username, self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
if let Some(user) = user_opt {
|
||||||
|
let sanctions = self.db.sanctions_by_user_id(user.id).await.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve sanctions by username {} for user_id {}: {}", username, self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||||
|
ty: polariton::serdes::TypePrefix::Str,
|
||||||
|
items: sanctions.into_iter().map(|x| {
|
||||||
|
let data = crate::data::sanction::SanctionJson {
|
||||||
|
type_: crate::data::sanction::SanctionType::from_db(x.descriptor),
|
||||||
|
reason: x.reason,
|
||||||
|
reporter: x.issuer_name,
|
||||||
|
issued: chrono::DateTime::from_timestamp(x.creation_time, 0).unwrap(),
|
||||||
|
};
|
||||||
|
polariton::operation::Typed::Str(data.as_json().into())
|
||||||
|
}).collect(),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_sanction(&self, sanction: super::SetSanction) -> Result<(), i16> {
|
||||||
|
self.check_perms_to_exec(&sanction.type_)?;
|
||||||
|
let user_opt = self.db.user_by_display_name(sanction.username.clone()).await.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve user by username {} for user_id {}: {}", sanction.username, self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
if let Some(user) = user_opt {
|
||||||
|
if sanction.is_adding {
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
let sanction_ty = crate::data::sanction::SanctionType::from_persist(sanction.type_).to_db();
|
||||||
|
let to_add = oj_rc_database::schema::sanction::ActiveModel {
|
||||||
|
user_id: oj_rc_database::sea_orm::ActiveValue::Set(user.id),
|
||||||
|
creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now),
|
||||||
|
issuer_id: oj_rc_database::sea_orm::ActiveValue::Set(self.account.id),
|
||||||
|
issuer_name: oj_rc_database::sea_orm::ActiveValue::Set(self.account.display_name.clone()),
|
||||||
|
descriptor: oj_rc_database::sea_orm::ActiveValue::Set(sanction_ty.clone()),
|
||||||
|
reason: oj_rc_database::sea_orm::ActiveValue::Set(sanction.reason),
|
||||||
|
duration: oj_rc_database::sea_orm::ActiveValue::Set(if sanction.duration <= 0 { None } else { Some(sanction.duration as i64) }),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if matches!(sanction_ty, oj_rc_database::schema::sanction::Descriptor::Ban) {
|
||||||
|
self.db.update_perms_by_user_id(oj_rc_database::schema::permissions::ActiveModel {
|
||||||
|
banned: oj_rc_database::sea_orm::ActiveValue::Set(true),
|
||||||
|
..Default::default()
|
||||||
|
}, user.id).await.map_err(|e| {
|
||||||
|
log::error!("Failed to update permissions (to ban) for user_id {} by user_id {}: {}", user.id, self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
self.db.insert_sanction(to_add).await.map_err(|e| {
|
||||||
|
log::error!("Failed to insert sanction for user_id {} by user_id {}: {}", user.id, self.account.id, e);
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// FIXME
|
||||||
|
log::error!("Modifying sanctions is not currently supported");
|
||||||
|
Err(crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_permission(&self, username: String, permission: super::UserRole, value: bool) -> Result<(), polariton_server::operations::SimpleOpError> {
|
||||||
|
if !(self.perms.developer || self.perms.royalty || self.perms.administrator) {
|
||||||
|
// technically this should already be handled by the chat command permission check
|
||||||
|
// this is just extra insurance in case of a bad server configuration
|
||||||
|
return Err(polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::ChatErrorCodes::AdminsOnly as i16,
|
||||||
|
format!("User {} cannot grant permissions for {}", self.account.id, username),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
let account_opt = self.db.user_by_display_name(username.clone()).await.map_err(|e| {
|
||||||
|
log::error!("Failed to retrieve user {} to grant permission by user {}: {}", username, self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16,
|
||||||
|
format!("Failed to retrieve user for permission grant: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if let Some(account) = account_opt {
|
||||||
|
self.db.update_perms_by_user_id(oj_rc_database::schema::permissions::ActiveModel {
|
||||||
|
id: oj_rc_database::sea_orm::ActiveValue::NotSet,
|
||||||
|
user_id: oj_rc_database::sea_orm::ActiveValue::NotSet,
|
||||||
|
moderator: if matches!(permission, super::UserRole::Moderator) { oj_rc_database::sea_orm::ActiveValue::Set(value) } else { oj_rc_database::sea_orm::ActiveValue::NotSet },
|
||||||
|
administrator: if matches!(permission, super::UserRole::Administrator) { oj_rc_database::sea_orm::ActiveValue::Set(value) } else { oj_rc_database::sea_orm::ActiveValue::NotSet },
|
||||||
|
developer: if matches!(permission, super::UserRole::Developer) { oj_rc_database::sea_orm::ActiveValue::Set(value) } else { oj_rc_database::sea_orm::ActiveValue::NotSet },
|
||||||
|
royalty: if matches!(permission, super::UserRole::Royalty) { oj_rc_database::sea_orm::ActiveValue::Set(value) } else { oj_rc_database::sea_orm::ActiveValue::NotSet },
|
||||||
|
banned: oj_rc_database::sea_orm::ActiveValue::NotSet,
|
||||||
|
}, account.id).await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to update permissions for user {} by user {}: {}", account.id, self.account.id, e);
|
||||||
|
polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16,
|
||||||
|
format!("Failed to update user permissions: {}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(polariton_server::operations::SimpleOpError::with_message(
|
||||||
|
crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16,
|
||||||
|
format!("User {} not found", username),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ mod inventory;
|
|||||||
pub use inventory::UnlockedParts;
|
pub use inventory::UnlockedParts;
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener};
|
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener, UserRole};
|
||||||
|
|
||||||
pub mod intercom;
|
pub mod intercom;
|
||||||
pub use intercom::generate_token as generate_intercom_token;
|
pub use intercom::generate_token as generate_intercom_token;
|
||||||
@@ -20,6 +20,7 @@ mod multiplayer;
|
|||||||
mod lobby;
|
mod lobby;
|
||||||
pub use lobby::TeamChooser;
|
pub use lobby::TeamChooser;
|
||||||
mod common;
|
mod common;
|
||||||
|
mod chat;
|
||||||
|
|
||||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||||
|
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ pub trait ChatUser: CommonUser + IntercomUser {
|
|||||||
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>;
|
async fn get_total_registered_users(&self) -> Result<u64, polariton_server::operations::SimpleOpError>;
|
||||||
|
async fn set_permission(&self, username: String, permission: UserRole, value: bool) -> Result<(), polariton_server::operations::SimpleOpError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SetSanction {
|
pub struct SetSanction {
|
||||||
@@ -238,6 +239,13 @@ impl SanctionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum UserRole {
|
||||||
|
Moderator,
|
||||||
|
Administrator,
|
||||||
|
Developer,
|
||||||
|
Royalty,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait LobbyUser {
|
pub trait LobbyUser {
|
||||||
fn user_id(&self) -> i32;
|
fn user_id(&self) -> i32;
|
||||||
|
|||||||
Reference in New Issue
Block a user