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}