Skip to main content

serenity/model/
monetization.rs

1#[cfg(feature = "model")]
2use crate::builder::{Builder as _, GetEntitlements};
3#[cfg(feature = "model")]
4use crate::http::{CacheHttp, Http};
5use crate::model::prelude::*;
6
7/// A premium offering that can be made available to an application's users and guilds.
8///
9/// [Discord docs](https://discord.com/developers/docs/monetization/skus#sku-object).
10#[derive(Clone, Debug, Serialize, Deserialize)]
11pub struct Sku {
12    /// The unique ID of the SKU.
13    pub id: SkuId,
14    /// The class of the SKU.
15    #[serde(rename = "type")]
16    pub kind: SkuKind,
17    /// Id of the SKU's parent application.
18    pub application_id: ApplicationId,
19    /// The customer-facing name of the premium offering.
20    pub name: String,
21    /// A system-generated URL slug based on the SKU.
22    pub slug: String,
23    /// Flags indicating the type of subscription the SKU represents.
24    pub flags: SkuFlags,
25}
26
27impl Sku {
28    /// Returns the store url for this SKU. If included in a message, will render as a rich embed.
29    /// See the [Discord docs] for details.
30    ///
31    /// [Discord docs]: https://discord.com/developers/docs/monetization/skus#linking-to-your-skus
32    #[must_use]
33    pub fn url(&self) -> String {
34        format!(
35            "https://discord.com/application-directory/{}/store/{}",
36            self.application_id, self.id
37        )
38    }
39}
40
41enum_number! {
42    /// Differentiates between SKU classes.
43    ///
44    /// [Discord docs](https://discord.com/developers/docs/monetization/skus#sku-object-sku-types).
45    #[derive(Clone, Debug, Serialize, Deserialize)]
46    #[serde(from = "u8", into = "u8")]
47    #[non_exhaustive]
48    pub enum SkuKind {
49        /// A durable one-time purchase.
50        Durable = 2,
51        /// A consumable one-time purchase.
52        Consumable = 3,
53        /// Represents a recurring subscription.
54        Subscription = 5,
55        /// A system-generated group for each SKU created of type [`SkuKind::Subscription`].
56        SubscriptionGroup = 6,
57        _ => Unknown(u8),
58    }
59}
60
61bitflags! {
62    /// Differentates between user and server subscriptions.
63    ///
64    /// [Discord docs](https://discord.com/developers/docs/monetization/skus#sku-object-sku-flags).
65    #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
66    pub struct SkuFlags: u64 {
67        /// SKU is available for purchase.
68        const AVAILABLE = 1 << 2;
69        /// Recurring SKU that can be purchased by a user and applied to a single server. Grants
70        /// access to every user in that server.
71        const GUILD_SUBSCRIPTION = 1 << 7;
72        /// Recurring SKU purchased by a user for themselves. Grants access to the purchasing user
73        /// in every server.
74        const USER_SUBSCRIPTION = 1 << 8;
75    }
76}
77
78/// Represents that a user or guild has access to a premium offering in the application.
79///
80/// [Discord docs](https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-structure).
81#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
82#[derive(Clone, Debug, Serialize, Deserialize)]
83pub struct Entitlement {
84    /// The ID of the entitlement.
85    pub id: EntitlementId,
86    /// The ID of the corresponding SKU.
87    pub sku_id: SkuId,
88    /// The ID of the parent application.
89    pub application_id: ApplicationId,
90    /// The ID of the user that is granted access to the SKU.
91    pub user_id: Option<UserId>,
92    /// The type of the entitlement.
93    #[serde(rename = "type")]
94    pub kind: EntitlementKind,
95    /// Whether the entitlement has been deleted or not. Entitlements are not deleted when they
96    /// expire.
97    pub deleted: bool,
98    /// Start date after which the entitlement is valid. Not present when using test entitlements.
99    pub starts_at: Option<Timestamp>,
100    /// End date after which the entitlement is no longer valid. Not present when using test
101    /// entitlements.
102    pub ends_at: Option<Timestamp>,
103    /// The ID of the guild that is granted access to the SKU.
104    pub guild_id: Option<GuildId>,
105    /// For consumable items, whether or not the entitlement has been consumed.
106    pub consumed: Option<bool>,
107}
108
109impl Entitlement {
110    /// Returns a link to the SKU corresponding to this entitlement. See [`Sku::url`] for details.
111    #[must_use]
112    pub fn sku_url(&self) -> String {
113        format!(
114            "https://discord.com/application-directory/{}/store/{}",
115            self.application_id, self.sku_id
116        )
117    }
118
119    /// For a one-time purchase consumable SKU (of kind [`Consumable`]), marks the entitlement as
120    /// consumed. On success, the [`consumed`] field will be set to `Some(true)`.
121    ///
122    /// # Errors
123    ///
124    /// Will fail if the corresponding SKU is not of kind [`Consumable`].
125    ///
126    /// [`Consumable`]: SkuKind::Consumable
127    /// [`consumed`]: Entitlement::consumed
128    #[cfg(feature = "model")]
129    pub async fn consume(&mut self, http: &Http) -> Result<()> {
130        http.consume_entitlement(self.id).await?;
131        self.consumed = Some(true);
132        Ok(())
133    }
134
135    /// Returns all entitlements for the current application, active and expired.
136    ///
137    /// # Errors
138    ///
139    /// May error due to an invalid response from discord, or network error.
140    #[cfg(feature = "model")]
141    pub async fn list(
142        cache_http: impl CacheHttp,
143        builder: GetEntitlements,
144    ) -> Result<Vec<Entitlement>> {
145        builder.execute(cache_http, ()).await
146    }
147}
148
149enum_number! {
150    /// Differentiates between Entitlement types.
151    ///
152    /// [Discord docs](https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-types).
153    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
154    #[derive(Clone, Debug, Serialize, Deserialize)]
155    #[serde(from = "u8", into = "u8")]
156    #[non_exhaustive]
157    pub enum EntitlementKind {
158        /// Entitlement was purchased by a user.
159        Purchase = 1,
160        /// Entitlement for a Discord Nitro subscription.
161        PremiumSubscription = 2,
162        /// Entitlement was gifted by an app developer.
163        DeveloperGift = 3,
164        /// Entitlement was purchased by a developer in application test mode.
165        TestModePurchase = 4,
166        /// Entitlement was granted when the corresponding SKU was free.
167        FreePurchase = 5,
168        /// Entitlement was gifted by another user.
169        UserGift = 6,
170        /// Entitlement was claimed by user for free as a Nitro Subscriber.
171        PremiumPurchase = 7,
172        /// Entitlement was purchased as an app subscription.
173        ApplicationSubscription = 8,
174        _ => Unknown(u8),
175    }
176}
177
178pub enum EntitlementOwner {
179    Guild(GuildId),
180    User(UserId),
181}