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]]
name = "scam-police"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"anyhow",
"dirs",

View File

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

View File

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

View File

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

View File

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

View File

@ -1,37 +1,52 @@
use matrix_sdk::{
room::Room,
ruma::{events::room::message::{
MessageType, OriginalSyncRoomMessageEvent, Relation, RoomMessageEvent
}, OwnedRoomId},
ruma::{
events::room::message::{
MessageType, OriginalSyncRoomMessageEvent, Relation, RoomMessageEvent,
},
OwnedRoomId,
},
Error, LoopCtrl,
};
use once_cell::sync::Lazy;
pub mod matrix;
pub mod config;
pub mod keywords;
pub mod judge;
pub mod keywords;
pub mod matrix;
static CONFIG: Lazy<config::Config> = Lazy::new(|| config::Config::load());
async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> anyhow::Result<()> {
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
let MessageType::Text(text_content) = event.to_owned().content.msgtype else {
return Ok(());
};
// 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 debug = text_content.contains(";spdebug");
// 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 };
@ -48,13 +63,22 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
} else {
None
}
},
_ => None
}
_ => None,
} {
let event = event.as_original().unwrap();
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 };
@ -67,7 +91,13 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
judge::JudgementResult::Ok => (),
judge::JudgementResult::MaybeScam => (),
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(());
}
}
@ -78,7 +108,13 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
judge::JudgementResult::Ok => return Ok(()),
judge::JudgementResult::MaybeScam => return Ok(()),
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(());
}
}
@ -99,9 +135,15 @@ async fn main() -> anyhow::Result<()> {
let (client, sync_token) = if session_file.exists() {
matrix::restore_session(&session_file).await?
} 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 {
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 {
@ -114,15 +156,17 @@ async fn main() -> anyhow::Result<()> {
client.add_event_handler(on_room_message);
client.sync_with_result_callback(sync_settings, |sync_result| async move {
let response = sync_result?;
client
.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
.map_err(|err| Error::UnknownError(err.into()))?;
Ok(LoopCtrl::Continue)
}).await?;
Ok(LoopCtrl::Continue)
})
.await?;
Ok(())
}

View File

@ -1,12 +1,12 @@
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 serde::{Deserialize, Serialize};
use serde_json::{Value, from_str};
use std::path::{Path, PathBuf};
use reqwest::Client as http;
use rpassword::prompt_password;
use serde::{Deserialize, Serialize};
use serde_json::{from_str, Value};
use std::path::{Path, PathBuf};
use tokio::fs;
#[derive(Debug, Serialize, Deserialize)]
@ -24,7 +24,6 @@ pub struct FullSession {
sync_token: Option<String>,
}
//
// Matrix Login & Init
//
@ -84,33 +83,33 @@ pub async fn build_client(data_dir: &Path, hs: String) -> anyhow::Result<(Client
.collect();
match Client::builder()
.homeserver_url(&hs)
.sled_store(&db_path, Some(&passphrase))?
.build()
.await
{
Ok(client) => {
println!("[*] Homeserver OK");
return Ok((
client,
ClientSession {
homeserver: hs,
db_path,
passphrase,
},
))
}
Err(error) => match &error {
matrix_sdk::ClientBuildError::AutoDiscovery(_)
| matrix_sdk::ClientBuildError::Url(_)
| matrix_sdk::ClientBuildError::Http(_) => {
anyhow::bail!("[!] {error:?}");
}
_ => {
return Err(error.into());
}
},
.homeserver_url(&hs)
.sled_store(&db_path, Some(&passphrase))?
.build()
.await
{
Ok(client) => {
println!("[*] Homeserver OK");
return Ok((
client,
ClientSession {
homeserver: hs,
db_path,
passphrase,
},
));
}
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();
}
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 {
Ok(r) => {
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();
Ok(discovered.to_string())
},
Err(e) => {
Err(e.into())
}
Err(e) => Err(e.into()),
}
}
//
// Persistence
//
@ -233,4 +232,4 @@ pub async fn restore_session(session_file: &Path) -> anyhow::Result<(Client, Opt
client.restore_login(user_session).await?;
Ok((client, sync_token))
}
}