From 031fc92e02c314cce9fc8febcc7900fa2d885941 Mon Sep 17 00:00:00 2001 From: Lakelezz <12222135+Lakelezz@users.noreply.github.com> Date: Thu, 30 Nov 2017 16:55:58 +0100 Subject: Make help-commands customisable (#227) --- src/framework/standard/command.rs | 118 +++++++++++++- src/framework/standard/create_command.rs | 8 +- src/framework/standard/create_group.rs | 9 +- src/framework/standard/create_help_command.rs | 213 ++++++++++++++++++++++++++ src/framework/standard/help_commands.rs | 125 +++++++++------ src/framework/standard/mod.rs | 70 +++++++-- 6 files changed, 467 insertions(+), 76 deletions(-) create mode 100644 src/framework/standard/create_help_command.rs (limited to 'src/framework') diff --git a/src/framework/standard/command.rs b/src/framework/standard/command.rs index 7697b3b..ec18766 100644 --- a/src/framework/standard/command.rs +++ b/src/framework/standard/command.rs @@ -4,17 +4,18 @@ use std::collections::HashMap; use std::fmt; use std::fmt::{Debug, Formatter}; use std::sync::Arc; -use super::{Args, Configuration}; +use utils::Colour; +use super::{Args, Configuration, HelpBehaviour}; pub type Check = Fn(&mut Context, &Message, &mut Args, &CommandOptions) -> bool + Send + Sync + 'static; -pub type HelpFunction = fn(&mut Context, &Message, HashMap>, Args) +pub type HelpFunction = fn(&mut Context, &Message, &HelpOptions, HashMap>, Args) -> Result<(), Error>; -pub struct Help(pub HelpFunction); +pub struct Help(pub HelpFunction, pub Arc); impl Debug for Help { fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -22,6 +23,12 @@ impl Debug for Help { } } +impl HelpCommand for Help { + fn execute(&self, c: &mut Context, m: &Message, ho: &HelpOptions,hm: HashMap>, a: Args) -> Result<(), Error> { + (self.0)(c, m, ho, hm, a) + } +} + pub type BeforeHook = Fn(&mut Context, &Message, &str) -> bool + Send + Sync + 'static; pub type AfterHook = Fn(&mut Context, &Message, &str, Result<(), Error>) + Send + Sync + 'static; pub(crate) type InternalCommand = Arc; @@ -116,6 +123,101 @@ pub struct CommandOptions { pub aliases: Vec, } +#[derive(Debug)] +pub struct HelpOptions { + /// Suggests a command's name. + pub suggestion_text: String, + /// If no help is available, this text will be displayed. + pub no_help_available_text: String, + /// How to use a command, `{usage_label}: {command_name} {args}` + pub usage_label: String, + /// Actual sample label, `{usage_sample_label}: {command_name} {args}` + pub usage_sample_label: String, + /// Text labeling ungrouped commands, `{ungrouped_label}: ...` + pub ungrouped_label: String, + /// Text labeling the start of the description. + pub description_label: String, + /// Text labeling grouped commands, `{grouped_label} {group_name}: ...` + pub grouped_label: String, + /// Text labeling a command's alternative names (aliases). + pub aliases_label: String, + /// Text specifying that a command is only usable in a guild. + pub guild_only_text: String, + /// Text specifying that a command is only usable in via DM. + pub dm_only_text: String, + /// Text specifying that a command can be used via DM and in guilds. + pub dm_and_guild_text: String, + /// Text expressing that a command is available. + pub available_text: String, + /// Error-message once a command could not be found. + /// Output-example (without whitespace between both substitutions: `{command_not_found_text}{command_name}` + /// `{command_name}` describes user's input as in: `{prefix}help {command_name}`. + pub command_not_found_text: String, + /// Explains the user on how to use access a single command's details. + pub individual_command_tip: String, + /// Explains reasoning behind striked commands, see fields requiring `HelpBehaviour` for further information. + /// If `HelpBehaviour::Strike` is unused, this field will evaluate to `None` during creation + /// inside of `CreateHelpCommand`. + pub striked_commands_tip: Option, + /// Announcing a group's prefix as in: {group_prefix} {prefix}. + pub group_prefix: String, + /// If a user lacks required roles, this will treat how these commands will be displayed. + pub lacking_role: HelpBehaviour, + /// If a user lacks permissions, this will treat how these commands will be displayed. + pub lacking_permissions: HelpBehaviour, + /// If a user is using the help-command in a channel where a command is not available, + /// this behaviour will be executed. + pub wrong_channel: HelpBehaviour, + /// Colour help-embed will use upon encountering an error. + pub embed_error_colour: Colour, + /// Colour help-embed will use if no error occured. + pub embed_success_colour: Colour, +} + +pub trait HelpCommand: Send + Sync + 'static { + fn execute(&self, &mut Context, &Message, &HelpOptions, HashMap>, Args) -> Result<(), Error>; + + fn options(&self) -> Arc { + Arc::clone(&DEFAULT_OPTIONS) + } +} + +impl HelpCommand for Arc { + fn execute(&self, c: &mut Context, m: &Message, ho: &HelpOptions, hm: HashMap>, a: Args) -> Result<(), Error> { + (**self).execute(c, m, ho, hm, a) + } +} + +impl Default for HelpOptions { + fn default() -> HelpOptions { + HelpOptions { + suggestion_text: "Did you mean {}?".to_string(), + no_help_available_text: "**Error**: No help available.".to_string(), + usage_label: "Usage".to_string(), + usage_sample_label: "Sample usage".to_string(), + ungrouped_label: "Ungrouped".to_string(), + grouped_label: "Group".to_string(), + aliases_label: "Aliases".to_string(), + description_label: "Description".to_string(), + guild_only_text: "Only in guilds".to_string(), + dm_only_text: "Only in DM".to_string(), + dm_and_guild_text: "In DM and guilds".to_string(), + available_text: "Available".to_string(), + command_not_found_text: "**Error**: Command `{}` not found.".to_string(), + individual_command_tip: "To get help with an individual command, pass its \ + name as an argument to this command.".to_string(), + group_prefix: "Prefix".to_string(), + striked_commands_tip: Some(String::new()), + lacking_role: HelpBehaviour::Strike, + lacking_permissions: HelpBehaviour::Strike, + wrong_channel: HelpBehaviour::Strike, + embed_error_colour: Colour::dark_red(), + embed_success_colour: Colour::rosewater(), + } + } +} + + lazy_static! { static ref DEFAULT_OPTIONS: Arc = Arc::new(CommandOptions::default()); } @@ -151,7 +253,7 @@ impl Command for Arc { (**self).init() } - fn before(&self, c: &mut Context, m: &Message) -> bool { + fn before(&self, c: &mut Context, m: &Message) -> bool { (**self).before(c, m) } @@ -173,7 +275,7 @@ impl Command for Box { (**self).init() } - fn before(&self, c: &mut Context, m: &Message) -> bool { + fn before(&self, c: &mut Context, m: &Message) -> bool { (**self).before(c, m) } @@ -182,9 +284,9 @@ impl Command for Box { } } -impl Command for F where F: Fn(&mut Context, &Message, Args) -> Result<(), Error> - + Send - + Sync +impl Command for F where F: Fn(&mut Context, &Message, Args) -> Result<(), Error> + + Send + + Sync + ?Sized + 'static { fn execute(&self, c: &mut Context, m: &Message, a: Args) -> Result<(), Error> { diff --git a/src/framework/standard/create_command.rs b/src/framework/standard/create_command.rs index 44e9988..0df649d 100644 --- a/src/framework/standard/create_command.rs +++ b/src/framework/standard/create_command.rs @@ -114,22 +114,22 @@ impl CreateCommand { self } - + /// Like [`exec`] but accepts a `Command` directly. /// /// [`exec`]: #method.exec pub fn cmd(mut self, c: C) -> Self { self.1 = FnOrCommand::Command(Arc::new(c)); - + self } - + /// Like [`cmd`] but says to the builder to use this command's options instead of its own. /// /// [`cmd`]: #method.cmd pub fn cmd_with_options(mut self, c: C) -> Self { self.1 = FnOrCommand::CommandWithOptions(Arc::new(c)); - + self } diff --git a/src/framework/standard/create_group.rs b/src/framework/standard/create_group.rs index 25279ca..77fb348 100644 --- a/src/framework/standard/create_group.rs +++ b/src/framework/standard/create_group.rs @@ -1,6 +1,6 @@ pub use super::command::{Command, CommandGroup, CommandOptions, Error as CommandError}; pub(crate) use super::command::CommandOrAlias; -pub(crate) use super::command::{Help, HelpFunction}; +pub use super::create_help_command::{CreateHelpCommand}; pub use super::create_command::{CreateCommand, FnOrCommand}; pub use super::Args; @@ -90,13 +90,6 @@ impl CreateGroup { self } - /// Sets what code should be execute when a user requests for `(prefix)help`. - pub fn help(mut self, f: HelpFunction) -> Self { - self.0.help = Some(Arc::new(Help(f))); - - self - } - /// If prefix is set, it will be required before all command names. /// For example, if bot prefix is "~" and group prefix is "image" /// we'd call a subcommand named "hibiki" by sending "~image hibiki". diff --git a/src/framework/standard/create_help_command.rs b/src/framework/standard/create_help_command.rs new file mode 100644 index 0000000..08cc6b4 --- /dev/null +++ b/src/framework/standard/create_help_command.rs @@ -0,0 +1,213 @@ +use super::command::{Help, HelpOptions, HelpFunction}; +pub use super::{Args, CommandGroup, CommandOptions, CommandError, HelpBehaviour}; + +use utils::Colour; +use std::fmt::Write; +use std::sync::Arc; + +pub struct CreateHelpCommand(pub HelpOptions, pub HelpFunction); + +impl CreateHelpCommand { + + /// Sets a message displaying if input could not be found + /// but a similar command is available. + /// + /// **Note**: `{}` will be substituted with the actual suggested command-name. + /// Hence no `{}` results in no command-name. + pub fn suggestion_text(mut self, text: &str) -> Self { + self.0.suggestion_text = text.to_string(); + + self + } + + /// Sets a message displaying if there is no help available. + pub fn no_help_available_text(mut self, text: &str) -> Self { + self.0.no_help_available_text = text.to_string(); + + self + } + + /// Sets a label for usage of a command. + pub fn usage_label(mut self, text: &str) -> Self { + self.0.usage_label = text.to_string(); + + self + } + + /// Sets a label for the usage examples of a command. + pub fn usage_sample_label(mut self, text: &str) -> Self { + self.0.usage_sample_label = text.to_string(); + + self + } + + /// Sets a label for ungrouped-commands + pub fn ungrouped_label(mut self, text: &str) -> Self { + self.0.ungrouped_label = text.to_string(); + + self + } + + /// Sets a label for grouped-commands. + pub fn grouped_label(mut self, text: &str) -> Self { + self.0.grouped_label = text.to_string(); + + self + } + + /// Sets a label for aliases. + pub fn aliases_label(mut self, text: &str) -> Self { + self.0.aliases_label = text.to_string(); + + self + } + + /// Sets a message displaying if a command is only available + /// in guilds. + pub fn guild_only_text(mut self, text: &str) -> Self { + self.0.guild_only_text = text.to_string(); + + self + } + + /// Sets a message displaying if a command is only available + /// in direct messages (DMs); + pub fn dm_only_text(mut self, text: &str) -> Self { + self.0.dm_only_text = text.to_string(); + + self + } + + /// Sets a message displaying if a command is available in + /// guilds and DMs. + pub fn dm_and_guilds_text(mut self, text: &str) -> Self { + self.0.dm_and_guild_text = text.to_string(); + + self + } + + /// Sets a message displaying if a command is available to use. + pub fn available_text(mut self, text: &str) -> Self { + self.0.available_text = text.to_string(); + + self + } + + /// Sets a message that will appear upon failing to find + /// an individual command. + /// As in: `{prefix}help {command_name}`, but a command or + /// alias like `{command_name}` does not exist. + /// + /// **Note**: `{}` will be substituted with the actual suggested command-name. + /// Hence no `{}` results in no command-name. + pub fn command_not_found_text(mut self, text: &str) -> Self { + self.0.command_not_found_text = text.to_string(); + + self + } + + /// Sets the message on top of the help-menu, informing the + /// user how to obtain more information about a single command. + pub fn individual_command_tip(mut self, text: &str) -> Self { + self.0.individual_command_tip = text.to_string(); + + self + } + + /// Sets how the group-prexix shall be labeled. + pub fn group_prefix(mut self, text: &str) -> Self { + self.0.group_prefix = text.to_string(); + + self + } + + /// Sets how a command requiring roles, that a user is lacking, + /// shall appear in the help-menu. + pub fn lacking_role(mut self, behaviour: HelpBehaviour) -> Self { + self.0.lacking_role = behaviour; + + self + } + + /// Sets how a command requiring permission, that a user is lacking, + /// shall be appear in the help-menu. + pub fn lacking_permissions(mut self, behaviour: HelpBehaviour) -> Self { + self.0.lacking_permissions = behaviour; + + self + } + + /// Sets how a command requiring to be sent in either via DM + /// or a guild should be treated in the help-menu. + pub fn wrong_channel(mut self, behaviour: HelpBehaviour) -> Self { + self.0.wrong_channel = behaviour; + + self + } + + /// Sets the tip (or legend) explaining why some commands are striked. + /// By default this is `Some(String)` and the `String` is empty resulting + /// in an automated substitution based on your `HelpBehaviour`-settings. + /// If set to `None`, no tip will be given nor will it be substituted. + /// If set to a non-empty `Some(String)`, the `String` will be displayed as tip. + pub fn striked_commands_tip(mut self, text: Option) -> Self { + self.0.striked_commands_tip = text; + + self + } + + /// Sets the colour for the embed if an error occured. + pub fn embed_success_colour(mut self, colour: Colour) -> Self { + self.0.embed_success_colour = colour; + + self + } + + /// Sets the colour for the embed if no error occured. + pub fn embed_error_colour(mut self, colour: Colour) -> Self { + self.0.embed_error_colour = colour; + + self + } + + /// Finishes the creation of a help-command, returning `Help`. + /// If `Some(String)` was set as `striked_commands_tip` and the `String` is empty, + /// the creator will substitute content based on the `HelpBehaviour`-settings. + pub(crate) fn finish(self) -> Arc { + if self.0.striked_commands_tip == Some(String::new()) { + let mut strike_text = String::from("~~`Striked commands`~~ are unavailable because they"); + let mut concat_with_comma = false; + + if self.0.lacking_permissions == HelpBehaviour::Strike { + let _ = write!(strike_text, "require permissions"); + concat_with_comma = true; + } + + if self.0.lacking_role == HelpBehaviour::Strike { + + if concat_with_comma { + let _ = write!(strike_text, ", require a specific role"); + } else { + let _ = write!(strike_text, " require a specific role"); + concat_with_comma = true; + } + } + + if self.0.wrong_channel == HelpBehaviour::Strike { + + if concat_with_comma { + let _ = write!(strike_text, " and are limited to DM/guilds"); + } else { + let _ = write!(strike_text, " are limited to DM/guilds"); + } + } + + let _ = write!(strike_text, "."); + let CreateHelpCommand(options, function) = self.striked_commands_tip(Some(strike_text)); + return Arc::new(Help(function, Arc::new(options))) + } + let CreateHelpCommand(options, function) = self; + + Arc::new(Help(function, Arc::new(options))) + } +} \ No newline at end of file diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs index 8111cea..b2fc9f5 100644 --- a/src/framework/standard/help_commands.rs +++ b/src/framework/standard/help_commands.rs @@ -29,13 +29,13 @@ use model::{ChannelId, Message}; use std::collections::HashMap; use std::sync::Arc; use std::fmt::Write; -use super::command::InternalCommand; -use super::{Args, CommandGroup, CommandOrAlias, CommandOptions, CommandError}; +use super::command::{InternalCommand}; +use super::{Args, CommandGroup, CommandOrAlias, HelpOptions, CommandOptions, CommandError, HelpBehaviour}; use utils::Colour; -fn error_embed(channel_id: &ChannelId, input: &str) { +fn error_embed(channel_id: &ChannelId, input: &str, colour: Colour) { let _ = channel_id.send_message(|m| { - m.embed(|e| e.colour(Colour::dark_red()).description(input)) + m.embed(|e| e.colour(colour).description(input)) }); } @@ -92,6 +92,7 @@ pub fn has_all_requirements(cmd: &Arc, msg: &Message) -> bool { /// ``` pub fn with_embeds(_: &mut Context, msg: &Message, + help_options: &HelpOptions, groups: HashMap>, args: Args) -> Result<(), CommandError> { @@ -130,7 +131,7 @@ pub fn with_embeds(_: &mut Context, }, CommandOrAlias::Alias(ref name) => { - let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name)); + let _ = msg.channel_id.say(help_options.suggestion_text.replace("{}", name)); return Ok(()); }, } @@ -142,50 +143,50 @@ pub fn with_embeds(_: &mut Context, if let Some((command_name, command)) = found { let command = command.options(); if !command.help_available { - error_embed(&msg.channel_id, "**Error**: No help available."); + error_embed(&msg.channel_id, &help_options.no_help_available_text, help_options.embed_error_colour); return Ok(()); } let _ = msg.channel_id.send_message(|m| { m.embed(|e| { - let mut embed = e.colour(Colour::rosewater()).title(command_name); + let mut embed = e.colour(help_options.embed_success_colour.clone()).title(command_name.clone()); if let Some(ref desc) = command.desc { embed = embed.description(desc); } if let Some(ref usage) = command.usage { - let value = format!("`{} {}`", command_name, usage); + let value = format!("`{} {}`", command_name.clone(), usage); - embed = embed.field("Usage", value, true); + embed = embed.field(&help_options.usage_label, value, true); } if let Some(ref example) = command.example { - let value = format!("`{} {}`", command_name, example); + let value = format!("`{} {}`", command_name.clone(), example); - embed = embed.field("Sample usage", value, true); + embed = embed.field(&help_options.usage_sample_label, value, true); } if group_name != "Ungrouped" { - embed = embed.field("Group", group_name, true); + embed = embed.field(&help_options.grouped_label, group_name, true); } if !command.aliases.is_empty() { let aliases = command.aliases.join(", "); - embed = embed.field("Aliases", aliases, true); + embed = embed.field(&help_options.aliases_label, aliases, true); } let available = if command.dm_only { - "Only in DM" + &help_options.dm_only_text } else if command.guild_only { - "Only in guilds" + &help_options.guild_only_text } else { - "In DM and guilds" + &help_options.dm_and_guild_text }; - embed = embed.field("Available", available, true); + embed = embed.field(&help_options.available_text, available, true); embed }) @@ -195,18 +196,24 @@ pub fn with_embeds(_: &mut Context, } } - let error_msg = format!("**Error**: Command `{}` not found.", name); - error_embed(&msg.channel_id, &error_msg); + let error_msg = help_options.command_not_found_text.replace("{}", &name); + error_embed(&msg.channel_id, &error_msg, help_options.embed_error_colour); return Ok(()); } let _ = msg.channel_id.send_message(|m| { m.embed(|mut e| { - e = e.colour(Colour::rosewater()).description( - "To get help with an individual command, pass its \ - name as an argument to this command.", - ); + + if let Some(striked_command_text) = help_options.striked_commands_tip.clone() { + e = e.colour(help_options.embed_success_colour).description( + format!("{}\n{}", &help_options.individual_command_tip, &striked_command_text), + ); + } else { + e = e.colour(help_options.embed_success_colour).description( + &help_options.individual_command_tip, + ); + } let mut group_names = groups.keys().collect::>(); group_names.sort(); @@ -216,7 +223,7 @@ pub fn with_embeds(_: &mut Context, let mut desc = String::new(); if let Some(ref x) = group.prefix { - let _ = write!(desc, "Prefix: {}\n", x); + let _ = write!(desc, "{}: `{}`\n", &help_options.group_prefix, x); } let mut has_commands = false; @@ -229,9 +236,41 @@ pub fn with_embeds(_: &mut Context, let cmd = &commands[name]; let cmd = cmd.options(); - if cmd.help_available && has_all_requirements(&cmd, msg) { - let _ = write!(desc, "`{}`\n", name); - has_commands = true; + if !cmd.dm_only && !cmd.guild_only || cmd.dm_only && msg.is_private() || cmd.guild_only && !msg.is_private() { + if cmd.help_available && has_correct_permissions(&cmd, msg) { + let _ = write!(desc, "`{}`\n", name); + has_commands = true; + } else { + match help_options.lacking_permissions { + HelpBehaviour::Strike => { + let name = format!("~~`{}`~~", &name); + let _ = write!(desc, "{}\n", name); + has_commands = true; + }, + HelpBehaviour::Nothing => { + let _ = write!(desc, "`{}`\n", name); + has_commands = true; + }, + HelpBehaviour::Hide => { + continue; + }, + } + } + } else { + match help_options.wrong_channel { + HelpBehaviour::Strike => { + let name = format!("~~`{}`~~", &name); + let _ = write!(desc, "{}\n", name); + has_commands = true; + }, + HelpBehaviour::Nothing => { + let _ = write!(desc, "`{}`\n", name); + has_commands = true; + }, + HelpBehaviour::Hide => { + continue; + }, + } } } @@ -266,6 +305,7 @@ pub fn with_embeds(_: &mut Context, /// ``` pub fn plain(_: &mut Context, msg: &Message, + help_options: &HelpOptions, groups: HashMap>, args: Args) -> Result<(), CommandError> { @@ -306,7 +346,7 @@ pub fn plain(_: &mut Context, }, CommandOrAlias::Alias(ref name) => { - let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name)); + let _ = msg.channel_id.say(help_options.suggestion_text.replace("{}", name)); return Ok(()); }, } @@ -319,7 +359,7 @@ pub fn plain(_: &mut Context, let command = command.options(); if !command.help_available { - let _ = msg.channel_id.say("**Error**: No help available."); + let _ = msg.channel_id.say(&help_options.no_help_available_text); return Ok(()); } @@ -327,36 +367,36 @@ pub fn plain(_: &mut Context, if !command.aliases.is_empty() { let aliases = command.aliases.join("`, `"); - let _ = write!(result, "**Aliases:** `{}`\n", aliases); + let _ = write!(result, "**{}:** `{}`\n", help_options.aliases_label, aliases); } if let Some(ref desc) = command.desc { - let _ = write!(result, "**Description:** {}\n", desc); + let _ = write!(result, "**{}:** {}\n", help_options.description_label, desc); } if let Some(ref usage) = command.usage { - let _ = write!(result, "**Usage:** `{} {}`\n", command_name, usage); + let _ = write!(result, "**{}:** `{} {}`\n", help_options.usage_label, command_name, usage); } if let Some(ref example) = command.example { - let _ = write!(result, "**Sample usage:** `{} {}`\n", command_name, example); + let _ = write!(result, "**{}:** `{} {}`\n", help_options.usage_sample_label, command_name, example); } if group_name != "Ungrouped" { - let _ = write!(result, "**Group:** {}\n", group_name); + let _ = write!(result, "**{}:** {}\n", help_options.grouped_label, group_name); } let only = if command.dm_only { - "Only in DM" + &help_options.dm_only_text } else if command.guild_only { - "Only in guilds" + &help_options.guild_only_text } else { - "In DM and guilds" + &help_options.dm_and_guild_text }; - result.push_str("**Available:** "); + result.push_str(&format!("**{}:** ", &help_options.available_text)); result.push_str(only); - result.push_str("\n"); + result.push_str(".\n"); let _ = msg.channel_id.say(&result); @@ -365,13 +405,12 @@ pub fn plain(_: &mut Context, } let _ = msg.channel_id - .say(&format!("**Error**: Command `{}` not found.", name)); + .say(&help_options.suggestion_text.replace("{}", &name)); return Ok(()); } - let mut result = "**Commands**\nTo get help with an individual command, pass its \ - name as an argument to this command.\n\n" + let mut result = format!("**Commands**\n{}\n\n", help_options.individual_command_tip) .to_string(); let mut group_names = groups.keys().collect::>(); @@ -398,7 +437,7 @@ pub fn plain(_: &mut Context, let _ = write!(result, "**{}:** ", group_name); if let Some(ref x) = group.prefix { - let _ = write!(result, "(prefix: `{}`): ", x); + let _ = write!(result, "({}: `{}`): ", help_options.group_prefix, x); } result.push_str(&group_help); diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs index 95e0028..6f7e180 100644 --- a/src/framework/standard/mod.rs +++ b/src/framework/standard/mod.rs @@ -4,16 +4,18 @@ pub mod help_commands; mod command; mod configuration; mod create_command; +mod create_help_command; mod create_group; mod buckets; mod args; pub use self::args::{Args, Iter, FromStrZc, Error as ArgError}; pub(crate) use self::buckets::{Bucket, Ratelimit}; -pub(crate) use self::command::{Help, HelpFunction}; -pub use self::command::{Command, CommandGroup, CommandOptions, Error as CommandError}; +pub(crate) use self::command::{Help}; +pub use self::command::{HelpFunction, HelpOptions, Command, CommandGroup, CommandOptions, Error as CommandError}; pub use self::command::CommandOrAlias; pub use self::configuration::Configuration; +pub use self::create_help_command::CreateHelpCommand; pub use self::create_command::{CreateCommand, FnOrCommand}; pub use self::create_group::CreateGroup; @@ -653,7 +655,7 @@ impl StandardFramework { group .commands .insert(name.to_string(), CommandOrAlias::Command(Arc::clone(&cmd))); - + cmd.init(); } } @@ -703,7 +705,7 @@ impl StandardFramework { group .commands .insert(name, CommandOrAlias::Command(Arc::clone(&cmd))); - + cmd.init(); } } @@ -713,13 +715,6 @@ impl StandardFramework { self } - /// Sets what code should be execute when a user requests for `(prefix)help`. - pub fn help(mut self, f: HelpFunction) -> Self { - self.help = Some(Arc::new(Help(f))); - - self - } - /// Adds a group which can organize several related commands. /// Groups are taken into account when using /// `serenity::framework::standard::help_commands`. @@ -881,6 +876,27 @@ impl StandardFramework { self } + + /// Sets what code should be executed when a user sends `(prefix)help`. + pub fn help(mut self, f: HelpFunction) -> Self { + let a = CreateHelpCommand(HelpOptions::default(), f).finish(); + + self.help = Some(a); + + self + } + + /// Sets what code should be executed when sends `(prefix)help`. + /// Additionally takes a closure with a `CreateHelpCommand` in order + /// to alter help-commands. + pub fn customised_help(mut self, f: HelpFunction, c: F) -> Self + where F: FnOnce(CreateHelpCommand) -> CreateHelpCommand { + let a = c(CreateHelpCommand(HelpOptions::default(), f)); + + self.help = Some(a.finish()); + + self + } } impl Framework for StandardFramework { @@ -964,16 +980,19 @@ impl Framework for StandardFramework { // This is a special case. if to_check == "help" { let help = self.help.clone(); + if let Some(help) = help { let groups = self.groups.clone(); threadpool.execute(move || { + if let Some(before) = before { + if !(before)(&mut context, &message, &built) { return; } } - let result = (help.0)(&mut context, &message, groups, args); + let result = (help.0)(&mut context, &message, &help.1, groups, args); if let Some(after) = after { (after)(&mut context, &message, &built, result); @@ -1016,7 +1035,7 @@ impl Framework for StandardFramework { let result = command.execute(&mut context, &message, args); command.after(&mut context, &message, &result); - + if let Some(after) = after { (after)(&mut context, &message, &built, result); } @@ -1055,3 +1074,28 @@ pub fn has_correct_roles(cmd: &Arc, guild: &Guild, member: &Memb .flat_map(|r| guild.role_by_name(r)) .any(|g| member.roles.contains(&g.id)) } + +/// Describes the behaviour the help-command shall execute once it encounters +/// a command which the user or command fails to meet following criteria : +/// Lacking required permissions to execute the command. +/// Lacking required roles to execute the command. +/// The command can't be used in the current channel (as in `DM only` or `guild only`). +#[derive(PartialEq, Debug)] +pub enum HelpBehaviour { + /// Strikes a command by applying `~~{comand_name}~~`. + Strike, + /// Does not list a command in the help-menu. + Hide, + /// The command will be displayed, hence nothing will be done. + Nothing +} + +impl fmt::Display for HelpBehaviour { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + HelpBehaviour::Strike => write!(f, "HelpBehaviour::Strike"), + HelpBehaviour::Hide => write!(f, "HelpBehaviour::Hide"), + HelpBehaviour::Nothing => write!(f, "HelBehaviour::Nothing"), + } + } +} \ No newline at end of file -- cgit v1.2.3