mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add minimum chat moderation to enable bans for #22
This commit is contained in:
@@ -12,6 +12,7 @@ pub mod weapon_list;
|
||||
pub mod weapon_upgrade;
|
||||
pub mod crf;
|
||||
pub mod channel;
|
||||
pub mod sanction;
|
||||
|
||||
pub mod error_codes;
|
||||
|
||||
|
||||
72
rc_core/src/data/sanction.rs
Normal file
72
rc_core/src/data/sanction.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct SanctionJson {
|
||||
#[serde(rename="Type")]
|
||||
pub type_: SanctionType,
|
||||
#[serde(rename="Reason")]
|
||||
pub reason: String,
|
||||
#[serde(rename="Reporter")]
|
||||
pub reporter: String,
|
||||
#[serde(rename="Issued", serialize_with="serde_issued::serialize")]
|
||||
pub issued: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
impl SanctionJson {
|
||||
pub fn as_json(&self) -> String {
|
||||
serde_json::to_string(&self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod serde_issued {
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/*pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<DateTime<Utc>, D::Error> {
|
||||
d.deserialize_str(todo!())
|
||||
}*/
|
||||
|
||||
pub fn serialize<S: serde::Serializer>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&dt.to_rfc2822())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub enum SanctionType {
|
||||
Warning = 0,
|
||||
Mute = 1,
|
||||
Suspension = 2,
|
||||
Note = 3,
|
||||
Kick = 4,
|
||||
}
|
||||
|
||||
impl SanctionType {
|
||||
pub(crate) fn from_db(t: rc_database::schema::sanction::Descriptor) -> Self {
|
||||
match t {
|
||||
rc_database::schema::sanction::Descriptor::Warn => Self::Warning,
|
||||
rc_database::schema::sanction::Descriptor::Mute => Self::Mute,
|
||||
rc_database::schema::sanction::Descriptor::Ban => Self::Suspension,
|
||||
rc_database::schema::sanction::Descriptor::Note => Self::Note,
|
||||
rc_database::schema::sanction::Descriptor::Kick => Self::Kick,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_db(self) -> rc_database::schema::sanction::Descriptor {
|
||||
match self {
|
||||
Self::Warning => rc_database::schema::sanction::Descriptor::Warn,
|
||||
Self::Mute => rc_database::schema::sanction::Descriptor::Mute,
|
||||
Self::Suspension => rc_database::schema::sanction::Descriptor::Ban,
|
||||
Self::Note => rc_database::schema::sanction::Descriptor::Note,
|
||||
Self::Kick => rc_database::schema::sanction::Descriptor::Kick,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_persist(t: crate::persist::user::SanctionType) -> Self {
|
||||
match t {
|
||||
crate::persist::user::SanctionType::Warn => Self::Warning,
|
||||
crate::persist::user::SanctionType::Mute => Self::Mute,
|
||||
crate::persist::user::SanctionType::Ban => Self::Suspension,
|
||||
crate::persist::user::SanctionType::Note => Self::Note,
|
||||
crate::persist::user::SanctionType::Kick => Self::Kick,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,6 +198,33 @@ impl UserData {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_perms_to_exec(&self, ty: &super::SanctionType) -> Result<(), i16> {
|
||||
match ty {
|
||||
super::SanctionType::Warn
|
||||
| super::SanctionType::Mute
|
||||
| super::SanctionType::Note => if self.has_any_elevated_perms() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(crate::data::error_codes::ChatErrorCodes::ModeratorsOnly as i16)
|
||||
},
|
||||
super::SanctionType::Ban
|
||||
| super::SanctionType::Kick => if self.has_admin_or_better_perms() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(crate::data::error_codes::ChatErrorCodes::AdminsOnly as i16)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_any_elevated_perms(&self) -> bool {
|
||||
self.perms.moderator | self.perms.administrator | self.perms.developer
|
||||
}
|
||||
|
||||
fn has_admin_or_better_perms(&self) -> bool {
|
||||
self.perms.administrator | self.perms.developer
|
||||
}
|
||||
}
|
||||
|
||||
const INVALID_ROBOT_ERR: i16 = crate::data::error_codes::WebServicesError::InvalidRobot as i16; // 140
|
||||
@@ -716,4 +743,83 @@ impl super::ChatUser for UserData {
|
||||
}
|
||||
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, 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 = rc_database::schema::sanction::ActiveModel {
|
||||
user_id: rc_database::sea_orm::ActiveValue::Set(user.id),
|
||||
creation_time: rc_database::sea_orm::ActiveValue::Set(now),
|
||||
issuer_id: rc_database::sea_orm::ActiveValue::Set(self.account.id),
|
||||
issuer_name: rc_database::sea_orm::ActiveValue::Set(self.account.display_name.clone()),
|
||||
descriptor: rc_database::sea_orm::ActiveValue::Set(sanction_ty.clone()),
|
||||
reason: rc_database::sea_orm::ActiveValue::Set(sanction.reason),
|
||||
duration: rc_database::sea_orm::ActiveValue::Set(if sanction.duration <= 0 { None } else { Some(sanction.duration as i64) }),
|
||||
..Default::default()
|
||||
};
|
||||
if matches!(sanction_ty, rc_database::schema::sanction::Descriptor::Ban) {
|
||||
self.db.update_perms_by_user_id(rc_database::schema::permissions::ActiveModel {
|
||||
banned: 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ mod inventory;
|
||||
pub use inventory::UnlockedParts;
|
||||
|
||||
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};
|
||||
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};
|
||||
|
||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||
|
||||
|
||||
@@ -190,5 +190,36 @@ pub trait ChatUser {
|
||||
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16>;
|
||||
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<polariton::operation::Typed<()>, i16>;
|
||||
async fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<(), i16>;
|
||||
//async fn has_pending_sanctions(&self) -> Result<bool, i16>;
|
||||
async fn get_sanctions(&self, username: String) -> Result<polariton::operation::Typed<()>, i16>;
|
||||
async fn set_sanction(&self, sanction: SetSanction) -> Result<(), i16>;
|
||||
}
|
||||
|
||||
pub struct SetSanction {
|
||||
pub is_adding: bool, // if false, it's modifying
|
||||
pub type_: SanctionType,
|
||||
pub duration: i32,
|
||||
pub reason: String,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
pub enum SanctionType {
|
||||
Warn = 0,
|
||||
Mute = 1,
|
||||
Ban = 2,
|
||||
Note = 3,
|
||||
Kick = 4,
|
||||
}
|
||||
|
||||
impl SanctionType {
|
||||
pub fn from_i32(i: i32) -> Result<Self, i16> {
|
||||
match i {
|
||||
0 => Ok(Self::Warn),
|
||||
1 => Ok(Self::Mute),
|
||||
2 => Ok(Self::Ban),
|
||||
3 => Ok(Self::Note),
|
||||
4 => Ok(Self::Kick),
|
||||
_ => Err(crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user