Finish refactoring

This commit is contained in:
2025-08-10 21:15:53 -03:00
parent f123282699
commit a004fe2308
50 changed files with 2639 additions and 587 deletions
-221
View File
@@ -1,221 +0,0 @@
use std::sync::Arc;
use async_trait::async_trait;
use twilight_model::channel::message::Embed;
use twilight_util::builder::embed::{
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource,
};
use crate::{
commands::{COMMANDS, PREFIX, get_command_by_name},
constants::{BOT_AVATAR, BOT_NAME, COLOR_RED, COLOR_WHITE},
framework::{
commands::{
Command, CommandError,
arguments::{Argument, NeedyString, ParseArguments},
context::CommandContext,
},
interactions::{InteractionError, components::Button, context::InteractionContext},
utils::EscapeDiscordMarkdown,
},
utils::ToTitleCase,
};
pub struct HelpCommand;
#[async_trait]
impl Command for HelpCommand {
fn name(&self) -> &'static str {
"help"
}
fn arguments(&self) -> &'static [Argument] {
&[Argument {
name: "command",
is_optional: true,
is_greedy: false,
}]
}
fn description(&self) -> &'static str {
"Lists all commands and shows information about them individually."
}
async fn execute(&self, ctx: &CommandContext, args: &str) -> Result<(), CommandError> {
let command_name: Option<NeedyString> = ParseArguments::parse(args).unwrap();
if let Some(command_name) = command_name {
if let Some(command) = get_command_by_name(&command_name) {
ctx.send()
.embed(get_command_help_embed(&command))
.component(HelpGoBackButton, 1)
.component(HelpSupportLinkButton, 1)
.await
.inspect_err(|e| log::error!("Failed to send message! {e}"))
.map_err(|_| CommandError::MessageSendingFailed)?;
} else {
ctx.send()
.embed(get_command_not_found_embed(command_name.0))
.component(HelpGoBackButton, 1)
.component(HelpSupportLinkButton, 1)
.await
.map_err(|_| CommandError::MessageSendingFailed)?;
}
} else {
ctx.send()
.embed(get_all_command_help_embed())
.component(HelpWebsiteLinkButton, 1)
.component(HelpSupportLinkButton, 1)
.component(HelpDonateLinkButton, 1)
.await
.map_err(|_| CommandError::MessageSendingFailed)?;
}
Ok(())
}
}
fn get_command_help_embed(command: &Arc<dyn Command>) -> Embed {
let name = format!("{PREFIX}{}", command.name());
let arguments = command
.arguments()
.iter()
.map(|arg| arg.to_string())
.collect::<Vec<_>>()
.join(" ");
let usage = if !arguments.is_empty() {
format!("{name} {arguments}")
} else {
name
};
EmbedBuilder::new()
.author(
EmbedAuthorBuilder::new(BOT_NAME)
.icon_url(ImageSource::url(BOT_AVATAR).unwrap())
.build(),
)
.thumbnail(ImageSource::url(BOT_AVATAR).unwrap())
.color(COLOR_WHITE)
.title(format!("{} Command", command.name().to_title_case()))
.description(command.description())
.field(EmbedFieldBuilder::new("Usage", format!("`{usage}`")).build())
.field(EmbedFieldBuilder::new(
"Aliases",
if !command.aliases().is_empty() {
command
.aliases()
.iter()
.map(|alias| format!("`{PREFIX}{alias}`"))
.collect::<Vec<_>>()
.join(", ")
} else {
String::from("*This command doesn't have aliases.*")
},
))
.footer(EmbedFooterBuilder::new(format!(
"Use {PREFIX}help <command> to get help about a specific command."
)))
.build()
}
fn get_all_command_help_embed() -> Embed {
EmbedBuilder::new()
.author(EmbedAuthorBuilder::new(BOT_NAME).icon_url(ImageSource::url(BOT_AVATAR).unwrap()))
.title("Pick up the phone and start making friends!")
.description(format!("Here is the list of all available commands. You can see more details about a specific command with `{PREFIX}help <command>`."))
.thumbnail(ImageSource::url(BOT_AVATAR).unwrap())
.color(COLOR_WHITE)
.field(EmbedFieldBuilder::new("Commands", get_commands_table()).build())
.footer(EmbedFooterBuilder::new(format!("v{} - Use {PREFIX}help <command> to get more information about a specific command.", env!("CARGO_PKG_VERSION"))))
.build()
}
fn get_commands_table() -> String {
let mut table = String::new();
for command in COMMANDS.iter() {
table.push_str(&format!("{:<12}", command.name()));
}
format!("```\n{table}\n```")
}
fn get_command_not_found_embed(name: String) -> Embed {
EmbedBuilder::new()
.color(COLOR_RED)
.author(EmbedAuthorBuilder::new(BOT_NAME).icon_url(ImageSource::url(BOT_AVATAR).unwrap()))
.title("That's not a command")
.description(format!("`{}` is not a valid command name (nor alias). If you put it with the prefix on, take the prefix off before trying again. For example, if you did `p.help p.call`, do `p.help call` instead.\n\nIf you still can't figure it out, press the button below to go back to the full list. Everything's there!", name.escape()))
.thumbnail(ImageSource::url(BOT_AVATAR).unwrap())
.build()
}
pub struct HelpGoBackButton;
#[async_trait]
impl Button for HelpGoBackButton {
fn label(&self) -> Option<&'static str> {
Some("← Go back to commands list")
}
async fn on_press(&self, ctx: InteractionContext) -> Result<(), InteractionError> {
ctx
.original_message()
.expect(
"Buttons always have the original message in the interaction context, this one didn't (got None)"
)
.edit()
.embed(get_all_command_help_embed())
.component(HelpWebsiteLinkButton, 1)
.component(HelpSupportLinkButton, 1)
.component(HelpDonateLinkButton, 1)
.await
.inspect_err(|e| log::error!("An error occurred while attempting to edit the original message of an interaction. {e:?}"))
.map_err(
|_| InteractionError::OriginalMessageEditingFailed
)?;
Ok(())
}
}
pub struct HelpSupportLinkButton;
#[async_trait]
impl Button for HelpSupportLinkButton {
fn label(&self) -> Option<&'static str> {
Some("Get help")
}
fn url(&self) -> Option<&'static str> {
Some("https://discord.gg/9daSsrfTmE")
}
}
pub struct HelpWebsiteLinkButton;
#[async_trait]
impl Button for HelpWebsiteLinkButton {
fn label(&self) -> Option<&'static str> {
Some("Website")
}
fn url(&self) -> Option<&'static str> {
Some("https://payphone.lat/")
}
}
pub struct HelpDonateLinkButton;
#[async_trait]
impl Button for HelpDonateLinkButton {
fn label(&self) -> Option<&'static str> {
Some("Donate")
}
fn url(&self) -> Option<&'static str> {
Some("https://ko-fi.com/payphonedev")
}
}