3 Commits
Author SHA1 Message Date
nyeki 458f89a3a3 Remove trace logs for the moderation module 2025-08-12 16:53:23 -03:00
nyeki 4df7c8ce7d Add message auto-censorship 2025-08-12 16:52:29 -03:00
nyeki 28f8c8e702 Push branch 2025-08-12 11:42:09 -03:00
3 changed files with 49 additions and 14 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 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)]
@@ -0,0 +1 @@
slap that ass
+40 -9
View File
@@ -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,9 +130,13 @@ pub fn normalize(message: String) -> NormalizedMessage {
fn regex_from_phrase(phrase: &str) -> String {
let mut pattern = String::new();
for ch in phrase.replace(" ", "").chars() {
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,17 +177,41 @@ pub fn censor_message(
) -> String {
let mut censored = original.to_string();
for (findings, rule) in analysis {
// Collect all ranges to censor
let mut ranges = Vec::new();
for (findings, _) in analysis {
for finding in findings {
while let Some(start) = normalized.message.find(finding) {
let end = start + finding.len();
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 {
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<Arc<dyn Rule>>) {
let normalized = normalize(original.to_string());
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,