serenity/model/guild/
emoji.rs

1use std::fmt;
2
3#[cfg(all(feature = "cache", feature = "model"))]
4use crate::cache::Cache;
5#[cfg(all(feature = "cache", feature = "model"))]
6use crate::http::CacheHttp;
7#[cfg(all(feature = "cache", feature = "model"))]
8use crate::internal::prelude::*;
9#[cfg(all(feature = "cache", feature = "model"))]
10use crate::model::id::GuildId;
11use crate::model::id::{EmojiId, RoleId};
12use crate::model::user::User;
13use crate::model::utils::default_true;
14#[cfg(all(feature = "cache", feature = "model"))]
15use crate::model::ModelError;
16
17/// Represents a custom guild emoji, which can either be created using the API, or via an
18/// integration. Emojis created using the API only work within the guild it was created in.
19///
20/// [Discord docs](https://discord.com/developers/docs/resources/emoji#emoji-object).
21#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
22#[derive(Clone, Debug, Deserialize, Serialize)]
23#[non_exhaustive]
24pub struct Emoji {
25    /// Whether the emoji is animated.
26    #[serde(default)]
27    pub animated: bool,
28    /// Whether the emoji can be used. This may be false when the guild loses boosts, reducing the
29    /// emoji limit.
30    #[serde(default = "default_true")]
31    pub available: bool,
32    /// The Id of the emoji.
33    pub id: EmojiId,
34    /// The name of the emoji. It must be at least 2 characters long and can only contain
35    /// alphanumeric characters and underscores.
36    pub name: String,
37    /// Whether the emoji is managed via an [`Integration`] service.
38    ///
39    /// [`Integration`]: super::Integration
40    #[serde(default)]
41    pub managed: bool,
42    /// Whether the emoji name needs to be surrounded by colons in order to be used by the client.
43    #[serde(default)]
44    pub require_colons: bool,
45    /// A list of [`Role`]s that are allowed to use the emoji. If there are no roles specified,
46    /// then usage is unrestricted.
47    ///
48    /// [`Role`]: super::Role
49    #[serde(default)]
50    pub roles: Vec<RoleId>,
51    /// The user who created the emoji.
52    pub user: Option<User>,
53}
54
55#[cfg(feature = "model")]
56impl Emoji {
57    /// Deletes the emoji. This method requires the cache to fetch the guild ID.
58    ///
59    /// **Note**: If the emoji was created by the current user, requires either the [Create Guild
60    /// Expressions] or the [Manage Guild Expressions] permission. Otherwise, the [Manage Guild
61    /// Expressions] permission is required.
62    ///
63    /// # Examples
64    ///
65    /// Delete a given emoji:
66    ///
67    /// ```rust,no_run
68    /// # use serenity::client::Context;
69    /// # use serenity::model::prelude::Emoji;
70    /// #
71    /// # async fn example(ctx: &Context, emoji: Emoji) -> Result<(), Box<dyn std::error::Error>> {
72    /// // assuming emoji has been set already
73    /// match emoji.delete(&ctx).await {
74    ///     Ok(()) => println!("Emoji deleted."),
75    ///     Err(_) => println!("Could not delete emoji."),
76    /// }
77    /// # Ok(())
78    /// # }
79    /// ```
80    ///
81    /// # Errors
82    ///
83    /// Returns [`Error::Http`] if the current user lacks permission, or may return
84    /// [`ModelError::ItemMissing`] if the emoji is not in the cache.
85    ///
86    /// [Create Guild Expressions]: crate::model::Permissions::CREATE_GUILD_EXPRESSIONS
87    /// [Manage Guild Expressions]: crate::model::Permissions::MANAGE_GUILD_EXPRESSIONS
88    #[deprecated = "Use Guild(Id)::delete_emoji, this performs a loop over all guilds!"]
89    #[cfg(feature = "cache")]
90    #[allow(deprecated)]
91    #[inline]
92    pub async fn delete(&self, cache_http: impl CacheHttp) -> Result<()> {
93        let guild_id = self.try_find_guild_id(&cache_http)?;
94        guild_id.delete_emoji(cache_http.http(), self).await
95    }
96
97    /// Edits the emoji by updating it with a new name. This method requires the cache to fetch the
98    /// guild ID.
99    ///
100    /// **Note**: If the emoji was created by the current user, requires either the [Create Guild
101    /// Expressions] or the [Manage Guild Expressions] permission. Otherwise, the [Manage Guild
102    /// Expressions] permission is required.
103    ///
104    /// # Errors
105    ///
106    /// Returns [`Error::Http`] if the current user lacks permission, or if an invalid name is
107    /// given.
108    ///
109    /// [Create Guild Expressions]: crate::model::Permissions::CREATE_GUILD_EXPRESSIONS
110    /// [Manage Guild Expressions]: crate::model::Permissions::MANAGE_GUILD_EXPRESSIONS
111    #[deprecated = "Use Guild(Id)::edit_emoji, this performs a loop over all guilds!"]
112    #[cfg(feature = "cache")]
113    #[allow(deprecated)]
114    pub async fn edit(&mut self, cache_http: impl CacheHttp, name: &str) -> Result<()> {
115        let guild_id = self.try_find_guild_id(&cache_http)?;
116        *self = guild_id.edit_emoji(cache_http.http(), self.id, name).await?;
117        Ok(())
118    }
119
120    /// Finds the [`Guild`] that owns the emoji by looking through the Cache.
121    ///
122    /// [`Guild`]: super::Guild
123    ///
124    /// # Examples
125    ///
126    /// Print the guild id that owns this emoji:
127    ///
128    /// ```rust,no_run
129    /// # use serenity::cache::Cache;
130    /// # use serenity::model::guild::Emoji;
131    /// #
132    /// # fn run(cache: Cache, emoji: Emoji) {
133    /// // assuming emoji has been set already
134    /// if let Some(guild_id) = emoji.find_guild_id(&cache) {
135    ///     println!("{} is owned by {}", emoji.name, guild_id);
136    /// }
137    /// # }
138    /// ```
139    #[deprecated = "This performs a loop over all guilds and should not be used."]
140    #[cfg(feature = "cache")]
141    #[allow(deprecated)]
142    #[must_use]
143    pub fn find_guild_id(&self, cache: impl AsRef<Cache>) -> Option<GuildId> {
144        for guild_entry in cache.as_ref().guilds.iter() {
145            let guild = guild_entry.value();
146
147            if guild.emojis.contains_key(&self.id) {
148                return Some(guild.id);
149            }
150        }
151
152        None
153    }
154
155    #[deprecated = "This performs a loop over all guilds and should not be used."]
156    #[cfg(feature = "cache")]
157    #[allow(deprecated)]
158    #[inline]
159    fn try_find_guild_id(&self, cache_http: impl CacheHttp) -> Result<GuildId> {
160        cache_http
161            .cache()
162            .and_then(|c| self.find_guild_id(c))
163            .ok_or(Error::Model(ModelError::ItemMissing))
164    }
165
166    /// Generates a URL to the emoji's image.
167    ///
168    /// # Examples
169    ///
170    /// Print the direct link to the given emoji:
171    ///
172    /// ```rust,no_run
173    /// # use serenity::model::guild::Emoji;
174    /// #
175    /// # fn run(emoji: Emoji) {
176    /// // assuming emoji has been set already
177    /// println!("Direct link to emoji image: {}", emoji.url());
178    /// # }
179    /// ```
180    #[inline]
181    #[must_use]
182    pub fn url(&self) -> String {
183        let extension = if self.animated { "gif" } else { "png" };
184        cdn!("/emojis/{}.{}", self.id, extension)
185    }
186}
187
188impl fmt::Display for Emoji {
189    /// Formats the emoji into a string that will cause Discord clients to render the emoji.
190    ///
191    /// This is in the format of either `<:NAME:EMOJI_ID>` for normal emojis, or
192    /// `<a:NAME:EMOJI_ID>` for animated emojis.
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        if self.animated {
195            f.write_str("<a:")?;
196        } else {
197            f.write_str("<:")?;
198        }
199        f.write_str(&self.name)?;
200        fmt::Write::write_char(f, ':')?;
201        fmt::Display::fmt(&self.id, f)?;
202        fmt::Write::write_char(f, '>')
203    }
204}
205
206impl From<Emoji> for EmojiId {
207    /// Gets the Id of an [`Emoji`].
208    fn from(emoji: Emoji) -> EmojiId {
209        emoji.id
210    }
211}
212
213impl From<&Emoji> for EmojiId {
214    /// Gets the Id of an [`Emoji`].
215    fn from(emoji: &Emoji) -> EmojiId {
216        emoji.id
217    }
218}