Code cleanup/reformat, added comments, other stuff i forgor

This commit is contained in:
0xf8 2023-04-14 21:21:45 -04:00
parent ff2d897549
commit 14246c7b60
Signed by: 0xf8
GPG Key ID: 446580D758689584
7 changed files with 203 additions and 125 deletions

2
Cargo.lock generated
View File

@ -2067,7 +2067,7 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]] [[package]]
name = "scam-police" name = "scam-police"
version = "0.4.0" version = "0.5.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"dirs", "dirs",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "scam-police" name = "scam-police"
version = "0.4.0" version = "0.5.0"
edition = "2021" edition = "2021"
authors = [ "@0xf8:projectsegfau.lt", "@jjj333:pain.agency" ] authors = [ "@0xf8:projectsegfau.lt", "@jjj333:pain.agency" ]

View File

@ -7,15 +7,19 @@ pub struct Config {
impl Config { impl Config {
pub fn load() -> Config { pub fn load() -> Config {
let keywords_reader = std::fs::File::open("config/keywords.json").expect("Couldn't find keywords.json"); let keywords_reader =
let keywords: Value = serde_json::from_reader(keywords_reader).expect("Couldn't read keywords"); std::fs::File::open("config/keywords.json").expect("Couldn't find keywords.json");
let keywords: Value =
let responses_reader = std::fs::File::open("config/responses.json").expect("Couldn't find responses.json"); serde_json::from_reader(keywords_reader).expect("Couldn't read keywords");
let responses: Value = serde_json::from_reader(responses_reader).expect("Couldn't read responses");
let responses_reader =
std::fs::File::open("config/responses.json").expect("Couldn't find responses.json");
let responses: Value =
serde_json::from_reader(responses_reader).expect("Couldn't read responses");
Self { Self {
keywords, keywords,
responses responses,
} }
} }
} }

View File

@ -1,11 +1,17 @@
use crate::{CONFIG, keywords::{Keywords, KeywordCategory}}; use crate::{
use matrix_sdk::{room::Joined, ruma::events::room::message::{RoomMessageEventContent, OriginalRoomMessageEvent}}; keywords::{KeywordCategory, Keywords},
CONFIG,
};
use matrix_sdk::{
room::Joined,
ruma::events::room::message::{OriginalRoomMessageEvent, RoomMessageEventContent},
};
use serde_json::json; use serde_json::json;
#[derive(Debug)] #[derive(Debug)]
pub enum JudgementResult { pub enum JudgementResult {
Ok, Ok,
MaybeScam, // hit atleast one category MaybeScam, // hit atleast one category
LikelyScam, // hit all categories LikelyScam, // hit all categories
} }
@ -14,25 +20,29 @@ impl JudgementResult {
match self { match self {
Self::Ok => "Ok", Self::Ok => "Ok",
Self::MaybeScam => "MaybeScam", Self::MaybeScam => "MaybeScam",
Self::LikelyScam => "LikelyScam" Self::LikelyScam => "LikelyScam",
} }
} }
} }
pub struct Judgement { pub struct Judgement {
pub text: String pub text: String,
} }
impl Judgement { impl Judgement {
pub fn judge(&self) -> anyhow::Result<JudgementResult> { pub fn judge(&self) -> anyhow::Result<JudgementResult> {
// Load keywords // Load keywords
let mut keywords = CONFIG.keywords.clone(); let mut keywords = CONFIG.keywords.clone();
let keywords = keywords.as_object_mut().unwrap().get_mut("keywords").unwrap(); let keywords = keywords
.as_object_mut()
.unwrap()
.get_mut("keywords")
.unwrap();
// Turn json into Keywords // Turn json into Keywords
let verbs = Keywords::create("verbs", &keywords["verbs"]); let verbs = Keywords::create("verbs", &keywords["verbs"]);
let currencies = Keywords::create("currencies", &keywords["currencies"]); let currencies = Keywords::create("currencies", &keywords["currencies"]);
let socials = Keywords::create("socials", &keywords["socials"]); let socials = Keywords::create("socials", &keywords["socials"]);
// Count occurences // Count occurences
let mut counter = KeywordCategory::create_counter_map(); let mut counter = KeywordCategory::create_counter_map();
@ -47,21 +57,29 @@ impl Judgement {
count_all = count_all + 1; count_all = count_all + 1;
} }
} }
if count_all == 0 { return Ok(JudgementResult::Ok) }; if count_all == 0 {
if count_all < total { return Ok(JudgementResult::MaybeScam) }; return Ok(JudgementResult::Ok);
};
if count_all < total {
return Ok(JudgementResult::MaybeScam);
};
Ok(JudgementResult::LikelyScam) Ok(JudgementResult::LikelyScam)
} }
pub async fn send_debug(&self, room: &Joined) -> anyhow::Result<()> { pub async fn send_debug(&self, room: &Joined) -> anyhow::Result<()> {
// Load keywords // Load keywords
let mut keywords = CONFIG.keywords.clone(); let mut keywords = CONFIG.keywords.clone();
let keywords = keywords.as_object_mut().unwrap().get_mut("keywords").unwrap(); let keywords = keywords
.as_object_mut()
.unwrap()
.get_mut("keywords")
.unwrap();
// Turn json into Keywords // Turn json into Keywords
let verbs = Keywords::create("verbs", &keywords["verbs"]); let verbs = Keywords::create("verbs", &keywords["verbs"]);
let currencies = Keywords::create("currencies", &keywords["currencies"]); let currencies = Keywords::create("currencies", &keywords["currencies"]);
let socials = Keywords::create("socials", &keywords["socials"]); let socials = Keywords::create("socials", &keywords["socials"]);
// Count occurences // Count occurences
let mut counter = KeywordCategory::create_counter_map(); let mut counter = KeywordCategory::create_counter_map();
@ -78,8 +96,12 @@ impl Judgement {
} }
let mut result = JudgementResult::LikelyScam; let mut result = JudgementResult::LikelyScam;
if count_all == 0 { result = JudgementResult::Ok } if count_all == 0 {
if count_all < total { result = JudgementResult::MaybeScam } result = JudgementResult::Ok
}
if count_all < total {
result = JudgementResult::MaybeScam
}
// Send message // Send message
let msg = RoomMessageEventContent::text_html( let msg = RoomMessageEventContent::text_html(
@ -90,11 +112,16 @@ impl Judgement {
Ok(()) Ok(())
} }
pub async fn alert(room: &Joined, event: &OriginalRoomMessageEvent, result: JudgementResult, is_reply: bool) -> anyhow::Result<()> { pub async fn alert(
room: &Joined,
event: &OriginalRoomMessageEvent,
result: JudgementResult,
is_reply: bool,
) -> anyhow::Result<()> {
let mut responses = CONFIG.responses.clone(); let mut responses = CONFIG.responses.clone();
let responses = responses.as_object_mut().unwrap(); let responses = responses.as_object_mut().unwrap();
// Add stats to end of response // Determine which message to send
let section = if is_reply { let section = if is_reply {
responses["reply"].as_object().unwrap() responses["reply"].as_object().unwrap()
} else { } else {
@ -117,14 +144,20 @@ impl Judgement {
// Send reaction // Send reaction
if !is_reply { if !is_reply {
room.send_raw(json!({ room.send_raw(
"m.relates_to": { json!({
"rel_type": "m.annotation", "m.relates_to": {
"event_id": event.event_id.to_string(), "rel_type": "m.annotation",
"key": "🚨🚨 SCAM 🚨🚨" "event_id": event.event_id.to_string(),
}}), "m.reaction", None).await.expect("Couldn't send reaction"); "key": "🚨🚨 SCAM 🚨🚨"
}}),
"m.reaction",
None,
)
.await
.expect("Couldn't send reaction");
} }
Ok(()) Ok(())
} }
} }

View File

@ -10,54 +10,52 @@ pub enum KeywordCategory {
impl std::fmt::Display for KeywordCategory { impl std::fmt::Display for KeywordCategory {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use KeywordCategory::*; use KeywordCategory::*;
match self { match self {
Verb => write!(f, "Verb"), Verb => write!(f, "Verb"),
Currency => write!(f, "Currency"), Currency => write!(f, "Currency"),
Social => write!(f, "Social"), Social => write!(f, "Social"),
} }
} }
} }
impl KeywordCategory { impl KeywordCategory {
pub fn to_json_var(&self) -> &str { pub fn to_json_var(&self) -> &str {
use KeywordCategory::*; use KeywordCategory::*;
match self { match self {
Verb => "verbs", Verb => "verbs",
Currency => "currencies", Currency => "currencies",
Social => "socials", Social => "socials",
} }
} }
pub fn from_json_var(var: &str) -> Result<Self,()> { pub fn from_json_var(var: &str) -> Result<Self, ()> {
use KeywordCategory::*; use KeywordCategory::*;
match var { match var {
"verbs" => Ok(Verb), "verbs" => Ok(Verb),
"currencies" => Ok(Currency), "currencies" => Ok(Currency),
"socials" => Ok(Social), "socials" => Ok(Social),
_ => Err(()) _ => Err(()),
} }
} }
pub fn create_counter_map() -> HashMap<KeywordCategory,u64> { pub fn create_counter_map() -> HashMap<KeywordCategory, u64> {
use KeywordCategory::*; use KeywordCategory::*;
let mut map: HashMap<KeywordCategory,u64> = HashMap::new(); let mut map: HashMap<KeywordCategory, u64> = HashMap::new();
map.insert(Verb, 0); map.insert(Verb, 0);
map.insert(Currency, 0); map.insert(Currency, 0);
map.insert(Social, 0); map.insert(Social, 0);
map map
} }
} }
pub struct Keywords { pub struct Keywords {
pub category: KeywordCategory, pub category: KeywordCategory,
pub words: Vec<Value> pub words: Vec<Value>,
} }
impl Keywords { impl Keywords {
@ -66,24 +64,24 @@ impl Keywords {
let Ok(category) = KeywordCategory::from_json_var(name) else { let Ok(category) = KeywordCategory::from_json_var(name) else {
panic!("Couldn't translate \"{name}\" to KeywordCategory"); panic!("Couldn't translate \"{name}\" to KeywordCategory");
}; };
Self { Self {
category, category,
words: v.to_vec() words: v.to_vec(),
} }
} }
pub fn find(&self, hay: &str) -> u64 { pub fn find(&self, hay: &str) -> u64 {
let mut hits: u64 = 0; let mut hits: u64 = 0;
for kw in self.words.to_owned().into_iter() { for kw in self.words.to_owned().into_iter() {
let kw = kw.as_str().unwrap(); let kw = kw.as_str().unwrap();
if hay.contains(kw) { if hay.contains(kw) {
hits += 1 hits += 1
} }
} }
hits hits
} }
} }

View File

@ -1,37 +1,52 @@
use matrix_sdk::{ use matrix_sdk::{
room::Room, room::Room,
ruma::{events::room::message::{ ruma::{
MessageType, OriginalSyncRoomMessageEvent, Relation, RoomMessageEvent events::room::message::{
}, OwnedRoomId}, MessageType, OriginalSyncRoomMessageEvent, Relation, RoomMessageEvent,
},
OwnedRoomId,
},
Error, LoopCtrl, Error, LoopCtrl,
}; };
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub mod matrix;
pub mod config; pub mod config;
pub mod keywords;
pub mod judge; pub mod judge;
pub mod keywords;
pub mod matrix;
static CONFIG: Lazy<config::Config> = Lazy::new(|| config::Config::load()); static CONFIG: Lazy<config::Config> = Lazy::new(|| config::Config::load());
async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> anyhow::Result<()> { async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> anyhow::Result<()> {
if let Room::Joined(room) = room { if let Room::Joined(room) = room {
let orig_event = event.to_owned().into_full_event(OwnedRoomId::from(room.room_id())); let orig_event = event
.to_owned()
.into_full_event(OwnedRoomId::from(room.room_id()));
// Ignore non-text // Ignore non-text
let MessageType::Text(text_content) = event.to_owned().content.msgtype else { let MessageType::Text(text_content) = event.to_owned().content.msgtype else {
return Ok(()); return Ok(());
}; };
// Too short to be a scam lol // Too short to be a scam lol
if text_content.body.chars().count() < 12 { return Ok(()) } if text_content.body.chars().count() < 12 {
return Ok(());
}
let text_content = text_content.body.to_lowercase(); let text_content = text_content.body.to_lowercase();
let debug = text_content.contains(";spdebug"); let debug = text_content.contains(";spdebug");
// Ignore own messages // Ignore own messages
if !debug && event.sender == room.client().user_id().expect("Couldn't get user_id").to_string() { return Ok(()) } if !debug
&& event.sender
== room
.client()
.user_id()
.expect("Couldn't get user_id")
.to_string()
{
return Ok(());
}
let judgement = judge::Judgement { text: text_content }; let judgement = judge::Judgement { text: text_content };
@ -48,13 +63,22 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
} else { } else {
None None
} }
}, }
_ => None _ => None,
} { } {
let event = event.as_original().unwrap(); let event = event.as_original().unwrap();
let content = event.content.to_owned().body().to_lowercase(); let content = event.content.to_owned().body().to_lowercase();
if !debug && event.sender == room.client().user_id().expect("Couldn't get user_id").to_string() { return Ok(()) } if !debug
&& event.sender
== room
.client()
.user_id()
.expect("Couldn't get user_id")
.to_string()
{
return Ok(());
}
let reply_judgement = judge::Judgement { text: content }; let reply_judgement = judge::Judgement { text: content };
@ -67,7 +91,13 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
judge::JudgementResult::Ok => (), judge::JudgementResult::Ok => (),
judge::JudgementResult::MaybeScam => (), judge::JudgementResult::MaybeScam => (),
judge::JudgementResult::LikelyScam => { judge::JudgementResult::LikelyScam => {
judge::Judgement::alert(&room, &orig_event, judge::JudgementResult::LikelyScam, true).await?; judge::Judgement::alert(
&room,
&orig_event,
judge::JudgementResult::LikelyScam,
true,
)
.await?;
return Ok(()); return Ok(());
} }
} }
@ -78,7 +108,13 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
judge::JudgementResult::Ok => return Ok(()), judge::JudgementResult::Ok => return Ok(()),
judge::JudgementResult::MaybeScam => return Ok(()), judge::JudgementResult::MaybeScam => return Ok(()),
judge::JudgementResult::LikelyScam => { judge::JudgementResult::LikelyScam => {
judge::Judgement::alert(&room, &orig_event, judge::JudgementResult::LikelyScam, false).await?; judge::Judgement::alert(
&room,
&orig_event,
judge::JudgementResult::LikelyScam,
false,
)
.await?;
return Ok(()); return Ok(());
} }
} }
@ -99,9 +135,15 @@ async fn main() -> anyhow::Result<()> {
let (client, sync_token) = if session_file.exists() { let (client, sync_token) = if session_file.exists() {
matrix::restore_session(&session_file).await? matrix::restore_session(&session_file).await?
} else if args.len() > 1 { } else if args.len() > 1 {
(matrix::login(&data_dir, &session_file, args.get(1).unwrap().to_owned()).await?, None) (
matrix::login(&data_dir, &session_file, args.get(1).unwrap().to_owned()).await?,
None,
)
} else { } else {
anyhow::bail!("No previous session found, please run \"{} <MXID>\"", args.get(0).unwrap()); anyhow::bail!(
"No previous session found, please run \"{} <MXID>\"",
args.get(0).unwrap()
);
}; };
let (client, sync_settings) = match matrix::sync(client, sync_token).await { let (client, sync_settings) = match matrix::sync(client, sync_token).await {
@ -114,15 +156,17 @@ async fn main() -> anyhow::Result<()> {
client.add_event_handler(on_room_message); client.add_event_handler(on_room_message);
client.sync_with_result_callback(sync_settings, |sync_result| async move { client
let response = sync_result?; .sync_with_result_callback(sync_settings, |sync_result| async move {
let response = sync_result?;
matrix::persist_sync_token(response.next_batch) matrix::persist_sync_token(response.next_batch)
.await .await
.map_err(|err| Error::UnknownError(err.into()))?; .map_err(|err| Error::UnknownError(err.into()))?;
Ok(LoopCtrl::Continue) Ok(LoopCtrl::Continue)
}).await?; })
.await?;
Ok(()) Ok(())
} }

View File

@ -1,12 +1,12 @@
use matrix_sdk::{ use matrix_sdk::{
config::SyncSettings, ruma::api::client::filter::FilterDefinition, Client, Session config::SyncSettings, ruma::api::client::filter::FilterDefinition, Client, Session,
}; };
use rand::{distributions::Alphanumeric, thread_rng, Rng}; use rand::{distributions::Alphanumeric, thread_rng, Rng};
use serde::{Deserialize, Serialize};
use serde_json::{Value, from_str};
use std::path::{Path, PathBuf};
use reqwest::Client as http; use reqwest::Client as http;
use rpassword::prompt_password; use rpassword::prompt_password;
use serde::{Deserialize, Serialize};
use serde_json::{from_str, Value};
use std::path::{Path, PathBuf};
use tokio::fs; use tokio::fs;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -24,7 +24,6 @@ pub struct FullSession {
sync_token: Option<String>, sync_token: Option<String>,
} }
// //
// Matrix Login & Init // Matrix Login & Init
// //
@ -84,33 +83,33 @@ pub async fn build_client(data_dir: &Path, hs: String) -> anyhow::Result<(Client
.collect(); .collect();
match Client::builder() match Client::builder()
.homeserver_url(&hs) .homeserver_url(&hs)
.sled_store(&db_path, Some(&passphrase))? .sled_store(&db_path, Some(&passphrase))?
.build() .build()
.await .await
{ {
Ok(client) => { Ok(client) => {
println!("[*] Homeserver OK"); println!("[*] Homeserver OK");
return Ok(( return Ok((
client, client,
ClientSession { ClientSession {
homeserver: hs, homeserver: hs,
db_path, db_path,
passphrase, passphrase,
}, },
)) ));
}
Err(error) => match &error {
matrix_sdk::ClientBuildError::AutoDiscovery(_)
| matrix_sdk::ClientBuildError::Url(_)
| matrix_sdk::ClientBuildError::Http(_) => {
anyhow::bail!("[!] {error:?}");
}
_ => {
return Err(error.into());
}
},
} }
Err(error) => match &error {
matrix_sdk::ClientBuildError::AutoDiscovery(_)
| matrix_sdk::ClientBuildError::Url(_)
| matrix_sdk::ClientBuildError::Http(_) => {
anyhow::bail!("[!] {error:?}");
}
_ => {
return Err(error.into());
}
},
}
} }
// //
@ -141,7 +140,10 @@ pub async fn resolve_homeserver(homeserver: String) -> anyhow::Result<String> {
hs.pop(); hs.pop();
} }
let ident = http::new().get(format!("{hs}/.well-known/matrix/client")).send().await; let ident = http::new()
.get(format!("{hs}/.well-known/matrix/client"))
.send()
.await;
match ident { match ident {
Ok(r) => { Ok(r) => {
let body = r.text().await?; let body = r.text().await?;
@ -150,14 +152,11 @@ pub async fn resolve_homeserver(homeserver: String) -> anyhow::Result<String> {
let discovered = json["m.homeserver"]["base_url"].as_str().unwrap(); let discovered = json["m.homeserver"]["base_url"].as_str().unwrap();
Ok(discovered.to_string()) Ok(discovered.to_string())
},
Err(e) => {
Err(e.into())
} }
Err(e) => Err(e.into()),
} }
} }
// //
// Persistence // Persistence
// //
@ -233,4 +232,4 @@ pub async fn restore_session(session_file: &Path) -> anyhow::Result<(Client, Opt
client.restore_login(user_session).await?; client.restore_login(user_session).await?;
Ok((client, sync_token)) Ok((client, sync_token))
} }