From a73375f1527302875fdbeda22dceb733a01f65e2 Mon Sep 17 00:00:00 2001 From: Zorchenhimer Date: Tue, 18 Jun 2019 22:13:53 -0400 Subject: [PATCH] Fix emotes This reworks how emotes are cached in relation to their physical location on disk. Functionally, they should be the same from the user perspective but it sets up some stuff that will make it easier to add emotes from various sources. --- chatcommands.go | 30 +++++---- chatroom.go | 2 +- common/emotes.go | 38 +++++++++--- common/emotes_test.go | 20 ++---- emotes.go | 138 +++++++++++++++++++++--------------------- handlers.go | 3 +- settings_example.json | 2 +- 7 files changed, 128 insertions(+), 105 deletions(-) diff --git a/chatcommands.go b/chatcommands.go index c18b6e3..40fa62a 100644 --- a/chatcommands.go +++ b/chatcommands.go @@ -438,19 +438,27 @@ var commands = &CommandControl{ common.CNReloadEmotes.String(): Command{ HelpText: "Reload the emotes on the server.", Function: func(cl *Client, args []string) (string, error) { - cl.SendServerMessage("Reloading emotes") - err := loadEmotes() - if err != nil { - common.LogErrorf("Unbale to reload emotes: %s\n", err) - return "", err - } + go func() { + cl.SendServerMessage("Reloading emotes") + err := loadEmotes() + if err != nil { + common.LogErrorf("Unbale to reload emotes: %s\n", err) + //return "", err - cl.belongsTo.AddChatMsg(common.NewChatHiddenMessage(common.CdEmote, common.Emotes)) - cl.belongsTo.AddModNotice(cl.name + " has reloaded emotes") + cl.SendChatData(common.NewChatMessage("", "", + err.Error(), + common.CmdlUser, common.MsgCommandResponse)) + return + } - num := len(Emotes) - common.LogInfof("Loaded %d emotes\n", num) - return fmt.Sprintf("Emotes loaded: %d", num), nil + cl.belongsTo.AddChatMsg(common.NewChatHiddenMessage(common.CdEmote, common.Emotes)) + cl.belongsTo.AddModNotice(cl.name + " has reloaded emotes") + + num := len(common.Emotes) + common.LogInfof("Loaded %d emotes\n", num) + cl.belongsTo.AddModNotice(fmt.Sprintf("%s reloaded %d emotes.", cl.name, num)) + }() + return "Reloading emotes...", nil }, }, diff --git a/chatroom.go b/chatroom.go index 1111189..c0c6342 100644 --- a/chatroom.go +++ b/chatroom.go @@ -40,7 +40,7 @@ func newChatRoom() (*ChatRoom, error) { if err != nil { return nil, fmt.Errorf("error loading emotes: %s", err) } - common.LogInfof("Loaded %d emotes\n", len(Emotes)) + common.LogInfof("Loaded %d emotes\n", len(common.Emotes)) //the "heartbeat" for broadcasting messages go cr.Broadcast() diff --git a/common/emotes.go b/common/emotes.go index 3bff321..fd49224 100644 --- a/common/emotes.go +++ b/common/emotes.go @@ -2,19 +2,41 @@ package common import ( "fmt" - "path" + "path/filepath" + "regexp" "strings" ) -var Emotes map[string]EmotePath +type EmotesMap map[string]string -type EmotePath struct { - Dir string - File string +var Emotes EmotesMap + +var reStripStatic = regexp.MustCompile(`^(\\|/)?static`) + +func init() { + Emotes = map[string]string{} } -func (e EmotePath) path() string { - return path.Join(e.Dir, e.File) +func (em EmotesMap) Add(fullpath string) { + fullpath = reStripStatic.ReplaceAllLiteralString(fullpath, "") + + base := filepath.Base(fullpath) + code := base[0 : len(base)-len(filepath.Ext(base))] + + _, exists := em[code] + + num := 0 + for exists { + num += 1 + _, exists = em[fmt.Sprintf("%s-%d", code, num)] + } + + if num > 0 { + code = fmt.Sprintf("%s-%d", code, num) + } + + Emotes[code] = fullpath + fmt.Printf("Added emote %s at path %q\n", code, fullpath) } func EmoteToHtml(file, title string) string { @@ -30,7 +52,7 @@ func ParseEmotesArray(words []string) []string { found := false for key, val := range Emotes { if key == wordTrimmed { - newWords = append(newWords, EmoteToHtml(val.File, key)) + newWords = append(newWords, EmoteToHtml(val, key)) found = true } } diff --git a/common/emotes_test.go b/common/emotes_test.go index 376dadb..bb31646 100644 --- a/common/emotes_test.go +++ b/common/emotes_test.go @@ -1,6 +1,7 @@ package common import ( + "os" "testing" ) @@ -25,21 +26,12 @@ var data_good = map[string]string{ } func TestMain(m *testing.M) { - Emotes = map[string]EmotePath{ - "one": EmotePath{ - Dir: "", - File: "one.png", - }, - "two": EmotePath{ - Dir: "", - File: "two.png", - }, - "three": EmotePath{ - Dir: "", - File: "three.gif", - }, + Emotes = map[string]string{ + "one": "/emotes/one.png", + "two": "/emotes/two.png", + "three": "/emotes/three.gif", } - // os.Exit(m.Run()) + os.Exit(m.Run()) } func TestEmotes_ParseEmotes(t *testing.T) { diff --git a/emotes.go b/emotes.go index c9a0431..34e80df 100644 --- a/emotes.go +++ b/emotes.go @@ -4,28 +4,19 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "log" "net/http" "os" - "path" + "path/filepath" "strings" "github.com/pkg/errors" + "github.com/zorchenhimer/MovieNight/common" ) const emoteDir = "./static/emotes/" -var Emotes map[string]Emote - -type Emote struct { - Dir string - File string -} - -func (e Emote) path() string { - return path.Join(e.Dir, e.File) -} - type TwitchUser struct { ID string Login string @@ -36,74 +27,85 @@ type EmoteInfo struct { Code string } -// func loadEmotes() error { -// newEmotes := map[string]string{} - -// emotePNGs, err := filepath.Glob("./static/emotes/*.png") -// if err != nil { -// return 0, fmt.Errorf("unable to glob emote directory: %s\n", err) -// } - -// emoteGIFs, err := filepath.Glob("./static/emotes/*.gif") -// if err != nil { -// return 0, fmt.Errorf("unable to glob emote directory: %s\n", err) -// } -// globbed_files := []string(emotePNGs) -// globbed_files = append(globbed_files, emoteGIFs...) - -// LogInfoln("Loading emotes...") -// emInfo := []string{} -// for _, file := range globbed_files { -// file = filepath.Base(file) -// key := file[0 : len(file)-4] -// newEmotes[key] = file -// emInfo = append(emInfo, key) -// } -// Emotes = newEmotes -// LogInfoln(strings.Join(emInfo, " ")) -// return len(Emotes), nil -// } - func loadEmotes() error { - fmt.Println(processEmoteDir(emoteDir)) + //fmt.Println(processEmoteDir(emoteDir)) + err := processEmoteDir(emoteDir) + if err != nil { + return err + } + return nil } -func processEmoteDir(path string) ([]Emote, error) { - dir, err := os.Open(path) +func processEmoteDir(path string) error { + dirInfo, err := ioutil.ReadDir(path) if err != nil { - return nil, errors.Wrap(err, "could not open emoteDir:") + return errors.Wrap(err, "could not open emoteDir:") } - files, err := dir.Readdir(0) - if err != nil { - return nil, errors.Wrap(err, "could not get files:") - } + subDirs := []string{} - var emotes []Emote - for _, file := range files { - emotes = append(emotes, Emote{Dir: path, File: file.Name()}) - } - - subdir, err := dir.Readdirnames(0) - if err != nil { - return nil, errors.Wrap(err, "could not get sub directories:") - } - - for _, d := range subdir { - subEmotes, err := processEmoteDir(d) - if err != nil { - return nil, errors.Wrapf(err, "could not process sub directory \"%s\":", d) + for _, item := range dirInfo { + // Get first level subdirs (eg, "twitch", "discord", etc) + if item.IsDir() { + subDirs = append(subDirs, item.Name()) + continue } - emotes = append(emotes, subEmotes...) } - return emotes, nil + // Find top level emotes + err = findEmotes(path) + if err != nil { + return errors.Wrap(err, "could not findEmotes() in top level directory:") + } + + // Get second level subdirs (eg, "twitch", "zorchenhimer", etc) + for _, dir := range subDirs { + subd, err := ioutil.ReadDir(filepath.Join(path, dir)) + if err != nil { + continue + } + for _, d := range subd { + if d.IsDir() { + //emotes = append(emotes, findEmotes(filepath.Join(path, dir, d.Name()))...) + findEmotes(filepath.Join(path, dir, d.Name())) + } + } + } + + return nil +} + +func findEmotes(dir string) error { + fmt.Printf("finding emotes in %q\n", dir) + emotePNGs, err := filepath.Glob(filepath.Join(dir, "*.png")) + if err != nil { + //return 0, fmt.Errorf("unable to glob emote directory: %s\n", err) + return nil + } + fmt.Printf("%d emotePNGs\n", len(emotePNGs)) + + emoteGIFs, err := filepath.Glob(filepath.Join(dir, "*.gif")) + if err != nil { + return errors.Wrap(err, "unable to glob emote directory:") + } + fmt.Printf("%d emoteGIFs\n", len(emoteGIFs)) + + for _, file := range emotePNGs { + common.Emotes.Add(file) + //emotes = append(emotes, common.Emote{FullPath: dir, Code: file}) + } + + for _, file := range emoteGIFs { + common.Emotes.Add(file) + } + + return nil } func getEmotes(names []string) error { users := getUserIDs(names) - users = append(users, TwitchUser{ID: "0", Login: "global"}) + users = append(users, TwitchUser{ID: "0", Login: "twitch"}) for _, user := range users { emotes, cheers, err := getChannelEmotes(user.ID) @@ -111,14 +113,14 @@ func getEmotes(names []string) error { return errors.Wrapf(err, "could not get emote data for \"%s\"", user.ID) } - emoteUserDir := path.Join(emoteDir, "twitch", user.Login) + emoteUserDir := filepath.Join(emoteDir, "twitch", user.Login) if _, err := os.Stat(emoteUserDir); os.IsNotExist(err) { os.MkdirAll(emoteUserDir, os.ModePerm) } for _, emote := range emotes { if !strings.ContainsAny(emote.Code, `:;\[]|?&`) { - filePath := path.Join(emoteUserDir, emote.Code+".png") + filePath := filepath.Join(emoteUserDir, emote.Code+".png") file, err := os.Create(filePath) if err != nil { @@ -134,7 +136,7 @@ func getEmotes(names []string) error { for amount, sizes := range cheers { name := fmt.Sprintf("%sCheer%s.gif", user.Login, amount) - filePath := path.Join(emoteUserDir, name) + filePath := filepath.Join(emoteUserDir, name) file, err := os.Create(filePath) if err != nil { return errors.Wrapf(err, "could not create emote file in path \"%s\":", filePath) diff --git a/handlers.go b/handlers.go index a7da689..fbbcc41 100644 --- a/handlers.go +++ b/handlers.go @@ -79,8 +79,7 @@ func wsImages(w http.ResponseWriter, r *http.Request) { } func wsEmotes(w http.ResponseWriter, r *http.Request) { - emotefile := filepath.Base(r.URL.Path) - http.ServeFile(w, r, path.Join(emoteDir, emotefile)) + http.ServeFile(w, r, path.Join("./static/", r.URL.Path)) } // Handling the websocket diff --git a/settings_example.json b/settings_example.json index 9700d7b..ea69bc9 100644 --- a/settings_example.json +++ b/settings_example.json @@ -1,6 +1,6 @@ { "MaxMessageCount": 300, - "TitleLength": 50, + "TitleLength": 50, "AdminPassword": "", "Bans": [], "StreamKey": "ALongStreamKey",