aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAustin Hellyer <[email protected]>2016-11-26 11:37:18 -0800
committerAustin Hellyer <[email protected]>2016-11-26 11:37:18 -0800
commit77354ab321bec1ff66af0e27eb87a7eec3e3db24 (patch)
tree693b43ae7be07be11426faf6e6282d838e426a04 /src
parentMake Cache::get_channel return a reference (diff)
downloadserenity-77354ab321bec1ff66af0e27eb87a7eec3e3db24.tar.xz
serenity-77354ab321bec1ff66af0e27eb87a7eec3e3db24.zip
Add a bit more docs
Diffstat (limited to 'src')
-rw-r--r--src/client/gateway/error.rs6
-rw-r--r--src/client/gateway/shard.rs48
-rw-r--r--src/client/mod.rs21
-rw-r--r--src/ext/cache/mod.rs289
-rw-r--r--src/ext/framework/command.rs1
-rw-r--r--src/ext/framework/configuration.rs22
-rw-r--r--src/ext/framework/mod.rs99
-rw-r--r--src/ext/mod.rs6
-rw-r--r--src/lib.rs8
-rw-r--r--src/model/gateway.rs1
-rw-r--r--src/utils/builder/edit_guild.rs113
-rw-r--r--src/utils/builder/edit_profile.rs2
-rw-r--r--src/utils/message_builder.rs1
13 files changed, 592 insertions, 25 deletions
diff --git a/src/client/gateway/error.rs b/src/client/gateway/error.rs
index fb44d3f..dc94209 100644
--- a/src/client/gateway/error.rs
+++ b/src/client/gateway/error.rs
@@ -1,8 +1,12 @@
use std::fmt::{self, Display};
+/// An error that occurred while attempting to deal with the gateway.
+///
+/// Note that - from a user standpoint - there should be no situation in which
+/// you manually handle these.
#[derive(Clone, Debug)]
pub enum Error {
- /// The connection closed
+ /// The connection unexpectedly (read: non-cleanly) closed.
Closed(Option<u16>, String),
/// Expected a Hello during a handshake
ExpectedHello,
diff --git a/src/client/gateway/shard.rs b/src/client/gateway/shard.rs
index c35fee2..ab2e561 100644
--- a/src/client/gateway/shard.rs
+++ b/src/client/gateway/shard.rs
@@ -32,6 +32,10 @@ type CurrentPresence = (Option<Game>, OnlineStatus, bool);
/// Refer to the [module-level documentation][module docs] for information on
/// effectively using multiple shards, if you need to.
///
+/// Note that there are additional methods available if you are manually
+/// managing a shard yourself, although they are hidden from the documentation
+/// since there are few use cases for doing such.
+///
/// # Stand-alone shards
///
/// You may instantiate a shard yourself - decoupled from the [`Client`] - if
@@ -63,6 +67,8 @@ pub struct Shard {
shard_info: Option<[u8; 2]>,
token: String,
ws_url: String,
+ /// The voice connections that this Shard is responsible for. The Shard will
+ /// update the voice connections' states.
#[cfg(feature = "voice")]
pub manager: VoiceManager,
}
@@ -159,6 +165,13 @@ impl Shard {
}}, ready, receiver))
}
+ /// Retrieves a copy of the current shard information.
+ ///
+ /// The first element is the _current_ shard - 0-indexed - while the second
+ /// element is the _total number_ of shards -- 1-indexed.
+ ///
+ /// For example, if using 3 shards in total, and if this is shard 1, then it
+ /// can be read as "the second of three shards".
pub fn shard_info(&self) -> Option<[u8; 2]> {
self.shard_info
}
@@ -188,6 +201,9 @@ impl Shard {
/// converted to [`Invisible`].
///
/// Other presence settings are maintained.
+ ///
+ /// [`Invisible`]: ../../model/enum.OnlineStatus.html#variant.Invisible
+ /// [`Offline`]: ../../model/enum.OnlineStatus.html#variant.Offline
pub fn set_status(&mut self, online_status: OnlineStatus) {
self.current_presence.1 = match online_status {
OnlineStatus::Offline => OnlineStatus::Invisible,
@@ -232,6 +248,17 @@ impl Shard {
self.update_presence();
}
+ /// Handles an event from the gateway over the receiver, requiring the
+ /// receiver to be passed if a reconnect needs to occur.
+ ///
+ /// The best case scenario is that one of two values is returned:
+ ///
+ /// - `Ok(None)`: a heartbeat, late hello, or session invalidation was
+ /// received;
+ /// - `Ok(Some((event, None)))`: an op0 dispatch was received, and the
+ /// shard's voice state will be updated, _if_ the `voice` feature is
+ /// enabled.
+ #[doc(hidden)]
pub fn handle_event(&mut self,
event: Result<GatewayEvent>,
mut receiver: &mut Receiver<WebSocketStream>)
@@ -318,6 +345,9 @@ impl Shard {
}
}
+ /// Shuts down the receiver by attempting to cleanly close the
+ /// connection.
+ #[doc(hidden)]
pub fn shutdown(&mut self, receiver: &mut Receiver<WebSocketStream>)
-> Result<()> {
let stream = receiver.get_mut().get_mut();
@@ -335,6 +365,9 @@ impl Shard {
Ok(())
}
+ /// Syncs a number of [`Call`]s, given by their associated channel Ids. This
+ /// will allow the current user to know what calls are currently occurring,
+ /// as otherwise events will not be received.
pub fn sync_calls(&self, channels: &[ChannelId]) {
for &channel in channels {
let msg = ObjectBuilder::new()
@@ -348,6 +381,18 @@ impl Shard {
}
}
+ /// Requests that one or multiple [`Guild`]s be synced.
+ ///
+ /// This will ask Discord to start sending member chunks for large guilds
+ /// (250 members+). If a guild is over 250 members, then a full member list
+ /// will not be downloaded, and must instead be requested to be sent in
+ /// "chunks" containing members.
+ ///
+ /// Member chunks are sent as the [`Event::GuildMembersChunk`] event. Each
+ /// chunk only contains a partial amount of the total members.
+ ///
+ /// If the `cache` feature is enabled, the cache will automatically be
+ /// updated with member chunks.
pub fn sync_guilds(&self, guild_ids: &[GuildId]) {
let msg = ObjectBuilder::new()
.insert("op", OpCode::SyncGuild.num())
@@ -383,7 +428,8 @@ impl Shard {
}}
}
- fn reconnect(&mut self, mut receiver: &mut Receiver<WebSocketStream>) -> Result<(Event, Receiver<WebSocketStream>)> {
+ fn reconnect(&mut self, mut receiver: &mut Receiver<WebSocketStream>)
+ -> Result<(Event, Receiver<WebSocketStream>)> {
debug!("Reconnecting");
// Take a few attempts at reconnecting; otherwise fall back to
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 274d438..21b74d4 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -76,24 +76,37 @@ use ::model::event::{
#[cfg(feature = "cache")]
lazy_static! {
- /// The CACHE is a mutable lazily-initialized static binding. It can be
- /// accessed across any function and in any context.
+ /// A mutable and lazily-initialized static binding. It can be accessed
+ /// across any function and in any context.
///
/// This [`Cache`] instance is updated for every event received, so you do
/// not need to maintain your own cache.
///
/// See the [cache module documentation] for more details.
///
+ /// The Cache itself is wrapped within an `RwLock`, which allows for
+ /// multiple readers or at most one writer at a time across threads. This
+ /// means that you may have multiple commands reading from the Cache
+ /// concurrently.
+ ///
/// # Examples
///
- /// Retrieve the [current user][`CurrentUser`]'s Id:
+ /// Retrieve the [current user][`CurrentUser`]'s Id, by opening a Read
+ /// guard:
///
/// ```rust,ignore
/// use serenity::client::CACHE;
///
- /// println!("{}", CACHE.lock().unwrap().user.id);
+ /// println!("{}", CACHE.read().unwrap().user.id);
/// ```
///
+ /// By `unwrap()`ing, the thread managing an event dispatch will be blocked
+ /// until the guard can be opened.
+ ///
+ /// If you do not want to block the current thread, you may instead use
+ /// `RwLock::try_read`. Refer to `RwLock`'s documentation in the stdlib for
+ /// more information.
+ ///
/// [`CurrentUser`]: ../model/struct.CurrentUser.html
/// [`Cache`]: ../ext/cache/struct.Cache.html
/// [cache module documentation]: ../ext/cache/index.html
diff --git a/src/ext/cache/mod.rs b/src/ext/cache/mod.rs
index a8d62c9..923c55a 100644
--- a/src/ext/cache/mod.rs
+++ b/src/ext/cache/mod.rs
@@ -1,3 +1,81 @@
+//! A cache of events received over a [`Shard`], where storing at least some
+//! data from the event is possible.
+//!
+//! This acts as a hot cache, to avoid making requests over the REST API through
+//! the [`http`] module where possible. All fields are public, and do not have
+//! getters, to allow you more flexibility with the stored data. However, this
+//! allows data to be "corrupted", and _may or may not_ cause misfunctions
+//! within the library. Mutate data at your own discretion.
+//!
+//! A "globally available" instance of the Cache is available at
+//! [`client::CACHE`]. This is the instance that is updated by the library,
+//! meaning you should _not_ need to maintain updating it yourself in any case.
+//!
+//! # Use by the Context
+//!
+//! The [`Context`] will automatically attempt to pull from the cache for you.
+//! For example, the [`Context::get_channel`] method will attempt to find the
+//! channel in the cache. If it can not find it, it will perform a request
+//! through the REST API, and then insert a clone of the channel - if found -
+//! into the Cache, giving you the original.
+//!
+//! This allows you to save a step, by only needing to perform the
+//! [`Context::get_channel`] call and not need to first search through the cache
+//! - and if not found - _then_ perform an HTTP request through the Context or
+//! [`http`] module.
+//!
+//! Additionally, note that some information received through events can _not_
+//! be retrieved through the REST API. This is information such as [`Role`]s in
+//! [`LiveGuild`]s.
+//!
+//! # Use by Models
+//!
+//! Most models of Discord objects, such as the [`Message`], [`PublicChannel`],
+//! or [`Emoji`], have methods for interacting with that single instance. This
+//! feature is only compiled if the `methods` feature is enabled. An example of
+//! this is [`LiveGuild::edit`], which performs a check to ensure that the
+//! current user is the owner of the guild, prior to actually performing the
+//! HTTP request. The cache is involved due to the function's use of unlocking
+//! the cache and retrieving the Id of the current user, and comparing it to
+//! the Id of the user that owns the guild. This is an inexpensive method of
+//! being able to access data required by these sugary methods.
+//!
+//! # Do I need the Cache?
+//!
+//! If you're asking this, the answer is likely "definitely yes" or
+//! "definitely no"; any in-between tends to be "yes". If you are low on RAM,
+//! and need to run on only a couple MB, then the answer is "definitely no". If
+//! you do not care about RAM and want your bot to be able to access data
+//! while needing to hit the REST API as little as possible, then the answer
+//! is "yes".
+//!
+//! # Special cases in the Cache
+//!
+//! Some items in the cache, notably [`Call`]s and [`Group`]s, will "always be
+//! empty". The exception to this rule, is for:
+//!
+//! 1. Bots which used to be userbots prior to the conversion made available by
+//! Discord when the official Bot API was introduced;
+//! 2. For groups and calls:
+//! 2a. Bots that have friends from before the conversion that have not been
+//! removed, as those users can still add the bots to groups;
+//! 2b. Bots that have the "Create Group" endpoint whitelisted specifically for
+//! them.
+//!
+//! [`Call`]: ../../model/struct.Call.html
+//! [`Context`]: ../../client/struct.Context.html
+//! [`Context::get_channel`]: ../../client/struct.Context.html#method.get_channel
+//! [`Emoji`]: ../../model/struct.Emoji.html
+//! [`Group`]: ../../model/struct.Group.html
+//! [`LiveGuild`]: ../../model/struct.LiveGuild.html
+//! [`LiveGuild::edit`]: ../../model/struct.LiveGuild.html#method.edit
+//! [`Message`]: ../../model/struct.Message.html
+//! [`PublicChannel`]: ../../model/struct.PublicChannel.html
+//! [`Role`]: ../../model/struct.Role.html
+//! [`Shard`]: ../../client/gateway/struct.Shard.html
+//! [`client::CACHE`]: ../../client/struct.CACHE.html
+//! [`http`]: ../../client/http/index.html
+
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::default::Default;
@@ -42,31 +120,86 @@ pub struct Cache {
/// where the key is the Id of the [`PrivateChannel`] or [`Group`] hosting
/// the call.
///
- /// For bot users this will almost always be empty.
+ /// For bot users this will always be empty, except for in [special cases].
+ ///
+ /// [`Group`]: ../../model/struct.Group.html
+ /// [`PrivateChannel`]: ../../model/struct.PrivateChannel.html
+ /// [special cases]: index.html#special-cases-in-the-cache
pub calls: HashMap<ChannelId, Call>,
/// A map of the groups that the current user is in.
///
- /// For bot users this will almost always be empty.
+ /// For bot users this will always be empty, except for in [special cases].
+ ///
+ /// [special cases]: index.html#special-cases-in-the-cache
pub groups: HashMap<ChannelId, Group>,
/// Settings specific to a guild.
///
- /// This will always be empty for bot accounts.
+ /// This will always be empty for bot users.
pub guild_settings: HashMap<Option<GuildId>, UserGuildSettings>,
+ /// A map of guilds with full data available. This includes data like
+ /// [`Role`]s and [`Emoji`]s that are not available through the REST API.
+ ///
+ /// [`Emoji`]: ../../model/struct.Emoji.html
+ /// [`Role`]: ../../model/struct.Role.html
pub guilds: HashMap<GuildId, Guild>,
- /// A list of notes that a user has made for individual users.
+ /// A map of notes that a user has made for individual users.
+ ///
+ /// An empty note is equivilant to having no note, and creating an empty
+ /// note is equivilant to deleting a note.
///
- /// This will always be empty for bot accounts.
+ /// This will always be empty for bot users.
pub notes: HashMap<UserId, String>,
+ /// A map of users' presences. This is updated in real-time. Note that
+ /// status updates are often "eaten" by the gateway, and this should not
+ /// be treated as being entirely 100% accurate.
pub presences: HashMap<UserId, Presence>,
+ /// A map of direct message channels that the current user has open with
+ /// other users.
pub private_channels: HashMap<ChannelId, PrivateChannel>,
+ /// A map of relationships that the current user has with other users.
+ ///
+ /// For bot users this will always be empty, except for in [special cases].
+ ///
+ /// [special cases]: index.html#special-cases-in-the-cache
pub relationships: HashMap<UserId, Relationship>,
/// Account-specific settings for a user account.
pub settings: Option<UserSettings>,
+ /// A list of guilds which are "unavailable". Refer to the documentation for
+ /// [`Event::GuildUnavailable`] for more information on when this can occur.
+ ///
+ /// Additionally, guilds are always unavailable for bot users when a Ready
+ /// is received. Guilds are "sent in" over time through the receiving of
+ /// [`Event::GuildCreate`]s.
+ ///
+ /// [`Event::GuildCreate`]: ../../model/enum.Event.html#variant.GuildCreate
+ /// [`Event::GuildUnavailable`]: ../../model/enum.Event.html#variant.GuildUnavailable
pub unavailable_guilds: Vec<GuildId>,
+ /// The current user "logged in" and for which events are being received
+ /// for.
+ ///
+ /// The current user contains information that a regular [`User`] does not,
+ /// such as whether it is a bot, whether the user is verified, etc.
+ ///
+ /// Refer to the documentation for [`CurrentUser`] for more information.
+ ///
+ /// [`CurrentUser`]: ../../model/struct.CurrentUser.html
+ /// [`User`]: ../../model/struct.User.html
pub user: CurrentUser,
}
impl Cache {
+ /// Calculates the number of [`Member`]s that have not had data received.
+ ///
+ /// The important detail to note here is that this is the number of
+ /// _member_s that have not had data downloaded. A single [`User`] may have
+ /// multiple associated member objects that have not been received.
+ ///
+ /// This can be used in combination with [`Shard::sync_guilds`], and can be
+ /// used to determine how many members have not yet been downloaded.
+ ///
+ /// [`Member`]: ../../model/struct.Member.html
+ /// [`Shard::sync_guilds`]: ../../client/gateway/struct.Shard.html#method.sync_guilds
+ /// [`User`]: ../../model/struct.User.html
pub fn unknown_members(&self) -> u64 {
let mut total = 0;
@@ -81,6 +214,16 @@ impl Cache {
total
}
+ /// Calculates a vector of all [`PrivateChannel`] and [`Group`] Ids that are
+ /// stored in the cache.
+ ///
+ /// # Examples
+ ///
+ /// If there are 6 private channels and 2 groups in the cache, then `8` Ids
+ /// will be returned.
+ ///
+ /// [`Group`]: ../../model/struct.Group.html
+ /// [`PrivateChannel`]: ../../model/struct.PrivateChannel.html
pub fn all_private_channels(&self) -> Vec<ChannelId> {
self.groups
.keys()
@@ -89,6 +232,15 @@ impl Cache {
.collect()
}
+ /// Calculates a vector of all [`Guild`]s' Ids that are stored in the cache.
+ ///
+ /// Note that if you are utilizing multiple [`Shard`]s, then the guilds
+ /// retrieved over all shards are included in this count -- not just the
+ /// current [`Context`]'s shard, if accessing from one.
+ ///
+ /// [`Context`]: ../../client/struct.Context.html
+ /// [`Guild`]: ../../model/struct.Guild.html
+ /// [`Shard`]: ../../client/gateway/struct.Shard.html
pub fn all_guilds(&self) -> Vec<GuildId> {
self.guilds
.values()
@@ -110,10 +262,25 @@ impl Cache {
.collect::<Vec<_>>()
}
+ /// Retrieves a reference to a [`Call`] from the cache based on the
+ /// associated [`Group`]'s channel Id.
+ ///
+ /// [`Call`]: ../../model/struct.Call.html
+ /// [`Group`]: ../../model/struct.Group.html
pub fn get_call<C: Into<ChannelId>>(&self, group_id: C) -> Option<&Call> {
self.calls.get(&group_id.into())
}
+ /// Retrieves a [`Channel`] from the cache based on the given Id.
+ ///
+ /// This will search the [`groups`] map, the [`private_channels`] map, and
+ /// then the map of [`guilds`] to find the channel.
+ ///
+ /// [`Channel`]: ../../model/enum.Channel.html
+ /// [`Guild`]: ../../model/struct.Guild.html
+ /// [`groups`]: #structfield.groups
+ /// [`private_channels`]: #structfield.private_channels
+ /// [`guilds`]: #structfield.guilds
pub fn get_channel<C: Into<ChannelId>>(&self, id: C) -> Option<ChannelRef> {
let id = id.into();
@@ -136,10 +303,38 @@ impl Cache {
None
}
+ /// Retrieves a reference to a guild from the cache based on the given Id.
pub fn get_guild<G: Into<GuildId>>(&self, id: G) -> Option<&Guild> {
self.guilds.get(&id.into())
}
+ /// Retrieves a reference to a [`Guild`]'s channel. Unlike [`get_channel`],
+ /// this will only search guilds for the given channel.
+ ///
+ /// # Examples
+ ///
+ /// Getting a guild's channel via the Id of the message received through a
+ /// [`Client::on_message`] event dispatch:
+ ///
+ /// ```rust,ignore
+ /// use serenity::cache::CACHE;
+ ///
+ /// let cache = CACHE.read().unwrap();
+ ///
+ /// let channel = match cache.get_guild_channel(message.channel_id) {
+ /// Some(channel) => channel,
+ /// None => {
+ /// context.say("Could not find guild's channel data")
+ /// .map_err(|why| println!("Err sending message: {:?}", why));
+ ///
+ /// return;
+ /// },
+ /// };
+ /// ```
+ ///
+ /// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message
+ /// [`Guild`]: ../../model/struct.Guild.html
+ /// [`get_channel`]: #method.get_channel
pub fn get_guild_channel<C: Into<ChannelId>>(&self, id: C) -> Option<&GuildChannel> {
let id = id.into();
@@ -152,10 +347,57 @@ impl Cache {
None
}
+ /// Retrieves a reference to a [`Group`] from the cache based on the given
+ /// associated channel Id.
+ ///
+ /// [`Group`]: ../../model/struct.Group.html
pub fn get_group<C: Into<ChannelId>>(&self, id: C) -> Option<&Group> {
self.groups.get(&id.into())
}
+ /// Retrieves a reference to a [`Guild`]'s member from the cache based on
+ /// the guild's and user's given Ids.
+ ///
+ /// # Examples
+ ///
+ /// Retrieving the member object of the user that posted a message, in a
+ /// [`Client::on_message`] context:
+ ///
+ /// ```rust,ignore
+ /// use serenity::client::CACHE;
+ ///
+ /// // assuming you are in a context
+ ///
+ /// let cache = CACHE.read().unwrap();
+ /// let member = {
+ /// let channel = match cache.get_guild_channel(message.channel_id) {
+ /// Some(channel) => channel,
+ /// None => {
+ /// if let Err(why) = context.say("Err finding channel data") {
+ /// println!("Err sending message: {:?}", why);
+ /// }
+ /// },
+ /// };
+ ///
+ /// match cache.get_member(channel.guild_id, message.author.id) {
+ /// Some(member) => member,
+ /// None => {
+ /// if let Err(why) = context.say("Err finding member data") {
+ /// println!("Err sending message: {:?}", why);
+ /// }
+ /// },
+ /// }
+ /// };
+ ///
+ /// let msg = format!("You have {} roles", member.roles.len());
+ ///
+ /// if let Err(why) = context.say(&msg) {
+ /// println!("Err sending message: {:?}", why);
+ /// }
+ /// ```
+ ///
+ /// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message
+ /// [`Guild`]: ../../model/struct.Guild.html
pub fn get_member<G, U>(&self, guild_id: G, user_id: U) -> Option<&Member>
where G: Into<GuildId>, U: Into<UserId> {
self.guilds
@@ -168,6 +410,9 @@ impl Cache {
})
}
+ /// Retrieves a reference to a [`Guild`]'s role by their Ids.
+ ///
+ /// [`Guild`]: ../../model/struct.Guild.html
pub fn get_role<G, R>(&self, guild_id: G, role_id: R) -> Option<&Role>
where G: Into<GuildId>, R: Into<RoleId> {
if let Some(guild) = self.guilds.get(&guild_id.into()) {
@@ -180,6 +425,7 @@ impl Cache {
/// Update the cache according to the changes described in the given event.
#[allow(cyclomatic_complexity)]
#[allow(unneeded_field_pattern)]
+ #[doc(hidden)]
pub fn update(&mut self, event: &Event) {
match *event {
Event::CallCreate(ref event) => {
@@ -300,6 +546,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_call_create(&mut self, event: &CallCreateEvent) {
match self.calls.entry(event.call.channel_id) {
Entry::Vacant(e) => {
@@ -311,11 +558,13 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_call_delete(&mut self, event: &CallDeleteEvent)
-> Option<Call> {
self.calls.remove(&event.channel_id)
}
+ #[doc(hidden)]
pub fn update_with_call_update(&mut self, event: &CallUpdateEvent, old: bool)
-> Option<Call> {
let item = if old {
@@ -334,6 +583,7 @@ impl Cache {
item
}
+ #[doc(hidden)]
pub fn update_with_channel_create(&mut self, event: &ChannelCreateEvent)
-> Option<Channel> {
match event.channel {
@@ -364,6 +614,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_channel_delete(&mut self, event: &ChannelDeleteEvent)
-> Option<Channel> {
match event.channel {
@@ -387,6 +638,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_channel_pins_update(&mut self,
event: &ChannelPinsUpdateEvent) {
if let Some(channel) = self.private_channels.get_mut(&event.channel_id) {
@@ -414,6 +666,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_channel_recipient_add(&mut self,
event: &ChannelRecipientAddEvent) {
self.groups
@@ -422,6 +675,7 @@ impl Cache {
event.user.clone()));
}
+ #[doc(hidden)]
pub fn update_with_channel_recipient_remove(&mut self,
event: &ChannelRecipientRemoveEvent) {
self.groups
@@ -429,6 +683,7 @@ impl Cache {
.map(|group| group.recipients.remove(&event.user.id));
}
+ #[doc(hidden)]
pub fn update_with_channel_update(&mut self, event: &ChannelUpdateEvent) {
match event.channel {
Channel::Group(ref group) => {
@@ -465,15 +720,18 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_create(&mut self, event: &GuildCreateEvent) {
self.guilds.insert(event.guild.id, event.guild.clone());
}
+ #[doc(hidden)]
pub fn update_with_guild_delete(&mut self, event: &GuildDeleteEvent)
-> Option<Guild> {
self.guilds.remove(&event.guild.id)
}
+ #[doc(hidden)]
pub fn update_with_guild_emojis_update(&mut self,
event: &GuildEmojisUpdateEvent) {
self.guilds
@@ -481,6 +739,7 @@ impl Cache {
.map(|guild| guild.emojis.extend(event.emojis.clone()));
}
+ #[doc(hidden)]
pub fn update_with_guild_member_add(&mut self,
event: &GuildMemberAddEvent) {
self.guilds
@@ -492,6 +751,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_guild_member_remove(&mut self,
event: &GuildMemberRemoveEvent)
-> Option<Member> {
@@ -508,6 +768,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_member_update(&mut self,
event: &GuildMemberUpdateEvent)
-> Option<Member> {
@@ -545,6 +806,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_members_chunk(&mut self,
event: &GuildMembersChunkEvent) {
self.guilds
@@ -552,6 +814,7 @@ impl Cache {
.map(|guild| guild.members.extend(event.members.clone()));
}
+ #[doc(hidden)]
pub fn update_with_guild_role_create(&mut self,
event: &GuildRoleCreateEvent) {
self.guilds
@@ -559,6 +822,7 @@ impl Cache {
.map(|guild| guild.roles.insert(event.role.id, event.role.clone()));
}
+ #[doc(hidden)]
pub fn update_with_guild_role_delete(&mut self,
event: &GuildRoleDeleteEvent)
-> Option<Role> {
@@ -572,6 +836,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_role_update(&mut self,
event: &GuildRoleUpdateEvent)
-> Option<Role> {
@@ -587,6 +852,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_sync(&mut self, event: &GuildSyncEvent) {
self.guilds
.get_mut(&event.guild_id)
@@ -597,6 +863,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_guild_unavailable(&mut self,
event: &GuildUnavailableEvent) {
if !self.unavailable_guilds.contains(&event.guild_id) {
@@ -604,6 +871,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_guild_update(&mut self, event: &GuildUpdateEvent) {
self.guilds
.get_mut(&event.guild.id)
@@ -619,6 +887,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_presences_replace(&mut self, event: &PresencesReplaceEvent) {
self.presences.clone_from(&{
let mut p = HashMap::default();
@@ -631,6 +900,7 @@ impl Cache {
});
}
+ #[doc(hidden)]
pub fn update_with_presence_update(&mut self, event: &PresenceUpdateEvent) {
if let Some(guild_id) = event.guild_id {
if let Some(guild) = self.guilds.get_mut(&guild_id) {
@@ -646,6 +916,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_ready(&mut self, ready: &ReadyEvent) {
let ready = ready.ready.clone();
@@ -695,16 +966,19 @@ impl Cache {
self.user = ready.user;
}
+ #[doc(hidden)]
pub fn update_with_relationship_add(&mut self, event: &RelationshipAddEvent) {
self.relationships.insert(event.relationship.id,
event.relationship.clone());
}
+ #[doc(hidden)]
pub fn update_with_relationship_remove(&mut self,
event: &RelationshipRemoveEvent) {
self.relationships.remove(&event.user_id);
}
+ #[doc(hidden)]
pub fn update_with_user_guild_settings_update(&mut self,
event: &UserGuildSettingsUpdateEvent)
-> Option<UserGuildSettings> {
@@ -713,6 +987,7 @@ impl Cache {
.map(|guild_setting| mem::replace(guild_setting, event.settings.clone()))
}
+ #[doc(hidden)]
pub fn update_with_user_note_update(&mut self,
event: &UserNoteUpdateEvent)
-> Option<String> {
@@ -723,6 +998,7 @@ impl Cache {
}
}
+ #[doc(hidden)]
pub fn update_with_user_settings_update(&mut self,
event: &UserSettingsUpdateEvent,
old: bool)
@@ -751,11 +1027,13 @@ impl Cache {
item
}
+ #[doc(hidden)]
pub fn update_with_user_update(&mut self, event: &UserUpdateEvent)
-> CurrentUser {
mem::replace(&mut self.user, event.current_user.clone())
}
+ #[doc(hidden)]
pub fn update_with_voice_state_update(&mut self,
event: &VoiceStateUpdateEvent) {
if let Some(guild_id) = event.guild_id {
@@ -857,7 +1135,6 @@ fn update_presence(presences: &mut HashMap<UserId, Presence>,
}
/// A reference to a private channel, guild's channel, or group.
-#[derive(Clone, Copy, Debug)]
pub enum ChannelRef<'a> {
/// A group's channel
Group(&'a Group),
diff --git a/src/ext/framework/command.rs b/src/ext/framework/command.rs
index 6a9a713..0c595b3 100644
--- a/src/ext/framework/command.rs
+++ b/src/ext/framework/command.rs
@@ -3,6 +3,7 @@ use super::{CommandType, Configuration};
use ::client::Context;
use ::model::Message;
+#[doc(hidden)]
pub type Command = Fn(Context, Message, Vec<String>) + Send + Sync;
#[doc(hidden)]
pub type InternalCommand = Arc<Command>;
diff --git a/src/ext/framework/configuration.rs b/src/ext/framework/configuration.rs
index 6dd4fd2..6a6c5e5 100644
--- a/src/ext/framework/configuration.rs
+++ b/src/ext/framework/configuration.rs
@@ -1,6 +1,28 @@
use std::default::Default;
use ::client::rest;
+/// The configuration to use for a [`Framework`] associated with a [`Client`]
+/// instance.
+///
+/// This allows setting configurations like the depth to search for commands,
+/// whether to treat mentions like a command prefix, etc.
+///
+/// # Examples
+///
+/// Responding to mentions and setting a command prefix of `"~"`:
+///
+/// ```rust,no_run
+/// use serenity::Client;
+/// use std::env;
+///
+/// let mut client = Client::login_bot(&env::var("DISCORD_BOT_TOKEN").unwrap());
+///
+/// client.with_framework(|f| f
+/// .configure(|c| c.on_mention(true).prefix("~")));
+/// ```
+///
+/// [`Client`]: ../../client/struct.Client.html
+/// [`Framework`]: struct.Framework.html
pub struct Configuration {
#[doc(hidden)]
pub depth: usize,
diff --git a/src/ext/framework/mod.rs b/src/ext/framework/mod.rs
index 361fdd8..2a92840 100644
--- a/src/ext/framework/mod.rs
+++ b/src/ext/framework/mod.rs
@@ -1,3 +1,58 @@
+//! The framework is a customizable method of separating commands, used in
+//! combination with [`Client::with_framework`].
+//!
+//! The framework has a number of configurations, and can have any number of
+//! commands bound to it. The primary purpose of it is to offer the utility of
+//! not needing to manually match message content strings to determine if a
+//! message is a command.
+//!
+//! Additionally, "checks" can be added to commands, to ensure that a certain
+//! condition is met prior to calling a command; this could be a check that the
+//! user who posted a message owns the bot, for example.
+//!
+//! Each command has a given named, and an associated function/closure. For
+//! example, you might have two commands: `"ping"` and `"weather"`. These each
+//! have an associated function that are called if the framework determines
+//! that a message is of that command.
+//!
+//! Assuming a command prefix of `"~"`, then the following would occur with the
+//! two previous commands:
+//!
+//! ```ignore
+//! ~ping // calls the ping command's function
+//! ~pin // does not
+//! ~ ping // _does_ call it _if_ the `allow_whitespace` option is enabled
+//! ~~ping // does not
+//! ```
+//!
+//! # Examples
+//!
+//! Configuring a Client with a framework, which has a prefix of `"~"` and a
+//! ping and about command:
+//!
+//! ```rust,no_run
+//! use serenity::client::{Client, Context};
+//! use serenity::model::Message;
+//! use std::env;
+//!
+//! let mut client = Client::login_bot(&env::var("DISCORD_BOT_TOKEN").unwrap());
+//!
+//! client.with_framework(|f| f
+//! .configure(|c| c.prefix("~"))
+//! .on("about", about)
+//! .on("ping", ping));
+//!
+//! fn about(context: Context, _message: Message, _args: Vec<String>) {
+//! let _ = context.say("A simple test bot");
+//! }
+//!
+//! fn ping(context: Context, _message: Message, _args: Vec<String>) {
+//! let _ = context.say("Pong!");
+//! }
+//! ```
+//!
+//! [`Client::with_framework`]: ../../client/struct.Client.html#method.with_framework
+
mod command;
mod configuration;
@@ -98,6 +153,11 @@ pub enum CommandType {
Prefix,
}
+/// A utility for easily managing dispatches to commands.
+///
+/// Refer to the [module-level documentation] for more information.
+///
+/// [module-level documentation]: index.html
#[allow(type_complexity)]
#[derive(Default)]
pub struct Framework {
@@ -218,6 +278,16 @@ impl Framework {
}
}
+ /// Adds a function to be associated with a command, which will be called
+ /// when a command is used in a message.
+ ///
+ /// This requires that a check - if one exists - passes, prior to being
+ /// called.
+ ///
+ /// Refer to the [module-level documentation] for more information and
+ /// usage.
+ ///
+ /// [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>) + Send + Sync + 'static,
S: Into<String> {
@@ -227,6 +297,35 @@ impl Framework {
self
}
+ /// Adds a "check" to a command, which checks whether or not the command's
+ /// associated function should be called.
+ ///
+ /// # Examples
+ ///
+ /// Ensure that the user who created a message, calling a "ping" command,
+ /// is the owner.
+ ///
+ /// ```rust,no_run
+ /// use serenity::client::{Client, Context};
+ /// use serenity::model::Message;
+ /// use std::env;
+ ///
+ /// let mut client = Client::login_bot(&env::var("DISCORD_TOKEN").unwrap());
+ ///
+ /// client.with_framework(|f| f
+ /// .configure(|c| c.prefix("~"))
+ /// .on("ping", ping)
+ /// .set_check("ping", owner_check));
+ ///
+ /// fn ping(context: Context, _message: Message, _args: Vec<String>) {
+ /// context.say("Pong!");
+ /// }
+ ///
+ /// fn owner_check(_context: &Context, message: &Message) -> bool {
+ /// // replace with your user ID
+ /// message.author.id == 7
+ /// }
+ /// ```
pub fn set_check<F, S>(mut self, command: S, check: F) -> Self
where F: Fn(&Context, &Message) -> bool + Send + Sync + 'static,
S: Into<String> {
diff --git a/src/ext/mod.rs b/src/ext/mod.rs
index 4484b0b..9605466 100644
--- a/src/ext/mod.rs
+++ b/src/ext/mod.rs
@@ -1,5 +1,5 @@
-//! The set of extensions is functionality that is not required for a
-//! [`Client`] and/or [`Connection`] to properly function.
+//! A set of extended functionality that is not required for a [`Client`] and/or
+//! [`Shard`] to properly function.
//!
//! These are flagged behind feature-gates and can be enabled and disabled.
//!
@@ -11,7 +11,7 @@
//! to be enabled (disabled by default).
//!
//! [`Client`]: ../client/struct.Client.html
-//! [`Connection`]: ../client/struct.Connection.html
+//! [`Shard`]: ../client/gateway/struct.Shard.html
#[cfg(feature = "cache")]
pub mod cache;
diff --git a/src/lib.rs b/src/lib.rs
index bb4251f..eb8ed78 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,10 +25,10 @@
//! to avoid unnecessary HTTP requests to the Discord API. For more information,
//! see the [cache's module-level documentation][cache docs].
//!
-//! Note that - although this documentation will try to be as up-to-date and
-//! accurate as possible - Discord hosts [official documentation][docs]. If you
-//! need to be sure that some information piece is accurate, refer to their
-//! docs.
+//! Note that, although this documentation will try to be as up-to-date and
+//! accurate as possible, Discord hosts [official documentation][docs]. If you
+//! need to be sure that some information piece is sanctioned by Discord, refer
+//! to their own documentation.
//!
//! # Dependencies
//!
diff --git a/src/model/gateway.rs b/src/model/gateway.rs
index 101f050..cda1c59 100644
--- a/src/model/gateway.rs
+++ b/src/model/gateway.rs
@@ -1,6 +1,7 @@
use super::utils::*;
use super::*;
use ::internal::prelude::*;
+use ::utils::decode_array;
impl Game {
#[cfg(feature="methods")]
diff --git a/src/utils/builder/edit_guild.rs b/src/utils/builder/edit_guild.rs
index 3ea9aaa..dd4ce84 100644
--- a/src/utils/builder/edit_guild.rs
+++ b/src/utils/builder/edit_guild.rs
@@ -3,20 +3,67 @@ use serde_json::Value;
use std::default::Default;
use ::model::{ChannelId, Region, VerificationLevel};
+/// A builder to optionally edit certain fields of a [`Guild`]. This is meant
+/// for usage with [`Context::edit_guild`] and [`LiveGuild::edit`].
+///
+/// **Note**: Editing a guild requires that the current user have the
+/// [Manage Guild] permission.
+///
+/// [`Context::edit_guild`]: ../../client/struct.Context.html
+/// [`Guild`]: ../../model/struct.Guild.html
+/// [`LiveGuild::edit`]: ../../model/struct.LiveGuild.html#method.edit
+/// [Manage Guild]: ../../model/permissions/constant.MANAGE_GUILD.html
pub struct EditGuild(pub ObjectBuilder);
impl EditGuild {
+ /// Set the "AFK voice channel" that users are to move to if they have been
+ /// AFK for an amount of time, configurable by [`afk_timeout`].
+ ///
+ /// The given channel must be either some valid voice channel, or `None` to
+ /// not set an AFK channel. The library does not check if a channel is
+ /// valid.
+ ///
+ /// [`afk_timeout`]: #method.afk_timeout
pub fn afk_channel<C: Into<ChannelId>>(self, channel: Option<C>) -> Self {
- EditGuild(match channel {
- Some(channel) => self.0.insert("afk_channel_id", channel.into().0),
- None => self.0.insert("afk-channel_id", Value::Null),
- })
+ EditGuild(self.0.insert("afk_channel_id", match channel {
+ Some(channel) => Value::U64(channel.into().0),
+ None => Value::Null,
+ }))
}
+ /// Set the amount of time a user is to be moved to the AFK channel -
+ /// configured via [`afk_channel`] - after being AFK.
+ ///
+ /// [`afk_channel`]: #method.afk_channel
pub fn afk_timeout(self, timeout: u64) -> Self {
EditGuild(self.0.insert("afk_timeout", timeout))
}
+ /// Set the icon of the guild. Pass `None` to remove the icon.
+ ///
+ /// # Examples
+ ///
+ /// Using the utility function - [`utils::read_image`] - to read an image
+ /// from the cwd and encode it in base64 to send to Discord.
+ ///
+ /// ```rust,ignore
+ /// use serenity::utils;
+ ///
+ /// // assuming a `guild` has already been bound
+ ///
+ /// let base64_icon = match utils::read_image("./guild_icon.png") {
+ /// Ok(base64_icon) => base64_icon,
+ /// Err(why) => {
+ /// println!("Error reading image: {:?}", why);
+ ///
+ /// return;
+ /// },
+ /// };
+ ///
+ /// let _ = guild.edit(|g| g.icon(base64_icon));
+ /// ```
+ ///
+ /// [`utils::read_image`]: ../../utils/fn.read_image.html
pub fn icon(self, icon: Option<&str>) -> Self {
EditGuild(self.0
.insert("icon",
@@ -24,14 +71,41 @@ impl EditGuild {
|x| Value::String(x.to_owned()))))
}
+ /// Set the name of the guild.
+ ///
+ /// **Note**: Must be between (and including) 2-100 chracters.
pub fn name(self, name: &str) -> Self {
EditGuild(self.0.insert("name", name))
}
+ /// Set the voice region of the server.
+ ///
+ /// # Examples
+ ///
+ /// Setting the region to [`Region::UsWest`]:
+ ///
+ /// ```rust,ignore
+ /// use serenity::model::Region;
+ ///
+ /// // assuming a `guild` has already been bound
+ ///
+ /// if let Err(why) = guild.edit(|g| g.region(Region::UsWest)) {
+ /// println!("Error editing guild's region: {:?}", why);
+ /// }
+ /// ```
+ ///
+ /// [`Region::UsWest`]: ../../model/enum.Region.html#variant.UsWest
pub fn region(self, region: Region) -> Self {
EditGuild(self.0.insert("region", region.name()))
}
+ /// Set the splash image of the guild on the invitation page.
+ ///
+ /// Requires that the guild have the [`InviteSplash`] feature enabled.
+ /// You can check this through a guild's [`features`] list.
+ ///
+ /// [`InviteSplash`]: ../../model/enum.Feature.html#variant.InviteSplash
+ /// [`features`]: ../../model/struct.LiveGuild.html#structfield.features
pub fn splash(self, splash: Option<&str>) -> Self {
EditGuild(self.0
.insert("splash",
@@ -39,6 +113,37 @@ impl EditGuild {
|x| Value::String(x.to_owned()))))
}
+ /// Set the verification level of the guild. This can restrict what a
+ /// user must have prior to being able to send messages in a guild.
+ ///
+ /// Refer to the documentation for [`VerificationLevel`] for more
+ /// information on each variant.
+ ///
+ /// [`VerificationLevel`]: ../../model/enum.VerificationLevel.html
+ ///
+ /// # Examples
+ ///
+ /// Setting the verification level to [`High`][`VerificationLevel::High`]:
+ ///
+ /// ```rust,ignore
+ /// use serenity::model::VerificationLevel;
+ ///
+ /// // assuming a `guild` has already been bound
+ ///
+ /// if let Err(why) = guild.edit(|g| g.verification_level(VerificationLevel::High)) {
+ /// println!("Error setting verification level: {:?}", why);
+ /// }
+ ///
+ /// // additionally, you may pass in just an integer of the verification
+ /// // level
+ ///
+ /// if let Err(why) = guild.edit(|g| g.verification_level(3)) {
+ /// println!("Error setting verification level: {:?}", why);
+ /// }
+ /// ```
+ ///
+ /// [`VerificationLevel`]: ../../model/enum.VerificationLevel.html
+ /// [`VerificationLevel::High`]: ../../model/enum.VerificationLevel.html#variant.High
pub fn verification_level<V>(self, verification_level: V) -> Self
where V: Into<VerificationLevel> {
EditGuild(self.0.insert("verification_level",
diff --git a/src/utils/builder/edit_profile.rs b/src/utils/builder/edit_profile.rs
index c83f049..257a8e5 100644
--- a/src/utils/builder/edit_profile.rs
+++ b/src/utils/builder/edit_profile.rs
@@ -12,7 +12,7 @@ impl EditProfile {
///
/// # Examples
///
- /// A utility method - [`utils::read_message`] - is provided to read an
+ /// A utility method - [`utils::read_image`] - is provided to read an
/// image from a file and return its contents in base64-encoded form:
///
/// ```rust,ignore
diff --git a/src/utils/message_builder.rs b/src/utils/message_builder.rs
index e30db05..ca1ac8d 100644
--- a/src/utils/message_builder.rs
+++ b/src/utils/message_builder.rs
@@ -112,7 +112,6 @@ impl MessageBuilder {
self
}
-
/// Mentions the [`Role`] in the built message.
///
/// This accepts anything that converts _into_ a [`RoleId`]. Refer to