diff --git a/api/api.go b/api/api.go index f300293..8253beb 100644 --- a/api/api.go +++ b/api/api.go @@ -13,6 +13,7 @@ import ( "go.fifitido.net/twitch/api/charity" "go.fifitido.net/twitch/api/chat" "go.fifitido.net/twitch/api/conduit" + "go.fifitido.net/twitch/api/entitlements" "go.fifitido.net/twitch/api/eventsub" ) @@ -31,6 +32,7 @@ type API struct { Chat *chat.Chat Conduit *conduit.Conduit CCLS *ccls.CCLS + Entitlements *entitlements.Entitlements EventSub *eventsub.EventSub } @@ -51,6 +53,7 @@ func New() *API { Chat: chat.New(client, baseUrl), Conduit: conduit.New(client, baseUrl), CCLS: ccls.New(client, baseUrl), + Entitlements: entitlements.New(client, baseUrl), EventSub: eventsub.New(client, baseUrl), } } diff --git a/api/entitlements/entitlements.go b/api/entitlements/entitlements.go new file mode 100644 index 0000000..6a353b9 --- /dev/null +++ b/api/entitlements/entitlements.go @@ -0,0 +1,18 @@ +package entitlements + +import ( + "net/http" + "net/url" +) + +type Entitlements struct { + client *http.Client + baseUrl *url.URL +} + +func New(client *http.Client, baseUrl *url.URL) *Entitlements { + return &Entitlements{ + client: client, + baseUrl: baseUrl, + } +} diff --git a/api/entitlements/get_drops_entitlements.go b/api/entitlements/get_drops_entitlements.go new file mode 100644 index 0000000..b268401 --- /dev/null +++ b/api/entitlements/get_drops_entitlements.go @@ -0,0 +1,111 @@ +package entitlements + +import ( + "context" + "encoding/json" + "net/http" + "net/url" + "time" + + "github.com/google/go-querystring/query" + "go.fifitido.net/twitch/api/types" +) + +type GetDropsEntitlementsParams struct { + // An ID that identifies the entitlement to get. + // Include this parameter for each entitlement you want to get. + // For example, id=1234&id=5678. + // You may specify a maximum of 100 IDs. + IDs []string `url:"id,omitempty"` + + // An ID that identifies a user that was granted entitlements. + UserID *string `url:"user_id,omitempty"` + + // An ID that identifies a game that offered entitlements. + GameID *string `url:"game_id,omitempty"` + + // The entitlement’s fulfillment status. Used to filter the list to only those with the specified status. + FulfillmentStatus *FulfillmentStatus `url:"fulfillment_status,omitempty"` + + // The cursor used to get the next page of results. + // The Pagination object in the response contains the cursor’s value. + // Read More: https://dev.twitch.tv/docs/api/guide#pagination + Cursor *string `url:"cursor,omitempty"` + + // The maximum number of entitlements to return per page in the response. + // The minimum page size is 1 entitlement per page and the maximum is 1000. + // The default is 20. + First *int `url:"first,omitempty"` +} + +type GetDropsEntitlementsResponse struct { + // The list of entitlements. + Data []Entitlement `json:"data"` + + // The information used to page through the list of results. + // The object is empty if there are no more pages left to page through. + // Read More: https://dev.twitch.tv/docs/api/guide#pagination + Pagination *types.Pagination `json:"pagination"` +} + +type Entitlement struct { + // An ID that identifies the entitlement. + ID string `json:"id"` + + // An ID that identifies the benefit (reward). + BenefitID string `json:"benefit_id"` + + // The UTC date and time (in RFC3339 format) of when the entitlement was granted. + Timestamp time.Time `json:"timestamp"` + + // An ID that identifies the user who was granted the entitlement. + UserID string `json:"user_id"` + + // An ID that identifies the game the user was playing when the reward was entitled. + GameID string `json:"game_id"` + + // The entitlement’s fulfillment status. + FulfillmentStatus FulfillmentStatus `json:"fulfillment_status"` + + // The UTC date and time (in RFC3339 format) of when the entitlement was last updated. + LastUpdated time.Time `json:"last_updated"` +} + +// Gets an organization’s list of entitlements that have been granted to a game, a user, or both. +// +// NOTE: Entitlements returned in the response body data are not guaranteed to be sorted by any field returned by the API. To retrieve CLAIMED or FULFILLED entitlements, use the fulfillment_status query parameter to filter results. To retrieve entitlements for a specific game, use the game_id query parameter to filter results. +// +// The following table identifies the request parameters that you may specify based on the type of access token used. +// Access token type | Parameter | Description +// ------------------|------------------|--------------------------------------------------------------------------------------------------------------------- +// App | None | If you don’t specify request parameters, the request returns all entitlements that your organization owns. +// App | user_id | The request returns all entitlements for any game that the organization granted to the specified user. +// App | user_id, game_id | The request returns all entitlements that the specified game granted to the specified user. +// App | game_id | The request returns all entitlements that the specified game granted to all entitled users. +// User | None | If you don’t specify request parameters, the request returns all entitlements for any game that the organization granted to the user identified in the access token. +// User | user_id | Invalid. +// User | user_id, game_id | Invalid. +// User | game_id | The request returns all entitlements that the specified game granted to the user identified in the access token. +// +// Requires an app access token or user access token. The client ID in the access token must own the game. +func (c *Entitlements) GetDropsEntitlements(ctx context.Context, params *GetDropsEntitlementsParams) (*GetDropsEntitlementsResponse, error) { + v, _ := query.Values(params) + endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "entitlements/drops", RawQuery: v.Encode()}) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) + if err != nil { + return nil, err + } + + res, err := c.client.Do(req) + if err != nil { + return nil, err + } + + var data GetDropsEntitlementsResponse + if err := json.NewDecoder(res.Body).Decode(&data); err != nil { + return nil, err + } + + return &data, nil +} diff --git a/api/entitlements/models.go b/api/entitlements/models.go new file mode 100644 index 0000000..8d09342 --- /dev/null +++ b/api/entitlements/models.go @@ -0,0 +1,18 @@ +package entitlements + +type FulfillmentStatus string + +const ( + FulfillmentStatusClaimed FulfillmentStatus = "CLAIMED" + FulfillmentStatusFulfilled FulfillmentStatus = "FULFILLED" +) + +type UpdateStatus string + +const ( + UpdateStatusInvalidID UpdateStatus = "INVALID_ID" + UpdateStatusNotFound UpdateStatus = "NOT_FOUND" + UpdateStatusSuccess UpdateStatus = "SUCCESS" + UpdateStatusUnauthorized UpdateStatus = "UNAUTHORIZED" + UpdateStatusUpdateFailed UpdateStatus = "UPDATE_FAILED" +) diff --git a/api/entitlements/update_drops_entitlements.go b/api/entitlements/update_drops_entitlements.go new file mode 100644 index 0000000..844b4dc --- /dev/null +++ b/api/entitlements/update_drops_entitlements.go @@ -0,0 +1,70 @@ +package entitlements + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/url" +) + +type UpdateDropsEntitlementsRequest struct { + // A list of IDs that identify the entitlements to update. You may specify a maximum of 100 IDs. + EntitlementIDs *[]string `json:"entitlement_ids,omitempty"` + + // The fulfillment status to set the entitlements to. + FulfillmentStatus *FulfillmentStatus `json:"fulfillment_status,omitempty"` +} + +type UpdateDropsEntitlementsResponse struct { + // A list that indicates which entitlements were successfully updated and those that weren’t. + Data []UpdateDropsEntitlementsData `json:"data"` +} + +type UpdateDropsEntitlementsData struct { + // A string that indicates whether the status of the entitlements in the ids field were successfully updated. + Status UpdateStatus `json:"status"` + + // The list of entitlements that the status in the status field applies to. + IDs []string `json:"ids"` +} + +// Updates the Drop entitlement’s fulfillment status. +// +// The following table identifies which entitlements are updated based on the type of access token used. +// Access token type | Data that’s updated +// ------------------|------------------------------------------------------------------------------------------------------------------------------------------ +// App | Updates all entitlements with benefits owned by the organization in the access token. +// User | Updates all entitlements owned by the user in the access token and where the benefits are owned by the organization in the access token. +// +// Requires an app access token or user access token. The client ID in the access token must own the game. +func (c *Entitlements) UpdateDropsEntitlements(ctx context.Context, request *UpdateDropsEntitlementsRequest) (*UpdateDropsEntitlementsResponse, error) { + endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "entitlements/drops"}) + + r, w := io.Pipe() + + go func() { + if err := json.NewEncoder(w).Encode(request); err != nil { + w.CloseWithError(err) + } else { + w.Close() + } + }() + + req, err := http.NewRequestWithContext(ctx, http.MethodPatch, endpoint.String(), r) + if err != nil { + return nil, err + } + + res, err := c.client.Do(req) + if err != nil { + return nil, err + } + + var data UpdateDropsEntitlementsResponse + if err := json.NewDecoder(res.Body).Decode(&data); err != nil { + return nil, err + } + + return &data, nil +} diff --git a/ptr_types.go b/ptr_types.go index b879531..db99898 100644 --- a/ptr_types.go +++ b/ptr_types.go @@ -6,6 +6,7 @@ import ( "go.fifitido.net/twitch/api/bits" "go.fifitido.net/twitch/api/channelpoints" "go.fifitido.net/twitch/api/conduit" + "go.fifitido.net/twitch/api/entitlements" "go.fifitido.net/twitch/api/types" ) @@ -184,3 +185,14 @@ func ToLocale(s *types.Locale) types.Locale { } return *s } + +func FulfillmentStatus(s entitlements.FulfillmentStatus) *entitlements.FulfillmentStatus { + return &s +} + +func ToFulfillmentStatus(s *entitlements.FulfillmentStatus) entitlements.FulfillmentStatus { + if s == nil { + return "" + } + return *s +}