Add context to all implemented endpoints

This commit is contained in:
Evan Fiordeliso 2024-02-27 23:03:40 -05:00
parent 4b44064fe5
commit 616cb935a0
29 changed files with 534 additions and 244 deletions

View File

@ -1,7 +1,9 @@
package ads package ads
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
) )
@ -36,18 +38,23 @@ type GetAdScheduleData struct {
// //
// Requires a user access token that includes the channel:read:ads scope. // Requires a user access token that includes the channel:read:ads scope.
// The user_id in the user access token must match the broadcaster_id. // The user_id in the user access token must match the broadcaster_id.
func (e *Ads) GetAdSchedule(broadcasterID string) (*GetAdScheduleResponse, error) { func (e *Ads) GetAdSchedule(ctx context.Context, broadcasterID string) (*GetAdScheduleResponse, error) {
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/ads", RawQuery: "broadcaster_id=" + broadcasterID}) endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/ads", RawQuery: "broadcaster_id=" + broadcasterID})
resp, err := e.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := e.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetAdScheduleResponse var data GetAdScheduleResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package ads package ads
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
) )
@ -27,18 +29,23 @@ type SnoozeNextAdData struct {
// //
// Requires a user access token that includes the channel:manage:ads scope. // Requires a user access token that includes the channel:manage:ads scope.
// The user_id in the user access token must match the broadcaster_id. // The user_id in the user access token must match the broadcaster_id.
func (e *Ads) SnoozeNextAd(broadcasterID string) (*SnoozeNextAdResponse, error) { func (e *Ads) SnoozeNextAd(ctx context.Context, broadcasterID string) (*SnoozeNextAdResponse, error) {
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/ads/schedule/snooze", RawQuery: "broadcaster_id=" + broadcasterID}) endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/ads/schedule/snooze", RawQuery: "broadcaster_id=" + broadcasterID})
resp, err := e.client.Post(endpoint.String(), "application/json", nil) req, err := http.NewRequest(http.MethodPost, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := e.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data SnoozeNextAdResponse var data SnoozeNextAdResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,8 +1,10 @@
package ads package ads
import ( import (
"context"
"encoding/json" "encoding/json"
"io" "io"
"net/http"
"net/url" "net/url"
) )
@ -39,28 +41,33 @@ type StartCommercialData struct {
// NOTE: Only the broadcaster may start a commercial; the broadcasters editors and moderators may not start commercials on behalf of the broadcaster. // NOTE: Only the broadcaster may start a commercial; the broadcasters editors and moderators may not start commercials on behalf of the broadcaster.
// //
// Requires a user access token that includes the channel:edit:commercial scope. // Requires a user access token that includes the channel:edit:commercial scope.
func (e *Ads) StartCommercial(req *StartCommercialRequest) (*StartCommercialResponse, error) { func (e *Ads) StartCommercial(ctx context.Context, body *StartCommercialRequest) (*StartCommercialResponse, error) {
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/commercial"}) endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/commercial"})
r, w := io.Pipe() r, w := io.Pipe()
go func() { go func() {
if err := json.NewEncoder(w).Encode(req); err != nil { if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err) w.CloseWithError(err)
} else { } else {
w.Close() w.Close()
} }
}() }()
resp, err := e.client.Post(endpoint.String(), "application/json", r) req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := e.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data StartCommercialResponse var data StartCommercialResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package analytics package analytics
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
@ -78,19 +80,24 @@ type ExtensionAnalyticsReport struct {
// Learn More: https://dev.twitch.tv/docs/insights // Learn More: https://dev.twitch.tv/docs/insights
// //
// Requires a user access token that includes the analytics:read:extensions scope. // Requires a user access token that includes the analytics:read:extensions scope.
func (e *Analytics) GetExtensionAnalytics(params GetExtensionAnalyticsParams) (*GetExtensionAnalyticsResponse, error) { func (e *Analytics) GetExtensionAnalytics(ctx context.Context, params GetExtensionAnalyticsParams) (*GetExtensionAnalyticsResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "analytics/extensions", RawQuery: v.Encode()}) endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "analytics/extensions", RawQuery: v.Encode()})
resp, err := e.client.Get(endpoint.String()) req, err := http.NewRequest(http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := e.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetExtensionAnalyticsResponse var data GetExtensionAnalyticsResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package analytics package analytics
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
@ -79,19 +81,24 @@ type GameAnalyticsReport struct {
// Learn more: https://dev.twitch.tv/docs/insights // Learn more: https://dev.twitch.tv/docs/insights
// //
// Requires a user access token that includes the analytics:read:games scope. // Requires a user access token that includes the analytics:read:games scope.
func (e *Analytics) GetGameAnalytics(params GetGameAnalyticsParams) (*GetGameAnalyticsResponse, error) { func (e *Analytics) GetGameAnalytics(ctx context.Context, params GetGameAnalyticsParams) (*GetGameAnalyticsResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "analytics/games", RawQuery: v.Encode()}) endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "analytics/games", RawQuery: v.Encode()})
resp, err := e.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := e.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetGameAnalyticsResponse var data GetGameAnalyticsResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package bits package bits
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
@ -66,19 +68,24 @@ type LeaderboardEntry struct {
// Gets the Bits leaderboard for the authenticated broadcaster. // Gets the Bits leaderboard for the authenticated broadcaster.
// //
// Requires a user access token that includes the bits:read scope. // Requires a user access token that includes the bits:read scope.
func (b *Bits) GetBitsLeaderboard(params *GetBitsLeaderboardParams) (*GetBitsLeaderboardResponse, error) { func (b *Bits) GetBitsLeaderboard(ctx context.Context, params *GetBitsLeaderboardParams) (*GetBitsLeaderboardResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "bits/leaderboard", RawQuery: v.Encode()}) endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "bits/leaderboard", RawQuery: v.Encode()})
resp, err := b.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := b.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetBitsLeaderboardResponse var data GetBitsLeaderboardResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package bits package bits
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
) )
@ -79,18 +81,23 @@ type CheermoteImageSizes struct {
// Gets a list of Cheermotes that users can use to cheer Bits in any Bits-enabled channels chat room. Cheermotes are animated emotes that viewers can assign Bits to. // Gets a list of Cheermotes that users can use to cheer Bits in any Bits-enabled channels chat room. Cheermotes are animated emotes that viewers can assign Bits to.
// //
// Requires an app access token or user access token. // Requires an app access token or user access token.
func (b *Bits) GetCheermotes(broadcasterID string) (*GetCheermotesResponse, error) { func (b *Bits) GetCheermotes(ctx context.Context, broadcasterID string) (*GetCheermotesResponse, error) {
endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "bits/cheermotes", RawQuery: "broadcaster_id=" + broadcasterID}) endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "bits/cheermotes", RawQuery: "broadcaster_id=" + broadcasterID})
resp, err := b.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := b.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetCheermotesResponse var data GetCheermotesResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package bits package bits
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
@ -93,19 +95,24 @@ type ProductDataCost struct {
// Gets an extensions list of transactions. A transaction records the exchange of a currency (for example, Bits) for a digital product. // Gets an extensions list of transactions. A transaction records the exchange of a currency (for example, Bits) for a digital product.
// //
// Requires an app access token. // Requires an app access token.
func (b *Bits) GetExtensionTransactions(params *GetExtensionTransactionsParams) (*GetExtensionTransactionsResponse, error) { func (b *Bits) GetExtensionTransactions(ctx context.Context, params *GetExtensionTransactionsParams) (*GetExtensionTransactionsResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "extensions/transactions", RawQuery: v.Encode()}) endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "extensions/transactions", RawQuery: v.Encode()})
resp, err := b.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := b.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetExtensionTransactionsResponse var data GetExtensionTransactionsResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,8 +1,10 @@
package channelpoints package channelpoints
import ( import (
"context"
"encoding/json" "encoding/json"
"io" "io"
"net/http"
"net/url" "net/url"
) )
@ -67,28 +69,33 @@ type CreateCustomRewardsResponse struct {
// Creates a Custom Reward in the broadcasters channel. The maximum number of custom rewards per channel is 50, which includes both enabled and disabled rewards. // Creates a Custom Reward in the broadcasters channel. The maximum number of custom rewards per channel is 50, which includes both enabled and disabled rewards.
// //
// Requires a user access token that includes the channel:manage:redemptions scope. // Requires a user access token that includes the channel:manage:redemptions scope.
func (c *ChannelPoints) CreateCustomRewards(broadcastID string, req *CreateCustomRewardsRequest) (*CreateCustomRewardsResponse, error) { func (c *ChannelPoints) CreateCustomRewards(ctx context.Context, broadcastID string, body *CreateCustomRewardsRequest) (*CreateCustomRewardsResponse, error) {
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: "broadcaster_id=" + broadcastID}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: "broadcaster_id=" + broadcastID})
r, w := io.Pipe() r, w := io.Pipe()
go func() { go func() {
if err := json.NewEncoder(w).Encode(req); err != nil { if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err) w.CloseWithError(err)
} else { } else {
w.Close() w.Close()
} }
}() }()
resp, err := c.client.Post(endpoint.String(), "application/json", r) req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data CreateCustomRewardsResponse var data CreateCustomRewardsResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,7 @@
package channelpoints package channelpoints
import ( import (
"context"
"net/http" "net/http"
"net/url" "net/url"
@ -20,20 +21,22 @@ type DeleteCustomRewardParams struct {
// The app used to create the reward is the only app that may delete it. // The app used to create the reward is the only app that may delete it.
// If the rewards redemption status is UNFULFILLED at the time the reward is deleted, its redemption status is marked as FULFILLED. // If the rewards redemption status is UNFULFILLED at the time the reward is deleted, its redemption status is marked as FULFILLED.
// //
// / Requires a user access token that includes the channel:manage:redemptions scope. // Requires a user access token that includes the channel:manage:redemptions scope.
func (c *ChannelPoints) DeleteCustomReward(params *DeleteCustomRewardParams) error { func (c *ChannelPoints) DeleteCustomReward(ctx context.Context, params *DeleteCustomRewardParams) error {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()})
resp, err := c.client.Do(&http.Request{ req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil)
Method: http.MethodDelete,
URL: endpoint,
})
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil return nil
} }

View File

@ -1,7 +1,9 @@
package channelpoints package channelpoints
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
@ -33,19 +35,24 @@ type GetCustomRewardResponse struct {
// NOTE: A channel may offer a maximum of 50 rewards, which includes both enabled and disabled rewards. // NOTE: A channel may offer a maximum of 50 rewards, which includes both enabled and disabled rewards.
// //
// Requires a user access token that includes the channel:read:redemptions or channel:manage:redemptions scope. // Requires a user access token that includes the channel:read:redemptions or channel:manage:redemptions scope.
func (c *ChannelPoints) GetCustomReward(params *GetCustomRewardParams) (*GetCustomRewardResponse, error) { func (c *ChannelPoints) GetCustomReward(ctx context.Context, params *GetCustomRewardParams) (*GetCustomRewardResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()})
resp, err := c.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetCustomRewardResponse var data GetCustomRewardResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package channelpoints package channelpoints
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
@ -49,19 +51,24 @@ type GetCustomRewardRedemptionResponse struct {
// Gets a list of redemptions for the specified custom reward. The app used to create the reward is the only app that may get the redemptions. // Gets a list of redemptions for the specified custom reward. The app used to create the reward is the only app that may get the redemptions.
// //
// Requires a user access token that includes the channel:read:redemptions or channel:manage:redemptions scope. // Requires a user access token that includes the channel:read:redemptions or channel:manage:redemptions scope.
func (c *ChannelPoints) GetCustomRewardRedemption(params *GetCustomRewardRedemptionParams) (*GetCustomRewardRedemptionResponse, error) { func (c *ChannelPoints) GetCustomRewardRedemption(ctx context.Context, params *GetCustomRewardRedemptionParams) (*GetCustomRewardRedemptionResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards/redemptions", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards/redemptions", RawQuery: v.Encode()})
resp, err := c.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetCustomRewardRedemptionResponse var data GetCustomRewardRedemptionResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,7 @@
package channelpoints package channelpoints
import ( import (
"context"
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
@ -79,33 +80,34 @@ type UpdateCustomRewardResponse struct {
// Updates a custom reward. The app used to create the reward is the only app that may update the reward. // Updates a custom reward. The app used to create the reward is the only app that may update the reward.
// //
// Requires a user access token that includes the channel:manage:redemptions scope. // Requires a user access token that includes the channel:manage:redemptions scope.
func (c *ChannelPoints) UpdateCustomReward(params *UpdateCustomRewardParams, req *UpdateCustomRewardRequest) (*UpdateCustomRewardResponse, error) { func (c *ChannelPoints) UpdateCustomReward(ctx context.Context, params *UpdateCustomRewardParams, body *UpdateCustomRewardRequest) (*UpdateCustomRewardResponse, error) {
v, _ := query.Values(req) v, _ := query.Values(body)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()})
r, w := io.Pipe() r, w := io.Pipe()
go func() { go func() {
if err := json.NewEncoder(w).Encode(req); err != nil { if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err) w.CloseWithError(err)
} else { } else {
w.Close() w.Close()
} }
}() }()
resp, err := c.client.Do(&http.Request{ req, err := http.NewRequestWithContext(ctx, http.MethodPatch, endpoint.String(), r)
Method: http.MethodPatch,
URL: endpoint,
Body: r,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data UpdateCustomRewardResponse var data UpdateCustomRewardResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,7 @@
package channelpoints package channelpoints
import ( import (
"context"
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
@ -39,33 +40,34 @@ type UpdateRedemptionStatusResponse struct {
// The app used to create the reward is the only app that may update the redemption. // The app used to create the reward is the only app that may update the redemption.
// //
// Requires a user access token that includes the channel:manage:redemptions scope. // Requires a user access token that includes the channel:manage:redemptions scope.
func (c *ChannelPoints) UpdateRedemptionStatus(params *UpdateRedemptionStatusParams, req *UpdateRedemptionStatusRequest) (*UpdateRedemptionStatusResponse, error) { func (c *ChannelPoints) UpdateRedemptionStatus(ctx context.Context, params *UpdateRedemptionStatusParams, body *UpdateRedemptionStatusRequest) (*UpdateRedemptionStatusResponse, error) {
v, _ := query.Values(req) v, _ := query.Values(body)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards/redemptions", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards/redemptions", RawQuery: v.Encode()})
r, w := io.Pipe() r, w := io.Pipe()
go func() { go func() {
if err := json.NewEncoder(w).Encode(req); err != nil { if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err) w.CloseWithError(err)
} else { } else {
w.Close() w.Close()
} }
}() }()
resp, err := c.client.Do(&http.Request{ req, err := http.NewRequestWithContext(ctx, http.MethodPatch, endpoint.String(), r)
Method: http.MethodPatch,
URL: endpoint,
Body: r,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data UpdateRedemptionStatusResponse var data UpdateRedemptionStatusResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package channels package channels
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
) )
@ -25,18 +27,23 @@ type ChannelEditor struct {
// Gets the broadcasters list editors. // Gets the broadcasters list editors.
// //
// Requires a user access token that includes the channel:read:editors scope. // Requires a user access token that includes the channel:read:editors scope.
func (c *Channels) GetChannelEditors(broadcasterID string) (*GetChannelEditorsResponse, error) { func (c *Channels) GetChannelEditors(ctx context.Context, broadcasterID string) (*GetChannelEditorsResponse, error) {
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels/editors", RawQuery: "broadcaster_id=" + broadcasterID}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels/editors", RawQuery: "broadcaster_id=" + broadcasterID})
resp, err := c.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetChannelEditorsResponse var data GetChannelEditorsResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package channels package channels
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
@ -67,19 +69,24 @@ type ChannelFollower struct {
// This endpoint will return specific follower information only if both of the above are true. // This endpoint will return specific follower information only if both of the above are true.
// If a scope is not provided or the user isnt the broadcaster or a moderator for the specified channel, // If a scope is not provided or the user isnt the broadcaster or a moderator for the specified channel,
// only the total follower count will be included in the response. // only the total follower count will be included in the response.
func (c *Channels) GetChannelFollowers(params *GetChannelFollowersParams) (*GetChannelFollowersResponse, error) { func (c *Channels) GetChannelFollowers(ctx context.Context, params *GetChannelFollowersParams) (*GetChannelFollowersResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels/followers", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels/followers", RawQuery: v.Encode()})
resp, err := c.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetChannelFollowersResponse var data GetChannelFollowersResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package channels package channels
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
@ -66,19 +68,24 @@ type ChannelInformation struct {
// Gets information about one or more channels. // Gets information about one or more channels.
// //
// Requires an app access token or user access token. // Requires an app access token or user access token.
func (c *Channels) GetChannelInformation(params *GetChannelInformationParams) (*GetChannelInformdationResponse, error) { func (c *Channels) GetChannelInformation(ctx context.Context, params *GetChannelInformationParams) (*GetChannelInformdationResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels", RawQuery: v.Encode()})
resp, err := c.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetChannelInformdationResponse var data GetChannelInformdationResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package channels package channels
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"time" "time"
@ -58,19 +60,24 @@ type FollowedChannel struct {
// Gets a list of broadcasters that the specified user follows. You can also use this endpoint to see whether a user follows a specific broadcaster. // Gets a list of broadcasters that the specified user follows. You can also use this endpoint to see whether a user follows a specific broadcaster.
// //
// Requires a user access token that includes the user:read:follows scope. // Requires a user access token that includes the user:read:follows scope.
func (c *Channels) GetFollowedChannels(params *GetFollowedChannelsParams) (*GetFollowedChannelsResponse, error) { func (c *Channels) GetFollowedChannels(ctx context.Context, params *GetFollowedChannelsParams) (*GetFollowedChannelsResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "users/follows", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "users/follows", RawQuery: v.Encode()})
resp, err := c.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetFollowedChannelsResponse var data GetFollowedChannelsResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,7 @@
package channels package channels
import ( import (
"context"
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
@ -52,24 +53,27 @@ type ModifyContentClassificationLabel struct {
// Updates a channels properties. // Updates a channels properties.
// //
// Requires a user access token that includes the channel:manage:broadcast scope. // Requires a user access token that includes the channel:manage:broadcast scope.
func (c *Channels) ModifyChannelInformation(broadcasterID string, req *ModifyChannelInformationRequest) error { func (c *Channels) ModifyChannelInformation(ctx context.Context, broadcasterID string, body *ModifyChannelInformationRequest) error {
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels", RawQuery: "broadcaster_id=" + broadcasterID}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels", RawQuery: "broadcaster_id=" + broadcasterID})
r, w := io.Pipe() r, w := io.Pipe()
go func() { go func() {
if err := json.NewEncoder(w).Encode(req); err != nil { if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err) w.CloseWithError(err)
} else { } else {
w.Close() w.Close()
} }
}() }()
_, err := c.client.Do(&http.Request{ req, err := http.NewRequestWithContext(ctx, http.MethodPatch, endpoint.String(), r)
Method: http.MethodPatch, if err != nil {
URL: endpoint, return err
Body: r, }
})
return err if _, err := c.client.Do(req); err != nil {
return err
}
return nil
} }

View File

@ -1,7 +1,9 @@
package charity package charity
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"go.fifitido.net/twitch/api/types" "go.fifitido.net/twitch/api/types"
@ -52,18 +54,23 @@ type CharityCampaign struct {
// subscribe to the channel.charity_campaign.progress subscription type. // subscribe to the channel.charity_campaign.progress subscription type.
// //
// Requires a user access token that includes the channel:read:charity scope. // Requires a user access token that includes the channel:read:charity scope.
func (c *Charity) GetCharityCampaign(broadcasterID string) (*GetCharityCampaignResponse, error) { func (c *Charity) GetCharityCampaign(ctx context.Context, broadcasterID string) (*GetCharityCampaignResponse, error) {
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "charity/campaigns", RawQuery: "broadcaster_id=" + broadcasterID}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "charity/campaigns", RawQuery: "broadcaster_id=" + broadcasterID})
resp, err := c.client.Get(endpoint.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetCharityCampaignResponse var data GetCharityCampaignResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,9 @@
package charity package charity
import ( import (
"context"
"encoding/json" "encoding/json"
"net/http"
"net/url" "net/url"
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
@ -56,17 +58,22 @@ type CharityCampaignDonation struct {
// To receive events as donations occur, subscribe to the channel.charity_campaign.donate subscription type. // To receive events as donations occur, subscribe to the channel.charity_campaign.donate subscription type.
// //
// Requires a user access token that includes the channel:read:charity scope. // Requires a user access token that includes the channel:read:charity scope.
func (c *Charity) GetCharityCampaignDonations(params *GetCharityCampaignDonationsParams) (*GetCharityCampaignDonationsResponse, error) { func (c *Charity) GetCharityCampaignDonations(ctx context.Context, params *GetCharityCampaignDonationsParams) (*GetCharityCampaignDonationsResponse, error) {
v, _ := query.Values(params) v, _ := query.Values(params)
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "charity/campaigns/donations", RawQuery: v.Encode()}) endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "charity/campaigns/donations", RawQuery: v.Encode()})
resp, err := c.client.Get(endpoint.String()) 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 { if err != nil {
return nil, err return nil, err
} }
var respBody GetCharityCampaignDonationsResponse var respBody GetCharityCampaignDonationsResponse
if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil { if err := json.NewDecoder(res.Body).Decode(&respBody); err != nil {
return nil, err return nil, err
} }

View File

@ -0,0 +1,83 @@
package eventsub
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
)
type CreateEventSubSubscriptionRequest struct {
// The type of subscription to create.
SubscriptionType
// A JSON object that contains the parameter values that are specific to the specified subscription type.
//For the objects required and optional fields, see the subscription types documentation.
Condition Condition `json:"condition"`
// The transport details that you want Twitch to use when sending you notifications.
Transport *Transport `json:"transport"`
}
type CreateEventSubSubscriptionResponse struct {
// A list that contains the single subscription that you created.
Data []*Subscription `json:"data"`
// The total number of subscriptions youve created.
Total int `json:"total"`
// The sum of all of your subscription costs. Learn More: https://dev.twitch.tv/docs/eventsub/manage-subscriptions/#subscription-limits
TotalCost int `json:"total_cost"`
// The maximum total cost that youre allowed to incur for all subscriptions you create.
MaxTotalCost int `json:"max_total_cost"`
}
// Creates an EventSub subscription.
//
// If you use webhooks to receive events, the request must specify an app access token.
// The request will fail if you use a user access token. If the subscription type requires user authorization,
// the user must have granted your app (client ID) permissions to receive those events before you subscribe to them.
// For example, to subscribe to channel.subscribe events, your app must get a user access token that includes the
// channel:read:subscriptions scope, which adds the required permission to your app access tokens client ID.
//
// If you use WebSockets to receive events, the request must specify a user access token.
// The request will fail if you use an app access token. If the subscription type requires user authorization,
// the token must include the required scope. However, if the subscription type doesnt include user authorization,
// the token may include any scopes or no scopes.
//
// If you use Conduits to receive events, the request must specify an app access token.
// The request will fail if you use a user access token.
func (e *EventSub) CreateEventSubSubscription(ctx context.Context, body *CreateEventSubSubscriptionRequest) (*CreateEventSubSubscriptionResponse, error) {
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "eventsub/subscriptions"})
r, w := io.Pipe()
go func() {
if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err)
} else {
w.Close()
}
}()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), r)
if err != nil {
return nil, err
}
res, err := e.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data CreateEventSubSubscriptionResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -1,48 +0,0 @@
package eventsub
import (
"encoding/json"
"io"
"net/url"
)
type CreateSubscriptionRequest struct {
SubscriptionType
Condition Condition `json:"condition"`
Transport *Transport `json:"transport"`
}
type CreateSubscriptionResponse struct {
Data []*Subscription `json:"data"`
Total int `json:"total"`
TotalCost int `json:"total_cost"`
MaxTotalCost int `json:"max_total_cost"`
}
func (e *EventSub) CreateSubscription(req *CreateSubscriptionRequest) (*CreateSubscriptionResponse, error) {
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "eventsub/subscriptions"})
r, w := io.Pipe()
go func() {
if err := json.NewEncoder(w).Encode(req); err != nil {
w.CloseWithError(err)
} else {
w.Close()
}
}()
resp, err := e.client.Post(endpoint.String(), "application/json", r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var data CreateSubscriptionResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,29 @@
package eventsub
import (
"context"
"net/http"
"net/url"
)
// Deletes an EventSub subscription.
//
// If you use webhooks to receive events, the request must specify an app access token.
// The request will fail if you use a user access token.
//
// If you use WebSockets to receive events, the request must specify a user access token.
// The request will fail if you use an app access token. The token may include any scopes.
func (e *EventSub) DeleteEventSubSubscription(ctx context.Context, id string) error {
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "eventsub/subscriptions", RawQuery: "id=" + id})
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil)
if err != nil {
return err
}
if _, err := e.client.Do(req); err != nil {
return err
}
return nil
}

View File

@ -1,20 +0,0 @@
package eventsub
import (
"net/http"
"net/url"
)
func (e *EventSub) DeleteSubscription(id string) error {
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "eventsub/subscriptions", RawQuery: "id=" + id})
_, err := e.client.Do(&http.Request{
Method: http.MethodDelete,
URL: endpoint,
})
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,80 @@
package eventsub
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
"go.fifitido.net/twitch/api/types"
)
type GetEventSubSubscriptionsParams struct {
// Filter subscriptions by its status.
Status *Status `url:"status,omitempty"`
// Filter subscriptions by subscription type. For a list of subscription types.
// See Subscription Types: https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#subscription-types
Type *SubscriptionType `url:"type,omitempty"`
// Filter subscriptions by user ID.
// The response contains subscriptions where this ID matches a user ID that you specified in the Condition object when you created the subscription.
UserID *string `url:"user_id,omitempty"`
// The cursor used to get the next page of results.
// The pagination object in the response contains the cursor's value.
After *types.Cursor `url:"after,omitempty"`
}
type GetEventSubSubscriptionsResponse struct {
// The list of subscriptions. The list is ordered by the oldest subscription first.
// The list is empty if the client hasn't created subscriptions or there are no subscriptions that match the specified filter criteria.
Data []Subscription `json:"data"`
// The total number of subscriptions that you've created.
Total int `json:"total"`
// The sum of all of your subscription costs.
// Learn More: https://dev.twitch.tv/docs/eventsub/manage-subscriptions/#subscription-limits
TotalCost int `json:"total_cost"`
// The maximum total cost that you're allowed to incur for all subscriptions that you create.
MaxTotalCost int `json:"max_total_cost"`
// An object that contains the cursor used to get the next page of subscriptions.
// The object is empty if there are no more pages to get.
// The number of subscriptions returned per page is undertermined.
Pagination types.Pagination `json:"pagination"`
}
// Gets a list of EventSub subscriptions that the client in the access token created.
//
// If you use webhooks to receive events, the request must specify an app access token.
// The request will fail if you use a user access token.
//
// If you use WebSockets to receive events, the request must specify a user access token.
// The request will fail if you use an app access token. The token may include any scopes.
func (e *EventSub) GetEventSubSubscriptions(ctx context.Context, params *GetEventSubSubscriptionsParams) (*GetEventSubSubscriptionsResponse, error) {
v, _ := query.Values(params)
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "eventsub/subscriptions", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := e.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetEventSubSubscriptionsResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -1,43 +0,0 @@
package eventsub
import (
"encoding/json"
"net/url"
"github.com/google/go-querystring/query"
"go.fifitido.net/twitch/api/types"
)
type GetSubscriptionsParams struct {
Status *Status `url:"status,omitempty"`
Type *SubscriptionType `url:"type,omitempty"`
UserID *string `url:"user_id,omitempty"`
After *types.Cursor `url:"after,omitempty"`
}
type GetSubscriptionsResponse struct {
Data []Subscription `json:"data"`
Total int `json:"total"`
TotalCost int `json:"total_cost"`
MaxTotalCost int `json:"max_total_cost"`
Pagination types.Pagination `json:"pagination"`
}
func (e *EventSub) GetSubscriptions(params *GetSubscriptionsParams) (*GetSubscriptionsResponse, error) {
v, _ := query.Values(params)
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "eventsub/subscriptions", RawQuery: v.Encode()})
resp, err := e.client.Get(endpoint.String())
if err != nil {
return nil, err
}
defer resp.Body.Close()
var data GetSubscriptionsResponse
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -5,42 +5,116 @@ import "time"
type Condition map[string]any type Condition map[string]any
type Subscription struct { type Subscription struct {
ID string `json:"id"` // An ID that identifies the subscription.
Status string `json:"status"` ID string `json:"id"`
Type string `json:"type"`
Version string `json:"version"` // The subscriptions status. The subscriber receives events only for enabled subscriptions. Possible values are:
Condition Condition `json:"condition"` //
CreatedAt time.Time `json:"created_at"` // enabled — The subscription is enabled.
//
// webhook_callback_verification_pending — The subscription is pending verification of the specified callback URL (see Responding to a challenge request).
Status string `json:"status"`
// The subscriptions type.
// See Subscription Types: https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#subscription-types
Type string `json:"type"`
// The version number that identifies this definition of the subscriptions data.
Version string `json:"version"`
// The subscriptions parameter values. This is a string-encoded JSON object whose contents are determined by the subscription type.
Condition Condition `json:"condition"`
// The date and time (in RFC3339 format) of when the subscription was created.
CreatedAt time.Time `json:"created_at"`
// The transport details used to send the notifications.
Transport *Transport `json:"transport"` Transport *Transport `json:"transport"`
Cost int `json:"cost"`
// The amount that the subscription counts against your limit.
// Learn More: https://dev.twitch.tv/docs/eventsub/manage-subscriptions/#subscription-limits
Cost int `json:"cost"`
} }
type Status string type Status string
const ( const (
StatusEnabled = "enabled" // enabled — The subscription is enabled.
StatusEnabled = "enabled"
// webhook_callback_verification_pending — The subscription is pending verification of the specified callback URL.
StatusWebhookCallbackVerificationPending = "webhook_callback_verification_pending" StatusWebhookCallbackVerificationPending = "webhook_callback_verification_pending"
StatusWebhookCallbackVerificationFailed = "webhook_callback_verification_failed"
NotificationFailuresExceeded = "notification_failures_exceeded" // webhook_callback_verification_failed — The specified callback URL failed verification.
AuthorizationRevoked = "authorization_revoked" StatusWebhookCallbackVerificationFailed = "webhook_callback_verification_failed"
ModeratorRemoved = "moderator_removed"
USerRemoved = "user_removed" // notification_failures_exceeded — The notification delivery failure rate was too high.
VersionRemoved = "version_removed" NotificationFailuresExceeded = "notification_failures_exceeded"
BetaMaintenance = "beta_maintenance"
WebsocketDisconnected = "websocket_disconnected" // authorization_revoked — The authorization was revoked for one or more users specified in the Condition object.
WebsocketFailedPingPong = "websocket_failed_ping_pong" AuthorizationRevoked = "authorization_revoked"
WebsocketReceivedInboundTraffic = "websocket_received_inbound_traffic"
WebsocketConnectionUnused = "websocket_connection_unused" // moderator_removed — The moderator that authorized the subscription is no longer one of the broadcaster's moderators.
WebsocketInternalError = "websocket_internal_error" ModeratorRemoved = "moderator_removed"
WebsocketNetworkTimeout = "websocket_network_timeout"
WebsocketnetworkError = "websocket_network_error" // user_removed — One of the users specified in the Condition object was removed.
UserRemoved = "user_removed"
// version_removed — The subscription to subscription type and version is no longer supported.
VersionRemoved = "version_removed"
// beta_maintenance — The subscription to the beta subscription type was removed due to maintenance.
BetaMaintenance = "beta_maintenance"
// websocket_disconnected — The client closed the connection.
WebsocketDisconnected = "websocket_disconnected"
// websocket_failed_ping_pong — The client failed to respond to a ping message.
WebsocketFailedPingPong = "websocket_failed_ping_pong"
// websocket_received_inbound_traffic — The client sent a non-pong message.
// Clients may only send pong messages (and only in response to a ping message).
WebsocketReceivedInboundTraffic = "websocket_received_inbound_traffic"
// websocket_connection_unused — The client failed to subscribe to events within the required time.
WebsocketConnectionUnused = "websocket_connection_unused"
// websocket_internal_error — The Twitch WebSocket server experienced an unexpected error.
WebsocketInternalError = "websocket_internal_error"
// websocket_network_timeout — The Twitch WebSocket server timed out writing the message to the client.
WebsocketNetworkTimeout = "websocket_network_timeout"
// websocket_network_error — The Twitch WebSocket server experienced a network error writing the message to the client.
WebsocketnetworkError = "websocket_network_error"
) )
type Transport struct { type Transport struct {
Method string `json:"method"` // The transport method. Possible values are:
Callback *string `json:"callback,omitempty"` //
Secret *string `json:"secret,omitempty"` // webhook, websocket, conduit
Method string `json:"method"`
// The callback URL where the notifications are sent. The URL must use the HTTPS protocol and port 443.
// See Processing an event. Specify this field only if method is set to webhook.
//
// NOTE: Redirects are not followed.
Callback *string `json:"callback,omitempty"`
// The secret used to verify the signature.
// The secret must be an ASCII string thats a minimum of 10 characters long and a maximum of 100 characters long.
// For information about how the secret is used,
// see Verifying the event message: https://dev.twitch.tv/docs/eventsub/handling-webhook-events#verifying-the-event-message
// Specify this field only if method is set to webhook.
Secret *string `json:"secret,omitempty"`
// An ID that identifies the WebSocket to send notifications to. When you connect to EventSub using WebSockets,
// the server returns the ID in the Welcome message. Specify this field only if method is set to websocket.
SessionID *string `json:"session_id,omitempty"` SessionID *string `json:"session_id,omitempty"`
// An ID that identifies the conduit to send notifications to.
// When you create a conduit, the server returns the conduit ID.
// Specify this field only if method is set to conduit.
ConduitID *string `json:"conduit_id,omitempty"` ConduitID *string `json:"conduit_id,omitempty"`
} }
@ -75,7 +149,10 @@ func ConduitTransport(conduitID string) *Transport {
} }
type SubscriptionType struct { type SubscriptionType struct {
Name string `json:"type"` // The type of subscription.
Name string `json:"type"`
// The version number that identifies the definition of the subscription type that you want the response to use.
Version string `json:"version"` Version string `json:"version"`
} }

View File

@ -1,6 +1,8 @@
package eventsub package eventsub
import ( import (
"context"
"go.fifitido.net/twitch/api" "go.fifitido.net/twitch/api"
"go.fifitido.net/twitch/api/eventsub" "go.fifitido.net/twitch/api/eventsub"
) )
@ -19,8 +21,8 @@ func New(api *api.API, trans *eventsub.Transport) *EventSub {
} }
} }
func (e *EventSub) Subscribe(subType eventsub.SubscriptionType, cond eventsub.Condition) error { func (e *EventSub) Subscribe(ctx context.Context, subType eventsub.SubscriptionType, cond eventsub.Condition) error {
res, err := e.api.EventSub.CreateSubscription(&eventsub.CreateSubscriptionRequest{ res, err := e.api.EventSub.CreateEventSubSubscription(ctx, &eventsub.CreateEventSubSubscriptionRequest{
SubscriptionType: subType, SubscriptionType: subType,
Condition: cond, Condition: cond,
Transport: e.transport, Transport: e.transport,
@ -39,7 +41,7 @@ func (e *EventSub) Subscribe(subType eventsub.SubscriptionType, cond eventsub.Co
func (e *EventSub) Close() error { func (e *EventSub) Close() error {
for _, sub := range e.subscriptions { for _, sub := range e.subscriptions {
if err := e.api.EventSub.DeleteSubscription(sub.ID); err != nil { if err := e.api.EventSub.DeleteEventSubSubscription(context.Background(), sub.ID); err != nil {
return err return err
} }
} }