Add no links rule
This commit is contained in:
@@ -34,7 +34,7 @@ pub fn first_warning_embed(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.title("Warning")
|
.title("Warning")
|
||||||
.description("You broke [Payphone's code of conduct](https://payphone.lat/code-of-conduct) during a call. Since it's your first offense, we assumed you didn't read the rules and made this a warning instead. Read the code of conduct to prevent getting banned.")
|
.description("You broke [Payphone's code of conduct](https://payphone.lat/code-of-conduct) during a call. Read the code of conduct to prevent getting banned.")
|
||||||
.field(EmbedFieldBuilder::new("What message broke the rules?", ellipse(message.content.clone(), 500)))
|
.field(EmbedFieldBuilder::new("What message broke the rules?", ellipse(message.content.clone(), 500)))
|
||||||
.field(EmbedFieldBuilder::new(
|
.field(EmbedFieldBuilder::new(
|
||||||
"What rules did you break?",
|
"What rules did you break?",
|
||||||
|
|||||||
@@ -85,6 +85,7 @@
|
|||||||
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use linkify::LinkFinder;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use twilight_model::gateway::payload::incoming::MessageCreate;
|
use twilight_model::gateway::payload::incoming::MessageCreate;
|
||||||
@@ -260,6 +261,10 @@ fn regex_from_phrases_list(phrases: &[&str]) -> Regex {
|
|||||||
Regex::new(&pattern).expect("Failed to compile regex")
|
Regex::new(&pattern).expect("Failed to compile regex")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The results of the analysis of a rule, including all detections (on the original message first,
|
||||||
|
/// on the normalized message later) and the associated rule.
|
||||||
|
type RuleAnalysis = ((Vec<String>, Vec<String>), Arc<dyn Rule>);
|
||||||
|
|
||||||
/// Scans all rules on the message and returns the results for each rule which gave detections.
|
/// Scans all rules on the message and returns the results for each rule which gave detections.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
@@ -267,15 +272,18 @@ fn regex_from_phrases_list(phrases: &[&str]) -> Regex {
|
|||||||
/// * `normalized` - The normalized version of the message.
|
/// * `normalized` - The normalized version of the message.
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// [`Vec<(Vec<String>, Arc<dyn Rule>)>`] - The results of the analysis, including all detections
|
/// [`Vec<RuleAnalysis>`] - The results of the analysis, including all detections (on the original
|
||||||
/// and their associated rules.
|
/// message first, on the normalized message later) and their associated rules.
|
||||||
pub fn analyze_message(original: &str, normalized: &str) -> Vec<(Vec<String>, Arc<dyn Rule>)> {
|
pub fn analyze_message(
|
||||||
|
original: &str,
|
||||||
|
normalized: &str,
|
||||||
|
) -> Vec<RuleAnalysis> {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
for rule in RULES.iter() {
|
for rule in RULES.iter() {
|
||||||
let detections = rule.detect(original, normalized);
|
let (original_detections, normalized_detections) = rule.detect(original, normalized);
|
||||||
if !detections.is_empty() {
|
if !normalized_detections.is_empty() || !original_detections.is_empty() {
|
||||||
results.push((detections, rule.clone()));
|
results.push(((original_detections, normalized_detections), rule.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,15 +303,15 @@ pub fn analyze_message(original: &str, normalized: &str) -> Vec<(Vec<String>, Ar
|
|||||||
pub fn censor_message(
|
pub fn censor_message(
|
||||||
original: &str,
|
original: &str,
|
||||||
normalized: NormalizedMessage,
|
normalized: NormalizedMessage,
|
||||||
analysis: &[(Vec<String>, Arc<dyn Rule>)],
|
analysis: &[RuleAnalysis],
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut censored = original.to_string();
|
let mut censored = original.to_string();
|
||||||
|
|
||||||
// Collect all ranges to censor
|
// Collect all ranges to censor
|
||||||
let mut ranges = Vec::new();
|
let mut ranges = Vec::new();
|
||||||
|
|
||||||
for (findings, _) in analysis {
|
for ((original_findings, normalized_findings), _) in analysis {
|
||||||
for finding in findings {
|
for finding in normalized_findings {
|
||||||
for (index, _) in normalized.message.match_indices(finding) {
|
for (index, _) in normalized.message.match_indices(finding) {
|
||||||
let start_index = normalized.mappings.get(index).cloned().unwrap_or(0);
|
let start_index = normalized.mappings.get(index).cloned().unwrap_or(0);
|
||||||
let end_index = normalized
|
let end_index = normalized
|
||||||
@@ -315,6 +323,15 @@ pub fn censor_message(
|
|||||||
ranges.push((start_index, end_index));
|
ranges.push((start_index, end_index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for finding in original_findings {
|
||||||
|
for (index, _) in original.match_indices(finding) {
|
||||||
|
let start_index = index;
|
||||||
|
let end_index = index + finding.len();
|
||||||
|
|
||||||
|
ranges.push((start_index, end_index));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort ranges in reverse order to avoid shifting indices
|
// Sort ranges in reverse order to avoid shifting indices
|
||||||
@@ -380,10 +397,9 @@ pub trait Rule: Send + Sync {
|
|||||||
/// * `normalized` - The normalized version of the message.
|
/// * `normalized` - The normalized version of the message.
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// [`Vec<String>`] - The list of detected rule violations in the normalized message.
|
/// [`(Vec<String>, Vec<String>)`] - The list of detected rule violations in the original
|
||||||
///
|
/// (first vector) and normalized (second vector) message.
|
||||||
/// TODO: Support detecting violations in the original message.
|
fn detect(&self, original: &str, normalized: &str) -> (Vec<String>, Vec<String>);
|
||||||
fn detect(&self, original: &str, normalized: &str) -> Vec<String>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a file containing phrases to be detected by rules.
|
/// Parses a file containing phrases to be detected by rules.
|
||||||
@@ -429,11 +445,14 @@ impl Rule for HateSpeechAndSlursRule {
|
|||||||
&Action::BlockAndPenalty
|
&Action::BlockAndPenalty
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect(&self, _original: &str, normalized: &str) -> Vec<String> {
|
fn detect(&self, _original: &str, normalized: &str) -> (Vec<String>, Vec<String>) {
|
||||||
self.0
|
(
|
||||||
.find_iter(normalized)
|
vec![],
|
||||||
.map(|m| normalized[m.start()..m.end()].to_string())
|
self.0
|
||||||
.collect()
|
.find_iter(normalized)
|
||||||
|
.map(|m| normalized[m.start()..m.end()].to_string())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,11 +478,14 @@ impl Rule for ThreatsAndSelfHarmRule {
|
|||||||
&Action::BlockAndPenalty
|
&Action::BlockAndPenalty
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect(&self, _original: &str, normalized: &str) -> Vec<String> {
|
fn detect(&self, _original: &str, normalized: &str) -> (Vec<String>, Vec<String>) {
|
||||||
self.0
|
(
|
||||||
.find_iter(normalized)
|
vec![],
|
||||||
.map(|m| normalized[m.start()..m.end()].to_string())
|
self.0
|
||||||
.collect()
|
.find_iter(normalized)
|
||||||
|
.map(|m| normalized[m.start()..m.end()].to_string())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,11 +511,14 @@ impl Rule for ExplicitSexualLanguageRule {
|
|||||||
"Explicit Sexual Language"
|
"Explicit Sexual Language"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect(&self, _original: &str, normalized: &str) -> Vec<String> {
|
fn detect(&self, _original: &str, normalized: &str) -> (Vec<String>, Vec<String>) {
|
||||||
self.0
|
(
|
||||||
.find_iter(normalized)
|
vec![],
|
||||||
.map(|m| normalized[m.start()..m.end()].to_string())
|
self.0
|
||||||
.collect()
|
.find_iter(normalized)
|
||||||
|
.map(|m| normalized[m.start()..m.end()].to_string())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,11 +543,36 @@ impl Rule for SuggestiveLanguageRule {
|
|||||||
"Suggestive Language"
|
"Suggestive Language"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect(&self, _original: &str, normalized: &str) -> Vec<String> {
|
fn detect(&self, _original: &str, normalized: &str) -> (Vec<String>, Vec<String>) {
|
||||||
self.0
|
(
|
||||||
.find_iter(normalized)
|
vec![],
|
||||||
.map(|m| normalized[m.start()..m.end()].to_string())
|
self.0
|
||||||
.collect()
|
.find_iter(normalized)
|
||||||
|
.map(|m| normalized[m.start()..m.end()].to_string())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LinksRule;
|
||||||
|
|
||||||
|
impl Rule for LinksRule {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Links"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn action(&self) -> &'static Action {
|
||||||
|
&Action::SoftCensorAndWarn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect(&self, original: &str, _normalized: &str) -> (Vec<String>, Vec<String>) {
|
||||||
|
let mut detections = Vec::new();
|
||||||
|
|
||||||
|
for link in LinkFinder::new().url_must_have_scheme(false).links(original) {
|
||||||
|
detections.push(link.as_str().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
(detections, vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,5 +583,6 @@ pub static RULES: Lazy<Vec<Arc<dyn Rule>>> = Lazy::new(|| {
|
|||||||
Arc::new(ThreatsAndSelfHarmRule::new()),
|
Arc::new(ThreatsAndSelfHarmRule::new()),
|
||||||
Arc::new(SuggestiveLanguageRule::new()),
|
Arc::new(SuggestiveLanguageRule::new()),
|
||||||
Arc::new(ExplicitSexualLanguageRule::new()),
|
Arc::new(ExplicitSexualLanguageRule::new()),
|
||||||
|
Arc::new(LinksRule),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user