Rework detection & response system, add debug
This commit is contained in:
parent
4fa1490707
commit
ff2d897549
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"scams":{
|
"keywords":{
|
||||||
"verbs":[
|
"verbs":[
|
||||||
"earn",
|
"earn",
|
||||||
"make", "making", "made",
|
"make", "making", "made",
|
||||||
@ -15,6 +15,7 @@
|
|||||||
"upload",
|
"upload",
|
||||||
"login",
|
"login",
|
||||||
"send",
|
"send",
|
||||||
|
"join",
|
||||||
"buy",
|
"buy",
|
||||||
"check",
|
"check",
|
||||||
"private"
|
"private"
|
||||||
@ -32,8 +33,6 @@
|
|||||||
"nft",
|
"nft",
|
||||||
"token",
|
"token",
|
||||||
"free",
|
"free",
|
||||||
"meet",
|
|
||||||
"upload",
|
|
||||||
"gift",
|
"gift",
|
||||||
"card",
|
"card",
|
||||||
"nude",
|
"nude",
|
||||||
@ -59,10 +58,9 @@
|
|||||||
"wickr",
|
"wickr",
|
||||||
"kik",
|
"kik",
|
||||||
"instagram",
|
"instagram",
|
||||||
|
"dm me",
|
||||||
"👇", "👆️",
|
"👇", "👆️",
|
||||||
"+1", "+2"
|
"+1", "+2"
|
||||||
],
|
]
|
||||||
"response":"Warning! This message is likely to be a scam, hoping to lure you in and steal your money! Please visit these resources for more information:\n- https://www.sec.gov/oiea/investor-alerts-and-bulletins/digital-asset-and-crypto-investment-scams-investor-alert \n [!mods !modhelp]",
|
|
||||||
"response_md":"Warning! This message is likely to be a <b>scam</b>, hoping to lure you in and steal your money! Please visit these resources for more information: <ul><li><a href=\"https://www.sec.gov/oiea/investor-alerts-and-bulletins/digital-asset-and-crypto-investment-scams-investor-alert\">https://www.sec.gov/oiea/investor-alerts-and-bulletins/digital-asset-and-crypto-investment-scams-investor-alert</a></li></ul> [!mods !modhelp]"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
config/responses.json
Normal file
18
config/responses.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"reply": {
|
||||||
|
"Ok": null,
|
||||||
|
"MaybeScam": null,
|
||||||
|
"LikelyScam": {
|
||||||
|
"plain": "Watch out, the message you replied to has been detected as a scam! Please don't do anything they ask you to do! Stay safe",
|
||||||
|
"html": "Watch out, the message you replied to has been detected as a <b>scam</b>! <u>Please don't do anything they ask you to do</u>! Stay safe"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"Ok": null,
|
||||||
|
"MaybeScam": null,
|
||||||
|
"LikelyScam": {
|
||||||
|
"plain": "Warning! This message is likely to be a scam, seeking to lure you in and steal your money! Please visit these resources for more information:\n- https://www.sec.gov/oiea/investor-alerts-and-bulletins/digital-asset-and-crypto-investment-scams-investor-alert \n [!mods !modhelp]",
|
||||||
|
"html": "Warning! This message is likely to be a <b>scam</b>, seeking to lure you in and steal your money! Please visit these resources for more information: <ul><li><a href=\"https://www.sec.gov/oiea/investor-alerts-and-bulletins/digital-asset-and-crypto-investment-scams-investor-alert\">https://www.sec.gov/oiea/investor-alerts-and-bulletins/digital-asset-and-crypto-investment-scams-investor-alert</a></li></ul> [!mods !modhelp]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,20 @@ use serde_json::Value;
|
|||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub keywords: Value,
|
pub keywords: Value,
|
||||||
|
pub responses: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = 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.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 {
|
Self {
|
||||||
keywords
|
keywords,
|
||||||
|
responses
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
87
src/judge.rs
87
src/judge.rs
@ -2,27 +2,37 @@ use crate::{CONFIG, keywords::{Keywords, KeywordCategory}};
|
|||||||
use matrix_sdk::{room::Joined, ruma::events::room::message::{RoomMessageEventContent, OriginalRoomMessageEvent}};
|
use matrix_sdk::{room::Joined, ruma::events::room::message::{RoomMessageEventContent, OriginalRoomMessageEvent}};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JudgementResult {
|
||||||
|
pub fn to_json_var(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Ok => "Ok",
|
||||||
|
Self::MaybeScam => "MaybeScam",
|
||||||
|
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 scams = keywords.as_object_mut().unwrap().get_mut("scams").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", &scams["verbs"]);
|
let verbs = Keywords::create("verbs", &keywords["verbs"]);
|
||||||
let currencies = Keywords::create("currencies", &scams["currencies"]);
|
let currencies = Keywords::create("currencies", &keywords["currencies"]);
|
||||||
let socials = Keywords::create("socials", &scams["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();
|
||||||
@ -38,33 +48,82 @@ impl Judgement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if count_all == 0 { return Ok(JudgementResult::Ok) };
|
if count_all == 0 { return Ok(JudgementResult::Ok) };
|
||||||
if count_all < total { return Ok(JudgementResult::MaybeScam) };
|
if count_all < total { return Ok(JudgementResult::MaybeScam) };
|
||||||
|
|
||||||
Ok(JudgementResult::LikelyScam)
|
Ok(JudgementResult::LikelyScam)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn alert(room: &Joined, event: &OriginalRoomMessageEvent) -> anyhow::Result<()> {
|
pub async fn send_debug(&self, room: &Joined) -> anyhow::Result<()> {
|
||||||
|
// Load keywords
|
||||||
let mut keywords = CONFIG.keywords.clone();
|
let mut keywords = CONFIG.keywords.clone();
|
||||||
let scams = keywords.as_object_mut().unwrap().get_mut("scams").unwrap();
|
let keywords = keywords.as_object_mut().unwrap().get_mut("keywords").unwrap();
|
||||||
|
|
||||||
// Add stats to end of response
|
// Turn json into Keywords
|
||||||
let response = scams["response"].as_str().unwrap();
|
let verbs = Keywords::create("verbs", &keywords["verbs"]);
|
||||||
let response_html = scams["response_md"].as_str().unwrap();
|
let currencies = Keywords::create("currencies", &keywords["currencies"]);
|
||||||
|
let socials = Keywords::create("socials", &keywords["socials"]);
|
||||||
|
|
||||||
|
// Count occurences
|
||||||
|
let mut counter = KeywordCategory::create_counter_map();
|
||||||
|
counter.insert(KeywordCategory::Verb, verbs.find(&self.text));
|
||||||
|
counter.insert(KeywordCategory::Currency, currencies.find(&self.text));
|
||||||
|
counter.insert(KeywordCategory::Social, socials.find(&self.text));
|
||||||
|
|
||||||
|
let mut count_all = 0;
|
||||||
|
let total = counter.len();
|
||||||
|
for (_category, count) in counter.to_owned() {
|
||||||
|
if count > 0 {
|
||||||
|
count_all = count_all + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = JudgementResult::LikelyScam;
|
||||||
|
if count_all == 0 { result = JudgementResult::Ok }
|
||||||
|
if count_all < total { result = JudgementResult::MaybeScam }
|
||||||
|
|
||||||
// Send message
|
// Send message
|
||||||
let msg = RoomMessageEventContent::text_html(response, response_html);
|
let msg = RoomMessageEventContent::text_html(
|
||||||
|
format!("{counter:?}, {count_all}/{total}, {result:?}"),
|
||||||
|
format!("<code>{counter:?}</code><br>Categories covered: <code>{count_all}/{total}</code><br>Verdict: <code>{result:?}</code>"));
|
||||||
|
room.send(msg, None).await.expect("Couldn't send message");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
let section = if is_reply {
|
||||||
|
responses["reply"].as_object().unwrap()
|
||||||
|
} else {
|
||||||
|
responses["message"].as_object().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_type = section.get(result.to_json_var()).unwrap();
|
||||||
|
if response_type.is_null() {
|
||||||
|
anyhow::bail!("Called alert with result that has no detection message");
|
||||||
|
}
|
||||||
|
|
||||||
|
let response_type = response_type.as_object().unwrap();
|
||||||
|
let plain = response_type["plain"].as_str().unwrap();
|
||||||
|
let html = response_type["html"].as_str().unwrap();
|
||||||
|
|
||||||
|
// Send message
|
||||||
|
let msg = RoomMessageEventContent::text_html(plain, html);
|
||||||
let reply = msg.make_reply_to(event);
|
let reply = msg.make_reply_to(event);
|
||||||
room.send(reply, None).await.expect("Couldn't send message");
|
room.send(reply, None).await.expect("Couldn't send message");
|
||||||
|
|
||||||
// Send reaction
|
// Send reaction
|
||||||
|
if !is_reply {
|
||||||
room.send_raw(json!({
|
room.send_raw(json!({
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
"rel_type": "m.annotation",
|
"rel_type": "m.annotation",
|
||||||
"event_id": event.event_id.to_string(),
|
"event_id": event.event_id.to_string(),
|
||||||
"key": "🚨🚨 SCAM 🚨🚨"
|
"key": "🚨🚨 SCAM 🚨🚨"
|
||||||
}}), "m.reaction", None).await.expect("Couldn't send reaction");
|
}}), "m.reaction", None).await.expect("Couldn't send reaction");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
21
src/main.rs
21
src/main.rs
@ -1,7 +1,7 @@
|
|||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
room::Room,
|
room::Room,
|
||||||
ruma::{events::room::message::{
|
ruma::{events::room::message::{
|
||||||
MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent, Relation, RoomMessageEvent
|
MessageType, OriginalSyncRoomMessageEvent, Relation, RoomMessageEvent
|
||||||
}, OwnedRoomId},
|
}, OwnedRoomId},
|
||||||
Error, LoopCtrl,
|
Error, LoopCtrl,
|
||||||
};
|
};
|
||||||
@ -23,14 +23,16 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ignore own messages
|
|
||||||
if event.sender == room.client().user_id().expect("Couldn't get user_id").to_string() { 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");
|
||||||
|
|
||||||
|
// Ignore own messages
|
||||||
|
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 };
|
||||||
|
|
||||||
// Handle replies
|
// Handle replies
|
||||||
@ -52,15 +54,20 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) -> any
|
|||||||
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 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 };
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
reply_judgement.send_debug(&room).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
match reply_judgement.judge()? {
|
match reply_judgement.judge()? {
|
||||||
judge::JudgementResult::Ok => (),
|
judge::JudgementResult::Ok => (),
|
||||||
judge::JudgementResult::MaybeScam => (),
|
judge::JudgementResult::MaybeScam => (),
|
||||||
judge::JudgementResult::LikelyScam => {
|
judge::JudgementResult::LikelyScam => {
|
||||||
judge::Judgement::alert(&room, &orig_event).await?;
|
judge::Judgement::alert(&room, &orig_event, judge::JudgementResult::LikelyScam, true).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +78,7 @@ 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).await?;
|
judge::Judgement::alert(&room, &orig_event, judge::JudgementResult::LikelyScam, false).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user