tangled
alpha
login
or
join now
isabelroses.com
/
blahaj
1
fork
atom
silly goober bot
1
fork
atom
overview
issues
pulls
pipelines
feat: moderation commands
isabelroses.com
11 months ago
483afc5c
d8d23a4b
+272
6 changed files
expand all
collapse all
unified
split
src
commands
mod.rs
moderation
ban.rs
kick.rs
mod.rs
timeout.rs
main.rs
+1
src/commands/mod.rs
···
1
1
pub mod bot;
2
2
pub mod fun;
3
3
pub mod misc;
4
4
+
pub mod moderation;
4
5
pub mod user;
+73
src/commands/moderation/ban.rs
···
1
1
+
use crate::Result;
2
2
+
use poise::serenity_prelude::all::User;
3
3
+
4
4
+
use crate::types::Context;
5
5
+
6
6
+
#[poise::command(slash_command, guild_only, required_permissions = "BAN_MEMBERS")]
7
7
+
pub async fn ban(
8
8
+
ctx: Context<'_>,
9
9
+
user: User,
10
10
+
delete_messages_day_count: Option<u8>,
11
11
+
reason: Option<String>,
12
12
+
) -> Result<()> {
13
13
+
let guild = ctx
14
14
+
.serenity_context()
15
15
+
.http
16
16
+
.get_guild(ctx.guild_id().unwrap())
17
17
+
.await?;
18
18
+
19
19
+
let user_member = guild.member(ctx, user.id).await.unwrap();
20
20
+
let bot_member = guild.member(ctx, ctx.framework().bot_id).await.unwrap();
21
21
+
let author_member = guild.member(ctx, ctx.author()).await.unwrap();
22
22
+
23
23
+
let user_highest_role = user_member
24
24
+
.roles
25
25
+
.iter()
26
26
+
.filter_map(|role| guild.roles.get(role))
27
27
+
.max_by_key(|role| role.position);
28
28
+
29
29
+
let author_highest_role = author_member
30
30
+
.roles
31
31
+
.iter()
32
32
+
.filter_map(|role| guild.roles.get(role))
33
33
+
.max_by_key(|role| role.position);
34
34
+
35
35
+
let bot_highest_role = bot_member
36
36
+
.roles
37
37
+
.iter()
38
38
+
.filter_map(|role| guild.roles.get(role))
39
39
+
.max_by_key(|role| role.position);
40
40
+
41
41
+
if !guild
42
42
+
.user_permissions_in(
43
43
+
&ctx.guild_channel().await.unwrap(),
44
44
+
&guild.member(ctx, ctx.framework().bot_id).await.unwrap(),
45
45
+
)
46
46
+
.ban_members()
47
47
+
{
48
48
+
ctx.say("Bot missing permission: ``Ban Members``").await?;
49
49
+
return Ok(());
50
50
+
}
51
51
+
52
52
+
if author_highest_role < user_highest_role {
53
53
+
ctx.say("User has higher role then you.").await?;
54
54
+
return Ok(());
55
55
+
}
56
56
+
57
57
+
if bot_highest_role < user_highest_role {
58
58
+
ctx.say("User has higher role then bot!").await?;
59
59
+
return Ok(());
60
60
+
}
61
61
+
62
62
+
guild
63
63
+
.member(ctx, user.id)
64
64
+
.await?
65
65
+
.ban_with_reason(
66
66
+
ctx,
67
67
+
delete_messages_day_count.unwrap_or(0),
68
68
+
&reason.unwrap_or("No reason provided.".to_string()),
69
69
+
)
70
70
+
.await?;
71
71
+
72
72
+
Ok(())
73
73
+
}
+63
src/commands/moderation/kick.rs
···
1
1
+
use crate::types::Context;
2
2
+
use color_eyre::Result;
3
3
+
use poise::serenity_prelude::all::User;
4
4
+
5
5
+
#[poise::command(slash_command, guild_only, required_permissions = "KICK_MEMBERS")]
6
6
+
pub async fn kick(ctx: Context<'_>, user: User, reason: Option<String>) -> Result<()> {
7
7
+
let guild = ctx
8
8
+
.serenity_context()
9
9
+
.http
10
10
+
.get_guild(ctx.guild_id().unwrap())
11
11
+
.await?;
12
12
+
13
13
+
let user_member = guild.member(ctx, user.id).await.unwrap();
14
14
+
let bot_member = guild.member(ctx, ctx.framework().bot_id).await.unwrap();
15
15
+
let author_member = guild.member(ctx, ctx.author()).await.unwrap();
16
16
+
17
17
+
let user_highest_role = user_member
18
18
+
.roles
19
19
+
.iter()
20
20
+
.filter_map(|role| guild.roles.get(role))
21
21
+
.max_by_key(|role| role.position);
22
22
+
23
23
+
let author_highest_role = author_member
24
24
+
.roles
25
25
+
.iter()
26
26
+
.filter_map(|role| guild.roles.get(role))
27
27
+
.max_by_key(|role| role.position);
28
28
+
29
29
+
let bot_highest_role = bot_member
30
30
+
.roles
31
31
+
.iter()
32
32
+
.filter_map(|role| guild.roles.get(role))
33
33
+
.max_by_key(|role| role.position);
34
34
+
35
35
+
if !guild
36
36
+
.user_permissions_in(
37
37
+
&ctx.guild_channel().await.unwrap(),
38
38
+
&guild.member(ctx, ctx.framework().bot_id).await.unwrap(),
39
39
+
)
40
40
+
.kick_members()
41
41
+
{
42
42
+
ctx.say("Bot missing permission: ``Kick Members``").await?;
43
43
+
return Ok(());
44
44
+
}
45
45
+
46
46
+
if author_highest_role < user_highest_role {
47
47
+
ctx.say("User has higher role then you.").await?;
48
48
+
return Ok(());
49
49
+
}
50
50
+
51
51
+
if bot_highest_role < user_highest_role {
52
52
+
ctx.say("User has higher role then bot!").await?;
53
53
+
return Ok(());
54
54
+
}
55
55
+
56
56
+
guild
57
57
+
.member(ctx, user.id)
58
58
+
.await?
59
59
+
.kick_with_reason(ctx, &reason.unwrap_or("No reason provided.".to_string()))
60
60
+
.await?;
61
61
+
62
62
+
Ok(())
63
63
+
}
+3
src/commands/moderation/mod.rs
···
1
1
+
pub mod ban;
2
2
+
pub mod kick;
3
3
+
pub mod timeout;
+128
src/commands/moderation/timeout.rs
···
1
1
+
use crate::types::Context;
2
2
+
use color_eyre::Result;
3
3
+
use poise::serenity_prelude::all::{EditMember, Timestamp, User};
4
4
+
use regex::Regex;
5
5
+
6
6
+
#[poise::command(slash_command, guild_only, required_permissions = "MODERATE_MEMBERS")]
7
7
+
pub async fn timeout(
8
8
+
ctx: Context<'_>,
9
9
+
user: User,
10
10
+
#[description = "how long the time out is for. (5s, 2m, 12h, 3d, 2w)"] duration: String,
11
11
+
reason: Option<String>,
12
12
+
) -> Result<()> {
13
13
+
let guild = ctx
14
14
+
.serenity_context()
15
15
+
.http
16
16
+
.get_guild(ctx.guild_id().unwrap())
17
17
+
.await?;
18
18
+
19
19
+
let timeout_max_value = 2419200;
20
20
+
21
21
+
let user_member = guild.member(ctx, user.id).await.unwrap();
22
22
+
let bot_member = guild.member(ctx, ctx.framework().bot_id).await.unwrap();
23
23
+
let author_member = guild.member(ctx, ctx.author()).await.unwrap();
24
24
+
25
25
+
let user_highest_role = user_member
26
26
+
.roles
27
27
+
.iter()
28
28
+
.filter_map(|role| guild.roles.get(role))
29
29
+
.max_by_key(|role| role.position);
30
30
+
31
31
+
let author_highest_role = author_member
32
32
+
.roles
33
33
+
.iter()
34
34
+
.filter_map(|role| guild.roles.get(role))
35
35
+
.max_by_key(|role| role.position);
36
36
+
37
37
+
let bot_highest_role = bot_member
38
38
+
.roles
39
39
+
.iter()
40
40
+
.filter_map(|role| guild.roles.get(role))
41
41
+
.max_by_key(|role| role.position);
42
42
+
43
43
+
if guild
44
44
+
.user_permissions_in(
45
45
+
&ctx.guild_channel().await.unwrap(),
46
46
+
&guild.member(ctx, user.id).await.unwrap(),
47
47
+
)
48
48
+
.administrator()
49
49
+
{
50
50
+
ctx.say("Target user has permission: ``Administrator``")
51
51
+
.await?;
52
52
+
return Ok(());
53
53
+
}
54
54
+
55
55
+
if !guild
56
56
+
.user_permissions_in(
57
57
+
&ctx.guild_channel().await.unwrap(),
58
58
+
&guild.member(ctx, ctx.framework().bot_id).await.unwrap(),
59
59
+
)
60
60
+
.moderate_members()
61
61
+
{
62
62
+
ctx.say("Bot missing permission: ``Moderate Members``")
63
63
+
.await?;
64
64
+
return Ok(());
65
65
+
}
66
66
+
67
67
+
if author_highest_role < user_highest_role {
68
68
+
ctx.say("User has higher role then you.").await?;
69
69
+
return Ok(());
70
70
+
}
71
71
+
72
72
+
if bot_highest_role < user_highest_role {
73
73
+
ctx.say("User has higher role then bot!").await?;
74
74
+
return Ok(());
75
75
+
}
76
76
+
77
77
+
let Some(seconds) = parse_duration(&duration) else {
78
78
+
ctx.say("Invalid duration! (Valid durations: 5s, 2m, 12h, 3d, 2w)")
79
79
+
.await?;
80
80
+
return Ok(());
81
81
+
};
82
82
+
83
83
+
if seconds > timeout_max_value {
84
84
+
ctx.say("Duration too long! (Max duration: 28d)").await?;
85
85
+
return Ok(());
86
86
+
}
87
87
+
88
88
+
let timeout_until =
89
89
+
Timestamp::from_unix_timestamp(Timestamp::now().unix_timestamp() + seconds as i64)?;
90
90
+
91
91
+
guild
92
92
+
.member(ctx, user.id)
93
93
+
.await?
94
94
+
.edit(
95
95
+
ctx,
96
96
+
EditMember::new()
97
97
+
.disable_communication_until(timeout_until.to_string())
98
98
+
.audit_log_reason(&reason.unwrap_or("No reason provided.".to_string())),
99
99
+
)
100
100
+
.await?;
101
101
+
102
102
+
ctx.say(format!("Timed out <@{}> for {}!", user.id, duration))
103
103
+
.await?;
104
104
+
105
105
+
Ok(())
106
106
+
}
107
107
+
108
108
+
fn parse_duration(input: &str) -> Option<u64> {
109
109
+
let re = Regex::new(r"(\d+)([smhdw])").unwrap();
110
110
+
111
111
+
if let Some(caps) = re.captures(input) {
112
112
+
let value: u64 = caps[1].parse().ok()?;
113
113
+
let unit = &caps[2];
114
114
+
115
115
+
let seconds = match unit {
116
116
+
"s" => value,
117
117
+
"m" => value * 60,
118
118
+
"h" => value * 3600,
119
119
+
"d" => value * 86400,
120
120
+
"w" => value * 604800,
121
121
+
_ => return None,
122
122
+
};
123
123
+
124
124
+
Some(seconds)
125
125
+
} else {
126
126
+
None
127
127
+
}
128
128
+
}
+4
src/main.rs
···
35
35
// misc commands
36
36
commands::misc::nixpkgs::nixpkgs(),
37
37
commands::misc::crates::crates(),
38
38
+
// moderation commands
39
39
+
commands::moderation::ban::ban(),
40
40
+
commands::moderation::kick::kick(),
41
41
+
commands::moderation::timeout::timeout(),
38
42
// fun commands
39
43
commands::fun::nix::nix(),
40
44
commands::fun::chance::roll(),