fix twitch APIs and usage values

This commit is contained in:
Martin Thielecke 2022-05-07 13:27:37 +02:00
parent 780d258cb4
commit 1bb87788f8
Signed by: mthie
GPG Key ID: D1D25A85C8604DFB
5 changed files with 140 additions and 154 deletions

View File

@ -1,72 +1,73 @@
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
"io/ioutil"
"net/http" "net/http"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
type TwitchChannel struct { type TwitchChannels struct {
ID string `json:"_id"` Data []*TwitchChannel `json:"data"`
Pagination *TwitchPagination `json:"pagination"`
BroadcasterLanguage string `json:"broadcaster_language"`
BroadcasterType string `json:"broadcaster_type"`
CreatedAt string `json:"created_at"`
DisplayName string `json:"display_name"`
Email string `json:"email"`
Followers int64 `json:"followers"`
Game string `json:"game"`
Language string `json:"language"`
Logo string `json:"logo"`
Mature bool `json:"mature"`
Name string `json:"name"`
Partner bool `json:"partner"`
ProfileBanner string `json:"profile_banner"`
ProfileBannerBackgroundColor string `json:"profile_banner_background_color"`
Status string `json:"status"`
StreamKey string `json:"stream_key"`
UpdatedAt string `json:"updated_at"`
URL string `json:"url"`
VideoBanner string `json:"video_banner"`
Views int64 `json:"views"`
} }
func getChannel(u *User) error { type TwitchChannel struct {
client := &http.Client{} BroadcasterID string `json:"broadcaster_id"`
req, err := http.NewRequest("GET", "https://api.twitch.tv/kraken/channel", nil) BroadcasterLogin string `json:"broadcaster_login"`
if err != nil { BroadcasterName string `json:"broadcaster_name"`
log.WithError(err).Error("Unable to create http request to get twitch channel data") BroadcasterLanguage string `json:"broadcaster_language"`
return err GameID string `json:"game_id"`
GameName string `json:"game_name"`
Title string `json:"title"`
Delay int `json:"delay"`
}
func getChannel(u *User) {
result := &TwitchChannels{}
after := ""
for {
client := twitchOauthConfig.Client(context.Background(), u.Token)
req, err := http.NewRequest("GET", "https://api.twitch.tv/helix/channels?broadcaster_id="+u.ID+after, nil)
if err != nil {
log.WithError(err).Error("Unable to create http request to get twitch streams data")
return
}
req.Header.Set("Client-ID", settings.ClientID)
resp, err := client.Do(req)
if err != nil {
log.WithError(err).Error("Unable to get twitch stream data")
return
}
t := &TwitchChannels{}
if err := json.NewDecoder(resp.Body).Decode(&t); err != nil {
log.WithError(err).Error("Unable to parse twitch streams data")
}
resp.Body.Close()
if len(t.Data) == 0 {
break
}
result.Data = append(result.Data, t.Data...)
if t.Pagination == nil || t.Pagination.Cursor == "" {
break
}
after = "&after=" + t.Pagination.Cursor
} }
req.Header.Set("Client-ID", settings.ClientID) if len(result.Data) < 1 {
req.Header.Set("Accept", "application/vnd.twitchtv.v5+json") log.Info("No streams")
req.Header.Set("Authorization", "OAuth "+u.Token.AccessToken) return
resp, err := client.Do(req)
if err != nil {
log.WithError(err).Error("Unable to get twitch channel data")
return err
} }
defer resp.Body.Close() if len(result.Data) > 0 {
b, err := ioutil.ReadAll(resp.Body) u.TwitchChannel = result.Data[0]
if err != nil {
log.WithError(err).Error("Unable to read response")
} }
c := &TwitchChannel{}
if err := json.Unmarshal(b, c); err != nil {
log.WithError(err).Error("Unable to parse twitch channel data")
log.Infof("Data: %s", string(b))
return err
}
u.TwitchChannel = c
return nil
} }
func (c *TwitchChannel) SaveFiles() { func (c *TwitchChannel) SaveFiles() {

View File

@ -1,13 +1,10 @@
package main package main
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"io/ioutil"
"net/http" "net/http"
"os" "strconv"
"strings"
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -81,36 +78,22 @@ func getFollows(u *User, max int) {
u.TwitchFollowers = result u.TwitchFollowers = result
} }
func readActiveUsers() map[string]bool {
res, err := ioutil.ReadFile(os.Args[1])
if err != nil {
log.WithError(err).Fatal("Unable to read active users")
}
data := make(map[string]bool)
if err := json.NewDecoder(bytes.NewBuffer(res)).Decode(&data); err != nil {
log.WithError(err).Fatal("Unable to decode json")
}
return data
}
func (f *TwitchFollowers) SaveFiles() { func (f *TwitchFollowers) SaveFiles() {
fs := []string{} saveContent("followers", "total", strconv.FormatInt(f.Total, 10))
active := readActiveUsers() saveJSON("followers", "complete_list", f)
ids := []string{}
for _, follower := range f.Data { /*
name := strings.ToLower(follower.FromName) fs := []string{}
id := follower.FromID ids := []string{}
if _, ok := active[name]; ok {
continue for _, follower := range f.Data {
name := strings.ToLower(follower.FromName)
id := follower.FromID
fs = append(fs, name)
ids = append(ids, id)
} }
fs = append(fs, name)
ids = append(ids, id)
}
saveJSON("followers", "inactive", fs) saveJSON("followers", "inactive", fs)
saveContent("followers", "inactive_ids", strings.Join(ids, "\n")) saveContent("followers", "inactive_ids", strings.Join(ids, "\n"))
*/
} }

37
main.go
View File

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"os"
"time" "time"
"github.com/Luzifer/rconfig" "github.com/Luzifer/rconfig"
@ -49,7 +48,18 @@ func main() {
go func() { go func() {
if user != nil { if user != nil {
handleSaves() handleSaves()
os.Exit(0) }
interval := 20 * time.Second
if settings.UpdateInterval > 10*time.Second {
interval = settings.UpdateInterval
}
log.Infof("Update interval: %s", interval)
c := time.Tick(interval)
for range c {
if user == nil {
continue
}
handleSaves()
} }
}() }()
@ -63,16 +73,39 @@ func handleSaves() {
} }
getUser(user) getUser(user)
if user.TwitchUser != nil {
user.TwitchUser.SaveFiles()
}
getChannel(user)
if user.TwitchChannel != nil {
user.TwitchChannel.SaveFiles()
}
maxFollower := -1 maxFollower := -1
if settings.MaxFollowers > 0 { if settings.MaxFollowers > 0 {
maxFollower = settings.MaxFollowers maxFollower = settings.MaxFollowers
} }
log.Infof("Fetch max follower: %d", maxFollower) log.Infof("Fetch max follower: %d", maxFollower)
getFollows(user, maxFollower) getFollows(user, maxFollower)
if user.TwitchFollowers != nil { if user.TwitchFollowers != nil {
user.TwitchFollowers.SaveFiles() user.TwitchFollowers.SaveFiles()
} }
maxSubs := -1
if settings.MaxSubs > 0 {
maxSubs = settings.MaxSubs
}
log.Infof("Fetch max subs: %d", maxSubs)
getSubs(user, maxSubs)
if user.TwitchSubscriptions != nil {
user.TwitchSubscriptions.SaveFiles()
}
getStreams(user)
if user.TwitchStream != nil {
user.TwitchStream.SaveFiles()
}
} }
func handleTwitchLogin(w http.ResponseWriter, r *http.Request) { func handleTwitchLogin(w http.ResponseWriter, r *http.Request) {

92
subs.go
View File

@ -1,109 +1,83 @@
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"sort"
"strconv" "strconv"
"strings"
"time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
type TwitchSubscriptions struct { type TwitchSubscriptions struct {
Total int64 `json:"_total"` Data []*TwitchSubscription `json:"data"`
Subscriptions []*TwitchSubscription `json:"subscriptions"` Pagination *TwitchPagination `json:"pagination"`
Total int64 `json:"total"`
Points int64 `json:"points"`
} }
type TwitchSubscription struct { type TwitchSubscription struct {
ID string `json:"_id"` BroadcasterID string `json:"broadcaster_id"`
CreatedAt time.Time `json:"created_at"` BroadcasterLogin string `json:"broadcaster_login"`
IsGift bool `json:"is_gift"` BroadcasterName string `json:"broadcaster_name"`
SubPlan string `json:"sub_plan"` GifterID string `json:"gifter_id"`
SubPlanName string `json:"sub_plan_name"` GifterLogin string `json:"gifter_login"`
User *TwitchSubUser `json:"user"` GifterName string `json:"gifter_name"`
} IsGift bool `json:"is_gift"`
Tier string `json:"gift"`
type TwitchSubUser struct { PlanName string `json:"plan_name"`
ID string `json:"_id"` UserID string `json:"user_id"`
Bio string `json:"bio"` UserLogin string `json:"user_login"`
CreatedAt time.Time `json:"created_at"` UserName string `json:"user_name"`
DisplayName string `json:"display_name"`
Logo string `json:"logo"`
Name string `json:"name"`
Type string `json:"type"`
UpdatedAt time.Time `json:"updated_at"`
} }
func getSubs(u *User, max int) { func getSubs(u *User, max int) {
result := &TwitchSubscriptions{} result := &TwitchSubscriptions{}
limit := 100 after := ""
offset := 0
for { for {
client := http.Client{} client := twitchOauthConfig.Client(context.Background(), u.Token)
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.twitch.tv/kraken/channels/%s/subscriptions?limit=%d&offset=%d", u.TwitchChannel.ID, limit, offset), nil) req, err := http.NewRequest("GET", "https://api.twitch.tv/helix/subscriptions?broadcaster_id="+u.ID+after, nil)
if err != nil { if err != nil {
log.WithError(err).Error("Unable to create http request to get twitch subs data") log.WithError(err).Error("Unable to create http request to get twitch streams data")
return return
} }
req.Header.Set("Client-ID", settings.ClientID) req.Header.Set("Client-ID", settings.ClientID)
req.Header.Set("Accept", "application/vnd.twitchtv.v5+json")
req.Header.Set("Authorization", "OAuth "+u.Token.AccessToken)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
log.WithError(err).Error("Unable to get twitch subs data") log.WithError(err).Error("Unable to get twitch stream data")
return return
} }
t := &TwitchSubscriptions{} t := &TwitchSubscriptions{}
if err := json.NewDecoder(resp.Body).Decode(&t); err != nil { if err := json.NewDecoder(resp.Body).Decode(&t); err != nil {
log.WithError(err).Error("Unable to parse twitch followers data") log.WithError(err).Error("Unable to parse twitch streams data")
} }
resp.Body.Close() resp.Body.Close()
if len(t.Subscriptions) == 0 { if len(t.Data) == 0 {
break break
} }
result.Total = t.Total result.Data = append(result.Data, t.Data...)
result.Subscriptions = append(result.Subscriptions, t.Subscriptions...) if t.Pagination == nil || t.Pagination.Cursor == "" {
if max > -1 && len(result.Subscriptions) >= max {
break break
} }
offset += limit after = "&after=" + t.Pagination.Cursor
} }
if len(result.Subscriptions) < 1 { if len(result.Data) < 1 {
log.Info("No Subs") log.Info("No streams")
return return
} }
u.TwitchSubscriptions = result if len(result.Data) > 0 {
u.TwitchSubscriptions = result
}
} }
func (s *TwitchSubscriptions) SaveFiles() { func (s *TwitchSubscriptions) SaveFiles() {
saveContent("subscriptions", "total", strconv.FormatInt(s.Total, 10)) saveContent("subscriptions", "total", strconv.FormatInt(s.Total, 10))
saveJSON("subscriptions", "complete_list", s) saveJSON("subscriptions", "complete_list", s)
sort.Slice(s.Subscriptions, func(i, j int) bool {
return s.Subscriptions[i].CreatedAt.Before(s.Subscriptions[j].CreatedAt)
})
start := len(s.Subscriptions) - 10
if start < 0 {
start = 0
}
lastSubs := s.Subscriptions[start:]
var lastSubsSlice []string
for _, v := range lastSubs {
lastSubsSlice = append(lastSubsSlice, v.User.DisplayName)
}
saveContent("subscriptions", "last_ten", strings.Join(lastSubsSlice, "\n"))
} }

11
user.go
View File

@ -14,9 +14,7 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
var ( var user *User
user *User
)
type WebUser struct { type WebUser struct {
ID string ID string
@ -119,7 +117,7 @@ func getUser(u *User) error {
func saveUser() { func saveUser() {
log.Info("Save user") log.Info("Save user")
f, err := os.OpenFile(".user.json", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) f, err := os.OpenFile(".user.json", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil { if err != nil {
log.WithError(err).Fatal("Unable to open file") log.WithError(err).Fatal("Unable to open file")
} }
@ -155,10 +153,7 @@ func loadUser() {
return return
} }
if err := getChannel(u); err != nil { getChannel(u)
log.WithError(err).Error("Unable to get channel information")
return
}
user = u user = u
} }