Skip to content

Commit 897c4c8

Browse files
committed
feat: Add basic /mute
Implementation is missing confirmation dialog and does not log `reason` or use `quiet` argument.
1 parent fd349e6 commit 897c4c8

3 files changed

Lines changed: 114 additions & 3 deletions

File tree

pi-bot/src/discord/commands/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn all_commands() -> Vec<Command> {
2828
general::info(),
2929
staff::slowmode(),
3030
staff::nuke(),
31+
staff::mute(),
3132
sync::sync(),
3233
]
3334
}

pi-bot/src/discord/commands/staff.rs

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
use indoc::formatdoc;
22
use poise::{
3-
CreateReply,
3+
ChoiceParameter, CreateReply,
44
serenity_prelude::{
55
ButtonStyle, Colour, ComponentInteractionDataKind, CreateActionRow, CreateButton,
66
CreateEmbed, CreateInteractionResponse, CreateInteractionResponseMessage, EditChannel,
7-
GetMessages, GuildChannel, Mentionable,
7+
FormattedTimestamp, FormattedTimestampStyle, GetMessages, GuildChannel, Member,
8+
Mentionable, Timestamp,
89
futures::future::{Either, select},
910
},
1011
};
1112
use std::{pin::pin, time::Duration};
1213
use tokio::time::Instant;
1314

14-
use crate::discord::{Context, Error};
15+
use crate::discord::{Context, Error, utils::ROLE_MUTED};
1516

1617
/// Manages slowmode for a channel.
1718
#[poise::command(
@@ -197,3 +198,111 @@ pub async fn nuke(
197198
reply_handler.edit(ctx, reply).await?;
198199
Ok(())
199200
}
201+
202+
#[derive(Debug, ChoiceParameter)]
203+
enum MuteLength {
204+
#[name = "10 minutes"]
205+
Mins10,
206+
#[name = "20 minutes"]
207+
Mins20,
208+
#[name = "1 hour"]
209+
Hours1,
210+
#[name = "2 hours"]
211+
Hours2,
212+
#[name = "8 hours"]
213+
Hours8,
214+
#[name = "1 day"]
215+
Days1,
216+
#[name = "4 days"]
217+
Days4,
218+
#[name = "7 days"]
219+
Days7,
220+
#[name = "4 weeks"]
221+
Weeks4,
222+
// #[name = "1 month"]
223+
// Month1,
224+
// #[name = "1 year"]
225+
// Year1,
226+
#[name = "Indefinitely"]
227+
Indefinitely,
228+
}
229+
230+
#[derive(Debug, ChoiceParameter)]
231+
enum YesNo {
232+
Yes,
233+
No,
234+
}
235+
236+
// TODO: add view to confirm mute
237+
// TODO: use `reason` and `quiet`
238+
/// Staff command. Mutes a user.
239+
#[poise::command(
240+
slash_command,
241+
guild_only,
242+
default_member_permissions = "MANAGE_ROLES",
243+
required_bot_permissions = "MANAGE_ROLES"
244+
)]
245+
pub async fn mute(
246+
ctx: Context<'_>,
247+
#[description = "The user to mute."] mut member: Member,
248+
#[description = "The reason to mute the user."] _reason: Option<String>,
249+
#[description = "How long to mute the user for. Mutally exclusive to `until`."]
250+
mute_length: Option<MuteLength>,
251+
#[description = "Unix timestamp (in secs) for how long to keep user muted until. Mutally exclusive to `mute_length`."]
252+
until: Option<i64>,
253+
#[description = "Does not DM the user upon mute. Defaults to no."] _quiet: Option<YesNo>,
254+
) -> Result<(), Error> {
255+
let guild = ctx.guild_id().unwrap();
256+
let now = Timestamp::now();
257+
let end_time =
258+
match (mute_length, until) {
259+
(Some(_), Some(_)) => return Err(
260+
"Provided `mute_length` and `until` options when only one of them should be sent"
261+
.into(),
262+
),
263+
(None, None) => return Err("One of `mute_length` and `until` should be sent".into()),
264+
(Some(duration_name), None) => {
265+
let duration = match duration_name {
266+
MuteLength::Mins10 => Some(Duration::from_mins(10).as_secs()),
267+
MuteLength::Mins20 => Some(Duration::from_mins(20).as_secs()),
268+
MuteLength::Hours1 => Some(Duration::from_hours(1).as_secs()),
269+
MuteLength::Hours2 => Some(Duration::from_hours(2).as_secs()),
270+
MuteLength::Hours8 => Some(Duration::from_hours(8).as_secs()),
271+
MuteLength::Days1 => Some(Duration::from_hours(24).as_secs()),
272+
MuteLength::Days4 => Some(Duration::from_hours(4 * 24).as_secs()),
273+
MuteLength::Days7 => Some(Duration::from_hours(7 * 24).as_secs()),
274+
MuteLength::Weeks4 => Some(Duration::from_hours(28 * 24).as_secs()),
275+
MuteLength::Indefinitely => None,
276+
};
277+
if let Some(duration) = duration {
278+
Some(Timestamp::from_unix_timestamp(
279+
now.unix_timestamp() + duration as i64,
280+
)?)
281+
} else {
282+
None
283+
}
284+
}
285+
(None, Some(until)) => Some(Timestamp::from_unix_timestamp(until)?),
286+
};
287+
if let Some(end_time) = end_time {
288+
member
289+
.disable_communication_until_datetime(ctx.http(), end_time)
290+
.await?;
291+
ctx.reply(format!(
292+
"{} was muted until {}.",
293+
member.mention(),
294+
FormattedTimestamp::new(end_time, Some(FormattedTimestampStyle::ShortDateTime))
295+
))
296+
.await?;
297+
} else {
298+
let roles = guild.roles(ctx.http()).await?;
299+
let mute_role = roles
300+
.values()
301+
.find(|role| role.name == ROLE_MUTED)
302+
.ok_or(format!("Could not find `{}` role", ROLE_MUTED))?;
303+
member.add_role(ctx.http(), mute_role.id).await?;
304+
ctx.reply(format!("{} was muted indefinitely.", member.mention()))
305+
.await?;
306+
}
307+
Ok(())
308+
}

pi-bot/src/discord/utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::discord::{Context, Error};
44

55
static ROLE_STAFF: &str = "Staff";
66
static ROLE_VIP: &str = "VIP";
7+
pub static ROLE_MUTED: &str = "Muted";
78

89
pub static EMOJI_LOADING: &str = "<a:loading:1409087568313712731>";
910

0 commit comments

Comments
 (0)