Add ban command

This commit is contained in:
2025-08-25 19:36:18 -03:00
parent 6d8217ed81
commit e44ab51adf
7 changed files with 189 additions and 27 deletions
+83 -14
View File
@@ -1,23 +1,25 @@
use async_trait::async_trait;
use time::UtcDateTime;
use crate::{
database::models::{Call, Message, User},
database::models::{UserIncident, UserIncidentType},
extras::Extras,
framework::{
commands::{
Command, CommandError,
arguments::{Arg, Argument, ArgumentParsingError, ParseArguments},
arguments::{Arg, Argument, ArgumentParsingError, NeedyGreedyString, ParseArguments},
context::CommandContext,
permissions::Permission,
},
runtime::Runtime,
},
groups::moderation::{
arguments::UserArgument,
arguments::{DurationArgument, UserArgument},
get_incident_page,
permissions::{BotModeratorPermission, BotOwnerPermission},
},
state::State,
utils::get_random_u64,
};
#[derive(Debug)]
@@ -149,34 +151,101 @@ impl Command<State, Extras> for IncidentsCommand {
}
#[derive(Debug)]
pub struct StatsCommand;
pub struct BanCommand;
#[async_trait]
impl Command<State, Extras> for StatsCommand {
impl Command<State, Extras> for BanCommand {
fn name(&self) -> &'static str {
"stats"
"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(BotOwnerPermission)]
vec![Box::new(BotModeratorPermission)]
}
fn show_in_help(&self, rt: &Runtime<State, Extras>, extras: &Extras) -> bool {
extras.user.id == rt.state.bot_owner_id
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,
args: &str,
) -> Result<(), CommandError> {
let users = User::count(&ctx.rt.state.db).await?;
let calls = Call::count(&ctx.rt.state.db).await?;
let messages = Message::count(&ctx.rt.state.db).await?;
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!(
"- Users: {users}\n- Calls: {calls}\n- Messages: {messages}"
"✅ User `{}` has been banned from Payphone{}.",
user.name,
duration
.map(|d| format!(" for {}", humantime::format_duration(*d)))
.unwrap_or_default()
))
.await?;