Add support for extras, conditionally displaying commands in the help command, and start working on moderation commands

This commit is contained in:
2025-08-22 18:06:52 -03:00
parent 55ae1c8937
commit 8868b18be3
41 changed files with 1075 additions and 368 deletions
+9 -17
View File
@@ -1,24 +1,16 @@
use async_trait::async_trait;
use crate::{
constants::DEFAULT_PREFIX,
database::models::Server,
discord::sanitizing::SanitizeDiscordMarkdown,
framework::commands::{
Command, CommandError,
arguments::{Argument, GreedyString, ParseArguments},
context::CommandContext,
permissions::Permission,
},
groups::servers::permissions::{GuildOnlyPermission, ServerModeratorPermission},
state::State,
constants::DEFAULT_PREFIX, database::models::Server, discord::sanitizing::SanitizeDiscordMarkdown, extras::Extras, framework::commands::{
arguments::{Arg, Argument, GreedyString, ParseArguments}, context::CommandContext, permissions::Permission, Command, CommandError
}, groups::servers::permissions::{GuildOnlyPermission, ServerModeratorPermission}, state::State
};
#[derive(Debug)]
pub struct PrefixCommand;
#[async_trait]
impl Command<State> for PrefixCommand {
impl Command<State, Extras> for PrefixCommand {
fn name(&self) -> &'static str {
"prefix"
}
@@ -39,7 +31,7 @@ impl Command<State> for PrefixCommand {
}]
}
fn permissions(&self) -> Vec<Box<dyn Permission<State> + Send + Sync + 'static>> {
fn permissions(&self) -> Vec<Box<dyn Permission<State, Extras> + Send + Sync + 'static>> {
vec![
Box::new(GuildOnlyPermission),
Box::new(ServerModeratorPermission),
@@ -48,10 +40,10 @@ impl Command<State> for PrefixCommand {
async fn execute(
&self,
ctx: CommandContext<'_, State>,
ctx: CommandContext<'_, State, Extras>,
args: &str,
) -> Result<(), CommandError> {
let prefix: GreedyString = ParseArguments::parse(args).unwrap();
let Arg(prefix) = Arg::<GreedyString>::parse(args, &ctx).await.unwrap();
let mut prefix = prefix.trim();
let mut server = Server::get_or_create_by_id(
@@ -64,7 +56,7 @@ impl Command<State> for PrefixCommand {
)
.await
.inspect_err(|e| log::error!("Failed to get or create server: {e}"))
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
if prefix.is_empty() {
prefix = DEFAULT_PREFIX;
@@ -74,7 +66,7 @@ impl Command<State> for PrefixCommand {
server
.upsert(&ctx.rt.state.db)
.await
.map_err(|_| CommandError::DatabaseQueryFailed)?;
.map_err(|_| CommandError::DatabaseFailed)?;
ctx.send()
.content(format!(
+19 -6
View File
@@ -7,6 +7,7 @@ use twilight_model::gateway::payload::incoming::{GuildCreate, GuildDelete, Guild
use crate::{
database::models::Server,
extras::Extras,
framework::{
events::traits::{
EventResult, OnServerCreateHandler, OnServerDeleteHandler, OnServerUpdateHandler,
@@ -21,8 +22,12 @@ use crate::{
pub struct ServerCreateHandler;
#[async_trait]
impl OnServerCreateHandler<State> for ServerCreateHandler {
async fn on_server_create(&self, event: &GuildCreate, rt: Arc<Runtime<State>>) -> EventResult {
impl OnServerCreateHandler<State, Extras> for ServerCreateHandler {
async fn on_server_create(
&self,
event: &GuildCreate,
rt: Arc<Runtime<State, Extras>>,
) -> EventResult {
let server = match event {
GuildCreate::Available(guild) => Server::from(guild.clone()),
GuildCreate::Unavailable(_) => {
@@ -51,8 +56,12 @@ impl OnServerCreateHandler<State> for ServerCreateHandler {
pub struct ServerUpdateHandler;
#[async_trait]
impl OnServerUpdateHandler<State> for ServerUpdateHandler {
async fn on_server_update(&self, event: &GuildUpdate, rt: Arc<Runtime<State>>) -> EventResult {
impl OnServerUpdateHandler<State, Extras> for ServerUpdateHandler {
async fn on_server_update(
&self,
event: &GuildUpdate,
rt: Arc<Runtime<State, Extras>>,
) -> EventResult {
let mut server = match Server::get_by_id(event.0.id.get(), &rt.state.db)
.await
.inspect_err(|e| {
@@ -83,8 +92,12 @@ impl OnServerUpdateHandler<State> for ServerUpdateHandler {
pub struct ServerDeleteHandler;
#[async_trait]
impl OnServerDeleteHandler<State> for ServerDeleteHandler {
async fn on_server_delete(&self, event: &GuildDelete, rt: Arc<Runtime<State>>) -> EventResult {
impl OnServerDeleteHandler<State, Extras> for ServerDeleteHandler {
async fn on_server_delete(
&self,
event: &GuildDelete,
rt: Arc<Runtime<State, Extras>>,
) -> EventResult {
Server::delete_by_id(event.id.get(), &rt.state.db)
.await
.inspect(|_| log::debug!("Deleted server with ID {} because Payphone left", event.id))
+16 -7
View File
@@ -6,6 +6,7 @@ use twilight_model::{
use twilight_util::permission_calculator::PermissionCalculator;
use crate::{
extras::Extras,
framework::commands::{CommandError, context::CommandContext, permissions::Permission},
state::State,
};
@@ -14,12 +15,15 @@ use crate::{
pub struct GuildOnlyPermission;
#[async_trait]
impl Permission<State> for GuildOnlyPermission {
async fn check(&self, ctx: &CommandContext<State>) -> Result<bool, CommandError> {
impl Permission<State, Extras> for GuildOnlyPermission {
async fn check(&self, ctx: &CommandContext<State, Extras>) -> Result<bool, CommandError> {
Ok(ctx.event.guild_id.is_some())
}
async fn on_handle_denied(&self, ctx: &CommandContext<State>) -> Result<(), CommandError> {
async fn on_handle_denied(
&self,
ctx: &CommandContext<State, Extras>,
) -> Result<(), CommandError> {
ctx.send()
.content("❌ This command can only be used in a server.")
.await?;
@@ -32,8 +36,8 @@ impl Permission<State> for GuildOnlyPermission {
pub struct ServerModeratorPermission;
#[async_trait]
impl Permission<State> for ServerModeratorPermission {
async fn check(&self, ctx: &CommandContext<State>) -> Result<bool, CommandError> {
impl Permission<State, Extras> for ServerModeratorPermission {
async fn check(&self, ctx: &CommandContext<State, Extras>) -> Result<bool, CommandError> {
if let Some(guild_id) = ctx.event.guild_id {
let Some(guild) = ctx.rt.cache.guild(guild_id) else {
log::warn!("Could not get guild for ID {guild_id}!");
@@ -76,14 +80,19 @@ impl Permission<State> for ServerModeratorPermission {
)
.owner_id(guild.owner_id());
return Ok(calculator.root().contains(Permissions::MANAGE_CHANNELS | Permissions::MANAGE_MESSAGES));
return Ok(calculator
.root()
.contains(Permissions::MANAGE_CHANNELS | Permissions::MANAGE_MESSAGES));
}
// DMs channel, the user has all permissions.
Ok(true)
}
async fn on_handle_denied(&self, ctx: &CommandContext<State>) -> Result<(), CommandError> {
async fn on_handle_denied(
&self,
ctx: &CommandContext<State, Extras>,
) -> Result<(), CommandError> {
ctx.send()
.content("❌ This command can only be run by a server moderator.\n-# 🛈 Moderators must have permission to manage messages and to manage channels.")
.await?;