mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 05:26:59 +00:00
feat(core/database): implement reports & snapshots
This commit is contained in:
@@ -4,7 +4,8 @@ use futures::lock::Mutex;
|
||||
|
||||
use crate::{
|
||||
Bot, Channel, ChannelCompositeKey, ChannelUnread, Emoji, File, Invite, Member,
|
||||
MemberCompositeKey, Message, RatelimitEvent, Server, ServerBan, User, UserSettings, Webhook,
|
||||
MemberCompositeKey, Message, RatelimitEvent, Report, Server, ServerBan, Snapshot, User,
|
||||
UserSettings, Webhook,
|
||||
};
|
||||
|
||||
database_derived!(
|
||||
@@ -25,7 +26,7 @@ database_derived!(
|
||||
pub server_bans: Arc<Mutex<HashMap<MemberCompositeKey, ServerBan>>>,
|
||||
pub server_members: Arc<Mutex<HashMap<MemberCompositeKey, Member>>>,
|
||||
pub servers: Arc<Mutex<HashMap<String, Server>>>,
|
||||
pub safety_reports: Arc<Mutex<HashMap<String, ()>>>,
|
||||
pub safety_snapshots: Arc<Mutex<HashMap<String, ()>>>,
|
||||
pub safety_reports: Arc<Mutex<HashMap<String, Report>>>,
|
||||
pub safety_snapshots: Arc<Mutex<HashMap<String, Snapshot>>>,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -8,6 +8,8 @@ mod emojis;
|
||||
mod files;
|
||||
mod messages;
|
||||
mod ratelimit_events;
|
||||
mod safety_reports;
|
||||
mod safety_snapshots;
|
||||
mod server_bans;
|
||||
mod server_members;
|
||||
mod servers;
|
||||
@@ -24,6 +26,8 @@ pub use emojis::*;
|
||||
pub use files::*;
|
||||
pub use messages::*;
|
||||
pub use ratelimit_events::*;
|
||||
pub use safety_reports::*;
|
||||
pub use safety_snapshots::*;
|
||||
pub use server_bans::*;
|
||||
pub use server_members::*;
|
||||
pub use servers::*;
|
||||
@@ -45,6 +49,8 @@ pub trait AbstractDatabase:
|
||||
+ files::AbstractAttachments
|
||||
+ messages::AbstractMessages
|
||||
+ ratelimit_events::AbstractRatelimitEvents
|
||||
+ safety_reports::AbstractReport
|
||||
+ safety_snapshots::AbstractSnapshot
|
||||
+ server_bans::AbstractServerBans
|
||||
+ server_members::AbstractServerMembers
|
||||
+ servers::AbstractServers
|
||||
|
||||
5
crates/core/database/src/models/safety_reports/mod.rs
Normal file
5
crates/core/database/src/models/safety_reports/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod model;
|
||||
mod ops;
|
||||
|
||||
pub use model::*;
|
||||
pub use ops::*;
|
||||
148
crates/core/database/src/models/safety_reports/model.rs
Normal file
148
crates/core/database/src/models/safety_reports/model.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
|
||||
auto_derived!(
|
||||
/// User-generated platform moderation report
|
||||
pub struct Report {
|
||||
/// Unique Id
|
||||
#[serde(rename = "_id")]
|
||||
pub id: String,
|
||||
/// Id of the user creating this report
|
||||
pub author_id: String,
|
||||
/// Reported content
|
||||
pub content: ReportedContent,
|
||||
/// Additional report context
|
||||
pub additional_context: String,
|
||||
/// Status of the report
|
||||
#[serde(flatten)]
|
||||
pub status: ReportStatus,
|
||||
/// Additional notes included on the report
|
||||
#[serde(default)]
|
||||
pub notes: String,
|
||||
}
|
||||
|
||||
/// Reason for reporting content (message or server)
|
||||
pub enum ContentReportReason {
|
||||
/// No reason has been specified
|
||||
NoneSpecified,
|
||||
|
||||
/// Illegal content catch-all reason
|
||||
Illegal,
|
||||
|
||||
/// Selling or facilitating use of drugs or other illegal goods
|
||||
IllegalGoods,
|
||||
|
||||
/// Extortion or blackmail
|
||||
IllegalExtortion,
|
||||
|
||||
/// Revenge or child pornography
|
||||
IllegalPornography,
|
||||
|
||||
/// Illegal hacking activity
|
||||
IllegalHacking,
|
||||
|
||||
/// Extreme violence, gore, or animal cruelty
|
||||
/// With exception to violence potrayed in media / creative arts
|
||||
ExtremeViolence,
|
||||
|
||||
/// Content that promotes harm to others / self
|
||||
PromotesHarm,
|
||||
|
||||
/// Unsolicited advertisements
|
||||
UnsolicitedSpam,
|
||||
|
||||
/// This is a raid
|
||||
Raid,
|
||||
|
||||
/// Spam or platform abuse
|
||||
SpamAbuse,
|
||||
|
||||
/// Scams or fraud
|
||||
ScamsFraud,
|
||||
|
||||
/// Distribution of malware or malicious links
|
||||
Malware,
|
||||
|
||||
/// Harassment or abuse targeted at another user
|
||||
Harassment,
|
||||
}
|
||||
|
||||
/// Reason for reporting a user
|
||||
pub enum UserReportReason {
|
||||
/// No reason has been specified
|
||||
NoneSpecified,
|
||||
|
||||
/// Unsolicited advertisements
|
||||
UnsolicitedSpam,
|
||||
|
||||
/// User is sending spam or otherwise abusing the platform
|
||||
SpamAbuse,
|
||||
|
||||
/// User's profile contains inappropriate content for a general audience
|
||||
InappropriateProfile,
|
||||
|
||||
/// User is impersonating another user
|
||||
Impersonation,
|
||||
|
||||
/// User is evading a ban
|
||||
BanEvasion,
|
||||
|
||||
/// User is not of minimum age to use the platform
|
||||
Underage,
|
||||
}
|
||||
|
||||
/// The content being reported
|
||||
#[serde(tag = "type")]
|
||||
pub enum ReportedContent {
|
||||
/// Report a message
|
||||
Message {
|
||||
/// ID of the message
|
||||
id: String,
|
||||
/// Reason for reporting message
|
||||
report_reason: ContentReportReason,
|
||||
},
|
||||
/// Report a server
|
||||
Server {
|
||||
/// ID of the server
|
||||
id: String,
|
||||
/// Reason for reporting server
|
||||
report_reason: ContentReportReason,
|
||||
},
|
||||
/// Report a user
|
||||
User {
|
||||
/// ID of the user
|
||||
id: String,
|
||||
/// Reason for reporting a user
|
||||
report_reason: UserReportReason,
|
||||
/// Message context
|
||||
message_id: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Status of the report
|
||||
#[serde(tag = "status")]
|
||||
pub enum ReportStatus {
|
||||
/// Report is waiting for triage / action
|
||||
Created {},
|
||||
|
||||
/// Report was rejected
|
||||
Rejected {
|
||||
rejection_reason: String,
|
||||
closed_at: Option<Timestamp>,
|
||||
},
|
||||
|
||||
/// Report was actioned and resolved
|
||||
Resolved { closed_at: Option<Timestamp> },
|
||||
}
|
||||
|
||||
/// Just the status of the report
|
||||
pub enum ReportStatusString {
|
||||
/// Report is waiting for triage / action
|
||||
Created,
|
||||
|
||||
/// Report was rejected
|
||||
Rejected,
|
||||
|
||||
/// Report was actioned and resolved
|
||||
Resolved,
|
||||
}
|
||||
);
|
||||
12
crates/core/database/src/models/safety_reports/ops.rs
Normal file
12
crates/core/database/src/models/safety_reports/ops.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::Report;
|
||||
|
||||
mod mongodb;
|
||||
mod reference;
|
||||
|
||||
#[async_trait]
|
||||
pub trait AbstractReport: Sync + Send {
|
||||
/// Insert a new report into the database
|
||||
async fn insert_report(&self, report: &Report) -> Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::MongoDb;
|
||||
use crate::Report;
|
||||
|
||||
use super::AbstractReport;
|
||||
|
||||
static COL: &str = "safety_reports";
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractReport for MongoDb {
|
||||
/// Insert a new report into the database
|
||||
async fn insert_report(&self, report: &Report) -> Result<()> {
|
||||
query!(self, insert_one, COL, &report).map(|_| ())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::ReferenceDb;
|
||||
use crate::Report;
|
||||
|
||||
use super::AbstractReport;
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractReport for ReferenceDb {
|
||||
/// Insert a new report into the database
|
||||
async fn insert_report(&self, report: &Report) -> Result<()> {
|
||||
let mut reports = self.safety_reports.lock().await;
|
||||
if reports.contains_key(&report.id) {
|
||||
Err(create_database_error!("insert", "report"))
|
||||
} else {
|
||||
reports.insert(report.id.to_string(), report.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
5
crates/core/database/src/models/safety_snapshots/mod.rs
Normal file
5
crates/core/database/src/models/safety_snapshots/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod model;
|
||||
mod ops;
|
||||
|
||||
pub use model::*;
|
||||
pub use ops::*;
|
||||
35
crates/core/database/src/models/safety_snapshots/model.rs
Normal file
35
crates/core/database/src/models/safety_snapshots/model.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use revolt_models::v0::{Message, Server, User};
|
||||
|
||||
auto_derived!(
|
||||
/// Snapshot of some content
|
||||
pub struct Snapshot {
|
||||
/// Unique Id
|
||||
#[serde(rename = "_id")]
|
||||
pub id: String,
|
||||
/// Report parent Id
|
||||
pub report_id: String,
|
||||
/// Snapshot of content
|
||||
pub content: SnapshotContent,
|
||||
}
|
||||
|
||||
/// Enum to map into different models
|
||||
/// that can be saved in a snapshot
|
||||
#[serde(tag = "_type")]
|
||||
pub enum SnapshotContent {
|
||||
Message {
|
||||
/// Context before the message
|
||||
#[serde(rename = "_prior_context", default)]
|
||||
prior_context: Vec<Message>,
|
||||
|
||||
/// Context after the message
|
||||
#[serde(rename = "_leading_context", default)]
|
||||
leading_context: Vec<Message>,
|
||||
|
||||
/// Message
|
||||
#[serde(flatten)]
|
||||
message: Message,
|
||||
},
|
||||
Server(Server),
|
||||
User(User),
|
||||
}
|
||||
);
|
||||
12
crates/core/database/src/models/safety_snapshots/ops.rs
Normal file
12
crates/core/database/src/models/safety_snapshots/ops.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::Snapshot;
|
||||
|
||||
mod mongodb;
|
||||
mod reference;
|
||||
|
||||
#[async_trait]
|
||||
pub trait AbstractSnapshot: Sync + Send {
|
||||
/// Insert a new snapshot into the database
|
||||
async fn insert_snapshot(&self, snapshot: &Snapshot) -> Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::MongoDb;
|
||||
use crate::Snapshot;
|
||||
|
||||
use super::AbstractSnapshot;
|
||||
|
||||
static COL: &str = "safety_snapshots";
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractSnapshot for MongoDb {
|
||||
/// Insert a new snapshot into the database
|
||||
async fn insert_snapshot(&self, snapshot: &Snapshot) -> Result<()> {
|
||||
query!(self, insert_one, COL, &snapshot).map(|_| ())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::ReferenceDb;
|
||||
use crate::Snapshot;
|
||||
|
||||
use super::AbstractSnapshot;
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractSnapshot for ReferenceDb {
|
||||
/// Insert a new report into the database
|
||||
async fn insert_snapshot(&self, snapshot: &Snapshot) -> Result<()> {
|
||||
let mut snapshots = self.safety_snapshots.lock().await;
|
||||
if snapshots.contains_key(&snapshot.id) {
|
||||
Err(create_database_error!("insert", "snapshot"))
|
||||
} else {
|
||||
snapshots.insert(snapshot.id.to_string(), snapshot.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user