Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Entitlements / SKUs #1552

Merged
merged 29 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f004123
add entitlement struct
jhoffi Aug 2, 2024
2e7eb30
add entitlements field to interaction struct
jhoffi Aug 2, 2024
4bbe559
add event handler for entitlements
jhoffi Aug 2, 2024
84ecf01
add description to Entitlement struct & type
jhoffi Aug 2, 2024
6725cb4
properly format changes in entitlement and events
jhoffi Aug 2, 2024
ccb5a90
format entitlement with gofmt
jhoffi Aug 2, 2024
7ad7e1b
move entitlement to structs
jhoffi Aug 2, 2024
87c3490
add subscription id to entitlement struct and add omitempty
jhoffi Aug 2, 2024
9620435
add endpoints
jhoffi Aug 2, 2024
38a1aff
add comments to EntitlementTest and EntitlementOwnerType
jhoffi Aug 2, 2024
efb7d7e
add sku struct
jhoffi Aug 2, 2024
351f5e9
add skus
jhoffi Aug 2, 2024
7bf00e8
fix typo
jhoffi Aug 2, 2024
d2057dc
rename skuid in entitlement struct
jhoffi Aug 2, 2024
b7ed8b8
add comments to SKUFlags
jhoffi Aug 2, 2024
5b3da13
change entitlement timestamps to time.Time
jhoffi Aug 2, 2024
34c29b0
rename skuid in entitlement-test struct
jhoffi Aug 3, 2024
6eb85e1
add documentation
jhoffi Aug 3, 2024
faf1279
remove undocumented subscriptionID field in entitlement struct
jhoffi Aug 3, 2024
887cdb3
add documentation
jhoffi Aug 3, 2024
20d5f76
update documentation
jhoffi Aug 14, 2024
54287e3
change order of EntitlementType values
jhoffi Aug 14, 2024
7cf6c73
move optional entitlement filter options into a separate struct
jhoffi Aug 15, 2024
2c97651
add premium button component
jhoffi Oct 5, 2024
e9b116e
add subscriptions
jhoffi Oct 5, 2024
d9d9f4b
optimize optional fields in entitlement struct
jhoffi Oct 5, 2024
621e4ad
Add documentation
jhoffi Oct 5, 2024
f81b609
typo
jhoffi Oct 5, 2024
347e932
remove unneccessary pointers
jhoffi Oct 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,20 @@ var (
return EndpointPoll(cID, mID) + "/expire"
}

EndpointSKUs = func(aID string) string {
return EndpointApplication(aID) + "/skus"
}

EndpointEntitlements = func(aID string) string {
return EndpointApplication(aID) + "/entitlements"
}
EndpointEntitlement = func(aID, eID string) string {
return EndpointEntitlements(aID) + "/" + eID
}
EndpointEntitlementConsume = func(aID, eID string) string {
return EndpointEntitlement(aID, eID) + "/consume"
}

EndpointApplicationGlobalCommands = func(aID string) string {
return EndpointApplication(aID) + "/commands"
}
Expand Down
72 changes: 72 additions & 0 deletions eventhandlers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions events.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,19 @@ type MessagePollVoteRemove struct {
GuildID string `json:"guild_id,omitempty"`
AnswerID int `json:"answer_id"`
}

// EntitlementCreate is the data for an EntitlementCreate event.
type EntitlementCreate struct {
*Entitlement
}

// EntitlementUpdate is the data for an EntitlementUpdate event.
type EntitlementUpdate struct {
*Entitlement
}

// EntitlementDelete is the data for an EntitlementDelete event.
// NOTE: Entitlements are not deleted when they expire.
type EntitlementDelete struct {
*Entitlement
}
4 changes: 4 additions & 0 deletions interactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ type Interaction struct {

Token string `json:"token"`
Version int `json:"version"`

// Any entitlements for the invoking user, representing access to premium SKUs.
// NOTE: this field is only filled in monetized apps
Entitlements []*Entitlement `json:"entitlements"`
}

type interaction Interaction
Expand Down
84 changes: 84 additions & 0 deletions restapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -3500,3 +3500,87 @@ func (s *Session) PollExpire(channelID, messageID string) (msg *Message, err err
err = unmarshal(body, &msg)
return
}

// ----------------------------------------------------------------------
// Functions specific to SKUs
// ----------------------------------------------------------------------

// SKUs returns all SKUs for a given application.
// appID : The ID of the application.
func (s *Session) SKUs(appID string) (skus []*SKU, err error) {
endpoint := EndpointSKUs(appID)

body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint)
if err != nil {
return
}

err = unmarshal(body, &skus)
return
}

// ----------------------------------------------------------------------
// Functions specific to entitlements
// ----------------------------------------------------------------------

// Entitlements returns all antitlements for a given app, active and expired.
// appID : The ID of the application.
// filterOptions : Optional filter options; otherwise set it to nil.
func (s *Session) Entitlements(appID string, filterOptions *EntitlementFilterOptions, options ...RequestOption) (entitlements []*Entitlement, err error) {
endpoint := EndpointEntitlements(appID)

queryParams := url.Values{}
if filterOptions != nil {
if filterOptions.UserID != "" {
queryParams.Set("user_id", filterOptions.UserID)
}
if filterOptions.SkuIDs != nil && len(filterOptions.SkuIDs) > 0 {
queryParams.Set("sku_ids", strings.Join(filterOptions.SkuIDs, ","))
}
if filterOptions.BeforeID != "" {
queryParams.Set("before", filterOptions.BeforeID)
}
if filterOptions.AfterID != "" {
queryParams.Set("after", filterOptions.AfterID)
}
if filterOptions.Limit > 0 {
queryParams.Set("limit", strconv.Itoa(filterOptions.Limit))
}
if filterOptions.GuildID != "" {
queryParams.Set("guild_id", filterOptions.GuildID)
}
if filterOptions.ExcludeEnded {
queryParams.Set("exclude_ended", "true")
}
}

body, err := s.RequestWithBucketID("GET", endpoint+"?"+queryParams.Encode(), nil, endpoint, options...)
if err != nil {
return
}

err = unmarshal(body, &entitlements)
return
}

// EntitlementConsume marks a given One-Time Purchase for the user as consumed.
func (s *Session) EntitlementConsume(appID, entitlementID string, options ...RequestOption) (err error) {
_, err = s.RequestWithBucketID("POST", EndpointEntitlementConsume(appID, entitlementID), nil, EndpointEntitlementConsume(appID, ""), options...)
return
}

// EntitlementTestCreate creates a test entitlement to a given SKU for a given guild or user.
// Discord will act as though that user or guild has entitlement to your premium offering.
func (s *Session) EntitlementTestCreate(appID string, data *EntitlementTest, options ...RequestOption) (err error) {
endpoint := EndpointEntitlements(appID)

_, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...)
return
}

// EntitlementTestDelete deletes a currently-active test entitlement. Discord will act as though
// that user or guild no longer has entitlement to your premium offering.
func (s *Session) EntitlementTestDelete(appID, entitlementID string, options ...RequestOption) (err error) {
_, err = s.RequestWithBucketID("DELETE", EndpointEntitlement(appID, entitlementID), nil, EndpointEntitlement(appID, ""), options...)
return
}
148 changes: 148 additions & 0 deletions structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2389,6 +2389,154 @@ type Poll struct {
Expiry *time.Time `json:"expiry,omitempty"`
}

// SKUType is the type of SKU (see SKUType* consts)
// https://discord.com/developers/docs/monetization/skus
type SKUType int

// Valid SKUType values
const (
SKUTypeDurable SKUType = 2
SKUTypeConsumable SKUType = 3
SKUTypeSubscription SKUType = 5
// SKUTypeSubscriptionGroup is a system-generated group for each subscription SKU.
SKUTypeSubscriptionGroup SKUType = 6
)

// SKUFlags is a bitfield of flags used to differentiate user and server subscriptions (see SKUFlag* consts)
// https://discord.com/developers/docs/monetization/skus#sku-object-sku-flags
type SKUFlags int

const (
// SKUFlagAvailable indicates that the SKU is available for purchase.
SKUFlagAvailable SKUFlags = 1 << 2
// SKUFlagGuildSubscription indicates that the SKU is a guild subscription.
SKUFlagGuildSubscription SKUFlags = 1 << 7
// SKUFlagUserSubscription indicates that the SKU is a user subscription.
SKUFlagUserSubscription SKUFlags = 1 << 8
)

// SKU (stock-keeping units) represent premium offerings
type SKU struct {
// The ID of the SKU
ID string `json:"id"`

// The Type of the SKU
Type SKUType `json:"type"`

// The ID of the parent application
ApplicationID string `json:"application_id"`

// Customer-facing name of the SKU.
Name string `json:"name"`

// System-generated URL slug based on the SKU's name.
Slug string `json:"slug"`

// SKUFlags combined as a bitfield. The presence of a certain flag can be checked
// by performing a bitwise AND operation between this int and the flag.
Flags SKUFlags `json:"flags"`
}

// EntitlementType is the type of entitlement (see EntitlementType* consts)
// https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-types
type EntitlementType int

// Valid EntitlementType values
const (
EntitlementTypePurchase = 1
EntitlementTypePremiumSubscription = 2
EntitlementTypeDeveloperGift = 3
EntitlementTypeTestModePurchase = 4
EntitlementTypeFreePurchase = 5
EntitlementTypeUserGift = 6
EntitlementTypePremiumPurchase = 7
EntitlementTypeApplicationSubscription = 8
)

// Entitlement represents that a user or guild has access to a premium offering
// in your application.
type Entitlement struct {
// The ID of the entitlement
ID string `json:"id"`

// The ID of the SKU
SKUID string `json:"sku_id"`

// The ID of the parent application
ApplicationID string `json:"application_id"`

// The ID of the user that is granted access to the entitlement's sku
// Only available for user subscriptions.
UserID string `json:"user_id,omitempty"`

// The type of the entitlement
Type EntitlementType `json:"type"`

// The entitlement was deleted
Deleted bool `json:"deleted"`

// The start date at which the entitlement is valid.
// Not present when using test entitlements.
StartsAt *time.Time `json:"starts_at,omitempty"`

// The date at which the entitlement is no longer valid.
// Not present when using test entitlements.
EndsAt *time.Time `json:"ends_at,omitempty"`

// The ID of the guild that is granted access to the entitlement's sku.
// Only available for guild subscriptions.
GuildID string `json:"guild_id,omitempty"`

// Whether or not the entitlement has been consumed.
// Only available for consumable items.
Consumed bool `json:"consumed,omitempty"`
}

// EntitlementOwnerType is the type of entitlement (see EntitlementOwnerType* consts)
type EntitlementOwnerType int

// Valid EntitlementOwnerType values
const (
EntitlementOwnerTypeGuildSubscription EntitlementOwnerType = 1
EntitlementOwnerTypeUserSubscription EntitlementOwnerType = 2
)

// EntitlementTest is used to test granting an entitlement to a user or guild
type EntitlementTest struct {
// The ID of the SKU to grant the entitlement to
SKUID string `json:"sku_id"`

// The ID of the guild or user to grant the entitlement to
OwnerID string `json:"owner_id"`

// OwnerType is the type of which the entitlement should be created
OwnerType EntitlementOwnerType `json:"owner_type"`
}

// EntitlementFilterOptions are the options for filtering Entitlements
type EntitlementFilterOptions struct {
// Optional user ID to look up for.
UserID string

// Optional array of SKU IDs to check for.
SkuIDs []string

// Optional timestamp (snowflake) to retrieve Entitlements before this time.
BeforeID string

// Optional timestamp (snowflake) to retrieve Entitlements after this time.
AfterID string

// Optional maximum number of entitlements to return (1-100, default 100).
Limit int

// Optional guild ID to look up for.
GuildID string

// Optional whether or not ended entitlements should be omitted.
ExcludeEnded bool
}

// Constants for the different bit offsets of text channel permissions
const (
// Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels
Expand Down
Loading