mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 20:01:17 +00:00
Add category search and channel update
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
9451354d30
commit
2b67673e95
1 changed files with 103 additions and 5 deletions
100
twitch/twitch.go
100
twitch/twitch.go
|
@ -1,11 +1,13 @@
|
|||
package twitch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Luzifer/go_helpers/v2/backoff"
|
||||
|
@ -22,12 +24,20 @@ const (
|
|||
twitchRequestTimeout = 2 * time.Second
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
type (
|
||||
Category struct {
|
||||
BoxArtURL string `json:"box_art_url"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
Client struct {
|
||||
clientID string
|
||||
token string
|
||||
|
||||
apiCache *APICache
|
||||
}
|
||||
)
|
||||
|
||||
func New(clientID, token string) *Client {
|
||||
return &Client{
|
||||
|
@ -140,6 +150,38 @@ func (c Client) GetFollowDate(from, to string) (time.Time, error) {
|
|||
return payload.Data[0].FollowedAt, nil
|
||||
}
|
||||
|
||||
func (c Client) SearchCategories(ctx context.Context, name string) ([]Category, error) {
|
||||
var out []Category
|
||||
|
||||
params := make(url.Values)
|
||||
params.Set("query", name)
|
||||
params.Set("first", "100")
|
||||
|
||||
var resp struct {
|
||||
Data []Category `json:"data"`
|
||||
Pagination struct {
|
||||
Cursor string `json:"cursor"`
|
||||
} `json:"pagination"`
|
||||
}
|
||||
|
||||
for {
|
||||
if err := c.request(ctx, http.MethodGet, fmt.Sprintf("https://api.twitch.tv/helix/search/categories?%s", params.Encode()), nil, &resp); err != nil {
|
||||
return nil, errors.Wrap(err, "executing request")
|
||||
}
|
||||
|
||||
out = append(out, resp.Data...)
|
||||
|
||||
if resp.Pagination.Cursor == "" {
|
||||
break
|
||||
}
|
||||
|
||||
params.Set("after", resp.Pagination.Cursor)
|
||||
resp.Pagination.Cursor = "" // Clear from struct as struct is reused
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c Client) HasLiveStream(username string) (bool, error) {
|
||||
cacheKey := []string{"hasLiveStream", username}
|
||||
if d := c.apiCache.Get(cacheKey); d != nil {
|
||||
|
@ -243,6 +285,58 @@ func (c Client) GetRecentStreamInfo(username string) (string, string, error) {
|
|||
return payload.Data[0].GameName, payload.Data[0].Title, nil
|
||||
}
|
||||
|
||||
func (c Client) ModifyChannelInformation(ctx context.Context, broadcaster string, game, title *string) error {
|
||||
if game == nil && title == nil {
|
||||
return errors.New("netiher game nor title provided")
|
||||
}
|
||||
|
||||
data := struct {
|
||||
GameID *string `json:"game_id,omitempty"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
}{
|
||||
Title: title,
|
||||
}
|
||||
|
||||
if game != nil {
|
||||
categories, err := c.SearchCategories(ctx, *game)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "searching for game")
|
||||
}
|
||||
|
||||
switch len(categories) {
|
||||
case 0:
|
||||
return errors.New("no matching game found")
|
||||
|
||||
case 1:
|
||||
data.GameID = &categories[0].ID
|
||||
|
||||
default:
|
||||
// Multiple matches: Search for exact one
|
||||
for _, c := range categories {
|
||||
if c.Name == *game {
|
||||
data.GameID = &c.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if data.GameID == nil {
|
||||
// No exact match found: This is an error
|
||||
return errors.New("no exact game match found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body := new(bytes.Buffer)
|
||||
if err := json.NewEncoder(body).Encode(data); err != nil {
|
||||
return errors.Wrap(err, "encoding payload")
|
||||
}
|
||||
|
||||
return errors.Wrap(
|
||||
c.request(ctx, http.MethodPost, fmt.Sprintf("https://api.twitch.tv/helix/channels?broadcaster_id=%s", broadcaster), body, nil),
|
||||
"executing request",
|
||||
)
|
||||
}
|
||||
|
||||
func (c Client) request(ctx context.Context, method, url string, body io.Reader, out interface{}) error {
|
||||
log.WithFields(log.Fields{
|
||||
"method": method,
|
||||
|
@ -267,6 +361,10 @@ func (c Client) request(ctx context.Context, method, url string, body io.Reader,
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if out == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Wrap(
|
||||
json.NewDecoder(resp.Body).Decode(out),
|
||||
"parse user info",
|
||||
|
|
Loading…
Reference in a new issue