feat(core/database): implement reports & snapshots

This commit is contained in:
Paul Makles
2024-04-07 22:42:46 +01:00
parent 1a8e43d280
commit 6b488f347e
12 changed files with 299 additions and 3 deletions

View File

@@ -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>>>,
}
);

View File

@@ -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

View File

@@ -0,0 +1,5 @@
mod model;
mod ops;
pub use model::*;
pub use ops::*;

View 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,
}
);

View 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<()>;
}

View File

@@ -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(|_| ())
}
}

View File

@@ -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(())
}
}
}

View File

@@ -0,0 +1,5 @@
mod model;
mod ops;
pub use model::*;
pub use ops::*;

View 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),
}
);

View 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<()>;
}

View File

@@ -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(|_| ())
}
}

View File

@@ -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(())
}
}
}