From f004123b109e9d79f5828f2fd516a915ede23b23 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 19:04:54 +0200 Subject: [PATCH 01/29] add entitlement struct --- entitlement.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 entitlement.go diff --git a/entitlement.go b/entitlement.go new file mode 100644 index 000000000..303d0e7e4 --- /dev/null +++ b/entitlement.go @@ -0,0 +1,52 @@ +package discordgo + +// The type of entitlement +type EntitlementType int + +// Valid EntitlementType values +// https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-types +const ( + EntitlementTypePurchase = 1 + EntitlementTypePremiumSubscription = 2 + EntitlementTypeDeveloperGift = 3 + EntitlementTypeTestModePurchase = 4 + EntitlementTypeFreePurchase = 5 + EntitlementTypeUserGift = 6 + EntitlementTypePremiumPurchase = 7 + EntitlementTypeApplicationSubscription = 8 +) + +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 + UserID string `json:"user_id"` + + // The type of 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 string `json:"starts_at"` + + // The date at which the entitlement is no longer valid. + // Not present when using test entitlements. + EndsAt string `json:"ends_at"` + + // The ID of the guild that is granted access to the entitlement's sku. + GuildID string `json:"guild_id"` + + // Whether or not the entitlement has been consumed. + // Only available for consumable items. + Consumed bool `json:"consumed"` +} \ No newline at end of file From 2e7eb309ed0d86369e4c69b21713dac4e2062249 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 19:05:19 +0200 Subject: [PATCH 02/29] add entitlements field to interaction struct --- interactions.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interactions.go b/interactions.go index 93ba366bb..774b17ff1 100644 --- a/interactions.go +++ b/interactions.go @@ -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 From 4bbe5590d6673a191bc27c29223c056ec92549f5 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 19:33:31 +0200 Subject: [PATCH 03/29] add event handler for entitlements --- eventhandlers.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ events.go | 16 +++++++++++ 2 files changed, 88 insertions(+) diff --git a/eventhandlers.go b/eventhandlers.go index 659b6748e..06df984bd 100644 --- a/eventhandlers.go +++ b/eventhandlers.go @@ -18,6 +18,9 @@ const ( channelUpdateEventType = "CHANNEL_UPDATE" connectEventType = "__CONNECT__" disconnectEventType = "__DISCONNECT__" + entitlementCreateEventType = "ENTITLEMENT_CREATE" + entitlementDeleteEventType = "ENTITLEMENT_DELETE" + entitlementUpdateEventType = "ENTITLEMENT_UPDATE" eventEventType = "__EVENT__" guildAuditLogEntryCreateEventType = "GUILD_AUDIT_LOG_ENTRY_CREATE" guildBanAddEventType = "GUILD_BAN_ADD" @@ -285,6 +288,66 @@ func (eh disconnectEventHandler) Handle(s *Session, i interface{}) { } } +// entitlementCreateEventHandler is an event handler for EntitlementCreate events. +type entitlementCreateEventHandler func(*Session, *EntitlementCreate) + +// Type returns the event type for EntitlementCreate events. +func (eh entitlementCreateEventHandler) Type() string { + return entitlementCreateEventType +} + +// New returns a new instance of EntitlementCreate. +func (eh entitlementCreateEventHandler) New() interface{} { + return &EntitlementCreate{} +} + +// Handle is the handler for EntitlementCreate events. +func (eh entitlementCreateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*EntitlementCreate); ok { + eh(s, t) + } +} + +// entitlementDeleteEventHandler is an event handler for EntitlementDelete events. +type entitlementDeleteEventHandler func(*Session, *EntitlementDelete) + +// Type returns the event type for EntitlementDelete events. +func (eh entitlementDeleteEventHandler) Type() string { + return entitlementDeleteEventType +} + +// New returns a new instance of EntitlementDelete. +func (eh entitlementDeleteEventHandler) New() interface{} { + return &EntitlementDelete{} +} + +// Handle is the handler for EntitlementDelete events. +func (eh entitlementDeleteEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*EntitlementDelete); ok { + eh(s, t) + } +} + +// entitlementUpdateEventHandler is an event handler for EntitlementUpdate events. +type entitlementUpdateEventHandler func(*Session, *EntitlementUpdate) + +// Type returns the event type for EntitlementUpdate events. +func (eh entitlementUpdateEventHandler) Type() string { + return entitlementUpdateEventType +} + +// New returns a new instance of EntitlementUpdate. +func (eh entitlementUpdateEventHandler) New() interface{} { + return &EntitlementUpdate{} +} + +// Handle is the handler for EntitlementUpdate events. +func (eh entitlementUpdateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*EntitlementUpdate); ok { + eh(s, t) + } +} + // eventEventHandler is an event handler for Event events. type eventEventHandler func(*Session, *Event) @@ -1401,6 +1464,12 @@ func handlerForInterface(handler interface{}) EventHandler { return connectEventHandler(v) case func(*Session, *Disconnect): return disconnectEventHandler(v) + case func(*Session, *EntitlementCreate): + return entitlementCreateEventHandler(v) + case func(*Session, *EntitlementDelete): + return entitlementDeleteEventHandler(v) + case func(*Session, *EntitlementUpdate): + return entitlementUpdateEventHandler(v) case func(*Session, *Event): return eventEventHandler(v) case func(*Session, *GuildAuditLogEntryCreate): @@ -1526,6 +1595,9 @@ func init() { registerInterfaceProvider(channelDeleteEventHandler(nil)) registerInterfaceProvider(channelPinsUpdateEventHandler(nil)) registerInterfaceProvider(channelUpdateEventHandler(nil)) + registerInterfaceProvider(entitlementCreateEventHandler(nil)) + registerInterfaceProvider(entitlementDeleteEventHandler(nil)) + registerInterfaceProvider(entitlementUpdateEventHandler(nil)) registerInterfaceProvider(guildAuditLogEntryCreateEventHandler(nil)) registerInterfaceProvider(guildBanAddEventHandler(nil)) registerInterfaceProvider(guildBanRemoveEventHandler(nil)) diff --git a/events.go b/events.go index 6a23a9e23..33e5320b3 100644 --- a/events.go +++ b/events.go @@ -445,3 +445,19 @@ type MessagePollVoteRemove struct { GuildID string `json:"guild_id,omitempty"` AnswerID int `json:"answer_id"` } + +// EntitlementCreate is the data for a EntitlementCreate event. +type EntitlementCreate struct { + *Entitlement +} + +// EntitlementUpdate is the data for a EntitlementUpdate event. +type EntitlementUpdate struct { + *Entitlement +} + +// EntitlementDelete is the data for a EntitlementDelete event. +// NOTE: Entitlements are not deleted when they expire. +type EntitlementDelete struct { + *Entitlement +} \ No newline at end of file From 84ecf01f15a208e658b62d85bd18ee2f7ceb9cfe Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 19:47:13 +0200 Subject: [PATCH 04/29] add description to Entitlement struct & type --- entitlement.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/entitlement.go b/entitlement.go index 303d0e7e4..8db894a6d 100644 --- a/entitlement.go +++ b/entitlement.go @@ -1,10 +1,10 @@ package discordgo -// The type of entitlement +// 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 -// https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-types const ( EntitlementTypePurchase = 1 EntitlementTypePremiumSubscription = 2 @@ -16,6 +16,8 @@ const ( EntitlementTypeApplicationSubscription = 8 ) +// Entitlements represent 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"` From 6725cb467d95b86103246074df60dd60f9859dda Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 21:52:09 +0200 Subject: [PATCH 05/29] properly format changes in entitlement and events --- entitlement.go | 6 +++--- events.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/entitlement.go b/entitlement.go index 8db894a6d..5e2f465ff 100644 --- a/entitlement.go +++ b/entitlement.go @@ -16,14 +16,14 @@ const ( EntitlementTypeApplicationSubscription = 8 ) -// Entitlements represent that a user or guild has access to a premium offering +// 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"` + SkuID string `json:"sku_id"` // The ID of the parent application ApplicationID string `json:"application_id"` @@ -51,4 +51,4 @@ type Entitlement struct { // Whether or not the entitlement has been consumed. // Only available for consumable items. Consumed bool `json:"consumed"` -} \ No newline at end of file +} diff --git a/events.go b/events.go index 33e5320b3..a147d8782 100644 --- a/events.go +++ b/events.go @@ -460,4 +460,4 @@ type EntitlementUpdate struct { // NOTE: Entitlements are not deleted when they expire. type EntitlementDelete struct { *Entitlement -} \ No newline at end of file +} From ccb5a90fb7e327695e252359d40214c557c6873e Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 21:59:01 +0200 Subject: [PATCH 06/29] format entitlement with gofmt --- entitlement.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/entitlement.go b/entitlement.go index 5e2f465ff..6732e1afd 100644 --- a/entitlement.go +++ b/entitlement.go @@ -6,14 +6,14 @@ type EntitlementType int // Valid EntitlementType values const ( - EntitlementTypePurchase = 1 - EntitlementTypePremiumSubscription = 2 - EntitlementTypeDeveloperGift = 3 - EntitlementTypeTestModePurchase = 4 - EntitlementTypeFreePurchase = 5 - EntitlementTypeUserGift = 6 - EntitlementTypePremiumPurchase = 7 + EntitlementTypePurchase = 1 + EntitlementTypePremiumSubscription = 2 + EntitlementTypeDeveloperGift = 3 + EntitlementTypeTestModePurchase = 4 + EntitlementTypeFreePurchase = 5 + EntitlementTypeUserGift = 6 EntitlementTypeApplicationSubscription = 8 + EntitlementTypePremiumPurchase = 7 ) // Entitlement represents that a user or guild has access to a premium offering @@ -37,11 +37,11 @@ type Entitlement struct { // The entitlement was deleted Deleted bool `json:"deleted"` - // The start date at which the entitlement is valid. + // The start date at which the entitlement is valid. // Not present when using test entitlements. StartsAt string `json:"starts_at"` - // The date at which the entitlement is no longer valid. + // The date at which the entitlement is no longer valid. // Not present when using test entitlements. EndsAt string `json:"ends_at"` From 7ad7e1b9ed451222569e9d09812559a6350f3229 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 22:26:43 +0200 Subject: [PATCH 07/29] move entitlement to structs --- entitlement.go | 54 -------------------------------------------------- structs.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 54 deletions(-) delete mode 100644 entitlement.go diff --git a/entitlement.go b/entitlement.go deleted file mode 100644 index 6732e1afd..000000000 --- a/entitlement.go +++ /dev/null @@ -1,54 +0,0 @@ -package discordgo - -// 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 - EntitlementTypeApplicationSubscription = 8 - EntitlementTypePremiumPurchase = 7 -) - -// 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 - UserID string `json:"user_id"` - - // The type of 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 string `json:"starts_at"` - - // The date at which the entitlement is no longer valid. - // Not present when using test entitlements. - EndsAt string `json:"ends_at"` - - // The ID of the guild that is granted access to the entitlement's sku. - GuildID string `json:"guild_id"` - - // Whether or not the entitlement has been consumed. - // Only available for consumable items. - Consumed bool `json:"consumed"` -} diff --git a/structs.go b/structs.go index 475107c0d..82c3e073a 100644 --- a/structs.go +++ b/structs.go @@ -2389,6 +2389,59 @@ type Poll struct { Expiry *time.Time `json:"expiry,omitempty"` } +// 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 + EntitlementTypeApplicationSubscription = 8 + EntitlementTypePremiumPurchase = 7 +) + +// 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 + UserID string `json:"user_id"` + + // The type of 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 string `json:"starts_at"` + + // The date at which the entitlement is no longer valid. + // Not present when using test entitlements. + EndsAt string `json:"ends_at"` + + // The ID of the guild that is granted access to the entitlement's sku. + GuildID string `json:"guild_id"` + + // Whether or not the entitlement has been consumed. + // Only available for consumable items. + Consumed bool `json:"consumed"` +} + // Constants for the different bit offsets of text channel permissions const ( // Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels From 87c34908f446966a80b0ff88dd2890117aeb3b30 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Aug 2024 23:16:19 +0200 Subject: [PATCH 08/29] add subscription id to entitlement struct and add omitempty --- structs.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/structs.go b/structs.go index 82c3e073a..9b2cc57ac 100644 --- a/structs.go +++ b/structs.go @@ -2418,9 +2418,9 @@ type Entitlement struct { ApplicationID string `json:"application_id"` // The ID of the user that is granted access to the entitlement's sku - UserID string `json:"user_id"` + UserID string `json:"user_id,omitempty"` - // The type of entitlement + // The type of the entitlement Type EntitlementType `json:"type"` // The entitlement was deleted @@ -2428,18 +2428,22 @@ type Entitlement struct { // The start date at which the entitlement is valid. // Not present when using test entitlements. - StartsAt string `json:"starts_at"` + StartsAt string `json:"starts_at,omitempty"` // The date at which the entitlement is no longer valid. // Not present when using test entitlements. - EndsAt string `json:"ends_at"` + EndsAt string `json:"ends_at,omitempty"` // The ID of the guild that is granted access to the entitlement's sku. - GuildID string `json:"guild_id"` + GuildID string `json:"guild_id,omitempty"` // Whether or not the entitlement has been consumed. // Only available for consumable items. - Consumed bool `json:"consumed"` + Consumed bool `json:"consumed,omitempty"` + + // The SubscriptionID of the entitlement. + // Not present when using test entitlements. + SubscriptionID string `json:"subscription_id,omitempty"` } // Constants for the different bit offsets of text channel permissions From 962043529b4172a6adcd1486250bad42c3479c4d Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 00:41:54 +0200 Subject: [PATCH 09/29] add endpoints --- endpoints.go | 10 ++++++++ restapi.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ structs.go | 19 ++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/endpoints.go b/endpoints.go index 29189af69..1a4731819 100644 --- a/endpoints.go +++ b/endpoints.go @@ -172,6 +172,16 @@ var ( return EndpointPoll(cID, mID) + "/expire" } + 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" } diff --git a/restapi.go b/restapi.go index ffb8e3022..ae53debcd 100644 --- a/restapi.go +++ b/restapi.go @@ -3500,3 +3500,73 @@ func (s *Session) PollExpire(channelID, messageID string) (msg *Message, err err err = unmarshal(body, &msg) return } + +// ---------------------------------------------------------------------- +// Functions specific to entitlements +// ---------------------------------------------------------------------- + +// Entitlements returns all antitlements for a given app, active and expired. +// appID : The ID of the application. +// userID : Optional user ID to look up for. +// skuIDs : Optional array of SKU IDs to check for. +// before : Optional timestamp to retrieve Entitlements before this time. +// after : Optional timestamp to retrieve Entitlements after this time. +// limit : Optional maximum number of entitlements to return (1-100, default 100). +// guildID : Optional guild ID to look up for. +// exclude_ended : Optional whether or not ended entitlements should be omitted. +func (s *Session) Entitlements(appID, userID string, skuIDs *[]string, beforeID, afterID string, limit int, guildID string, excludeEnded bool, options ...RequestOption) (entitlements []*Entitlement, err error) { + endpoint := EndpointEntitlements(appID) + + queryParams := url.Values{} + if userID != "" { + queryParams.Set("user_id", userID) + } + if skuIDs != nil && len(*skuIDs) > 0 { + queryParams.Set("sku_ids", strings.Join(*skuIDs, ",")) + } + if beforeID != "" { + queryParams.Set("before", beforeID) + } + if afterID != "" { + queryParams.Set("after", afterID) + } + if limit > 0 { + queryParams.Set("limit", strconv.Itoa(limit)) + } + if guildID != "" { + queryParams.Set("guild_id", guildID) + } + if 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 +} diff --git a/structs.go b/structs.go index 9b2cc57ac..5b57d0022 100644 --- a/structs.go +++ b/structs.go @@ -2446,6 +2446,25 @@ type Entitlement struct { SubscriptionID string `json:"subscription_id,omitempty"` } +// EntitlementOwnerType is the type of entitlement +type EntitlementOwnerType int + +const ( + EntitlementOwnerTypeGuildSubscription EntitlementOwnerType = 1 + EntitlementOwnerTypeUserSubscription EntitlementOwnerType = 2 +) + +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"` +} + // Constants for the different bit offsets of text channel permissions const ( // Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels From 38a1affbd435dd38a5f88d8bcd265519d569efb1 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 00:49:54 +0200 Subject: [PATCH 10/29] add comments to EntitlementTest and EntitlementOwnerType --- structs.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/structs.go b/structs.go index 5b57d0022..1c6c16f36 100644 --- a/structs.go +++ b/structs.go @@ -2449,11 +2449,13 @@ type Entitlement struct { // EntitlementOwnerType is the type of entitlement 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"` From efb7d7e84668ac6ef11229b3732f3c5799300a0e Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 01:16:30 +0200 Subject: [PATCH 11/29] add sku struct --- structs.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/structs.go b/structs.go index 1c6c16f36..781b5fe31 100644 --- a/structs.go +++ b/structs.go @@ -2389,6 +2389,50 @@ 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 +) + +// SKUFlag is a bitfield of flags used to differentiate user and server subscriptions. +type SKUFlag int + +const ( + // SKUFlageAvailable indicates that the SKU is available for purchase. + SKUFlagAvailable SKUFlag = 1 << 2 + SKUFlagGuildSubscription SKUFlag = 1 << 7 + SKUFlageUserSubscription SKUFlag = 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 slub based on the SKU's name. + Slug string `json:"slug"` + + // SKUFlags combined as a bitfield + Flags int `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 From 351f5e91cbbbefa8d540b81021bfedffe8c9d11d Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 01:34:08 +0200 Subject: [PATCH 12/29] add skus --- endpoints.go | 4 ++++ restapi.go | 18 ++++++++++++++++++ structs.go | 19 ++++++++++--------- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/endpoints.go b/endpoints.go index 1a4731819..412bc84f8 100644 --- a/endpoints.go +++ b/endpoints.go @@ -172,6 +172,10 @@ var ( return EndpointPoll(cID, mID) + "/expire" } + EndpointSKUs = func(aID string) string { + return EndpointApplication(aID) + "/skus" + } + EndpointEntitlements = func(aID string) string { return EndpointApplication(aID) + "/entitlements" } diff --git a/restapi.go b/restapi.go index ae53debcd..061b845c1 100644 --- a/restapi.go +++ b/restapi.go @@ -3501,6 +3501,24 @@ func (s *Session) PollExpire(channelID, messageID string) (msg *Message, err err 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 // ---------------------------------------------------------------------- diff --git a/structs.go b/structs.go index 781b5fe31..202676413 100644 --- a/structs.go +++ b/structs.go @@ -2395,21 +2395,21 @@ type SKUType int // Valid SKUType values const ( - SKUTypeDurable SKUType = 2 - SKUTypeConsumable SKUType = 3 + SKUTypeDurable SKUType = 2 + SKUTypeConsumable SKUType = 3 SKUTypeSubscription SKUType = 5 // SKUTypeSubscriptionGroup is a system-generated group for each subscription SKU. SKUTypeSubscriptionGroup SKUType = 6 ) -// SKUFlag is a bitfield of flags used to differentiate user and server subscriptions. -type SKUFlag int +// SKUFlags is a bitfield of flags used to differentiate user and server subscriptions. +type SKUFlags int const ( // SKUFlageAvailable indicates that the SKU is available for purchase. - SKUFlagAvailable SKUFlag = 1 << 2 - SKUFlagGuildSubscription SKUFlag = 1 << 7 - SKUFlageUserSubscription SKUFlag = 1 << 8 + SKUFlagAvailable SKUFlags = 1 << 2 + SKUFlagGuildSubscription SKUFlags = 1 << 7 + SKUFlageUserSubscription SKUFlags = 1 << 8 ) // SKU (stock-keeping units) represent premium offerings. @@ -2429,8 +2429,9 @@ type SKU struct { // System-generated URL slub based on the SKU's name. Slug string `json:"slug"` - // SKUFlags combined as a bitfield - Flags int `json:"flags"` + // SKUFlags combined as a bitfield. The presence of a certain falg 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) From 7bf00e87c70dee9d18263e61263e9d3704b5dd87 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 01:40:44 +0200 Subject: [PATCH 13/29] fix typo --- structs.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/structs.go b/structs.go index 202676413..8ec82c29b 100644 --- a/structs.go +++ b/structs.go @@ -2406,10 +2406,10 @@ const ( type SKUFlags int const ( - // SKUFlageAvailable indicates that the SKU is available for purchase. + // SKUFlagAvailable indicates that the SKU is available for purchase. SKUFlagAvailable SKUFlags = 1 << 2 SKUFlagGuildSubscription SKUFlags = 1 << 7 - SKUFlageUserSubscription SKUFlags = 1 << 8 + SKUFlagUserSubscription SKUFlags = 1 << 8 ) // SKU (stock-keeping units) represent premium offerings. @@ -2426,10 +2426,10 @@ type SKU struct { // Customer-facing name of the SKU. Name string `json:"name"` - // System-generated URL slub based on the SKU's 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 falg can be checked + // 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"` } From d2057dc3f46f29cf26c2fe79f4db1691f2c4d417 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 01:41:43 +0200 Subject: [PATCH 14/29] rename skuid in entitlement struct --- structs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structs.go b/structs.go index 8ec82c29b..c6486feb9 100644 --- a/structs.go +++ b/structs.go @@ -2457,7 +2457,7 @@ type Entitlement struct { ID string `json:"id"` // The ID of the SKU - SkuID string `json:"sku_id"` + SKUID string `json:"sku_id"` // The ID of the parent application ApplicationID string `json:"application_id"` From b7ed8b8afcbfd9c510180b1d1286ddf50f1860b1 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 01:43:49 +0200 Subject: [PATCH 15/29] add comments to SKUFlags --- structs.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/structs.go b/structs.go index c6486feb9..9053cd972 100644 --- a/structs.go +++ b/structs.go @@ -2407,8 +2407,10 @@ type SKUFlags int const ( // SKUFlagAvailable indicates that the SKU is available for purchase. - SKUFlagAvailable SKUFlags = 1 << 2 + 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 ) From 5b3da1397148a98b9f8448f32b46db1f513a8ad4 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 01:56:29 +0200 Subject: [PATCH 16/29] change entitlement timestamps to time.Time --- structs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structs.go b/structs.go index 9053cd972..2ace7c7f7 100644 --- a/structs.go +++ b/structs.go @@ -2475,11 +2475,11 @@ type Entitlement struct { // The start date at which the entitlement is valid. // Not present when using test entitlements. - StartsAt string `json:"starts_at,omitempty"` + StartsAt *time.Time `json:"starts_at,omitempty"` // The date at which the entitlement is no longer valid. // Not present when using test entitlements. - EndsAt string `json:"ends_at,omitempty"` + EndsAt *time.Time `json:"ends_at,omitempty"` // The ID of the guild that is granted access to the entitlement's sku. GuildID string `json:"guild_id,omitempty"` From 34c29b052effaeb24e2bdaf2e3335a67f1ade501 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 23:26:28 +0200 Subject: [PATCH 17/29] rename skuid in entitlement-test struct --- structs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structs.go b/structs.go index 2ace7c7f7..66c9aef1a 100644 --- a/structs.go +++ b/structs.go @@ -2505,7 +2505,7 @@ const ( // 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"` + SKUID string `json:"sku_id"` // The ID of the guild or user to grant the entitlement to OwnerID string `json:"owner_id"` From 6eb85e1425e1bf621057834059a1fff53d6b35e8 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 23:31:06 +0200 Subject: [PATCH 18/29] add documentation --- structs.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/structs.go b/structs.go index 66c9aef1a..91d64f83f 100644 --- a/structs.go +++ b/structs.go @@ -2402,7 +2402,8 @@ const ( SKUTypeSubscriptionGroup SKUType = 6 ) -// SKUFlags is a bitfield of flags used to differentiate user and server subscriptions. +// 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 ( @@ -2414,7 +2415,7 @@ const ( SKUFlagUserSubscription SKUFlags = 1 << 8 ) -// SKU (stock-keeping units) represent premium offerings. +// SKU (stock-keeping units) represent premium offerings type SKU struct { // The ID of the SKU ID string `json:"id"` @@ -2493,7 +2494,7 @@ type Entitlement struct { SubscriptionID string `json:"subscription_id,omitempty"` } -// EntitlementOwnerType is the type of entitlement +// EntitlementOwnerType is the type of entitlement (see EntitlementOwnerType* consts) type EntitlementOwnerType int // Valid EntitlementOwnerType values From faf127994523c1fb640c1f633c3eb4eddd716c61 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 23:32:22 +0200 Subject: [PATCH 19/29] remove undocumented subscriptionID field in entitlement struct --- structs.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/structs.go b/structs.go index 91d64f83f..af9dfcf23 100644 --- a/structs.go +++ b/structs.go @@ -2488,10 +2488,6 @@ type Entitlement struct { // Whether or not the entitlement has been consumed. // Only available for consumable items. Consumed bool `json:"consumed,omitempty"` - - // The SubscriptionID of the entitlement. - // Not present when using test entitlements. - SubscriptionID string `json:"subscription_id,omitempty"` } // EntitlementOwnerType is the type of entitlement (see EntitlementOwnerType* consts) From 887cdb364335cc45b719ad7b49d4c7f1d7fd42c9 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 3 Aug 2024 23:38:37 +0200 Subject: [PATCH 20/29] add documentation --- structs.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/structs.go b/structs.go index af9dfcf23..0033f1256 100644 --- a/structs.go +++ b/structs.go @@ -2466,6 +2466,7 @@ type Entitlement struct { 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 @@ -2483,6 +2484,7 @@ type Entitlement struct { 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. From 20d5f76073da3d015bb244fbe6620ac767eba3eb Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 15 Aug 2024 00:23:29 +0200 Subject: [PATCH 21/29] update documentation --- events.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/events.go b/events.go index a147d8782..6a410fe35 100644 --- a/events.go +++ b/events.go @@ -446,17 +446,17 @@ type MessagePollVoteRemove struct { AnswerID int `json:"answer_id"` } -// EntitlementCreate is the data for a EntitlementCreate event. +// EntitlementCreate is the data for an EntitlementCreate event. type EntitlementCreate struct { *Entitlement } -// EntitlementUpdate is the data for a EntitlementUpdate event. +// EntitlementUpdate is the data for an EntitlementUpdate event. type EntitlementUpdate struct { *Entitlement } -// EntitlementDelete is the data for a EntitlementDelete event. +// EntitlementDelete is the data for an EntitlementDelete event. // NOTE: Entitlements are not deleted when they expire. type EntitlementDelete struct { *Entitlement From 54287e39e880c0bfbc0e61bc8a82d489c98a1010 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 15 Aug 2024 00:23:56 +0200 Subject: [PATCH 22/29] change order of EntitlementType values --- structs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structs.go b/structs.go index 0033f1256..32def1b4e 100644 --- a/structs.go +++ b/structs.go @@ -2449,8 +2449,8 @@ const ( EntitlementTypeTestModePurchase = 4 EntitlementTypeFreePurchase = 5 EntitlementTypeUserGift = 6 - EntitlementTypeApplicationSubscription = 8 EntitlementTypePremiumPurchase = 7 + EntitlementTypeApplicationSubscription = 8 ) // Entitlement represents that a user or guild has access to a premium offering From 7cf6c73ba328e7d28e22c76267baba55a9d18b69 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 15 Aug 2024 22:20:54 +0200 Subject: [PATCH 23/29] move optional entitlement filter options into a separate struct --- restapi.go | 52 ++++++++++++++++++++++++---------------------------- structs.go | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/restapi.go b/restapi.go index 061b845c1..eaecc71b2 100644 --- a/restapi.go +++ b/restapi.go @@ -3525,37 +3525,33 @@ func (s *Session) SKUs(appID string) (skus []*SKU, err error) { // Entitlements returns all antitlements for a given app, active and expired. // appID : The ID of the application. -// userID : Optional user ID to look up for. -// skuIDs : Optional array of SKU IDs to check for. -// before : Optional timestamp to retrieve Entitlements before this time. -// after : Optional timestamp to retrieve Entitlements after this time. -// limit : Optional maximum number of entitlements to return (1-100, default 100). -// guildID : Optional guild ID to look up for. -// exclude_ended : Optional whether or not ended entitlements should be omitted. -func (s *Session) Entitlements(appID, userID string, skuIDs *[]string, beforeID, afterID string, limit int, guildID string, excludeEnded bool, options ...RequestOption) (entitlements []*Entitlement, err error) { +// 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 userID != "" { - queryParams.Set("user_id", userID) - } - if skuIDs != nil && len(*skuIDs) > 0 { - queryParams.Set("sku_ids", strings.Join(*skuIDs, ",")) - } - if beforeID != "" { - queryParams.Set("before", beforeID) - } - if afterID != "" { - queryParams.Set("after", afterID) - } - if limit > 0 { - queryParams.Set("limit", strconv.Itoa(limit)) - } - if guildID != "" { - queryParams.Set("guild_id", guildID) - } - if excludeEnded { - queryParams.Set("exclude_ended", "true") + 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...) diff --git a/structs.go b/structs.go index 32def1b4e..6197507e9 100644 --- a/structs.go +++ b/structs.go @@ -2513,6 +2513,30 @@ type EntitlementTest struct { 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 From 2c97651b741cc6f4a402df67b3c047a8a333f1c4 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 5 Oct 2024 14:01:33 +0200 Subject: [PATCH 24/29] add premium button component --- components.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components.go b/components.go index af3d7aba5..7d320780e 100644 --- a/components.go +++ b/components.go @@ -121,6 +121,8 @@ const ( DangerButton ButtonStyle = 4 // LinkButton is a special type of button which navigates to a URL. Has grey color. LinkButton ButtonStyle = 5 + // PremiumButton is a special type of button with a blurple color that links to a SKU. + PremiumButton ButtonStyle = 6 ) // ComponentEmoji represents button emoji, if it does have one. @@ -140,6 +142,8 @@ type Button struct { // NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID. URL string `json:"url,omitempty"` CustomID string `json:"custom_id,omitempty"` + // Identifier for a purchasable SKU. Only available when using premium-style buttons. + SKUID string `json:"sku_id,omitempty"` } // MarshalJSON is a method for marshaling Button to a JSON object. From e9b116e9deb249a55b516145fab154704f21fdc1 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 5 Oct 2024 14:13:10 +0200 Subject: [PATCH 25/29] add subscriptions --- endpoints.go | 10 +++++++- restapi.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++------ structs.go | 45 +++++++++++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/endpoints.go b/endpoints.go index 412bc84f8..3e1a5bd03 100644 --- a/endpoints.go +++ b/endpoints.go @@ -33,6 +33,7 @@ var ( EndpointWebhooks = EndpointAPI + "webhooks/" EndpointStickers = EndpointAPI + "stickers/" EndpointStageInstances = EndpointAPI + "stage-instances" + EndpointSKUs = EndpointAPI + "skus" EndpointCDN = "https://cdn.discordapp.com/" EndpointCDNAttachments = EndpointCDN + "attachments/" @@ -172,7 +173,7 @@ var ( return EndpointPoll(cID, mID) + "/expire" } - EndpointSKUs = func(aID string) string { + EndpointApplicationSKUs = func(aID string) string { return EndpointApplication(aID) + "/skus" } @@ -186,6 +187,13 @@ var ( return EndpointEntitlement(aID, eID) + "/consume" } + EndpointSubscriptions = func(skuID string) string { + return EndpointSKUs + "/" + skuID + "/subscriptions" + } + EndpointSubscription = func(skuID, subID string) string { + return EndpointSubscriptions(skuID) + "/" + subID + } + EndpointApplicationGlobalCommands = func(aID string) string { return EndpointApplication(aID) + "/commands" } diff --git a/restapi.go b/restapi.go index eaecc71b2..3f5a58808 100644 --- a/restapi.go +++ b/restapi.go @@ -3502,13 +3502,13 @@ func (s *Session) PollExpire(channelID, messageID string) (msg *Message, err err } // ---------------------------------------------------------------------- -// Functions specific to SKUs +// Functions specific to monetization // ---------------------------------------------------------------------- // 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) + endpoint := EndpointApplicationSKUs(appID) body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) if err != nil { @@ -3519,11 +3519,7 @@ func (s *Session) SKUs(appID string) (skus []*SKU, err error) { return } -// ---------------------------------------------------------------------- -// Functions specific to entitlements -// ---------------------------------------------------------------------- - -// Entitlements returns all antitlements for a given app, active and expired. +// Entitlements returns all Entitlements 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) { @@ -3584,3 +3580,57 @@ func (s *Session) EntitlementTestDelete(appID, entitlementID string, options ... _, err = s.RequestWithBucketID("DELETE", EndpointEntitlement(appID, entitlementID), nil, EndpointEntitlement(appID, ""), options...) return } + +// Returns all subscriptions containing the SKU. +// skuID : The ID of the SKU. +// userID : User ID for which to return subscriptions. Required except for OAuth queries. +// before : Optional timestamp to retrieve subscriptions before this time. +// after : Optional timestamp to retrieve subscriptions after this time. +// limit : Optional maximum number of subscriptions to return (1-100, default 50). +func (s *Session) Subscriptions(skuID string, userID string, before, after *time.Time, limit int, options ...RequestOption) (subscriptions []*Subscription, err error) { + endpoint := EndpointSubscriptions(skuID) + + queryParams := url.Values{} + if before != nil { + queryParams.Set("before", before.Format(time.RFC3339)) + } + if after != nil { + queryParams.Set("after", after.Format(time.RFC3339)) + } + if userID != "" { + queryParams.Set("user_id", userID) + } + if limit > 0 { + queryParams.Set("limit", strconv.Itoa(limit)) + } + + body, err := s.RequestWithBucketID("GET", endpoint+"?"+queryParams.Encode(), nil, endpoint, options...) + if err != nil { + return + } + + err = unmarshal(body, &subscriptions) + return +} + +// Subscription returns a subscription by its SKU and subscription ID. +// skuID : The ID of the SKU. +// subscriptionID : The ID of the subscription. +// userID : User ID for which to return the subscription. Required except for OAuth queries. +func (s *Session) Subscription(skuID, subscriptionID, userID string, options ...RequestOption) (subscription *Subscription, err error) { + endpoint := EndpointSubscription(skuID, subscriptionID) + + queryParams := url.Values{} + if userID != "" { + // Unlike stated in the documentation, the user_id parameter is required here. + queryParams.Set("user_id", userID) + } + + body, err := s.RequestWithBucketID("GET", endpoint+"?"+queryParams.Encode(), nil, endpoint, options...) + if err != nil { + return + } + + err = unmarshal(body, &subscription) + return +} diff --git a/structs.go b/structs.go index 6197507e9..4aba67a64 100644 --- a/structs.go +++ b/structs.go @@ -2437,6 +2437,45 @@ type SKU struct { Flags SKUFlags `json:"flags"` } +// https://discord.com/developers/docs/resources/subscription#subscription-object +type Subscription struct { + // ID of the subscription + ID string `json:"id"` + + // ID of the user who is subscribed + UserID string `json:"user_id"` + + // List of SKUs subscribed to + SKUIDs []string `json:"sku_ids"` + + // List of entitlements granted for this subscription + EntitlementIDs []string `json:"entitlement_ids"` + + // Start of the current subscription period + CurrentPeriodStart time.Time `json:"current_period_start"` + + // End of the current subscription period + CurrentPeriodEnd time.Time `json:"current_period_end"` + + // Current status of the subscription + Status SubscriptionStatus `json:"status"` + + // When the subscription was canceled. Only present if the subscription has been canceled. + CanceledAt *time.Time `json:"canceled_at,omitempty"` + + // ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. + Country *string `json:"country,omitempty"` +} + +// SubscriptionStatus is the current status of a Subscription Object +type SubscriptionStatus int + +const ( + SubscriptionStatusActive = 0 + SubscriptionStatusEnding = 1 + SubscriptionStatusInactive = 2 +) + // EntitlementType is the type of entitlement (see EntitlementType* consts) // https://discord.com/developers/docs/monetization/entitlements#entitlement-object-entitlement-types type EntitlementType int @@ -2480,7 +2519,7 @@ type Entitlement struct { StartsAt *time.Time `json:"starts_at,omitempty"` // The date at which the entitlement is no longer valid. - // Not present when using test entitlements. + // Not present when using test entitlements or when receiving an ENTITLEMENT_CREATE event. EndsAt *time.Time `json:"ends_at,omitempty"` // The ID of the guild that is granted access to the entitlement's sku. @@ -2490,6 +2529,10 @@ type Entitlement struct { // Whether or not the entitlement has been consumed. // Only available for consumable items. Consumed bool `json:"consumed,omitempty"` + + // The SubscriptionID of the entitlement. + // Not present when using test entitlements. + SubscriptionID *string `json:"subscription_id,omitempty"` } // EntitlementOwnerType is the type of entitlement (see EntitlementOwnerType* consts) From d9d9f4b160c28baa3c49ac524878151f61065d02 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 5 Oct 2024 14:27:40 +0200 Subject: [PATCH 26/29] optimize optional fields in entitlement struct --- restapi.go | 8 ++++---- structs.go | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/restapi.go b/restapi.go index 3f5a58808..3aa89e5e9 100644 --- a/restapi.go +++ b/restapi.go @@ -3533,11 +3533,11 @@ func (s *Session) Entitlements(appID string, filterOptions *EntitlementFilterOpt 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.Before != nil { + queryParams.Set("before", filterOptions.Before.Format(time.RFC3339)) } - if filterOptions.AfterID != "" { - queryParams.Set("after", filterOptions.AfterID) + if filterOptions.After != nil { + queryParams.Set("after", filterOptions.After.Format(time.RFC3339)) } if filterOptions.Limit > 0 { queryParams.Set("limit", strconv.Itoa(filterOptions.Limit)) diff --git a/structs.go b/structs.go index 4aba67a64..0bd16a741 100644 --- a/structs.go +++ b/structs.go @@ -2506,7 +2506,7 @@ type Entitlement struct { // 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"` + UserID *string `json:"user_id,omitempty"` // The type of the entitlement Type EntitlementType `json:"type"` @@ -2524,11 +2524,11 @@ type Entitlement struct { // 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"` + GuildID *string `json:"guild_id,omitempty"` // Whether or not the entitlement has been consumed. // Only available for consumable items. - Consumed bool `json:"consumed,omitempty"` + Consumed *bool `json:"consumed,omitempty"` // The SubscriptionID of the entitlement. // Not present when using test entitlements. @@ -2564,11 +2564,11 @@ type EntitlementFilterOptions struct { // Optional array of SKU IDs to check for. SkuIDs []string - // Optional timestamp (snowflake) to retrieve Entitlements before this time. - BeforeID string + // Optional timestamp to retrieve Entitlements before this time. + Before *time.Time - // Optional timestamp (snowflake) to retrieve Entitlements after this time. - AfterID string + // Optional timestamp to retrieve Entitlements after this time. + After *time.Time // Optional maximum number of entitlements to return (1-100, default 100). Limit int From 621e4ad347d45ef0a57aa829cadd2652d06e2a3b Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 5 Oct 2024 14:32:26 +0200 Subject: [PATCH 27/29] Add documentation --- restapi.go | 2 +- structs.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/restapi.go b/restapi.go index 3aa89e5e9..cc3afc724 100644 --- a/restapi.go +++ b/restapi.go @@ -3581,7 +3581,7 @@ func (s *Session) EntitlementTestDelete(appID, entitlementID string, options ... return } -// Returns all subscriptions containing the SKU. +// Subscriptions returns all subscriptions containing the SKU. // skuID : The ID of the SKU. // userID : User ID for which to return subscriptions. Required except for OAuth queries. // before : Optional timestamp to retrieve subscriptions before this time. diff --git a/structs.go b/structs.go index 0bd16a741..0a1e9b8ec 100644 --- a/structs.go +++ b/structs.go @@ -2437,6 +2437,7 @@ type SKU struct { Flags SKUFlags `json:"flags"` } +// Subscriptions represent a user making recurring payments for at least one SKU over an ongoing period. // https://discord.com/developers/docs/resources/subscription#subscription-object type Subscription struct { // ID of the subscription @@ -2468,8 +2469,10 @@ type Subscription struct { } // SubscriptionStatus is the current status of a Subscription Object +// https://discord.com/developers/docs/resources/subscription#subscription-statuses type SubscriptionStatus int +// Valid SubscriptionStatus values const ( SubscriptionStatusActive = 0 SubscriptionStatusEnding = 1 From f81b6096047d1b361dff9e1caad2fd8362892622 Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 5 Oct 2024 14:34:45 +0200 Subject: [PATCH 28/29] typo --- structs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structs.go b/structs.go index 0a1e9b8ec..ccac21129 100644 --- a/structs.go +++ b/structs.go @@ -2437,7 +2437,7 @@ type SKU struct { Flags SKUFlags `json:"flags"` } -// Subscriptions represent a user making recurring payments for at least one SKU over an ongoing period. +// Subscription represents a user making recurring payments for at least one SKU over an ongoing period. // https://discord.com/developers/docs/resources/subscription#subscription-object type Subscription struct { // ID of the subscription From 347e93263e049d027d5094be172eaf475e89f06c Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 15 Oct 2024 21:15:46 +0200 Subject: [PATCH 29/29] remove unneccessary pointers --- structs.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/structs.go b/structs.go index ccac21129..7555a70e2 100644 --- a/structs.go +++ b/structs.go @@ -2465,7 +2465,7 @@ type Subscription struct { CanceledAt *time.Time `json:"canceled_at,omitempty"` // ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. - Country *string `json:"country,omitempty"` + Country string `json:"country,omitempty"` } // SubscriptionStatus is the current status of a Subscription Object @@ -2509,7 +2509,7 @@ type Entitlement struct { // 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"` + UserID string `json:"user_id,omitempty"` // The type of the entitlement Type EntitlementType `json:"type"` @@ -2527,7 +2527,7 @@ type Entitlement struct { // 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"` + GuildID string `json:"guild_id,omitempty"` // Whether or not the entitlement has been consumed. // Only available for consumable items. @@ -2535,7 +2535,7 @@ type Entitlement struct { // The SubscriptionID of the entitlement. // Not present when using test entitlements. - SubscriptionID *string `json:"subscription_id,omitempty"` + SubscriptionID string `json:"subscription_id,omitempty"` } // EntitlementOwnerType is the type of entitlement (see EntitlementOwnerType* consts)