diff options
| author | acdenisSK <[email protected]> | 2017-07-22 16:59:10 +0200 |
|---|---|---|
| committer | acdenisSK <[email protected]> | 2017-07-22 16:59:10 +0200 |
| commit | 2b053ea007d6ca9cc820cb910597e8b5dad89d70 (patch) | |
| tree | c0664957758043b80fc7e40d9acc7923da53d3c1 /src/framework | |
| parent | Remove the uneccessary function and `Send + Sync` bounds (diff) | |
| download | serenity-2b053ea007d6ca9cc820cb910597e8b5dad89d70.tar.xz serenity-2b053ea007d6ca9cc820cb910597e8b5dad89d70.zip | |
Fix #130
Removed action support from the builtin one as well, due to it adding some uneccassery complexity and it being only asked upon by one user
Diffstat (limited to 'src/framework')
| -rw-r--r-- | src/framework/configuration.rs | 30 | ||||
| -rw-r--r-- | src/framework/help_commands.rs | 9 | ||||
| -rw-r--r-- | src/framework/mod.rs | 384 |
3 files changed, 210 insertions, 213 deletions
diff --git a/src/framework/configuration.rs b/src/framework/configuration.rs index 16c595f..c74739e 100644 --- a/src/framework/configuration.rs +++ b/src/framework/configuration.rs @@ -22,10 +22,11 @@ use ::model::{GuildId, Message, UserId}; /// impl EventHandler for Handler {} /// use serenity::Client; /// use std::env; +/// use serenity::framework::BuiltinFramework; /// /// let mut client = Client::new(&env::var("DISCORD_BOT_TOKEN").unwrap(), Handler); /// -/// client.with_framework(|f| f +/// client.with_framework(BuiltinFramework::new() /// .configure(|c| c.on_mention(true).prefix("~"))); /// ``` /// @@ -109,8 +110,9 @@ impl Configuration { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// use serenity::model::GuildId; + /// use serenity::framework::BuiltinFramework; /// - /// client.with_framework(|f| f.configure(|c| c + /// client.with_framework(BuiltinFramework::new().configure(|c| c /// .blocked_guilds(vec![GuildId(7), GuildId(77)].into_iter().collect()))); /// ``` pub fn blocked_guilds(mut self, guilds: HashSet<GuildId>) -> Self { @@ -133,8 +135,9 @@ impl Configuration { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// use serenity::model::UserId; + /// use serenity::framework::BuiltinFramework; /// - /// client.with_framework(|f| f.configure(|c| c + /// client.with_framework(BuiltinFramework::new().configure(|c| c /// .blocked_users(vec![UserId(7), UserId(77)].into_iter().collect()))); /// ``` pub fn blocked_users(mut self, users: HashSet<UserId>) -> Self { @@ -169,10 +172,11 @@ impl Configuration { /// # /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); + /// use serenity::framework::BuiltinFramework; /// /// let disabled = vec!["ping"].into_iter().map(|x| x.to_owned()).collect(); /// - /// client.with_framework(|f| f + /// client.with_framework(BuiltinFramework::new() /// .command("ping", |c| c.exec_str("pong!")) /// .configure(|c| c.disabled_commands(disabled))); /// ``` @@ -198,7 +202,9 @@ impl Configuration { /// # /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .command("ping", |c| c.exec_str("Pong!")) /// .configure(|c| c.dynamic_prefix(|_, msg| { /// Some(if msg.channel_id.0 % 5 == 0 { @@ -282,8 +288,9 @@ impl Configuration { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// use serenity::model::UserId; + /// use serenity::framework::BuiltinFramework; /// - /// client.with_framework(|f| f.configure(|c| c + /// client.with_framework(BuiltinFramework::new().configure(|c| c /// .owners(vec![UserId(7), UserId(77)].into_iter().collect()))); /// ``` /// @@ -297,12 +304,13 @@ impl Configuration { /// # let mut client = Client::new("token", Handler); /// use serenity::model::UserId; /// use std::collections::HashSet; + /// use serenity::framework::BuiltinFramework; /// /// let mut set = HashSet::new(); /// set.insert(UserId(7)); /// set.insert(UserId(77)); /// - /// client.with_framework(|f| f.configure(|c| c.owners(set))); + /// client.with_framework(BuiltinFramework::new().configure(|c| c.owners(set))); /// ``` pub fn owners(mut self, user_ids: HashSet<UserId>) -> Self { self.owners = user_ids; @@ -324,7 +332,9 @@ impl Configuration { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f.configure(|c| c + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new().configure(|c| c /// .prefix("!"))); /// ``` pub fn prefix(mut self, prefix: &str) -> Self { @@ -347,7 +357,9 @@ impl Configuration { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f.configure(|c| c + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new().configure(|c| c /// .prefixes(vec!["!", ">", "+"]))); /// ``` pub fn prefixes(mut self, prefixes: Vec<&str>) -> Self { diff --git a/src/framework/help_commands.rs b/src/framework/help_commands.rs index a35ce7d..d589ded 100644 --- a/src/framework/help_commands.rs +++ b/src/framework/help_commands.rs @@ -11,8 +11,9 @@ //! use std::env; //! //! let mut client = Client::new(&env::var("DISCORD_TOKEN").unwrap()); +//! use serenity::framework::BuiltinFramework; //! -//! client.with_framework(|f| f +//! client.with_framework(BuiltinFramework::new() //! .command("help", |c| c.exec_help(help_commands::with_embeds))); //! ``` //! @@ -65,8 +66,9 @@ fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &I /// # let mut client = Client::new("token", Handler); /// # /// use serenity::ext::framework::help_commands; +/// use serenity::framework::BuiltinFramework; /// -/// client.with_framework(|f| f +/// client.with_framework(BuiltinFramework::new() /// .command("help", |c| c.exec_help(help_commands::with_embeds))); /// ``` pub fn with_embeds(_: &mut Context, @@ -217,8 +219,9 @@ pub fn with_embeds(_: &mut Context, /// # let mut client = Client::new("token", Handler); /// # /// use serenity::ext::framework::help_commands; +/// use serenity::framework::BuiltinFramework; /// -/// client.with_framework(|f| f +/// client.with_framework(BuiltinFramework::new() /// .command("help", |c| c.exec_help(help_commands::plain))); /// ``` pub fn plain(_: &mut Context, diff --git a/src/framework/mod.rs b/src/framework/mod.rs index 73188ca..d04f60f 100644 --- a/src/framework/mod.rs +++ b/src/framework/mod.rs @@ -74,7 +74,7 @@ use std::collections::HashMap; use std::default::Default; use std::sync::Arc; use ::client::Context; -use ::model::{Message, MessageId, UserId, GuildId, ChannelId, ReactionType}; +use ::model::{Message, UserId, GuildId, ChannelId}; use ::model::permissions::Permissions; use ::utils; use tokio_core::reactor::Handle; @@ -84,6 +84,20 @@ use ::client::CACHE; #[cfg(feature="cache")] use ::model::Channel; +/// This trait allows for serenity to either use its builtin framework, or yours. +/// +/// When implementing, be sure to use `tokio_handle.spawn_fn(|| ...; Ok())` when dispatching commands. +/// +/// Note that you may see some other methods in here as well, but they're meant to be internal only for the builtin framework. +pub trait Framework { + fn dispatch(&mut self, Context, Message, &Handle); + + #[cfg(feature="builtin_framework")] + fn update_current_user(&mut self, UserId, bool) {} + #[cfg(feature="builtin_framework")] + fn initialized(&self) -> bool { false } +} + /// A macro to generate "named parameters". This is useful to avoid manually /// using the "arguments" parameter and manually parsing types. /// @@ -209,16 +223,6 @@ pub enum DispatchError { type DispatchErrorHook = Fn(Context, Message, DispatchError) + 'static; -pub(crate) type ActionFn = Fn(Context, MessageId, ChannelId) + 'static; - -/// Defines wheter this action should be called when -/// a reaction's added, or removed. -#[derive(Clone, Eq, Hash, PartialEq)] -pub enum ReactionAction { - Add(ReactionType), - Remove(ReactionType), -} - /// A utility for easily managing dispatches to commands. /// /// Refer to the [module-level documentation] for more information. @@ -226,13 +230,12 @@ pub enum ReactionAction { /// [module-level documentation]: index.html #[allow(type_complexity)] #[derive(Default)] -pub struct Framework { +pub struct BuiltinFramework { configuration: Configuration, groups: HashMap<String, Arc<CommandGroup>>, before: Option<Arc<BeforeHook>>, dispatch_error_handler: Option<Arc<DispatchErrorHook>>, buckets: HashMap<String, Bucket>, - pub(crate) reaction_actions: HashMap<ReactionAction, Arc<ActionFn>>, after: Option<Arc<AfterHook>>, /// Whether the framework has been "initialized". /// @@ -253,7 +256,11 @@ pub struct Framework { user_info: (u64, bool), } -impl Framework { +impl BuiltinFramework { + pub fn new() -> Self { + BuiltinFramework::default() + } + /// Configures the framework, setting non-default values. All fields are /// optional. Refer to [`Configuration::default`] for more information on /// the default values. @@ -268,10 +275,11 @@ impl Framework { /// # struct Handler; /// # impl EventHandler for Handler {} /// use serenity::Client; + /// use serenity::framework::BuiltinFramework; /// use std::env; /// /// let mut client = Client::new(&env::var("DISCORD_TOKEN").unwrap(), Handler); - /// client.with_framework(|f| f + /// client.with_framework(BuiltinFramework::new() /// .configure(|c| c /// .depth(3) /// .allow_whitespace(true) @@ -305,7 +313,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .bucket("basic", 2, 10, 3) /// .command("ping", |c| c /// .bucket("basic") @@ -336,12 +346,14 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .complex_bucket("basic", 2, 10, 3, |_, guild_id, channel_id, user_id| { /// // check if the guild is `123` and the channel where the command(s) was called: `456` /// // and if the user who called the command(s) is `789` /// // otherwise don't apply the bucket at all. - /// guild_id == 123 && channel_id == 456 && user_id == 789 + /// guild_id.is_some() && guild_id.unwrap() == 123 && channel_id == 456 && user_id == 789 /// }) /// .command("ping", |c| c /// .bucket("basic") @@ -376,7 +388,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .complex_bucket("basic", 2, 10, 3, |_, channel_id, user_id| { /// // check if the channel's id where the command(s) was called is `456` /// // and if the user who called the command(s) is `789` @@ -418,7 +432,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .simple_bucket("simple", 2) /// .command("ping", |c| c /// .bucket("simple") @@ -438,58 +454,6 @@ impl Framework { self } - /// Defines a "reaction action", that will be called if a reaction was - /// added; or deleted in a message. - /// - /// # Examples - /// ```rust,no_run - /// use serenity::model::ReactionType; - /// use serenity::framework::ReactionAction; - /// # use serenity::prelude::*; - /// # struct Handler; - /// # - /// # impl EventHandler for Handler {} - /// # let mut client = Client::new("token", Handler); - /// # - /// client.with_framework(|f| f - /// .action(ReactionAction::Add(ReactionType::Unicode("❤".to_string())), |_, _, channel_id| { - /// let _ = channel_id.say("love you too"); - /// }) - /// ); - /// ``` - pub fn action<F>(mut self, action: ReactionAction, f: F) -> Self - where F: Fn(Context, MessageId, ChannelId) + Send + Sync + 'static { - self.reaction_actions.insert(action, Arc::new(f)); - - self - } - - /// Remove the action from any further usage by the framework. - /// - /// # Examples - /// ```rust,no_run - /// use serenity::model::ReactionType; - /// use serenity::framework::ReactionAction; - /// # use serenity::prelude::*; - /// # struct Handler; - /// # - /// # impl EventHandler for Handler {} - /// # let mut client = Client::new("token", Handler); - /// # - /// let action = ReactionAction::Add(ReactionType::Unicode("❤".to_string())); - /// client.with_framework(|f| f - /// .action(action.clone(), |_, _, channel_id| { - /// let _ = channel_id.say("love you too"); - /// }) - /// .remove_action(action) - /// ); - /// ``` - pub fn remove_action(mut self, action: ReactionAction) -> Self { - self.reaction_actions.remove(&action); - - self - } - #[cfg(feature="cache")] fn is_blocked_guild(&self, message: &Message) -> bool { if let Some(Channel::Guild(channel)) = CACHE.read().unwrap().channel(message.channel_id) { @@ -613,126 +577,6 @@ impl Framework { } } - #[allow(cyclomatic_complexity)] - pub(crate) fn dispatch(&mut self, mut context: Context, message: Message, tokio_handle: &Handle) { - let res = command::positions(&mut context, &message, &self.configuration); - - let positions = match res { - Some(mut positions) => { - // First, take out the prefixes that are as long as _or_ longer - // than the message, to avoid character boundary violations. - positions.retain(|p| *p < message.content.len()); - - // Ensure that there is _at least one_ position remaining. There - // is no point in continuing if there is not. - if positions.is_empty() { - return; - } - - positions - }, - None => return, - }; - - 'outer: for position in positions { - let mut built = String::new(); - let round = message.content.chars() - .skip(position) - .collect::<String>(); - let round = round.trim() - .split_whitespace() - .collect::<Vec<&str>>(); - - for i in 0..self.configuration.depth { - if i != 0 { - built.push(' '); - } - - built.push_str(match round.get(i) { - Some(piece) => piece, - None => continue 'outer, - }); - - let groups = self.groups.clone(); - - for group in groups.values() { - let command_length = built.len(); - - if let Some(&CommandOrAlias::Alias(ref points_to)) = group.commands.get(&built) { - built = points_to.to_owned(); - } - - let to_check = if let Some(ref prefix) = group.prefix { - if built.starts_with(prefix) && command_length > prefix.len() + 1 { - built[(prefix.len() + 1)..].to_owned() - } else { - continue; - } - } else { - built.clone() - }; - - if let Some(&CommandOrAlias::Command(ref command)) = group.commands.get(&to_check) { - let before = self.before.clone(); - let command = command.clone(); - let after = self.after.clone(); - let groups = self.groups.clone(); - - let args = { - let content = message.content[position..].trim(); - - if command.use_quotes { - utils::parse_quotes(&content[command_length..]) - } else { - content[command_length..] - .split_whitespace() - .map(|arg| arg.to_owned()) - .collect::<Vec<String>>() - } - }; - - if let Some(error) = self.should_fail(&mut context, &message, &command, args.len(), &to_check, &built) { - if let Some(ref handler) = self.dispatch_error_handler { - handler(context, message, error); - } - return; - } - - tokio_handle.spawn_fn(move || { - if let Some(before) = before { - if !(before)(&mut context, &message, &built) { - return Ok(()); - } - } - - let result = match command.exec { - CommandType::StringResponse(ref x) => { - let _ = message.channel_id.say(x); - - Ok(()) - }, - CommandType::Basic(ref x) => { - (x)(&mut context, &message, args) - }, - CommandType::WithCommands(ref x) => { - (x)(&mut context, &message, groups, &args) - } - }; - - if let Some(after) = after { - (after)(&mut context, &message, &built, result); - } - - Ok(()) - }); - - return; - } - } - } - } - } - /// Adds a function to be associated with a command, which will be called /// when a command is used in a message. /// @@ -763,7 +607,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f.on("ping", ping)); + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new().on("ping", ping)); /// /// command!(ping(_ctx, msg) { /// let _ = msg.channel_id.say("pong!"); @@ -771,7 +617,7 @@ impl Framework { /// # } /// ``` pub fn on<F, S>(mut self, command_name: S, f: F) -> Self - where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static, + where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + 'static, S: Into<String> { { let ungrouped = self.groups.entry("Ungrouped".to_owned()) @@ -844,7 +690,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .group("ping-pong", |g| g /// .command("ping", |c| c.exec_str("pong!")) /// .command("pong", |c| c.exec_str("ping!")))); @@ -875,8 +723,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// use serenity::framework::DispatchError::{NotEnoughArguments, TooManyArguments}; + /// use serenity::framework::BuiltinFramework; /// - /// client.with_framework(|f| f + /// client.with_framework(BuiltinFramework::new() /// .on_dispatch_error(|_, msg, error| { /// match error { /// NotEnoughArguments { min, given } => { @@ -914,7 +763,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .before(|ctx, msg, cmd_name| { /// println!("Running command {}", cmd_name); /// true @@ -930,7 +781,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .before(|_, msg, cmd_name| { /// if let Ok(channel) = msg.channel_id.get() { /// // Don't run unless in nsfw channel @@ -966,7 +819,9 @@ impl Framework { /// # impl EventHandler for Handler {} /// # let mut client = Client::new("token", Handler); /// # - /// client.with_framework(|f| f + /// use serenity::framework::BuiltinFramework; + /// + /// client.with_framework(BuiltinFramework::new() /// .after(|ctx, msg, cmd_name, error| { /// // Print out an error if it happened /// if let Err(why) = error { @@ -980,8 +835,135 @@ impl Framework { self } +} + +impl Framework for BuiltinFramework { + fn dispatch(&mut self, mut context: Context, message: Message, tokio_handle: &Handle) { + let res = command::positions(&mut context, &message, &self.configuration); + + let positions = match res { + Some(mut positions) => { + // First, take out the prefixes that are as long as _or_ longer + // than the message, to avoid character boundary violations. + positions.retain(|p| *p < message.content.len()); + + // Ensure that there is _at least one_ position remaining. There + // is no point in continuing if there is not. + if positions.is_empty() { + return; + } - pub(crate) fn update_current_user(&mut self, user_id: UserId, is_bot: bool) { + positions + }, + None => return, + }; + + 'outer: for position in positions { + let mut built = String::new(); + let round = message.content.chars() + .skip(position) + .collect::<String>(); + let round = round.trim() + .split_whitespace() + .collect::<Vec<&str>>(); + + for i in 0..self.configuration.depth { + if i != 0 { + built.push(' '); + } + + built.push_str(match round.get(i) { + Some(piece) => piece, + None => continue 'outer, + }); + + let groups = self.groups.clone(); + + for group in groups.values() { + let command_length = built.len(); + + if let Some(&CommandOrAlias::Alias(ref points_to)) = group.commands.get(&built) { + built = points_to.to_owned(); + } + + let to_check = if let Some(ref prefix) = group.prefix { + if built.starts_with(prefix) && command_length > prefix.len() + 1 { + built[(prefix.len() + 1)..].to_owned() + } else { + continue; + } + } else { + built.clone() + }; + + if let Some(&CommandOrAlias::Command(ref command)) = group.commands.get(&to_check) { + let before = self.before.clone(); + let command = command.clone(); + let after = self.after.clone(); + let groups = self.groups.clone(); + + let args = { + let content = message.content[position..].trim(); + + if command.use_quotes { + utils::parse_quotes(&content[command_length..]) + } else { + content[command_length..] + .split_whitespace() + .map(|arg| arg.to_owned()) + .collect::<Vec<String>>() + } + }; + + if let Some(error) = self.should_fail(&mut context, &message, &command, args.len(), &to_check, &built) { + if let Some(ref handler) = self.dispatch_error_handler { + handler(context, message, error); + } + return; + } + + tokio_handle.spawn_fn(move || { + if let Some(before) = before { + if !(before)(&mut context, &message, &built) { + return Ok(()); + } + } + + let result = match command.exec { + CommandType::StringResponse(ref x) => { + let _ = message.channel_id.say(x); + + Ok(()) + }, + CommandType::Basic(ref x) => { + (x)(&mut context, &message, args) + }, + CommandType::WithCommands(ref x) => { + (x)(&mut context, &message, groups, &args) + } + }; + + if let Some(after) = after { + (after)(&mut context, &message, &built, result); + } + + Ok(()) + }); + + return; + } + } + } + } + } + + #[cfg(feature="builtin_framework")] + fn update_current_user(&mut self, user_id: UserId, is_bot: bool) { self.user_info = (user_id.0, is_bot); } -} + + #[cfg(feature="builtin_framework")] + fn initialized(&self) -> bool { + self.initialized + } +}
\ No newline at end of file |