Skip to content

Commit

Permalink
twitch: add Get Streams
Browse files Browse the repository at this point in the history
  • Loading branch information
zephyrtronium committed Aug 9, 2024
1 parent 9c993a9 commit a6bff33
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 1 deletion.
60 changes: 60 additions & 0 deletions twitch/streams.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package twitch

import (
"context"
"fmt"
"net/url"
"time"
)

// Stream is the response type from https://dev.twitch.tv/docs/api/reference/#get-streams.
type Stream struct {
ID string `json:"id"`
UserID string `json:"user_id"`
UserLogin string `json:"user_login"`
UserName string `json:"user_name"`
GameID string `json:"game_id"`
GameName string `json:"game_name"`
Type string `json:"type"`
Title string `json:"title"`
Tags []string `json:"tags"`
ViewerCount int `json:"viewer_count"`
StartedAt time.Time `json:"started_at"`
Language string `json:"language"`
ThumbnailURL string `json:"thumbnail_url"`
IsMature bool `json:"is_mature"`
// tag_ids is deprecated and always empty
}

// UserStreams gets stream information for a list of up to 100 users.
// For each stream in the given list, if the user ID is provided, then the
// query is made by user ID, and otherwise it is made by user login.
// (NOTE: the ID field is not used as input; only UserID.)
// Logins used to search are normalized to lower case.
// The result reuses the memory in streams, but may be of different length and
// in any order.
//
// If a given stream has both an ID and a login, the ID is used and the login
// is replaced with the result from the API.
// If a stream has neither, it is ignored.
func UserStreams(ctx context.Context, client Client, streams []Stream) ([]Stream, error) {
v := url.Values{
"user_id": nil,
"user_login": nil,
}
for _, s := range streams {
if s.UserID != "" {
v["user_id"] = append(v["user_id"], s.UserID)
continue
}
if s.UserLogin != "" {
v["user_login"] = append(v["user_login"], s.UserLogin)
}
}
url := apiurl("/helix/streams", v)
err := reqjson(ctx, client, "GET", url, nil, &streams)
if err != nil {
return nil, fmt.Errorf("couldn't get streams info: %w", err)
}
return streams, nil
}
57 changes: 57 additions & 0 deletions twitch/streams_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package twitch

import (
"context"
"net/http"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"golang.org/x/oauth2"
)

func TestUserStreams(t *testing.T) {
// Test that we successfully interpret the example response in the API doc.
t.Run("decode", func(t *testing.T) {
spy := apiresp(200, "streams.json")
cl := Client{
HTTP: &http.Client{
Transport: spy,
},
Token: &oauth2.Token{
AccessToken: "bocchi",
},
}
s := []Stream{
{UserID: "98765"},
{UserLogin: "sandysanderman"},
}
s, err := UserStreams(context.Background(), cl, s)
if err != nil {
t.Error(err)
}
if len(s) != 1 {
t.Fatalf("wrong number of results: want 1, got %d", len(s))
}
want := Stream{
ID: "40952121085",
UserID: "101051819",
UserLogin: "afro",
UserName: "Afro",
GameID: "32982",
GameName: "Grand Theft Auto V",
Type: "live",
Title: "Jacob: Digital Den Laptops & Routers | NoPixel | !MAINGEAR !FCF",
Tags: []string{"English"},
ViewerCount: 1490,
StartedAt: time.Date(2021, 3, 10, 3, 18, 11, 0, time.UTC),
Language: "en",
ThumbnailURL: "https://static-cdn.jtvnw.net/previews-ttv/live_user_afro-{width}x{height}.jpg",
IsMature: false,
}
got := s[0]
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("wrong result (+got/-want):\n%s", diff)
}
})
}
24 changes: 24 additions & 0 deletions twitch/testdata/streams.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"data": [
{
"id": "40952121085",
"user_id": "101051819",
"user_login": "afro",
"user_name": "Afro",
"game_id": "32982",
"game_name": "Grand Theft Auto V",
"type": "live",
"title": "Jacob: Digital Den Laptops & Routers | NoPixel | !MAINGEAR !FCF",
"tags": [
"English"
],
"viewer_count": 1490,
"started_at": "2021-03-10T03:18:11Z",
"language": "en",
"thumbnail_url": "https://static-cdn.jtvnw.net/previews-ttv/live_user_afro-{width}x{height}.jpg",
"tag_ids": [],
"is_mature": false
}
],
"pagination": {}
}
20 changes: 19 additions & 1 deletion twitch/twitch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package twitch

import (
"context"
_ "embed"
"embed"
"errors"
"io"
"net/http"
"path"
"strings"
"testing"

Expand All @@ -27,6 +28,23 @@ func (r *reqspy) RoundTrip(req *http.Request) (*http.Response, error) {
return r.respond, nil
}

//go:embed testdata/*.json
var jsonFiles embed.FS

// apiresp creates a reqspy responding with the given testdata document.
func apiresp(status int, file string) *reqspy {
f, err := jsonFiles.Open(path.Join("testdata/", file))
if err != nil {
panic(err)
}
return &reqspy{
respond: &http.Response{
StatusCode: status,
Body: f,
},
}
}

func TestReqJSON(t *testing.T) {
t.Run("ok", func(t *testing.T) {
spy := &reqspy{
Expand Down

0 comments on commit a6bff33

Please sign in to comment.