From fab56e39eae78c58ec30bdabb74dd1ca2e6dc3e1 Mon Sep 17 00:00:00 2001 From: Zorchenhimer Date: Sun, 19 Apr 2020 12:26:27 -0400 Subject: [PATCH] Rework emote parsing to properly handle wrapped emotes Reworked the emote parsing to properly handle "wrapped" emotes. A wrapped emote is one that is wrapped in colons or square braces (eg, :Kappa: or [Kappa]). This allows emotes to be input without a space between them, like is required with non-wrapped emotes. A new configuration setting has been added to only allow parsing of wrapped emotes: "WrappedEmotesOnly". This defaults to False as to not break current configurations. The emote autocompletion will only insert non-wrapped emotes. Setting "WrappedEmotesOnly" configuration value to True will not change this. Lastly, made the bare-word emote parsing a bit cleaner by removing a for loop in favor of a map lookup with a check. This should in theory be more efficient. This change is in response to #111. The issue should not be considered resolved until the autocompletion handles the new setting correctly. --- common/emotes.go | 29 ++++++++++++++++++++-------- common/emotes_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++ settings.go | 7 +++++++ settings_example.json | 3 ++- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/common/emotes.go b/common/emotes.go index 5f1d3ec..fcd3746 100644 --- a/common/emotes.go +++ b/common/emotes.go @@ -10,8 +10,12 @@ import ( type EmotesMap map[string]string var Emotes EmotesMap +var WrappedEmotesOnly bool = false -var reStripStatic = regexp.MustCompile(`^(\\|/)?static`) +var ( + reStripStatic = regexp.MustCompile(`^(\\|/)?static`) + reWrappedEmotes = regexp.MustCompile(`[:\[][^\s:\/\\\?=#\]\[]+[:\]]`) +) func init() { Emotes = NewEmotesMap() @@ -40,7 +44,6 @@ func (em EmotesMap) Add(fullpath string) EmotesMap { } em[code] = fullpath - //fmt.Printf("Added emote %s at path %q\n", code, fullpath) return em } @@ -48,23 +51,33 @@ func EmoteToHtml(file, title string) string { return fmt.Sprintf(``, file, title) } +// Used with a regexp.ReplaceAllStringFunc() call. Needs to lookup the value as it +// cannot be passed in with the regex function call. +func emoteToHmtl2(key string) string { + key = strings.Trim(key, ":[]") + if val, ok := Emotes[key]; ok { + return fmt.Sprintf(``, val, key) + } + return key +} + func ParseEmotesArray(words []string) []string { newWords := []string{} for _, word := range words { - // make :emote: and [emote] valid for replacement. - wordTrimmed := strings.Trim(word, ":[]") - found := false - for key, val := range Emotes { - if key == wordTrimmed { - newWords = append(newWords, EmoteToHtml(val, key)) + if !WrappedEmotesOnly { + if val, ok := Emotes[word]; ok { + newWords = append(newWords, EmoteToHtml(val, word)) found = true } } + if !found { + word = reWrappedEmotes.ReplaceAllStringFunc(word, emoteToHmtl2) newWords = append(newWords, word) } } + return newWords } diff --git a/common/emotes_test.go b/common/emotes_test.go index bb31646..0e61f87 100644 --- a/common/emotes_test.go +++ b/common/emotes_test.go @@ -14,10 +14,18 @@ var data_good = map[string]string{ ":two:": ``, ":three:": ``, + ":one::one:": ``, + ":one:one:": `one:`, + "oneone": "oneone", + "one:one:": `one`, + "[one]": ``, "[two]": ``, "[three]": ``, + "[one][one]": ``, + "[one]one": `one`, + ":one: two [three]": ` `, "nope one what": `nope what`, @@ -25,6 +33,34 @@ var data_good = map[string]string{ "nope [three] what": `nope what`, } +var data_wrapped = map[string]string{ + "one": `one`, + "two": `two`, + "three": `three`, + + ":one:": ``, + ":two:": ``, + ":three:": ``, + + ":one::one:": ``, + ":one:one:": `one:`, + "oneone": "oneone", + "one:one:": `one`, + + "[one]": ``, + "[two]": ``, + "[three]": ``, + + "[one][one]": ``, + "[one]one": `one`, + + ":one: two [three]": ` two `, + + "nope one what": `nope one what`, + "nope :two: what": `nope what`, + "nope [three] what": `nope what`, +} + func TestMain(m *testing.M) { Emotes = map[string]string{ "one": "/emotes/one.png", @@ -42,3 +78,12 @@ func TestEmotes_ParseEmotes(t *testing.T) { } } } + +func TestEmotes_ParseEmotes_WrappedOnly(t *testing.T) { + for input, expected := range data_good { + got := ParseEmotes(input) + if got != expected { + t.Errorf("%s failed to parse into %q. Received: %q", input, expected, got) + } + } +} diff --git a/settings.go b/settings.go index 18f0512..fd19ac5 100644 --- a/settings.go +++ b/settings.go @@ -40,6 +40,8 @@ type Settings struct { RoomAccessPin string // The current pin NewPin bool // Auto generate a new pin on start. Overwrites RoomAccessPin if set. + WrappedEmotesOnly bool // only allow "wrapped" emotes. eg :Kappa: and [Kappa] but not Kappa + // Rate limiting stuff, in seconds RateLimitChat time.Duration RateLimitNick time.Duration @@ -128,6 +130,11 @@ func LoadSettings(filename string) (*Settings, error) { s.RateLimitDuplicate = 30 } + if s.WrappedEmotesOnly { + common.LogInfoln("Only allowing wrapped emotes") + common.WrappedEmotesOnly = true + } + // Print this stuff before we multiply it by time.Second common.LogInfof("RateLimitChat: %v", s.RateLimitChat) common.LogInfof("RateLimitNick: %v", s.RateLimitNick) diff --git a/settings_example.json b/settings_example.json index cd9583c..1352b6f 100644 --- a/settings_example.json +++ b/settings_example.json @@ -14,5 +14,6 @@ "RateLimitColor": 60, "RateLimitAuth": 5, "RateLimitDuplicate": 30, - "NoCache": false + "NoCache": false, + "WrappedEmotesOnly": false }