Skip to content

Commit

Permalink
Initila monetization support
Browse files Browse the repository at this point in the history
  • Loading branch information
mkrasnitski committed Oct 7, 2023
1 parent 9121e0c commit d61e2ff
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 0 deletions.
99 changes: 99 additions & 0 deletions src/http/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,33 @@ impl Http {
.await
}

pub async fn create_test_entitlement(
&self,
sku_id: SkuId,
owner: EntitlementOwner,
) -> Result<Entitlement> {
let (owner_id, owner_type) = match owner {
EntitlementOwner::Guild(id) => (id.get(), 1),
EntitlementOwner::User(id) => (id.get(), 2),
};
let map = json!({
"sku_id": sku_id,
"owner_id": owner_id,
"owner_type": owner_type
});
self.fire(Request {
body: Some(to_vec(&map)?),
multipart: None,
headers: None,
method: LightMethod::Post,
route: Route::Entitlements {
application_id: self.try_application_id()?,
},
params: None,
})
.await
}

/// Creates a webhook for the given [channel][`GuildChannel`]'s Id, passing in the given data.
///
/// This method requires authentication.
Expand Down Expand Up @@ -1349,6 +1376,21 @@ impl Http {
.await
}

pub async fn delete_test_entitlement(&self, entitlement_id: EntitlementId) -> Result<()> {
self.wind(204, Request {
body: None,
multipart: None,
headers: None,
method: LightMethod::Delete,
route: Route::Entitlement {
application_id: self.try_application_id()?,
entitlement_id,
},
params: None,
})
.await
}

/// Deletes a [`Webhook`] given its Id.
///
/// This method requires authentication, whereas [`Self::delete_webhook_with_token`] does not.
Expand Down Expand Up @@ -3071,6 +3113,49 @@ impl Http {
.await
}

pub async fn get_entitlements(
&self,
user_id: Option<UserId>,
sku_ids: Option<Vec<SkuId>>,
// before: ?
// after: ?
limit: Option<u8>,
guild_id: Option<GuildId>,
exclude_ended: Option<bool>,
) -> Result<Vec<Entitlement>> {
let mut params = vec![];
if let Some(user_id) = user_id {
params.push(("user_id", user_id.to_string()));
}
if let Some(sku_ids) = sku_ids {
params.push((
"sku_ids",
sku_ids.iter().map(ToString::to_string).collect::<Vec<_>>().join(","),
));
}
if let Some(limit) = limit {
params.push(("limit", limit.to_string()));
}
if let Some(guild_id) = guild_id {
params.push(("guild_id", guild_id.to_string()));
}
if let Some(exclude_ended) = exclude_ended {
params.push(("exclude_ended", exclude_ended.to_string()));
}

self.fire(Request {
body: None,
multipart: None,
headers: None,
method: LightMethod::Get,
route: Route::Entitlements {
application_id: self.try_application_id()?,
},
params: Some(params),
})
.await
}

/// Gets current gateway.
pub async fn get_gateway(&self) -> Result<Gateway> {
self.fire(Request {
Expand Down Expand Up @@ -3924,6 +4009,20 @@ impl Http {
.await
}

pub async fn get_skus(&self) -> Result<Vec<Sku>> {
self.fire(Request {
body: None,
multipart: None,
headers: None,
method: LightMethod::Get,
route: Route::Skus {
application_id: self.try_application_id()?,
},
params: None,
})
.await
}

/// Gets a sticker.
pub async fn get_sticker(&self, sticker_id: StickerId) -> Result<Sticker> {
self.fire(Request {
Expand Down
12 changes: 12 additions & 0 deletions src/http/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,18 @@ routes! ('a, {
api!("/applications/{}/guilds/{}/commands/permissions", application_id, guild_id),
Some(RatelimitingKind::PathAndId(application_id.0));

Skus { application_id: ApplicationId },
api!("/applications/{}/skus", application_id),
Some(RatelimitingKind::PathAndId(application_id.0));

Entitlement { application_id: ApplicationId, entitlement_id: EntitlementId },
api!("/applications/{}/entitlements/{}", application_id, entitlement_id),
Some(RatelimitingKind::PathAndId(application_id.0));

Entitlements { application_id: ApplicationId },
api!("/applications/{}/entitlements", application_id),
Some(RatelimitingKind::PathAndId(application_id.0));

StageInstances,
api!("/stage-instances"),
Some(RatelimitingKind::Path);
Expand Down
5 changes: 5 additions & 0 deletions src/model/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ pub struct StageInstanceId(#[serde(with = "snowflake")] pub NonZeroU64);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct ForumTagId(#[serde(with = "snowflake")] pub NonZeroU64);

/// An identifier for an entitlement.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct EntitlementId(#[serde(with = "snowflake")] pub NonZeroU64);

id_u64! {
AttachmentId;
ApplicationId;
Expand All @@ -264,6 +268,7 @@ id_u64! {
StageInstanceId;
RuleId;
ForumTagId;
EntitlementId;
}

id_from_str! {
Expand Down
2 changes: 2 additions & 0 deletions src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod id;
pub mod invite;
pub mod mention;
pub mod misc;
pub mod monetization;
pub mod permissions;
pub mod sticker;
pub mod timestamp;
Expand Down Expand Up @@ -90,6 +91,7 @@ pub mod prelude {
invite::*,
mention::*,
misc::*,
monetization::*,
permissions::*,
sticker::*,
user::*,
Expand Down
78 changes: 78 additions & 0 deletions src/model/monetization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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 type 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,
}

enum_number! {
/// Differentiates between SKU types.
///
/// [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 {
/// Represents a recurring subscription.
Subscription = 5,
/// A system-generated group for each SKU created of type [`SkuKind::Subscription`].
SubscriptionGroup = 6,
_ => Unknown(u8),
}
}

/// 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)
#[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 user that is granted access to the SKU.
pub user_id: Option<UserId>,
/// The ID of the guild that is granted access to the SKU.
pub guild_id: Option<GuildId>,
/// The ID of the parent application.
pub application_id: ApplicationId,
/// The type of the entitlement.
#[serde(rename = "type")]
pub kind: EntitlementKind,
/// Whether the entitlement has been consumed or not. Will always be `false` for ongoing App
/// Subscriptions.
pub consumed: 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>,
}

enum_number! {
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(from = "u8", into = "u8")]
#[non_exhaustive]
pub enum EntitlementKind {
ApplicationSubscription = 8,
_ => Unknown(u8),
}
}

pub enum EntitlementOwner {
Guild(GuildId),
User(UserId),
}

0 comments on commit d61e2ff

Please sign in to comment.