//! User information-related models. use serde_json; use std::fmt; use super::utils::deserialize_u16; use super::prelude::*; use internal::prelude::*; use model::misc::Mentionable; use chrono::NaiveDateTime; use std::fmt::Write; /// Information about the current user. #[derive(Clone, Default, Debug, Deserialize, Serialize)] pub struct CurrentUser { pub id: UserId, pub avatar: Option, #[serde(default)] pub bot: bool, #[serde(deserialize_with = "deserialize_u16")] pub discriminator: u16, pub email: Option, pub mfa_enabled: bool, #[serde(rename = "username")] pub name: String, pub verified: bool, } 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 /// # use serenity::CACHE; /// # /// # let cache = CACHE.read(); /// # /// // assuming the cache has been unlocked /// let user = &cache.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] pub fn avatar_url(&self) -> Option { 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] pub fn default_avatar_url(&self) -> String { default_avatar_url(self.discriminator) } /// Retrieves the URL to the current user's avatar, falling back to the /// default avatar if needed. /// /// This will call [`avatar_url`] first, and if that returns `None`, it /// then falls back to [`default_avatar_url`]. /// /// [`avatar_url`]: #method.avatar_url /// [`default_avatar_url`]: #method.default_avatar_url pub fn face(&self) -> String { self.avatar_url() .unwrap_or_else(|| self.default_avatar_url()) } /// 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::CACHE; /// # /// # let cache = CACHE.read(); /// # /// // assuming the cache has been unlocked /// let user = &cache.user; /// /// 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] pub fn static_avatar_url(&self) -> Option { 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::CACHE; /// # /// # let cache = CACHE.read(); /// # /// // assuming the cache has been unlocked /// println!("The current user's distinct identifier is {}", cache.user.tag()); /// ``` #[inline] 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 [`name`] on the enum. /// /// [`name`]: #method.name #[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] 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. pub fn name(&self) -> Result { serde_json::to_string(self).map_err(From::from) } } /// The representation of a user's status. /// /// # Examples /// /// - [`DoNotDisturb`]; /// - [`Invisible`]. /// /// [`DoNotDisturb`]: #variant.DoNotDisturb /// [`Invisible`]: #variant.Invisible #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)] 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 { 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)] 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, /// 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 [`name`]. The name+discriminator pair is always unique. /// /// [`name`]: #structfield.name #[serde(deserialize_with = "deserialize_u16")] 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, } 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(&self, hasher: &mut H) { self.id.hash(hasher); } } 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] pub fn avatar_url(&self) -> Option { avatar_url(self.id, self.avatar.as_ref()) } /// Retrieves the time that this user was created at. #[inline] pub fn created_at(&self) -> NaiveDateTime { self.id.created_at() } /// Returns the formatted URL to the user's default avatar URL. /// /// This will produce a PNG URL. #[inline] pub fn default_avatar_url(&self) -> String { default_avatar_url(self.discriminator) } /// Retrieves the URL to the user's avatar, falling back to the default /// avatar if needed. /// /// This will call [`avatar_url`] first, and if that returns `None`, it /// then falls back to [`default_avatar_url`]. /// /// [`avatar_url`]: #method.avatar_url /// [`default_avatar_url`]: #method.default_avatar_url pub fn face(&self) -> String { self.avatar_url() .unwrap_or_else(|| self.default_avatar_url()) } /// Returns a static formatted URL of the user's icon, if one exists. /// /// This will always produce a WEBP image URL. #[inline] pub fn static_avatar_url(&self) -> Option { 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 /// # use serenity::prelude::*; /// # use serenity::model::prelude::*; /// # /// use serenity::utils::MessageBuilder; /// use serenity::utils::ContentModifier::Bold; /// /// struct Handler; /// /// impl EventHandler for Handler { /// fn message(&self, _: 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(&content); /// } /// } /// } /// let mut client = Client::new("token", Handler).unwrap(); /// /// client.start().unwrap(); /// ``` #[inline] pub fn tag(&self) -> String { tag(&self.name, self.discriminator) } } 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) } } impl From 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 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 } } fn avatar_url(user_id: UserId, hash: Option<&String>) -> Option { hash.map(|hash| { let ext = if hash.starts_with("a_") { "gif" } else { "webp" }; cdn!("/avatars/{}/{}.{}?size=1024", user_id.0, hash, ext) }) } fn default_avatar_url(discriminator: u16) -> String { cdn!("/embed/avatars/{}.png", discriminator % 5u16) } fn static_avatar_url(user_id: UserId, hash: Option<&String>) -> Option { hash.map(|hash| cdn!("/avatars/{}/{}.webp?size=1024", user_id, hash)) } 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('#'); let _ = write!(tag, "{:04}", discriminator); tag }