diff --git a/src/groups/calls/events.rs b/src/groups/calls/events.rs index f357cec..e14edcd 100644 --- a/src/groups/calls/events.rs +++ b/src/groups/calls/events.rs @@ -1,15 +1,18 @@ -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; use async_trait::async_trait; -use linkify::LinkFinder; use twilight_model::{gateway::payload::incoming::MessageCreate, id::Id}; use twilight_validate::request::webhook_username; use crate::{ - database::models::{Call, Channel}, discord::{images, sanitizing::SanitizeDiscordMarkdown}, framework::{ + database::models::{Call, Channel}, + discord::images, + framework::{ events::traits::{EventResult, OnMessageHandler}, runtime::Runtime, - }, groups::calls::moderation::escape_message, state::State + }, + groups::calls::moderation::escape_message, + state::State, }; #[derive(Debug)] diff --git a/src/groups/calls/lists/suggestive-language.txt b/src/groups/calls/lists/suggestive-language.txt index e69de29..8945550 100644 --- a/src/groups/calls/lists/suggestive-language.txt +++ b/src/groups/calls/lists/suggestive-language.txt @@ -0,0 +1 @@ +slap that ass \ No newline at end of file diff --git a/src/groups/calls/moderation.rs b/src/groups/calls/moderation.rs index 11ed38f..544b66f 100644 --- a/src/groups/calls/moderation.rs +++ b/src/groups/calls/moderation.rs @@ -62,11 +62,11 @@ pub fn escape_message(event: &MessageCreate) -> String { 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)] @@ -113,13 +113,16 @@ pub fn normalize(message: String) -> NormalizedMessage { for char in unidecoded.chars() { if char.is_alphanumeric() { normalized.push(char); - mappings.push(i); + } else { + normalized.push(' '); } + + mappings.push(i); } } NormalizedMessage { - message: normalized, + message: normalized.to_ascii_lowercase(), mappings, } } @@ -127,8 +130,12 @@ pub fn normalize(message: String) -> NormalizedMessage { fn regex_from_phrase(phrase: &str) -> String { let mut pattern = String::new(); - for ch in phrase.replace(" ", "").chars() { - pattern.push_str(&format!("{ch}+\\s*")); + for (i, ch) in phrase.replace(" ", "").chars().enumerate() { + if i == phrase.len() - 1 { + pattern.push_str(&format!("{ch}+")); + } else { + pattern.push_str(&format!("{ch}+\\s*")); + } } let pattern = format!("\\b{pattern}\\b"); @@ -170,19 +177,44 @@ pub fn censor_message( ) -> String { let mut censored = original.to_string(); - for (findings, rule) in analysis { - for finding in findings { - while let Some(start) = normalized.message.find(finding) { - let end = start + finding.len(); + // Collect all ranges to censor + let mut ranges = Vec::new(); - 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 } +pub fn moderate_message(original: &str) -> (String, Vec>) { + 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)] pub enum Action { BlockAndPenalty, @@ -218,6 +250,8 @@ impl HateSpeechAndSlursRule { let regex = regex_from_phrases_list(&phrases); + log::trace!("Hate Speech and Slurs Rule regex: {regex}"); + Self(regex) } } @@ -248,6 +282,8 @@ impl ThreatsAndSelfHarmRule { let regex = regex_from_phrases_list(&phrases); + log::trace!("Threats and Self Harm Rule regex: {regex}"); + Self(regex) } } @@ -278,6 +314,8 @@ impl ExplicitSexualLanguageRule { let regex = regex_from_phrases_list(&phrases); + log::trace!("Explicit Sexual Language Rule regex: {regex}"); + Self(regex) } } @@ -307,6 +345,8 @@ impl SuggestiveLanguageRule { let regex = regex_from_phrases_list(&phrases); + log::trace!("Suggestive Language Rule regex: {regex}"); + Self(regex) } }