diff options
| author | Austin Hellyer <[email protected]> | 2016-11-10 20:25:32 -0700 |
|---|---|---|
| committer | Austin Hellyer <[email protected]> | 2016-12-29 11:55:10 -0800 |
| commit | 0359f512a8aada5ae0371049eb7c66ecd8d68d84 (patch) | |
| tree | f88dd9b362a2d349d0cdcd13b0859c4cc3329f46 /src/utils | |
| parent | Rework some event handles (diff) | |
| download | serenity-0359f512a8aada5ae0371049eb7c66ecd8d68d84.tar.xz serenity-0359f512a8aada5ae0371049eb7c66ecd8d68d84.zip | |
Add guild and channel search
Diffstat (limited to 'src/utils')
| -rw-r--r-- | src/utils/builder/mod.rs | 2 | ||||
| -rw-r--r-- | src/utils/builder/search.rs | 379 |
2 files changed, 381 insertions, 0 deletions
diff --git a/src/utils/builder/mod.rs b/src/utils/builder/mod.rs index 3fc0f05..9ded8b8 100644 --- a/src/utils/builder/mod.rs +++ b/src/utils/builder/mod.rs @@ -15,6 +15,7 @@ mod edit_profile; mod edit_role; mod execute_webhook; mod get_messages; +mod search; pub use self::create_embed::{ CreateEmbed, @@ -31,3 +32,4 @@ pub use self::edit_profile::EditProfile; pub use self::edit_role::EditRole; pub use self::execute_webhook::ExecuteWebhook; pub use self::get_messages::GetMessages; +pub use self::search::{Search, SortingMode, SortingOrder}; diff --git a/src/utils/builder/search.rs b/src/utils/builder/search.rs new file mode 100644 index 0000000..1aadec4 --- /dev/null +++ b/src/utils/builder/search.rs @@ -0,0 +1,379 @@ +use std::collections::BTreeMap; +use ::model::{MessageId, UserId}; + +/// An indicator of the type of sorting mode to use when searching for +/// [`Message`]s via the [`Search`] builder. +/// +/// [`Message`]: ../../model/struct.Message.html +/// [`Search`]: struct.Search.html +pub enum SortingMode { + /// Search by messages' relevance to parameters. + Relevance, + /// Search by messages' timestamp, where results will match according to + /// parameters. This is used in conjunction in the [`Search::sort_order`] + /// method, and is used in conjunction with [`SortingOrder`]. + /// + /// [`Search::sort_order`]: struct.Search.html#method.sort_order + /// [`SortingOrder`]: enum.SortingOrder.html + Timestamp, +} + +impl SortingMode { + /// Retrieves the name of the sorting mode. This is equivilant to a + /// lowercase string version of each variant. + pub fn name(&self) -> &str { + match *self { + SortingMode::Relevance => "relevance", + SortingMode::Timestamp => "timestamp", + } + } +} + +/// An indicator of how to sort results when searching for [`Message`]s via the +/// [`Search`] builder. +/// +/// [`Message`]: ../../model/struct.Message.html +/// [`Search`]: struct.Search.html +pub enum SortingOrder { + /// Search message results in ascending order. + /// + /// In the case of [`SortingMode::Relevance`], this will search from the + /// least relevant to the most relevant. + /// + /// In the case of [`SortingMode::Timestamp`], this will indicate to search + /// from the least recent to the most recent. + /// + /// [`SortingMode::Relevance`]: enum.SortingMode.html#variant.Relevance + /// [`SortingMode::Timestamp`]: enum.SortingMode.html#variant.Timestamp + Ascending, + /// Search message results in descending order. + /// + /// In the case of [`SortingMode::Relevance`], this will search from the + /// most relevant to least relevant. + /// + /// In the case of [`SortingMode::Timestamp`], this will search from the + /// most recent to least recent. + /// + /// [`SortingMode::Relevance`]: enum.SortingMode.html#variant.Relevance + /// [`SortingMode::Timestamp`]: enum.SortingMode.html#variant.Timestamp + Descending, +} + +impl SortingOrder { + /// Retrieves the name of the sorting order. This is equivilant to a + /// lowercase string version of each variant. + pub fn name(&self) -> &str { + match *self { + SortingOrder::Ascending => "asc", + SortingOrder::Descending => "desc", + } + } +} + +/// A builder used to query a [`Channel`] or [`Guild`] for its [`Message`]s, +/// specifying certain parameters to narrow down the returned messages. +/// +/// Many methods are provided to narrow down the results, such as [`sort_by`] - +/// which is used with the [`SortingMode`] enum to sort the results - or +/// [`limit`], which can be used in conjunction with [`offset`] to paginate +/// results. +/// +/// # Examples +/// +/// Provided are multiple in-depth examples for searching through different +/// means. Also see [example 08] for a fully runnable bot. +/// +/// ### Searching a Channel +/// +/// Search for messages via [`Context::search_channel`] with the content +/// `"rust"`, which have no embed, no attachment, searching by relevance in +/// ascending order, and limiting to 5 results: +/// +/// ```rust,ignore +/// // assuming you are in a context +/// +/// let res = context.search_channel(message.channel_id, |s| s +/// .content("rust") +/// .has_embed(false) +/// .has_attachment(false) +/// .limit(5) +/// .sort_by(SortingMode::Relevance) +/// .sort_order(SortingOrder::Ascending)); +/// ``` +/// +/// ### Searching a Guild's Channels +/// +/// Search for messages with a query provided by a user, which have an +/// embed, have no attachment, searching by timestamp in descending order, +/// limiting to 2 results, and only searching channels that have a name +/// prefixed with `"search-"`: +/// +/// ```rust,no_run +/// use serenity::client::{Client, Context}; +/// use serenity::model::Message; +/// use serenity::utils::builder::{SortingMode, SortingOrder}; +/// use std::env; +/// +/// let client = Client::login_bot(&env::var("DISCORD_BOT_TOKEN").unwrap()); +/// +/// client.with_framework(|f| f +/// .configure(|c| c.prefix("~").on_mention(true)) +/// .on("search", search)); +/// +/// fn search(context: Context, message: Message, args: Vec<String>) { +/// let query = args.join(" "); +/// +/// if query.is_empty() { +/// let _ = context.say("You must provide a query"); +/// +/// return; +/// } +/// +/// let guild = match message.guild().unwrap(); +/// +/// let channel_ids = guild.iter() +/// .channels +/// .values() +/// .filter(|c| c.name.starts_with("search-")) +/// .map(|c| c.id) +/// .collect(); +/// +/// let search = context.search_guild(guild.id, channel_ids, |s| s +/// .content(&query) +/// .context_size(0) +/// .has_attachment(true) +/// .has_embed(true) +/// .max_id(message.id.0 - 1) +/// .sort_by(SortingMode::Timestamp) +/// .sort_order(SortingOrder::Descending)); +/// +/// let messages = match search { +/// Ok(messages) => messages, +/// Err(why) => { +/// println!("Error performing search '{}': {:?}", query, why); +/// +/// let _ = context.say("Error occurred while searching"); +/// +/// return; +/// }, +/// }; +/// +/// let _ = context.send_message(message.channel_id, |m| m +/// .content(&format!("Found {} total results", messages.total)) +/// .embed(|e| { +/// for (i, messages) in messages.results.iter().enumerate() { +/// let mut message = messages[0]; +/// message.content.truncate(1000); +/// +/// e.field(|f| f +/// .name(&format!("Result {}", i)) +/// .value(&message.content)); +/// } +/// +/// e +/// })); +/// } +/// ``` +/// +/// [`Channel`]: ../../model/enum.Channel.html +/// [`Context::search_channel`]: ../../client/struct.Context.html#method.search_channel +/// [`Guild`]: ../../model/struct.Guild.html +/// [`Message`]: ../../model/struct.Message.html +/// [`SortingMode`]: enum.SortingMode.html +/// [`limit`]: #method.limit +/// [`offset`]: #method.offset +/// [`sort_by`]: #method.sort_by +/// [example 08]: https://github.com/zeyla/serenity.rs/tree/master/examples/08_search +pub struct Search<'a>(pub BTreeMap<&'a str, String>); + +impl<'a> Search<'a> { + /// Sets the list of attachment extensions to search by. + /// + /// When providing a vector of extensions, do _not_ include the period (`.`) + /// character as part of the search. + /// + /// This is sent to Discord as a comma-separated value list of extension + /// names. + pub fn attachment_extensions(mut self, attachment_extensions: &[&str]) -> Self { + let list = attachment_extensions.join(" "); + + self.0.insert("attachment_extensions", list); + + self + } + + /// Sets the filename of the attachments to search for. + pub fn attachment_filename(mut self, attachment_filename: &str) -> Self { + self.0.insert("attachment_filename", attachment_filename.to_owned()); + + self + } + + /// Sets the Id of the author of [`Message`]s to search for. This excludes + /// all messages by other [`User`]s. + /// + /// [`Message`]: ../../model/struct.Message.html + /// [`User`]: ../../model/struct.User.html + pub fn author_id<U: Into<UserId>>(mut self, author_id: U) -> Self { + self.0.insert("author_id", author_id.into().0.to_string()); + + self + } + + /// Sets the content of the [`Message`] to search for. This is a fuzzy + /// search, and can partially match the given query content. + /// + /// [`Message`]: ../../model/struct.Message.html + pub fn content(mut self, content: &str) -> Self { + self.0.insert("content", content.to_owned()); + + self + } + + /// Sets the amount of "context" [`Message`]s to provide, at maximum. This + /// is the number of messages to provide around each side + /// (ascending+descending) of the "hit" (aka found) message. + /// + /// The default value is `2`. The minimum value is `0`. The maximum value is + /// `2`. + pub fn context_size(mut self, context_size: u8) -> Self { + self.0.insert("context_size", context_size.to_string()); + + self + } + + /// Sets the embed providers to search by. + /// + /// This is a list of the providers' names. + /// + /// This is sent to Discord as a comma-separated value list of provider + /// names. + pub fn embed_providers(mut self, embed_providers: &[&str]) -> Self { + self.0.insert("embed_providers", embed_providers.join(" ")); + + self + } + + /// Sets the type of [`Embed`]s to search by. + /// + /// An example of an [embed type][`Embed::kind`] is `"rich"`. + /// + /// [`Embed`]: ../../model/struct.Embed.html + /// [`Embed::kind`]: ../../model/struct.Embed.html#structfield.kind + pub fn embed_types(mut self, embed_types: &[&str]) -> Self { + self.0.insert("embed_types", embed_types.join(" ")); + + self + } + + /// Sets whether to search for methods that do - or do not - have an + /// attachment. + /// + /// Do not specify to search for both. + pub fn has_attachment(mut self, has_attachment: bool) -> Self { + self.0.insert("has_attachment", has_attachment.to_string()); + + self + } + + /// Sets whether to search for methods that do - or do not - have an embed. + /// + /// Do not specify to search for both. + pub fn has_embed(mut self, has_embed: bool) -> Self { + self.0.insert("has_embed", has_embed.to_string()); + + self + } + + /// Sets the number of messages to retrieve _at maximum_. This can be used + /// in conjunction with [`offset`]. + /// + /// The minimum value is `1`. The maximum value is `25`. + /// + /// [`offset`]: #method.offset + pub fn limit(mut self, limit: u8) -> Self { + self.0.insert("limit", limit.to_string()); + + self + } + + /// Set the maximum [`Message`] Id to search up to. All messages with an Id + /// greater than the given value will be ignored. + /// + /// [`Message`]: ../../model/struct.Message.html + pub fn max_id<M: Into<MessageId>>(mut self, message_id: M) -> Self { + self.0.insert("max_id", message_id.into().0.to_string()); + + self + } + + /// Set the minimum [`Message`]s Id to search down to. All messages with an + /// Id less than the given value will be ignored. + /// + /// [`Message`]: ../../model/struct.Message.html + pub fn min_id<M: Into<MessageId>>(mut self, message_id: M) -> Self { + self.0.insert("min_id", message_id.into().0.to_string()); + + self + } + + /// Set the offset of [`Message`]s to return. This can be used in + /// conjunction with [`limit`]. + /// + /// The minimum value is `0`. The maximum value is `5000`. + /// + /// [`Message`]: ../../model/struct.Message.html + /// [`limit`]: fn.limit.html + pub fn offset(mut self, offset: u16) -> Self { + self.0.insert("offset", offset.to_string()); + + self + } + + /// The sorting mode to use. + /// + /// Refer to [`SortingMode`] for more information. + /// + /// [`SortingMode`]: enum.SortingMode.html + pub fn sort_by(mut self, sorting_mode: SortingMode) -> Self { + self.0.insert("sort_by", sorting_mode.name().to_string()); + + self + } + + /// The order to sort results by. + /// + /// Refer to the documentation for [`SortingOrder`] for more information. + /// + /// [`SortingOrder`]: enum.SortingOrder.html + pub fn sort_order(mut self, sorting_order: SortingOrder) -> Self { + self.0.insert("sort_order", sorting_order.name().to_string()); + + self + } +} + +impl<'a> Default for Search<'a> { + /// Creates a new builder for searching for [`Message`]s. Refer to each + /// method to learn what minimum and maximum values are available for each + /// field, as well as restrictions and other useful information. + /// + /// The library does not provide defaults differently than what Discord + /// itself defaults to. + /// + /// This list of defaults is: + /// + /// - [`context_size`]: 2 + /// - [`limit`]: 25 + /// - [`offset`]: 0 + /// - [`sort_by`]: [`SortingMode::Timestamp`] + /// + /// [`SortingMode::Timestamp`]: enum.SortingMode.html#variant.Timestamp + /// [`context_size`]: #method.context_size + /// [`limit`]: #method.limit + /// [`offset`]: #method.offset + /// [`sort_by`]: #method.sort_by + fn default<'b>() -> Search<'b> { + Search(BTreeMap::default()) + } +} |