aboutsummaryrefslogtreecommitdiff
path: root/src/framework
diff options
context:
space:
mode:
authorMishio595 <[email protected]>2018-11-13 22:23:39 -0700
committerMishio595 <[email protected]>2018-11-13 22:23:39 -0700
commit1dad6996d8e9c983dc9e1689a93dbef5b0696c22 (patch)
tree1ee7226178cae76b20e6adcdc7e7cfcc3c477c32 /src/framework
parentFinish rebase (diff)
parentMake `Region`s `Japan`-variant lowercase. (diff)
downloadserenity-1dad6996d8e9c983dc9e1689a93dbef5b0696c22.tar.xz
serenity-1dad6996d8e9c983dc9e1689a93dbef5b0696c22.zip
Merge branch 'upstream'
Diffstat (limited to 'src/framework')
-rw-r--r--src/framework/standard/args.rs45
-rw-r--r--src/framework/standard/command.rs15
-rw-r--r--src/framework/standard/configuration.rs20
-rw-r--r--src/framework/standard/create_command.rs16
-rw-r--r--src/framework/standard/create_group.rs19
-rw-r--r--src/framework/standard/create_help_command.rs6
-rw-r--r--src/framework/standard/help_commands.rs13
-rw-r--r--src/framework/standard/mod.rs130
8 files changed, 228 insertions, 36 deletions
diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs
index 2700bfb..6841bd7 100644
--- a/src/framework/standard/args.rs
+++ b/src/framework/standard/args.rs
@@ -348,6 +348,51 @@ impl Args {
self.args.get(self.offset).map(|t| t.lit.as_str())
}
+ /// Trims the current argument off leading and trailing whitespace.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new(" 42 ", &[]);
+ ///
+ /// assert_eq!(args.trim().current(), Some("42"));
+ /// ```
+ pub fn trim(&mut self) -> &mut Self {
+ if self.is_empty() {
+ return self;
+ }
+
+ self.args[self.offset].lit = self.args[self.offset].lit.trim().to_string();
+
+ self
+ }
+
+ /// Trims all of the arguments after the offset off leading and trailing whitespace.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new(" 42 , 84 ,\t168\t", &[",".to_string()]);
+ ///
+ /// args.trim_all();
+ /// assert_eq!(args.single::<String>().unwrap(), "42");
+ /// assert_eq!(args.single::<String>().unwrap(), "84");
+ /// assert_eq!(args.single::<String>().unwrap(), "168");
+ /// ```
+ pub fn trim_all(&mut self) {
+ if self.is_empty() {
+ return;
+ }
+
+ for token in &mut self.args[self.offset..] {
+ token.lit = token.lit.trim().to_string();
+ }
+ }
+
/// Parses the current argument and advances.
///
/// # Examples
diff --git a/src/framework/standard/command.rs b/src/framework/standard/command.rs
index 2b1149e..5345f4b 100644
--- a/src/framework/standard/command.rs
+++ b/src/framework/standard/command.rs
@@ -60,6 +60,7 @@ impl HelpCommand for Help {
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 type UnrecognisedCommandHook = Fn(&mut Context, &Message, &str) + Send + Sync + 'static;
+pub type MessageWithoutCommandHook = Fn(&mut Context, &Message) + Send + Sync + 'static;
pub(crate) type InternalCommand = Arc<Command>;
pub type PrefixCheck = Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static;
@@ -81,7 +82,7 @@ impl fmt::Debug for CommandOrAlias {
#[derive(Clone, Debug)]
pub struct Error(pub String);
-// TODO: Have seperate `From<(&)String>` and `From<&str>` impls via specialization
+// TODO: Have separate `From<(&)String>` and `From<&str>` impls via specialization
impl<D: fmt::Display> From<D> for Error {
fn from(d: D) -> Self {
Error(d.to_string())
@@ -99,6 +100,7 @@ pub struct CommandGroup {
pub help_available: bool,
pub dm_only: bool,
pub guild_only: bool,
+ pub owner_privileges: bool,
pub owners_only: bool,
pub help: Option<Arc<Help>>,
/// A set of checks to be called prior to executing the command-group. The checks
@@ -117,6 +119,7 @@ impl Default for CommandGroup {
required_permissions: Permissions::empty(),
dm_only: false,
guild_only: false,
+ owner_privileges: true,
help_available: true,
owners_only: false,
allowed_roles: Vec::new(),
@@ -141,7 +144,7 @@ pub struct CommandOptions {
pub example: Option<String>,
/// Command usage schema, used by other commands.
pub usage: Option<String>,
- /// Minumum amount of arguments that should be passed.
+ /// Minimum amount of arguments that should be passed.
pub min_args: Option<i32>,
/// Maximum amount of arguments that can be passed.
pub max_args: Option<i32>,
@@ -155,6 +158,8 @@ pub struct CommandOptions {
pub dm_only: bool,
/// Whether command can be used only in guilds or not.
pub guild_only: bool,
+ /// Whether the command treats owners as normal users.
+ pub owner_privileges: bool,
/// Whether command can only be used by owners or not.
pub owners_only: bool,
/// Other names that can be used to call this command instead.
@@ -216,7 +221,7 @@ pub struct HelpOptions {
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.
+ /// Colour help-embed will use if no error occurred.
pub embed_success_colour: Colour,
/// If not 0, help will check whether a command is similar to searched named.
pub max_levenshtein_distance: usize,
@@ -335,6 +340,7 @@ impl Default for CommandOptions {
required_permissions: Permissions::empty(),
dm_only: false,
guild_only: false,
+ owner_privileges: true,
help_available: true,
owners_only: false,
allowed_roles: Vec::new(),
@@ -374,7 +380,8 @@ pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Opti
// If the above do not fill `positions`, then that means no kind of prefix was present.
// Check if a no-prefix-execution is applicable.
- if conf.no_dm_prefix && private && positions.is_empty() {
+ if conf.no_dm_prefix && private && positions.is_empty() &&
+ !(conf.ignore_bots && msg.author.bot) {
positions.push(0);
}
}
diff --git a/src/framework/standard/configuration.rs b/src/framework/standard/configuration.rs
index 795493d..a8ef075 100644
--- a/src/framework/standard/configuration.rs
+++ b/src/framework/standard/configuration.rs
@@ -6,9 +6,10 @@ use model::{
};
use std::{
collections::HashSet,
- default::Default
+ default::Default,
+ sync::Arc,
};
-use super::command::PrefixCheck;
+use super::command::{Command, InternalCommand, PrefixCheck};
/// The configuration to use for a [`StandardFramework`] associated with a [`Client`]
/// instance.
@@ -59,6 +60,7 @@ pub struct Configuration {
#[doc(hidden)] pub no_dm_prefix: bool,
#[doc(hidden)] pub delimiters: Vec<String>,
#[doc(hidden)] pub case_insensitive: bool,
+ #[doc(hidden)] pub prefix_only_cmd: Option<InternalCommand>,
}
impl Configuration {
@@ -448,7 +450,7 @@ impl Configuration {
///
/// # Examples
///
- /// Have the args be seperated by a comma and a space:
+ /// Have the args be separated by a comma and a space:
///
/// ```rust,no_run
/// # use serenity::prelude::*;
@@ -475,7 +477,7 @@ impl Configuration {
///
/// # Examples
///
- /// Have the args be seperated by a comma and a space; and a regular space:
+ /// Have the args be separated by a comma and a space; and a regular space:
///
/// ```rust,no_run
/// # use serenity::prelude::*;
@@ -511,6 +513,15 @@ impl Configuration {
self
}
+
+ /// Sets a command to dispatch if user's input is a prefix only.
+ ///
+ /// **Note**: Defaults to no command and ignores prefix only.
+ pub fn prefix_only_cmd<C: Command + 'static>(mut self, c: C) -> Self {
+ self.prefix_only_cmd = Some(Arc::new(c));
+
+ self
+ }
}
impl Default for Configuration {
@@ -550,6 +561,7 @@ impl Default for Configuration {
on_mention: None,
owners: HashSet::default(),
prefixes: vec![],
+ prefix_only_cmd: None,
}
}
}
diff --git a/src/framework/standard/create_command.rs b/src/framework/standard/create_command.rs
index a769d4c..3976f6d 100644
--- a/src/framework/standard/create_command.rs
+++ b/src/framework/standard/create_command.rs
@@ -168,6 +168,14 @@ impl CreateCommand {
self
}
+ /// Whether owners shall bypass buckets, missing permissions,
+ /// wrong channels, missing roles, and checks.
+ pub fn owner_privileges(mut self, owner_privileges: bool) -> Self {
+ self.0.owner_privileges = owner_privileges;
+
+ self
+ }
+
/// Whether command should be displayed in help list or not, used by other commands.
pub fn help_available(mut self, help_available: bool) -> Self {
self.0.help_available = help_available;
@@ -189,7 +197,7 @@ impl CreateCommand {
self
}
- /// Minumum amount of arguments that should be passed.
+ /// Minimum amount of arguments that should be passed.
pub fn min_args(mut self, min_args: i32) -> Self {
self.0.min_args = Some(min_args);
@@ -236,7 +244,7 @@ impl CreateCommand {
/// Sets an initialise middleware to be called upon the command's actual registration.
///
- /// This is similiar to implementing the `init` function on `Command`.
+ /// This is similar to implementing the `init` function on `Command`.
pub fn init<F: Fn() + Send + Sync + 'static>(mut self, f: F) -> Self {
self.2.init = Some(Arc::new(f));
@@ -245,7 +253,7 @@ impl CreateCommand {
/// Sets a before middleware to be called before the command's execution.
///
- /// This is similiar to implementing the `before` function on `Command`.
+ /// This is similar to implementing the `before` function on `Command`.
pub fn before<F: Send + Sync + 'static>(mut self, f: F) -> Self
where F: Fn(&mut Context, &Message) -> bool {
self.2.before = Some(Arc::new(f));
@@ -255,7 +263,7 @@ impl CreateCommand {
/// Sets an after middleware to be called after the command's execution.
///
- /// This is similiar to implementing the `after` function on `Command`.
+ /// This is similar to implementing the `after` function on `Command`.
pub fn after<F: Send + Sync + 'static>(mut self, f: F) -> Self
where F: Fn(&mut Context, &Message, &Result<(), CommandError>) {
self.2.after = Some(Arc::new(f));
diff --git a/src/framework/standard/create_group.rs b/src/framework/standard/create_group.rs
index 3cd4047..0049cf3 100644
--- a/src/framework/standard/create_group.rs
+++ b/src/framework/standard/create_group.rs
@@ -43,7 +43,8 @@ impl CreateGroup {
.dm_only(self.0.dm_only)
.guild_only(self.0.guild_only)
.help_available(self.0.help_available)
- .owners_only(self.0.owners_only);
+ .owners_only(self.0.owners_only)
+ .owner_privileges(self.0.owner_privileges);
if let Some(ref bucket) = self.0.bucket {
cmd = cmd.bucket(bucket);
@@ -151,6 +152,14 @@ impl CreateGroup {
self
}
+ /// Whether owners shall bypass buckets, missing permissions,
+ /// wrong channels, missing roles, and checks.
+ pub fn owner_privileges(mut self, owner_privileges: bool) -> Self {
+ self.0.owner_privileges = owner_privileges;
+
+ self
+ }
+
/// Whether command should be displayed in help list or not, used by other commands.
pub fn help_available(mut self, help_available: bool) -> Self {
self.0.help_available = help_available;
@@ -198,11 +207,11 @@ impl CreateGroup {
/// Adds a command for a group that will be executed if no command-name
/// has been passed.
pub fn default_cmd<C: Command + 'static>(mut self, c: C) -> Self {
- let cmd: Arc<Command> = Arc::new(c);
-
- self.0.default_command = Some(CommandOrAlias::Command(Arc::clone(&cmd)));
+ c.init();
- cmd.init();
+ let cmd_with_group_options = self.build_command().cmd(c).finish();
+ let cmd_finished = CommandOrAlias::Command(cmd_with_group_options);
+ self.0.default_command = Some(cmd_finished);
self
}
diff --git a/src/framework/standard/create_help_command.rs b/src/framework/standard/create_help_command.rs
index b8eb57a..c05cb32 100644
--- a/src/framework/standard/create_help_command.rs
+++ b/src/framework/standard/create_help_command.rs
@@ -126,7 +126,7 @@ impl CreateHelpCommand {
self
}
- /// Sets how the group-prexix shall be labeled.
+ /// Sets how the group-prefix shall be labeled.
pub fn group_prefix(mut self, text: &str) -> Self {
self.0.group_prefix = text.to_string();
@@ -204,14 +204,14 @@ impl CreateHelpCommand {
self
}
- /// Sets the colour for the embed if no error occured.
+ /// Sets the colour for the embed if no error occurred.
pub fn embed_success_colour(mut self, colour: Colour) -> Self {
self.0.embed_success_colour = colour;
self
}
- /// Sets the colour for the embed if an error occured.
+ /// Sets the colour for the embed if an error occurred.
pub fn embed_error_colour(mut self, colour: Colour) -> Self {
self.0.embed_error_colour = colour;
diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs
index ca8fdc6..94c9127 100644
--- a/src/framework/standard/help_commands.rs
+++ b/src/framework/standard/help_commands.rs
@@ -112,8 +112,8 @@ impl Suggestions {
&self.0
}
- /// Concats names of suggestions with a given `seperator`.
- fn join(&self, seperator: &str) -> String {
+ /// Concats names of suggestions with a given `separator`.
+ fn join(&self, separator: &str) -> String {
let mut iter = self.as_vec().iter();
let first_iter_element = match iter.next() {
@@ -122,12 +122,12 @@ impl Suggestions {
};
let size = self.as_vec().iter().fold(0, |total_size, size| total_size + size.name.len());
- let byte_len_of_sep = self.as_vec().len().checked_sub(1).unwrap_or(0) * seperator.len();
+ let byte_len_of_sep = self.as_vec().len().checked_sub(1).unwrap_or(0) * separator.len();
let mut result = String::with_capacity(size + byte_len_of_sep);
result.push_str(first_iter_element.name.borrow());
for element in iter {
- result.push_str(&*seperator);
+ result.push_str(&*separator);
result.push_str(element.name.borrow());
}
@@ -303,6 +303,7 @@ pub fn is_command_visible(command_options: &Arc<CommandOptions>, msg: &Message,
/// Tries to extract a single command matching searched command name otherwise
/// returns similar commands.
+#[cfg(feature = "cache")]
fn fetch_single_command<'a, H: BuildHasher>(
groups: &'a HashMap<String, Arc<CommandGroup>, H>,
name: &str,
@@ -423,6 +424,7 @@ fn fetch_single_command<'a, H: BuildHasher>(
}
/// Tries to extract a single command matching searched command name.
+#[cfg(feature = "cache")]
fn fetch_all_eligible_commands_in_group<'a>(
commands: &HashMap<&String, &InternalCommand>,
command_names: &[&&String],
@@ -471,6 +473,7 @@ fn fetch_all_eligible_commands_in_group<'a>(
}
/// Fetch groups with their commands.
+#[cfg(feature = "cache")]
fn create_command_group_commands_pair_from_groups<'a, H: BuildHasher>(
groups: &'a HashMap<String, Arc<CommandGroup>, H>,
group_names: &[&'a String],
@@ -498,6 +501,7 @@ fn create_command_group_commands_pair_from_groups<'a, H: BuildHasher>(
}
/// Fetches a single group with its commands.
+#[cfg(feature = "cache")]
fn create_single_group<'a>(
group: &CommandGroup,
group_name: &'a str,
@@ -527,6 +531,7 @@ let commands = remove_aliases(&group.commands);
/// Iterates over all commands and forges them into a `CustomisedHelpData`
/// taking `HelpOptions` into consideration when deciding on whether a command
/// shall be picked and in what textual format.
+#[cfg(feature = "cache")]
pub fn create_customised_help_data<'a, H: BuildHasher>(
groups: &'a HashMap<String, Arc<CommandGroup>, H>,
args: &'a Args,
diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs
index aa32d11..162871d 100644
--- a/src/framework/standard/mod.rs
+++ b/src/framework/standard/mod.rs
@@ -39,7 +39,7 @@ use model::{
id::{ChannelId, GuildId, UserId},
Permissions
};
-use self::command::{AfterHook, BeforeHook, UnrecognisedCommandHook};
+use self::command::{AfterHook, BeforeHook, MessageWithoutCommandHook, UnrecognisedCommandHook};
use std::{
collections::HashMap,
default::Default,
@@ -217,6 +217,7 @@ pub struct StandardFramework {
buckets: HashMap<String, Bucket>,
after: Option<Arc<AfterHook>>,
unrecognised_command: Option<Arc<UnrecognisedCommandHook>>,
+ message_without_command: Option<Arc<MessageWithoutCommandHook>>,
/// Whether the framework has been "initialized".
///
/// The framework is initialized once one of the following occurs:
@@ -532,7 +533,7 @@ impl StandardFramework {
}
}
- if self.configuration.owners.contains(&message.author.id) {
+ if command.owner_privileges && self.configuration.owners.contains(&message.author.id) {
return None;
}
@@ -949,9 +950,34 @@ impl StandardFramework {
self
}
+ /// Specify the function to be called if a message contains no command.
+ ///
+ /// # Examples
+ ///
+ /// Using `message_without_command`:
+ ///
+ /// ```rust,no_run
+ /// # use serenity::prelude::*;
+ /// # struct Handler;
+ /// #
+ /// # impl EventHandler for Handler {}
+ /// # let mut client = Client::new("token", Handler).unwrap();
+ /// #
+ /// use serenity::framework::StandardFramework;
+ ///
+ /// client.with_framework(StandardFramework::new()
+ /// .message_without_command(|ctx, msg| { }));
+ /// ```
+ pub fn message_without_command<F>(mut self, f: F) -> Self
+ where F: Fn(&mut Context, &Message) + Send + Sync + 'static {
+ self.message_without_command = Some(Arc::new(f));
+
+ self
+ }
+
/// Sets what code should be executed when a user sends `(prefix)help`.
///
- /// If a command named `help` was set with [`command`], then this takes precendence first.
+ /// If a command named `help` was set with [`command`], then this takes precedence first.
///
/// [`command`]: #method.command
pub fn help(mut self, f: HelpFunction) -> Self {
@@ -1004,12 +1030,54 @@ impl Framework for StandardFramework {
// Ensure that there is _at least one_ position remaining. There
// is no point in continuing if there is not.
if positions.is_empty() {
+
+ if let Some(ref prefix_only_cmd) =
+ self.configuration.prefix_only_cmd {
+ let prefix_only_cmd = Arc::clone(prefix_only_cmd);
+ let before = self.before.clone();
+ let after = self.after.clone();
+
+ threadpool.execute(move || {
+ if let Some(before) = before {
+ if !(before)(&mut context, &message, "") {
+ return;
+ }
+ }
+
+ if !prefix_only_cmd.before(&mut context, &message) {
+ return;
+ }
+
+ let result = prefix_only_cmd.execute(&mut context,
+ &message, Args::new("", &Vec::new()));
+
+ prefix_only_cmd.after(&mut context, &message,
+ &result);
+
+ if let Some(after) = after {
+ (after)(&mut context, &message, "", result);
+ }
+ });
+ }
+
return;
}
positions
},
- None => return,
+ None => {
+ if let &Some(ref message_without_command) = &self.message_without_command {
+
+ if !(self.configuration.ignore_bots && message.author.bot) {
+ let message_without_command = message_without_command.clone();
+ threadpool.execute(move || {
+ (message_without_command)(&mut context, &message);
+ });
+ }
+ }
+
+ return;
+ },
};
'outer: for position in positions {
@@ -1160,6 +1228,21 @@ impl Framework for StandardFramework {
Args::new(&orginal_round[longest_matching_prefix_len..], &self.configuration.delimiters)
};
+ if let Some(error) = self.should_fail(
+ &mut context,
+ &message,
+ &command.options(),
+ &group,
+ &mut args,
+ &to_check,
+ &built,
+ ) {
+ if let Some(ref handler) = self.dispatch_error_handler {
+ handler(context, message, error);
+ }
+ return;
+ }
+
threadpool.execute(move || {
if let Some(before) = before {
if !(before)(&mut context, &message, &args.full()) {
@@ -1190,9 +1273,33 @@ impl Framework for StandardFramework {
if !(self.configuration.ignore_bots && message.author.bot) {
if let &Some(ref unrecognised_command) = &self.unrecognised_command {
- let unrecognised_command = unrecognised_command.clone();
- threadpool.execute(move || {
- (unrecognised_command)(&mut context, &message, &unrecognised_command_name);
+
+ // If both functions are set, we need to clone `Context` and
+ // `Message`, else we can avoid it.
+ if let &Some(ref message_without_command) = &self.message_without_command {
+ let mut context_unrecognised = context.clone();
+ let message_unrecognised = message.clone();
+
+ let unrecognised_command = unrecognised_command.clone();
+ threadpool.execute(move || {
+ (unrecognised_command)(&mut context_unrecognised, &message_unrecognised,
+ &unrecognised_command_name);
+ });
+
+ let message_without_command = message_without_command.clone();
+ threadpool.execute(move || {
+ (message_without_command)(&mut context, &message);
+ });
+ } else {
+ let unrecognised_command = unrecognised_command.clone();
+ threadpool.execute(move || {
+ (unrecognised_command)(&mut context, &message, &unrecognised_command_name);
+ });
+ }
+ } else if let &Some(ref message_without_command) = &self.message_without_command {
+ let message_without_command = message_without_command.clone();
+ threadpool.execute(move || {
+ (message_without_command)(&mut context, &message);
});
}
}
@@ -1205,8 +1312,9 @@ impl Framework for StandardFramework {
#[cfg(feature = "cache")]
pub fn has_correct_permissions(command: &Arc<CommandOptions>, message: &Message) -> bool {
- if !command.required_permissions.is_empty() {
-
+ if command.required_permissions.is_empty() {
+ true
+ } else {
if let Some(guild) = message.guild() {
let perms = guild
.with(|g| g.permissions_in(message.channel_id, message.author.id));
@@ -1215,8 +1323,6 @@ pub fn has_correct_permissions(command: &Arc<CommandOptions>, message: &Message)
} else {
false
}
- } else {
- true
}
}
@@ -1238,7 +1344,7 @@ pub fn has_correct_roles(cmd: &Arc<CommandOptions>, guild: &Guild, member: &Memb
/// 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}~~`.
+ /// Strikes a command by applying `~~{command_name}~~`.
Strike,
/// Does not list a command in the help-menu.
Hide,