diff options
| author | Austin Hellyer <[email protected]> | 2016-11-18 11:00:45 -0800 |
|---|---|---|
| committer | Austin Hellyer <[email protected]> | 2016-11-18 11:00:45 -0800 |
| commit | cf128b1a10d0636c8b4b5233d46b068cfe665688 (patch) | |
| tree | f36b1cd08d00cec69a76eb10c493ab3b86d61678 /src | |
| parent | Feature macros should use else as block separator (diff) | |
| download | serenity-cf128b1a10d0636c8b4b5233d46b068cfe665688.tar.xz serenity-cf128b1a10d0636c8b4b5233d46b068cfe665688.zip | |
A bit of docs
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/context.rs | 228 | ||||
| -rw-r--r-- | src/client/http/mod.rs | 160 | ||||
| -rw-r--r-- | src/client/http/ratelimiting.rs | 81 | ||||
| -rw-r--r-- | src/client/login_type.rs | 19 | ||||
| -rw-r--r-- | src/client/mod.rs | 164 | ||||
| -rw-r--r-- | src/error.rs | 28 | ||||
| -rw-r--r-- | src/ext/framework/configuration.rs | 59 | ||||
| -rw-r--r-- | src/ext/framework/mod.rs | 62 | ||||
| -rw-r--r-- | src/ext/state/mod.rs | 40 | ||||
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/model/channel.rs | 4 | ||||
| -rw-r--r-- | src/model/invite.rs | 46 | ||||
| -rw-r--r-- | src/utils/builder/create_embed.rs | 4 | ||||
| -rw-r--r-- | src/utils/builder/create_invite.rs | 1 | ||||
| -rw-r--r-- | src/utils/builder/execute_webhook.rs | 64 | ||||
| -rw-r--r-- | src/utils/builder/get_messages.rs | 1 | ||||
| -rw-r--r-- | src/utils/colour.rs | 160 | ||||
| -rw-r--r-- | src/utils/message_builder.rs | 77 | ||||
| -rw-r--r-- | src/utils/mod.rs | 31 |
19 files changed, 1045 insertions, 186 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); diff --git a/src/error.rs b/src/error.rs index 97524b0..84f4e04 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,10 +10,17 @@ use ::client::{ClientError, ConnectionError}; use ::ext::voice::VoiceError; /// The common result type between most library functions. +/// +/// The library exposes functions which, for a result type, exposes only one +/// type, rather than the usual 2 (`Result<T, Error>`). This is because all +/// functions that return a result return serenity's [`Error`], so this is +/// implied, and a "simpler" result is used. +/// +/// [`Error`]: enum.Error.html pub type Result<T> = ::std::result::Result<T, Error>; /// A common error enum returned by most of the library's functionality within a -/// [`Result`]. +/// custom [`Result`]. /// /// The most common error types, the [`ClientError`] and [`ConnectionError`] /// enums, are both wrapped around this in the form of the [`Client`] and @@ -26,9 +33,14 @@ pub type Result<T> = ::std::result::Result<T, Error>; /// [`Result`]: type.Result.html #[derive(Debug)] pub enum Error { - /// An Http or Client error. + /// An [http] or [client] error. + /// + /// [client]: client/index.html + /// [http]: client/http/index.html Client(ClientError), - /// An error with the WebSocket connection. + /// An error with the WebSocket [`Connection`]. + /// + /// [`Connection`]: client/struct.Connection.html Connection(ConnectionError), /// An error while decoding a payload. Decode(&'static str, Value), @@ -38,11 +50,17 @@ pub enum Error { Io(IoError), /// An error from the `serde_json` crate. Json(JsonError), - /// Some other error. + /// Some other error. This is only used for "Expected value <TYPE>" errors, + /// when a more detailed error can not be easily provided via the + /// [`Error::Decode`] variant. + /// + /// [`Error::Decode`]: #variant.Decode Other(&'static str), /// An error from the `url` crate. Url(String), - /// Indicating an error within the voice module. + /// Indicating an error within the [voice module]. + /// + /// [voice module]: ext/voice/index.html #[cfg(feature="voice")] Voice(VoiceError), /// An error from the `rust-websocket` crate. diff --git a/src/ext/framework/configuration.rs b/src/ext/framework/configuration.rs index 09874c8..4b0fc48 100644 --- a/src/ext/framework/configuration.rs +++ b/src/ext/framework/configuration.rs @@ -2,20 +2,52 @@ use std::default::Default; use ::client::http; pub struct Configuration { + #[doc(hidden)] pub depth: usize, + #[doc(hidden)] pub on_mention: Option<Vec<String>>, + #[doc(hidden)] pub allow_whitespace: bool, + #[doc(hidden)] pub prefix: Option<String>, } impl Configuration { /// The default depth of the message to check for commands. Defaults to 5. + /// This determines how "far" into a message to check for a valid command. + /// + /// # Examples + /// + /// If you set a depth of `1`, and make a command of `"music play"`, but + /// not a `"music"` command, then the former command will never be + /// triggered, as its "depth" is `2`. pub fn depth(mut self, depth: u8) -> Self { self.depth = depth as usize; self } + /// Whether or not to respond to commands initiated with a mention. Note + /// that this can be used in conjunction with [`prefix`]. + /// + /// By default this is set to `false`. + /// + /// # Examples + /// + /// Setting this to `true` will allow the following types of mentions to be + /// responded to: + /// + /// ```ignore + /// <@245571012924538880> about + /// <@!245571012924538880> about + /// ``` + /// + /// The former is a direct mention, while the latter is a nickname mention, + /// which aids mobile devices in determining whether to display a user's + /// nickname. It has no real meaning for your bot, and the library + /// encourages you to ignore differentiating between the two. + /// + /// [`prefix`]: #method.prefix pub fn on_mention(mut self, on_mention: bool) -> Self { if !on_mention { return self; @@ -31,18 +63,27 @@ impl Configuration { self } - /// Whether to allow whitespace being optional between a mention and a - /// command. + /// Whether to allow whitespace being optional between a mention/prefix and + /// a command. /// /// **Note**: Defaults to `false`. /// /// # Examples /// - /// Setting this to `true` will allow this scenario to occur, while `false` - /// will not: + /// Setting this to `false` will _only_ allow this scenario to occur: /// /// ```ignore - /// <@BOT_ID>about + /// <@245571012924538880> about + /// !about + /// + /// // bot processes and executes the "about" command if it exists + /// ``` + /// + /// while setting this to `true` will _also_ allow this scenario to occur: + /// + /// ```ignore + /// <@245571012924538880>about + /// ! about /// /// // bot processes and executes the "about" command if it exists /// ``` @@ -53,6 +94,8 @@ impl Configuration { self } + /// Sets the prefix to respond to. This can either be a single-char or + /// multi-char string. pub fn prefix<S: Into<String>>(mut self, prefix: S) -> Self { self.prefix = Some(prefix.into()); @@ -61,6 +104,12 @@ impl Configuration { } impl Default for Configuration { + /// Builds a default framework configuration, setting the following: + /// + /// - **allow_whitespace** to `false` + /// - **depth** to `5` + /// - **on_mention** to `false` (basically) + /// - **prefix** to `None` fn default() -> Configuration { Configuration { depth: 5, diff --git a/src/ext/framework/mod.rs b/src/ext/framework/mod.rs index c5e68ec..2c1ffb5 100644 --- a/src/ext/framework/mod.rs +++ b/src/ext/framework/mod.rs @@ -11,8 +11,29 @@ use std::thread; use ::client::Context; use ::model::Message; +/// The type of command being received. +/// +/// The [`Mention`] variant is emitted if the bot is being commanded via a +/// mention (`<@USER_ID>` or `<@!USER_ID>`). This can only be emitted if +/// [`Configuration::on_mention`] is set to `true`. +/// +/// The [`Prefix`] variant is emitted if a message starts with the prefix set +/// via [`Configuration::prefix`]. +/// +/// [`Mention`]: #variant.Mention +/// [`Prefix`]: #variant.Prefix +// This is public due to being leaked by [`command::positions`], which is used +// in [`Framework::dispatch`]. It therefore is hidden from the docs, due to +// having no use to users. +// +// [`Framework::dispatch`]: struct.Framework.html#method.dispatch +// [`command::positions`]: command/fn.positions.html #[derive(Clone, Copy, Debug)] +#[doc(hidden)] pub enum CommandType { + /// This is emitted if the bot is being commanded via a mention + /// (`<@USER_ID>` or `<@!USER_ID>`). This can only be emitted if + /// [`Configuration::on_mention`] is set to `true`. Mention, None, Prefix, @@ -24,10 +45,51 @@ pub struct Framework { configuration: Configuration, commands: HashMap<String, InternalCommand>, checks: HashMap<String, Arc<Fn(&Context, &Message) -> bool + Send + Sync + 'static>>, + /// Whether the framework has been "initialized". + /// + /// The framework is initialized once one of the following occurs: + /// + /// - configuration has been set; + /// - a command handler has been set; + /// - a command check has been set. + /// + /// This is used internally to determine whether or not - in addition to + /// dispatching to the [`Client::on_message`] handler - to have the + /// framework check if a [`Event::MessageCreate`] should be processed by + /// itself. + /// + /// [`Client::on_message`]: ../../client/struct.Client.html#method.on_message + /// [`Event::MessageCreate`]: ../../model/enum.Event.html#variant.MessageCreate pub initialized: bool, } impl Framework { + /// Configures the framework, setting non-default values. All fields are + /// optional. Refer to [`Configuration::default`] for more information on + /// the default values. + /// + /// # Examples + /// + /// Configuring the framework for a [`Client`], setting the [`depth`] to 3, + /// [allowing whitespace], and setting the [`prefix`] to `"~"`: + /// + /// ```rust,no_run + /// use serenity::Client; + /// use std::env; + /// + /// let mut client = Client::login_bot(&env::var("DISCORD_TOKEN").unwrap()); + /// client.with_framework(|f| f + /// .configure(|c| c + /// .depth(3) + /// .allow_whitespace(true) + /// .prefix("~"))); + /// ``` + /// + /// [`Client`]: ../../client/struct.Client.html + /// [`Configuration::default`]: struct.Configuration.html#method.default + /// [`depth`]: struct.Configuration.html#method.depth + /// [`prefix`]: struct.Configuration.html#method.prefix + /// [allowing whitespace]: struct.Configuration.html#method.allow_whitespace pub fn configure<F>(mut self, f: F) -> Self where F: FnOnce(Configuration) -> Configuration { self.configuration = f(self.configuration); diff --git a/src/ext/state/mod.rs b/src/ext/state/mod.rs index fad2a60..a97cc71 100644 --- a/src/ext/state/mod.rs +++ b/src/ext/state/mod.rs @@ -4,10 +4,48 @@ use std::default::Default; use std::mem; use ::model::*; -/// Known state composed from received events. +/// A state of all events received over a [`Connection`], where storing at least +/// some data from the event is possible. +/// +/// This acts as a 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. +/// +/// # Use by the Context +/// +/// The [`Context`] will automatically attempt to pull from the state for you. +/// For example, the [`Context::get_channel`] method will attempt to find the +/// channel in the state. 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 State. +/// +/// This allows you to only need to perform the `Context::get_channel` call, +/// and not need to first search through the state - 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. +/// +/// [`Connection`]: ../../client/struct.Connection.html +/// [`Context`]: ../../client/struct.Context.html +/// [`Context::get_channel`]: ../../client/struct.Context.html#method.get_channel +/// [`LiveGuild`]: ../../model/struct.LiveGuild.html +/// [`Role`]: ../../model/struct.Role.html +/// [`http`]: ../../client/http/index.html #[derive(Debug, Clone)] pub struct State { + /// A map of the currently active calls that the current user knows about, + /// where the key is the Id of the [`PrivateChannel`] or [`Group`] hosting + /// the call. + /// + /// For bot users this will almost always be empty. 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. pub groups: HashMap<ChannelId, Group>, /// Settings specific to a guild. /// @@ -40,7 +40,7 @@ //! //! A basic ping-pong bot looks like: //! -//! ```rust,ignore +//! ```rust,no_run //! extern crate serenity; //! //! use serenity::Client; diff --git a/src/model/channel.rs b/src/model/channel.rs index 0534ff3..e994d4b 100644 --- a/src/model/channel.rs +++ b/src/model/channel.rs @@ -284,6 +284,8 @@ impl Group { /// Adds the given user to the group. If the user is already in the group, /// then nothing is done. /// + /// Refer to [`http::add_group_recipient`] for more information. + /// /// **Note**: Groups have a limit of 10 recipients, including the current /// user. #[cfg(feature = "methods")] @@ -1066,7 +1068,7 @@ impl ReactionType { pub fn as_data(&self) -> String { match *self { ReactionType::Custom { id, ref name } => { - format!("{}:{}", id, name) + format!("{}:{}", name, id) }, ReactionType::Unicode(ref unicode) => unicode.clone(), } diff --git a/src/model/invite.rs b/src/model/invite.rs index 55467e0..58773d1 100644 --- a/src/model/invite.rs +++ b/src/model/invite.rs @@ -8,26 +8,25 @@ use ::client::STATE; impl Invite { /// Accepts the invite, placing the current user in the [`Guild`] that the - /// invite was for. This will fire the [`Client::on_guild_create`] handler - /// once the associated event is received. + /// invite was for. /// - /// **Note**: This will fail if you are already in the `Guild`, or are - /// banned. A ban is equivilant to an IP ban. + /// Refer to [`http::accept_invite`] for more information. /// - /// **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: + /// **Note**: This will fail if you are already in the guild, or are banned. + /// A ban is equivilant to an IP ban. /// - /// `https://discordapp.com/oauth2/authorize?client_id=CLIENT_ID&scope=bot` + /// **Note**: Requires that the current user be a user account. /// /// # Errors /// - /// Returns a [`ClientError::InvalidOperationAsBot`] if the current user is - /// a bot user. + /// If the `state` features is enabled, then this returns a + /// [`ClientError::InvalidOperationAsBot`] if the current user does not have + /// the required [permission]. /// /// [`ClientError::InvalidOperationAsBot`]: enum.ClientError.html#variant.InvalidOperationAsBot - /// [`Client::on_guild_create`]: ../client/struct.Client.html#method.on_guild_create /// [`Guild`]: struct.Guild.html + /// [`http::accept_invite`]: ../client/http/fn.accept_invite.html + /// [permission]: permissions/index.html #[cfg(feature="methods")] pub fn accept(&self) -> Result<Invite> { feature_state_enabled! {{ @@ -50,6 +49,7 @@ impl Invite { /// /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html + /// [permission]: permissions/index.html #[cfg(feature="methods")] pub fn delete(&self) -> Result<Invite> { let req = permissions::MANAGE_GUILD; @@ -64,11 +64,12 @@ impl Invite { impl RichInvite { /// Accepts the invite, placing the current user in the [`Guild`] that the - /// invite was for. This will fire the [`Client::on_guild_create`] handler - /// once the associated event is received. + /// invite was for. /// - /// Refer to the documentation for [`Invite::accept`] for restrictions on - /// accepting an invite. + /// Refer to [`http::accept_invite`] for more information. + /// + /// **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. /// @@ -78,7 +79,8 @@ impl RichInvite { /// a bot user. /// /// [`ClientError::InvalidOperationAsBot`]: enum.ClientError.html#variant.InvalidOperationAsBot - /// [`Invite::accept`]: struct.Invite.html#method.accept + /// [`Guild`]: struct.Guild.html + /// [`http::accept_invite`]: ../client/http/fn.accept_invite.html #[cfg(feature="methods")] pub fn accept(&self) -> Result<Invite> { feature_state_enabled! {{ @@ -92,17 +94,21 @@ impl RichInvite { /// Deletes the invite. /// - /// Refer to the documentation for [`Invite::delete`] for restrictions on - /// deleting an invite. + /// Refer to [`http::delete_invite`] for more information. + /// + /// **Note**: Requires the [Manage Guild] permission. /// /// # Errors /// - /// Returns a [`ClientError::InvalidPermissions`] if the current user does - /// not have the required [permission]. + /// If the `state` feature is enabled, then this returns a + /// [`ClientError::InvalidPermissions`] if the current user does not have + /// the required [permission]. /// /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [`Invite::delete`]: struct.Invite.html#method.delete + /// [`http::delete_invite`]: ../client/http/fn.delete_invite.html /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html + /// [permission]: permissions/index.html #[cfg(feature="methods")] pub fn delete(&self) -> Result<Invite> { let req = permissions::MANAGE_GUILD; diff --git a/src/utils/builder/create_embed.rs b/src/utils/builder/create_embed.rs index 4264175..fe15ed4 100644 --- a/src/utils/builder/create_embed.rs +++ b/src/utils/builder/create_embed.rs @@ -197,6 +197,7 @@ impl CreateEmbedAuthor { } impl Default for CreateEmbedAuthor { + /// Creates a builder with no default values. fn default() -> CreateEmbedAuthor { CreateEmbedAuthor(ObjectBuilder::new()) } @@ -259,6 +260,7 @@ impl CreateEmbedFooter { } impl Default for CreateEmbedFooter { + /// Creates a builder with no default values. fn default() -> CreateEmbedFooter { CreateEmbedFooter(ObjectBuilder::new()) } @@ -294,6 +296,7 @@ impl CreateEmbedThumbnail { } impl Default for CreateEmbedThumbnail { + /// Creates a builder with no default values. fn default() -> CreateEmbedThumbnail { CreateEmbedThumbnail(ObjectBuilder::new()) } @@ -329,6 +332,7 @@ impl CreateEmbedVideo { } impl Default for CreateEmbedVideo { + /// Creates a builder with no default values. fn default() -> CreateEmbedVideo { CreateEmbedVideo(ObjectBuilder::new()) } diff --git a/src/utils/builder/create_invite.rs b/src/utils/builder/create_invite.rs index 55cc1bd..c8e175a 100644 --- a/src/utils/builder/create_invite.rs +++ b/src/utils/builder/create_invite.rs @@ -65,6 +65,7 @@ impl CreateInvite { } impl Default for CreateInvite { + /// Creates a builder with default values, setting `validate` to `null`. fn default() -> CreateInvite { CreateInvite(ObjectBuilder::new().insert("validate", Value::Null)) } diff --git a/src/utils/builder/execute_webhook.rs b/src/utils/builder/execute_webhook.rs index d2a14aa..a434057 100644 --- a/src/utils/builder/execute_webhook.rs +++ b/src/utils/builder/execute_webhook.rs @@ -10,7 +10,47 @@ use std::default::Default; /// Refer to the documentation for [`execute_webhook`] on restrictions with /// execution payloads and its fields. /// +/// # Examples +/// +/// Creating two embeds, and then sending them as part of the delivery +/// payload of [`Webhook::execute`]: +/// +/// ```rust,no_run +/// use serenity::client::http; +/// use serenity::model::Embed; +/// use serenity::utils::Colour; +/// +/// let id = 245037420704169985; +/// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV"; +/// +/// let webhook = http::get_webhook_with_token(id, token) +/// .expect("valid webhook"); +/// +/// let website = Embed::fake(|e| e +/// .title("The Rust Language Website") +/// .description("Rust is a systems programming language.") +/// .colour(Colour::from_rgb(222, 165, 132))); +/// +/// let resources = Embed::fake(|e| e +/// .title("Rust Resources") +/// .description("A few resources to help with learning Rust") +/// .colour(0xDEA584) +/// .field(|f| f +/// .inline(false) +/// .name("The Rust Book") +/// .value("A comprehensive resource for all topics related to Rust")) +/// .field(|f| f +/// .inline(false) +/// .name("Rust by Example") +/// .value("A collection of Rust examples on topics, useable in-browser"))); +/// +/// let _ = webhook.execute(|w| w +/// .content("Here's some information on Rust:") +/// .embeds(vec![website, resources])); +/// ``` +/// /// [`Webhook`]: ../model/struct.Webhook.html +/// [`Webhook::execute`]: ../../model/struct.Webhook.html#method.execute /// [`execute_webhook`]: ../client/http/fn.execute_webhook.html pub struct ExecuteWebhook(pub ObjectBuilder); @@ -21,11 +61,28 @@ impl ExecuteWebhook { } /// Set the content of the message. + /// + /// Note that when setting at least one embed via [`embeds`], this may be + /// omitted. + /// + /// [`embeds`]: #method.embeds pub fn content(self, content: &str) -> Self { ExecuteWebhook(self.0.insert("content", content)) } - // Set the embeds associated with the message. + /// Set the embeds associated with the message. + /// + /// This should be used in combination with [`Embed::fake`], creating one + /// or more fake embeds to send to the API. + /// + /// # Examples + /// + /// Refer to the [struct-level documentation] for an example on how to use + /// embeds. + /// + /// [`Embed::fake`]: ../../model/struct.Embed.html#method.fake + /// [`Webhook::execute`]: ../../model/struct.Webhook.html#method.execute + /// [struct-level documentation]: #examples pub fn embeds(self, embeds: Vec<Value>) -> Self { ExecuteWebhook(self.0.insert("embeds", embeds)) } @@ -46,9 +103,12 @@ impl ExecuteWebhook { impl Default for ExecuteWebhook { /// Returns a default set of values for a [`Webhook`] execution. /// - /// The only default value is `tts` being set to `true`. In the event that + /// The only default value is [`tts`] being set to `true`. In the event that /// there is a bug that Discord defaults `tts` to `true`, at least /// serenity.rs won't be a part of it. + /// + /// [`Webhook`]: ../../model/struct.Webhook.html + /// [`tts`]: #method.tts fn default() -> ExecuteWebhook { ExecuteWebhook(ObjectBuilder::new().insert("tts", false)) } diff --git a/src/utils/builder/get_messages.rs b/src/utils/builder/get_messages.rs index 4fdadff..d5088e8 100644 --- a/src/utils/builder/get_messages.rs +++ b/src/utils/builder/get_messages.rs @@ -75,6 +75,7 @@ impl GetMessages { } impl Default for GetMessages { + /// Creates a builder with no default values. fn default() -> GetMessages { GetMessages(BTreeMap::default()) } diff --git a/src/utils/colour.rs b/src/utils/colour.rs index adaf1cc..de55df6 100644 --- a/src/utils/colour.rs +++ b/src/utils/colour.rs @@ -2,9 +2,10 @@ use std::default::Default; use ::internal::prelude::*; macro_rules! colour { - ($struct_:ident; $($name:ident, $val:expr;)*) => { + ($struct_:ident; $(#[$attr:meta] $name:ident, $val:expr;)*) => { impl $struct_ { $( + #[$attr] pub fn $name() -> Colour { Colour::new($val) } @@ -39,7 +40,7 @@ macro_rules! colour { /// /// Creating an instance with the [`dark_teal`] presets: /// -/// ```rust,ignore +/// ```rust /// use serenity::utils::Colour; /// /// let colour = Colour::dark_teal(); @@ -52,13 +53,28 @@ macro_rules! colour { /// [`get_g`]: #method.get_g #[derive(Clone, Copy, Debug)] pub struct Colour { - /// The raw inner integer value of this Colour. This is worked with to - /// generate values such as the red component value. + /// The raw inner 32-bit unsigned integer value of this Colour. This is + /// worked with to generate values such as the red component value. pub value: u32, } impl Colour { /// Generates a new Colour with the given integer value set. + /// + /// # Examples + /// + /// Create a new Colour, and then ensure that its inner value is equivilant + /// to a specific RGB value, retrieved via [`get_tuple`]: + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// let colour = Colour::new(6573123); + /// + /// assert_eq!(colour.get_tuple(), (100, 76, 67)); + /// ``` + /// + /// [`get_tuple`]: #method.get_tuple pub fn new(value: u32) -> Colour { Colour { value: value, @@ -109,33 +125,112 @@ impl Colour { } /// Returns the red RGB component of this Colour. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// assert_eq!(Colour::new(6573123).get_r(), 100); + /// ``` pub fn get_r(&self) -> u8 { ((self.value >> 16) & 255) as u8 } /// Returns the green RGB component of this Colour. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// assert_eq!(Colour::new(6573123).get_g(), 76); + /// ``` pub fn get_g(&self) -> u8 { ((self.value >> 8) & 255) as u8 } /// Returns the blue RGB component of this Colour. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// assert_eq!(Colour::new(6573123).get_b(), 67); pub fn get_b(&self) -> u8 { (self.value & 255) as u8 } /// Returns a tuple of the red, green, and blue components of this Colour. + /// + /// This is equivilant to creating a tuple with the return values of + /// [`get_r`], [`get_g`], and [`get_b`]. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// assert_eq!(Colour::new(6573123).get_tuple(), (100, 76, 67)); + /// ``` + /// + /// [`get_r`]: #method.get_r + /// [`get_g`]: #method.get_g + /// [`get_b`]: #method.get_b pub fn get_tuple(&self) -> (u8, u8, u8) { (self.get_r(), self.get_g(), self.get_b()) } } +impl From<i32> for Colour { + /// Constructs a Colour from a i32. + /// + /// This is used for functions that accept `Into<Colour>`. + /// + /// This is useful when providing hex values. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// assert_eq!(Colour::from(0xDEA584).get_tuple(), (222, 165, 132)); + /// ``` + fn from(value: i32) -> Colour { + Colour::new(value as u32) + } +} + impl From<u32> for Colour { + /// Constructs a Colour from a u32. + /// + /// This is used for functions that accept `Into<Colour>`. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// assert_eq!(Colour::from(6573123u32).get_r(), 100); + /// ``` fn from(value: u32) -> Colour { Colour::new(value) } } impl From<u64> for Colour { + /// Constructs a Colour from a u32. + /// + /// This is used for functions that accept `Into<Colour>`. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::Colour; + /// + /// assert_eq!(Colour::from(6573123u64).get_r(), 100); + /// ``` fn from(value: u64) -> Colour { Colour::new(value as u32) } @@ -143,28 +238,49 @@ impl From<u64> for Colour { colour! { Colour; - blue, 0x3498db; + /// Creates a new `Colour`, setting its RGB value to `(52, 152, 219)`. + blue, 0x3498DB; + /// Creates a new `Colour`, setting its RGB value to `(32, 102, 148)`. dark_blue, 0x206694; - dark_green, 0x1f8b4c; - dark_gold, 0xc27c0e; - dark_grey, 0x607d8b; - dark_magenta, 0xad1457; - dark_orange, 0xa84300; - dark_purple, 0x71368a; - dark_red, 0x992d22; - dark_teal, 0x11806a; - darker_grey, 0x546e7a; - gold, 0xf1c40f; - light_grey, 0x979c9f; - lighter_grey, 0x95a5a6; - magenta, 0xe91e63; - orange, 0xe67e22; - purple, 0x9b59b6; - red, 0xe74c3c; - teal, 0x1abc9c; + /// Creates a new `Colour`, setting its RGB value to `(31, 139, 76)`. + dark_green, 0x1F8B4C; + /// Creates a new `Colour`, setting its RGB value to `(194, 124, 14)`. + dark_gold, 0xC27C0E; + /// Creates a new `Colour`, setting its RGB value to `(96, 125, 139)`. + dark_grey, 0x607D8B; + /// Creates a new `Colour`, setting its RGB value to `(173, 20, 87)`. + dark_magenta, 0xAD1457; + /// Creates a new `Colour`, setting its RGB value to `(168, 67, 0)`. + dark_orange, 0xA84300; + /// Creates a new `Colour`, setting its RGB value to `(113, 54, 138)`. + dark_purple, 0x71368A; + /// Creates a new `Colour`, setting its RGB value to `(153, 45, 34)`. + dark_red, 0x992D22; + /// Creates a new `Colour`, setting its RGB value to `(17, 128, 106)`. + dark_teal, 0x11806A; + /// Creates a new `Colour`, setting its RGB value to `(84, 110, 122)`. + darker_grey, 0x546E7A; + /// Creates a new `Colour`, setting its RGB value to `(241, 196, 15)`. + gold, 0xF1C40F; + /// Creates a new `Colour`, setting its RGB value to `(151, 156, 159)`. + light_grey, 0x979C9F; + /// Creates a new `Colour`, setting its RGB value to `(149, 165, 166)`. + lighter_grey, 0x95A5A6; + /// Creates a new `Colour`, setting its RGB value to `(233, 30, 99)`. + magenta, 0xE91E63; + /// Creates a new `Colour`, setting its RGB value to `(230, 126, 34)`. + orange, 0xE67E22; + /// Creates a new `Colour`, setting its RGB value to `(155, 89, 182)`. + purple, 0x9B59B6; + /// Creates a new `Colour`, setting its RGB value to `(231, 76, 60)`. + red, 0xE74C3C; + /// Creates a new `Colour`, setting its RGB value to `(26, 188, 156)`. + teal, 0x1ABC9C; } impl Default for Colour { + /// Creates a default value for a `Colour`, setting the inner value to `0`. + /// This is equivilant to setting the RGB value to `(0, 0, 0)`. fn default() -> Colour { Colour { value: 0, diff --git a/src/utils/message_builder.rs b/src/utils/message_builder.rs index 7ba7717..a9a88ae 100644 --- a/src/utils/message_builder.rs +++ b/src/utils/message_builder.rs @@ -5,11 +5,12 @@ use ::model::{ChannelId, Emoji, Mentionable, RoleId, UserId}; /// The Message Builder is an ergonomic utility to easily build a message, /// by adding text and mentioning mentionable structs. /// -/// The finalized value can be accessed via `.build()` or the inner value. +/// The finalized value can be accessed via [`build`] or the inner value. /// /// # Examples /// -/// Build a message, mentioning a user and an emoji: +/// Build a message, mentioning a [`user`] and an [`emoji`], and retrieving the +/// value: /// /// ```rust,ignore /// use serenity::utils::MessageBuilder; @@ -23,6 +24,10 @@ use ::model::{ChannelId, Emoji, Mentionable, RoleId, UserId}; /// .mention(emoji) /// .build(); /// ``` +/// +/// [`build`]: #method.build +/// [`emoji`]: #method.emoji +/// [`user`]: #method.user pub struct MessageBuilder(pub String); impl MessageBuilder { @@ -31,20 +36,46 @@ impl MessageBuilder { MessageBuilder::default() } - /// Pulls the inner value out of the builder. This is equivilant to simply - /// retrieving the value. + /// Pulls the inner value out of the builder. + /// + /// # Examples + /// + /// This is equivilant to simply retrieving the tuple struct's first value: + /// + /// ```rust + /// use serenity::utils::MessageBuilder; + /// + /// let content = MessageBuilder::new().push("test").0; + /// + /// assert_eq!(content, "test"); + /// ``` pub fn build(self) -> String { self.0 } - /// Mentions the channel in the built message. + /// Mentions the [`PublicChannel`] in the built message. + /// + /// This accepts anything that converts _into_ a [`ChannelId`]. Refer to + /// `ChannelId`'s documentation for more information. + /// + /// Refer to `ChannelId`'s [Display implementation] for more information on + /// how this is formatted. + /// + /// [`ChannelId`]: ../model/struct.ChannelId.html + /// [`PublicChannel`]: ../model/struct.PublicChannel.html + /// [Display implementation]: ../model/struct.ChannelId.html#method.fmt-1 pub fn channel<C: Into<ChannelId>>(mut self, channel: C) -> Self { self.0.push_str(&format!("{}", channel.into())); self } - /// Uses and displays the given emoji in the built message. + /// Displays the given emoji in the built message. + /// + /// Refer to `Emoji`s [Display implementation] for more information on how + /// this is formatted. + /// + /// [Display implementation]: ../model/struct.Emoji.html#method.fmt pub fn emoji(mut self, emoji: Emoji) -> Self { self.0.push_str(&format!("{}", emoji)); @@ -65,6 +96,16 @@ impl MessageBuilder { /// Note that this does not mutate either the given data or the internal /// message content in anyway prior to appending the given content to the /// internal message. + /// + /// # Examples + /// + /// ```rust + /// use serenity::utils::MessageBuilder; + /// + /// let message = MessageBuilder::new().push("test"); + /// + /// assert_eq!(message.push("ing").0, "testing"); + /// ``` pub fn push(mut self, content: &str) -> Self { self.0.push_str(content); @@ -72,14 +113,34 @@ impl MessageBuilder { } - /// Mentions the role in the built message. + /// Mentions the [`Role`] in the built message. + /// + /// This accepts anything that converts _into_ a [`RoleId`]. Refer to + /// `RoleId`'s documentation for more information. + /// + /// Refer to `RoleId`'s [Display implementation] for more information on how + /// this is formatted. + /// + /// [`Role`]: ../model/struct.Role.html + /// [`RoleId`]: ../model/struct.RoleId.html + /// [Display implementation]: ../model/struct.RoleId.html#method.fmt-1 pub fn role<R: Into<RoleId>>(mut self, role: R) -> Self { self.0.push_str(&format!("{}", role.into())); self } - /// Mentions the user in the built message. + /// Mentions the [`User`] in the built message. + /// + /// This accepts anything that converts _into_ a [`UserId`]. Refer to + /// `UserId`'s documentation for more information. + /// + /// Refer to `UserId`'s [Display implementation] for more information on how + /// this is formatted. + /// + /// [`User`]: ../model/struct.User.html + /// [`UserId`]: ../model/struct.UserId.html + /// [Display implementation]: ../model/struct.UserId.html#method.fmt-1 pub fn user<U: Into<UserId>>(mut self, user: U) -> Self { self.0.push_str(&format!("{}", user.into())); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ccf7787..cb97a58 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -93,14 +93,37 @@ pub fn into_array(value: Value) -> Result<Vec<Value>> { /// Retrieves the "code" part of an [invite][`RichInvite`] out of a URL. /// /// # Examples -/// Retrieving the code from the URL `https://discord.gg/0cDvIgU2voY8RSYL`: /// -/// ```rust,ignore +/// Three formats of codes are supported: +/// +/// 1. Retrieving the code from the URL `"https://discord.gg/0cDvIgU2voY8RSYL"`: +/// +/// ```rust /// use serenity::utils; /// /// let url = "https://discord.gg/0cDvIgU2voY8RSYL"; /// -/// assert!(utils::parse_invite(url) == "0cDvIgU2voY8RSYL"); +/// assert_eq!(utils::parse_invite(url), "0cDvIgU2voY8RSYL"); +/// ``` +/// +/// 2. Retrieving the code from the URL `"http://discord.gg/0cDvIgU2voY8RSYL"`: +/// +/// ```rust +/// use serenity::utils; +/// +/// let url = "http://discord.gg/0cDvIgU2voY8RSYL"; +/// +/// assert_eq!(utils::parse_invite(url), "0cDvIgU2voY8RSYL"); +/// ``` +/// +/// 3. Retrieving the code from the URL `"discord.gg/0cDvIgU2voY8RSYL"`: +/// +/// ```rust +/// use serenity::utils; +/// +/// let url = "discord.gg/0cDvIgU2voY8RSYL"; +/// +/// assert_eq!(utils::parse_invite(url), "0cDvIgU2voY8RSYL"); /// ``` /// /// [`RichInvite`]: ../model/struct.RichInvite.html @@ -124,7 +147,7 @@ pub fn parse_invite(code: &str) -> &str { /// /// Reads an image located at `./cat.png` into a base64-encoded string: /// -/// ```rust,ignore +/// ```rust,no_run /// use serenity::utils; /// /// let image = match utils::read_image("./cat.png") { |