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