139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/Luzifer/go_helpers/v2/str"
|
|
"github.com/Luzifer/rconfig/v2"
|
|
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
|
|
)
|
|
|
|
var (
|
|
cfg = struct {
|
|
ExcludeEmoteType []string `flag:"exclude-emote-type,e" default:"" description:"Filter out these emote types"`
|
|
IncludeEmoteType []string `flag:"include-emote-type,i" default:"" description:"Only return these emote types"`
|
|
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
|
|
Output string `flag:"output,o" default:"emote-code" description:"Output format: emote-code, json"`
|
|
TwitchClientID string `flag:"twitch-client-id" default:"" description:"ClientID to token belongs to"`
|
|
TwitchClientSecret string `flag:"twitch-client-secret" default:"" description:"ClientSecret for the ClientID"`
|
|
TwitchToken string `flag:"twitch-token" default:"" description:"Token to identify the user"`
|
|
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
|
}{}
|
|
|
|
version = "dev"
|
|
)
|
|
|
|
func initApp() error {
|
|
rconfig.AutoEnv(true)
|
|
if err := rconfig.ParseAndValidate(&cfg); err != nil {
|
|
return errors.Wrap(err, "parsing cli options")
|
|
}
|
|
|
|
l, err := logrus.ParseLevel(cfg.LogLevel)
|
|
if err != nil {
|
|
return errors.Wrap(err, "parsing log-level")
|
|
}
|
|
logrus.SetLevel(l)
|
|
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
var err error
|
|
if err = initApp(); err != nil {
|
|
logrus.WithError(err).Fatal("initializing app")
|
|
}
|
|
|
|
if cfg.VersionAndExit {
|
|
logrus.WithField("version", version).Info("twitch-my-emotes")
|
|
os.Exit(0)
|
|
}
|
|
|
|
if len(cfg.ExcludeEmoteType) == 1 && cfg.ExcludeEmoteType[0] == "" {
|
|
cfg.ExcludeEmoteType = nil
|
|
}
|
|
|
|
if len(cfg.IncludeEmoteType) == 1 && cfg.IncludeEmoteType[0] == "" {
|
|
cfg.IncludeEmoteType = nil
|
|
}
|
|
|
|
emotes, err := collectEmotes()
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("fetching emotes")
|
|
}
|
|
|
|
switch cfg.Output {
|
|
case "emote-code":
|
|
var codes []string
|
|
for _, e := range emotes {
|
|
codes = append(codes, e.Name)
|
|
}
|
|
|
|
sort.Strings(codes)
|
|
fmt.Println(strings.Join(codes, "\n")) //nolint:forbidigo // Okay for this case
|
|
|
|
case "json":
|
|
if err = json.NewEncoder(os.Stdout).Encode(emotes); err != nil {
|
|
logrus.WithError(err).Fatal("encoding emotes to JSON")
|
|
}
|
|
|
|
default:
|
|
logrus.Fatal("unknown output format")
|
|
}
|
|
}
|
|
|
|
func collectEmotes() ([]emote, error) {
|
|
client := twitch.New(cfg.TwitchClientID, cfg.TwitchClientSecret, cfg.TwitchToken, "")
|
|
userID, _, err := client.GetAuthorizedUser(context.Background())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("fetching logged-in user: %w", err)
|
|
}
|
|
|
|
var (
|
|
emotes []emote
|
|
params = make(url.Values)
|
|
)
|
|
params.Set("user_id", userID)
|
|
|
|
for {
|
|
var payload emoteResponse
|
|
|
|
if err = client.Request(context.Background(), twitch.ClientRequestOpts{
|
|
AuthType: twitch.AuthTypeBearerToken,
|
|
Method: http.MethodGet,
|
|
OKStatus: http.StatusOK,
|
|
Out: &payload,
|
|
URL: fmt.Sprintf("https://api.twitch.tv/helix/chat/emotes/user?%s", params.Encode()),
|
|
}); err != nil {
|
|
return nil, fmt.Errorf("fetching emotes: %w", err)
|
|
}
|
|
|
|
logrus.Debugf("fetched %d emotes", len(payload.Data))
|
|
|
|
for _, e := range payload.Data {
|
|
if str.StringInSlice(e.EmoteType, cfg.ExcludeEmoteType) || len(cfg.IncludeEmoteType) > 0 && !str.StringInSlice(e.EmoteType, cfg.IncludeEmoteType) {
|
|
continue
|
|
}
|
|
|
|
emotes = append(emotes, e)
|
|
}
|
|
|
|
if payload.Pagination == nil || payload.Pagination.Cursor == "" {
|
|
break
|
|
}
|
|
|
|
params.Set("after", payload.Pagination.Cursor)
|
|
}
|
|
|
|
return emotes, nil
|
|
}
|