Skip to content

Commit

Permalink
Add support for premium buttons (#2905)
Browse files Browse the repository at this point in the history
Fixes #2904

This adds support for the new premium buttons that replace the
`PREMIUM_REQUIRED` interaction response type, which has been deprecated.
  • Loading branch information
mkrasnitski authored Jul 25, 2024
1 parent ee455a0 commit c7540cd
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 2 deletions.
19 changes: 17 additions & 2 deletions src/builder/create_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ impl CreateButton {
})
}

/// Creates a new premium button associated with the given SKU.
///
/// Clicking this button _will not_ trigger an interaction event in your bot.
pub fn new_premium(sku_id: impl Into<SkuId>) -> Self {
Self(Button {
kind: ComponentType::Button,
data: ButtonKind::Premium {
sku_id: sku_id.into(),
},
label: None,
emoji: None,
disabled: false,
})
}

/// Creates a normal button with the given custom ID. You must also set [`Self::label`] and/or
/// [`Self::emoji`] after this.
pub fn new(custom_id: impl Into<String>) -> Self {
Expand All @@ -71,7 +86,7 @@ impl CreateButton {
/// Sets the custom id of the button, a developer-defined identifier. Replaces the current
/// value as set in [`Self::new`].
///
/// Has no effect on link buttons.
/// Has no effect on link buttons and premium buttons.
pub fn custom_id(mut self, id: impl Into<String>) -> Self {
if let ButtonKind::NonLink {
custom_id, ..
Expand All @@ -84,7 +99,7 @@ impl CreateButton {

/// Sets the style of this button.
///
/// Has no effect on link buttons.
/// Has no effect on link buttons and premium buttons.
pub fn style(mut self, new_style: ButtonStyle) -> Self {
if let ButtonKind::NonLink {
style, ..
Expand Down
2 changes: 2 additions & 0 deletions src/builder/create_interaction_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ pub enum CreateInteractionResponse {
/// Responds to the interaction with an upgrade button.
///
/// Corresponds to Discord's `PREMIUM_REQUIRED'.
#[deprecated = "use premium button components via `CreateButton::new_premium` instead"]
PremiumRequired,
}

impl serde::Serialize for CreateInteractionResponse {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
use serde::ser::Error as _;

#[allow(deprecated)]
#[allow(clippy::match_same_arms)] // hurts readability
json!({
"type": match self {
Expand Down
21 changes: 21 additions & 0 deletions src/model/application/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ impl From<SelectMenu> for ActionRowComponent {
#[serde(untagged)]
pub enum ButtonKind {
Link { url: String },
Premium { sku_id: SkuId },
NonLink { custom_id: String, style: ButtonStyle },
}

Expand All @@ -120,6 +121,8 @@ impl Serialize for ButtonKind {
url: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
custom_id: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
sku_id: Option<SkuId>,
}

let helper = match self {
Expand All @@ -129,6 +132,15 @@ impl Serialize for ButtonKind {
style: 5,
url: Some(url),
custom_id: None,
sku_id: None,
},
ButtonKind::Premium {
sku_id,
} => Helper {
style: 6,
url: None,
custom_id: None,
sku_id: Some(*sku_id),
},
ButtonKind::NonLink {
custom_id,
Expand All @@ -137,6 +149,7 @@ impl Serialize for ButtonKind {
style: (*style).into(),
url: None,
custom_id: Some(custom_id),
sku_id: None,
},
};
helper.serialize(serializer)
Expand Down Expand Up @@ -323,5 +336,13 @@ mod tests {
&button,
json!({"type": 2, "style": 5, "url": "https://google.com", "label": "a", "disabled": false}),
);

button.data = ButtonKind::Premium {
sku_id: 1234965026943668316.into(),
};
assert_json(
&button,
json!({"type": 2, "style": 6, "sku_id": "1234965026943668316", "label": "a", "disabled": false}),
);
}
}
11 changes: 11 additions & 0 deletions src/model/application/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ pub struct CurrentApplicationInfo {
std::collections::HashMap<InstallationContext, InstallationContextConfig>,
}

impl CurrentApplicationInfo {
/// Returns the store url for the application. 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/managing-your-store#linking-to-your-store
#[must_use]
pub fn store_url(&self) -> String {
format!("https://discord.com/application-directory/{}/store", self.id)
}
}

#[cfg(feature = "unstable_discord_api")]
enum_number! {
/// An enum representing the [installation contexts].
Expand Down
25 changes: 25 additions & 0 deletions src/model/monetization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ pub struct Sku {
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.
///
Expand Down Expand Up @@ -82,6 +96,17 @@ pub struct Entitlement {
pub guild_id: Option<GuildId>,
}

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
)
}
}

enum_number! {
/// Differentiates between Entitlement types.
///
Expand Down

0 comments on commit c7540cd

Please sign in to comment.