aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorAustin Hellyer <[email protected]>2016-11-18 11:00:45 -0800
committerAustin Hellyer <[email protected]>2016-11-18 11:00:45 -0800
commitcf128b1a10d0636c8b4b5233d46b068cfe665688 (patch)
treef36b1cd08d00cec69a76eb10c493ab3b86d61678 /src/client
parentFeature macros should use else as block separator (diff)
downloadserenity-cf128b1a10d0636c8b4b5233d46b068cfe665688.tar.xz
serenity-cf128b1a10d0636c8b4b5233d46b068cfe665688.zip
A bit of docs
Diffstat (limited to 'src/client')
-rw-r--r--src/client/context.rs228
-rw-r--r--src/client/http/mod.rs160
-rw-r--r--src/client/http/ratelimiting.rs81
-rw-r--r--src/client/login_type.rs19
-rw-r--r--src/client/mod.rs164
5 files changed, 535 insertions, 117 deletions
diff --git a/src/client/context.rs b/src/client/context.rs
index 522361a..5dd3053 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -23,15 +23,76 @@ use ::utils;
#[cfg(feature = "state")]
use super::STATE;
+/// The context is a general utility struct provided on event dispatches, which
+/// helps with dealing with the current "context" of the event dispatch,
+/// and providing helper methods where possible. The context also acts as a
+/// general high-level interface over the associated [`Connection`] which
+/// received the event, or the low-level [`http`] module.
+///
+/// For example, when the [`Client::on_message`] handler is dispatched to, the
+/// context will contain the Id of the [`Channel`] that the message was created
+/// for. This allows for using shortcuts like [`say`], which will
+/// post its given argument to the associated channel for you as a [`Message`].
+///
+/// Additionally, the context contains "shortcuts", like for interacting with
+/// the connection. Methods like [`set_game`] will unlock the connection and
+/// perform an update for you to save a bit of work.
+///
+/// A context will only live for the event it was dispatched for. After the
+/// event handler finished, it is destroyed and will not be re-used.
+///
+/// # Automatically using the State
+///
+/// The context makes use of the [`State`] being global, and will first check
+/// the state for associated data before hitting the REST API. This is to save
+/// Discord requests, and ultimately save your bot bandwidth and time. This also
+/// acts as a clean interface for retrieving from the state without needing to
+/// check it yourself first, and then performing a request if it does not exist.
+/// The context ultimately acts as a means to simplify these two operations into
+/// one.
+///
+/// For example, if you are needing information about a
+/// [channel][`PublicChannel`] within a [guild][`LiveGuild`], then you can
+/// use [`get_channel`] to retrieve it. Under most circumstances, the guild and
+/// its channels will be cached within the state, and `get_channel` will just
+/// pull from the state. If it does not exist, it will make a request to the
+/// REST API, and then insert a clone of the channel into the state, returning
+/// you the channel.
+///
+/// In this scenario, now that the state has the channel, performing the same
+/// request to `get_channel` will instead pull from the state, as it is now
+/// cached.
+///
+/// [`Channel`]: ../model/enum.Channel.html
+/// [`Client::on_message`]: struct.Client.html#method.on_message
+/// [`Connection`]: struct.Connection.html
+/// [`LiveGuild`]: ../model/struct.LiveGuild.html
+/// [`Message`]: ../model/struct.Message.html
+/// [`PublicChannel`]: ../model/struct.PublicChannel.html
+/// [`State`]: ../ext/state/struct.State.html
+/// [`get_channel`]: #method.get_channel
+/// [`http`]: http/index.html
+/// [`say`]: #method.say
+/// [`set_game`]: #method.set_game
#[derive(Clone)]
pub struct Context {
channel_id: Option<ChannelId>,
+ /// The associated connection which dispatched the event handler.
+ ///
+ /// Note that if you are sharding, in relevant terms, this is the shard
+ /// which received the event being dispatched.
pub connection: Arc<Mutex<Connection>>,
login_type: LoginType,
}
impl Context {
/// Create a new Context to be passed to an event handler.
+ ///
+ /// There's no real reason to use this yourself. But the option is there.
+ /// Highly re-consider _not_ using this if you're tempted.
+ ///
+ /// Or don't do what I say. I'm just a comment hidden from the generated
+ /// documentation.
#[doc(hidden)]
pub fn new(channel_id: Option<ChannelId>,
connection: Arc<Mutex<Connection>>,
@@ -45,14 +106,10 @@ impl Context {
/// Accepts the given invite.
///
- /// Refer to the documentation for [`Invite::accept`] for restrictions on
- /// accepting an invite.
- ///
- /// **Note**: Requires that the current user be a user account. Bots can not
- /// accept invites. Instead they must be accepted via OAuth2 authorization
- /// links. These are in the format of:
+ /// Refer to the documentation for [`http::accept_invite`] for restrictions
+ /// on accepting an invite.
///
- /// `https://discordapp.com/oauth2/authorize?client_id=CLIENT_ID&scope=bot`
+ /// **Note**: Requires that the current user be a user account.
///
/// # Errors
///
@@ -71,19 +128,23 @@ impl Context {
http::accept_invite(code)
}
- /// Mark a message as being read in a channel. This will mark up to the
- /// given message as read. Any messages created after that message will not
- /// be marked as read.
+ /// Mark a [`Channel`] as being read up to a certain [`Message`].
+ ///
+ /// Refer to the documentation for [`http::ack_message`] for more
+ /// information.
///
/// # Errors
///
/// Returns a [`ClientError::InvalidOperationAsBot`] if this is a bot.
///
+ /// [`Channel`]: ../../model/enum.Channel.html
/// [`ClientError::InvalidOperationAsBot`]: ../enum.ClientError.html#variant.InvalidOperationAsUser
+ /// [`Message`]: ../../model/struct.Message.html
+ /// [`http::ack_message`]: http/fn.ack_message.html
pub fn ack<C, M>(&self, channel_id: C, message_id: M) -> Result<()>
where C: Into<ChannelId>, M: Into<MessageId> {
if self.login_type == LoginType::User {
- return Err(Error::Client(ClientError::InvalidOperationAsUser))
+ return Err(Error::Client(ClientError::InvalidOperationAsUser));
}
http::ack_message(channel_id.into().0, message_id.into().0)
@@ -92,8 +153,7 @@ impl Context {
/// Ban a [`User`] from a [`Guild`], removing their messages sent in the
/// last X number of days.
///
- /// `0` days is equivilant to not removing any messages. Up to `7` days'
- /// worth of messages may be deleted.
+ /// Refer to the documentation for [`http::ban_user`] for more information.
///
/// **Note**: Requires that you have the [Ban Members] permission.
///
@@ -145,7 +205,9 @@ impl Context {
/// Creates a [`PublicChannel`] in the given [`Guild`].
///
- /// Requires that you have the [Manage Channels] permission.
+ /// Refer to [`http::create_channel`] for more information.
+ ///
+ /// **Note**: Requires the [Manage Channels] permission.
///
/// # Examples
///
@@ -159,6 +221,7 @@ impl Context {
///
/// [`Guild`]: ../model/struct.Guild.html
/// [`PublicChannel`]: ../model/struct.PublicChannel.html
+ /// [`http::create_channel`]: http/fn.create_channel.html
/// [Manage Channels]: ../model/permissions/constant.MANAGE_CHANNELS.html
pub fn create_channel<G>(&self, guild_id: G, name: &str, kind: ChannelType)
-> Result<Channel> where G: Into<GuildId> {
@@ -170,6 +233,22 @@ impl Context {
http::create_channel(guild_id.into().0, map)
}
+ /// Creates an emoji in the given guild with a name and base64-encoded
+ /// image. The [`utils::read_image`] function is provided for you as a
+ /// simple method to read an image and encode it into base64, if you are
+ /// reading from the filesystem.
+ ///
+ /// **Note**: Requires the [Manage Emojis] permission.
+ ///
+ /// # Examples
+ ///
+ /// See the [`EditProfile::avatar`] example for an in-depth example as to
+ /// how to read an image from the filesystem and encode it as base64. Most
+ /// of the example can be applied similarly for this method.
+ ///
+ /// [`EditProfile::avatar`]: ../utils/builder/struct.EditProfile.html#method.avatar
+ /// [`utils::read_image`]: ../utils/fn.read_image.html
+ /// [Manage Emojis]: ../model/permissions/constant.MANAGE_EMOJIS.html
pub fn create_emoji<G>(&self, guild_id: G, name: &str, image: &str)
-> Result<Emoji> where G: Into<GuildId> {
let map = ObjectBuilder::new()
@@ -182,9 +261,14 @@ impl Context {
/// Creates a [`Guild`] with the data provided.
///
+ /// **Note**: This endpoint is usually only available for user accounts.
+ /// Refer to Discord's information for the endpoint [here][whitelist] for
+ /// more information. If you require this as a bot, re-think what you are
+ /// doing and if it _really_ needs to be doing this.
+ ///
/// # Examples
///
- /// Create a guild called `test` in the [US West region] with no icon:
+ /// Create a guild called `"test"` in the [US West region] with no icon:
///
/// ```rust,ignore
/// use serenity::model::Region;
@@ -194,6 +278,7 @@ impl Context {
///
/// [`Guild`]: ../model/struct.Guild.html
/// [US West region]: ../model/enum.Region.html#variant.UsWest
+ /// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
pub fn create_guild(&self, name: &str, region: Region, icon: Option<&str>)
-> Result<Guild> {
let map = ObjectBuilder::new()
@@ -205,6 +290,13 @@ impl Context {
http::create_guild(map)
}
+ /// Creates an [`Integration`] for a [`Guild`].
+ ///
+ /// **Note**: Requires the [Manage Guild] permission.
+ ///
+ /// [`Guild`]: ../model/struct.Guild.html
+ /// [`Integration`]: ../model/struct.Integration.html
+ /// [Manage Guild]: ../model/permissions/constant.MANAGE_GUILD.html
pub fn create_integration<G, I>(&self,
guild_id: G,
integration_id: I,
@@ -220,6 +312,16 @@ impl Context {
http::create_guild_integration(guild_id.into().0, integration_id.0, map)
}
+ /// Creates an invite for the channel, providing a builder so that fields
+ /// may optionally be set.
+ ///
+ /// See the documentation for the [`CreateInvite`] builder for information
+ /// on how to use this and the default values that it provides.
+ ///
+ /// **Note**: Requires the [Create Invite] permission.
+ ///
+ /// [`CreateInvite`]: ../utils/builder/struct.CreateInvite.html
+ /// [Create Invite]: ../model/permissions/constant.CREATE_INVITE.html
pub fn create_invite<C, F>(&self, channel_id: C, f: F) -> Result<RichInvite>
where C: Into<ChannelId>, F: FnOnce(CreateInvite) -> CreateInvite {
let map = f(CreateInvite::default()).0.build();
@@ -227,6 +329,72 @@ impl Context {
http::create_invite(channel_id.into().0, map)
}
+ /// Creates a [permission overwrite][`PermissionOverwrite`] for either a
+ /// single [`Member`] or [`Role`] within a [`Channel`].
+ ///
+ /// Refer to the documentation for [`PermissionOverwrite`]s for more
+ /// information.
+ ///
+ /// **Note**: Requires the [Manage Channels] permission.
+ ///
+ /// # Examples
+ ///
+ /// Creating a permission overwrite for a member by specifying the
+ /// [`PermissionOverwrite::Member`] variant, allowing it the [Send Messages]
+ /// permission, but denying the [Send TTS Messages] and [Attach Files]
+ /// permissions:
+ ///
+ /// ```rust,ignore
+ /// use serenity::model::{ChannelId, PermissionOverwrite, permissions};
+ ///
+ /// // assuming you are in a context
+ ///
+ /// let channel_id = 7;
+ /// let user_id = 8;
+ ///
+ /// let allow = permissions::SEND_MESSAGES;
+ /// let deny = permissions::SEND_TTS_MESSAGES | permissions::ATTACH_FILES;
+ /// let overwrite = PermissionOverwrite {
+ /// allow: allow,
+ /// deny: deny,
+ /// kind: PermissionOverwriteType::Member(user_id),
+ /// };
+ ///
+ /// let _result = context.create_permission(channel_id, overwrite);
+ /// ```
+ ///
+ /// Creating a permission overwrite for a role by specifying the
+ /// [`PermissionOverwrite::Role`] variant, allowing it the [Manage Webhooks]
+ /// permission, but denying the [Send TTS Messages] and [Attach Files]
+ /// permissions:
+ ///
+ /// ```rust,ignore
+ /// use serenity::model::{ChannelId, PermissionOverwrite, permissions};
+ ///
+ /// // assuming you are in a context
+ ///
+ /// let channel_id = 7;
+ /// let user_id = 8;
+ ///
+ /// let allow = permissions::SEND_MESSAGES;
+ /// let deny = permissions::SEND_TTS_MESSAGES | permissions::ATTACH_FILES;
+ /// let overwrite = PermissionOverwrite {
+ /// allow: allow,
+ /// deny: deny,
+ /// kind: PermissionOverwriteType::Member(user_id),
+ /// };
+ ///
+ /// let _result = context.create_permission(channel_id, overwrite);
+ /// ```
+ ///
+ /// [`Channel`]: ../model/enum.Channel.html
+ /// [`Member`]: ../model/struct.Member.html
+ /// [`PermissionOverwrite`]: ../model/struct.PermissionOverWrite.html
+ /// [`PermissionOverwrite::Member`]: ../model/struct.PermissionOverwrite.html#variant.Member
+ /// [`Role`]: ../model/struct.Role.html
+ /// [Attach Files]: ../model/permissions/constant.ATTACH_FILES.html
+ /// [Manage Channels]: ../model/permissions/constant.MANAGE_CHANNELS.html
+ /// [Send TTS Messages]: ../model/permissions/constant.SEND_TTS_MESSAGES.html
pub fn create_permission<C>(&self,
channel_id: C,
target: PermissionOverwrite)
@@ -246,7 +414,12 @@ impl Context {
http::create_permission(channel_id.into().0, id, map)
}
- pub fn create_private_channel<U>(&self, user_id: U)
+ /// Creates a direct message channel between the [current user] and another
+ /// [`User`]. This can also retrieve the channel if one already exists.
+ ///
+ /// [`User`]: ../model/struct.User.html
+ /// [current user]: ../model/struct.CurrentUser.html
+ pub fn create_direct_message_channel<U>(&self, user_id: U)
-> Result<PrivateChannel> where U: Into<UserId> {
let map = ObjectBuilder::new()
.insert("recipient_id", user_id.into().0)
@@ -257,11 +430,16 @@ impl Context {
/// React to a [`Message`] with a custom [`Emoji`] or unicode character.
///
- /// **Note**: Requires the [Add Reactions] permission.
+ /// [`Message::react`] may be a more suited method of reacting in most
+ /// cases.
+ ///
+ /// **Note**: Requires the [Add Reactions] permission, _if_ the current user
+ /// is the first user to perform a react with a certain emoji.
///
- /// [`Emoji`]: ../models/struct.Emoji.html
- /// [`Message`]: ../models/struct.Message.html
- /// [Add Reactions]: ../models/permissions/constant.ADD_REACTIONS.html
+ /// [`Emoji`]: ../model/struct.Emoji.html
+ /// [`Message`]: ../model/struct.Message.html
+ /// [`Message::react`]: ../model/struct.Message.html#method.react
+ /// [Add Reactions]: ../model/permissions/constant.ADD_REACTIONS.html
pub fn create_reaction<C, M, R>(&self,
channel_id: C,
message_id: M,
@@ -300,6 +478,12 @@ impl Context {
http::delete_channel(channel_id.into().0)
}
+ /// Deletes an emoji in a [`Guild`] given its Id.
+ ///
+ /// **Note**: Requires the [Manage Emojis] permission.
+ ///
+ /// [`Guild`]: ../model/struct.Guild.html
+ /// [Manage Emojis]: ../model/permissions/constant.MANAGE_EMOJIS.html
pub fn delete_emoji<E, G>(&self, guild_id: G, emoji_id: E) -> Result<()>
where E: Into<EmojiId>, G: Into<GuildId> {
http::delete_emoji(guild_id.into().0, emoji_id.into().0)
@@ -406,8 +590,8 @@ impl Context {
/// **Note**: Requires the [`Manage Messages`] permission, _if_ the current
/// user did not perform the reaction.
///
- /// [`Reaction`]: ../models/struct.Reaction.html
- /// [Manage Messages]: ../models/permissions/constant.MANAGE_MESSAGES.html
+ /// [`Reaction`]: ../model/struct.Reaction.html
+ /// [Manage Messages]: ../model/permissions/constant.MANAGE_MESSAGES.html
pub fn delete_reaction<C, M, R>(&self,
channel_id: C,
message_id: M,
diff --git a/src/client/http/mod.rs b/src/client/http/mod.rs
index 66bbb8b..ea15230 100644
--- a/src/client/http/mod.rs
+++ b/src/client/http/mod.rs
@@ -16,7 +16,13 @@
//!
//! If a request spuriously fails, it will be retried once.
//!
+//! Note that you may want to perform requests through a [`Context`] or through
+//! [model]s' instance methods where possible, as they each offer different
+//! levels of a high-level interface to the HTTP module.
+//!
//! [`Client`]: ../struct.Client.html
+//! [`Context`]: ../struct.Context.html
+//! [model]: ../../model/index.html
mod ratelimiting;
@@ -45,17 +51,68 @@ lazy_static! {
static ref TOKEN: Arc<Mutex<String>> = Arc::new(Mutex::new(String::default()));
}
+/// Sets the token to be used across all requests which require authentication.
+///
+/// This is really only for internal use, and if you are reading this as a user,
+/// you should _not_ use this yourself.
#[doc(hidden)]
pub fn set_token(token: &str) {
TOKEN.lock().unwrap().clone_from(&token.to_owned());
}
+/// Accepts the [`Invite`] given its code, placing the current user in the
+/// [`Guild`] that the invite was for.
+///
+/// Use [`utils::parse_invite`] to retrieve codes from URLs.
+///
+/// Refer to the documentation for [`Context::accept_invite`] for restrictions on
+/// accepting an invite.
+///
+/// This will fire the [`Client::on_guild_create`] handler once the associated
+/// event is received.
+///
+/// **Note**: This will fail if you are already in the guild, or are banned. A
+/// ban is equivilant to an IP ban.
+///
+/// **Note**: Requires that the current user be a user account. Bots can not
+/// accept invites. Instead, they must be accepted via OAuth2 authorization
+/// links. These are in the format of:
+///
+/// `https://discordapp.com/oauth2/authorize?client_id=CLIENT_ID&scope=bot`
+///
+/// # Examples
+///
+/// Accept an invite given a code from a URL:
+///
+/// ```rust,no_run
+/// use serenity::client::http;
+/// use serenity::utils;
+///
+/// let url = "https://discord.gg/0cDvIgU2voY8RSYL";
+/// let code = utils::parse_invite(url);
+///
+/// let _result = http::accept_invite(code);
+/// ```
+///
+/// [`Context::accept_invite`]: ../struct.Context.html#method.accept_invite
+/// [`Invite`]: ../../model/struct.Invite.html
+/// [`utils::parse_invite`]: ../../utils/fn.parse_invite.html
pub fn accept_invite(code: &str) -> Result<Invite> {
let response = request!(Route::InvitesCode, post, "/invites/{}", code);
Invite::decode(try!(serde_json::from_reader(response)))
}
+/// Marks a [`Channel`] as being "read" up to a certain [`Message`]. Any
+/// message past the given one will not be marked as read.
+///
+/// Usually you should use this to mark the latest message as being read.
+///
+/// **Note**: Bot users should not use this, as it has no bearing on them
+/// whatsoever.
+///
+/// [`Channel`]: ../../model/enum.Channel.html
+/// [`Message`]: ../../model/struct.Message.html
pub fn ack_message(channel_id: u64, message_id: u64) -> Result<()> {
verify(204, request!(Route::ChannelsIdMessagesIdAck(channel_id),
post,
@@ -64,6 +121,13 @@ pub fn ack_message(channel_id: u64, message_id: u64) -> Result<()> {
message_id))
}
+/// Adds a [`User`] as a recipient to a [`Group`].
+///
+/// **Note**: Groups have a limit of 10 recipients, including the current user.
+///
+/// [`Group`]: ../../model/struct.Group.html
+/// [`Group::add_recipient`]: ../../model/struct.Group.html#method.add_recipient
+/// [`User`]: ../../model/struct.User.html
pub fn add_group_recipient(group_id: u64, user_id: u64)
-> Result<()> {
verify(204, request!(Route::None,
@@ -91,6 +155,17 @@ pub fn add_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()>
role_id))
}
+/// Bans a [`User`] from a [`Guild`], removing their messages sent in the last
+/// X number of days.
+///
+/// Passing a `delete_message_days` of `0` is equivilant to not removing any
+/// messages. Up to `7` days' worth of messages may be deleted.
+///
+/// **Note**: Requires that you have the [Ban Members] permission.
+///
+/// [`Guild`]: ../../model/struct.Guild.html
+/// [`User`]: ../../model/struct.User.html
+/// [Ban Members]: ../../model/permissions/constant.BAN_MEMBERS.html
pub fn ban_user(guild_id: u64, user_id: u64, delete_message_days: u8)
-> Result<()> {
verify(204, request!(Route::GuildsIdBansUserId(guild_id),
@@ -101,6 +176,15 @@ pub fn ban_user(guild_id: u64, user_id: u64, delete_message_days: u8)
delete_message_days))
}
+/// Broadcasts that the current user is typing in the given [`Channel`].
+///
+/// This lasts for about 10 seconds, and will then need to be renewed to
+/// indicate that the current user is still typing.
+///
+/// This should rarely be used for bots, although it is a good indicator that a
+/// long-running command is still being processed.
+///
+/// [`Channel`]: ../../model/enum.Channel.html
pub fn broadcast_typing(channel_id: u64) -> Result<()> {
verify(204, request!(Route::ChannelsIdTyping(channel_id),
post,
@@ -108,6 +192,16 @@ pub fn broadcast_typing(channel_id: u64) -> Result<()> {
channel_id))
}
+/// Creates a [`PublicChannel`] in the [`Guild`] given its Id.
+///
+/// Refer to the Discord's [docs] for information on what fields this requires.
+///
+/// **Note**: Requires the [Manage Channels] permission.
+///
+/// [`Guild`]: ../../model/struct.Guild.html
+/// [`PublicChannel`]: ../../model/struct.PublicChannel.html
+/// [docs]: https://discordapp.com/developers/docs/resources/guild#create-guild-channel
+/// [Manage Channels]: ../../model/permissions/constant.MANAGE_CHANNELS.html
pub fn create_channel(guild_id: u64, map: Value) -> Result<Channel> {
let body = try!(serde_json::to_string(&map));
let response = request!(Route::GuildsIdChannels(guild_id),
@@ -118,6 +212,16 @@ pub fn create_channel(guild_id: u64, map: Value) -> Result<Channel> {
Channel::decode(try!(serde_json::from_reader(response)))
}
+/// Creates an emoji in the given [`Guild`] with the given data.
+///
+/// View the source code for [`Context::create_emoji`] to see what fields this
+/// requires.
+///
+/// **Note**: Requires the [Manage Emojis] permission.
+///
+/// [`Context::create_emoji`]: ../struct.Context.html#method.create_emoji
+/// [`Guild`]: ../../model/struct.Guild.html
+/// [Manage Emojis]: ../../model/permissions/constant.MANAGE_EMOJIS.html
pub fn create_emoji(guild_id: u64, map: Value)
-> Result<Emoji> {
let body = try!(serde_json::to_string(&map));
@@ -129,6 +233,35 @@ pub fn create_emoji(guild_id: u64, map: Value)
Emoji::decode(try!(serde_json::from_reader(response)))
}
+/// Creates a [`Guild`] with the data provided.
+///
+/// **Note**: This endpoint is usually only available for user accounts. Refer
+/// to Discord's documentation for the endpoint [here][whitelist] for more
+/// information. If your bot requires this, re-think what you are doing and
+/// whether it _really_ needs to be doing this.
+///
+/// # Examples
+///
+/// Create a guild called `"test"` in the [US West region]:
+///
+/// ```rust,ignore
+/// extern crate serde_json;
+///
+/// use serde_json::builder::ObjectBuilder;
+/// use serde_json::Value;
+/// use serenity::client::http;
+///
+/// let map = ObjectBuilder::new()
+/// .insert("name", "test")
+/// .insert("region", "us-west")
+/// .build();
+///
+/// let _result = http::create_guild(map);
+/// ```
+///
+/// [`Guild`]: ../../model/struct.Guild.html
+/// [US West Region]: ../../model/enum.Region.html#variant.UsWest
+/// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
pub fn create_guild(map: Value) -> Result<Guild> {
let body = try!(serde_json::to_string(&map));
let response = request!(Route::Guilds, post(body), "/guilds");
@@ -136,9 +269,18 @@ pub fn create_guild(map: Value) -> Result<Guild> {
Guild::decode(try!(serde_json::from_reader(response)))
}
-pub fn create_guild_integration(guild_id: u64,
- integration_id: u64,
- map: Value) -> Result<()> {
+/// Creates an [`Integration`] for a [`Guild`].
+///
+/// Refer to Discord's [docs] for field information.
+///
+/// **Note**: Requires the [Manage Guild] permission.
+///
+/// [`Guild`]: ../../model/struct.Guild.html
+/// [`Integration`]: ../../model/struct.Integration.html
+/// [Manage Guild]: ../../model/permissions/constant.MANAGE_GUILD.html
+/// [docs]: https://discordapp.com/developers/docs/resources/guild#create-guild-integration
+pub fn create_guild_integration(guild_id: u64, integration_id: u64, map: Value)
+ -> Result<()> {
let body = try!(serde_json::to_string(&map));
verify(204, request!(Route::GuildsIdIntegrations(guild_id),
@@ -148,6 +290,18 @@ pub fn create_guild_integration(guild_id: u64,
integration_id))
}
+/// Creates a [`RichInvite`] for the given [channel][`PublicChannel`].
+///
+/// Refer to Discord's [docs] for field information.
+///
+/// All fields are optional.
+///
+/// **Note**: Requires the [Create Invite] permission.
+///
+/// [`PublicChannel`]: ../../model/struct.PublicChannel.html
+/// [`RichInvite`]: ../../model/struct.RichInvite.html
+/// [Create Invite]: ../../model/permissions/constant.CREATE_INVITE.html
+/// [docs]: https://discordapp.com/developers/docs/resources/channel#create-channel-invite
pub fn create_invite(channel_id: u64, map: Value)
-> Result<RichInvite> {
let body = try!(serde_json::to_string(&map));
diff --git a/src/client/http/ratelimiting.rs b/src/client/http/ratelimiting.rs
index 3518bdc..9281563 100644
--- a/src/client/http/ratelimiting.rs
+++ b/src/client/http/ratelimiting.rs
@@ -1,3 +1,44 @@
+//! Routes are used for ratelimiting. These are to differentiate between the
+//! different _types_ of routes - such as getting the current user's channels -
+//! for the most part, with the exception being major parameters.
+//!
+//! [Taken from] the Discord docs, major parameters are:
+//!
+//! > Additionally, rate limits take into account major parameters in the URL.
+//! > For example, `/channels/:channel_id` and
+//! > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
+//! > account when generating rate limits since it's the major parameter. The
+//! only current major parameters are `channel_id` and `guild_id`.
+//!
+//! This results in the two URIs of `GET /channels/4/messages/7` and
+//! `GET /channels/5/messages/8` being rate limited _separately_. However, the
+//! two URIs of `GET /channels/10/messages/11` and
+//! `GET /channels/10/messages/12` will count towards the "same ratelimit", as
+//! the major parameter - `10` is equivilant in both URIs' format.
+//!
+//! # Examples
+//!
+//! First: taking the first two URIs - `GET /channels/4/messages/7` and
+//! `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
+//! `10`, requesting the first URI will result in the response containing a
+//! `remaining` of `9`. Immediately after - prior to buckets resetting -
+//! performing a request to the _second_ URI will also contain a `remaining` of
+//! `9` in the response, as the major parameter - `channel_id` - is different
+//! in the two requests (`4` and `5`).
+//!
+//! Second: take for example the last two URIs. Assuming the bucket's `limit` is
+//! `10`, requesting the first URI will return a `remaining` of `9` in the
+//! response. Immediately after - prior to buckets resetting - performing a
+//! request to the _second_ URI will return a `remaining` of `8` in the
+//! response, as the major parameter - `channel_id` - is equivilant for the two
+//! requests (`10`).
+//!
+//!
+//! With the examples out of the way: major parameters are why some variants
+//! (i.e. all of the channel/guild variants) have an associated u64 as data.
+//! This is the Id of the parameter, differentiating between different
+//! ratelimits.
+
use hyper::client::{RequestBuilder, Response};
use hyper::header::Headers;
use hyper::status::StatusCode;
@@ -14,46 +55,6 @@ lazy_static! {
static ref ROUTES: Arc<Mutex<HashMap<Route, RateLimit>>> = Arc::new(Mutex::new(HashMap::default()));
}
-/// Routes are used for ratelimiting. These are to differentiate between the
-/// different _types_ of routes - such as getting the current user's channels -
-/// for the most part, with the exception being major parameters.
-///
-/// [Taken from] the Discord docs, major parameters are:
-///
-/// > Additionally, rate limits take into account major parameters in the URL.
-/// > For example, `/channels/:channel_id` and
-/// > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
-/// > account when generating rate limits since it's the major parameter. The
-/// only current major parameters are `channel_id` and `guild_id`.
-///
-/// This results in the two URIs of `GET /channels/4/messages/7` and
-/// `GET /channels/5/messages/8` being rate limited _separately_. However, the
-/// two URIs of `GET /channels/10/messages/11` and
-/// `GET /channels/10/messages/12` will count towards the "same ratelimit", as
-/// the major parameter - `10` is equivilant in both URIs.
-///
-/// # Examples
-///
-/// First: taking the first two URIs - `GET /channels/4/messages/7` and
-/// `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
-/// `10`, requesting the first URI will result in the response containing a
-/// `remaining` of `9`. Immediately after - prior to buckets resetting -
-/// performing a request to the _second_ URI will also contain a `remaining` of
-/// `9` in the response, as the major parameter - `channel_id` - is different
-/// in the two requests (`4` and `5`).
-///
-/// Second: take for example the last two URIs. Assuming the bucket's `limit` is
-/// `10`, requesting the first URI will return a `remaining` of `9` in the
-/// response. Immediately after - prior to buckets resetting - performing a
-/// request to the _second_ URI will return a `remaining` of `8` in the
-/// response, as the major parameter - `channel_id` - is equivilant for the two
-/// requests (`10`).
-///
-///
-/// With the examples out of the way: major parameters are why some variants
-/// (i.e. all of the channel/guild variants) have an associated u64 as data.
-/// This is the Id of the parameter, differentiating between different
-/// ratelimits.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Route {
ChannelsId(u64),
diff --git a/src/client/login_type.rs b/src/client/login_type.rs
index e62f19a..468ead5 100644
--- a/src/client/login_type.rs
+++ b/src/client/login_type.rs
@@ -1,5 +1,24 @@
+/// The type of login to perform.
+///
+/// Use [`Bot`] if you are using a bot which responds to others, created through
+/// the [applications page]. See the [`README`] for more information on using
+/// bots.
+///
+/// Use [`User`] if you are creating a selfbot which responds only to you.
+///
+/// [`Bot`]: #variant.Bot
+/// [`README`]: https://github.com/zeyla/serenity.rs/blob/master/README.md#Bots
+/// [`User`]: #variant.User
+/// [applications page]: https://discordapp.com/developers/applications/me
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Ord, PartialOrd)]
pub enum LoginType {
+ /// An indicator to login as a bot. This will automatically prefix your
+ /// token with `"Bot "`, which is a requirement by Discord.
Bot,
+ /// An indicator to login under your own user account token. Only use this
+ /// if you are creating a "selfbot", which triggers on events from yourself.
+ ///
+ /// **Note**: _Do not_ use this for a "userbot" which responds to others, or
+ /// you _can_ be banned.
User,
}
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 37bb6dc..7665955 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -1,37 +1,25 @@
-//! The Client contains information about a single bot or user's "session" with
-//! Discord. Event handers and starting the connection are handled directly via
-//! the client. In addition, the [http module] and [`State`] are also
-//! automatically handled by the Client module for you.
+//! The Client contains information about a single bot or user's token, as well
+//! as event handlers. Dispatching events to configured handlers and starting
+//! the connection are handled directly via the client. In addition, the
+//! [`http`] module and [`State`] are also automatically handled by the Client
+//! module for you.
//!
-//! A [`Context`] is provided for every handler. The
-//! context is an ergonomic way of accessing the lower-level Http struct's
-//! methods.
+//! A [`Context`] is provided for every handler. The context is an ergonomic
+//! method of accessing the lower-level http functions.
//!
-//! The Http struct is the lower-level method of accessing the Discord REST API.
-//! Realistically there should be little reason to use this yourself, as the
-//! Context will do this for you. A possible use case of using the Http struct
-//! is if you do not have a state for purposes such as low memory requirements.
+//! The `http` module is the lower-level method of interacting with the Discord
+//! REST API. Realistically, there should be little reason to use this yourself,
+//! as the Context will do this for you. A possible use case of using the `http`
+//! module is if you do not have a State, for purposes such as low memory
+//! requirements.
//!
-//! Creating a Client instance and adding a handler on every message
-//! receive, acting as a "ping-pong" bot is simple:
-//!
-//! ```rust,ignore
-//! use serenity::Client;
-//!
-//! let mut client = Client::login_bot("my token here");
-//!
-//! client.on_message(|context, message| {
-//! if message.content == "!ping" {
-//! context.say("Pong!");
-//! }
-//! });
-//!
-//! client.start();
-//! ```
+//! Click [here][Client examples] for an example on how to use a `Client`.
//!
+//! [`Client`]: struct.Client.html#examples
//! [`Context`]: struct.Context.html
-//! [`State`]: ext/state/index.html
-//! [http module]: client/http/index.html
+//! [`State`]: ../ext/state/index.html
+//! [`http`]: http/index.html
+//! [Client examples]: struct.Client.html#examples
pub mod http;
@@ -100,30 +88,45 @@ lazy_static! {
/// # Examples
///
/// Matching an [`Error`] with this variant may look something like the
-/// following for the [`Context::ban_user`] method:
+/// following for the [`Client::ban`] method, which in this example is used to
+/// re-ban all members with an odd discriminator:
///
-/// ```rust,ignore
-/// use serenity::client::ClientError;
+/// ```rust,no_run
+/// use serenity::client::{Client, ClientError};
/// use serenity::Error;
+/// use std::env;
+///
+/// let token = env::var("DISCORD_BOT_TOKEN").unwrap();
+/// let mut client = Client::login_bot(&token);
+///
+/// client.on_member_unban(|context, guild_id, user| {
+/// let discriminator = match user.discriminator.parse::<u16>() {
+/// Ok(discriminator) => discriminator,
+/// Err(_why) => return,
+/// };
///
-/// // assuming you are in a context and a `guild_id` has been bound
+/// // If the user has an even discriminator, don't re-ban them.
+/// if discriminator % 2 == 0 {
+/// return;
+/// }
///
-/// match context.ban_user(context.guild_id, context.message.author, 8) {
-/// Ok(()) => {
-/// // Ban successful.
-/// },
-/// Err(Error::Client(ClientError::DeleteMessageDaysAmount(amount))) => {
-/// println!("Tried deleting {} days' worth of messages", amount);
-/// },
-/// Err(why) => {
-/// println!("Unexpected error: {:?}", why);
-/// },
-/// }
+/// match context.ban(guild_id, user, 8) {
+/// Ok(()) => {
+/// // Ban successful.
+/// },
+/// Err(Error::Client(ClientError::DeleteMessageDaysAmount(amount))) => {
+/// println!("Failed deleting {} days' worth of messages", amount);
+/// },
+/// Err(why) => {
+/// println!("Unexpected error: {:?}", why);
+/// },
+/// }
+/// });
/// ```
///
/// [`Client`]: struct.Client.html
/// [`Context`]: struct.Context.html
-/// [`Context::ban_user`]: struct.Context.html#method.ban_user
+/// [`Context::ban`]: struct.Context.html#method.ban
/// [`Error::Client`]: ../enum.Error.html#variant.Client
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum ClientError {
@@ -222,7 +225,50 @@ pub enum ClientError {
UnknownStatus(u16),
}
+/// The Client is the way to "login" and be able to start sending authenticated
+/// requests over the REST API, as well as initializing a WebSocket
+/// [`Connection`]. Refer to `Connection`'s [information on using sharding] for
+/// more information.
+///
+/// # Event Handlers
+///
+/// Event handlers can be configured. For example, the event handler
+/// [`on_message`] will be dispatched to whenever a [`Event::MessageCreate`] is
+/// received over the connection.
+///
+/// Note that you do not need to manually handle events, as they are handled
+/// internally and then dispatched to your event handlers.
+///
+/// # Examples
+///
+/// Creating a Client instance and adding a handler on every message
+/// receive, acting as a "ping-pong" bot is simple:
+///
+/// ```rust,ignore
+/// use serenity::Client;
+///
+/// let mut client = Client::login_bot("my token here");
+///
+/// client.on_message(|context, message| {
+/// if message.content == "!ping" {
+/// context.say("Pong!");
+/// }
+/// });
+///
+/// client.start();
+/// ```
+///
+/// [`Connection`]: struct.Connection.html
+/// [`on_message`]: #method.on_message
+/// [`Event::MessageCreate`]: ../model/enum.Event.html#variant.MessageCreate
+/// [information on using sharding]: struct.Connection.html#sharding
pub struct Client {
+ /// A vector of all active connections that have received their
+ /// [`Event::Ready`] payload, and have dispatched to [`on_ready`] if an
+ /// event handler was configured.
+ ///
+ /// [`Event::Ready`]: ../model/enum.Event.html#variant.Ready
+ /// [`on_ready`]: #method.on_ready
pub connections: Vec<Arc<Mutex<Connection>>>,
event_store: Arc<Mutex<EventStore>>,
#[cfg(feature="framework")]
@@ -233,19 +279,33 @@ pub struct Client {
#[allow(type_complexity)]
impl Client {
- /// Creates a Client for a bot.
+ /// Creates a Client for a bot user.
+ ///
+ /// Discord has a requirement of prefixing bot tokens with `"Bot "`, which
+ /// this function will automatically do for you.
pub fn login_bot(bot_token: &str) -> Client {
let token = format!("Bot {}", bot_token);
login(&token, LoginType::Bot)
}
- /// Create an instance from "raw values"
+
+ /// Create an instance from "raw values". This allows you to manually
+ /// specify whether to login as a [`Bot`] or [`User`], and does not modify
+ /// the token in any way regardless.
+ ///
+ /// [`Bot`]: enum.LoginType.html#variant.Bot
+ /// [`User`]: enum.LoginType.html#variant.User
#[doc(hidden)]
pub fn login_raw(token: &str, login_type: LoginType) -> Client {
login(&token.to_owned(), login_type)
}
/// Creates a Client for a user.
+ ///
+ /// **Note**: Read the notes for [`LoginType::User`] prior to using this, as
+ /// there are restrictions on usage.
+ ///
+ /// [`LoginType::User`]: enum.LoginType.html#variant.User
pub fn login_user(user_token: &str) -> Client {
login(&user_token.to_owned(), LoginType::User)
}
@@ -376,8 +436,8 @@ impl Client {
/// use serenity::Client;
/// use std::env;
///
- /// let mut client = Client::login_bot(&env::var("DISCORD_BOT_TOKEN")
- /// .unwrap());
+ /// let token = env::var("DISCORD_BOT_TOKEN").unwrap();
+ /// let mut client = Client::login_bot(&token);
///
/// let _ = client.start_shard_range([4, 7], 10);
/// ```
@@ -699,12 +759,12 @@ impl Client {
///
/// Print the [current user][`CurrentUser`]'s name on ready:
///
- /// ```rust,ignore
+ /// ```rust,no_run
/// use serenity::Client;
/// use std::env;
///
- /// let mut client = Client::login_bot(&env::var("DISCORD_BOT_TOKEN")
- /// .unwrap());
+ /// let token = env::var("DISCORD_BOT_TOKEN").unwrap();
+ /// let mut client = Client::login_bot(&token);
///
/// client.on_ready(|_context, ready| {
/// println!("{} is connected", ready.user.name);