Initial commit

This commit is contained in:
Evan Fiordeliso 2024-02-27 22:13:57 -05:00
commit 422102ccee
37 changed files with 2335 additions and 0 deletions

7
api.go Normal file
View File

@ -0,0 +1,7 @@
package twitch
import "go.fifitido.net/twitch/api"
func NewAPI() *api.API {
return api.New()
}

18
api/ads/ads.go Normal file
View File

@ -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,
}
}

View File

@ -0,0 +1,55 @@
package ads
import (
"encoding/json"
"net/url"
"time"
)
type GetAdScheduleResponse struct {
// A list that contains information related to the channels 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 broadcasters 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 broadcasters 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
}

46
api/ads/snooze_next_ad.go Normal file
View File

@ -0,0 +1,46 @@
package ads
import (
"encoding/json"
"net/url"
"time"
)
type SnoozeNextAdResponse struct {
// A list that contains information about the channels 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 broadcasters 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 dashboards 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
}

View File

@ -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 thats 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 thats 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 broadcasters editors and moderators may not start commercials on behalf of the broadcaster.
//
// Requires a user access token that includes the channel:edit:commercial scope.
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
}

View File

@ -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,
}
}

View File

@ -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 cursors 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 windows 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
}

View File

@ -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 games 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 users 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 windows 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 todays date. If you specify an earlier date,
// the API ignores it and uses a date thats one year prior to todays date. If you dont 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 windows 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 thats 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 cursors 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 windows 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
}

44
api/api.go Normal file
View File

@ -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),
}
}

38
api/bits/bits.go Normal file
View File

@ -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"
)

View File

@ -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 leaderboards top leaders, dont 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 theyve cheered.
// The array is empty if nobody has cheered bits.
Data []LeaderboardEntry `json:"data"`
// The reporting windows start and end dates, in RFC3339 format. The dates are calculated by using the started_at and period query parameters.
// If you dont 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 users login name.
UserLogin string `json:"user_login"`
// The users display name.
UserName string `json:"user_name"`
// The users 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
}

View File

@ -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 fields 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 channels 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
}

View File

@ -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 cursors 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 broadcasters login name.
BroadcasterLogin string `json:"broadcaster_login"`
// The broadcasters 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 products 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 extensions 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
}

View File

@ -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,
}
}

View File

@ -0,0 +1,96 @@
package channelpoints
import (
"encoding/json"
"io"
"net/url"
)
type CreateCustomRewardsRequest struct {
// The custom rewards title. The title may contain a maximum of 45 characters and it must be unique amongst all of the broadcasters 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 broadcasters channel. The maximum number of custom rewards per channel is 50, which includes both enabled and disabled rewards.
//
// Requires a user access token that includes the channel:manage:redemptions scope.
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
}

View File

@ -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 rewards redemption status is UNFULFILLED at the time the reward is deleted, its redemption status is marked as FULFILLED.
//
// / Requires a user access token that includes the channel:manage:redemptions scope.
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
}

View File

@ -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 hasnt 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
}

View File

@ -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 dont specify the id query parameter.
//
// NOTE: Canceled and fulfilled redemptions are returned for only a few days after theyre 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 cursors 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
}

153
api/channelpoints/modals.go Normal file
View File

@ -0,0 +1,153 @@
package channelpoints
import "time"
type CustomReward struct {
// The ID that uniquely identifies the broadcaster.
BroadcasterID string `json:"broadcaster_id"`
// The broadcasters login name.
BroadcasterLogin string `json:"broadcaster_login"`
// The broadcasters 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 didnt 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 arent 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 cant 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 cant 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 broadcasters stream isnt live or max_per_stream_setting isnt enabled.
RedemptionsRedeemedCurrentStream *int `json:"redemptions_redeemed_current_stream"`
// The timestamp of when the cooldown period expires. Is null if the reward isnt 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 rewards cost, in Channel Points.
Cost int64 `json:"cost"`
} `json:"reward"`
}

View File

@ -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 thats 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 rewards title. The title may contain a maximum of 45 characters and it must be unique amongst all of the broadcasters 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
}

View File

@ -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 thats 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 thats 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 users channel points.
Status RewardRedemptionStatus `json:"status"`
}
type UpdateRedemptionStatusResponse struct {
// The list contains the single redemption that you updated.
Data []CustomRewardRedemption `json:"data"`
}
// Updates a redemptions 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
}

18
api/channels/channels.go Normal file
View File

@ -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,
}
}

View File

@ -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 doesnt 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 users display name.
UserName string `json:"user_name"`
// The date and time, in RFC3339 format, when the user became one of the broadcasters editors.
CreatedAt time.Time `json:"created_at"`
}
// Gets the broadcasters 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
}

View File

@ -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 users 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 broadcasters 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 cursors 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 isnt in the follower list,
// the user access token is missing the moderator:read:followers scope, or the user isnt 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 thats following the broadcaster.
UserID string `json:"user_id"`
// The users login name.
UserLogin string `json:"user_login"`
// The users 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 isnt 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
}

View File

@ -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 werent found.
Data []ChannelInformation `json:"data"`
}
type ChannelInformation struct {
// An ID that uniquely identifies the broadcaster.
BroadcasterID string `json:"broadcaster_id"`
// The broadcasters login name.
BroadcasterLogin string `json:"broadcaster_login"`
// The broadcasters display name.
BroadcasterName string `json:"broadcaster_name"`
// The broadcasters 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 broadcasters stream delay setting, in seconds.
// This fields 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
}

View File

@ -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 users 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 broadcasters 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 cursors 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 doesnt 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 broadcasters login name.
BroadcasterLogin string `json:"broadcaster_login"`
// The broadcasters 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
}

View File

@ -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 isnt a game ID that Twitch recognizes.
// To unset this field, use “0” or “” (an empty string).
GameID *string `json:"game_id,omitempty"`
// The users preferred language. Set the value to an ISO 639-1 two-letter language code (for example, en for English).
// Set to “other” if the users preferred language is not a Twitch supported language.
// The language isnt updated if the language code isnt a Twitch supported language.
Language *string `json:"language,omitempty"`
// The title of the users 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 Channels 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 channels 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
}

View File

@ -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
}

View File

@ -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
}

18
api/eventsub/eventsub.go Normal file
View File

@ -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,
}
}

View File

@ -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
}

140
api/eventsub/models.go Normal file
View File

@ -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"}
)

66
api/types/types.go Normal file
View File

@ -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 requests 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"
)

48
eventsub/eventsub.go Normal file
View File

@ -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
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module go.fifitido.net/twitch
go 1.21.7
require github.com/google/go-querystring v1.1.0

5
go.sum Normal file
View File

@ -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=

152
ptr_types.go Normal file
View File

@ -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
}