aboutsummaryrefslogtreecommitdiff
path: root/src/framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/framework')
-rw-r--r--src/framework/configuration.rs30
-rw-r--r--src/framework/help_commands.rs9
-rw-r--r--src/framework/mod.rs384
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