diff --git a/Cargo.lock b/Cargo.lock index 94c8e09..ce957e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,7 +336,7 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "furbooru" version = "0.3.1" -source = "git+https://github.com/Xe/furbooru#589dcaf2b5a0cc5c32c9ea5f1ad291b00183e10b" +source = "git+https://github.com/Xe/furbooru#175b0973dc0db4e4f385be2b94042e594859e5c7" dependencies = [ "anyhow", "async-trait", @@ -1511,7 +1511,7 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tron" -version = "0.1.1" +version = "0.2.1" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 26d63a3..da238c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tron" -version = "0.1.1" +version = "0.2.1" authors = ["Christine Dodrill "] edition = "2018" repository = "https://tulpa.dev/cadey/tron" diff --git a/regexes.dhall b/regexes.dhall index e69b569..ef9eb71 100644 --- a/regexes.dhall +++ b/regexes.dhall @@ -17,4 +17,9 @@ in [ Rule::{ regex = "(n|z)igg(er|a)", why = "racism" } , regex = "transsexual" , why = "probably false alarm, but transphobia" } + , Rule::{ regex = "chimp out", why = "racism" } + , Rule::{ regex = "kike", why = "antisemitism" } + , Rule::{ regex = "fat trixie", why = "icjb" } + , Rule::{ regex = "soros", why = "conspiracy theories" } + , Rule::{ regex = "(foal|loli)con", why = "illegal stuff" } ] diff --git a/src/main.rs b/src/main.rs index 2997937..d5a534a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ use anyhow::Result; use async_trait::async_trait; use discord_webhook::Body; -use furbooru::{Client, Comment, FirehoseAdaptor, Image}; +use furbooru::{Client, Comment, FirehoseAdaptor, Forum, Image, Post, Topic}; use regex::Regex; use serde::Deserialize; -use std::path::PathBuf; +use std::{fmt, path::PathBuf}; #[derive(Deserialize, Debug, Clone)] pub(crate) struct Config { @@ -38,71 +38,117 @@ pub(crate) fn user_agent(username: String) -> String { ) } -struct Hit{ +struct Hit { matches: String, why: String, } +impl fmt::Display for Hit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "- match on rule `{}` ({})", self.matches, self.why) + } +} + struct Rules(Vec, Config); impl Rules { - fn check(&self, text: String) -> Option> { - None + fn check(&self, text: &String) -> Option> { + let mut result: Vec = vec![]; + let mut found = false; + + for rule in &self.0 { + if rule.regex.is_match(text) { + log::debug!("{:?} matches {}", text, rule.raw); + found = true; + result.push(Hit { + matches: rule.raw.clone(), + why: rule.why.clone(), + }); + } + } + + if found { + Some(result) + } else { + None + } } } #[async_trait] impl FirehoseAdaptor for Rules { async fn image_created(&self, img: Image) -> Result<()> { - let mut buf: String = String::new(); - let mut found = false; + let url = img.view_url; + log::debug!("got image {}", url); + if let Some(hits) = self.check(&img.description.to_lowercase()) { + let mut buf: String = String::new(); - for rule in &self.0 { - if rule.regex.is_match(&img.description.to_lowercase()) { - log::debug!("{:?} matches {}", img.description, rule.raw); - found = true; - buf.push_str(&format!("\n- match on rule `{}` ({})", rule.raw, rule.why)); + for hit in hits { + buf.push_str(&format!("\n{}", hit)); } - } - - if found { discord_webhook::execute( self.1.discord_webhook_url.clone(), - Body::new(format!("matches found on <{}>:{}", img.view_url, buf)), + Body::new(format!( + "matches from **{}** found on <{}>:{}", + img.uploader.or(Some("An anonymous user".into())).unwrap(), + url, + buf + )), ) .await?; - log::info!("the description of {} has naughty words", img.view_url); + log::info!("the description of {} has naughty words", url); } Ok(()) } async fn comment_created(&self, cmt: Comment) -> Result<()> { - let mut buf: String = String::new(); - let mut found = false; + let url = format!("https://furbooru.org/{}#comment_{}", cmt.image_id, cmt.id); + log::debug!("got comment {}", url); + if let Some(hits) = self.check(&cmt.body.to_lowercase()) { + let mut buf: String = String::new(); - for rule in &self.0 { - if rule.regex.is_match(&cmt.body.to_lowercase()) { - log::debug!("{:?} matches {}", cmt.body, rule.raw); - found = true; - buf.push_str(&format!("\n- match on rule `{}` ({})", rule.raw, rule.why)); + for hit in hits { + buf.push_str(&format!("\n{}", hit)); } - } - - if found { discord_webhook::execute( self.1.discord_webhook_url.clone(), Body::new(format!( - "matches found on :{}", - cmt.image_id, cmt.id, buf + "matches from **{}** found on <{}>:{}", + cmt.author, url, buf )), ) .await?; - log::info!( - "comment https://furbooru.org/{}#comment_{} has naughty words", - cmt.image_id, - cmt.id - ); + log::info!("comment {} has naughty words", url); + } + + Ok(()) + } + + async fn post_created(&self, frm: Forum, top: Topic, pst: Post) -> Result<()> { + let url = format!( + "https://furbooru.org/forums/{forum}/topics/{topic}?post_id={post}#post_{post}", + forum = frm.short_name, + topic = top.slug, + post = pst.id + ); + log::debug!("got post {}", url); + + if let Some(hits) = self.check(&pst.body.to_lowercase()) { + let mut buf: String = String::new(); + + for hit in hits { + buf.push_str(&format!("\n{}", hit)); + } + discord_webhook::execute( + self.1.discord_webhook_url.clone(), + Body::new(format!( + "matches from **{}** found on <{}>:{}", + pst.author, url, buf + )), + ) + .await?; + log::info!("post {} has naughty words", url); } Ok(()) @@ -115,9 +161,20 @@ async fn main() -> Result<()> { pretty_env_logger::init(); let cfg: Config = envy::from_env()?; log::debug!("cfg: {:?}", cfg); + + log::debug!("loaded list of words to watch"); let rexes: Vec = serde_dhall::from_file(cfg.regexes.clone()).parse()?; let mut compiled_rules: Vec = Vec::new(); + #[cfg(not(debug_assertions))] + { + discord_webhook::execute( + cfg.discord_webhook_url.clone(), + Body::new("I fight for the user!"), + ) + .await?; + } + for rule in rexes { log::debug!("{} -> {}", rule.regex, rule.why); compiled_rules.push(CompiledRule {