Skip to content

Commit 30a2878

Browse files
committed
add antimention system
1 parent 3e9a467 commit 30a2878

5 files changed

Lines changed: 100 additions & 7 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bot-rs"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
edition = "2024"
55

66
[dependencies]

src/env.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pub struct Env {
88
pub database_url: String,
99
pub database_migrate: bool,
1010

11+
pub antimention_user_ids: Vec<u64>,
12+
pub antimention_whitelisted_role_ids: Vec<u64>,
13+
1114
pub github_channel_id: u64,
1215
pub github_sponsors_channel_id: Option<u64>,
1316
pub github_sponsors_login: Option<String>,
@@ -44,6 +47,17 @@ impl Env {
4447
.parse()
4548
.unwrap(),
4649

50+
antimention_user_ids: std::env::var("ANTIMENTION_USER_IDS")
51+
.unwrap_or("".to_string())
52+
.split(',')
53+
.filter_map(|s| s.trim().parse().ok())
54+
.collect(),
55+
antimention_whitelisted_role_ids: std::env::var("ANTIMENTION_WHITELISTED_ROLE_IDS")
56+
.unwrap_or("".to_string())
57+
.split(',')
58+
.filter_map(|s| s.trim().parse().ok())
59+
.collect(),
60+
4761
github_channel_id: std::env::var("GITHUB_CHANNEL_ID")
4862
.expect("GITHUB_CHANNEL_ID is required")
4963
.trim_matches('"')

src/events.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
use serenity::all::{ActivityData, Event, Interaction, ReactionType};
1+
use std::{
2+
collections::HashMap,
3+
sync::{Arc, LazyLock},
4+
};
5+
6+
use serenity::all::{ActivityData, Event, Interaction, ReactionType, Timestamp, UserId};
7+
use tokio::{sync::Mutex, time};
28

39
pub struct EventHandler {
410
pub components: crate::components::ComponentList,
@@ -44,6 +50,79 @@ impl serenity::all::RawEventHandler for EventHandler {
4450
.react(&ctx.http, ReactionType::Unicode('👋'.into()))
4551
.await?;
4652
}
53+
54+
let Some(guild_id) = event.message.guild_id else {
55+
return Ok(());
56+
};
57+
58+
for mention in &event.message.mentions {
59+
if state.env.antimention_user_ids.contains(&mention.id.get())
60+
&& !state
61+
.env
62+
.antimention_user_ids
63+
.contains(&event.message.author.id.get())
64+
&& let Ok(mut member) =
65+
guild_id.member(&ctx.http, event.message.author.id).await
66+
{
67+
for role_id in &state.env.antimention_whitelisted_role_ids {
68+
if member.roles.contains(&(*role_id).into()) {
69+
return Ok(());
70+
}
71+
}
72+
73+
static TIMEOUT_MAP: LazyLock<Arc<Mutex<HashMap<UserId, u32>>>> =
74+
LazyLock::new(|| {
75+
let map = Arc::new(Mutex::new(HashMap::new()));
76+
77+
tokio::spawn({
78+
let map = Arc::clone(&map);
79+
async move {
80+
loop {
81+
time::sleep(time::Duration::from_hours(6)).await;
82+
let mut map = map.lock().await;
83+
map.retain(|_, count| *count > 0);
84+
for count in map.values_mut() {
85+
if *count > 0 {
86+
*count -= 1;
87+
}
88+
}
89+
}
90+
}
91+
});
92+
93+
map
94+
});
95+
96+
let mut timeout_map = TIMEOUT_MAP.lock().await;
97+
let timeout_count = timeout_map.entry(mention.id).or_insert(0);
98+
*timeout_count += 1;
99+
let timeout_duration = match *timeout_count {
100+
1 | 2 => 30,
101+
3 | 4 => 60,
102+
5 => 300,
103+
6 => 600,
104+
_ => 3600,
105+
};
106+
drop(timeout_map);
107+
108+
let timestamp = match Timestamp::from_unix_timestamp(
109+
chrono::Utc::now().timestamp() + timeout_duration,
110+
) {
111+
Ok(t) => t,
112+
Err(_) => return Ok(()),
113+
};
114+
115+
member
116+
.disable_communication_until(&ctx.http, timestamp)
117+
.await?;
118+
event
119+
.message
120+
.reply_ping(&ctx.http, "👋 Hey, please do not mention this person. You have been temporarily timed out, repeated offenses will result in longer timeouts.")
121+
.await?;
122+
123+
break;
124+
}
125+
}
47126
}
48127
Event::InteractionCreate(event) => {
49128
if let Interaction::Component(component) = &event.interaction

src/routes/github.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -553,13 +553,13 @@ async fn handle_organization_event(
553553
let mut container_components = Vec::new();
554554
let mut channel_id = state.env.github_channel_id;
555555

556-
if state.env.github_token.is_some() {
557-
return Ok(());
558-
}
559-
560556
if let WebhookEventPayload::Sponsorship(sponsorship) = event.specific
561557
&& sponsorship.action == SponsorshipWebhookEventAction::Created
562558
{
559+
if state.env.github_token.is_some() {
560+
return Ok(());
561+
}
562+
563563
#[derive(Deserialize)]
564564
struct SponsorshipMaintainer {
565565
avatar_url: String,

0 commit comments

Comments
 (0)