Finish refactoring
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
framework::commands::{
|
||||
Command, CommandError,
|
||||
arguments::{Argument, NeedyString, ParseArguments},
|
||||
context::CommandContext,
|
||||
},
|
||||
groups::about::{
|
||||
components::{
|
||||
HelpDonateLinkButton, HelpGoBackButton, HelpSupportLinkButton, HelpWebsiteLinkButton,
|
||||
},
|
||||
embeds::{get_all_command_help_embed, get_command_help_embed, get_command_not_found_embed},
|
||||
},
|
||||
state::State,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HelpCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl Command<State> 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<'_, State>,
|
||||
args: &str,
|
||||
) -> Result<(), CommandError> {
|
||||
let command_name: Option<NeedyString> = ParseArguments::parse(args).unwrap();
|
||||
|
||||
if let Some(command_name) = command_name {
|
||||
if let Some(command) = ctx.rt.get_command(&command_name) {
|
||||
ctx.send()
|
||||
.embed(get_command_help_embed(&command, &ctx))
|
||||
.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(ctx.rt, command_name.0))
|
||||
.component(HelpGoBackButton, 1)
|
||||
.component(HelpSupportLinkButton, 1)
|
||||
.await
|
||||
.map_err(|_| CommandError::MessageSendingFailed)?;
|
||||
}
|
||||
} else {
|
||||
ctx.send()
|
||||
.embed(get_all_command_help_embed(ctx.prefix, ctx.rt))
|
||||
.component(HelpWebsiteLinkButton, 1)
|
||||
.component(HelpSupportLinkButton, 1)
|
||||
.component(HelpDonateLinkButton, 1)
|
||||
.await
|
||||
.map_err(|_| CommandError::MessageSendingFailed)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
framework::interactions::{InteractionError, components::Button, context::InteractionContext},
|
||||
groups::about::embeds::get_all_command_help_embed,
|
||||
state::State,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HelpGoBackButton;
|
||||
|
||||
#[async_trait]
|
||||
impl Button<State> for HelpGoBackButton {
|
||||
fn label(&self) -> Option<&'static str> {
|
||||
Some("← Go back to commands list")
|
||||
}
|
||||
|
||||
async fn on_press(&self, ctx: InteractionContext<State>) -> 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(&ctx.prefix, ctx.rt))
|
||||
.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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HelpSupportLinkButton;
|
||||
|
||||
#[async_trait]
|
||||
impl Button<State> for HelpSupportLinkButton {
|
||||
fn label(&self) -> Option<&'static str> {
|
||||
Some("Get help")
|
||||
}
|
||||
|
||||
fn url(&self) -> Option<&'static str> {
|
||||
Some("https://discord.gg/9daSsrfTmE")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HelpWebsiteLinkButton;
|
||||
|
||||
#[async_trait]
|
||||
impl Button<State> for HelpWebsiteLinkButton {
|
||||
fn label(&self) -> Option<&'static str> {
|
||||
Some("Website")
|
||||
}
|
||||
|
||||
fn url(&self) -> Option<&'static str> {
|
||||
Some("https://payphone.lat/")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HelpDonateLinkButton;
|
||||
|
||||
#[async_trait]
|
||||
impl Button<State> for HelpDonateLinkButton {
|
||||
fn label(&self) -> Option<&'static str> {
|
||||
Some("Donate")
|
||||
}
|
||||
|
||||
fn url(&self) -> Option<&'static str> {
|
||||
Some("https://ko-fi.com/payphonedev")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use twilight_model::channel::message::Embed;
|
||||
use twilight_util::builder::embed::{
|
||||
EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder, EmbedFooterBuilder, ImageSource,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
constants::{COLOR_RED, COLOR_WHITE},
|
||||
discord::{images, sanitizing::SanitizeDiscordMarkdown},
|
||||
framework::{
|
||||
commands::{Command, context::CommandContext},
|
||||
runtime::Runtime,
|
||||
},
|
||||
state::State,
|
||||
utils::ToTitleCase,
|
||||
};
|
||||
|
||||
pub(super) fn get_command_help_embed(
|
||||
command: &Arc<dyn Command<State>>,
|
||||
ctx: &CommandContext<'_, State>,
|
||||
) -> Embed {
|
||||
let name = format!("{}{}", ctx.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(ctx.rt.app.name.clone())
|
||||
.icon_url(
|
||||
ImageSource::url(images::get_avatar_url(
|
||||
ctx.rt.app.bot.as_ref().unwrap().avatar,
|
||||
ctx.rt.app.bot.as_ref().unwrap().id.get(),
|
||||
ctx.rt.app.bot.as_ref().unwrap().discriminator as u64,
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.thumbnail(
|
||||
ImageSource::url(images::get_avatar_url(
|
||||
ctx.rt.app.bot.as_ref().unwrap().avatar,
|
||||
ctx.rt.app.bot.as_ref().unwrap().id.get(),
|
||||
ctx.rt.app.bot.as_ref().unwrap().discriminator as u64,
|
||||
))
|
||||
.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!("`{}{alias}`", ctx.prefix))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
} else {
|
||||
String::from("*This command doesn't have aliases.*")
|
||||
},
|
||||
))
|
||||
.footer(EmbedFooterBuilder::new(format!(
|
||||
"Use {}help <command> to get help about a specific command.",
|
||||
ctx.prefix
|
||||
)))
|
||||
.build()
|
||||
}
|
||||
|
||||
pub(super) fn get_all_command_help_embed<T>(prefix: &str, rt: Arc<Runtime<T>>) -> Embed {
|
||||
EmbedBuilder::new()
|
||||
.author(EmbedAuthorBuilder::new(rt.app.name.clone()).icon_url(ImageSource::url(images::get_avatar_url(
|
||||
rt.app.bot.as_ref().unwrap().avatar,
|
||||
rt.app.bot.as_ref().unwrap().id.get(),
|
||||
rt.app.bot.as_ref().unwrap().discriminator as u64,
|
||||
))
|
||||
.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(images::get_avatar_url(
|
||||
rt.app.bot.as_ref().unwrap().avatar,
|
||||
rt.app.bot.as_ref().unwrap().id.get(),
|
||||
rt.app.bot.as_ref().unwrap().discriminator as u64,
|
||||
))
|
||||
.unwrap())
|
||||
.color(COLOR_WHITE)
|
||||
.field(EmbedFieldBuilder::new("Commands", get_commands_table(rt)).build())
|
||||
.footer(EmbedFooterBuilder::new(format!("v{} - Use {}help <command> to get more information about a specific command.", env!("CARGO_PKG_VERSION"), prefix)))
|
||||
.build()
|
||||
}
|
||||
|
||||
pub(super) fn get_commands_table<T>(rt: Arc<Runtime<T>>) -> String {
|
||||
let mut table = String::new();
|
||||
|
||||
for command in rt.commands.iter() {
|
||||
table.push_str(&format!("{:<12}", command.name()));
|
||||
}
|
||||
|
||||
format!("```\n{table}\n```")
|
||||
}
|
||||
|
||||
pub(super) fn get_command_not_found_embed<T>(rt: Arc<Runtime<T>>, name: String) -> Embed {
|
||||
EmbedBuilder::new()
|
||||
.color(COLOR_RED)
|
||||
.author(EmbedAuthorBuilder::new(rt.app.name.clone()).icon_url(ImageSource::url(images::get_avatar_url(
|
||||
rt.app.bot.as_ref().unwrap().avatar,
|
||||
rt.app.bot.as_ref().unwrap().id.get(),
|
||||
rt.app.bot.as_ref().unwrap().discriminator as u64,
|
||||
))
|
||||
.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.sanitize(true)))
|
||||
.thumbnail(ImageSource::url(images::get_avatar_url(
|
||||
rt.app.bot.as_ref().unwrap().avatar,
|
||||
rt.app.bot.as_ref().unwrap().id.get(),
|
||||
rt.app.bot.as_ref().unwrap().discriminator as u64,
|
||||
))
|
||||
.unwrap())
|
||||
.build()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod commands;
|
||||
pub mod components;
|
||||
pub mod embeds;
|
||||
Reference in New Issue
Block a user