serenity/model/
sticker.rs

1#[cfg(feature = "model")]
2use crate::builder::EditSticker;
3#[cfg(feature = "model")]
4use crate::http::{CacheHttp, Http};
5#[cfg(feature = "model")]
6use crate::internal::prelude::*;
7use crate::model::prelude::*;
8use crate::model::utils::comma_separated_string;
9
10#[cfg(feature = "model")]
11impl StickerPackId {
12    /// Gets the [`StickerPack`] object.
13    ///
14    /// # Errors
15    ///
16    /// Returns [`Error::Http`] if a [`StickerPack`] with that [`StickerPackId`] does not exist, or
17    /// is otherwise unavailable.
18    pub async fn to_sticker_pack(self, http: impl AsRef<Http>) -> Result<StickerPack> {
19        http.as_ref().get_sticker_pack(self).await
20    }
21}
22
23#[cfg(feature = "model")]
24impl StickerId {
25    /// Delete a guild sticker.
26    ///
27    /// **Note**: If the sticker was created by the current user, requires either the [Create Guild
28    /// Expressions] or the [Manage Guild Expressions] permission. Otherwise, the [Manage Guild
29    /// Expressions] permission is required.
30    ///
31    /// # Errors
32    ///
33    /// Returns [`Error::Http`] if the current user lacks permission.
34    ///
35    /// [Create Guild Expressions]: Permissions::CREATE_GUILD_EXPRESSIONS
36    /// [Manage Guild Expressions]: Permissions::MANAGE_GUILD_EXPRESSIONS
37    #[deprecated = "use `GuildId::delete_sticker` instead"]
38    pub async fn delete(self, http: impl AsRef<Http>, guild_id: impl Into<GuildId>) -> Result<()> {
39        guild_id.into().delete_sticker(http, self).await
40    }
41
42    /// Requests the sticker via the REST API to get a [`Sticker`] with all details.
43    ///
44    /// # Errors
45    ///
46    /// Returns [`Error::Http`] if a [`Sticker`] with that [`StickerId`] does not exist, or is
47    /// otherwise unavailable.
48    pub async fn to_sticker(self, http: impl AsRef<Http>) -> Result<Sticker> {
49        http.as_ref().get_sticker(self).await
50    }
51
52    /// Edits the sticker.
53    ///
54    /// **Note**: If the sticker was created by the current user, requires either the [Create Guild
55    /// Expressions] or the [Manage Guild Expressions] permission. Otherwise, the [Manage Guild
56    /// Expressions] permission is required.
57    ///
58    /// # Errors
59    ///
60    /// Returns [`Error::Http`] if the current user lacks permission, or if invalid data is given.
61    ///
62    /// [Create Guild Expressions]: Permissions::CREATE_GUILD_EXPRESSIONS
63    /// [Manage Guild Expressions]: Permissions::MANAGE_GUILD_EXPRESSIONS
64    #[deprecated = "use `GuildId::edit_sticker` instead"]
65    pub async fn edit(
66        self,
67        cache_http: impl CacheHttp,
68        guild_id: impl Into<GuildId>,
69        builder: EditSticker<'_>,
70    ) -> Result<Sticker> {
71        guild_id.into().edit_sticker(cache_http, self, builder).await
72    }
73}
74
75/// The smallest amount of data required to render a sticker.
76///
77/// [Discord docs](https://discord.com/developers/docs/resources/sticker#sticker-item-object).
78#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
79#[derive(Clone, Debug, Deserialize, Serialize)]
80#[non_exhaustive]
81pub struct StickerItem {
82    /// The unique ID given to this sticker.
83    pub id: StickerId,
84    /// The name of the sticker.
85    pub name: String,
86    /// The type of sticker format.
87    pub format_type: StickerFormatType,
88}
89
90#[cfg(feature = "model")]
91impl StickerItem {
92    /// Requests the sticker via the REST API to get a [`Sticker`] with all details.
93    ///
94    /// # Errors
95    ///
96    /// Returns [`Error::Http`] if a [`Sticker`] with that [`StickerId`] does
97    /// not exist, or is otherwise unavailable.
98    #[inline]
99    pub async fn to_sticker(&self, http: impl AsRef<Http>) -> Result<Sticker> {
100        self.id.to_sticker(http).await
101    }
102
103    /// Retrieves the URL to the sticker image.
104    ///
105    /// **Note**: This will only be `None` if the format_type is unknown.
106    #[inline]
107    #[must_use]
108    pub fn image_url(&self) -> Option<String> {
109        sticker_url(self.id, self.format_type)
110    }
111}
112
113/// A sticker sent with a message.
114///
115/// Bots currently can only receive messages with stickers, not send.
116///
117/// [Discord docs](https://discord.com/developers/docs/resources/sticker#sticker-pack-object).
118#[derive(Clone, Debug, Deserialize, Serialize)]
119#[non_exhaustive]
120pub struct StickerPack {
121    /// The unique ID given to this sticker sticker pack.
122    pub id: StickerPackId,
123    /// The stickers in the pack
124    pub stickers: Vec<Sticker>,
125    /// The name of the sticker pack
126    pub name: String,
127    /// The unique ID given to the pack's SKU.
128    pub sku_id: SkuId,
129    /// ID of a sticker in the pack which is shown as the pack's icon.
130    pub cover_sticker_id: Option<StickerId>,
131    /// Description of the sticker pack.
132    pub description: String,
133    /// The unique ID given to the sticker pack's banner image.
134    pub banner_asset_id: StickerPackBannerId,
135}
136
137#[cfg(feature = "model")]
138impl StickerPack {
139    /// Returns the sticker that is shown as the pack's icon
140    #[must_use]
141    pub fn cover_sticker(&self) -> Option<&Sticker> {
142        self.cover_sticker_id.and_then(|id| self.stickers.iter().find(|s| s.id == id))
143    }
144
145    #[must_use]
146    pub fn banner_url(&self) -> String {
147        banner_url(self.banner_asset_id)
148    }
149}
150
151#[cfg(feature = "model")]
152fn banner_url(banner_asset_id: StickerPackBannerId) -> String {
153    cdn!("/app-assets/710982414301790216/store/{}.webp?size=1024", banner_asset_id)
154}
155
156/// A sticker sent with a message.
157///
158/// [Discord docs](https://discord.com/developers/docs/resources/sticker#sticker-object).
159#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
160#[derive(Clone, Debug, Deserialize, Serialize)]
161#[non_exhaustive]
162pub struct Sticker {
163    /// The unique ID given to this sticker.
164    pub id: StickerId,
165    /// The unique ID of the pack the sticker is from.
166    pub pack_id: Option<StickerPackId>,
167    /// The name of the sticker.
168    pub name: String,
169    /// Description of the sticker
170    pub description: Option<String>,
171    /// For guild stickers, the Discord name of a unicode emoji representing the sticker's
172    /// expression. For standard stickers, a list of related expressions.
173    #[serde(with = "comma_separated_string")]
174    pub tags: Vec<String>,
175    /// The type of sticker.
176    #[serde(rename = "type")]
177    pub kind: StickerType,
178    /// The type of sticker format.
179    pub format_type: StickerFormatType,
180    /// Whether or not this guild sticker can be used, may be false due to loss of Server Boosts.
181    #[serde(default)]
182    pub available: bool,
183    /// Id of the guild that owns this sticker.
184    pub guild_id: Option<GuildId>,
185    /// User that uploaded the sticker. This will be `None` if the current user does not have
186    /// either the [Create Guild Expressions] nor the [Manage Guild Expressions] permission.
187    ///
188    /// [Create Guild Expressions]: Permissions::CREATE_GUILD_EXPRESSIONS
189    /// [Manage Guild Expressions]: Permissions::MANAGE_GUILD_EXPRESSIONS
190    pub user: Option<User>,
191    /// A sticker's sort order within a pack.
192    pub sort_value: Option<u16>,
193}
194
195#[cfg(feature = "model")]
196impl Sticker {
197    /// Deletes the [`Sticker`] from its guild.
198    ///
199    /// **Note**: If the sticker was created by the current user, requires either the [Create Guild
200    /// Expressions] or the [Manage Guild Expressions] permission. Otherwise, the [Manage Guild
201    /// Expressions] permission is required.
202    ///
203    /// # Errors
204    ///
205    /// Returns [`Error::Http`] if the current user lacks permission to delete the sticker.
206    ///
207    /// [Create Guild Expressions]: Permissions::CREATE_GUILD_EXPRESSIONS
208    /// [Manage Guild Expressions]: Permissions::MANAGE_GUILD_EXPRESSIONS
209    #[inline]
210    pub async fn delete(&self, http: impl AsRef<Http>) -> Result<()> {
211        if let Some(guild_id) = self.guild_id {
212            guild_id.delete_sticker(http, self.id).await
213        } else {
214            Err(Error::Model(ModelError::DeleteNitroSticker))
215        }
216    }
217
218    /// Edits the sticker.
219    ///
220    /// **Note**: If the sticker was created by the current user, requires either the [Create Guild
221    /// Expressions] or the [Manage Guild Expressions] permission. Otherwise, the [Manage Guild
222    /// Expressions] permission is required.
223    ///
224    /// # Examples
225    ///
226    /// Rename a sticker:
227    ///
228    /// ```rust,no_run
229    /// # use serenity::http::Http;
230    /// # use serenity::model::id::GuildId;
231    /// # use serenity::model::sticker::Sticker;
232    /// use serenity::builder::EditSticker;
233    ///
234    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
235    /// # let http: Http = unimplemented!();
236    /// # let mut sticker: Sticker = unimplemented!();
237    /// let builder = EditSticker::new().name("Bun bun meow");
238    /// sticker.edit(&http, builder).await?;
239    /// # Ok(())
240    /// # }
241    /// ```
242    ///
243    /// # Errors
244    ///
245    /// Returns [`Error::Http`] if the current user lacks permission.
246    ///
247    /// [Create Guild Expressions]: Permissions::CREATE_GUILD_EXPRESSIONS
248    /// [Manage Guild Expressions]: Permissions::MANAGE_GUILD_EXPRESSIONS
249    #[inline]
250    pub async fn edit(
251        &mut self,
252        cache_http: impl CacheHttp,
253        builder: EditSticker<'_>,
254    ) -> Result<()> {
255        if let Some(guild_id) = self.guild_id {
256            *self = guild_id.edit_sticker(cache_http, self.id, builder).await?;
257            Ok(())
258        } else {
259            Err(Error::Model(ModelError::DeleteNitroSticker))
260        }
261    }
262
263    /// Retrieves the URL to the sticker image.
264    ///
265    /// **Note**: This will only be `None` if the format_type is unknown.
266    #[inline]
267    #[must_use]
268    pub fn image_url(&self) -> Option<String> {
269        sticker_url(self.id, self.format_type)
270    }
271}
272
273enum_number! {
274    /// Differentiates between sticker types.
275    ///
276    /// [Discord docs](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-types).
277    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
278    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
279    #[serde(from = "u8", into = "u8")]
280    #[non_exhaustive]
281    pub enum StickerType {
282        /// An official sticker in a pack, part of Nitro or in a removed purchasable pack.
283        Standard = 1,
284        /// A sticker uploaded to a Boosted guild for the guild's members.
285        Guild = 2,
286        _ => Unknown(u8),
287    }
288}
289
290enum_number! {
291    /// Differentiates between sticker formats.
292    ///
293    /// [Discord docs](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-format-types).
294    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
295    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
296    #[serde(from = "u8", into = "u8")]
297    #[non_exhaustive]
298    pub enum StickerFormatType {
299        /// A PNG format sticker.
300        Png = 1,
301        /// An APNG format animated sticker.
302        Apng = 2,
303        /// A LOTTIE format animated sticker.
304        Lottie = 3,
305        /// A GIF format animated sticker.
306        Gif = 4,
307        _ => Unknown(u8),
308    }
309}
310
311#[cfg(feature = "model")]
312fn sticker_url(sticker_id: StickerId, sticker_format_type: StickerFormatType) -> Option<String> {
313    let ext = match sticker_format_type {
314        StickerFormatType::Png | StickerFormatType::Apng => "png",
315        StickerFormatType::Lottie => "json",
316        StickerFormatType::Gif => "gif",
317        StickerFormatType::Unknown(_) => return None,
318    };
319
320    Some(cdn!("/stickers/{}.{}", sticker_id, ext))
321}