1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00

Allow specifying k8s context in configuration

This commit is contained in:
davidsbond
2020-01-17 20:15:40 +00:00
parent e46d9eb02c
commit 1acaeb6ef8
376 changed files with 34823 additions and 2171 deletions

2
vendor/github.com/nicklaw5/helix/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
.DS_Store
debug.test

17
vendor/github.com/nicklaw5/helix/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,17 @@
language: go
sudo: false
go:
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
script:
- go test -v -parallel=10 -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci

21
vendor/github.com/nicklaw5/helix/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Nicholas Law
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

103
vendor/github.com/nicklaw5/helix/README.md generated vendored Normal file
View File

@@ -0,0 +1,103 @@
# helix
A Twitch Helix API client written in Go (Golang).
[![Build Status](https://travis-ci.org/nicklaw5/helix.svg?branch=master)](https://travis-ci.org/nicklaw5/helix)
[![Coverage Status](https://coveralls.io/repos/github/nicklaw5/helix/badge.svg)](https://coveralls.io/github/nicklaw5/helix)
## Package Status
This project is a work in progress. Twitch has not finished all available endpoints/features for the Helix API, but as these get released they are likely to be implemented in this package.
## Documentation & Examples
All documentation and usage examples for this package can be found in the [docs directory](docs). If you are looking for the Twitch API docs, see the [Twitch Developer website](https://dev.twitch.tv/docs/api).
## Supported Endpoints & Features
**Authentication:**
- [x] Generate Authorization URL
- [x] Get App Access Tokens (OAuth Client Credentials Flow)
- [x] Get User Access Tokens (OAuth Authorization Code Flow)
- [x] Refresh User Access Tokens
- [x] Revoke User Access Tokens
**API Endpoint:**
- [x] Get Bits Leaderboard
- [x] Get Clip
- [x] Create Clip
- [x] Create Entitlement Grants Upload URL
- [x] Get Games
- [x] Get Top Games
- [x] Get Extension Analytics
- [x] Get Game Analytics
- [x] Get Streams
- [x] Get Streams Metadata
- [x] Create Stream Marker
- [x] Get Stream Markers
- [x] Get Users
- [x] Get Users Follows
- [x] Update User
- [x] Get Videos
- [x] Get Webhook Subscriptions
## Quick Usage Example
This is a quick example of how to get users. Note that you don't need to provide both a list of ids and logins, one or the other will suffice.
```go
client, err := helix.NewClient(&helix.Options{
ClientID: "your-client-id",
})
if err != nil {
// handle error
}
resp, err := client.GetUsers(&helix.UsersParams{
IDs: []string{"26301881", "18074328"},
Logins: []string{"summit1g", "lirik"},
})
if err != nil {
// handle error
}
fmt.Printf("Status code: %d\n", resp.StatusCode)
fmt.Printf("Rate limit: %d\n", resp.GetRateLimit())
fmt.Printf("Rate limit remaining: %d\n", resp.GetRateLimitRemaining())
fmt.Printf("Rate limit reset: %d\n\n", resp.GetRateLimitReset())
for _, user := range resp.Data.Users {
fmt.Printf("ID: %s Name: %s\n", user.ID, user.DisplayName)
}
```
Output:
```txt
Status code: 200
Rate limit: 30
Rate limit remaining: 29
Rate limit reset: 1517695315
ID: 26301881 Name: sodapoppin
ID: 18074328 Name: destiny
ID: 26490481 Name: summit1g
ID: 23161357 Name: lirik
```
## Contributions
PRs are very much welcome. Where possible, please write tests for any code that is introduced by your PRs.
## Contributors
Thanks to all of the following people (ordered alphabetically) for contributing their time for improving this library:
- Y.Horie ([@u5surf](https://github.com/u5surf))
- Philipp Führer ([@flipkick](https://github.com/flipkick))
## License
This package is distributed under the terms of the [MIT](License) License.

101
vendor/github.com/nicklaw5/helix/analytics.go generated vendored Normal file
View File

@@ -0,0 +1,101 @@
package helix
// ExtensionAnalytic ...
type ExtensionAnalytic struct {
ExtensionID string `json:"extension_id"`
URL string `json:"URL"`
Type string `json:"type"`
DateRange DateRange `json:"date_range"`
}
// ManyExtensionAnalytics ...
type ManyExtensionAnalytics struct {
ExtensionAnalytics []ExtensionAnalytic `json:"data"`
Pagination Pagination `json:"pagination"`
}
// ExtensionAnalyticsResponse ...
type ExtensionAnalyticsResponse struct {
ResponseCommon
Data ManyExtensionAnalytics
}
// ExtensionAnalyticsParams ...
type ExtensionAnalyticsParams struct {
ExtensionID string `query:"extension_id"`
First int `query:"first,20"`
After string `query:"after"`
StartedAt Time `query:"started_at"`
EndedAt Time `query:"ended_at"`
Type string `query:"type"`
}
// GetExtensionAnalytics returns a URL to the downloadable CSV file
// containing analytics data. Valid for 5 minutes.
func (c *Client) GetExtensionAnalytics(params *ExtensionAnalyticsParams) (*ExtensionAnalyticsResponse, error) {
resp, err := c.get("/analytics/extensions", &ManyExtensionAnalytics{}, params)
if err != nil {
return nil, err
}
users := &ExtensionAnalyticsResponse{}
users.StatusCode = resp.StatusCode
users.Header = resp.Header
users.Error = resp.Error
users.ErrorStatus = resp.ErrorStatus
users.ErrorMessage = resp.ErrorMessage
users.Data.ExtensionAnalytics = resp.Data.(*ManyExtensionAnalytics).ExtensionAnalytics
users.Data.Pagination = resp.Data.(*ManyExtensionAnalytics).Pagination
return users, nil
}
// GameAnalytic ...
type GameAnalytic struct {
GameID string `json:"game_id"`
URL string `json:"URL"`
Type string `json:"type"`
DateRange DateRange `json:"date_range"`
}
// ManyGameAnalytics ...
type ManyGameAnalytics struct {
GameAnalytics []GameAnalytic `json:"data"`
Pagination Pagination `json:"pagination"`
}
// GameAnalyticsResponse ...
type GameAnalyticsResponse struct {
ResponseCommon
Data ManyGameAnalytics
}
// GameAnalyticsParams ...
type GameAnalyticsParams struct {
GameID string `query:"game_id"`
First int `query:"first,20"`
After string `query:"after"`
StartedAt Time `query:"started_at"`
EndedAt Time `query:"ended_at"`
Type string `query:"type"`
}
// GetGameAnalytics returns a URL to the downloadable CSV file
// containing analytics data for the specified game. Valid for 5 minutes.
func (c *Client) GetGameAnalytics(params *GameAnalyticsParams) (*GameAnalyticsResponse, error) {
resp, err := c.get("/analytics/games", &ManyGameAnalytics{}, params)
if err != nil {
return nil, err
}
users := &GameAnalyticsResponse{}
users.StatusCode = resp.StatusCode
users.Header = resp.Header
users.Error = resp.Error
users.ErrorStatus = resp.ErrorStatus
users.ErrorMessage = resp.ErrorMessage
users.Data.GameAnalytics = resp.Data.(*ManyGameAnalytics).GameAnalytics
users.Data.Pagination = resp.Data.(*ManyGameAnalytics).Pagination
return users, nil
}

208
vendor/github.com/nicklaw5/helix/authentication.go generated vendored Normal file
View File

@@ -0,0 +1,208 @@
package helix
import (
"strings"
)
var authPaths = map[string]string{
"token": "/token",
"revoke": "/revoke",
}
// GetAuthorizationURL ...
func (c *Client) GetAuthorizationURL(state string, forceVerify bool) string {
opts := c.opts
url := AuthBaseURL + "/authorize?response_type=code"
url += "&client_id=" + opts.ClientID
url += "&redirect_uri=" + opts.RedirectURI
if state != "" {
url += "&state=" + state
}
if forceVerify {
url += "&force_verify=true"
}
if len(opts.Scopes) > 0 {
url += "&scope=" + strings.Join(opts.Scopes, "%20")
}
return url
}
// AppAccessCredentials ...
type AppAccessCredentials struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}
// AppAccessTokenResponse ...
type AppAccessTokenResponse struct {
ResponseCommon
Data AppAccessCredentials
}
type appAccessTokenRequestData struct {
ClientID string `query:"client_id"`
ClientSecret string `query:"client_secret"`
RedirectURI string `query:"redirect_uri"`
GrantType string `query:"grant_type"`
}
// GetAppAccessToken ...
func (c *Client) GetAppAccessToken() (*AppAccessTokenResponse, error) {
opts := c.opts
data := &accessTokenRequestData{
ClientID: opts.ClientID,
ClientSecret: opts.ClientSecret,
RedirectURI: opts.RedirectURI,
GrantType: "client_credentials",
}
resp, err := c.post(authPaths["token"], &AppAccessCredentials{}, data)
if err != nil {
return nil, err
}
token := &AppAccessTokenResponse{}
token.StatusCode = resp.StatusCode
token.Error = resp.Error
token.ErrorStatus = resp.ErrorStatus
token.ErrorMessage = resp.ErrorMessage
token.Data.AccessToken = resp.Data.(*AppAccessCredentials).AccessToken
token.Data.ExpiresIn = resp.Data.(*AppAccessCredentials).ExpiresIn
return token, nil
}
// UserAccessCredentials ...
type UserAccessCredentials struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
Scopes []string `json:"scope"`
}
// UserAccessTokenResponse ...
type UserAccessTokenResponse struct {
ResponseCommon
Data UserAccessCredentials
}
type accessTokenRequestData struct {
Code string `query:"code"`
ClientID string `query:"client_id"`
ClientSecret string `query:"client_secret"`
RedirectURI string `query:"redirect_uri"`
GrantType string `query:"grant_type"`
}
// GetUserAccessToken ...
func (c *Client) GetUserAccessToken(code string) (*UserAccessTokenResponse, error) {
opts := c.opts
data := &accessTokenRequestData{
Code: code,
ClientID: opts.ClientID,
ClientSecret: opts.ClientSecret,
RedirectURI: opts.RedirectURI,
GrantType: "authorization_code",
}
resp, err := c.post(authPaths["token"], &UserAccessCredentials{}, data)
if err != nil {
return nil, err
}
token := &UserAccessTokenResponse{}
token.StatusCode = resp.StatusCode
token.Error = resp.Error
token.ErrorStatus = resp.ErrorStatus
token.ErrorMessage = resp.ErrorMessage
token.Data.AccessToken = resp.Data.(*UserAccessCredentials).AccessToken
token.Data.RefreshToken = resp.Data.(*UserAccessCredentials).RefreshToken
token.Data.ExpiresIn = resp.Data.(*UserAccessCredentials).ExpiresIn
token.Data.Scopes = resp.Data.(*UserAccessCredentials).Scopes
return token, nil
}
// RefreshTokenResponse ...
type RefreshTokenResponse struct {
ResponseCommon
Data UserAccessCredentials
}
type refreshTokenRequestData struct {
ClientID string `query:"client_id"`
ClientSecret string `query:"client_secret"`
GrantType string `query:"grant_type"`
RefreshToken string `query:"refresh_token"`
}
// RefreshUserAccessToken submits a request to have the longevity of an
// access token extended. Twitch OAuth2 access tokens have expirations.
// Token-expiration periods vary in length. You should build your applications
// in such a way that they are resilient to token authentication failures.
func (c *Client) RefreshUserAccessToken(refreshToken string) (*RefreshTokenResponse, error) {
opts := c.opts
data := &refreshTokenRequestData{
ClientID: opts.ClientID,
ClientSecret: opts.ClientSecret,
GrantType: "refresh_token",
RefreshToken: refreshToken,
}
resp, err := c.post(authPaths["token"], &UserAccessCredentials{}, data)
if err != nil {
return nil, err
}
refresh := &RefreshTokenResponse{}
refresh.StatusCode = resp.StatusCode
refresh.Error = resp.Error
refresh.ErrorStatus = resp.ErrorStatus
refresh.ErrorMessage = resp.ErrorMessage
refresh.Data.AccessToken = resp.Data.(*UserAccessCredentials).AccessToken
refresh.Data.RefreshToken = resp.Data.(*UserAccessCredentials).RefreshToken
refresh.Data.ExpiresIn = resp.Data.(*UserAccessCredentials).ExpiresIn
refresh.Data.Scopes = resp.Data.(*UserAccessCredentials).Scopes
return refresh, nil
}
// RevokeAccessTokenResponse ...
type RevokeAccessTokenResponse struct {
ResponseCommon
}
type revokeAccessTokenRequestData struct {
ClientID string `query:"client_id"`
AccessToken string `query:"token"`
}
// RevokeUserAccessToken submits a request to Twitch to have an access token revoked.
//
// Both successful requests and requests with bad tokens return 200 OK with
// no body. Requests with bad tokens return the same response, as there is no
// meaningful action a client can take after sending a bad token.
func (c *Client) RevokeUserAccessToken(accessToken string) (*RevokeAccessTokenResponse, error) {
data := &revokeAccessTokenRequestData{
ClientID: c.opts.ClientID,
AccessToken: accessToken,
}
resp, err := c.post(authPaths["revoke"], nil, data)
if err != nil {
return nil, err
}
revoke := &RevokeAccessTokenResponse{}
revoke.StatusCode = resp.StatusCode
revoke.Error = resp.Error
revoke.ErrorStatus = resp.ErrorStatus
revoke.ErrorMessage = resp.ErrorMessage
return revoke, nil
}

55
vendor/github.com/nicklaw5/helix/bits.go generated vendored Normal file
View File

@@ -0,0 +1,55 @@
package helix
import "time"
// UserBitTotal ...
type UserBitTotal struct {
UserID string `json:"user_id"`
UserName string `json:"user_name"`
Rank int `json:"rank"`
Score int `json:"score"`
}
// ManyUserBitTotals ...
type ManyUserBitTotals struct {
Total int `json:"total"`
DateRange DateRange `json:"date_range"`
UserBitTotals []UserBitTotal `json:"data"`
}
// BitsLeaderboardResponse ...
type BitsLeaderboardResponse struct {
ResponseCommon
Data ManyUserBitTotals
}
// BitsLeaderboardParams ...
type BitsLeaderboardParams struct {
Count int `query:"count,10"` // Maximum 100
Period string `query:"period,all"` // "all" (default), "day", "week", "month" and "year"
StartedAt time.Time `query:"started_at"`
UserID string `query:"user_id"`
}
// GetBitsLeaderboard gets a ranked list of Bits leaderboard
// information for an authorized broadcaster.
//
// Required Scope: bits:read
func (c *Client) GetBitsLeaderboard(params *BitsLeaderboardParams) (*BitsLeaderboardResponse, error) {
resp, err := c.get("/bits/leaderboard", &ManyUserBitTotals{}, params)
if err != nil {
return nil, err
}
bits := &BitsLeaderboardResponse{}
bits.StatusCode = resp.StatusCode
bits.Header = resp.Header
bits.Error = resp.Error
bits.ErrorStatus = resp.ErrorStatus
bits.ErrorMessage = resp.ErrorMessage
bits.Data.Total = resp.Data.(*ManyUserBitTotals).Total
bits.Data.DateRange = resp.Data.(*ManyUserBitTotals).DateRange
bits.Data.UserBitTotals = resp.Data.(*ManyUserBitTotals).UserBitTotals
return bits, nil
}

127
vendor/github.com/nicklaw5/helix/clips.go generated vendored Normal file
View File

@@ -0,0 +1,127 @@
package helix
// Clip ...
type Clip struct {
ID string `json:"id"`
URL string `json:"url"`
EmbedURL string `json:"embed_url"`
BroadcasterID string `json:"broadcaster_id"`
BroadcasterName string `json:"broadcaster_name"`
CreatorID string `json:"creator_id"`
CreatorName string `json:"creator_name"`
VideoID string `json:"video_id"`
GameID string `json:"game_id"`
Language string `json:"language"`
Title string `json:"title"`
ViewCount int `json:"view_count"`
CreatedAt string `json:"created_at"`
ThumbnailURL string `json:"thumbnail_url"`
}
// ManyClips ...
type ManyClips struct {
Clips []Clip `json:"data"`
Pagination Pagination `json:"pagination"`
}
// ClipsResponse ...
type ClipsResponse struct {
ResponseCommon
Data ManyClips
}
// ClipsParams ...
type ClipsParams struct {
// One of the below
BroadcasterID string `query:"broadcaster_id"`
GameID string `query:"game_id"`
IDs []string `query:"id"` // Limit 100
// Optional
First int `query:"first,20"` // Maximum 100
After string `query:"after"`
Before string `query:"before"`
StartedAt Time `query:"started_at"`
EndedAt Time `query:"ended_at"`
}
// GetClips returns information about a specified clip.
func (c *Client) GetClips(params *ClipsParams) (*ClipsResponse, error) {
resp, err := c.get("/clips", &ManyClips{}, params)
if err != nil {
return nil, err
}
clips := &ClipsResponse{}
clips.StatusCode = resp.StatusCode
clips.Header = resp.Header
clips.Error = resp.Error
clips.ErrorStatus = resp.ErrorStatus
clips.ErrorMessage = resp.ErrorMessage
clips.Data.Clips = resp.Data.(*ManyClips).Clips
clips.Data.Pagination = resp.Data.(*ManyClips).Pagination
return clips, nil
}
// ClipEditURL ...
type ClipEditURL struct {
ID string `json:"id"`
EditURL string `json:"edit_url"`
}
// ManyClipEditURLs ...
type ManyClipEditURLs struct {
ClipEditURLs []ClipEditURL `json:"data"`
}
// CreateClipResponse ...
type CreateClipResponse struct {
ResponseCommon
Data ManyClipEditURLs
}
// GetClipsCreationRateLimit returns the "Ratelimit-Helixclipscreation-Limit"
// header as an int.
func (ccr *CreateClipResponse) GetClipsCreationRateLimit() int {
return ccr.convertHeaderToInt(ccr.Header.Get("Ratelimit-Helixclipscreation-Limit"))
}
// GetClipsCreationRateLimitRemaining returns the "Ratelimit-Helixclipscreation-Remaining"
// header as an int.
func (ccr *CreateClipResponse) GetClipsCreationRateLimitRemaining() int {
return ccr.convertHeaderToInt(ccr.Header.Get("Ratelimit-Helixclipscreation-Remaining"))
}
// CreateClipParams ...
type CreateClipParams struct {
BroadcasterID string `query:"broadcaster_id"`
// Optional
HasDelay bool `query:"has_delay,false"`
}
// CreateClip creates a clip programmatically. This returns both an ID and
// an edit URL for the new clip. Clip creation takes time. We recommend that
// you query Get Clip, with the clip ID that is returned here. If Get Clip
// returns a valid clip, your clip creation was successful. If, after 15 seconds,
// you still have not gotten back a valid clip from Get Clip, assume that the
// clip was not created and retry Create Clip.
//
// Required scope: clips:edit
func (c *Client) CreateClip(params *CreateClipParams) (*CreateClipResponse, error) {
resp, err := c.post("/clips", &ManyClipEditURLs{}, params)
if err != nil {
return nil, err
}
clips := &CreateClipResponse{}
clips.StatusCode = resp.StatusCode
clips.Header = resp.Header
clips.Error = resp.Error
clips.ErrorStatus = resp.ErrorStatus
clips.ErrorMessage = resp.ErrorMessage
clips.Data.ClipEditURLs = resp.Data.(*ManyClipEditURLs).ClipEditURLs
return clips, nil
}

48
vendor/github.com/nicklaw5/helix/entitlement_grants.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
package helix
type entitlementUploadURLRequest struct {
ManifestID string `query:"manifest_id"`
Type string `query:"type"`
}
// EntitlementsUploadURL ...
type EntitlementsUploadURL struct {
URL string `json:"url"`
}
// ManyEntitlementsUploadURLs ...
type ManyEntitlementsUploadURLs struct {
URLs []EntitlementsUploadURL `json:"data"`
}
// EntitlementsUploadResponse ...
type EntitlementsUploadResponse struct {
ResponseCommon
Data ManyEntitlementsUploadURLs
}
// CreateEntitlementsUploadURL return a URL where you can upload a manifest
// file and notify users that they have an entitlement. Entitlements are digital
// items that users are entitled to use. Twitch entitlements are granted to users
// gratis or as part of a purchase on Twitch.
func (c *Client) CreateEntitlementsUploadURL(manifestID, entitlementType string) (*EntitlementsUploadResponse, error) {
data := &entitlementUploadURLRequest{
ManifestID: manifestID,
Type: entitlementType,
}
resp, err := c.post("/entitlements/upload", &ManyEntitlementsUploadURLs{}, data)
if err != nil {
return nil, err
}
url := &EntitlementsUploadResponse{}
url.StatusCode = resp.StatusCode
url.Header = resp.Header
url.Error = resp.Error
url.ErrorStatus = resp.ErrorStatus
url.ErrorMessage = resp.ErrorMessage
url.Data.URLs = resp.Data.(*ManyEntitlementsUploadURLs).URLs
return url, nil
}

81
vendor/github.com/nicklaw5/helix/games.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package helix
// Game ...
type Game struct {
ID string `json:"id"`
Name string `json:"name"`
BoxArtURL string `json:"box_art_url"`
}
// ManyGames ...
type ManyGames struct {
Games []Game `json:"data"`
}
// GamesResponse ...
type GamesResponse struct {
ResponseCommon
Data ManyGames
}
// GamesParams ...
type GamesParams struct {
IDs []string `query:"id"` // Limit 100
Names []string `query:"name"` // Limit 100
}
// GetGames ...
func (c *Client) GetGames(params *GamesParams) (*GamesResponse, error) {
resp, err := c.get("/games", &ManyGames{}, params)
if err != nil {
return nil, err
}
games := &GamesResponse{}
games.StatusCode = resp.StatusCode
games.Header = resp.Header
games.Error = resp.Error
games.ErrorStatus = resp.ErrorStatus
games.ErrorMessage = resp.ErrorMessage
games.Data.Games = resp.Data.(*ManyGames).Games
return games, nil
}
// ManyGamesWithPagination ...
type ManyGamesWithPagination struct {
ManyGames
Pagination Pagination `json:"pagination"`
}
// TopGamesParams ...
type TopGamesParams struct {
After string `query:"after"`
Before string `query:"before"`
First int `query:"first,20"` // Limit 100
}
// TopGamesResponse ...
type TopGamesResponse struct {
ResponseCommon
Data ManyGamesWithPagination
}
// GetTopGames ...
func (c *Client) GetTopGames(params *TopGamesParams) (*TopGamesResponse, error) {
resp, err := c.get("/games/top", &ManyGamesWithPagination{}, params)
if err != nil {
return nil, err
}
games := &TopGamesResponse{}
games.StatusCode = resp.StatusCode
games.Header = resp.Header
games.Error = resp.Error
games.ErrorStatus = resp.ErrorStatus
games.ErrorMessage = resp.ErrorMessage
games.Data.Games = resp.Data.(*ManyGamesWithPagination).Games
games.Data.Pagination = resp.Data.(*ManyGamesWithPagination).Pagination
return games, nil
}

418
vendor/github.com/nicklaw5/helix/helix.go generated vendored Normal file
View File

@@ -0,0 +1,418 @@
package helix
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strconv"
"strings"
"sync"
"time"
)
const (
// APIBaseURL is the base URL for composing API requests.
APIBaseURL = "https://api.twitch.tv/helix"
// AuthBaseURL is the base URL for composing authentication requests.
AuthBaseURL = "https://id.twitch.tv/oauth2"
)
// HTTPClient ...
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
// Client ...
type Client struct {
mu sync.RWMutex
opts *Options
baseURL string
lastResponse *Response
}
// Options ...
type Options struct {
ClientID string
ClientSecret string
AppAccessToken string
UserAccessToken string
UserAgent string
RedirectURI string
Scopes []string
HTTPClient HTTPClient
RateLimitFunc RateLimitFunc
}
// DateRange is a generic struct used by various responses.
type DateRange struct {
StartedAt Time `json:"started_at"`
EndedAt Time `json:"ended_at"`
}
// RateLimitFunc ...
type RateLimitFunc func(*Response) error
// ResponseCommon ...
type ResponseCommon struct {
StatusCode int
Header http.Header
Error string `json:"error"`
ErrorStatus int `json:"status"`
ErrorMessage string `json:"message"`
}
func (rc *ResponseCommon) convertHeaderToInt(str string) int {
i, _ := strconv.Atoi(str)
return i
}
// GetRateLimit returns the "RateLimit-Limit" header as an int.
func (rc *ResponseCommon) GetRateLimit() int {
return rc.convertHeaderToInt(rc.Header.Get("RateLimit-Limit"))
}
// GetRateLimitRemaining returns the "RateLimit-Remaining" header as an int.
func (rc *ResponseCommon) GetRateLimitRemaining() int {
return rc.convertHeaderToInt(rc.Header.Get("RateLimit-Remaining"))
}
// GetRateLimitReset returns the "RateLimit-Reset" header as an int.
func (rc *ResponseCommon) GetRateLimitReset() int {
return rc.convertHeaderToInt(rc.Header.Get("RateLimit-Reset"))
}
// Response ...
type Response struct {
ResponseCommon
Data interface{}
}
// Pagination ...
type Pagination struct {
Cursor string `json:"cursor"`
}
// NewClient returns a new Twicth Helix API client. It returns an
// if clientID is an empty string. It is concurrecy safe.
func NewClient(options *Options) (*Client, error) {
if options.ClientID == "" {
return nil, errors.New("A client ID was not provided but is required")
}
if options.HTTPClient == nil {
options.HTTPClient = http.DefaultClient
}
client := &Client{
opts: options,
baseURL: APIBaseURL,
}
return client, nil
}
func (c *Client) get(path string, respData, reqData interface{}) (*Response, error) {
return c.sendRequest(http.MethodGet, path, respData, reqData)
}
func (c *Client) post(path string, respData, reqData interface{}) (*Response, error) {
return c.sendRequest(http.MethodPost, path, respData, reqData)
}
func (c *Client) put(path string, respData, reqData interface{}) (*Response, error) {
return c.sendRequest(http.MethodPut, path, respData, reqData)
}
func (c *Client) sendRequest(method, path string, respData, reqData interface{}) (*Response, error) {
resp := &Response{}
if respData != nil {
resp.Data = respData
}
req, err := c.newRequest(method, path, reqData)
if err != nil {
return nil, err
}
err = c.doRequest(req, resp)
if err != nil {
return nil, err
}
return resp, nil
}
func buildQueryString(req *http.Request, v interface{}) (string, error) {
isNil, err := isZero(v)
if err != nil {
return "", err
}
if isNil {
return "", nil
}
query := req.URL.Query()
vType := reflect.TypeOf(v).Elem()
vValue := reflect.ValueOf(v).Elem()
for i := 0; i < vType.NumField(); i++ {
var defaultValue string
field := vType.Field(i)
tag := field.Tag.Get("query")
// Get the default value from the struct tag
if strings.Contains(tag, ",") {
tagSlice := strings.Split(tag, ",")
tag = tagSlice[0]
defaultValue = tagSlice[1]
}
if field.Type.Kind() == reflect.Slice {
// Attach any slices as query params
fieldVal := vValue.Field(i)
for j := 0; j < fieldVal.Len(); j++ {
query.Add(tag, fmt.Sprintf("%v", fieldVal.Index(j)))
}
} else if isDatetimeTagField(tag) {
// Get and correctly format datetime fields, and attach them query params
dateStr := fmt.Sprintf("%v", vValue.Field(i))
if strings.Contains(dateStr, " m=") {
datetimeSplit := strings.Split(dateStr, " m=")
dateStr = datetimeSplit[0]
}
date, err := time.Parse(requestDateTimeFormat, dateStr)
if err != nil {
return "", err
}
// Determine if the date has been set. If it has we'll add it to the query.
if !date.IsZero() {
query.Add(tag, date.Format(time.RFC3339))
}
} else {
// Add any scalar values as query params
fieldVal := fmt.Sprintf("%v", vValue.Field(i))
// If no value was set by the user, use the default
// value specified in the struct tag.
if fieldVal == "" || fieldVal == "0" {
if defaultValue == "" {
continue
}
fieldVal = defaultValue
}
query.Add(tag, fieldVal)
}
}
return query.Encode(), nil
}
func isZero(v interface{}) (bool, error) {
t := reflect.TypeOf(v)
if !t.Comparable() {
return false, fmt.Errorf("type is not comparable: %v", t)
}
return v == reflect.Zero(t).Interface(), nil
}
func (c *Client) newRequest(method, path string, data interface{}) (*http.Request, error) {
url := c.getBaseURL(path) + path
// We want to send Webhook POST request data in JSON form.
// So here we determine if data is of type `WebhookSubscriptionPayload`.
_, ok := data.(*WebhookSubscriptionPayload)
if ok {
return c.newJSONRequest(method, url, data)
}
return c.newStandardRequest(method, url, data)
}
func (c *Client) newStandardRequest(method, url string, data interface{}) (*http.Request, error) {
req, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}
if data == nil {
return req, nil
}
query, err := buildQueryString(req, data)
if err != nil {
return nil, err
}
req.URL.RawQuery = query
return req, nil
}
func (c *Client) newJSONRequest(method, url string, data interface{}) (*http.Request, error) {
b, err := json.Marshal(data)
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(b)
req, err := http.NewRequest(method, url, buf)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
return req, nil
}
func (c *Client) getBaseURL(path string) string {
for _, authPath := range authPaths {
if strings.Contains(path, authPath) {
return AuthBaseURL
}
}
return APIBaseURL
}
func (c *Client) doRequest(req *http.Request, resp *Response) error {
c.setRequestHeaders(req)
rateLimitFunc := c.opts.RateLimitFunc
for {
if c.lastResponse != nil && rateLimitFunc != nil {
err := rateLimitFunc(c.lastResponse)
if err != nil {
return err
}
}
response, err := c.opts.HTTPClient.Do(req)
if err != nil {
return fmt.Errorf("Failed to execute API request: %s", err.Error())
}
defer response.Body.Close()
resp.Header = response.Header
setResponseStatusCode(resp, "StatusCode", response.StatusCode)
bodyBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
// Only attempt to decode the response if we have a response we can handle
if len(bodyBytes) > 0 && resp.StatusCode < http.StatusInternalServerError {
if resp.Data != nil && resp.StatusCode < http.StatusBadRequest {
// Successful request
err = json.Unmarshal(bodyBytes, &resp.Data)
} else {
// Failed request
err = json.Unmarshal(bodyBytes, &resp)
}
if err != nil {
return fmt.Errorf("Failed to decode API response: %s", err.Error())
}
}
if rateLimitFunc == nil {
break
} else {
c.mu.Lock()
c.lastResponse = resp
c.mu.Unlock()
if rateLimitFunc != nil &&
c.lastResponse.StatusCode == http.StatusTooManyRequests {
// Rate limit exceeded, retry to send request after
// applying rate limiter callback
continue
}
break
}
}
return nil
}
func (c *Client) setRequestHeaders(req *http.Request) {
opts := c.opts
req.Header.Set("Client-ID", opts.ClientID)
if opts.UserAgent != "" {
req.Header.Set("User-Agent", opts.UserAgent)
}
var bearerToken string
if opts.AppAccessToken != "" {
bearerToken = opts.AppAccessToken
}
if opts.UserAccessToken != "" {
bearerToken = opts.UserAccessToken
}
if bearerToken != "" {
req.Header.Set("Authorization", "Bearer "+bearerToken)
}
}
func setResponseStatusCode(v interface{}, fieldName string, code int) {
s := reflect.ValueOf(v).Elem()
field := s.FieldByName(fieldName)
field.SetInt(int64(code))
}
// SetAppAccessToken ...
func (c *Client) SetAppAccessToken(accessToken string) {
c.mu.Lock()
defer c.mu.Unlock()
c.opts.AppAccessToken = accessToken
}
// SetUserAccessToken ...
func (c *Client) SetUserAccessToken(accessToken string) {
c.mu.Lock()
defer c.mu.Unlock()
c.opts.UserAccessToken = accessToken
}
// SetUserAgent ...
func (c *Client) SetUserAgent(userAgent string) {
c.mu.Lock()
defer c.mu.Unlock()
c.opts.UserAgent = userAgent
}
// SetScopes ...
func (c *Client) SetScopes(scopes []string) {
c.mu.Lock()
defer c.mu.Unlock()
c.opts.Scopes = scopes
}
// SetRedirectURI ...
func (c *Client) SetRedirectURI(uri string) {
c.mu.Lock()
defer c.mu.Unlock()
c.opts.RedirectURI = uri
}

122
vendor/github.com/nicklaw5/helix/stream_markers.go generated vendored Normal file
View File

@@ -0,0 +1,122 @@
package helix
// Marker ...
type Marker struct {
ID string `json:"id"`
CreatedAt Time `json:"created_at"`
Description string `json:"description"`
PositionSeconds int `json:"position_seconds"`
URL string `json:"URL"`
}
// VideoMarker ...
type VideoMarker struct {
VideoID string `json:"video_id"`
Markers []Marker `json:"markers"`
}
// StreamMarker ...
type StreamMarker struct {
UserID string `json:"user_id"`
UserName string `json:"user_name"`
Videos []VideoMarker `json:"videos"`
}
// ManyStreamMarkers ...
type ManyStreamMarkers struct {
StreamMarkers []StreamMarker `json:"data"`
Pagination Pagination `json:"pagination"`
}
// StreamMarkersResponse ...
type StreamMarkersResponse struct {
ResponseCommon
Data ManyStreamMarkers
}
// StreamMarkersParams requires _either_ UserID or VideoID set
//
// UserID: fetches stream markers of the current livestream of the given user
// (VOD recording must be enabled).
// VideoID: fetches streams markers of the VOD.
type StreamMarkersParams struct {
UserID string `query:"user_id"`
VideoID string `query:"video_id"`
// Optional
After string `query:"after"`
Before string `query:"before"`
First int `query:"first,20"` // Limit 100
}
// GetStreamMarkers gets stream markers of a VOD or of the current live stream
// of an user being recorded as VOD.
//
// Required Scope: user:read:broadcast
func (c *Client) GetStreamMarkers(params *StreamMarkersParams) (*StreamMarkersResponse, error) {
resp, err := c.get("/streams/markers", &ManyStreamMarkers{}, params)
if err != nil {
return nil, err
}
markers := &StreamMarkersResponse{}
markers.StatusCode = resp.StatusCode
markers.Header = resp.Header
markers.Error = resp.Error
markers.ErrorStatus = resp.ErrorStatus
markers.ErrorMessage = resp.ErrorMessage
markers.Data.StreamMarkers = resp.Data.(*ManyStreamMarkers).StreamMarkers
markers.Data.Pagination = resp.Data.(*ManyStreamMarkers).Pagination
return markers, nil
}
// CreateStreamMarker ...
type CreateStreamMarker struct {
ID string `json:"id"`
CreatedAt Time `json:"created_at"`
Description string `json:"description"`
PositionSeconds int `json:"position_seconds"`
}
// ManyCreateStreamMarkers ...
type ManyCreateStreamMarkers struct {
CreateStreamMarkers []CreateStreamMarker `json:"data"`
}
// CreateStreamMarkerResponse ...
type CreateStreamMarkerResponse struct {
ResponseCommon
Data ManyCreateStreamMarkers
}
// CreateStreamMarkerParams ...
type CreateStreamMarkerParams struct {
UserID string `query:"user_id"`
// Optional
Description string `query:"description"`
}
// CreateStreamMarker creates a stream marker for a live stream at the current time.
// The user has to be the stream owner or an editor. Stream markers cannot be created
// in some cases, see:
// https://dev.twitch.tv/docs/api/reference/#create-stream-marker
//
// Required Scope: user:edit:broadcast
func (c *Client) CreateStreamMarker(params *CreateStreamMarkerParams) (*CreateStreamMarkerResponse, error) {
resp, err := c.post("/streams/markers", &ManyCreateStreamMarkers{}, params)
if err != nil {
return nil, err
}
markers := &CreateStreamMarkerResponse{}
markers.StatusCode = resp.StatusCode
markers.Header = resp.Header
markers.Error = resp.Error
markers.ErrorStatus = resp.ErrorStatus
markers.ErrorMessage = resp.ErrorMessage
markers.Data.CreateStreamMarkers = resp.Data.(*ManyCreateStreamMarkers).CreateStreamMarkers
return markers, nil
}

151
vendor/github.com/nicklaw5/helix/streams.go generated vendored Normal file
View File

@@ -0,0 +1,151 @@
package helix
import "time"
// Stream ...
type Stream struct {
ID string `json:"id"`
UserID string `json:"user_id"`
UserName string `json:"user_name"`
GameID string `json:"game_id"`
TagIDs []string `json:"tag_ids"`
Type string `json:"type"`
Title string `json:"title"`
ViewerCount int `json:"viewer_count"`
StartedAt time.Time `json:"started_at"`
Language string `json:"language"`
ThumbnailURL string `json:"thumbnail_url"`
}
// ManyStreams ...
type ManyStreams struct {
Streams []Stream `json:"data"`
Pagination Pagination `json:"pagination"`
}
// StreamsResponse ...
type StreamsResponse struct {
ResponseCommon
Data ManyStreams
}
// StreamsParams ...
type StreamsParams struct {
After string `query:"after"`
Before string `query:"before"`
First int `query:"first,20"` // Limit 100
GameIDs []string `query:"game_id"` // Limit 100
Language []string `query:"language"` // Limit 100
Type string `query:"type,all"` // "all" (default), "live" and "vodcast"
UserIDs []string `query:"user_id"` // limit 100
UserLogins []string `query:"user_login"` // limit 100
}
// GetStreams ...
func (c *Client) GetStreams(params *StreamsParams) (*StreamsResponse, error) {
resp, err := c.get("/streams", &ManyStreams{}, params)
if err != nil {
return nil, err
}
streams := &StreamsResponse{}
streams.StatusCode = resp.StatusCode
streams.Header = resp.Header
streams.Error = resp.Error
streams.ErrorStatus = resp.ErrorStatus
streams.ErrorMessage = resp.ErrorMessage
streams.Data.Streams = resp.Data.(*ManyStreams).Streams
streams.Data.Pagination = resp.Data.(*ManyStreams).Pagination
return streams, nil
}
// HearthstoneHero ...
type HearthstoneHero struct {
Class string `json:"class"`
Name string `json:"name"`
Type string `json:"type"`
}
// HearthstonePlayerData ...
type HearthstonePlayerData struct {
Hero HearthstoneHero `json:"hero"`
}
// HearthstoneMetadata ...
type HearthstoneMetadata struct {
Broadcaster HearthstonePlayerData `json:"broadcaster"`
Opponent HearthstonePlayerData `json:"opponent"`
}
// OverwatchHero ...
type OverwatchHero struct {
Ability string `json:"ability"`
Name string `json:"name"`
Role string `json:"role"`
}
// OverwatchBroadcaster ...
type OverwatchBroadcaster struct {
Hero OverwatchHero `json:"hero"`
}
// OverwatchMetadata ...
type OverwatchMetadata struct {
Broadcaster OverwatchBroadcaster `json:"broadcaster"`
}
// StreamMetadata ...
type StreamMetadata struct {
UserID string `json:"user_id"`
UserName string `json:"user_name"`
GameID string `json:"game_id"`
Hearthstone HearthstoneMetadata `json:"hearthstone"`
Overwatch OverwatchMetadata `json:"overwatch"`
}
// ManyStreamsMetadata ...
type ManyStreamsMetadata struct {
Streams []StreamMetadata `json:"data"`
Pagination Pagination `json:"pagination"`
}
// StreamsMetadataResponse ...
type StreamsMetadataResponse struct {
ResponseCommon
Data ManyStreamsMetadata
}
// GetStreamsMetadataRateLimit returns the "Ratelimit-Helixstreamsmetadata-Limit"
// header as an int.
func (sr *StreamsMetadataResponse) GetStreamsMetadataRateLimit() int {
return sr.convertHeaderToInt(sr.Header.Get("Ratelimit-Helixstreamsmetadata-Limit"))
}
// GetStreamsMetadataRateLimitRemaining returns the "Ratelimit-Helixstreamsmetadata-Remaining"
// header as an int.
func (sr *StreamsMetadataResponse) GetStreamsMetadataRateLimitRemaining() int {
return sr.convertHeaderToInt(sr.Header.Get("Ratelimit-Helixstreamsmetadata-Remaining"))
}
// StreamsMetadataParams ...
type StreamsMetadataParams StreamsParams
// GetStreamsMetadata ...
func (c *Client) GetStreamsMetadata(params *StreamsMetadataParams) (*StreamsMetadataResponse, error) {
resp, err := c.get("/streams/metadata", &ManyStreamsMetadata{}, params)
if err != nil {
return nil, err
}
streams := &StreamsMetadataResponse{}
streams.StatusCode = resp.StatusCode
streams.Header = resp.Header
streams.Error = resp.Error
streams.ErrorStatus = resp.ErrorStatus
streams.ErrorMessage = resp.ErrorMessage
streams.Data.Streams = resp.Data.(*ManyStreamsMetadata).Streams
streams.Data.Pagination = resp.Data.(*ManyStreamsMetadata).Pagination
return streams, nil
}

52
vendor/github.com/nicklaw5/helix/subscriptions.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package helix
// Subscription ...
type Subscription struct {
BroadcasterID string `json:"broadcaster_id"`
BroadcasterName string `json:"broadcaster_name"`
IsGift bool `json:"is_gift"`
Tier string `json:"tier"`
PlanName string `json:"plan_name"`
UserID string `json:"user_id"`
UserName string `json:"user_name"`
}
// ManySubscriptions ...
type ManySubscriptions struct {
Subscriptions []Subscription `json:"data"`
Pagination Pagination `json:"pagination"`
}
// SubscriptionsResponse ...
type SubscriptionsResponse struct {
ResponseCommon
Data ManySubscriptions
}
// SubscriptionsParams ...
type SubscriptionsParams struct {
BroadcasterID string `query:"broadcaster_id"` // Limit 1
UserID []string `query:"user_id"` // Limit 100
}
// GetSubscriptions gets subscriptions about one Twitch broadcaster.
// Broadcasters can only request their own subscriptions.
//
// Required scope: channel:read:subscriptions
func (c *Client) GetSubscriptions(params *SubscriptionsParams) (*SubscriptionsResponse, error) {
resp, err := c.get("/subscriptions", &ManySubscriptions{}, params)
if err != nil {
return nil, err
}
subscriptions := &SubscriptionsResponse{}
subscriptions.StatusCode = resp.StatusCode
subscriptions.Header = resp.Header
subscriptions.Error = resp.Error
subscriptions.ErrorStatus = resp.ErrorStatus
subscriptions.ErrorMessage = resp.ErrorMessage
subscriptions.Data.Subscriptions = resp.Data.(*ManySubscriptions).Subscriptions
subscriptions.Data.Pagination = resp.Data.(*ManySubscriptions).Pagination
return subscriptions, nil
}

47
vendor/github.com/nicklaw5/helix/time.go generated vendored Normal file
View File

@@ -0,0 +1,47 @@
package helix
import (
"strings"
"time"
)
const (
requestDateTimeFormat = "2006-01-02 15:04:05 -0700 MST"
)
var (
datetimeFields = []string{"started_at", "ended_at"}
)
// Time is our custom time struct.
type Time struct {
time.Time
}
// UnmarshalJSON is our custom datetime unmarshaller. Twitch sometimes
// returns datetimes as empty strings, which casuses issues with the native time
// UnmarshalJSON method when decoding the JSON string. Here we hanlde that scenario,
// by returning a zero time value for any JSON time field that is either an
// empty string or "null".
func (t *Time) UnmarshalJSON(b []byte) (err error) {
timeStr := strings.Trim(string(b), "\"")
if timeStr == "" || timeStr == "null" {
t.Time = time.Time{}
return
}
t.Time, err = time.Parse(time.RFC3339, timeStr)
return
}
func isDatetimeTagField(tag string) bool {
for _, tagField := range datetimeFields {
if tagField == tag {
return true
}
}
return false
}

135
vendor/github.com/nicklaw5/helix/users.go generated vendored Normal file
View File

@@ -0,0 +1,135 @@
package helix
import "time"
// User ...
type User struct {
ID string `json:"id"`
Login string `json:"login"`
DisplayName string `json:"display_name"`
Type string `json:"type"`
BroadcasterType string `json:"broadcaster_type"`
Description string `json:"description"`
ProfileImageURL string `json:"profile_image_url"`
OfflineImageURL string `json:"offline_image_url"`
ViewCount int `json:"view_count"`
Email string `json:"email"`
}
// ManyUsers ...
type ManyUsers struct {
Users []User `json:"data"`
}
// UsersResponse ...
type UsersResponse struct {
ResponseCommon
Data ManyUsers
}
// UsersParams ...
type UsersParams struct {
IDs []string `query:"id"` // Limit 100
Logins []string `query:"login"` // Limit 100
}
// GetUsers gets information about one or more specified Twitch users.
// Users are identified by optional user IDs and/or login name. If neither
// a user ID nor a login name is specified, the user is looked up by Bearer token.
//
// Optional scope: user:read:email
func (c *Client) GetUsers(params *UsersParams) (*UsersResponse, error) {
resp, err := c.get("/users", &ManyUsers{}, params)
if err != nil {
return nil, err
}
users := &UsersResponse{}
users.StatusCode = resp.StatusCode
users.Header = resp.Header
users.Error = resp.Error
users.ErrorStatus = resp.ErrorStatus
users.ErrorMessage = resp.ErrorMessage
users.Data.Users = resp.Data.(*ManyUsers).Users
return users, nil
}
// UpdateUserParams ...
type UpdateUserParams struct {
Description string `query:"description"`
}
// UpdateUser updates the description of a user specified
// by a Bearer token.
//
// Required scope: user:edit
func (c *Client) UpdateUser(params *UpdateUserParams) (*UsersResponse, error) {
resp, err := c.put("/users", &ManyUsers{}, params)
if err != nil {
return nil, err
}
users := &UsersResponse{}
users.StatusCode = resp.StatusCode
users.Header = resp.Header
users.Error = resp.Error
users.ErrorStatus = resp.ErrorStatus
users.ErrorMessage = resp.ErrorMessage
users.Data.Users = resp.Data.(*ManyUsers).Users
return users, nil
}
// UserFollow ...
type UserFollow struct {
FromID string `json:"from_id"`
FromName string `json:"from_name"`
ToID string `json:"to_id"`
ToName string `json:"to_name"`
FollowedAt time.Time `json:"followed_at"`
}
// ManyFollows ...
type ManyFollows struct {
Total int `json:"total"`
Follows []UserFollow `json:"data"`
Pagination Pagination `json:"pagination"`
}
// UsersFollowsResponse ...
type UsersFollowsResponse struct {
ResponseCommon
Data ManyFollows
}
// UsersFollowsParams ...
type UsersFollowsParams struct {
After string `query:"after"`
First int `query:"first,20"` // Limit 100
FromID string `query:"from_id"`
ToID string `query:"to_id"`
}
// GetUsersFollows gets information on follow relationships between two Twitch users.
// Information returned is sorted in order, most recent follow first. This can return
// information like “who is lirik following,” “who is following lirik,” or “is user X
// following user Y.”
func (c *Client) GetUsersFollows(params *UsersFollowsParams) (*UsersFollowsResponse, error) {
resp, err := c.get("/users/follows", &ManyFollows{}, params)
if err != nil {
return nil, err
}
users := &UsersFollowsResponse{}
users.StatusCode = resp.StatusCode
users.Header = resp.Header
users.Error = resp.Error
users.ErrorStatus = resp.ErrorStatus
users.ErrorMessage = resp.ErrorMessage
users.Data.Total = resp.Data.(*ManyFollows).Total
users.Data.Follows = resp.Data.(*ManyFollows).Follows
users.Data.Pagination = resp.Data.(*ManyFollows).Pagination
return users, nil
}

67
vendor/github.com/nicklaw5/helix/videos.go generated vendored Normal file
View File

@@ -0,0 +1,67 @@
package helix
// Video ...
type Video struct {
ID string `json:"id"`
UserID string `json:"user_id"`
UserName string `json:"user_name"`
Title string `json:"title"`
Description string `json:"description"`
CreatedAt string `json:"created_at"`
PublishedAt string `json:"published_at"`
URL string `json:"url"`
ThumbnailURL string `json:"thumbnail_url"`
Viewable string `json:"viewable"`
ViewCount int `json:"view_count"`
Language string `json:"language"`
Type string `json:"type"`
Duration string `json:"duration"`
}
// ManyVideos ...
type ManyVideos struct {
Videos []Video `json:"data"`
Pagination Pagination `json:"pagination"`
}
// VideosParams ...
type VideosParams struct {
IDs []string `query:"id"` // Limit 100
UserID string `query:"user_id"` // Limit 1
GameID string `query:"game_id"` // Limit 1
// Optional
After string `query:"after"`
Before string `query:"before"`
First int `query:"first,20"` // Limit 100
Language string `query:"language"` // Limit 1
Period string `query:"period,all"` // "all" (default), "day", "month", and "week"
Sort string `query:"sort,time"` // "time" (default), "trending", and "views"
Type string `query:"type,all"` // "all" (default), "upload", "archive", and "highlight"
}
// VideosResponse ...
type VideosResponse struct {
ResponseCommon
Data ManyVideos
}
// GetVideos gets video information by video ID (one or more), user ID (one only),
// or game ID (one only).
func (c *Client) GetVideos(params *VideosParams) (*VideosResponse, error) {
resp, err := c.get("/videos", &ManyVideos{}, params)
if err != nil {
return nil, err
}
videos := &VideosResponse{}
videos.StatusCode = resp.StatusCode
videos.Header = resp.Header
videos.Error = resp.Error
videos.ErrorStatus = resp.ErrorStatus
videos.ErrorMessage = resp.ErrorMessage
videos.Data.Videos = resp.Data.(*ManyVideos).Videos
videos.Data.Pagination = resp.Data.(*ManyVideos).Pagination
return videos, nil
}

197
vendor/github.com/nicklaw5/helix/webhooks.go generated vendored Normal file
View File

@@ -0,0 +1,197 @@
package helix
import (
"net/http"
"regexp"
)
// WebhookSubscription ...
type WebhookSubscription struct {
Topic string `json:"topic"`
Callback string `json:"callback"`
ExpiresAt Time `json:"expires_at"`
}
// ManyWebhookSubscriptions ...
type ManyWebhookSubscriptions struct {
Total int `json:"total"`
WebhookSubscriptions []WebhookSubscription `json:"data"`
Pagination Pagination `json:"pagination"`
}
// WebhookSubscriptionsResponse ...
type WebhookSubscriptionsResponse struct {
ResponseCommon
Data ManyWebhookSubscriptions
}
// WebhookSubscriptionsParams ...
type WebhookSubscriptionsParams struct {
After string `query:"after"`
First int `query:"first,20"` // Limit 100
}
// GetWebhookSubscriptions gets webhook subscriptions, in order of expiration.
// Requires an app access token.
func (c *Client) GetWebhookSubscriptions(params *WebhookSubscriptionsParams) (*WebhookSubscriptionsResponse, error) {
resp, err := c.get("/webhooks/subscriptions", &ManyWebhookSubscriptions{}, params)
if err != nil {
return nil, err
}
webhooks := &WebhookSubscriptionsResponse{}
webhooks.StatusCode = resp.StatusCode
webhooks.Header = resp.Header
webhooks.Error = resp.Error
webhooks.ErrorStatus = resp.ErrorStatus
webhooks.ErrorMessage = resp.ErrorMessage
webhooks.Data.Total = resp.Data.(*ManyWebhookSubscriptions).Total
webhooks.Data.WebhookSubscriptions = resp.Data.(*ManyWebhookSubscriptions).WebhookSubscriptions
webhooks.Data.Pagination = resp.Data.(*ManyWebhookSubscriptions).Pagination
return webhooks, nil
}
// WebhookSubscriptionResponse ...
type WebhookSubscriptionResponse struct {
ResponseCommon
}
// WebhookSubscriptionPayload ...
type WebhookSubscriptionPayload struct {
Mode string `json:"hub.mode"`
Topic string `json:"hub.topic"`
Callback string `json:"hub.callback"`
LeaseSeconds int `json:"hub.lease_seconds,omitempty"`
Secret string `json:"hub.secret,omitempty"`
}
// PostWebhookSubscription ...
func (c *Client) PostWebhookSubscription(payload *WebhookSubscriptionPayload) (*WebhookSubscriptionResponse, error) {
resp, err := c.post("/webhooks/hub", nil, payload)
if err != nil {
return nil, err
}
webhook := &WebhookSubscriptionResponse{}
webhook.StatusCode = resp.StatusCode
webhook.Header = resp.Header
webhook.Error = resp.Error
webhook.ErrorStatus = resp.ErrorStatus
webhook.ErrorMessage = resp.ErrorMessage
return webhook, nil
}
// Regular expressions used for parsing webhook link headers
var (
UserFollowsRegexp = regexp.MustCompile("helix/users/follows\\?first=1(&from_id=(?P<from_id>\\d+))?(&to_id=(?P<to_id>\\d+))?>")
StreamChangedRegexp = regexp.MustCompile("helix/streams\\?user_id=(?P<user_id>\\d+)>")
UserChangedRegexp = regexp.MustCompile("helix/users\\?id=(?P<id>\\d+)>")
GameAnalyticsRegexp = regexp.MustCompile("helix/analytics\\?game_id=(?P<game_id>\\w+)>")
ExtensionAnalyticsRegexp = regexp.MustCompile("helix/analytics\\?extension_id=(?P<extension_id>\\w+)>")
)
// WebhookTopic is a topic that relates to a specific webhook event.
type WebhookTopic int
// Enumerated webhook topics
const (
UserFollowsTopic WebhookTopic = iota
StreamChangedTopic
UserChangedTopic
GameAnalyticsTopic
ExtensionAnalyticsTopic
)
// GetWebhookTopicFromRequest inspects the "Link" request header to
// determine if it matches against any recognised webhooks topics.
// The matched topic is returned. Otherwise -1 is returned.
func GetWebhookTopicFromRequest(req *http.Request) WebhookTopic {
header := getLinkHeaderFromWebhookRequest(req)
if UserFollowsRegexp.MatchString(header) {
return UserFollowsTopic
}
if StreamChangedRegexp.MatchString(header) {
return StreamChangedTopic
}
if UserChangedRegexp.MatchString(header) {
return UserChangedTopic
}
if GameAnalyticsRegexp.MatchString(header) {
return GameAnalyticsTopic
}
if ExtensionAnalyticsRegexp.MatchString(header) {
return ExtensionAnalyticsTopic
}
return -1
}
// GetWebhookTopicValuesFromRequest inspects the "Link" request header to
// determine if it matches against any recognised webhooks topics and
// returns the unique values specified in the header.
//
// For example, say we receive a "User Follows" webhook event from Twitch.
// Its "Link" header value look likes the following:
//
// <https://api.twitch.tv/helix/webhooks/hub>; rel="hub", <https://api.twitch.tv/helix/users/follows?first=1&from_id=111116&to_id=22222>; rel="self"
//
// From which GetWebhookTopicValuesFromRequest will return a map with the
// values of from_id and to_id:
//
// map[from_id:111116 to_id:22222]
//
// This is particularly useful for webhooks events that do not have a distinguishable
// JSON payload, such as the "Stream Changed" down event.
//
// Additionally, if topic is not known you can pass -1 as its value and
func GetWebhookTopicValuesFromRequest(req *http.Request, topic WebhookTopic) map[string]string {
values := make(map[string]string)
webhookTopic := topic
header := getLinkHeaderFromWebhookRequest(req)
// Webhook topic may not be known, so let's attempt to
// determine its value based on the request.
if webhookTopic < 0 && header != "" {
webhookTopic = GetWebhookTopicFromRequest(req)
}
switch webhookTopic {
case UserFollowsTopic:
values = findStringSubmatchMap(UserFollowsRegexp, header)
case StreamChangedTopic:
values = findStringSubmatchMap(StreamChangedRegexp, header)
case UserChangedTopic:
values = findStringSubmatchMap(UserChangedRegexp, header)
case GameAnalyticsTopic:
values = findStringSubmatchMap(GameAnalyticsRegexp, header)
case ExtensionAnalyticsTopic:
values = findStringSubmatchMap(ExtensionAnalyticsRegexp, header)
}
return values
}
func getLinkHeaderFromWebhookRequest(req *http.Request) string {
return req.Header.Get("link")
}
func findStringSubmatchMap(r *regexp.Regexp, s string) map[string]string {
captures := make(map[string]string)
match := r.FindStringSubmatch(s)
if match == nil {
return captures
}
for i, name := range r.SubexpNames() {
if i == 0 || name == "" {
continue
}
captures[name] = match[i]
}
return captures
}