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 }