Skip to content

Commit 2d47e2d

Browse files
Merge pull request #1 from bwmarrin/master
Add Entitlements / SKUs (bwmarrin#1552)
2 parents de71cd3 + 33ffff2 commit 2d47e2d

7 files changed

+446
-0
lines changed

components.go

+4
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ const (
121121
DangerButton ButtonStyle = 4
122122
// LinkButton is a special type of button which navigates to a URL. Has grey color.
123123
LinkButton ButtonStyle = 5
124+
// PremiumButton is a special type of button with a blurple color that links to a SKU.
125+
PremiumButton ButtonStyle = 6
124126
)
125127

126128
// ComponentEmoji represents button emoji, if it does have one.
@@ -140,6 +142,8 @@ type Button struct {
140142
// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
141143
URL string `json:"url,omitempty"`
142144
CustomID string `json:"custom_id,omitempty"`
145+
// Identifier for a purchasable SKU. Only available when using premium-style buttons.
146+
SKUID string `json:"sku_id,omitempty"`
143147
}
144148

145149
// MarshalJSON is a method for marshaling Button to a JSON object.

endpoints.go

+22
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var (
3333
EndpointWebhooks = EndpointAPI + "webhooks/"
3434
EndpointStickers = EndpointAPI + "stickers/"
3535
EndpointStageInstances = EndpointAPI + "stage-instances"
36+
EndpointSKUs = EndpointAPI + "skus"
3637

3738
EndpointCDN = "https://cdn.discordapp.com/"
3839
EndpointCDNAttachments = EndpointCDN + "attachments/"
@@ -172,6 +173,27 @@ var (
172173
return EndpointPoll(cID, mID) + "/expire"
173174
}
174175

176+
EndpointApplicationSKUs = func(aID string) string {
177+
return EndpointApplication(aID) + "/skus"
178+
}
179+
180+
EndpointEntitlements = func(aID string) string {
181+
return EndpointApplication(aID) + "/entitlements"
182+
}
183+
EndpointEntitlement = func(aID, eID string) string {
184+
return EndpointEntitlements(aID) + "/" + eID
185+
}
186+
EndpointEntitlementConsume = func(aID, eID string) string {
187+
return EndpointEntitlement(aID, eID) + "/consume"
188+
}
189+
190+
EndpointSubscriptions = func(skuID string) string {
191+
return EndpointSKUs + "/" + skuID + "/subscriptions"
192+
}
193+
EndpointSubscription = func(skuID, subID string) string {
194+
return EndpointSubscriptions(skuID) + "/" + subID
195+
}
196+
175197
EndpointApplicationGlobalCommands = func(aID string) string {
176198
return EndpointApplication(aID) + "/commands"
177199
}

eventhandlers.go

+72
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

events.go

+16
Original file line numberDiff line numberDiff line change
@@ -445,3 +445,19 @@ type MessagePollVoteRemove struct {
445445
GuildID string `json:"guild_id,omitempty"`
446446
AnswerID int `json:"answer_id"`
447447
}
448+
449+
// EntitlementCreate is the data for an EntitlementCreate event.
450+
type EntitlementCreate struct {
451+
*Entitlement
452+
}
453+
454+
// EntitlementUpdate is the data for an EntitlementUpdate event.
455+
type EntitlementUpdate struct {
456+
*Entitlement
457+
}
458+
459+
// EntitlementDelete is the data for an EntitlementDelete event.
460+
// NOTE: Entitlements are not deleted when they expire.
461+
type EntitlementDelete struct {
462+
*Entitlement
463+
}

interactions.go

+4
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ type Interaction struct {
255255

256256
Token string `json:"token"`
257257
Version int `json:"version"`
258+
259+
// Any entitlements for the invoking user, representing access to premium SKUs.
260+
// NOTE: this field is only filled in monetized apps
261+
Entitlements []*Entitlement `json:"entitlements"`
258262
}
259263

260264
type interaction Interaction

restapi.go

+134
Original file line numberDiff line numberDiff line change
@@ -3578,3 +3578,137 @@ func (s *Session) PollExpire(channelID, messageID string) (msg *Message, err err
35783578
err = unmarshal(body, &msg)
35793579
return
35803580
}
3581+
3582+
// ----------------------------------------------------------------------
3583+
// Functions specific to monetization
3584+
// ----------------------------------------------------------------------
3585+
3586+
// SKUs returns all SKUs for a given application.
3587+
// appID : The ID of the application.
3588+
func (s *Session) SKUs(appID string) (skus []*SKU, err error) {
3589+
endpoint := EndpointApplicationSKUs(appID)
3590+
3591+
body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint)
3592+
if err != nil {
3593+
return
3594+
}
3595+
3596+
err = unmarshal(body, &skus)
3597+
return
3598+
}
3599+
3600+
// Entitlements returns all Entitlements for a given app, active and expired.
3601+
// appID : The ID of the application.
3602+
// filterOptions : Optional filter options; otherwise set it to nil.
3603+
func (s *Session) Entitlements(appID string, filterOptions *EntitlementFilterOptions, options ...RequestOption) (entitlements []*Entitlement, err error) {
3604+
endpoint := EndpointEntitlements(appID)
3605+
3606+
queryParams := url.Values{}
3607+
if filterOptions != nil {
3608+
if filterOptions.UserID != "" {
3609+
queryParams.Set("user_id", filterOptions.UserID)
3610+
}
3611+
if filterOptions.SkuIDs != nil && len(filterOptions.SkuIDs) > 0 {
3612+
queryParams.Set("sku_ids", strings.Join(filterOptions.SkuIDs, ","))
3613+
}
3614+
if filterOptions.Before != nil {
3615+
queryParams.Set("before", filterOptions.Before.Format(time.RFC3339))
3616+
}
3617+
if filterOptions.After != nil {
3618+
queryParams.Set("after", filterOptions.After.Format(time.RFC3339))
3619+
}
3620+
if filterOptions.Limit > 0 {
3621+
queryParams.Set("limit", strconv.Itoa(filterOptions.Limit))
3622+
}
3623+
if filterOptions.GuildID != "" {
3624+
queryParams.Set("guild_id", filterOptions.GuildID)
3625+
}
3626+
if filterOptions.ExcludeEnded {
3627+
queryParams.Set("exclude_ended", "true")
3628+
}
3629+
}
3630+
3631+
body, err := s.RequestWithBucketID("GET", endpoint+"?"+queryParams.Encode(), nil, endpoint, options...)
3632+
if err != nil {
3633+
return
3634+
}
3635+
3636+
err = unmarshal(body, &entitlements)
3637+
return
3638+
}
3639+
3640+
// EntitlementConsume marks a given One-Time Purchase for the user as consumed.
3641+
func (s *Session) EntitlementConsume(appID, entitlementID string, options ...RequestOption) (err error) {
3642+
_, err = s.RequestWithBucketID("POST", EndpointEntitlementConsume(appID, entitlementID), nil, EndpointEntitlementConsume(appID, ""), options...)
3643+
return
3644+
}
3645+
3646+
// EntitlementTestCreate creates a test entitlement to a given SKU for a given guild or user.
3647+
// Discord will act as though that user or guild has entitlement to your premium offering.
3648+
func (s *Session) EntitlementTestCreate(appID string, data *EntitlementTest, options ...RequestOption) (err error) {
3649+
endpoint := EndpointEntitlements(appID)
3650+
3651+
_, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...)
3652+
return
3653+
}
3654+
3655+
// EntitlementTestDelete deletes a currently-active test entitlement. Discord will act as though
3656+
// that user or guild no longer has entitlement to your premium offering.
3657+
func (s *Session) EntitlementTestDelete(appID, entitlementID string, options ...RequestOption) (err error) {
3658+
_, err = s.RequestWithBucketID("DELETE", EndpointEntitlement(appID, entitlementID), nil, EndpointEntitlement(appID, ""), options...)
3659+
return
3660+
}
3661+
3662+
// Subscriptions returns all subscriptions containing the SKU.
3663+
// skuID : The ID of the SKU.
3664+
// userID : User ID for which to return subscriptions. Required except for OAuth queries.
3665+
// before : Optional timestamp to retrieve subscriptions before this time.
3666+
// after : Optional timestamp to retrieve subscriptions after this time.
3667+
// limit : Optional maximum number of subscriptions to return (1-100, default 50).
3668+
func (s *Session) Subscriptions(skuID string, userID string, before, after *time.Time, limit int, options ...RequestOption) (subscriptions []*Subscription, err error) {
3669+
endpoint := EndpointSubscriptions(skuID)
3670+
3671+
queryParams := url.Values{}
3672+
if before != nil {
3673+
queryParams.Set("before", before.Format(time.RFC3339))
3674+
}
3675+
if after != nil {
3676+
queryParams.Set("after", after.Format(time.RFC3339))
3677+
}
3678+
if userID != "" {
3679+
queryParams.Set("user_id", userID)
3680+
}
3681+
if limit > 0 {
3682+
queryParams.Set("limit", strconv.Itoa(limit))
3683+
}
3684+
3685+
body, err := s.RequestWithBucketID("GET", endpoint+"?"+queryParams.Encode(), nil, endpoint, options...)
3686+
if err != nil {
3687+
return
3688+
}
3689+
3690+
err = unmarshal(body, &subscriptions)
3691+
return
3692+
}
3693+
3694+
// Subscription returns a subscription by its SKU and subscription ID.
3695+
// skuID : The ID of the SKU.
3696+
// subscriptionID : The ID of the subscription.
3697+
// userID : User ID for which to return the subscription. Required except for OAuth queries.
3698+
func (s *Session) Subscription(skuID, subscriptionID, userID string, options ...RequestOption) (subscription *Subscription, err error) {
3699+
endpoint := EndpointSubscription(skuID, subscriptionID)
3700+
3701+
queryParams := url.Values{}
3702+
if userID != "" {
3703+
// Unlike stated in the documentation, the user_id parameter is required here.
3704+
queryParams.Set("user_id", userID)
3705+
}
3706+
3707+
body, err := s.RequestWithBucketID("GET", endpoint+"?"+queryParams.Encode(), nil, endpoint, options...)
3708+
if err != nil {
3709+
return
3710+
}
3711+
3712+
err = unmarshal(body, &subscription)
3713+
return
3714+
}

0 commit comments

Comments
 (0)