Initial commit
This commit is contained in:
commit
422102ccee
|
@ -0,0 +1,7 @@
|
|||
package twitch
|
||||
|
||||
import "go.fifitido.net/twitch/api"
|
||||
|
||||
func NewAPI() *api.API {
|
||||
return api.New()
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package ads
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type Ads struct {
|
||||
client *http.Client
|
||||
baseUrl *url.URL
|
||||
}
|
||||
|
||||
func New(client *http.Client, baseUrl *url.URL) *Ads {
|
||||
return &Ads{
|
||||
client: client,
|
||||
baseUrl: baseUrl,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package ads
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GetAdScheduleResponse struct {
|
||||
// A list that contains information related to the channel’s ad schedule.
|
||||
Data []GetAdScheduleData `json:"data"`
|
||||
}
|
||||
|
||||
type GetAdScheduleData struct {
|
||||
// The number of snoozes available for the broadcaster.
|
||||
SnoozeCount int `json:"snooze_count"`
|
||||
|
||||
// The UTC timestamp when the broadcaster will gain an additional snooze, in RFC3339 format.
|
||||
SnoozeRefreshAt time.Time `json:"snooze_refresh_at"`
|
||||
|
||||
// The UTC timestamp of the broadcaster’s next scheduled ad, in RFC3339 format. Empty if the channel has no ad scheduled or is not live.
|
||||
NextAdAt time.Time `json:"next_ad_at"`
|
||||
|
||||
// The length in seconds of the scheduled upcoming ad break.
|
||||
Duration int `json:"duration"`
|
||||
|
||||
// The UTC timestamp of the broadcaster’s last ad-break, in RFC3339 format. Empty if the channel has not run an ad or is not live.
|
||||
LastAdAt time.Time `json:"last_ad_at"`
|
||||
|
||||
// The amount of pre-roll free time remaining for the channel in seconds. Returns 0 if they are currently not pre-roll free.
|
||||
PrerollFreeTime int `json:"preroll_free_time"`
|
||||
}
|
||||
|
||||
// This endpoint returns ad schedule related information, including snooze, when the last ad was run, when the next ad is scheduled,
|
||||
// and if the channel is currently in pre-roll free time. Note that a new ad cannot be run until 8 minutes after running a previous ad.
|
||||
//
|
||||
// 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.
|
||||
func (e *Ads) GetAdSchedule(broadcasterID string) (*GetAdScheduleResponse, error) {
|
||||
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/ads", RawQuery: "broadcaster_id=" + broadcasterID})
|
||||
|
||||
resp, err := e.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetAdScheduleResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package ads
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SnoozeNextAdResponse struct {
|
||||
// A list that contains information about the channel’s snoozes and next upcoming ad after successfully snoozing.
|
||||
Data []SnoozeNextAdData `json:"data"`
|
||||
}
|
||||
|
||||
type SnoozeNextAdData struct {
|
||||
// The number of snoozes available for the broadcaster.
|
||||
SnoozeCount int `json:"snooze_count"`
|
||||
|
||||
// The UTC timestamp when the broadcaster will gain an additional snooze, in RFC3339 format.
|
||||
SnoozeRefreshAt time.Time `json:"snooze_refresh_at"`
|
||||
|
||||
// The UTC timestamp of the broadcaster’s next scheduled ad, in RFC3339 format.
|
||||
NextAdAt time.Time `json:"next_ad_at"`
|
||||
}
|
||||
|
||||
// If available, pushes back the timestamp of the upcoming automatic mid-roll ad by 5 minutes.
|
||||
// This endpoint duplicates the snooze functionality in the creator dashboard’s Ads Manager.
|
||||
//
|
||||
// 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.
|
||||
func (e *Ads) SnoozeNextAd(broadcasterID string) (*SnoozeNextAdResponse, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data SnoozeNextAdResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package ads
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type StartCommercialRequest struct {
|
||||
// The ID of the partner or affiliate broadcaster that wants to run the commercial. This ID must match the user ID found in the OAuth token.
|
||||
BroadcasterID string `json:"broadcaster_id"`
|
||||
|
||||
// The length of the commercial to run, in seconds.
|
||||
// Twitch tries to serve a commercial that’s the requested length, but it may be shorter or longer.
|
||||
// The maximum length you should request is 180 seconds.
|
||||
Duration int `json:"duration"`
|
||||
}
|
||||
|
||||
type StartCommercialResponse struct {
|
||||
// An array that contains a single object with the status of your start commercial request.
|
||||
Data []StartCommercialData `json:"data"`
|
||||
}
|
||||
|
||||
type StartCommercialData struct {
|
||||
// The length of the commercial you requested. If you request a commercial that’s longer than 180 seconds, the API uses 180 seconds.
|
||||
Length int `json:"length"`
|
||||
|
||||
// A message that indicates whether Twitch was able to serve an ad.
|
||||
Message string `json:"message"`
|
||||
|
||||
// The number of seconds you must wait before running another commercial.
|
||||
RetryAfter int `json:"retry_after"`
|
||||
}
|
||||
|
||||
// Starts a commercial on the specified channel.
|
||||
//
|
||||
// NOTE: Only partners and affiliates may run commercials and they must be streaming live at the time.
|
||||
//
|
||||
// NOTE: Only the broadcaster may start a commercial; the broadcaster’s editors and moderators may not start commercials on behalf of the broadcaster.
|
||||
//
|
||||
// Requires a user access token that includes the channel:edit:commercial scope.
|
||||
func (e *Ads) StartCommercial(req *StartCommercialRequest) (*StartCommercialResponse, error) {
|
||||
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "channels/commercial"})
|
||||
|
||||
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 StartCommercialResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package analytics
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type Analytics struct {
|
||||
client *http.Client
|
||||
baseUrl *url.URL
|
||||
}
|
||||
|
||||
func New(client *http.Client, baseUrl *url.URL) *Analytics {
|
||||
return &Analytics{
|
||||
client: client,
|
||||
baseUrl: baseUrl,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package analytics
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetExtensionAnalyticsParams struct {
|
||||
// The extension's client ID. If specified, the response contains a report for the specified extension.
|
||||
// If not specified, the response includes a report for each extension that the authenticated user owns.
|
||||
ExtensionID *string `url:"extension_id,omitempty"`
|
||||
|
||||
// The type of analytics report to get. Possible values are:
|
||||
//
|
||||
// - overview_v2
|
||||
Type *string `url:"type,omitempty"`
|
||||
|
||||
// The reporting window's start date, in RFC3339 format. Set the time portion to zeroes (for example, 2021-10-22T00:00:00Z).
|
||||
//
|
||||
// The start date must be on or after January 31, 2018. If you specify an earlier date, the API ignores it and uses January 31, 2018.
|
||||
// If you specify a start date, you must specify an end date. If you don't specify a start and end date,
|
||||
// the report includes all available data since January 31, 2018.
|
||||
//
|
||||
// The report contains one row of data for each day in the reporting window.
|
||||
StartedAt *time.Time `url:"started_at,omitempty"`
|
||||
|
||||
// The reporting window's end date, in RFC3339 format. Set the time portion to zeroes (for example, 2021-10-27T00:00:00Z).
|
||||
// The report is inclusive of the end date.
|
||||
//
|
||||
// Specify an end date only if you provide a start date. Because it can take up to two days for the data to be available,
|
||||
// you must specify an end date that's earlier than today minus one to two days.
|
||||
// If not, the API ignores your end date and uses an end date that is today minus one to two days.
|
||||
EndedAt *time.Time `url:"ended_at,omitempty"`
|
||||
|
||||
// The maximum number of report URLs to return per page in the response.
|
||||
// The minimum page size is 1 URL per page and the maximum is 100 URLs per page. The default is 20.
|
||||
//
|
||||
// NOTE: While you may specify a maximum value of 100, the response will contain at most 20 URLs per page.
|
||||
First *int `url:"first,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
|
||||
//
|
||||
// This parameter is ignored if the extension_id parameter is set.
|
||||
After *types.Cursor `url:"after,omitempty"`
|
||||
}
|
||||
|
||||
type GetExtensionAnalyticsResponse struct {
|
||||
// A list of reports. The reports are returned in no particular order; however, the data within each report is in ascending order by date (newest first).
|
||||
// The report contains one row of data per day of the reporting window; the report contains rows for only those days that the extension was used.
|
||||
// The array is empty if there are no reports.
|
||||
Data []ExtensionAnalyticsReport `json:"data"`
|
||||
|
||||
// Contains 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 ExtensionAnalyticsReport struct {
|
||||
// An ID that identifies the extension that the report was generated for.
|
||||
ExtensionID string `json:"extension_id"`
|
||||
|
||||
// The URL that you use to download the report. The URL is valid for 5 minutes.
|
||||
URL string `json:"URL"`
|
||||
|
||||
// The type of report.
|
||||
Type string `json:"type"`
|
||||
|
||||
// The reporting window’s start and end dates, in RFC3339 format.
|
||||
DateRange types.DateRange `json:"date_range"`
|
||||
}
|
||||
|
||||
// Gets an analytics report for one or more extensions. The response contains the URLs used to download the reports (CSV files).
|
||||
// Learn More: https://dev.twitch.tv/docs/insights
|
||||
//
|
||||
// Requires a user access token that includes the analytics:read:extensions scope.
|
||||
func (e *Analytics) GetExtensionAnalytics(params GetExtensionAnalyticsParams) (*GetExtensionAnalyticsResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "analytics/extensions", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := e.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetExtensionAnalyticsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package analytics
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetGameAnalyticsParams struct {
|
||||
// The game’s client ID. If specified, the response contains a report for the specified game.
|
||||
// If not specified, the response includes a report for each of the authenticated user’s games.
|
||||
GameID *string `url:"game_id,omitempty"`
|
||||
|
||||
// The type of analytics report to get. Possible values are:
|
||||
//
|
||||
// - overview_v2
|
||||
Type *string `url:"type,omitempty"`
|
||||
|
||||
// The reporting window’s start date, in RFC3339 format. Set the time portion to zeroes (for example, 2021-10-22T00:00:00Z).
|
||||
// If you specify a start date, you must specify an end date.
|
||||
//
|
||||
// The start date must be within one year of today’s date. If you specify an earlier date,
|
||||
// the API ignores it and uses a date that’s one year prior to today’s date. If you don’t specify a start and end date,
|
||||
// the report includes all available data for the last 365 days from today.
|
||||
//
|
||||
// The report contains one row of data for each day in the reporting window.
|
||||
StartedAt *time.Time `url:"started_at,omitempty"`
|
||||
|
||||
// The reporting window’s end date, in RFC3339 format. Set the time portion to zeroes (for example, 2021-10-22T00:00:00Z).
|
||||
// The report is inclusive of the end date.
|
||||
//
|
||||
// Specify an end date only if you provide a start date. Because it can take up to two days for the data to be available,
|
||||
// you must specify an end date that’s earlier than today minus one to two days.
|
||||
// If not, the API ignores your end date and uses an end date that is today minus one to two days.
|
||||
EndedAt *time.Time `url:"ended_at,omitempty"`
|
||||
|
||||
// The maximum number of report URLs to return per page in the response.
|
||||
// The minimum page size is 1 URL per page and the maximum is 100 URLs per page.The default is 20.
|
||||
//
|
||||
// NOTE: While you may specify a maximum value of 100, the response will contain at most 20 URLs per page.
|
||||
First *int `url:"first,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
|
||||
//
|
||||
// This parameter is ignored if game_id parameter is set.
|
||||
After *types.Cursor `url:"after,omitempty"`
|
||||
}
|
||||
|
||||
type GetGameAnalyticsResponse struct {
|
||||
// A list of reports. The reports are returned in no particular order; however, the data within each report is in ascending order by date (newest first).
|
||||
// The report contains one row of data per day of the reporting window; the report contains rows for only those days that the game was used.
|
||||
// A report is available only if the game was broadcast for at least 5 hours over the reporting period. The array is empty if there are no reports.
|
||||
Data []GameAnalyticsReport `json:"data"`
|
||||
|
||||
// Contains the information used to page through the list of results.
|
||||
// The object is empty if there are no more pages left to page through.
|
||||
Pagination types.Pagination `json:"pagination"`
|
||||
}
|
||||
|
||||
type GameAnalyticsReport struct {
|
||||
// An ID that identifies the game that the report was generated for.
|
||||
GameID string `json:"game_id"`
|
||||
|
||||
// The URL that you use to download the report. The URL is valid for 5 minutes.
|
||||
URL string `json:"URL"`
|
||||
|
||||
// The type of report.
|
||||
Type string `json:"type"`
|
||||
|
||||
// The reporting window’s start and end dates, in RFC3339 format.
|
||||
DateRange types.DateRange `json:"date_range"`
|
||||
}
|
||||
|
||||
// Gets an analytics report for one or more games. The response contains the URLs used to download the reports (CSV files).
|
||||
// Learn more: https://dev.twitch.tv/docs/insights
|
||||
//
|
||||
// Requires a user access token that includes the analytics:read:games scope.
|
||||
func (e *Analytics) GetGameAnalytics(params GetGameAnalyticsParams) (*GetGameAnalyticsResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := e.baseUrl.ResolveReference(&url.URL{Path: "analytics/games", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := e.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetGameAnalyticsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"go.fifitido.net/twitch/api/ads"
|
||||
"go.fifitido.net/twitch/api/analytics"
|
||||
"go.fifitido.net/twitch/api/bits"
|
||||
"go.fifitido.net/twitch/api/channelpoints"
|
||||
"go.fifitido.net/twitch/api/channels"
|
||||
"go.fifitido.net/twitch/api/eventsub"
|
||||
)
|
||||
|
||||
const HelixBaseUrl = "https://api.twitch.tv/helix"
|
||||
|
||||
type API struct {
|
||||
client *http.Client
|
||||
baseUrl *url.URL
|
||||
|
||||
Ads *ads.Ads
|
||||
Analytics *analytics.Analytics
|
||||
Bits *bits.Bits
|
||||
Channels *channels.Channels
|
||||
ChannelPoints *channelpoints.ChannelPoints
|
||||
EventSub *eventsub.EventSub
|
||||
}
|
||||
|
||||
func New() *API {
|
||||
client := &http.Client{}
|
||||
baseUrl, _ := url.Parse(HelixBaseUrl)
|
||||
|
||||
return &API{
|
||||
client: client,
|
||||
baseUrl: baseUrl,
|
||||
|
||||
Ads: ads.New(client, baseUrl),
|
||||
Analytics: analytics.New(client, baseUrl),
|
||||
Bits: bits.New(client, baseUrl),
|
||||
Channels: channels.New(client, baseUrl),
|
||||
ChannelPoints: channelpoints.New(client, baseUrl),
|
||||
EventSub: eventsub.New(client, baseUrl),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package bits
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type Bits struct {
|
||||
client *http.Client
|
||||
baseUrl *url.URL
|
||||
}
|
||||
|
||||
func New(client *http.Client, baseUrl *url.URL) *Bits {
|
||||
return &Bits{
|
||||
client: client,
|
||||
baseUrl: baseUrl,
|
||||
}
|
||||
}
|
||||
|
||||
type Period string
|
||||
|
||||
const (
|
||||
Day Period = "day"
|
||||
Week Period = "week"
|
||||
Month Period = "month"
|
||||
Year Period = "year"
|
||||
All Period = "all"
|
||||
)
|
||||
|
||||
type CheermoteType string
|
||||
|
||||
const (
|
||||
GlobalFirstParty CheermoteType = "global_first_party"
|
||||
GlobalThirdParty CheermoteType = "global_third_party"
|
||||
ChannelCustom CheermoteType = "channel_custom"
|
||||
DisplayOnly CheermoteType = "display_only"
|
||||
Sponsored CheermoteType = "sponsored"
|
||||
)
|
|
@ -0,0 +1,86 @@
|
|||
package bits
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetBitsLeaderboardParams struct {
|
||||
// The number of results to return. The minimum count is 1 and the maximum is 100. The default is 10.
|
||||
Count *int `url:"count,omitempty"`
|
||||
|
||||
// The time period over which data is aggregated (uses the PST time zone).
|
||||
Period *Period `url:"period,omitempty"`
|
||||
|
||||
// The start date, in RFC3339 format, used for determining the aggregation period. Specify this parameter only if you specify the period query parameter.
|
||||
// The start date is ignored if period is all.
|
||||
//
|
||||
// Note that the date is converted to PST before being used, so if you set the start time to 2022-01-01T00:00:00.0Z and period to month,
|
||||
// the actual reporting period is December 2021, not January 2022. If you want the reporting period to be January 2022,
|
||||
// you must set the start time to 2022-01-01T08:00:00.0Z or 2022-01-01T00:00:00.0-08:00.
|
||||
//
|
||||
// If your start date uses the ‘+’ offset operator (for example, 2022-01-01T00:00:00.0+05:00), you must URL encode the start date.
|
||||
StartedAt *time.Time `url:"started_at,omitempty"`
|
||||
|
||||
// An ID that identifies a user that cheered bits in the channel.
|
||||
// If count is greater than 1, the response may include users ranked above and below the specified user.
|
||||
// To get the leaderboard’s top leaders, don’t specify a user ID.
|
||||
UserID *string `url:"user_id,omitempty"`
|
||||
}
|
||||
|
||||
type GetBitsLeaderboardResponse struct {
|
||||
// A list of leaderboard leaders. The leaders are returned in rank order by how much they’ve cheered.
|
||||
// The array is empty if nobody has cheered bits.
|
||||
Data []LeaderboardEntry `json:"data"`
|
||||
|
||||
// The reporting window’s start and end dates, in RFC3339 format. The dates are calculated by using the started_at and period query parameters.
|
||||
// If you don’t specify the started_at query parameter, the fields contain empty strings.
|
||||
DateRange types.DateRange `json:"date_range"`
|
||||
|
||||
// The number of ranked users in data.
|
||||
// This is the value in the count query parameter or the total number of entries on the leaderboard, whichever is less.
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type LeaderboardEntry struct {
|
||||
// An ID that identifies a user on the leaderboard.
|
||||
UserID string `json:"user_id"`
|
||||
|
||||
// The user’s login name.
|
||||
UserLogin string `json:"user_login"`
|
||||
|
||||
// The user’s display name.
|
||||
UserName string `json:"user_name"`
|
||||
|
||||
// The user’s position on the leaderboard.
|
||||
Rank int `json:"rank"`
|
||||
|
||||
// The number of Bits the user has cheered.
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
// Gets the Bits leaderboard for the authenticated broadcaster.
|
||||
//
|
||||
// Requires a user access token that includes the bits:read scope.
|
||||
func (b *Bits) GetBitsLeaderboard(params *GetBitsLeaderboardParams) (*GetBitsLeaderboardResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "bits/leaderboard", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := b.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetBitsLeaderboardResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package bits
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GetCheermotesResponse struct {
|
||||
// The list of Cheermotes. The list is in ascending order by the order field’s value.
|
||||
Data []Cheermote `json:"data"`
|
||||
}
|
||||
|
||||
type Cheermote struct {
|
||||
// The name portion of the Cheermote string that you use in chat to cheer Bits.
|
||||
// The full Cheermote string is the concatenation of {prefix} + {number of Bits}.
|
||||
// For example, if the prefix is “Cheer” and you want to cheer 100 Bits, the full Cheermote string is Cheer100.
|
||||
// When the Cheermote string is entered in chat, Twitch converts it to the image associated with the Bits tier that was cheered.
|
||||
Prefix string `json:"prefix"`
|
||||
|
||||
// A list of tier levels that the Cheermote supports.
|
||||
// Each tier identifies the range of Bits that you can cheer at that tier level and an image that graphically identifies the tier level.
|
||||
Tiers []CheermoteTier `json:"tiers"`
|
||||
|
||||
// The type of Cheermote.
|
||||
Type CheermoteType `json:"type"`
|
||||
|
||||
// The order that the Cheermotes are shown in the Bits card. The numbers may not be consecutive. For example, the numbers may jump from 1 to 7 to 13.
|
||||
// The order numbers are unique within a Cheermote type (for example, global_first_party) but may not be unique amongst all Cheermotes in the response.
|
||||
Order int `json:"order"`
|
||||
|
||||
// The date and time, in RFC3339 format, when this Cheermote was last updated.
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
|
||||
// A Boolean value that indicates whether this Cheermote provides a charitable contribution match during charity campaigns.
|
||||
IsCharitable bool `json:"is_charitable"`
|
||||
}
|
||||
|
||||
type CheermoteTier struct {
|
||||
// The minimum number of Bits that you must cheer at this tier level.
|
||||
// The maximum number of Bits that you can cheer at this level is determined by the required minimum Bits of the next tier level minus 1.
|
||||
// For example, if min_bits is 1 and min_bits for the next tier is 100, the Bits range for this tier level is 1 through 99.
|
||||
// The minimum Bits value of the last tier is the maximum number of Bits you can cheer using this Cheermote. For example, 10000.
|
||||
MinBits int `json:"min_bits"`
|
||||
|
||||
// The tier level. Possible tiers are:
|
||||
//
|
||||
// 1, 100, 500, 1000, 5000, 10000, 100000
|
||||
ID string `json:"id"`
|
||||
|
||||
// The hex code of the color associated with this tier level (for example, #979797).
|
||||
Color string `json:"color"`
|
||||
|
||||
// The animated and static image sets for the Cheermote. The dictionary of images is organized by theme, format, and size.
|
||||
// The theme keys are dark and light. Each theme is a dictionary of formats: animated and static.
|
||||
// Each format is a dictionary of sizes: 1, 1.5, 2, 3, and 4. The value of each size contains the URL to the image.
|
||||
Images map[string]CheermoteImage `json:"images"`
|
||||
|
||||
// A Boolean value that determines whether users can cheer at this tier level.
|
||||
CanCheer bool `json:"can_cheer"`
|
||||
|
||||
// A Boolean value that determines whether this tier level is shown in the Bits card. Is true if this tier level is shown in the Bits card.
|
||||
ShowInBitsCard bool `json:"show_in_bits_card"`
|
||||
}
|
||||
|
||||
type CheermoteImage struct {
|
||||
Animated *CheermoteImageSizes `json:"animated"`
|
||||
Static *CheermoteImageSizes `json:"static"`
|
||||
}
|
||||
|
||||
type CheermoteImageSizes struct {
|
||||
One CheermoteImage `json:"1"`
|
||||
One5 CheermoteImage `json:"1.5"`
|
||||
Two CheermoteImage `json:"2"`
|
||||
Three CheermoteImage `json:"3"`
|
||||
Four CheermoteImage `json:"4"`
|
||||
}
|
||||
|
||||
// Gets a list of Cheermotes that users can use to cheer Bits in any Bits-enabled channel’s chat room. Cheermotes are animated emotes that viewers can assign Bits to.
|
||||
//
|
||||
// Requires an app access token or user access token.
|
||||
func (b *Bits) GetCheermotes(broadcasterID string) (*GetCheermotesResponse, error) {
|
||||
endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "bits/cheermotes", RawQuery: "broadcaster_id=" + broadcasterID})
|
||||
|
||||
resp, err := b.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetCheermotesResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package bits
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetExtensionTransactionsParams struct {
|
||||
// The ID of the extension whose list of transactions you want to get.
|
||||
ExtensionID string `url:"extension_id"`
|
||||
|
||||
// A transaction ID used to filter the list of transactions. Specify this parameter for each transaction you want to get.
|
||||
// For example, id=1234&id=5678. You may specify a maximum of 100 IDs.
|
||||
IDs []string `url:"ids,omitempty"`
|
||||
|
||||
// The maximum number of items to return per page in the response.
|
||||
// The minimum page size is 1 item per page and the maximum is 100 items per page. The default is 20.
|
||||
First *int `url:"first,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
|
||||
After *types.Cursor `url:"after,omitempty"`
|
||||
}
|
||||
|
||||
type GetExtensionTransactionsResponse struct {
|
||||
// The list of transactions.
|
||||
Data []ExtensionTransaction `json:"data"`
|
||||
|
||||
// Contains 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 ExtensionTransaction struct {
|
||||
// An ID that identifies the transaction.
|
||||
ID string `json:"id"`
|
||||
|
||||
// The UTC date and time (in RFC3339 format) of the transaction.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
|
||||
// The ID of the broadcaster that owns the channel where the transaction occurred.
|
||||
BroadcasterID string `json:"broadcaster_id"`
|
||||
|
||||
// The broadcaster’s login name.
|
||||
BroadcasterLogin string `json:"broadcaster_login"`
|
||||
|
||||
// The broadcaster’s display name.
|
||||
BroadcasterName string `json:"broadcaster_name"`
|
||||
|
||||
// The type of transaction. Possible values are:
|
||||
//
|
||||
// BITS_IN_EXTENSION
|
||||
ProductType string `json:"product_type"`
|
||||
}
|
||||
|
||||
type ProductData struct {
|
||||
// An ID that identifies the digital product.
|
||||
SKU string `json:"sku"`
|
||||
|
||||
// Set to twitch.ext. + <the extension's ID>.
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// Contains details about the digital product’s cost.
|
||||
Cost ProductDataCost `json:"cost"`
|
||||
|
||||
// A Boolean value that determines whether the product is in development. Is true if the digital product is in development and cannot be exchanged.
|
||||
InDevelopment bool `json:"in_development"`
|
||||
|
||||
// The name of the digital product.
|
||||
DisplayName string `json:"display_name"`
|
||||
|
||||
// This field is always empty since you may purchase only unexpired products.
|
||||
Expiration string `json:"expiration"`
|
||||
|
||||
// A Boolean value that determines whether the data was broadcast to all instances of the extension. Is true if the data was broadcast to all instances.
|
||||
Broadcast bool `json:"broadcast"`
|
||||
}
|
||||
|
||||
type ProductDataCost struct {
|
||||
// The amount exchanged for the digital product.
|
||||
Amount int `json:"amount"`
|
||||
|
||||
// The type of currency exchanged. Possible values are:
|
||||
//
|
||||
// bits
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// Gets an extension’s list of transactions. A transaction records the exchange of a currency (for example, Bits) for a digital product.
|
||||
//
|
||||
// Requires an app access token.
|
||||
func (b *Bits) GetExtensionTransactions(params *GetExtensionTransactionsParams) (*GetExtensionTransactionsResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := b.baseUrl.ResolveReference(&url.URL{Path: "extensions/transactions", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := b.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetExtensionTransactionsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package channelpoints
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type ChannelPoints struct {
|
||||
client *http.Client
|
||||
baseUrl *url.URL
|
||||
}
|
||||
|
||||
func New(client *http.Client, baseUrl *url.URL) *ChannelPoints {
|
||||
return &ChannelPoints{
|
||||
client: client,
|
||||
baseUrl: baseUrl,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package channelpoints
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type CreateCustomRewardsRequest struct {
|
||||
// The custom reward’s title. The title may contain a maximum of 45 characters and it must be unique amongst all of the broadcaster’s custom rewards.
|
||||
Title string `json:"title"`
|
||||
|
||||
// The cost of the reward, in Channel Points.
|
||||
// The minimum is 1 point.
|
||||
Cost int64 `json:"cost"`
|
||||
|
||||
// The prompt shown to the viewer when they redeem the reward.
|
||||
// Specify a prompt if is_user_input_required is true.
|
||||
// The prompt is limited to a maximum of 200 characters.
|
||||
Prompt *string `json:"prompt"`
|
||||
|
||||
// A Boolean value that determines whether the reward is enabled. Viewers see only enabled rewards.
|
||||
// The default is true.
|
||||
IsEnabled *bool `json:"is_enabled"`
|
||||
|
||||
// The background color to use for the reward. Specify the color using Hex format (for example, #9147FF).
|
||||
BackgroundColor *string `json:"background_color"`
|
||||
|
||||
// A Boolean value that determines whether the user needs to enter information when redeeming the reward. See the prompt field.
|
||||
// The default is false.
|
||||
IsUserInputRequired *bool `json:"is_user_input_required"`
|
||||
|
||||
// A Boolean value that determines whether to limit the maximum number of redemptions allowed per live stream (see the max_per_stream field).
|
||||
// The default is false.
|
||||
IsMaxPerStreamEnabled *bool `json:"is_max_per_stream_enabled"`
|
||||
|
||||
// The maximum number of redemptions allowed per live stream. Applied only if is_max_per_stream_enabled is true.
|
||||
// The minimum value is 1.
|
||||
MaxPerStream *int `json:"max_per_stream"`
|
||||
|
||||
// A Boolean value that determines whether to limit the maximum number of redemptions allowed per user per stream (see the max_per_user_per_stream field).
|
||||
// The default is false.
|
||||
IsMaxPerUserPerStreamEnabled *bool `json:"is_max_per_user_per_stream_enabled"`
|
||||
|
||||
// The maximum number of redemptions allowed per user per stream. Applied only if is_max_per_user_per_stream_enabled is true.
|
||||
// The minimum value is 1.
|
||||
MaxPerUserPerStream *int `json:"max_per_user_per_stream"`
|
||||
|
||||
// A Boolean value that determines whether to apply a cooldown period between redemptions (see the global_cooldown_seconds field for the duration of the cooldown period).
|
||||
// The default is false.
|
||||
IsGlobalCooldownEnabled *bool `json:"is_global_cooldown_enabled"`
|
||||
|
||||
// The cooldown period, in seconds. Applied only if the is_global_cooldown_enabled field is true.
|
||||
// The minimum value is 1; however, the minimum value is 60 for it to be shown in the Twitch UX.
|
||||
GlobalCooldownSeconds *int `json:"global_cooldown_seconds"`
|
||||
|
||||
// A Boolean value that determines whether redemptions should be set to FULFILLED status immediately when a reward is redeemed.
|
||||
// If false, status is set to UNFULFILLED and follows the normal request queue process.
|
||||
// The default is false.
|
||||
ShouldRedemptionsSkipRequestQueue *bool `json:"should_redemptions_skip_request_queue"`
|
||||
}
|
||||
|
||||
type CreateCustomRewardsResponse struct {
|
||||
Data []CustomReward `json:"data"`
|
||||
}
|
||||
|
||||
// Creates a Custom Reward in the broadcaster’s 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.
|
||||
func (c *ChannelPoints) CreateCustomRewards(broadcastID string, req *CreateCustomRewardsRequest) (*CreateCustomRewardsResponse, error) {
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: "broadcaster_id=" + broadcastID})
|
||||
|
||||
r, w := io.Pipe()
|
||||
|
||||
go func() {
|
||||
if err := json.NewEncoder(w).Encode(req); err != nil {
|
||||
w.CloseWithError(err)
|
||||
} else {
|
||||
w.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
resp, err := c.client.Post(endpoint.String(), "application/json", r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data CreateCustomRewardsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package channelpoints
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
type DeleteCustomRewardParams struct {
|
||||
// The ID of the broadcaster that created the custom reward. This ID must match the user ID found in the OAuth token.
|
||||
BroadcasterID string `url:"broadcaster_id"`
|
||||
|
||||
// The ID of the custom reward to delete.
|
||||
ID string `url:"id"`
|
||||
}
|
||||
|
||||
// Deletes a custom reward that the broadcaster created.
|
||||
//
|
||||
// The app used to create the reward is the only app that may delete it.
|
||||
// If the reward’s 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.
|
||||
func (c *ChannelPoints) DeleteCustomReward(params *DeleteCustomRewardParams) error {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := c.client.Do(&http.Request{
|
||||
Method: http.MethodDelete,
|
||||
URL: endpoint,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package channelpoints
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
type GetCustomRewardParams struct {
|
||||
// The ID of the broadcaster whose custom rewards you want to get. This ID must match the user ID found in the OAuth token.
|
||||
BroadcasterID string `url:"broadcaster_id"`
|
||||
|
||||
// A list of IDs to filter the rewards by. To specify more than one ID, include this parameter for each reward you want to get.
|
||||
// For example, id=1234&id=5678. You may specify a maximum of 50 IDs.
|
||||
//
|
||||
// Duplicate IDs are ignored. The response contains only the IDs that were found. If none of the IDs were found, the response is 404 Not Found.
|
||||
IDs []string `url:"id,omitempty"`
|
||||
|
||||
// A Boolean value that determines whether the response contains only the custom rewards that the app may manage
|
||||
// (the app is identified by the ID in the Client-Id header). Set to true to get only the custom rewards that the app may manage.
|
||||
// The default is false.
|
||||
OnlyManageableRewards *bool `url:"only_manageable_rewards,omitempty"`
|
||||
}
|
||||
|
||||
type GetCustomRewardResponse struct {
|
||||
// A list of custom rewards. The list is in ascending order by id. If the broadcaster hasn’t created custom rewards, the list is empty.
|
||||
Data []CustomReward `json:"data"`
|
||||
}
|
||||
|
||||
// Gets a list of custom rewards that the specified broadcaster created.
|
||||
//
|
||||
// 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.
|
||||
func (c *ChannelPoints) GetCustomReward(params *GetCustomRewardParams) (*GetCustomRewardResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := c.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetCustomRewardResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package channelpoints
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetCustomRewardRedemptionParams struct {
|
||||
// The ID of the broadcaster that owns the custom reward. This ID must match the user ID found in the user OAuth token.
|
||||
BroadcasterID string `url:"broadcaster_id"`
|
||||
|
||||
// The ID that identifies the custom reward whose redemptions you want to get.
|
||||
RewardID string `url:"reward_id"`
|
||||
|
||||
// The status of the redemptions to return. The possible case-sensitive values are:
|
||||
//
|
||||
// NOTE: This field is required only if you don’t specify the id query parameter.
|
||||
//
|
||||
// NOTE: Canceled and fulfilled redemptions are returned for only a few days after they’re canceled or fulfilled.
|
||||
Status *RewardRedemptionStatus `url:"status,omitempty"`
|
||||
|
||||
// A list of IDs to filter the redemptions by. To specify more than one ID, include this parameter for each redemption you want to get.
|
||||
// For example, id=1234&id=5678. You may specify a maximum of 50 IDs.
|
||||
//
|
||||
// Duplicate IDs are ignored. The response contains only the IDs that were found. If none of the IDs were found, the response is 404 Not Found.
|
||||
IDs []string `url:"id,omitempty"`
|
||||
|
||||
// The order to sort redemptions by. The default is OLDEST.
|
||||
Sort *types.SortOrder `url:"sort,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
|
||||
After *string `url:"after,omitempty"`
|
||||
|
||||
// The maximum number of redemptions to return per page in the response.
|
||||
// The minimum page size is 1 redemption per page and the maximum is 50.
|
||||
// The default is 20.
|
||||
First *int `url:"first,omitempty"`
|
||||
}
|
||||
|
||||
type GetCustomRewardRedemptionResponse struct {
|
||||
// The list of redemptions for the specified reward. The list is empty if there are no redemptions that match the redemption criteria.
|
||||
Data []CustomRewardRedemption `json:"data"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (c *ChannelPoints) GetCustomRewardRedemption(params *GetCustomRewardRedemptionParams) (*GetCustomRewardRedemptionResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards/redemptions", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := c.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetCustomRewardRedemptionResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package channelpoints
|
||||
|
||||
import "time"
|
||||
|
||||
type CustomReward struct {
|
||||
// The ID that uniquely identifies the broadcaster.
|
||||
BroadcasterID string `json:"broadcaster_id"`
|
||||
|
||||
// The broadcaster’s login name.
|
||||
BroadcasterLogin string `json:"broadcaster_login"`
|
||||
|
||||
// The broadcaster’s display name.
|
||||
BroadcasterName string `json:"broadcaster_name"`
|
||||
|
||||
// The ID that uniquely identifies this custom reward.
|
||||
ID string `json:"id"`
|
||||
|
||||
// The title of the custom reward.
|
||||
Title string `json:"title"`
|
||||
|
||||
// The prompt shown to the viewer when they redeem the reward if user input is required (see the is_user_input_required field).
|
||||
Prompt string `json:"prompt"`
|
||||
|
||||
// The cost of the reward in Channel Points.
|
||||
Cost int `json:"cost"`
|
||||
|
||||
// A set of custom images for the reward.
|
||||
// This field is set to null if the broadcaster didn’t upload images.
|
||||
CustomImages *CustomRewardImage `json:"custom_images"`
|
||||
|
||||
// A set of default images for the reward.
|
||||
DefaultImage CustomRewardImage `json:"default_image"`
|
||||
|
||||
// The background color to use for the reward. The color is in Hex format (for example, #00E5CB).
|
||||
BackgroundColor string `json:"background_color"`
|
||||
|
||||
// A Boolean value that determines whether the reward is enabled.
|
||||
// Is true if enabled; otherwise, false. Disabled rewards aren’t shown to the user.
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
|
||||
// A Boolean value that determines whether the user must enter information when redeeming the reward.
|
||||
// Is true if the reward requires user input.
|
||||
IsUserInputRequired bool `json:"is_user_input_required"`
|
||||
|
||||
// The settings used to determine whether to apply a maximum to the number to the redemptions allowed per live stream.
|
||||
MaxPerStreamSetting struct {
|
||||
// A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per live stream.
|
||||
// Is true if the reward applies a limit.
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
|
||||
// The maximum number of redemptions allowed per live stream.
|
||||
MaxPerStream int64 `json:"max_per_stream"`
|
||||
} `json:"max_per_stream_setting"`
|
||||
|
||||
// The settings used to determine whether to apply a maximum to the number of redemptions allowed per user per live stream.
|
||||
MaxPerUserPerStreamSetting struct {
|
||||
// A Boolean value that determines whether the reward applies a limit on the number of redemptions allowed per user per live stream.
|
||||
// Is true if the reward applies a limit.
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
|
||||
// The maximum number of redemptions allowed per user per live stream.
|
||||
MaxPerUserPerStream int64 `json:"max_per_user_per_stream"`
|
||||
} `json:"max_per_user_per_stream_setting"`
|
||||
|
||||
// The settings used to determine whether to apply a cooldown period between redemptions and the length of the cooldown.
|
||||
GlobalCooldownSetting struct {
|
||||
// A Boolean value that determines whether to apply a cooldown period. Is true if a cooldown period is enabled.
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
|
||||
// The cooldown period, in seconds.
|
||||
GlobalCooldownSeconds int64 `json:"global_cooldown_seconds"`
|
||||
} `json:"global_cooldown_setting"`
|
||||
|
||||
// A Boolean value that determines whether the reward is currently paused. Is true if the reward is paused. Viewers can’t redeem paused rewards.
|
||||
IsPaused bool `json:"is_paused"`
|
||||
|
||||
// A Boolean value that determines whether the reward is currently in stock. Is true if the reward is in stock. Viewers can’t redeem out of stock rewards.
|
||||
IsInStock bool `json:"is_in_stock"`
|
||||
|
||||
// A Boolean value that determines whether redemptions should be set to FULFILLED status immediately when a reward is redeemed.
|
||||
// If false, status is UNFULFILLED and follows the normal request queue process.
|
||||
ShouldRedemptionsSkipRequestQueue bool `json:"should_redemptions_skip_request_queue"`
|
||||
|
||||
// The number of redemptions redeemed during the current live stream. The number counts against the max_per_stream_setting limit.
|
||||
// This field is null if the broadcaster’s stream isn’t live or max_per_stream_setting isn’t enabled.
|
||||
RedemptionsRedeemedCurrentStream *int `json:"redemptions_redeemed_current_stream"`
|
||||
|
||||
// The timestamp of when the cooldown period expires. Is null if the reward isn’t in a cooldown state (see the global_cooldown_setting field).
|
||||
CooldownExpiresAt *time.Time `json:"cooldown_expires_at"`
|
||||
}
|
||||
|
||||
type CustomRewardImage struct {
|
||||
// The URL to a small version of the image.
|
||||
URL1X string `json:"url_1x"`
|
||||
|
||||
// The URL to a medium version of the image.
|
||||
URL2X string `json:"url_2x"`
|
||||
|
||||
// The URL to a large version of the image.
|
||||
URL4X string `json:"url_4x"`
|
||||
}
|
||||
|
||||
type RewardRedemptionStatus string
|
||||
|
||||
const (
|
||||
RewardRedemptionStatusCanceled RewardRedemptionStatus = "CANCELED"
|
||||
RewardRedemptionStatusFulfilled RewardRedemptionStatus = "FULFILLED"
|
||||
RewardRedemptionStatusUnfulfilled RewardRedemptionStatus = "UNFULFILLED"
|
||||
)
|
||||
|
||||
type CustomRewardRedemption struct {
|
||||
// The ID that uniquely identifies the broadcaster.
|
||||
BroadcasterID string `json:"broadcaster_id"`
|
||||
|
||||
// The broadcaster's login name.
|
||||
BroadcasterLogin string `json:"broadcaster_login"`
|
||||
|
||||
// The broadcaster's display name.
|
||||
BroadcasterName string `json:"broadcaster_name"`
|
||||
|
||||
// The ID that uniquely identifies this redemption.
|
||||
ID string `json:"id"`
|
||||
|
||||
// The ID that uniquely identifies the user that redeemed the reward.
|
||||
UserID string `json:"user_id"`
|
||||
|
||||
// The user's login name.
|
||||
UserLogin string `json:"user_login"`
|
||||
|
||||
// The user's display name.
|
||||
UserName string `json:"user_name"`
|
||||
|
||||
// The state of the redemption.
|
||||
Status RewardRedemptionStatus `json:"status"`
|
||||
|
||||
//The date and time of when the reward was redeemed, in RFC3339 format.
|
||||
RedeemedAt time.Time `json:"redeemed_at"`
|
||||
|
||||
// The reward that the user redeemed.
|
||||
Reward struct {
|
||||
// The ID that uniquely identifies the redeemed reward.
|
||||
ID string `json:"id"`
|
||||
|
||||
// The reward's title.
|
||||
Title string `json:"title"`
|
||||
|
||||
// The prompt displayed to the viewer if user input is required.
|
||||
Prompt string `json:"prompt"`
|
||||
|
||||
// The reward’s cost, in Channel Points.
|
||||
Cost int64 `json:"cost"`
|
||||
} `json:"reward"`
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package channelpoints
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
type UpdateCustomRewardParams struct {
|
||||
// The ID of the broadcaster that’s updating the reward. This ID must match the user ID found in the OAuth token.
|
||||
BroadcasterID string `url:"broadcaster_id"`
|
||||
|
||||
// The ID of the reward to update.
|
||||
ID string `url:"id"`
|
||||
}
|
||||
|
||||
type UpdateCustomRewardRequest struct {
|
||||
// The custom reward’s title. The title may contain a maximum of 45 characters and it must be unique amongst all of the broadcaster’s custom rewards.
|
||||
Title *string `json:"title"`
|
||||
|
||||
// The cost of the reward, in Channel Points.
|
||||
// The minimum is 1 point.
|
||||
Cost *int64 `json:"cost"`
|
||||
|
||||
// The prompt shown to the viewer when they redeem the reward.
|
||||
// Specify a prompt if is_user_input_required is true.
|
||||
// The prompt is limited to a maximum of 200 characters.
|
||||
Prompt *string `json:"prompt"`
|
||||
|
||||
// A Boolean value that determines whether the reward is enabled. Viewers see only enabled rewards.
|
||||
// The default is true.
|
||||
IsEnabled *bool `json:"is_enabled"`
|
||||
|
||||
// The background color to use for the reward. Specify the color using Hex format (for example, #9147FF).
|
||||
BackgroundColor *string `json:"background_color"`
|
||||
|
||||
// A Boolean value that determines whether the user needs to enter information when redeeming the reward. See the prompt field.
|
||||
// The default is false.
|
||||
IsUserInputRequired *bool `json:"is_user_input_required"`
|
||||
|
||||
// A Boolean value that determines whether to limit the maximum number of redemptions allowed per live stream (see the max_per_stream field).
|
||||
// The default is false.
|
||||
IsMaxPerStreamEnabled *bool `json:"is_max_per_stream_enabled"`
|
||||
|
||||
// The maximum number of redemptions allowed per live stream. Applied only if is_max_per_stream_enabled is true.
|
||||
// The minimum value is 1.
|
||||
MaxPerStream *int `json:"max_per_stream"`
|
||||
|
||||
// A Boolean value that determines whether to limit the maximum number of redemptions allowed per user per stream (see the max_per_user_per_stream field).
|
||||
// The default is false.
|
||||
IsMaxPerUserPerStreamEnabled *bool `json:"is_max_per_user_per_stream_enabled"`
|
||||
|
||||
// The maximum number of redemptions allowed per user per stream. Applied only if is_max_per_user_per_stream_enabled is true.
|
||||
// The minimum value is 1.
|
||||
MaxPerUserPerStream *int `json:"max_per_user_per_stream"`
|
||||
|
||||
// A Boolean value that determines whether to apply a cooldown period between redemptions (see the global_cooldown_seconds field for the duration of the cooldown period).
|
||||
// The default is false.
|
||||
IsGlobalCooldownEnabled *bool `json:"is_global_cooldown_enabled"`
|
||||
|
||||
// The cooldown period, in seconds. Applied only if the is_global_cooldown_enabled field is true.
|
||||
// The minimum value is 1; however, the minimum value is 60 for it to be shown in the Twitch UX.
|
||||
GlobalCooldownSeconds *int `json:"global_cooldown_seconds"`
|
||||
|
||||
// A Boolean value that determines whether redemptions should be set to FULFILLED status immediately when a reward is redeemed.
|
||||
// If false, status is set to UNFULFILLED and follows the normal request queue process.
|
||||
// The default is false.
|
||||
ShouldRedemptionsSkipRequestQueue *bool `json:"should_redemptions_skip_request_queue"`
|
||||
}
|
||||
|
||||
type UpdateCustomRewardResponse struct {
|
||||
// The list contains the single reward that you updated.
|
||||
Data []CustomReward `json:"data"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (c *ChannelPoints) UpdateCustomReward(params *UpdateCustomRewardParams, req *UpdateCustomRewardRequest) (*UpdateCustomRewardResponse, error) {
|
||||
v, _ := query.Values(req)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards", RawQuery: v.Encode()})
|
||||
|
||||
r, w := io.Pipe()
|
||||
|
||||
go func() {
|
||||
if err := json.NewEncoder(w).Encode(req); err != nil {
|
||||
w.CloseWithError(err)
|
||||
} else {
|
||||
w.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
resp, err := c.client.Do(&http.Request{
|
||||
Method: http.MethodPatch,
|
||||
URL: endpoint,
|
||||
Body: r,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data UpdateCustomRewardResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package channelpoints
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
type UpdateRedemptionStatusParams struct {
|
||||
// A list of IDs that identify the redemptions to update. To specify more than one ID, include this parameter for each redemption you want to update.
|
||||
// For example, id=1234&id=5678. You may specify a maximum of 50 IDs.
|
||||
IDs []string `url:"id"`
|
||||
|
||||
// The ID of the broadcaster that’s updating the redemption. This ID must match the user ID in the user access token.
|
||||
BroadcasterID string `url:"broadcaster_id"`
|
||||
|
||||
// The ID that identifies the reward that’s been redeemed.
|
||||
RewardID string `url:"reward_id"`
|
||||
}
|
||||
|
||||
type UpdateRedemptionStatusRequest struct {
|
||||
// The status to set the redemption to. Possible values are:
|
||||
//
|
||||
// CANCELED, FULFILLED
|
||||
//
|
||||
// Setting the status to CANCELED refunds the user’s channel points.
|
||||
Status RewardRedemptionStatus `json:"status"`
|
||||
}
|
||||
|
||||
type UpdateRedemptionStatusResponse struct {
|
||||
// The list contains the single redemption that you updated.
|
||||
Data []CustomRewardRedemption `json:"data"`
|
||||
}
|
||||
|
||||
// Updates a redemption’s status. You may update a redemption only if its status is UNFULFILLED.
|
||||
// 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.
|
||||
func (c *ChannelPoints) UpdateRedemptionStatus(params *UpdateRedemptionStatusParams, req *UpdateRedemptionStatusRequest) (*UpdateRedemptionStatusResponse, error) {
|
||||
v, _ := query.Values(req)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channel_points/custom_rewards/redemptions", RawQuery: v.Encode()})
|
||||
|
||||
r, w := io.Pipe()
|
||||
|
||||
go func() {
|
||||
if err := json.NewEncoder(w).Encode(req); err != nil {
|
||||
w.CloseWithError(err)
|
||||
} else {
|
||||
w.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
resp, err := c.client.Do(&http.Request{
|
||||
Method: http.MethodPatch,
|
||||
URL: endpoint,
|
||||
Body: r,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data UpdateRedemptionStatusResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package channels
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type Channels struct {
|
||||
client *http.Client
|
||||
baseUrl *url.URL
|
||||
}
|
||||
|
||||
func New(client *http.Client, baseUrl *url.URL) *Channels {
|
||||
return &Channels{
|
||||
client: client,
|
||||
baseUrl: baseUrl,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package channels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GetChannelEditorsResponse struct {
|
||||
// A list of users that are editors for the specified broadcaster. The list is empty if the broadcaster doesn’t have editors.
|
||||
Data []ChannelEditor `json:"data"`
|
||||
}
|
||||
|
||||
type ChannelEditor struct {
|
||||
// An ID that uniquely identifies a user with editor permissions.
|
||||
UserID string `json:"user_id"`
|
||||
|
||||
// The user’s display name.
|
||||
UserName string `json:"user_name"`
|
||||
|
||||
// The date and time, in RFC3339 format, when the user became one of the broadcaster’s editors.
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// Gets the broadcaster’s list editors.
|
||||
//
|
||||
// Requires a user access token that includes the channel:read:editors scope.
|
||||
func (c *Channels) GetChannelEditors(broadcasterID string) (*GetChannelEditorsResponse, error) {
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels/editors", RawQuery: "broadcaster_id=" + broadcasterID})
|
||||
|
||||
resp, err := c.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetChannelEditorsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package channels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetChannelFollowersParams struct {
|
||||
// A user’s ID. Use this parameter to see whether the user follows this broadcaster.
|
||||
// If specified, the response contains this user if they follow the broadcaster.
|
||||
// If not specified, the response contains all users that follow the broadcaster.
|
||||
//
|
||||
// Using this parameter requires both a user access token with the moderator:read:followers scope and the user ID
|
||||
// in the access token match the broadcaster_id or be the user ID for a moderator of the specified broadcaster.
|
||||
UserID *string `url:"user_id"`
|
||||
|
||||
// The broadcaster’s ID. Returns the list of users that follow this broadcaster.
|
||||
BroadcasterID string `url:"broadcaster_id"`
|
||||
|
||||
// The maximum number of items to return per page in the response. The minimum page size is 1 item per page and the maximum is 100. The default is 20.
|
||||
First *int `url:"first,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
|
||||
After *types.Cursor `url:"after,omitempty"`
|
||||
}
|
||||
|
||||
type GetChannelFollowersResponse struct {
|
||||
// The list of users that follow the specified broadcaster. The list is in descending order by followed_at (with the most recent follower first).
|
||||
// The list is empty if nobody follows the broadcaster, the specified user_id isn’t in the follower list,
|
||||
// the user access token is missing the moderator:read:followers scope, or the user isn’t the broadcaster or moderator for the channel.
|
||||
Data []ChannelFollower `json:"data"`
|
||||
|
||||
// Contains 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"`
|
||||
|
||||
// The total number of users that follow this broadcaster.
|
||||
// As someone pages through the list, the number of users may change as users follow or unfollow the broadcaster.
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type ChannelFollower struct {
|
||||
// An ID that uniquely identifies the user that’s following the broadcaster.
|
||||
UserID string `json:"user_id"`
|
||||
|
||||
// The user’s login name.
|
||||
UserLogin string `json:"user_login"`
|
||||
|
||||
// The user’s display name.
|
||||
UserName string `json:"user_name"`
|
||||
|
||||
// The UTC timestamp when the user started following the broadcaster.
|
||||
FollowedAt time.Time `json:"followed_at"`
|
||||
}
|
||||
|
||||
// Gets a list of users that follow the specified broadcaster. You can also use this endpoint to see whether a specific user follows the broadcaster.
|
||||
//
|
||||
// - Requires a user access token that includes the moderator:read:followers scope.
|
||||
//
|
||||
// - The ID in the broadcaster_id query parameter must match the user ID in the access token or the user ID in the access token must be a moderator for the specified broadcaster.
|
||||
//
|
||||
// This endpoint will return specific follower information only if both of the above are true.
|
||||
// If a scope is not provided or the user isn’t the broadcaster or a moderator for the specified channel,
|
||||
// only the total follower count will be included in the response.
|
||||
func (c *Channels) GetChannelFollowers(params *GetChannelFollowersParams) (*GetChannelFollowersResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels/followers", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := c.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetChannelFollowersResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package channels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetChannelInformationParams struct {
|
||||
// The ID of the broadcaster whose channel you want to get. To specify more than one ID, include this parameter for each broadcaster you want to get.
|
||||
// For example, broadcaster_id=1234&broadcaster_id=5678. You may specify a maximum of 100 IDs. The API ignores duplicate IDs and IDs that are not found.
|
||||
BroadcasterIDs []string `url:"broadcaster_id,omitempty"`
|
||||
}
|
||||
|
||||
type GetChannelInformdationResponse struct {
|
||||
// A list that contains information about the specified channels. The list is empty if the specified channels weren’t found.
|
||||
Data []ChannelInformation `json:"data"`
|
||||
}
|
||||
|
||||
type ChannelInformation struct {
|
||||
// An ID that uniquely identifies the broadcaster.
|
||||
BroadcasterID string `json:"broadcaster_id"`
|
||||
|
||||
// The broadcaster’s login name.
|
||||
BroadcasterLogin string `json:"broadcaster_login"`
|
||||
|
||||
// The broadcaster’s display name.
|
||||
BroadcasterName string `json:"broadcaster_name"`
|
||||
|
||||
// The broadcaster’s preferred language. The value is an ISO 639-1 two-letter language code (for example, en for English).
|
||||
// The value is set to “other” if the language is not a Twitch supported language.
|
||||
BroadcasterLanguage string `json:"broadcaster_language"`
|
||||
|
||||
// The name of the game that the broadcaster is playing or last played. The value is an empty string if the broadcaster has never played a game.
|
||||
GameName string `json:"game_name"`
|
||||
|
||||
// An ID that uniquely identifies the game that the broadcaster is playing or last played.
|
||||
// The value is an empty string if the broadcaster has never played a game.
|
||||
GameID string `json:"game_id"`
|
||||
|
||||
// The title of the stream that the broadcaster is currently streaming or last streamed. The value is an empty string if the broadcaster has never streamed.
|
||||
Title string `json:"title"`
|
||||
|
||||
// The value of the broadcaster’s stream delay setting, in seconds.
|
||||
// This field’s value defaults to zero unless
|
||||
//
|
||||
// 1) the request specifies a user access token
|
||||
//
|
||||
// 2) the ID in the broadcaster_id query parameter matches the user ID in the access token
|
||||
//
|
||||
// 3) the broadcaster has partner status and they set a non-zero stream delay value.
|
||||
Delay uint `json:"delay"`
|
||||
|
||||
// The tags applied to the channel.
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
// The CCLs applied to the channel.
|
||||
ContentClassficationLabels []types.CCL `json:"content_classification"`
|
||||
|
||||
// Boolean flag indicating if the channel has branded content.
|
||||
IsBrandedContent bool `json:"is_branded_content"`
|
||||
}
|
||||
|
||||
// Gets information about one or more channels.
|
||||
//
|
||||
// Requires an app access token or user access token.
|
||||
func (c *Channels) GetChannelInformation(params *GetChannelInformationParams) (*GetChannelInformdationResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := c.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetChannelInformdationResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package channels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type GetFollowedChannelsParams struct {
|
||||
// A user’s ID. Returns the list of broadcasters that this user follows. This ID must match the user ID in the user OAuth token.
|
||||
UserID string `url:"user_id"`
|
||||
|
||||
// A broadcaster’s ID. Use this parameter to see whether the user follows this broadcaster.
|
||||
// If specified, the response contains this broadcaster if the user follows them.
|
||||
// If not specified, the response contains all broadcasters that the user follows.
|
||||
BroadcasterID *string `url:"broadcaster_id,omitempty"`
|
||||
|
||||
// The maximum number of items to return per page in the response. The minimum page size is 1 item per page and the maximum is 100. The default is 20.
|
||||
First *int `url:"first,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
|
||||
After *types.Cursor `url:"after,omitempty"`
|
||||
}
|
||||
|
||||
type GetFollowedChannelsResponse struct {
|
||||
// The list of broadcasters that the user follows.
|
||||
// The list is in descending order by followed_at (with the most recently followed broadcaster first)
|
||||
// The list is empty if the user doesn’t follow anyone.
|
||||
Data []FollowedChannel `json:"data"`
|
||||
|
||||
// Contains 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"`
|
||||
|
||||
// The total number of broadcasters that the user follows.
|
||||
// As someone pages through the list, the number may change as the user follows or unfollows broadcasters.
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type FollowedChannel struct {
|
||||
// An ID that uniquely identifies the broadcaster that this user is following.
|
||||
BroadcasterID string `json:"broadcaster_id"`
|
||||
|
||||
// The broadcaster’s login name.
|
||||
BroadcasterLogin string `json:"broadcaster_login"`
|
||||
|
||||
// The broadcaster’s display name.
|
||||
BroadcasterName string `json:"broadcaster_name"`
|
||||
|
||||
// The UTC timestamp when the user started following the broadcaster.
|
||||
FollowedAt time.Time `json:"followed_at"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (c *Channels) GetFollowedChannels(params *GetFollowedChannelsParams) (*GetFollowedChannelsResponse, error) {
|
||||
v, _ := query.Values(params)
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "users/follows", RawQuery: v.Encode()})
|
||||
|
||||
resp, err := c.client.Get(endpoint.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var data GetFollowedChannelsResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package channels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
type ModifyChannelInformationRequest struct {
|
||||
// The ID of the game that the user plays. The game is not updated if the ID isn’t a game ID that Twitch recognizes.
|
||||
// To unset this field, use “0” or “” (an empty string).
|
||||
GameID *string `json:"game_id,omitempty"`
|
||||
|
||||
// The user’s preferred language. Set the value to an ISO 639-1 two-letter language code (for example, en for English).
|
||||
// Set to “other” if the user’s preferred language is not a Twitch supported language.
|
||||
// The language isn’t updated if the language code isn’t a Twitch supported language.
|
||||
Language *string `json:"language,omitempty"`
|
||||
|
||||
// The title of the user’s stream. You may not set this field to an empty string.
|
||||
Title *string `json:"title,omitempty"`
|
||||
|
||||
// The number of seconds you want your broadcast buffered before streaming it live. The delay helps ensure fairness during competitive play.
|
||||
// Only users with Partner status may set this field. The maximum delay is 900 seconds (15 minutes).
|
||||
Delay *int `json:"delay,omitempty"`
|
||||
|
||||
// A list of channel-defined tags to apply to the channel. To remove all tags from the channel, set tags to an empty array.
|
||||
// Tags help identify the content that the channel streams. Learn More: https://help.twitch.tv/s/article/guide-to-tags
|
||||
//
|
||||
// A channel may specify a maximum of 10 tags.
|
||||
// Each tag is limited to a maximum of 25 characters and may not be an empty string or contain spaces or special characters.
|
||||
// Tags are case insensitive. For readability, consider using camelCasing or PascalCasing.
|
||||
Tags *[]string `json:"tags,omitempty"`
|
||||
|
||||
// List of labels that should be set as the Channel’s CCLs.
|
||||
ContentClassificationLabels []ModifyContentClassificationLabel `json:"content_classification_labels,omitempty"`
|
||||
|
||||
// Boolean flag indicating if the channel has branded content.
|
||||
IsBrandedContent *bool `json:"is_branded_content,omitempty"`
|
||||
}
|
||||
|
||||
type ModifyContentClassificationLabel struct {
|
||||
// ID of the Content Classification Labels that must be added/removed from the channel.
|
||||
ID types.CCL `json:"id"`
|
||||
|
||||
// Boolean flag indicating whether the label should be enabled (true) or disabled for the channel.
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
}
|
||||
|
||||
// Updates a channel’s properties.
|
||||
//
|
||||
// Requires a user access token that includes the channel:manage:broadcast scope.
|
||||
func (c *Channels) ModifyChannelInformation(broadcasterID string, req *ModifyChannelInformationRequest) error {
|
||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "channels", RawQuery: "broadcaster_id=" + broadcasterID})
|
||||
|
||||
r, w := io.Pipe()
|
||||
|
||||
go func() {
|
||||
if err := json.NewEncoder(w).Encode(req); err != nil {
|
||||
w.CloseWithError(err)
|
||||
} else {
|
||||
w.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
_, err := c.client.Do(&http.Request{
|
||||
Method: http.MethodPatch,
|
||||
URL: endpoint,
|
||||
Body: r,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package eventsub
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type EventSub struct {
|
||||
client *http.Client
|
||||
baseUrl *url.URL
|
||||
}
|
||||
|
||||
func New(client *http.Client, baseUrl *url.URL) *EventSub {
|
||||
return &EventSub{
|
||||
client: client,
|
||||
baseUrl: baseUrl,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package eventsub
|
||||
|
||||
import "time"
|
||||
|
||||
type Condition map[string]any
|
||||
|
||||
type Subscription struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Condition Condition `json:"condition"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Transport *Transport `json:"transport"`
|
||||
Cost int `json:"cost"`
|
||||
}
|
||||
|
||||
type Status string
|
||||
|
||||
const (
|
||||
StatusEnabled = "enabled"
|
||||
StatusWebhookCallbackVerificationPending = "webhook_callback_verification_pending"
|
||||
StatusWebhookCallbackVerificationFailed = "webhook_callback_verification_failed"
|
||||
NotificationFailuresExceeded = "notification_failures_exceeded"
|
||||
AuthorizationRevoked = "authorization_revoked"
|
||||
ModeratorRemoved = "moderator_removed"
|
||||
USerRemoved = "user_removed"
|
||||
VersionRemoved = "version_removed"
|
||||
BetaMaintenance = "beta_maintenance"
|
||||
WebsocketDisconnected = "websocket_disconnected"
|
||||
WebsocketFailedPingPong = "websocket_failed_ping_pong"
|
||||
WebsocketReceivedInboundTraffic = "websocket_received_inbound_traffic"
|
||||
WebsocketConnectionUnused = "websocket_connection_unused"
|
||||
WebsocketInternalError = "websocket_internal_error"
|
||||
WebsocketNetworkTimeout = "websocket_network_timeout"
|
||||
WebsocketnetworkError = "websocket_network_error"
|
||||
)
|
||||
|
||||
type Transport struct {
|
||||
Method string `json:"method"`
|
||||
Callback *string `json:"callback,omitempty"`
|
||||
Secret *string `json:"secret,omitempty"`
|
||||
SessionID *string `json:"session_id,omitempty"`
|
||||
ConduitID *string `json:"conduit_id,omitempty"`
|
||||
}
|
||||
|
||||
func WebhookTransport(callback string, secret string) *Transport {
|
||||
return &Transport{
|
||||
Method: "webhook",
|
||||
Callback: &callback,
|
||||
Secret: &secret,
|
||||
SessionID: nil,
|
||||
ConduitID: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func WebSocketTransport(sessionID string) *Transport {
|
||||
return &Transport{
|
||||
Method: "websocket",
|
||||
Callback: nil,
|
||||
Secret: nil,
|
||||
SessionID: &sessionID,
|
||||
ConduitID: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func ConduitTransport(conduitID string) *Transport {
|
||||
return &Transport{
|
||||
Method: "websocket",
|
||||
Callback: nil,
|
||||
Secret: nil,
|
||||
SessionID: nil,
|
||||
ConduitID: &conduitID,
|
||||
}
|
||||
}
|
||||
|
||||
type SubscriptionType struct {
|
||||
Name string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
var (
|
||||
ChannelUpdate = SubscriptionType{Name: "channel.update", Version: "2"}
|
||||
ChannelFollow = SubscriptionType{Name: "channel.follow", Version: "2"}
|
||||
ChannelAdBreakBegin = SubscriptionType{Name: "channel.ad_break_begin", Version: "1"}
|
||||
ChannelChatClear = SubscriptionType{Name: "channel.chat.clear", Version: "1"}
|
||||
ChannelChatClearUserMessages = SubscriptionType{Name: "channel.chat.clear_user_messages", Version: "1"}
|
||||
ChannelChatMessage = SubscriptionType{Name: "channel.chat.message", Version: "1"}
|
||||
ChannelChatMessageDelete = SubscriptionType{Name: "channel.chat.message_delete", Version: "1"}
|
||||
ChannelChatNotification = SubscriptionType{Name: "channel.chat.notification", Version: "1"}
|
||||
ChannelChatSettingsUpdate = SubscriptionType{Name: "channel.chat_settings.update", Version: "beta"}
|
||||
ChannelSubscribe = SubscriptionType{Name: "channel.subscribe", Version: "1"}
|
||||
ChannelSubscriptionEnd = SubscriptionType{Name: "channel.subscription.end", Version: "1"}
|
||||
ChannelSubscriptionGift = SubscriptionType{Name: "channel.subscription.gift", Version: "1"}
|
||||
ChannelSubscriptionMessage = SubscriptionType{Name: "channel.subscription.message", Version: "1"}
|
||||
ChannelCheer = SubscriptionType{Name: "channel.cheer", Version: "1"}
|
||||
ChannelRaid = SubscriptionType{Name: "channel.raid", Version: "1"}
|
||||
ChannelBan = SubscriptionType{Name: "channel.ban", Version: "1"}
|
||||
ChannelUnban = SubscriptionType{Name: "channel.unban", Version: "1"}
|
||||
ChannelModeratorAdd = SubscriptionType{Name: "channel.moderator.add", Version: "1"}
|
||||
ChannelModeratorRemove = SubscriptionType{Name: "channel.moderator.remove", Version: "1"}
|
||||
ChannelGuestStarSessionBegin = SubscriptionType{Name: "channel.guest_star_session.begin", Version: "beta"}
|
||||
ChannelGuestStarSessionEnd = SubscriptionType{Name: "channel.guest_star_session.end", Version: "beta"}
|
||||
ChannelGuestStarGuestUpdate = SubscriptionType{Name: "channel.guest_star_guest.update", Version: "beta"}
|
||||
ChannelGuestStarSettingsUpdate = SubscriptionType{Name: "channel.guest_star_settings.update", Version: "beta"}
|
||||
ChannelPointsCustomRewardAdd = SubscriptionType{Name: "channel.channel_points_custom_reward.add", Version: "1"}
|
||||
ChannelPointsCustomRewardUpdate = SubscriptionType{Name: "channel.channel_points_custom_reward.update", Version: "1"}
|
||||
ChannelPointsCustomRewardRemove = SubscriptionType{Name: "channel.channel_points_custom_reward.remove", Version: "1"}
|
||||
ChannelPointsCustomRewardRedemptionAdd = SubscriptionType{Name: "channel.channel_points_custom_reward_redemption.add", Version: "1"}
|
||||
ChannelPointsCustomRewardRedemptionUpdate = SubscriptionType{Name: "channel.channel_points_custom_reward_redemption.update", Version: "1"}
|
||||
ChannelPollBegin = SubscriptionType{Name: "channel.poll.begin", Version: "1"}
|
||||
ChannelPollProgress = SubscriptionType{Name: "channel.poll.progress", Version: "1"}
|
||||
ChannelPollEnd = SubscriptionType{Name: "channel.poll.end", Version: "1"}
|
||||
ChannelPredictionBegin = SubscriptionType{Name: "channel.prediction.begin", Version: "1"}
|
||||
ChannelPredictionProgress = SubscriptionType{Name: "channel.prediction.progress", Version: "1"}
|
||||
ChannelPredictionLock = SubscriptionType{Name: "channel.prediction.lock", Version: "1"}
|
||||
ChannelPredictionEnd = SubscriptionType{Name: "channel.prediction.end", Version: "1"}
|
||||
CharityCampaignDonate = SubscriptionType{Name: "channel.charity_campaign.donate", Version: "1"}
|
||||
CharityCampaignStart = SubscriptionType{Name: "channel.charity_campaign.start", Version: "1"}
|
||||
CharityCampaignProgress = SubscriptionType{Name: "channel.charity_campaign.progress", Version: "1"}
|
||||
CharityCampaignStop = SubscriptionType{Name: "channel.charity_campaign.stop", Version: "1"}
|
||||
ConduitShardDisabled = SubscriptionType{Name: "conduit.shard.disabled", Version: "1"}
|
||||
DropEntitlementGrant = SubscriptionType{Name: "drop.entitlement.grant", Version: "1"}
|
||||
ExtensionBitsTransactionCreate = SubscriptionType{Name: "extension.bits.transaction.create", Version: "1"}
|
||||
GoalBegin = SubscriptionType{Name: "goal.begin", Version: "1"}
|
||||
GoalProgress = SubscriptionType{Name: "goal.progress", Version: "1"}
|
||||
GoalEnd = SubscriptionType{Name: "goal.end", Version: "1"}
|
||||
HypeTrainBegin = SubscriptionType{Name: "hype_train.begin", Version: "1"}
|
||||
HypeTrainProgress = SubscriptionType{Name: "hype_train.progress", Version: "1"}
|
||||
HypeTrainEnd = SubscriptionType{Name: "hype_train.end", Version: "1"}
|
||||
ShieldModeBegin = SubscriptionType{Name: "shield_mode.begin", Version: "1"}
|
||||
ShieldModeEnd = SubscriptionType{Name: "shield_mode.end", Version: "1"}
|
||||
ShoutoutCreate = SubscriptionType{Name: "shoutout.create", Version: "1"}
|
||||
ShoutoutReceived = SubscriptionType{Name: "shoutout.received", Version: "1"}
|
||||
StreamOnline = SubscriptionType{Name: "stream.online", Version: "1"}
|
||||
StreamOffline = SubscriptionType{Name: "stream.offline", Version: "1"}
|
||||
UserAuthorizationGrant = SubscriptionType{Name: "user.authorization.grant", Version: "1"}
|
||||
UserAuthorizationRevoke = SubscriptionType{Name: "user.authorization.revoke", Version: "1"}
|
||||
UserUpdate = SubscriptionType{Name: "user.update", Version: "1"}
|
||||
)
|
|
@ -0,0 +1,66 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cursor string
|
||||
|
||||
func (c Cursor) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
func (c *Cursor) UnmarshalText(text []byte) error {
|
||||
*c = Cursor(string(text))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Cursor) MarshalText() ([]byte, error) {
|
||||
return []byte(string(c)), nil
|
||||
}
|
||||
|
||||
func (c *Cursor) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*c = Cursor(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Cursor) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(string(c))
|
||||
}
|
||||
|
||||
// Contains the information used to page through the list of results.
|
||||
// The object is empty if there are no more pages left to page through.
|
||||
type Pagination struct {
|
||||
// The cursor used to get the next page of results. Use the cursor to set the request’s after query parameter.
|
||||
Cursor Cursor `json:"cursor"`
|
||||
}
|
||||
|
||||
type DateRange struct {
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
EndedAt time.Time `json:"ended_at"`
|
||||
}
|
||||
|
||||
// Content Classification Label
|
||||
type CCL string
|
||||
|
||||
const (
|
||||
CCLDrugsIntoxication CCL = "DrugsIntoxication"
|
||||
CCLSexualThemes CCL = "SexualThemes"
|
||||
CCLViolentGraphic CCL = "ViolentGraphic"
|
||||
CCLGambling CCL = "Gambling"
|
||||
CCLProfanityVulgarity CCL = "ProfanityVulgarity"
|
||||
)
|
||||
|
||||
// Sort Order
|
||||
type SortOrder string
|
||||
|
||||
const (
|
||||
SortOrderOldest SortOrder = "OLDEST"
|
||||
SortOrderNewest SortOrder = "NEWEST"
|
||||
)
|
|
@ -0,0 +1,48 @@
|
|||
package eventsub
|
||||
|
||||
import (
|
||||
"go.fifitido.net/twitch/api"
|
||||
"go.fifitido.net/twitch/api/eventsub"
|
||||
)
|
||||
|
||||
type EventSub struct {
|
||||
api *api.API
|
||||
transport *eventsub.Transport
|
||||
|
||||
subscriptions map[string]*eventsub.Subscription
|
||||
}
|
||||
|
||||
func New(api *api.API, trans *eventsub.Transport) *EventSub {
|
||||
return &EventSub{
|
||||
api: api,
|
||||
transport: trans,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EventSub) Subscribe(subType eventsub.SubscriptionType, cond eventsub.Condition) error {
|
||||
res, err := e.api.EventSub.CreateSubscription(&eventsub.CreateSubscriptionRequest{
|
||||
SubscriptionType: subType,
|
||||
Condition: cond,
|
||||
Transport: e.transport,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sub := range res.Data {
|
||||
e.subscriptions[sub.ID] = sub
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EventSub) Close() error {
|
||||
for _, sub := range e.subscriptions {
|
||||
if err := e.api.EventSub.DeleteSubscription(sub.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module go.fifitido.net/twitch
|
||||
|
||||
go 1.21.7
|
||||
|
||||
require github.com/google/go-querystring v1.1.0
|
|
@ -0,0 +1,5 @@
|
|||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
@ -0,0 +1,152 @@
|
|||
package twitch
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.fifitido.net/twitch/api/bits"
|
||||
"go.fifitido.net/twitch/api/channelpoints"
|
||||
"go.fifitido.net/twitch/api/types"
|
||||
)
|
||||
|
||||
func String(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func ToString(s *string) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
||||
|
||||
func StringSlice(s []string) *[]string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func ToStringSlice(s *[]string) []string {
|
||||
if s == nil {
|
||||
return []string{}
|
||||
}
|
||||
return *s
|
||||
}
|
||||
|
||||
func Bool(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
func ToBool(b *bool) bool {
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
return *b
|
||||
}
|
||||
|
||||
func Int(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
func ToInt(i *int) int {
|
||||
if i == nil {
|
||||
return 0
|
||||
}
|
||||
return *i
|
||||
}
|
||||
|
||||
func Int64(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
func ToInt64(i *int64) int64 {
|
||||
if i == nil {
|
||||
return 0
|
||||
}
|
||||
return *i
|
||||
}
|
||||
|
||||
func Int32(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
func ToInt32(i *int32) int32 {
|
||||
if i == nil {
|
||||
return 0
|
||||
}
|
||||
return *i
|
||||
}
|
||||
|
||||
func Float32(f float32) *float32 {
|
||||
return &f
|
||||
}
|
||||
|
||||
func ToFloat32(f *float32) float32 {
|
||||
if f == nil {
|
||||
return 0
|
||||
}
|
||||
return *f
|
||||
}
|
||||
|
||||
func Float64(f float64) *float64 {
|
||||
return &f
|
||||
}
|
||||
|
||||
func ToFloat64(f *float64) float64 {
|
||||
if f == nil {
|
||||
return 0
|
||||
}
|
||||
return *f
|
||||
}
|
||||
|
||||
func Time(t time.Time) *time.Time {
|
||||
return &t
|
||||
}
|
||||
|
||||
func ToTime(t *time.Time) time.Time {
|
||||
if t == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return *t
|
||||
}
|
||||
|
||||
func Cursor(c types.Cursor) *types.Cursor {
|
||||
return &c
|
||||
}
|
||||
|
||||
func ToCursor(c *types.Cursor) types.Cursor {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return *c
|
||||
}
|
||||
|
||||
func BitsPeriod(p bits.Period) *bits.Period {
|
||||
return &p
|
||||
}
|
||||
|
||||
func ToBitsPeriod(p *bits.Period) bits.Period {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
return *p
|
||||
}
|
||||
|
||||
func RewardRedemptionStatus(s channelpoints.RewardRedemptionStatus) *channelpoints.RewardRedemptionStatus {
|
||||
return &s
|
||||
}
|
||||
|
||||
func ToRewardRedemptionStatus(s *channelpoints.RewardRedemptionStatus) channelpoints.RewardRedemptionStatus {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
||||
|
||||
func SortOrder(s types.SortOrder) *types.SortOrder {
|
||||
return &s
|
||||
}
|
||||
|
||||
func ToSortOrder(s *types.SortOrder) types.SortOrder {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
Loading…
Reference in New Issue