justlog/helix/user.go

209 lines
5.3 KiB
Go
Raw Permalink Normal View History

2018-12-02 14:53:01 +01:00
package helix
2018-12-02 19:23:54 +01:00
import (
"net/http"
"strings"
"sync"
2020-05-05 22:45:58 +02:00
"time"
2018-12-02 19:23:54 +01:00
2020-03-02 20:33:58 +01:00
helixClient "github.com/nicklaw5/helix"
2018-12-02 19:23:54 +01:00
log "github.com/sirupsen/logrus"
)
2020-03-02 20:33:58 +01:00
// Client wrapper for helix
2018-12-02 19:23:54 +01:00
type Client struct {
2020-05-05 22:45:58 +02:00
clientID string
clientSecret string
appAccessToken string
client *helixClient.Client
httpClient *http.Client
2018-12-02 19:23:54 +01:00
}
var (
userCacheByID sync.Map
userCacheByUsername sync.Map
2018-12-02 19:23:54 +01:00
)
2020-12-06 16:36:16 +01:00
type TwitchApiClient interface {
GetUsersByUserIds([]string) (map[string]UserData, error)
GetUsersByUsernames([]string) (map[string]UserData, error)
}
2020-03-02 20:33:58 +01:00
// NewClient Create helix client
2020-05-05 22:04:11 +02:00
func NewClient(clientID string, clientSecret string) Client {
2020-03-02 20:33:58 +01:00
client, err := helixClient.NewClient(&helixClient.Options{
2020-05-05 22:04:11 +02:00
ClientID: clientID,
ClientSecret: clientSecret,
2020-03-02 20:33:58 +01:00
})
if err != nil {
panic(err)
}
2020-11-08 19:07:35 +01:00
resp, err := client.RequestAppAccessToken([]string{})
2020-05-05 22:45:58 +02:00
if err != nil {
panic(err)
}
log.Infof("Requested access token, response: %d, expires in: %d", resp.StatusCode, resp.Data.ExpiresIn)
client.SetAppAccessToken(resp.Data.AccessToken)
2018-12-02 19:23:54 +01:00
return Client{
2020-05-05 22:45:58 +02:00
clientID: clientID,
clientSecret: clientSecret,
2020-05-05 22:47:56 +02:00
appAccessToken: resp.Data.AccessToken,
2020-05-05 22:45:58 +02:00
client: client,
httpClient: &http.Client{},
2018-12-02 19:23:54 +01:00
}
}
2020-03-02 20:33:58 +01:00
// UserData exported data from twitch
2018-12-02 19:23:54 +01:00
type UserData 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"`
}
2020-05-05 22:45:58 +02:00
// StartRefreshTokenRoutine refresh our token
func (c *Client) StartRefreshTokenRoutine() {
ticker := time.NewTicker(24 * time.Hour)
for range ticker.C {
2020-11-08 19:07:35 +01:00
resp, err := c.client.RequestAppAccessToken([]string{})
2020-05-05 22:45:58 +02:00
if err != nil {
log.Error(err)
continue
}
log.Infof("Requested access token from routine, response: %d, expires in: %d", resp.StatusCode, resp.Data.ExpiresIn)
c.client.SetAppAccessToken(resp.Data.AccessToken)
}
}
func chunkBy(items []string, chunkSize int) (chunks [][]string) {
for chunkSize < len(items) {
items, chunks = items[chunkSize:], append(chunks, items[0:chunkSize:chunkSize])
}
return append(chunks, items)
}
2020-03-02 20:33:58 +01:00
// GetUsersByUserIds receive userData for given ids
2018-12-02 19:23:54 +01:00
func (c *Client) GetUsersByUserIds(userIDs []string) (map[string]UserData, error) {
var filteredUserIDs []string
for _, id := range userIDs {
if _, ok := userCacheByID.Load(id); !ok {
2018-12-02 19:23:54 +01:00
filteredUserIDs = append(filteredUserIDs, id)
}
}
2020-03-02 20:33:58 +01:00
if len(filteredUserIDs) > 0 {
chunks := chunkBy(filteredUserIDs, 100)
for _, chunk := range chunks {
resp, err := c.client.GetUsers(&helixClient.UsersParams{
IDs: chunk,
})
if err != nil {
return map[string]UserData{}, err
}
2018-12-02 19:23:54 +01:00
log.Infof("%d GetUsersByUserIds %v", resp.StatusCode, chunk)
for _, user := range resp.Data.Users {
data := &UserData{
ID: user.ID,
Login: user.Login,
DisplayName: user.Login,
Type: user.Type,
BroadcasterType: user.BroadcasterType,
Description: user.Description,
ProfileImageURL: user.ProfileImageURL,
OfflineImageURL: user.OfflineImageURL,
ViewCount: user.ViewCount,
Email: user.Email,
}
userCacheByID.Store(user.ID, data)
userCacheByUsername.Store(user.Login, data)
2018-12-02 19:23:54 +01:00
}
}
}
result := make(map[string]UserData)
for _, id := range userIDs {
value, ok := userCacheByID.Load(id)
if !ok {
log.Debugf("Could not find userId, channel might be banned: %s", id)
2020-04-13 17:44:15 +02:00
continue
}
result[id] = *(value.(*UserData))
2018-12-02 19:23:54 +01:00
}
return result, nil
}
// GetUsersByUsernames fetches userdata from helix
2018-12-02 19:23:54 +01:00
func (c *Client) GetUsersByUsernames(usernames []string) (map[string]UserData, error) {
var filteredUsernames []string
for _, username := range usernames {
username = strings.ToLower(username)
if _, ok := userCacheByUsername.Load(username); !ok {
filteredUsernames = append(filteredUsernames, username)
2018-12-02 19:23:54 +01:00
}
}
2020-03-02 20:33:58 +01:00
if len(filteredUsernames) > 0 {
chunks := chunkBy(filteredUsernames, 100)
for _, chunk := range chunks {
resp, err := c.client.GetUsers(&helixClient.UsersParams{
Logins: chunk,
})
if err != nil {
return map[string]UserData{}, err
}
2018-12-02 19:23:54 +01:00
log.Infof("%d GetUsersByUsernames %v", resp.StatusCode, chunk)
for _, user := range resp.Data.Users {
data := &UserData{
ID: user.ID,
Login: user.Login,
DisplayName: user.Login,
Type: user.Type,
BroadcasterType: user.BroadcasterType,
Description: user.Description,
ProfileImageURL: user.ProfileImageURL,
OfflineImageURL: user.OfflineImageURL,
ViewCount: user.ViewCount,
Email: user.Email,
}
userCacheByID.Store(user.ID, data)
userCacheByUsername.Store(user.Login, data)
2018-12-02 19:23:54 +01:00
}
}
}
result := make(map[string]UserData)
for _, username := range usernames {
username = strings.ToLower(username)
value, ok := userCacheByUsername.Load(username)
if !ok {
log.Debugf("Could not find username, channel might be banned: %s", username)
2020-04-13 17:44:15 +02:00
continue
}
result[username] = *(value.(*UserData))
2018-12-02 19:23:54 +01:00
}
return result, nil
}