-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from CrawKatt/antispam
✨ feat: Adding a new anti-spam system and refactoring
- Loading branch information
Showing
6 changed files
with
215 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
use std::sync::Arc; | ||
use regex::Regex; | ||
use tokio::sync::Mutex; | ||
use serenity::all::{Channel, ChannelId, Context, CreateEmbed, CreateEmbedAuthor, CreateMessage, GetMessages, GuildId, Member, Message, Timestamp, UserId}; | ||
use std::time::Instant; | ||
use once_cell::sync::Lazy; | ||
use serenity::all::standard::CommandResult; | ||
|
||
#[derive(Debug)] | ||
pub struct MessageTracker { | ||
author_id: UserId, | ||
message_content: Arc<String>, | ||
channel_ids: Vec<ChannelId>, | ||
last_message_time: Instant, | ||
} | ||
|
||
impl MessageTracker { | ||
pub fn builder() -> MessageTrackerBuilder { | ||
MessageTrackerBuilder::default() | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct MessageTrackerBuilder { | ||
author_id: Option<UserId>, | ||
message_content: Option<Arc<String>>, | ||
channel_ids: Option<Vec<ChannelId>>, | ||
} | ||
|
||
impl MessageTrackerBuilder { | ||
pub fn author_id(mut self, author_id: UserId) -> Self { | ||
self.author_id = Some(author_id); | ||
self | ||
} | ||
|
||
pub fn message_content(mut self, message_content: Arc<String>) -> Self { | ||
self.message_content = Some(message_content); | ||
self | ||
} | ||
|
||
pub fn channel_ids(mut self, channel_ids: Vec<ChannelId>) -> Self { | ||
self.channel_ids = Some(channel_ids); | ||
self | ||
} | ||
|
||
pub fn build(self) -> Result<MessageTracker, &'static str> { | ||
Ok(MessageTracker { | ||
author_id: self.author_id.ok_or("Author id is missing")?, | ||
message_content: self.message_content.ok_or("Message content is missing")?, | ||
channel_ids: self.channel_ids.ok_or("Channel ids are missing")?, | ||
last_message_time: Instant::now(), | ||
}) | ||
} | ||
} | ||
|
||
static MESSAGE_TRACKER: Lazy<Mutex<Vec<MessageTracker>>> = Lazy::new(|| { | ||
Mutex::new(Vec::new()) | ||
}); | ||
|
||
pub fn extract_link(text: &str) -> Option<String> { | ||
Regex::new(r"(https?://\S+)").map_or(None, |url_re| url_re.find(text).map(|m| m.as_str().to_string())) | ||
} | ||
|
||
pub async fn spam_checker( | ||
message_content: Arc<String>, | ||
channel_id: ChannelId, | ||
ctx: &Context, | ||
time: i64, | ||
new_message: &Message, | ||
guild_id: GuildId | ||
) -> CommandResult { | ||
let author_id = new_message.author.id; | ||
let mut member = guild_id.member(&ctx.http, new_message.author.id).await?; | ||
let mut message_tracker = MESSAGE_TRACKER.lock().await; | ||
|
||
if let Some(last_message) = message_tracker | ||
.iter() | ||
.last() | ||
{ | ||
if last_message.author_id == author_id && last_message.message_content != message_content { | ||
message_tracker.clear(); | ||
} | ||
} | ||
|
||
let message = if let Some(message) = message_tracker | ||
.iter_mut() | ||
.find(|m| m.author_id == author_id && m.message_content == message_content) | ||
{ | ||
// Inicializa el tiempo del último mensaje | ||
message.last_message_time = Instant::now(); | ||
|
||
// Si el mensaje existe y el canal no está en la lista de canales, añade el canal a la lista de canales | ||
if message.channel_ids.contains(&channel_id) { | ||
// Si el mensaje se repite en el mismo canal, borra el vector | ||
// Debug: println!("Message repeated in the same channel, clearing the vector"); | ||
message_tracker.clear(); | ||
|
||
return Ok(()); | ||
} | ||
message.channel_ids.push(channel_id); | ||
|
||
message | ||
} else { | ||
// Si el mensaje no existe, crea un nuevo rastreador de mensajes y añádelo a la lista | ||
let message = MessageTracker::builder() | ||
.author_id(author_id) | ||
.message_content(message_content.clone()) | ||
.channel_ids(vec![channel_id]) | ||
.build()?; | ||
|
||
message_tracker.push(message); | ||
message_tracker.last_mut().ok_or("Failed to get the last message tracker")? | ||
}; | ||
|
||
if message.channel_ids.len() >= 3 { | ||
apply_timeout(&mut member, ctx, time, new_message).await?; | ||
delete_spam_messages(message, ctx, author_id, message_content).await?; | ||
|
||
// Limpia completamente el rastreador de mensajes para reiniciar el rastreo de mensajes | ||
message_tracker.retain(|m| m.author_id != author_id); | ||
} | ||
// Debug: println!("Tracker: {message_tracker:#?}"); | ||
|
||
drop(message_tracker); | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn delete_spam_messages( | ||
message: &MessageTracker, | ||
ctx: &Context, | ||
author_id: UserId, | ||
message_content: Arc<String>, | ||
) -> CommandResult { | ||
// Borra cada mensaje individualmente | ||
for channel_id in &message.channel_ids { | ||
let channel = channel_id.to_channel(ctx).await?; | ||
let Channel::Guild(channel) = channel else { | ||
return Ok(()) | ||
}; | ||
|
||
let messages = channel.messages(&ctx.http, GetMessages::new()).await?; | ||
for message in messages { | ||
if message.author.id == author_id && &*message.content == &*message_content { | ||
message.delete(&ctx.http).await?; | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn create_embed( | ||
ctx: &Context, | ||
guild_id: GuildId, | ||
author_id: UserId, | ||
message_content: &str, | ||
) -> CommandResult { | ||
let log_channel = ChannelId::new(1230718736206532628); | ||
let author_user = author_id.to_user(&ctx.http).await?; | ||
let author_member = guild_id.member(&ctx.http, author_id).await?; | ||
let username = author_member.distinct(); | ||
let embed = CreateEmbed::default() | ||
.title("Spam detectado") | ||
.author(CreateEmbedAuthor::new(username) | ||
.icon_url(author_user.face())) | ||
.description(format!("El usuario <@{author_id}> Es sospechoso de enviar spam en el servidor.\nMensaje: {message_content}")) | ||
.color(0x00ff_0000); | ||
let builder = CreateMessage::default().embed(embed); | ||
|
||
log_channel.send_message(&ctx.http, builder).await?; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Silencia al autor del mensaje y elimina el mensaje | ||
pub async fn apply_timeout( | ||
member: &mut Member, | ||
ctx: &Context, | ||
time_out_timer: i64, | ||
message: &Message, | ||
) -> CommandResult { | ||
|
||
let time = Timestamp::now().unix_timestamp() + time_out_timer; | ||
let time = Timestamp::from_unix_timestamp(time)?; | ||
member.disable_communication_until_datetime(&ctx.http, time).await?; | ||
message.delete(&ctx.http).await?; | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
pub mod join; | ||
pub mod daily_challenge; | ||
pub mod anti_spam; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters