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:
2
vendor/github.com/nicklaw5/helix/.gitignore
generated
vendored
Normal file
2
vendor/github.com/nicklaw5/helix/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
debug.test
|
||||
17
vendor/github.com/nicklaw5/helix/.travis.yml
generated
vendored
Normal file
17
vendor/github.com/nicklaw5/helix/.travis.yml
generated
vendored
Normal 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
21
vendor/github.com/nicklaw5/helix/LICENSE
generated
vendored
Normal 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
103
vendor/github.com/nicklaw5/helix/README.md
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
# helix
|
||||
|
||||
A Twitch Helix API client written in Go (Golang).
|
||||
|
||||
[](https://travis-ci.org/nicklaw5/helix)
|
||||
[](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
101
vendor/github.com/nicklaw5/helix/analytics.go
generated
vendored
Normal 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
208
vendor/github.com/nicklaw5/helix/authentication.go
generated
vendored
Normal 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
55
vendor/github.com/nicklaw5/helix/bits.go
generated
vendored
Normal 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
127
vendor/github.com/nicklaw5/helix/clips.go
generated
vendored
Normal 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
48
vendor/github.com/nicklaw5/helix/entitlement_grants.go
generated
vendored
Normal 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
81
vendor/github.com/nicklaw5/helix/games.go
generated
vendored
Normal 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
418
vendor/github.com/nicklaw5/helix/helix.go
generated
vendored
Normal 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
122
vendor/github.com/nicklaw5/helix/stream_markers.go
generated
vendored
Normal 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
151
vendor/github.com/nicklaw5/helix/streams.go
generated
vendored
Normal 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
52
vendor/github.com/nicklaw5/helix/subscriptions.go
generated
vendored
Normal 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
47
vendor/github.com/nicklaw5/helix/time.go
generated
vendored
Normal 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
135
vendor/github.com/nicklaw5/helix/users.go
generated
vendored
Normal 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
67
vendor/github.com/nicklaw5/helix/videos.go
generated
vendored
Normal 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
197
vendor/github.com/nicklaw5/helix/webhooks.go
generated
vendored
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user