diff options
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | examples/06_command_framework/src/main.rs | 6 | ||||
| -rw-r--r-- | examples/08_search/src/main.rs | 15 | ||||
| -rw-r--r-- | src/client/context.rs | 39 | ||||
| -rw-r--r-- | src/ext/framework/command.rs | 16 | ||||
| -rw-r--r-- | src/ext/framework/configuration.rs | 2 | ||||
| -rw-r--r-- | src/ext/framework/create_command.rs | 10 | ||||
| -rw-r--r-- | src/ext/framework/create_group.rs | 4 | ||||
| -rw-r--r-- | src/ext/framework/help_commands.rs | 6 | ||||
| -rw-r--r-- | src/ext/framework/mod.rs | 38 | ||||
| -rw-r--r-- | src/utils/builder/search.rs | 11 | ||||
| -rw-r--r-- | tests/test_create_embed.rs | 22 |
12 files changed, 106 insertions, 64 deletions
@@ -51,4 +51,5 @@ cache = [] debug = [] framework = [] methods = [] +extras = [] voice = ["opus", "sodiumoxide"] diff --git a/examples/06_command_framework/src/main.rs b/examples/06_command_framework/src/main.rs index 4aeafe1..35a6994 100644 --- a/examples/06_command_framework/src/main.rs +++ b/examples/06_command_framework/src/main.rs @@ -81,9 +81,9 @@ fn main() { let entry = counter.entry(command_name.clone()).or_insert(0); *entry += 1; - true + true // if `before` returns false, command processing doesn't happen. }) - // Very similar to `before`, except this will be called directly _after_ + // Similar to `before`, except will be called directly _after_ // command execution. .after(|_, _, command_name, error| { match error { @@ -153,7 +153,7 @@ command!(commands(context, _msg, _args) { // In this case, this command checks to ensure you are the owner of the message // in order for the command to be executed. If the check fails, the command is // not called. -fn owner_check(_: &Context, message: &Message) -> bool { +fn owner_check(_: &mut Context, message: &Message) -> bool { // Replace 7 with your ID message.author.id == 7 } diff --git a/examples/08_search/src/main.rs b/examples/08_search/src/main.rs index 53d194f..4fe0a0e 100644 --- a/examples/08_search/src/main.rs +++ b/examples/08_search/src/main.rs @@ -13,6 +13,7 @@ //! This particular example will automatically ensure that only the current user //! may search; this acts as a "selfbot". +#[macro_use] extern crate serenity; use serenity::client::{CACHE, Client, Context}; @@ -37,17 +38,17 @@ fn main() { } } -fn self_check(_context: &Context, message: &Message) -> bool { +fn self_check(_context: &mut Context, message: &Message) -> bool { message.author.id == CACHE.read().unwrap().user.id } -fn search(context: &Context, message: &Message, args: Vec<String>) { +command!(search(context, message, args) { let query = args.join(" "); if query.is_empty() { let _ = context.say("You must provide a query"); - return; + return Ok(()); } let guild_id = match message.guild_id() { @@ -55,7 +56,7 @@ fn search(context: &Context, message: &Message, args: Vec<String>) { None => { let _ = context.say("Only supports guilds"); - return; + return Ok(()); }, }; @@ -67,7 +68,7 @@ fn search(context: &Context, message: &Message, args: Vec<String>) { None => { let _ = context.say("Guild data not found"); - return; + return Ok(()); }, }; @@ -94,7 +95,7 @@ fn search(context: &Context, message: &Message, args: Vec<String>) { let _ = context.say("Error occurred while searching"); - return; + return Ok(()); }, }; @@ -112,4 +113,4 @@ fn search(context: &Context, message: &Message, args: Vec<String>) { e })); -} +}); diff --git a/src/client/context.rs b/src/client/context.rs index d6d9ef9..ccf05fc 100644 --- a/src/client/context.rs +++ b/src/client/context.rs @@ -23,6 +23,9 @@ use ::internal::prelude::*; use ::model::*; use ::utils; +#[cfg(feature="extras")] +use std::ops::ShlAssign; + #[cfg(feature="cache")] use super::CACHE; @@ -94,6 +97,8 @@ pub struct Context { /// Note that if you are sharding, in relevant terms, this is the shard /// which received the event being dispatched. pub shard: Arc<Mutex<Shard>>, + /// The queue of messages that are sent after context goes out of scope. + pub queue: String, login_type: LoginType, } @@ -115,6 +120,7 @@ impl Context { data: data, shard: shard, login_type: login_type, + queue: String::new(), } } @@ -903,7 +909,7 @@ impl Context { /// Change the current user's username: /// /// ```rust,ignore - /// context.edit_profile(|p| p.username("meew0")); + /// context.edit_profile(|p| p.username("Hakase")); /// ``` pub fn edit_profile<F: FnOnce(EditProfile) -> EditProfile>(&self, f: F) -> Result<CurrentUser> { @@ -1445,6 +1451,20 @@ impl Context { } } + /// Adds a string to message queue, which is sent joined by a newline + /// when context goes out of scope. + /// + /// **Note**: Only works in a context where a channel is present. Refer to + /// [`say`] for a list of events where this is applicable. + /// + /// [`say`]: #method.say + pub fn queue(&mut self, content: &str) -> &mut Self { + self.queue.push('\n'); + self.queue.push_str(content); + + self + } + /// Searches a [`Channel`]'s messages by providing query parameters via the /// search builder. /// @@ -1880,3 +1900,20 @@ impl Context { rest::unpin_message(channel_id.into().0, message_id.into().0) } } + +impl Drop for Context { + /// Combines and sends all queued messages. + fn drop(&mut self) { + if !self.queue.is_empty() { + let _ = self.say(&self.queue); + } + } +} + +/// Allows the `<<=` operator to be used to queue messages. +#[cfg(feature="extras")] +impl<'a> ShlAssign<&'a str> for &'a mut Context { + fn shl_assign(&mut self, rhs: &str) { + self.queue(rhs); + } +} diff --git a/src/ext/framework/command.rs b/src/ext/framework/command.rs index 92e6e93..70ee4ff 100644 --- a/src/ext/framework/command.rs +++ b/src/ext/framework/command.rs @@ -5,14 +5,14 @@ use ::model::Message; use ::model::Permissions; use std::collections::HashMap; -pub type Check = Fn(&Context, &Message) -> bool + Send + Sync + 'static; -pub type Exec = Fn(&Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static; -pub type Help = Fn(&Context, &Message, HashMap<String, Arc<CommandGroup>>, Vec<String>) -> Result<(), String> + Send + Sync + 'static; -pub type BeforeHook = Fn(&Context, &Message, &String) -> bool + Send + Sync + 'static; -pub type AfterHook = Fn(&Context, &Message, &String, Result<(), String>) + Send + Sync + 'static; +pub type Check = Fn(&mut Context, &Message) -> bool + Send + Sync + 'static; +pub type Exec = Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static; +pub type Help = Fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Vec<String>) -> Result<(), String> + Send + Sync + 'static; +pub type BeforeHook = Fn(&mut Context, &Message, &String) -> bool + Send + Sync + 'static; +pub type AfterHook = Fn(&mut Context, &Message, &String, Result<(), String>) + Send + Sync + 'static; #[doc(hidden)] pub type InternalCommand = Arc<Command>; -pub type PrefixCheck = Fn(&Context) -> Option<String> + Send + Sync + 'static; +pub type PrefixCheck = Fn(&mut Context) -> Option<String> + Send + Sync + 'static; #[doc(hidden)] pub enum CommandOrAlias { @@ -71,7 +71,7 @@ pub struct Command { impl Command { pub fn new<F>(f: F) -> Self - where F: Fn(&Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static { + where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static { Command { aliases: Vec::new(), checks: Vec::default(), @@ -92,7 +92,7 @@ impl Command { } } -pub fn positions(ctx: &Context, content: &str, conf: &Configuration) -> Option<Vec<usize>> { +pub fn positions(ctx: &mut Context, content: &str, conf: &Configuration) -> Option<Vec<usize>> { if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() { // Find out if they were mentioned. If not, determine if the prefix // was used. If not, return None. diff --git a/src/ext/framework/configuration.rs b/src/ext/framework/configuration.rs index d4ebb3e..611775a 100644 --- a/src/ext/framework/configuration.rs +++ b/src/ext/framework/configuration.rs @@ -177,7 +177,7 @@ impl Configuration { /// Sets the prefix to respond to. This can either be a single- or /// multi-char string. pub fn dynamic_prefix<F>(mut self, dynamic_prefix: F) -> Self - where F: Fn(&Context) -> Option<String> + Send + Sync + 'static { + where F: Fn(&mut Context) -> Option<String> + Send + Sync + 'static { self.dynamic_prefix = Some(Box::new(dynamic_prefix)); self diff --git a/src/ext/framework/create_command.rs b/src/ext/framework/create_command.rs index c758f4a..4d1e1a5 100644 --- a/src/ext/framework/create_command.rs +++ b/src/ext/framework/create_command.rs @@ -54,19 +54,19 @@ impl CreateCommand { /// .desc("Replies to a ping with a pong") /// .exec(ping))); /// - /// fn ping(context: &Context, _message: &Message, _args: Vec<String>) -> Result<(), String> { + /// fn ping(context: &mut Context, _message: &Message, _args: Vec<String>) -> Result<(), String> { /// let _ = context.say("Pong!"); /// /// Ok(()) /// } /// - /// fn owner_check(_context: &Context, message: &Message) -> bool { + /// fn owner_check(_context: &mut Context, message: &Message) -> bool { /// // replace with your user ID /// message.author.id == 7 /// } /// ``` pub fn check<F>(mut self, check: F) -> Self - where F: Fn(&Context, &Message) -> bool + Send + Sync + 'static { + where F: Fn(&mut Context, &Message) -> bool + Send + Sync + 'static { self.0.checks.push(Box::new(check)); self @@ -100,7 +100,7 @@ impl CreateCommand { /// /// [`exec_str`]: #method.exec_str pub fn exec<F>(mut self, func: F) -> Self - where F: Fn(&Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static { + where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static { self.0.exec = CommandType::Basic(Box::new(func)); self @@ -112,7 +112,7 @@ impl CreateCommand { /// /// You can return Err(string) if there's an error. pub fn exec_help<F>(mut self, f: F) -> Self - where F: Fn(&Context, &Message, HashMap<String, Arc<CommandGroup>>, Vec<String>) -> Result<(), String> + Send + Sync + 'static { + where F: Fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Vec<String>) -> Result<(), String> + Send + Sync + 'static { self.0.exec = CommandType::WithCommands(Box::new(f)); self diff --git a/src/ext/framework/create_group.rs b/src/ext/framework/create_group.rs index 465224d..03fc33e 100644 --- a/src/ext/framework/create_group.rs +++ b/src/ext/framework/create_group.rs @@ -21,7 +21,7 @@ pub struct CreateGroup(pub CommandGroup); /// framework.group("Information", |g| g /// .prefix("info") /// .command("name", |c| c -/// .exec_str("meew0"))) +/// .exec_str("Hakase"))) /// ``` impl CreateGroup { /// Adds a command to group. @@ -45,7 +45,7 @@ impl CreateGroup { /// Adds a command to group with simplified API. /// You can return Err(string) if there's an error. pub fn on<F>(mut self, command_name: &str, f: F) -> Self - where F: Fn(&Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static { + where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static { let cmd = Arc::new(Command::new(f)); self.0.commands.insert(command_name.to_owned(), CommandOrAlias::Command(cmd)); diff --git a/src/ext/framework/help_commands.rs b/src/ext/framework/help_commands.rs index 230d78a..119e362 100644 --- a/src/ext/framework/help_commands.rs +++ b/src/ext/framework/help_commands.rs @@ -7,7 +7,7 @@ use ::client::Context; use ::model::Message; use ::utils::Colour; -fn error_embed(ctx: &Context, message: &Message, input: &str) { +fn error_embed(ctx: &mut Context, message: &Message, input: &str) { let _ = ctx.send_message(message.channel_id, |m| m .embed(|e| e .colour(Colour::dark_red()) @@ -26,7 +26,7 @@ fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &I result } -pub fn with_embeds(ctx: &Context, +pub fn with_embeds(ctx: &mut Context, message: &Message, groups: HashMap<String, Arc<CommandGroup>>, args: Vec<String>) -> Result<(), String> { @@ -154,7 +154,7 @@ pub fn with_embeds(ctx: &Context, Ok(()) } -pub fn plain(ctx: &Context, +pub fn plain(ctx: &mut Context, _: &Message, groups: HashMap<String, Arc<CommandGroup>>, args: Vec<String>) -> Result<(), String> { diff --git a/src/ext/framework/mod.rs b/src/ext/framework/mod.rs index f1d5020..77c874f 100644 --- a/src/ext/framework/mod.rs +++ b/src/ext/framework/mod.rs @@ -113,28 +113,32 @@ use ::client::CACHE; #[macro_export] macro_rules! command { ($fname:ident($c:ident) $b:block) => { - pub fn $fname($c: &$crate::client::Context, _: &$crate::model::Message, _: Vec<String>) -> ::std::result::Result<(), String> { + #[allow(unused_mut)] + pub fn $fname(mut $c: &mut $crate::client::Context, _: &$crate::model::Message, _: Vec<String>) -> ::std::result::Result<(), String> { $b Ok(()) } }; ($fname:ident($c:ident, $m:ident) $b:block) => { - pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, _: Vec<String>) -> ::std::result::Result<(), String> { + #[allow(unused_mut)] + pub fn $fname(mut $c: &mut $crate::client::Context, $m: &$crate::model::Message, _: Vec<String>) -> ::std::result::Result<(), String> { $b Ok(()) } }; ($fname:ident($c:ident, $m:ident, $a:ident) $b:block) => { - pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> ::std::result::Result<(), String> { + #[allow(unused_mut)] + pub fn $fname(mut $c: &mut $crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> ::std::result::Result<(), String> { $b Ok(()) } }; ($fname:ident($c:ident, $m:ident, $a:ident, $($name:ident: $t:ty),*) $b:block) => { - pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> ::std::result::Result<(), String> { + #[allow(unused_mut)] + pub fn $fname(mut $c: &mut $crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> ::std::result::Result<(), String> { let mut i = $a.iter(); let mut arg_counter = 0; @@ -260,7 +264,7 @@ impl Framework { #[allow(cyclomatic_complexity)] #[doc(hidden)] - pub fn dispatch(&mut self, context: Context, message: Message) { + pub fn dispatch(&mut self, mut context: Context, message: Message) { match self.configuration.account_type { AccountType::Selfbot => { if message.author.id != self.user_info.0 { @@ -281,7 +285,7 @@ impl Framework { }, AccountType::Any => {} } - let res = command::positions(&context, &message.content, &self.configuration); + let res = command::positions(&mut context, &message.content, &self.configuration); let positions = match res { Some(mut positions) => { @@ -437,7 +441,7 @@ impl Framework { } for check in &command.checks { - if !(check)(&context, &message) { + if !(check)(&mut context, &message) { if let Some(ref message) = self.configuration.invalid_check_message { let _ = context.say(message); } @@ -516,27 +520,27 @@ impl Framework { thread::spawn(move || { if let Some(before) = before { - if !(before)(&context, &message, &built) && !is_owner { + if !(before)(&mut context, &message, &built) && !is_owner { return; } } let result = match command.exec { CommandType::StringResponse(ref x) => { - let _ = &context.say(x); + let _ = &mut context.say(x); Ok(()) }, CommandType::Basic(ref x) => { - (x)(&context, &message, args) + (x)(&mut context, &message, args) }, CommandType::WithCommands(ref x) => { - (x)(&context, &message, groups, args) + (x)(&mut context, &message, groups, args) } }; if let Some(after) = after { - (after)(&context, &message, &built, result); + (after)(&mut context, &message, &built, result); } }); @@ -563,7 +567,7 @@ impl Framework { /// [`command`]: #method.command /// [module-level documentation]: index.html pub fn on<F, S>(mut self, command_name: S, f: F) -> Self - where F: Fn(&Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static, + where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static, S: Into<String> { { let ungrouped = self.groups.entry("Ungrouped".to_owned()) @@ -636,7 +640,7 @@ impl Framework { /// Specify the function to be called prior to every command's execution. /// If that function returns true, the command will be executed. pub fn before<F>(mut self, f: F) -> Self - where F: Fn(&Context, &Message, &String) -> bool + Send + Sync + 'static { + where F: Fn(&mut Context, &Message, &String) -> bool + Send + Sync + 'static { self.before = Some(Arc::new(f)); self @@ -645,7 +649,7 @@ impl Framework { /// Specify the function to be called after every command's execution. /// Fourth argument exists if command returned an error which you can handle. pub fn after<F>(mut self, f: F) -> Self - where F: Fn(&Context, &Message, &String, Result<(), String>) + Send + Sync + 'static { + where F: Fn(&mut Context, &Message, &String, Result<(), String>) + Send + Sync + 'static { self.after = Some(Arc::new(f)); self @@ -675,14 +679,14 @@ impl Framework { /// let _ = context.say("Pong!"); /// }); /// - /// fn owner_check(_context: &Context, message: &Message) -> bool { + /// fn owner_check(_context: &mut Context, message: &Message) -> bool { /// // replace with your user ID /// message.author.id == 7 /// } /// ``` #[deprecated(since="0.1.2", note="Use the `CreateCommand` builder's `check` instead.")] pub fn set_check<F, S>(mut self, command: S, check: F) -> Self - where F: Fn(&Context, &Message) -> bool + Send + Sync + 'static, + where F: Fn(&mut Context, &Message) -> bool + Send + Sync + 'static, S: Into<String> { { let ungrouped = self.groups.entry("Ungrouped".to_owned()) diff --git a/src/utils/builder/search.rs b/src/utils/builder/search.rs index 5b54157..cf61bed 100644 --- a/src/utils/builder/search.rs +++ b/src/utils/builder/search.rs @@ -108,7 +108,7 @@ impl SortingOrder { /// limiting to 2 results, and only searching channels that have a name /// prefixed with `"search-"`: /// -/// ```rust,no_run +/// ```rust,ignore /// use serenity::client::{Client, Context}; /// use serenity::model::Message; /// use serenity::utils::builder::{SortingMode, SortingOrder}; @@ -120,7 +120,7 @@ impl SortingOrder { /// .configure(|c| c.prefix("~").on_mention(true)) /// .on("search", search)); /// -/// fn search(context: &Context, message: &Message, args: Vec<String>) -> Result<(), String> { +/// command!(search(context, message, args) { /// let query = args.join(" "); /// /// if query.is_empty() { @@ -131,7 +131,8 @@ impl SortingOrder { /// /// let guild = message.guild().unwrap(); /// -/// let channel_ids = guild.channels +/// let channel_ids = guild +/// .channels /// .values() /// .filter(|c| c.name.starts_with("search-")) /// .map(|c| c.id) @@ -175,9 +176,7 @@ impl SortingOrder { /// /// e /// })); -/// -/// Ok(()) -/// } +/// }); /// ``` /// /// [`Channel`]: ../../model/enum.Channel.html diff --git a/tests/test_create_embed.rs b/tests/test_create_embed.rs index be9b82e..e76c907 100644 --- a/tests/test_create_embed.rs +++ b/tests/test_create_embed.rs @@ -28,30 +28,30 @@ fn test_from_embed() { image: Some(EmbedImage { height: 213, proxy_url: "a".to_owned(), - url: "https://i.imgur.com/q9MqLqZ.png".to_owned(), + url: "https://i.imgur.com/XfWpfCV.gif".to_owned(), width: 224, }), kind: "rich".to_owned(), provider: None, thumbnail: None, timestamp: None, - title: Some("funny cat meme".to_owned()), - url: Some("https://i.imgur.com/q9MqLqZ.png".to_owned()), + title: Some("hakase".to_owned()), + url: Some("https://i.imgur.com/XfWpfCV.gif".to_owned()), video: None, }; let builder = CreateEmbed::from(embed) .colour(0xFF0000) - .description("This is a cat description") - .image("https://i.imgur.com/q9MqLqZ.jpg") - .title("still a funny cat meme") - .url("https://i.imgur.com/q9MqLqZ.jpg"); + .description("This is a hakase description") + .image("https://i.imgur.com/XfWpfCV.gif") + .title("still a hakase") + .url("https://i.imgur.com/XfWpfCV.gif"); let built = Value::Object(builder.0); let obj = ObjectBuilder::new() .insert("color", 0xFF0000) - .insert("description", "This is a cat description") + .insert("description", "This is a hakase description") .insert_array("fields", |a| a .push_object(|o| o .insert("inline", false) @@ -62,10 +62,10 @@ fn test_from_embed() { .insert("name", "c") .insert("value", "z"))) .insert_object("image", |o| o - .insert("url", "https://i.imgur.com/q9MqLqZ.jpg")) - .insert("title", "still a funny cat meme") + .insert("url", "https://i.imgur.com/XfWpfCV.gif")) + .insert("title", "still a hakase") .insert("type", "rich") - .insert("url", "https://i.imgur.com/q9MqLqZ.jpg") + .insert("url", "https://i.imgur.com/XfWpfCV.gif") .build(); assert_eq!(built, obj); |