diff --git a/Makefile b/Makefile index c51bb8b..8ad7e2f 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ TAGS= -.PHONY: fmt vet get clean dev setdev +.PHONY: fmt vet get clean dev setdev test -all: fmt vet MovieNight MovieNight.exe static/main.wasm +all: fmt vet test MovieNight MovieNight.exe static/main.wasm setdev: $(eval export TAGS=-tags "dev") @@ -32,3 +32,6 @@ get: vet: go vet $(TAGS) ./... GOOS=js GOARCH=wasm go vet $(TAGS) ./... + +test: + go test $(TAGS) ./... \ No newline at end of file diff --git a/chatclient.go b/chatclient.go index edd9902..244daeb 100644 --- a/chatclient.go +++ b/chatclient.go @@ -10,6 +10,12 @@ import ( "github.com/zorchenhimer/MovieNight/common" ) +var ( + regexSpoiler = regexp.MustCompile(`\|\|(.*?)\|\|`) + spoilerStart = `` + spoilerEnd = `` +) + type Client struct { name string // Display name conn *chatConnection @@ -18,6 +24,7 @@ type Client struct { CmdLevel common.CommandLevel IsColorForced bool IsNameForced bool + regexName *regexp.Regexp } //Client has a new message to broadcast @@ -49,6 +56,10 @@ func (cl *Client) NewMsg(data common.ClientData) { msg = removeDumbSpaces(msg) msg = strings.Trim(msg, " ") + // Add the spoiler tag outside of the command vs message statement + // because the /me command outputs to the messages + msg = addSpoilerTags(msg) + // Don't send zero-length messages if len(msg) == 0 { return @@ -94,7 +105,11 @@ func (cl *Client) NewMsg(data common.ClientData) { func (cl *Client) SendChatData(data common.ChatData) error { // Colorize name on chat messages if data.Type == common.DTChat { - data = replaceColorizedName(data, cl) + var err error + data = cl.replaceColorizedName(data) + if err != nil { + return fmt.Errorf("could not colorize name: %v", err) + } } cd, err := data.ToJSON() @@ -164,6 +179,24 @@ func (cl *Client) Host() string { return cl.conn.Host() } +func (cl *Client) setName(s string) error { + regex, err := regexp.Compile(fmt.Sprintf("(%s|@%s)", s, s)) + if err != nil { + return fmt.Errorf("could not compile regex: %v", err) + } + + cl.name = s + cl.regexName = regex + return nil +} + +func (cl *Client) replaceColorizedName(chatData common.ChatData) common.ChatData { + data := chatData.Data.(common.DataMessage) + data.Message = cl.regexName.ReplaceAllString(data.Message, `$1`) + chatData.Data = data + return chatData +} + var dumbSpaces = []string{ "\n", "\t", @@ -187,12 +220,6 @@ func removeDumbSpaces(msg string) string { return newMsg } -func replaceColorizedName(chatData common.ChatData, client *Client) common.ChatData { - data := chatData.Data.(common.DataMessage) - - data.Message = regexp.MustCompile(fmt.Sprintf(`(%s|@%s)`, client.name, client.name)). - ReplaceAllString(data.Message, `$1`) - - chatData.Data = data - return chatData +func addSpoilerTags(msg string) string { + return regexSpoiler.ReplaceAllString(msg, fmt.Sprintf(`%s$1%s`, spoilerStart, spoilerEnd)) } diff --git a/chatclient_test.go b/chatclient_test.go new file mode 100644 index 0000000..d757cd1 --- /dev/null +++ b/chatclient_test.go @@ -0,0 +1,23 @@ +package main + +import "testing" + +func TestClient_addSpoilerTag(t *testing.T) { + data := [][]string{ + {"||||", spoilerStart + spoilerEnd}, + {"|||||", spoilerStart + spoilerEnd + "|"}, + {"||||||", spoilerStart + spoilerEnd + "||"}, + {"|||||||", spoilerStart + spoilerEnd + "|||"}, + {"||||||||", spoilerStart + spoilerEnd + spoilerStart + spoilerEnd}, + {"||test||", spoilerStart + "test" + spoilerEnd}, + {"|| ||", spoilerStart + " " + spoilerEnd}, + {"|s|||", "|s|||"}, + } + + for i := range data { + s := addSpoilerTags(data[i][0]) + if s != data[i][1] { + t.Errorf("expected %#v, got %#v with %#v", data[i][1], s, data[i][0]) + } + } +} diff --git a/chatroom.go b/chatroom.go index aa720af..aebde3e 100644 --- a/chatroom.go +++ b/chatroom.go @@ -98,12 +98,16 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) { conn.clientName = name client := &Client{ - name: name, conn: conn, belongsTo: cr, color: common.RandomColor(), } + err := client.setName(name) + if err != nil { + return nil, fmt.Errorf("could not set client name to %#v: %v", name, err) + } + host := client.Host() if banned, names := settings.IsBanned(host); banned { @@ -483,7 +487,10 @@ func (cr *ChatRoom) changeName(oldName, newName string, forced bool) error { } if currentClient != nil { - currentClient.name = newName + err := currentClient.setName(newName) + if err != nil { + return fmt.Errorf("could not set client name to %#v: %v", newName, err) + } common.LogDebugf("%q -> %q\n", oldName, newName) if forced { diff --git a/common/colors.go b/common/colors.go index 32890b0..57fb7b1 100644 --- a/common/colors.go +++ b/common/colors.go @@ -42,6 +42,10 @@ var colors = []string{ "whitesmoke", "yellow", "yellowgreen", } +var ( + regexColor = regexp.MustCompile(`^#([0-9A-Fa-f]{3}){1,2}$`) +) + // IsValidColor takes a string s and compares it against a list of css color names. // It also accepts hex codes in the form of #000 (RGB), to #00000000 (RRGGBBAA), with A // being the alpha value @@ -53,7 +57,7 @@ func IsValidColor(s string) bool { } } - if regexp.MustCompile(`^#([0-9A-Fa-f]{3}){1,2}$`).MatchString(s) { + if regexColor.MatchString(s) { c, err := colorful.Hex(s) if err != nil { return false diff --git a/common/logging.go b/common/logging.go index 8bbd5c1..01bf824 100644 --- a/common/logging.go +++ b/common/logging.go @@ -23,7 +23,6 @@ const ( logPrefixChat string = "[CHAT] " logPrefixInfo string = "[INFO] " logPrefixDebug string = "[DEBUG] " - logPrefixDev string = "[DEV] " ) var ( @@ -31,18 +30,8 @@ var ( logChat *log.Logger logInfo *log.Logger logDebug *log.Logger - logDev *log.Logger ) -//func ParseLogLevel(input string) LogLevel { -// switch LogLevel(input) { -// case LLError, LLChat, LLInfo, LLDebug: -// return LogLevel(input) -// default: -// return LLError -// } -//} - func SetupLogging(level LogLevel, file string) error { switch level { case LLDebug: diff --git a/common/logging_dev.go b/common/logging_dev.go index 32c9646..1539835 100644 --- a/common/logging_dev.go +++ b/common/logging_dev.go @@ -2,18 +2,21 @@ package common -func LogDevf(format string, v ...interface{}) { - if logError == nil { - panic("Logging not setup!") - } +import ( + "log" + "os" +) - logError.Printf(format, v...) +var logDev *log.Logger + +func init() { + logDev = log.New(os.Stdout, "[DEV]", log.LstdFlags) +} + +func LogDevf(format string, v ...interface{}) { + logDev.Printf(format, v...) } func LogDevln(v ...interface{}) { - if logError == nil { - panic("Logging not setup!") - } - - logError.Println(v...) + logDev.Println(v...) } diff --git a/settings.go b/settings.go index dc47eb5..dccd9fb 100644 --- a/settings.go +++ b/settings.go @@ -49,7 +49,7 @@ func init() { panic("Missing stream key is settings.json") } - if err = common.SetupLogging(settings.LogLevel, settings.LogFile); err != nil { + if err = settings.SetupLogging(); err != nil { panic("Unable to setup logger: " + err.Error()) } @@ -184,3 +184,7 @@ func (s *Settings) GetStreamKey() string { } return s.StreamKey } + +func (s *Settings) SetupLogging() error { + return common.SetupLogging(s.LogLevel, s.LogFile) +} diff --git a/static/css/site.css b/static/css/site.css index c708c98..7b53c86 100644 --- a/static/css/site.css +++ b/static/css/site.css @@ -116,6 +116,22 @@ span.svmsg { color: var(--var-contrast-color); } +.spoiler { + border-radius: 3px; + padding: 0px 3px; +} + +.spoiler *, +.spoiler { + background: var(--var-popout-color); + color: var(--var-popout-color); +} + +.spoiler-active { + background: var(--var-background-color); + color: aqua; +} + .range-div { margin-bottom: 5px; display: flex;