twitch-my-emotes/main.go

140 lines
3.6 KiB
Go
Raw Permalink Normal View History

2024-03-09 10:35:02 +00:00
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
}