255 lines
7.4 KiB
Rust
255 lines
7.4 KiB
Rust
use async_trait::async_trait;
|
|
use time::UtcDateTime;
|
|
|
|
use crate::{
|
|
database::models::{UserIncident, UserIncidentType},
|
|
extras::Extras,
|
|
framework::{
|
|
commands::{
|
|
Command, CommandError,
|
|
arguments::{Arg, Argument, ArgumentParsingError, NeedyGreedyString, ParseArguments},
|
|
context::CommandContext,
|
|
permissions::Permission,
|
|
},
|
|
runtime::Runtime,
|
|
},
|
|
groups::moderation::{
|
|
arguments::{DurationArgument, UserArgument},
|
|
get_incident_page,
|
|
permissions::{BotModeratorPermission, BotOwnerPermission},
|
|
},
|
|
state::State,
|
|
utils::get_random_u64,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub struct ModeratorCommand;
|
|
|
|
#[async_trait]
|
|
impl Command<State, Extras> for ModeratorCommand {
|
|
fn name(&self) -> &'static str {
|
|
"moderator"
|
|
}
|
|
|
|
fn aliases(&self) -> &'static [&'static str] {
|
|
&["mod"]
|
|
}
|
|
|
|
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
|
|
vec![Box::new(BotOwnerPermission)]
|
|
}
|
|
|
|
fn show_in_help(&self, rt: &Runtime<State, Extras>, extras: &Extras) -> bool {
|
|
extras.user.id == rt.state.bot_owner_id
|
|
}
|
|
|
|
fn arguments(&self) -> &'static [Argument] {
|
|
&[Argument {
|
|
name: "user",
|
|
is_optional: false,
|
|
is_greedy: false,
|
|
}]
|
|
}
|
|
|
|
async fn execute(
|
|
&self,
|
|
ctx: CommandContext<'_, State, Extras>,
|
|
args: &str,
|
|
) -> Result<(), CommandError> {
|
|
let Arg(mut user) = match Arg::<UserArgument>::parse(args, &ctx).await {
|
|
Ok(arg) => arg,
|
|
Err(e) => match e {
|
|
ArgumentParsingError::InvalidArgument => {
|
|
ctx.send().content("❌ User not found in DB.").await?;
|
|
return Ok(());
|
|
}
|
|
ArgumentParsingError::DatabaseFailed(e) => {
|
|
return Err(CommandError::DatabaseFailed(e));
|
|
}
|
|
e => {
|
|
unreachable!("Unexpected error: {e}");
|
|
}
|
|
},
|
|
};
|
|
|
|
user.is_moderator = !user.is_moderator;
|
|
|
|
user.insert(&ctx.rt.state.db).await?;
|
|
|
|
ctx.send()
|
|
.content(format!(
|
|
"👍 Done. <@{}> {}",
|
|
user.id,
|
|
if user.is_moderator {
|
|
"**is now** a moderator"
|
|
} else {
|
|
"**is no longer** a moderator"
|
|
}
|
|
))
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct IncidentsCommand;
|
|
|
|
#[async_trait]
|
|
impl Command<State, Extras> for IncidentsCommand {
|
|
fn name(&self) -> &'static str {
|
|
"incidents"
|
|
}
|
|
|
|
fn aliases(&self) -> &'static [&'static str] {
|
|
&["i"]
|
|
}
|
|
|
|
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
|
|
vec![Box::new(BotModeratorPermission)]
|
|
}
|
|
|
|
fn arguments(&self) -> &'static [Argument] {
|
|
&[Argument {
|
|
name: "user",
|
|
is_optional: false,
|
|
is_greedy: false,
|
|
}]
|
|
}
|
|
|
|
fn show_in_help(&self, _rt: &Runtime<State, Extras>, extras: &Extras) -> bool {
|
|
extras.user.is_moderator
|
|
}
|
|
|
|
async fn execute(
|
|
&self,
|
|
ctx: CommandContext<'_, State, Extras>,
|
|
args: &str,
|
|
) -> Result<(), CommandError> {
|
|
let user = match Arg::<UserArgument>::parse(args, &ctx).await {
|
|
Ok(Arg(user)) => user,
|
|
Err(e) => match e {
|
|
ArgumentParsingError::InvalidArgument => {
|
|
ctx.send().content("❌ User not found in DB.").await?;
|
|
return Ok(());
|
|
}
|
|
ArgumentParsingError::DatabaseFailed(e) => {
|
|
return Err(CommandError::DatabaseFailed(e));
|
|
}
|
|
e => {
|
|
unreachable!("Unexpected error: {e}");
|
|
}
|
|
},
|
|
};
|
|
|
|
let message = get_incident_page(user.id, &ctx.rt.state.db, 0).await?;
|
|
|
|
ctx.send_from_builder(message).await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct BanCommand;
|
|
|
|
#[async_trait]
|
|
impl Command<State, Extras> for BanCommand {
|
|
fn name(&self) -> &'static str {
|
|
"ban"
|
|
}
|
|
|
|
fn aliases(&self) -> &'static [&'static str] {
|
|
&["b"]
|
|
}
|
|
|
|
fn description(&self) -> &'static str {
|
|
"Bans a user from Payphone calls.\n\nThe user can be specified via either replying to them (works on call too), mentioning them, specifying their username, specifying their ID, or not setting it to default to yourself.\n\nThe duration can be specified as defined in the [`humantime::parse_duration` documentation](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html)."
|
|
}
|
|
|
|
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
|
|
vec![Box::new(BotModeratorPermission)]
|
|
}
|
|
|
|
fn show_in_help(&self, _rt: &Runtime<State, Extras>, extras: &Extras) -> bool {
|
|
extras.user.is_moderator
|
|
}
|
|
|
|
fn arguments(&self) -> &'static [Argument] {
|
|
&[
|
|
Argument {
|
|
name: "user",
|
|
is_optional: false,
|
|
is_greedy: false,
|
|
},
|
|
Argument {
|
|
name: "duration",
|
|
is_optional: true,
|
|
is_greedy: false,
|
|
},
|
|
Argument {
|
|
name: "reason",
|
|
is_optional: false,
|
|
is_greedy: true,
|
|
},
|
|
]
|
|
}
|
|
|
|
async fn execute(
|
|
&self,
|
|
ctx: CommandContext<'_, State, Extras>,
|
|
args: &str,
|
|
) -> Result<(), CommandError> {
|
|
let (user, duration, reason) = match <(
|
|
UserArgument,
|
|
Option<DurationArgument>,
|
|
NeedyGreedyString,
|
|
)>::parse(args, &ctx)
|
|
.await
|
|
{
|
|
Ok(arg) => arg,
|
|
Err(e) => match e {
|
|
ArgumentParsingError::InvalidArgument => {
|
|
ctx.send()
|
|
.content("❌ User not found in DB or no reason was provided.")
|
|
.await?;
|
|
return Ok(());
|
|
}
|
|
ArgumentParsingError::DatabaseFailed(e) => {
|
|
return Err(CommandError::DatabaseFailed(e));
|
|
}
|
|
ArgumentParsingError::MissingArgument => {
|
|
ctx.send().content(format!("❌ No reason was provided. Use `{}help ban` to learn how to use this command.", ctx.prefix)).await?;
|
|
return Ok(());
|
|
}
|
|
},
|
|
};
|
|
|
|
let incident = UserIncident {
|
|
created_at: UtcDateTime::now(),
|
|
user_id: user.id,
|
|
incident_id: get_random_u64(),
|
|
report_id: None,
|
|
r#type: UserIncidentType::Ban,
|
|
message: None,
|
|
created_by_id: Some(ctx.event.author.id.get()),
|
|
reason: (*reason).clone(),
|
|
expires_at: duration.map(|d| UtcDateTime::now() + *d),
|
|
duration: duration.map(|d| humantime::format_duration(*d).to_string()),
|
|
};
|
|
incident.insert(&ctx.rt.state.db).await?;
|
|
|
|
ctx.send()
|
|
.content(format!(
|
|
"✅ User `{}` has been banned from Payphone{}.",
|
|
user.name,
|
|
duration
|
|
.map(|d| format!(" for {}", humantime::format_duration(*d)))
|
|
.unwrap_or_default()
|
|
))
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|