Add message auto-censorship

This commit is contained in:
2025-08-12 16:52:29 -03:00
parent 28f8c8e702
commit 4df7c8ce7d
3 changed files with 60 additions and 16 deletions
+7 -4
View File
@@ -1,15 +1,18 @@
use std::{collections::HashMap, sync::Arc}; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use linkify::LinkFinder;
use twilight_model::{gateway::payload::incoming::MessageCreate, id::Id}; use twilight_model::{gateway::payload::incoming::MessageCreate, id::Id};
use twilight_validate::request::webhook_username; use twilight_validate::request::webhook_username;
use crate::{ use crate::{
database::models::{Call, Channel}, discord::{images, sanitizing::SanitizeDiscordMarkdown}, framework::{ database::models::{Call, Channel},
discord::images,
framework::{
events::traits::{EventResult, OnMessageHandler}, events::traits::{EventResult, OnMessageHandler},
runtime::Runtime, runtime::Runtime,
}, groups::calls::moderation::escape_message, state::State },
groups::calls::moderation::escape_message,
state::State,
}; };
#[derive(Debug)] #[derive(Debug)]
@@ -0,0 +1 @@
slap that ass
+52 -12
View File
@@ -62,11 +62,11 @@ pub fn escape_message(event: &MessageCreate) -> String {
message = message.replace(url, &"\\#".repeat(url.len())); message = message.replace(url, &"\\#".repeat(url.len()));
} }
let normalized = normalize(message.clone()); let (censored, rules) = moderate_message(&message);
log::debug!("Escaped and normalized message: {normalized:?}"); log::debug!("Escaped and normalized message: {censored:?}");
message.sanitize(false) censored
} }
#[derive(Debug)] #[derive(Debug)]
@@ -113,13 +113,16 @@ pub fn normalize(message: String) -> NormalizedMessage {
for char in unidecoded.chars() { for char in unidecoded.chars() {
if char.is_alphanumeric() { if char.is_alphanumeric() {
normalized.push(char); normalized.push(char);
mappings.push(i); } else {
normalized.push(' ');
} }
mappings.push(i);
} }
} }
NormalizedMessage { NormalizedMessage {
message: normalized, message: normalized.to_ascii_lowercase(),
mappings, mappings,
} }
} }
@@ -127,8 +130,12 @@ pub fn normalize(message: String) -> NormalizedMessage {
fn regex_from_phrase(phrase: &str) -> String { fn regex_from_phrase(phrase: &str) -> String {
let mut pattern = String::new(); let mut pattern = String::new();
for ch in phrase.replace(" ", "").chars() { for (i, ch) in phrase.replace(" ", "").chars().enumerate() {
pattern.push_str(&format!("{ch}+\\s*")); if i == phrase.len() - 1 {
pattern.push_str(&format!("{ch}+"));
} else {
pattern.push_str(&format!("{ch}+\\s*"));
}
} }
let pattern = format!("\\b{pattern}\\b"); let pattern = format!("\\b{pattern}\\b");
@@ -170,19 +177,44 @@ pub fn censor_message(
) -> String { ) -> String {
let mut censored = original.to_string(); let mut censored = original.to_string();
for (findings, rule) in analysis { // Collect all ranges to censor
for finding in findings { let mut ranges = Vec::new();
while let Some(start) = normalized.message.find(finding) {
let end = start + finding.len();
todo!(); for (findings, _) in analysis {
for finding in findings {
for (index, _) in normalized.message.match_indices(finding) {
let start_index = normalized.mappings.get(index).cloned().unwrap_or(0);
let end_index = normalized.mappings.get(index + finding.len()).cloned().unwrap_or(original.len());
ranges.push((start_index, end_index));
} }
} }
} }
// Sort ranges in reverse order to avoid shifting indices
ranges.sort_by(|a, b| b.0.cmp(&a.0));
for (start_index, end_index) in ranges {
log::trace!("Censoring from {start_index} to {end_index}");
if start_index < end_index && end_index <= censored.len() {
censored.replace_range(start_index..end_index, "[censored]");
}
}
censored censored
} }
pub fn moderate_message(original: &str) -> (String, Vec<Arc<dyn Rule>>) {
let normalized = normalize(original.to_string());
log::trace!("Normalized message: {:?}", normalized.message);
let analysis = analyze_message(&normalized.message);
let censored = censor_message(original, normalized, &analysis);
(censored, analysis.iter().map(|(_, r)| r.clone()).collect())
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Action { pub enum Action {
BlockAndPenalty, BlockAndPenalty,
@@ -218,6 +250,8 @@ impl HateSpeechAndSlursRule {
let regex = regex_from_phrases_list(&phrases); let regex = regex_from_phrases_list(&phrases);
log::trace!("Hate Speech and Slurs Rule regex: {regex}");
Self(regex) Self(regex)
} }
} }
@@ -248,6 +282,8 @@ impl ThreatsAndSelfHarmRule {
let regex = regex_from_phrases_list(&phrases); let regex = regex_from_phrases_list(&phrases);
log::trace!("Threats and Self Harm Rule regex: {regex}");
Self(regex) Self(regex)
} }
} }
@@ -278,6 +314,8 @@ impl ExplicitSexualLanguageRule {
let regex = regex_from_phrases_list(&phrases); let regex = regex_from_phrases_list(&phrases);
log::trace!("Explicit Sexual Language Rule regex: {regex}");
Self(regex) Self(regex)
} }
} }
@@ -307,6 +345,8 @@ impl SuggestiveLanguageRule {
let regex = regex_from_phrases_list(&phrases); let regex = regex_from_phrases_list(&phrases);
log::trace!("Suggestive Language Rule regex: {regex}");
Self(regex) Self(regex)
} }
} }