serenity 0.11.5

A Rust library for the Discord API.
Documentation
//! User information-related models.

use std::fmt;
#[cfg(feature = "model")]
use std::fmt::Write;

#[cfg(feature = "model")]
use futures::future::{BoxFuture, FutureExt};
use serde::{Deserialize, Serialize};

use super::prelude::*;
#[cfg(feature = "model")]
use crate::builder::{CreateBotAuthParameters, CreateMessage, EditProfile};
#[cfg(all(feature = "cache", feature = "model"))]
use crate::cache::Cache;
#[cfg(feature = "collector")]
use crate::client::bridge::gateway::ShardMessenger;
#[cfg(feature = "collector")]
use crate::collector::{
    CollectReaction,
    CollectReply,
    MessageCollectorBuilder,
    ReactionCollectorBuilder,
};
#[cfg(feature = "model")]
use crate::http::GuildPagination;
#[cfg(feature = "model")]
use crate::http::{CacheHttp, Http};
use crate::internal::prelude::*;
#[cfg(feature = "model")]
use crate::json;
#[cfg(feature = "model")]
use crate::json::json;
use crate::json::to_string;
#[cfg(feature = "model")]
use crate::model::application::oauth::Scope;
use crate::model::mention::Mentionable;

/// Used with `#[serde(with|deserialize_with|serialize_with)]`
///
/// # Examples
///
/// ```rust,ignore
/// #[derive(Deserialize, Serialize)]
/// struct A {
///     #[serde(with = "discriminator")]
///     id: u16,
/// }
///
/// #[derive(Deserialize)]
/// struct B {
///     #[serde(deserialize_with = "discriminator::deserialize")]
///     id: u16,
/// }
///
/// #[derive(Serialize)]
/// struct C {
///     #[serde(serialize_with = "discriminator::serialize")]
///     id: u16,
/// }
/// ```
pub(crate) mod discriminator {
    use std::convert::TryFrom;
    use std::fmt;

    use serde::de::{Error, Visitor};
    use serde::{Deserializer, Serializer};

    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u16, D::Error> {
        deserializer.deserialize_any(DiscriminatorVisitor)
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn serialize<S: Serializer>(value: &u16, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.collect_str(&format_args!("{:04}", value))
    }

    struct DiscriminatorVisitor;

    impl<'de> Visitor<'de> for DiscriminatorVisitor {
        type Value = u16;

        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
            formatter.write_str("string or integer discriminator")
        }

        fn visit_u64<E: Error>(self, value: u64) -> Result<Self::Value, E> {
            u16::try_from(value).map_err(Error::custom)
        }

        fn visit_str<E: Error>(self, s: &str) -> Result<Self::Value, E> {
            s.parse().map_err(Error::custom)
        }
    }

    /// Used with `#[serde(with|deserialize_with|serialize_with)]`
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// #[derive(Deserialize, Serialize)]
    /// struct A {
    ///     #[serde(with = "discriminator::option")]
    ///     id: Option<u16>,
    /// }
    ///
    /// #[derive(Deserialize)]
    /// struct B {
    ///     #[serde(deserialize_with = "discriminator::option::deserialize")]
    ///     id: Option<u16>,
    /// }
    ///
    /// #[derive(Serialize)]
    /// struct C {
    ///     #[serde(serialize_with = "discriminator::option::serialize")]
    ///     id: Option<u16>,
    /// }
    /// ```
    pub(crate) mod option {
        use std::fmt;

        use serde::de::{Error, Visitor};
        use serde::{Deserializer, Serializer};

        use super::DiscriminatorVisitor;

        pub fn deserialize<'de, D: Deserializer<'de>>(
            deserializer: D,
        ) -> Result<Option<u16>, D::Error> {
            deserializer.deserialize_option(OptionalDiscriminatorVisitor)
        }

        #[allow(clippy::trivially_copy_pass_by_ref)]
        pub fn serialize<S: Serializer>(
            value: &Option<u16>,
            serializer: S,
        ) -> Result<S::Ok, S::Error> {
            match value {
                Some(value) => serializer.serialize_some(&format_args!("{:04}", value)),
                None => serializer.serialize_none(),
            }
        }

        struct OptionalDiscriminatorVisitor;

        impl<'de> Visitor<'de> for OptionalDiscriminatorVisitor {
            type Value = Option<u16>;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("optional string or integer discriminator")
            }

            fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
                Ok(None)
            }

            fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
                Ok(None)
            }

            fn visit_some<D: Deserializer<'de>>(
                self,
                deserializer: D,
            ) -> Result<Self::Value, D::Error> {
                Ok(Some(deserializer.deserialize_any(DiscriminatorVisitor)?))
            }
        }
    }
}

/// Information about the current user.
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct CurrentUser {
    pub id: UserId,
    pub avatar: Option<String>,
    #[serde(default)]
    pub bot: bool,
    #[serde(with = "discriminator")]
    pub discriminator: u16,
    pub email: Option<String>,
    pub mfa_enabled: bool,
    #[serde(rename = "username")]
    pub name: String,
    pub verified: Option<bool>,
    pub public_flags: Option<UserPublicFlags>,
    pub banner: Option<String>,
    #[cfg(feature = "utils")]
    pub accent_colour: Option<Colour>,
    #[cfg(not(feature = "utils"))]
    pub accent_colour: Option<u32>,
}

#[cfg(feature = "model")]
impl CurrentUser {
    /// Returns the formatted URL of the user's icon, if one exists.
    ///
    /// This will produce a WEBP image URL, or GIF if the user has a GIF avatar.
    ///
    /// # Examples
    ///
    /// Print out the current user's avatar url if one is set:
    ///
    /// ```rust,no_run
    /// # #[cfg(feature = "cache")]
    /// # fn run() {
    /// # use serenity::cache::Cache;
    /// #
    /// # let cache = Cache::default();
    /// // assuming the cache has been unlocked
    /// let user = cache.current_user();
    ///
    /// match user.avatar_url() {
    ///     Some(url) => println!("{}'s avatar can be found at {}", user.name, url),
    ///     None => println!("{} does not have an avatar set.", user.name),
    /// }
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub fn avatar_url(&self) -> Option<String> {
        avatar_url(self.id, self.avatar.as_ref())
    }

    /// Returns the formatted URL to the user's default avatar URL.
    ///
    /// This will produce a PNG URL.
    #[inline]
    #[must_use]
    pub fn default_avatar_url(&self) -> String {
        default_avatar_url(self.discriminator)
    }

    /// Edits the current user's profile settings.
    ///
    /// This mutates the current user in-place.
    ///
    /// Refer to [`EditProfile`]'s documentation for its methods.
    ///
    /// # Examples
    ///
    /// Change the avatar:
    ///
    /// ```rust,no_run
    /// # use serenity::http::Http;
    /// # use serenity::model::user::CurrentUser;
    /// #
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// #     let http = Http::new("token");
    /// #     let mut user = CurrentUser::default();
    /// let avatar = serenity::utils::read_image("./avatar.png")?;
    ///
    /// user.edit(&http, |p| p.avatar(Some(&avatar))).await;
    /// #     Ok(())
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an [`Error::Http`] if an invalid value is set.
    /// May also return an [`Error::Json`] if there is an error in
    /// deserializing the API response.
    pub async fn edit<F>(&mut self, http: impl AsRef<Http>, f: F) -> Result<()>
    where
        F: FnOnce(&mut EditProfile) -> &mut EditProfile,
    {
        let mut map = HashMap::new();
        map.insert("username", Value::from(self.name.clone()));

        if let Some(email) = self.email.as_ref() {
            map.insert("email", Value::from(email.clone()));
        }

        let mut edit_profile = EditProfile(map);
        f(&mut edit_profile);
        let map = json::hashmap_to_json_map(edit_profile.0);

        *self = http.as_ref().edit_profile(&map).await?;

        Ok(())
    }

    /// Retrieves the URL to the current user's avatar, falling back to the
    /// default avatar if needed.
    ///
    /// This will call [`Self::avatar_url`] first, and if that returns [`None`], it
    /// then falls back to [`Self::default_avatar_url`].
    #[inline]
    #[must_use]
    pub fn face(&self) -> String {
        self.avatar_url().unwrap_or_else(|| self.default_avatar_url())
    }

    /// Gets a list of guilds that the current user is in.
    ///
    /// # Examples
    ///
    /// Print out the names of all guilds the current user is in:
    ///
    /// ```rust,no_run
    /// # use serenity::http::Http;
    /// # use serenity::model::user::CurrentUser;
    /// #
    /// # async fn run() {
    /// #     let user = CurrentUser::default();
    /// #     let http = Http::new("token");
    /// // assuming the user has been bound
    ///
    /// if let Ok(guilds) = user.guilds(&http).await {
    ///     for (index, guild) in guilds.into_iter().enumerate() {
    ///         println!("{}: {}", index, guild.name);
    ///     }
    /// }
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// May return an [`Error::Http`] if the Discord API returns an error.
    /// Also can return [`Error::Json`] if there is an error in deserializing
    /// the data returned by the API.
    pub async fn guilds(&self, http: impl AsRef<Http>) -> Result<Vec<GuildInfo>> {
        let mut guilds = Vec::new();
        loop {
            let mut pagination = http
                .as_ref()
                .get_guilds(
                    Some(&GuildPagination::After(
                        guilds.last().map_or(GuildId(1), |g: &GuildInfo| g.id),
                    )),
                    Some(100),
                )
                .await?;
            let len = pagination.len();
            guilds.append(&mut pagination);
            if len != 100 {
                break;
            }
        }
        Ok(guilds)
    }

    /// Returns the invite url for the bot with the given permissions.
    ///
    /// This queries the REST API for the client id.
    ///
    /// If the permissions passed are empty, the permissions part will be dropped.
    ///
    /// Only the `bot` scope is used, if you wish to use more, such as slash commands, see
    /// [`Self::invite_url_with_oauth2_scopes`]
    ///
    /// # Examples
    ///
    /// Get the invite url with no permissions set:
    ///
    /// ```rust,no_run
    /// # use serenity::http::Http;
    /// # use serenity::model::user::CurrentUser;
    /// #
    /// # async fn run() {
    /// #     let user = CurrentUser::default();
    /// #     let http = Http::new("token");
    /// use serenity::model::Permissions;
    ///
    /// // assuming the user has been bound
    /// let url = match user.invite_url(&http, Permissions::empty()).await {
    ///     Ok(v) => v,
    ///     Err(why) => {
    ///         println!("Error getting invite url: {:?}", why);
    ///
    ///         return;
    ///     },
    /// };
    ///
    /// assert_eq!(
    ///     url,
    ///     "https://discordapp.com/api/oauth2/authorize? \
    ///                  client_id=249608697955745802&scope=bot"
    /// );
    /// # }
    /// ```
    ///
    /// Get the invite url with some basic permissions set:
    ///
    /// ```rust,no_run
    /// # use serenity::http::Http;
    /// # use serenity::model::user::CurrentUser;
    /// #
    /// # async fn run() {
    /// #     let user = CurrentUser::default();
    /// #     let http = Http::new("token");
    /// use serenity::model::Permissions;
    ///
    /// // assuming the user has been bound
    /// let permissions =
    ///     Permissions::VIEW_CHANNEL | Permissions::SEND_MESSAGES | Permissions::EMBED_LINKS;
    /// let url = match user.invite_url(&http, permissions).await {
    ///     Ok(v) => v,
    ///     Err(why) => {
    ///         println!("Error getting invite url: {:?}", why);
    ///
    ///         return;
    ///     },
    /// };
    ///
    /// assert_eq!(
    ///     url,
    ///     "https://discordapp.
    /// com/api/oauth2/authorize?client_id=249608697955745802&scope=bot&permissions=19456"
    /// );
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an
    /// [`HttpError::UnsuccessfulRequest(Unauthorized)`][`HttpError::UnsuccessfulRequest`]
    /// If the user is not authorized for this end point.
    ///
    /// Should never return [`Error::Url`] as all the data is controlled over.
    ///
    /// [`HttpError::UnsuccessfulRequest`]: crate::http::HttpError::UnsuccessfulRequest
    pub async fn invite_url(
        &self,
        http: impl AsRef<Http>,
        permissions: Permissions,
    ) -> Result<String> {
        self.invite_url_with_oauth2_scopes(http, permissions, &[Scope::Bot]).await
    }

    /// Generate an invite url, but with custom scopes.
    ///
    /// # Examples
    ///
    /// Get the invite url with no permissions set and slash commands support:
    ///
    /// ```rust,no_run
    /// # use serenity::http::Http;
    /// # use serenity::model::user::CurrentUser;
    /// #
    /// # async fn run() {
    /// #     let user = CurrentUser::default();
    /// #     let http = Http::new("token");
    /// use serenity::model::application::oauth::Scope;
    /// use serenity::model::Permissions;
    ///
    /// let scopes = vec![Scope::Bot, Scope::ApplicationsCommands];
    ///
    /// // assuming the user has been bound
    /// let url = match user.invite_url_with_oauth2_scopes(&http, Permissions::empty(), &scopes).await {
    ///     Ok(v) => v,
    ///     Err(why) => {
    ///         println!("Error getting invite url: {:?}", why);
    ///
    ///         return;
    ///     },
    /// };
    ///
    /// assert_eq!(
    ///     url,
    ///     "https://discordapp.com/api/oauth2/authorize? \
    ///                  client_id=249608697955745802&scope=bot%20applications.commands"
    /// );
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an
    /// [`HttpError::UnsuccessfulRequest(Unauthorized)`][`HttpError::UnsuccessfulRequest`]
    /// If the user is not authorized for this end point.
    ///
    /// Should never return [`Error::Url`] as all the data is controlled over.
    ///
    /// [`HttpError::UnsuccessfulRequest`]: crate::http::HttpError::UnsuccessfulRequest
    pub async fn invite_url_with_oauth2_scopes(
        &self,
        http: impl AsRef<Http>,
        permissions: Permissions,
        scopes: &[Scope],
    ) -> Result<String> {
        let mut builder = CreateBotAuthParameters::default();

        builder.permissions(permissions);
        builder.auto_client_id(http).await?;
        builder.scopes(scopes);

        Ok(builder.build())
    }

    /// Returns a static formatted URL of the user's icon, if one exists.
    ///
    /// This will always produce a WEBP image URL.
    ///
    /// # Examples
    ///
    /// Print out the current user's static avatar url if one is set:
    ///
    /// ```rust,no_run
    /// # use serenity::model::user::CurrentUser;
    /// #
    /// # fn run() {
    /// #     let user = CurrentUser::default();
    /// // assuming the user has been bound
    ///
    /// match user.static_avatar_url() {
    ///     Some(url) => println!("{}'s static avatar can be found at {}", user.name, url),
    ///     None => println!("Could not get static avatar for {}.", user.name),
    /// }
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub fn static_avatar_url(&self) -> Option<String> {
        static_avatar_url(self.id, self.avatar.as_ref())
    }

    /// Returns the tag of the current user.
    ///
    /// # Examples
    ///
    /// Print out the current user's distinct identifier (e.g., Username#1234):
    ///
    /// ```rust,no_run
    /// # use serenity::model::user::CurrentUser;
    /// #
    /// # fn run() {
    /// #     let user = CurrentUser::default();
    /// // assuming the user has been bound
    ///
    /// println!("The current user's distinct identifier is {}", user.tag());
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub fn tag(&self) -> String {
        tag(&self.name, self.discriminator)
    }
}

/// An enum that represents a default avatar.
///
/// The default avatar is calculated via the result of `discriminator % 5`.
///
/// The has of the avatar can be retrieved via calling [`Self::name`] on the enum.
#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
#[non_exhaustive]
pub enum DefaultAvatar {
    /// The avatar when the result is `0`.
    #[serde(rename = "6debd47ed13483642cf09e832ed0bc1b")]
    Blurple,
    /// The avatar when the result is `1`.
    #[serde(rename = "322c936a8c8be1b803cd94861bdfa868")]
    Grey,
    /// The avatar when the result is `2`.
    #[serde(rename = "dd4dbc0016779df1378e7812eabaa04d")]
    Green,
    /// The avatar when the result is `3`.
    #[serde(rename = "0e291f67c9274a1abdddeb3fd919cbaa")]
    Orange,
    /// The avatar when the result is `4`.
    #[serde(rename = "1cbd08c76f8af6dddce02c5138971129")]
    Red,
}

impl DefaultAvatar {
    /// Retrieves the String hash of the default avatar.
    ///
    /// # Errors
    ///
    /// May return a [`Error::Json`] if there is a serialization error.
    pub fn name(self) -> Result<String> {
        to_string(&self).map_err(From::from)
    }
}

/// The representation of a user's status.
///
/// # Examples
///
/// - [`DoNotDisturb`];
/// - [`Invisible`].
///
/// [`DoNotDisturb`]: OnlineStatus::DoNotDisturb
/// [`Invisible`]: OnlineStatus::Invisible
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)]
#[non_exhaustive]
pub enum OnlineStatus {
    #[serde(rename = "dnd")]
    DoNotDisturb,
    #[serde(rename = "idle")]
    Idle,
    #[serde(rename = "invisible")]
    Invisible,
    #[serde(rename = "offline")]
    Offline,
    #[serde(rename = "online")]
    Online,
}

impl OnlineStatus {
    #[must_use]
    pub fn name(&self) -> &str {
        match *self {
            OnlineStatus::DoNotDisturb => "dnd",
            OnlineStatus::Idle => "idle",
            OnlineStatus::Invisible => "invisible",
            OnlineStatus::Offline => "offline",
            OnlineStatus::Online => "online",
        }
    }
}

impl Default for OnlineStatus {
    fn default() -> OnlineStatus {
        OnlineStatus::Online
    }
}

/// Information about a user.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct User {
    /// The unique Id of the user. Can be used to calculate the account's
    /// creation date.
    pub id: UserId,
    /// Optional avatar hash.
    pub avatar: Option<String>,
    /// Indicator of whether the user is a bot.
    #[serde(default)]
    pub bot: bool,
    /// The account's discriminator to differentiate the user from others with
    /// the same [`Self::name`]. The name+discriminator pair is always unique.
    #[serde(with = "discriminator")]
    pub discriminator: u16,
    /// The account's username. Changing username will trigger a discriminator
    /// change if the username+discriminator pair becomes non-unique.
    #[serde(rename = "username")]
    pub name: String,
    /// The public flags on a user's account
    pub public_flags: Option<UserPublicFlags>,
    /// Optional banner hash.
    ///
    /// **Note**: This will only be present if the user is fetched via Rest API,
    /// e.g. with [`Http::get_user`].
    pub banner: Option<String>,
    /// The user's banner colour encoded as an integer representation of
    /// hexadecimal colour code
    ///
    /// **Note**: This will only be present if the user is fetched via Rest API,
    /// e.g. with [`Http::get_user`].
    #[cfg(feature = "utils")]
    #[serde(rename = "accent_color")]
    pub accent_colour: Option<Colour>,
    #[cfg(not(feature = "utils"))]
    #[serde(rename = "accent_color")]
    pub accent_colour: Option<u32>,
}

bitflags! {
    /// User's public flags
    #[derive(Default)]
    pub struct UserPublicFlags: u32 {
        /// User's flag as discord employee
        const DISCORD_EMPLOYEE = 1 << 0;
        /// User's flag as partnered server owner
        const PARTNERED_SERVER_OWNER = 1 << 1;
        /// User's flag as hypesquad events
        const HYPESQUAD_EVENTS = 1 << 2;
        /// User's flag as bug hunter level 1
        const BUG_HUNTER_LEVEL_1 = 1 << 3;
        /// User's flag as house bravery
        const HOUSE_BRAVERY = 1 << 6;
        /// User's flag as house brilliance
        const HOUSE_BRILLIANCE = 1 << 7;
        /// User's flag as house balance
        const HOUSE_BALANCE = 1 << 8;
        /// User's flag as early supporter
        const EARLY_SUPPORTER = 1 << 9;
        /// User's flag as team user
        const TEAM_USER = 1 << 10;
        /// User's flag as system
        const SYSTEM = 1 << 12;
        /// User's flag as bug hunter level 2
        const BUG_HUNTER_LEVEL_2 = 1 << 14;
        /// User's flag as verified bot
        const VERIFIED_BOT = 1 << 16;
        /// User's flag as early verified bot developer
        const EARLY_VERIFIED_BOT_DEVELOPER = 1 << 17;
        /// User's flag as discord certified moderator
        const DISCORD_CERTIFIED_MODERATOR = 1 << 18;
        /// Bot's running with HTTP interactions
        const BOT_HTTP_INTERACTIONS = 1 << 19;
    }
}

impl Default for User {
    /// Initializes a [`User`] with default values. Setting the following:
    /// - **id** to `UserId(210)`
    /// - **avatar** to `Some("abc")`
    /// - **bot** to `true`.
    /// - **discriminator** to `1432`.
    /// - **name** to `"test"`.
    /// - **public_flags** to [`None`].
    fn default() -> Self {
        User {
            id: UserId(210),
            avatar: Some("abc".to_string()),
            bot: true,
            discriminator: 1432,
            name: "test".to_string(),
            public_flags: None,
            banner: None,
            accent_colour: None,
        }
    }
}

use std::hash::{Hash, Hasher};

impl PartialEq for User {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for User {}

impl Hash for User {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        self.id.hash(hasher);
    }
}

#[cfg(feature = "model")]
impl User {
    /// Returns the formatted URL of the user's icon, if one exists.
    ///
    /// This will produce a WEBP image URL, or GIF if the user has a GIF avatar.
    #[inline]
    #[must_use]
    pub fn avatar_url(&self) -> Option<String> {
        avatar_url(self.id, self.avatar.as_ref())
    }

    /// Returns the formatted URL of the user's banner, if one exists.
    ///
    /// This will produce a WEBP image URL, or GIF if the user has a GIF banner.
    ///
    /// **Note**: This will only be present if the user is fetched via Rest API,
    /// e.g. with [`Http::get_user`].
    #[inline]
    #[must_use]
    pub fn banner_url(&self) -> Option<String> {
        banner_url(self.id, self.banner.as_ref())
    }

    /// Creates a direct message channel between the [current user] and the
    /// user. This can also retrieve the channel if one already exists.
    ///
    /// [current user]: CurrentUser
    ///
    /// # Errors
    ///
    /// See [`UserId::create_dm_channel`] for what errors may be returned.
    #[inline]
    pub async fn create_dm_channel(&self, cache_http: impl CacheHttp) -> Result<PrivateChannel> {
        if self.bot {
            return Err(Error::Model(ModelError::MessagingBot));
        }

        self.id.create_dm_channel(cache_http).await
    }

    /// Retrieves the time that this user was created at.
    #[inline]
    #[must_use]
    pub fn created_at(&self) -> Timestamp {
        self.id.created_at()
    }

    /// Returns the formatted URL to the user's default avatar URL.
    ///
    /// This will produce a PNG URL.
    #[inline]
    #[must_use]
    pub fn default_avatar_url(&self) -> String {
        default_avatar_url(self.discriminator)
    }

    /// Sends a message to a user through a direct message channel. This is a
    /// channel that can only be accessed by you and the recipient.
    ///
    /// # Examples
    ///
    /// When a user sends a message with a content of `"~help"`, DM the author a
    /// help message, and then react with `'👌'` to verify message sending:
    ///
    /// ```rust,no_run
    /// # #[cfg(feature="client")] {
    /// # use serenity::prelude::*;
    /// # use serenity::model::prelude::*;
    /// #
    /// use serenity::model::Permissions;
    ///
    /// struct Handler;
    ///
    /// #[serenity::async_trait]
    /// impl EventHandler for Handler {
    /// #   #[cfg(feature = "cache")]
    ///     async fn message(&self, ctx: Context, msg: Message) {
    ///         if msg.content == "~help" {
    ///             let url =
    ///                 match ctx.cache.current_user().invite_url(&ctx, Permissions::empty()).await {
    ///                     Ok(v) => v,
    ///                     Err(why) => {
    ///                         println!("Error creating invite url: {:?}", why);
    ///
    ///                         return;
    ///                     },
    ///                 };
    ///
    ///             let help = format!("Helpful info here. Invite me with this link: <{}>", url,);
    ///
    ///             let dm = msg.author.direct_message(&ctx, |m| m.content(&help)).await;
    ///
    ///             match dm {
    ///                 Ok(_) => {
    ///                     let _ = msg.react(&ctx, '👌').await;
    ///                 },
    ///                 Err(why) => {
    ///                     println!("Err sending help: {:?}", why);
    ///
    ///                     let _ = msg.reply(&ctx, "There was an error DMing you help.").await;
    ///                 },
    ///             };
    ///         }
    ///     }
    /// }
    ///
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// let mut client =
    ///     Client::builder("token", GatewayIntents::default()).event_handler(Handler).await?;
    /// #     Ok(())
    /// # }
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::MessagingBot`] if the user being direct messaged
    /// is a bot user.
    ///
    /// May also return an [`Error::Http`] if the message was illformed, or if the
    /// user cannot be sent a direct message.
    ///
    /// [`Error::Json`] can also be returned if there is an error deserializing
    /// the API response.
    pub async fn direct_message<'a, F>(&self, cache_http: impl CacheHttp, f: F) -> Result<Message>
    where
        for<'b> F: FnOnce(&'b mut CreateMessage<'a>) -> &'b mut CreateMessage<'a>,
    {
        self.create_dm_channel(&cache_http).await?.send_message(&cache_http.http(), f).await
    }

    /// This is an alias of [`Self::direct_message`].
    #[allow(clippy::missing_errors_doc)]
    #[inline]
    pub async fn dm<'a, F>(&self, cache_http: impl CacheHttp, f: F) -> Result<Message>
    where
        for<'b> F: FnOnce(&'b mut CreateMessage<'a>) -> &'b mut CreateMessage<'a>,
    {
        self.direct_message(cache_http, f).await
    }

    /// Retrieves the URL to the user's avatar, falling back to the default
    /// avatar if needed.
    ///
    /// This will call [`Self::avatar_url`] first, and if that returns [`None`], it
    /// then falls back to [`Self::default_avatar_url`].
    #[must_use]
    pub fn face(&self) -> String {
        self.avatar_url().unwrap_or_else(|| self.default_avatar_url())
    }

    /// Check if a user has a [`Role`]. This will retrieve the [`Guild`] from
    /// the [`Cache`] if it is available, and then check if that guild has the
    /// given [`Role`].
    ///
    /// Three forms of data may be passed in to the guild parameter: either a
    /// [`PartialGuild`], a [`GuildId`], or a `u64`.
    ///
    /// # Examples
    ///
    /// Check if a guild has a [`Role`] by Id:
    ///
    /// ```rust,ignore
    /// // Assumes a 'guild_id' and `role_id` have already been bound
    /// let _ = message.author.has_role(guild_id, role_id);
    /// ```
    ///
    /// [`Cache`]: crate::cache::Cache
    ///
    /// # Errors
    ///
    /// Returns an [`Error::Http`] if the given [`Guild`] is unavailable,
    /// if that [`Role`] does not exist in the given [`Guild`], or if the
    /// given [`User`] is not in that [`Guild`].
    ///
    /// May also return an [`Error::Json`] if there is an error in
    /// deserializing the API response.
    #[inline]
    pub async fn has_role(
        &self,
        cache_http: impl CacheHttp,
        guild: impl Into<GuildContainer>,
        role: impl Into<RoleId>,
    ) -> Result<bool> {
        self._has_role(&cache_http, guild.into(), role.into()).await
    }

    fn _has_role<'a>(
        &'a self,
        cache_http: &'a impl CacheHttp,
        guild: GuildContainer,
        role: RoleId,
    ) -> BoxFuture<'a, Result<bool>> {
        async move {
            match guild {
                GuildContainer::Guild(partial_guild) => {
                    self._has_role(cache_http, GuildContainer::Id(partial_guild.id), role).await
                },
                GuildContainer::Id(guild_id) => {
                    // Silences a warning when compiling without the `cache` feature.
                    #[allow(unused_mut)]
                    let mut has_role = None;

                    #[cfg(feature = "cache")]
                    {
                        if let Some(cache) = cache_http.cache() {
                            if let Some(member) = cache.member(guild_id, self.id) {
                                has_role = Some(member.roles.contains(&role));
                            }
                        }
                    }

                    if let Some(has_role) = has_role {
                        Ok(has_role)
                    } else {
                        cache_http
                            .http()
                            .get_member(guild_id.0, self.id.0)
                            .await
                            .map(|m| m.roles.contains(&role))
                    }
                },
            }
        }
        .boxed()
    }

    /// Refreshes the information about the user.
    ///
    /// Replaces the instance with the data retrieved over the REST API.
    ///
    /// # Errors
    ///
    /// See [`UserId::to_user`] for what errors may be returned.
    #[inline]
    pub async fn refresh(&mut self, cache_http: impl CacheHttp) -> Result<()> {
        *self = self.id.to_user(cache_http).await?;

        Ok(())
    }

    /// Returns a static formatted URL of the user's icon, if one exists.
    ///
    /// This will always produce a WEBP image URL.
    #[inline]
    #[must_use]
    pub fn static_avatar_url(&self) -> Option<String> {
        static_avatar_url(self.id, self.avatar.as_ref())
    }

    /// Returns the "tag" for the user.
    ///
    /// The "tag" is defined as "username#discriminator", such as "zeyla#5479".
    ///
    /// # Examples
    ///
    /// Make a command to tell the user what their tag is:
    ///
    /// ```rust,no_run
    /// # #[cfg(feature = "client")]
    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
    /// # use serenity::prelude::*;
    /// # use serenity::model::prelude::*;
    /// #
    /// use serenity::utils::ContentModifier::Bold;
    /// use serenity::utils::MessageBuilder;
    ///
    /// struct Handler;
    ///
    /// #[serenity::async_trait]
    /// impl EventHandler for Handler {
    ///     async fn message(&self, context: Context, msg: Message) {
    ///         if msg.content == "!mytag" {
    ///             let content = MessageBuilder::new()
    ///                 .push("Your tag is ")
    ///                 .push(Bold + msg.author.tag())
    ///                 .build();
    ///
    ///             let _ = msg.channel_id.say(&context.http, &content).await;
    ///         }
    ///     }
    /// }
    /// let mut client =
    ///     Client::builder("token", GatewayIntents::default()).event_handler(Handler).await?;
    ///
    /// client.start().await?;
    /// #     Ok(())
    /// # }
    /// ```
    #[inline]
    #[must_use]
    pub fn tag(&self) -> String {
        tag(&self.name, self.discriminator)
    }

    /// Returns the user's nickname in the given `guild_id`.
    ///
    /// If none is used, it returns [`None`].
    #[inline]
    pub async fn nick_in(
        &self,
        cache_http: impl CacheHttp,
        guild_id: impl Into<GuildId>,
    ) -> Option<String> {
        let guild_id = guild_id.into();

        #[cfg(feature = "cache")]
        {
            if let Some(cache) = cache_http.cache() {
                if let Some(guild) = guild_id.to_guild_cached(cache) {
                    if let Some(member) = guild.members.get(&self.id) {
                        return member.nick.clone();
                    }
                }
            }
        }

        guild_id.member(cache_http, &self.id).await.ok().and_then(|member| member.nick)
    }

    /// Returns a future that will await one message by this user.
    #[cfg(feature = "collector")]
    pub fn await_reply(&self, shard_messenger: impl AsRef<ShardMessenger>) -> CollectReply {
        CollectReply::new(shard_messenger).author_id(self.id.0)
    }

    /// Returns a stream builder which can be awaited to obtain a stream of messages sent by this user.
    #[cfg(feature = "collector")]
    pub fn await_replies(
        &self,
        shard_messenger: impl AsRef<ShardMessenger>,
    ) -> MessageCollectorBuilder {
        MessageCollectorBuilder::new(shard_messenger).author_id(self.id.0)
    }

    /// Await a single reaction by this user.
    #[cfg(feature = "collector")]
    pub fn await_reaction(&self, shard_messenger: impl AsRef<ShardMessenger>) -> CollectReaction {
        CollectReaction::new(shard_messenger).author_id(self.id.0)
    }

    /// Returns a stream builder which can be awaited to obtain a stream of reactions sent by this user.
    #[cfg(feature = "collector")]
    pub fn await_reactions(
        &self,
        shard_messenger: impl AsRef<ShardMessenger>,
    ) -> ReactionCollectorBuilder {
        ReactionCollectorBuilder::new(shard_messenger).author_id(self.id.0)
    }
}

impl fmt::Display for User {
    /// Formats a string which will mention the user.
    // This is in the format of: `<@USER_ID>`
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.id.mention(), f)
    }
}

#[cfg(feature = "model")]
impl UserId {
    /// Creates a direct message channel between the [current user] and the
    /// user. This can also retrieve the channel if one already exists.
    ///
    /// [current user]: CurrentUser
    ///
    /// # Errors
    ///
    /// Returns [`Error::Http`] if a [`User`] with that [`UserId`] does not exist,
    /// or is otherwise unavailable.
    ///
    /// May also return an [`Error::Json`] if there is an error in deserializing
    /// the channel data returned by the Discord API.
    pub async fn create_dm_channel(self, cache_http: impl CacheHttp) -> Result<PrivateChannel> {
        #[cfg(feature = "cache")]
        {
            if let Some(cache) = cache_http.cache() {
                for channel_entry in cache.private_channels().iter() {
                    let channel = channel_entry.value();

                    if channel.recipient.id == self {
                        return Ok(channel.clone());
                    }
                }
            }
        }

        let map = json!({
            "recipient_id": self.0,
        });

        cache_http.http().create_private_channel(&map).await
    }

    /// Attempts to find a [`User`] by its Id in the cache.
    #[cfg(feature = "cache")]
    #[inline]
    pub async fn to_user_cached(self, cache: impl AsRef<Cache>) -> Option<User> {
        cache.as_ref().user(self)
    }

    /// First attempts to find a [`User`] by its Id in the cache,
    /// upon failure requests it via the REST API.
    ///
    /// **Note**: If the cache is not enabled, REST API will be used only.
    ///
    /// **Note**: If the cache is enabled, you might want to enable the `temp_cache` feature to
    /// cache user data retrieved by this function for a short duration.
    ///
    /// # Errors
    ///
    /// May return an [`Error::Http`] if a [`User`] with that [`UserId`] does not exist,
    /// or otherwise cannot be fetched.
    ///
    /// May also return an [`Error::Json`] if there is an error in
    /// deserializing the user.
    #[inline]
    pub async fn to_user(self, cache_http: impl CacheHttp) -> Result<User> {
        #[cfg(feature = "cache")]
        {
            if let Some(cache) = cache_http.cache() {
                if let Some(user) = cache.user(self) {
                    return Ok(user);
                }
            }
        }

        let user = cache_http.http().get_user(self.0).await?;

        #[cfg(all(feature = "cache", feature = "temp_cache"))]
        {
            if let Some(cache) = cache_http.cache() {
                cache.temp_users.insert(user.id, user.clone());
            }
        }

        Ok(user)
    }
}

impl From<CurrentUser> for User {
    fn from(user: CurrentUser) -> Self {
        Self {
            avatar: user.avatar,
            bot: user.bot,
            discriminator: user.discriminator,
            id: user.id,
            name: user.name,
            public_flags: user.public_flags,
            banner: user.banner,
            accent_colour: user.accent_colour,
        }
    }
}

impl<'a> From<&'a CurrentUser> for User {
    fn from(user: &'a CurrentUser) -> Self {
        Self {
            avatar: user.avatar.clone(),
            bot: user.bot,
            discriminator: user.discriminator,
            id: user.id,
            name: user.name.clone(),
            public_flags: user.public_flags,
            banner: user.banner.clone(),
            accent_colour: user.accent_colour,
        }
    }
}

impl From<CurrentUser> for UserId {
    /// Gets the Id of a [`CurrentUser`] struct.
    fn from(current_user: CurrentUser) -> UserId {
        current_user.id
    }
}

impl<'a> From<&'a CurrentUser> for UserId {
    /// Gets the Id of a [`CurrentUser`] struct.
    fn from(current_user: &CurrentUser) -> UserId {
        current_user.id
    }
}

impl From<Member> for UserId {
    /// Gets the Id of a [`Member`].
    fn from(member: Member) -> UserId {
        member.user.id
    }
}

impl<'a> From<&'a Member> for UserId {
    /// Gets the Id of a [`Member`].
    fn from(member: &Member) -> UserId {
        member.user.id
    }
}

impl From<User> for UserId {
    /// Gets the Id of a [`User`].
    fn from(user: User) -> UserId {
        user.id
    }
}

impl<'a> From<&'a User> for UserId {
    /// Gets the Id of a [`User`].
    fn from(user: &User) -> UserId {
        user.id
    }
}

#[cfg(feature = "model")]
fn avatar_url(user_id: UserId, hash: Option<&String>) -> Option<String> {
    hash.map(|hash| {
        let ext = if hash.starts_with("a_") { "gif" } else { "webp" };

        cdn!("/avatars/{}/{}.{}?size=1024", user_id.0, hash, ext)
    })
}

#[cfg(feature = "model")]
fn default_avatar_url(discriminator: u16) -> String {
    cdn!("/embed/avatars/{}.png", discriminator % 5u16)
}

#[cfg(feature = "model")]
fn static_avatar_url(user_id: UserId, hash: Option<&String>) -> Option<String> {
    hash.map(|hash| cdn!("/avatars/{}/{}.webp?size=1024", user_id, hash))
}

#[cfg(feature = "model")]
fn banner_url(user_id: UserId, hash: Option<&String>) -> Option<String> {
    hash.map(|hash| {
        let ext = if hash.starts_with("a_") { "gif" } else { "webp" };

        cdn!("/banners/{}/{}.{}?size=1024", user_id.0, hash, ext)
    })
}

#[cfg(feature = "model")]
fn tag(name: &str, discriminator: u16) -> String {
    // 32: max length of username
    // 1: `#`
    // 4: max length of discriminator
    let mut tag = String::with_capacity(37);
    tag.push_str(name);
    tag.push('#');
    write!(tag, "{:04}", discriminator).unwrap();

    tag
}

#[cfg(test)]
mod test {
    #[test]
    fn test_discriminator_serde() {
        use serde::{Deserialize, Serialize};
        use serde_test::{assert_de_tokens, assert_tokens, Token};

        use super::discriminator;

        #[derive(Debug, PartialEq, Deserialize, Serialize)]
        struct User {
            #[serde(with = "discriminator")]
            discriminator: u16,
        }
        #[derive(Debug, PartialEq, Deserialize, Serialize)]
        struct UserOpt {
            #[serde(
                default,
                skip_serializing_if = "Option::is_none",
                with = "discriminator::option"
            )]
            discriminator: Option<u16>,
        }

        let user = User {
            discriminator: 123,
        };
        assert_tokens(&user, &[
            Token::Struct {
                name: "User",
                len: 1,
            },
            Token::Str("discriminator"),
            Token::Str("0123"),
            Token::StructEnd,
        ]);
        assert_de_tokens(&user, &[
            Token::Struct {
                name: "User",
                len: 1,
            },
            Token::Str("discriminator"),
            Token::U16(123),
            Token::StructEnd,
        ]);

        let user = UserOpt {
            discriminator: Some(123),
        };
        assert_tokens(&user, &[
            Token::Struct {
                name: "UserOpt",
                len: 1,
            },
            Token::Str("discriminator"),
            Token::Some,
            Token::Str("0123"),
            Token::StructEnd,
        ]);
        assert_de_tokens(&user, &[
            Token::Struct {
                name: "UserOpt",
                len: 1,
            },
            Token::Str("discriminator"),
            Token::Some,
            Token::U16(123),
            Token::StructEnd,
        ]);

        let user_no_discriminator = UserOpt {
            discriminator: None,
        };
        assert_tokens(&user_no_discriminator, &[
            Token::Struct {
                name: "UserOpt",
                len: 0,
            },
            Token::StructEnd,
        ]);
    }

    #[cfg(feature = "model")]
    mod model {
        use crate::model::user::User;

        #[test]
        fn test_core() {
            let mut user = User::default();

            assert!(user.avatar_url().unwrap().ends_with("/avatars/210/abc.webp?size=1024"));
            assert!(user.static_avatar_url().unwrap().ends_with("/avatars/210/abc.webp?size=1024"));

            user.avatar = Some("a_aaa".to_string());
            assert!(user.avatar_url().unwrap().ends_with("/avatars/210/a_aaa.gif?size=1024"));
            assert!(user
                .static_avatar_url()
                .unwrap()
                .ends_with("/avatars/210/a_aaa.webp?size=1024"));

            user.avatar = None;
            assert!(user.avatar_url().is_none());

            assert_eq!(user.tag(), "test#1432");
        }

        #[test]
        fn default_avatars() {
            let mut user = User {
                discriminator: 0,
                ..Default::default()
            };

            assert!(user.default_avatar_url().ends_with("0.png"));
            user.discriminator = 1;
            assert!(user.default_avatar_url().ends_with("1.png"));
            user.discriminator = 2;
            assert!(user.default_avatar_url().ends_with("2.png"));
            user.discriminator = 3;
            assert!(user.default_avatar_url().ends_with("3.png"));
            user.discriminator = 4;
            assert!(user.default_avatar_url().ends_with("4.png"));
        }
    }
}