From 6f865b7a53ed78b1c0768156b1da78ba350d6cee Mon Sep 17 00:00:00 2001 From: Zorchenhimer Date: Sun, 17 Mar 2019 23:46:46 -0400 Subject: [PATCH] Start adding support to download twitch emotes The code has been transferred over from the utility, and should work, but it hasn't been tested nor has it been linked to the command. --- common/chatcommands.go | 1 + emotes.go | 165 +++++++++++++++++++++++++++++++++++++++++ settings.go | 1 + 3 files changed, 167 insertions(+) create mode 100644 emotes.go diff --git a/common/chatcommands.go b/common/chatcommands.go index e9817a6..5b429e4 100644 --- a/common/chatcommands.go +++ b/common/chatcommands.go @@ -35,6 +35,7 @@ var ( CNReloadEmotes ChatCommandNames = []string{"reloademotes"} CNModpass ChatCommandNames = []string{"modpass"} CNIP ChatCommandNames = []string{"iplist"} + CNAddEmotes ChatCommandNames = []string{"addemotes"} ) var ChatCommands = []ChatCommandNames{ diff --git a/emotes.go b/emotes.go new file mode 100644 index 0000000..7f3df62 --- /dev/null +++ b/emotes.go @@ -0,0 +1,165 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "strings" +) + +type twitchChannel struct { + ChannelName string `json:"channel_name"` + DisplayName string `json:"display_name"` + ChannelId string `json:"channel_id"` + BroadcasterType string `json:"broadcaster_type"` + Plans map[string]string `json:"plans"` + Emotes []struct { + Code string `json:"code"` + Set string `json:"emoticon_set"` + Id int `json:"id"` + } `json:"emotes"` + BaseSetId string `json:"base_set_id"` + GeneratedAt string `json:"generated_at"` +} + +// Used in settings +type EmoteSet struct { + Channel string // channel name + Prefix string // emote prefix + Found bool `json:"-"` +} + +const subscriberJson string = `subscribers.json` + +// Download a single channel's emote set +func (tc *twitchChannel) downloadEmotes() (*EmoteSet, error) { + es := &EmoteSet{Channel: strings.ToLower(tc.ChannelName)} + for _, emote := range tc.Emotes { + url := fmt.Sprintf(`https://static-cdn.jtvnw.net/emoticons/v1/%d/1.0`, emote.Id) + png := `static/emotes/` + emote.Code + `.png` + + if len(es.Prefix) == 0 { + // For each letter + for i := 0; i < len(emote.Code); i++ { + // Find the first capital + b := emote.Code[i] + if b >= 'A' && b <= 'Z' { + es.Prefix = emote.Code[0 : i-1] + fmt.Printf("Found prefix for channel %q: %q (%q)\n", es.Channel, es.Prefix, emote) + break + } + } + } + + resp, err := http.Get(url) + if err != nil { + return nil, err + } + + f, err := os.Create(png) + if err != nil { + return nil, err + } + + _, err = io.Copy(f, resp.Body) + if err != nil { + return nil, err + } + } + + return es, nil +} + +func GetEmotes(names []string) ([]*EmoteSet, error) { + // Do this up-front + for i := 0; i < len(names); i++ { + names[i] = strings.ToLower(names[i]) + } + + channels, err := findChannels(names) + if err != nil { + return nil, fmt.Errorf("Error reading %q: %v", subscriberJson, err) + } + + emoteSets := []*EmoteSet{} + for _, c := range channels { + es, err := c.downloadEmotes() + if err != nil { + return nil, fmt.Errorf("Error downloading emotes: %v", err) + } + emoteSets = append(emoteSets, es) + } + + for _, es := range emoteSets { + found := false + for _, name := range names { + if es.Channel == name { + found = true + break + } + } + if !found { + es.Found = false + } + } + + return emoteSets, nil +} + +func findChannels(names []string) ([]twitchChannel, error) { + file, err := os.Open(subscriberJson) + if err != nil { + return nil, err + } + defer file.Close() + + data := []twitchChannel{} + dec := json.NewDecoder(file) + + // Open bracket + _, err = dec.Token() + if err != nil { + return nil, err + } + + done := false + for dec.More() && !done { + // opening bracket of channel + _, err = dec.Token() + if err != nil { + return nil, err + } + + // Decode the channel stuff + var c twitchChannel + err = dec.Decode(&c) + if err != nil { + return nil, err + } + + // Is this a channel we are looking for? + found := false + for _, search := range names { + if strings.ToLower(c.ChannelName) == search { + found = true + break + } + } + + // Yes it is. Add it to the data + if found { + data = append(data, c) + } + + // Check for completion. Don't bother parsing the rest of + // the json file if we've already found everything that we're + // looking for. + if len(data) == len(names) { + done = true + } + } + + return data, nil +} diff --git a/settings.go b/settings.go index 278c523..7143cea 100644 --- a/settings.go +++ b/settings.go @@ -28,6 +28,7 @@ type Settings struct { AdminPassword string StreamKey string ListenAddress string + ApprovedEmotes []EmoteSet // list of channels that have been approved for emote use. Global emotes are always "approved". Bans []BanInfo LogLevel common.LogLevel LogFile string