diff --git a/chatcommands.go b/chatcommands.go index f043355..b4b5927 100644 --- a/chatcommands.go +++ b/chatcommands.go @@ -26,7 +26,9 @@ var commands = &CommandControl{ common.CNMe.String(): Command{ HelpText: "Display an action message.", Function: func(client *Client, args []string) string { - client.Me(strings.Join(args, " ")) + if len(args) != 0 { + client.Me(strings.Join(args, " ")) + } return "" }, }, @@ -139,7 +141,7 @@ var commands = &CommandControl{ // Clear/hide title if sent with no arguments. if len(args) == 0 { cl.belongsTo.ClearPlaying() - return "" + return "Title cleared" } link := "" title := "" diff --git a/chatroom.go b/chatroom.go index 29ea36c..8d128a4 100644 --- a/chatroom.go +++ b/chatroom.go @@ -48,7 +48,7 @@ func newChatRoom() (*ChatRoom, error) { fmt.Printf("Loaded %d emotes\n", num) //the "heartbeat" for broadcasting messages - go cr.BroadCast() + go cr.Broadcast() return cr, nil } @@ -96,6 +96,7 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) { } } + conn.clientName = name client := &Client{ name: name, conn: conn, @@ -123,8 +124,8 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) { return client, nil } -// TODO: fix this up a bit. kick and leave are the same, incorrect, error: "That name was already used!" -//leaving the chatroom +// TODO: fix this up a bit. kick and leave are the same, incorrect, error: "That +// name was already used!" leaving the chatroom func (cr *ChatRoom) Leave(name, color string) { defer cr.clientsMtx.Unlock() cr.clientsMtx.Lock() //preventing simultaneous access to the `clients` map @@ -307,7 +308,7 @@ func (cr *ChatRoom) UserCount() int { } //broadcasting all the messages in the queue in one block -func (cr *ChatRoom) BroadCast() { +func (cr *ChatRoom) Broadcast() { send := func(data common.ChatData, client *Client) { err := client.SendChatData(data) if err != nil { @@ -320,20 +321,24 @@ func (cr *ChatRoom) BroadCast() { case msg := <-cr.queue: cr.clientsMtx.Lock() for _, client := range cr.clients { - send(msg, client) + go send(msg, client) } - for _, conn := range cr.tempConn { - data, err := msg.ToJSON() - if err != nil { - fmt.Printf("Error converting ChatData to ChatDataJSON: %v\n", err) - // Break out early because if one conversion fails, they all will fail - break - } - err = conn.WriteData(data) - if err != nil { - fmt.Printf("Error writing data to connection: %v\n", err) + + data, err := msg.ToJSON() + if err != nil { + fmt.Printf("Error converting ChatData to ChatDataJSON: %v\n", err) + } else { + for uuid, conn := range cr.tempConn { + go func(c *chatConnection, suid string) { + err = c.WriteData(data) + if err != nil { + fmt.Printf("Error writing data to connection: %v\n", err) + delete(cr.tempConn, suid) + } + }(conn, uuid) } } + cr.clientsMtx.Unlock() case msg := <-cr.modqueue: cr.clientsMtx.Lock() diff --git a/common/colors.go b/common/colors.go index a658234..32890b0 100644 --- a/common/colors.go +++ b/common/colors.go @@ -3,50 +3,72 @@ package common import ( "regexp" "strings" + + "github.com/lucasb-eyer/go-colorful" ) +// the values in colors must be lowercase so it matches with the color input +// this saves from having to call strings.ToLower(color) every time to check var colors = []string{ - "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", - "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", - "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", - "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", - "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", - "DarkGrey", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", - "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", - "DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise", "DarkViolet", - "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", - "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", - "GhostWhite", "Gold", "GoldenRod", "Gray", "Grey", - "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", - "Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", - "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", - "LightGoldenRodYellow", "LightGray", "LightGrey", "LightGreen", "LightPink", - "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSlateGrey", - "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", - "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", - "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", - "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", - "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", - "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", - "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", - "Pink", "Plum", "PowderBlue", "Purple", "RebeccaPurple", - "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", - "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", - "SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", - "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", - "Tomato", "Turquoise", "Violet", "Wheat", "White", - "WhiteSmoke", "Yellow", "YellowGreen", + "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", + "beige", "bisque", "black", "blanchedalmond", "blue", + "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", + "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", + "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", + "darkgrey", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", + "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", + "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", + "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", + "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", + "ghostwhite", "gold", "goldenrod", "gray", "grey", + "green", "greenyellow", "honeydew", "hotpink", "indianred", + "indigo", "ivory", "khaki", "lavender", "lavenderblush", + "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan", + "lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen", "lightpink", + "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", + "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", + "magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", + "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", + "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", + "navajowhite", "navy", "oldlace", "olive", "olivedrab", + "orange", "orangered", "orchid", "palegoldenrod", "palegreen", + "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", + "pink", "plum", "powderblue", "purple", "rebeccapurple", + "red", "rosybrown", "royalblue", "saddlebrown", "salmon", + "sandybrown", "seagreen", "seashell", "sienna", "silver", + "skyblue", "slateblue", "slategray", "slategrey", "snow", + "springgreen", "steelblue", "tan", "teal", "thistle", + "tomato", "turquoise", "violet", "wheat", "white", + "whitesmoke", "yellow", "yellowgreen", } // 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 func IsValidColor(s string) bool { + s = strings.ToLower(s) for _, c := range colors { - if strings.ToLower(c) == strings.ToLower(s) { + if s == c { return true } } - return regexp.MustCompile(`^#[0-9A-Fa-f]{6}$`).MatchString(s) + if regexp.MustCompile(`^#([0-9A-Fa-f]{3}){1,2}$`).MatchString(s) { + c, err := colorful.Hex(s) + if err != nil { + return false + } + total := c.R + c.G + c.B + return total > 0.7 && c.B/total < 0.7 + } + return false +} + +// RandomColor returns a hex color code +func RandomColor() string { + var color colorful.Color + for !IsValidColor(color.Hex()) { + color = colorful.FastHappyColor() + } + return color.Hex() } diff --git a/common/utils.go b/common/utils.go index 5df441f..808af11 100644 --- a/common/utils.go +++ b/common/utils.go @@ -3,8 +3,6 @@ package common // Misc utils import ( - "fmt" - "math/rand" "regexp" ) @@ -16,14 +14,3 @@ func IsValidName(name string) bool { return 3 <= len(name) && len(name) <= 36 && usernameRegex.MatchString(name) && !IsValidColor(name) } - -// RandomColor returns a hex color code -func RandomColor() string { - nums := []int32{} - for i := 0; i < 6; i++ { - nums = append(nums, rand.Int31n(15)) - } - return fmt.Sprintf("#%X%X%X%X%X%X", - nums[0], nums[1], nums[2], - nums[3], nums[4], nums[5]) -} diff --git a/connection.go b/connection.go index 7e1e19e..715b412 100644 --- a/connection.go +++ b/connection.go @@ -10,24 +10,30 @@ import ( type chatConnection struct { *websocket.Conn - mutex sync.Mutex + mutex sync.RWMutex forwardedFor string + clientName string } func (cc *chatConnection) ReadData(data interface{}) error { - defer cc.mutex.Unlock() - cc.mutex.Lock() + cc.mutex.RLock() + defer cc.mutex.RUnlock() + stats.msgInInc() return cc.ReadJSON(data) } func (cc *chatConnection) WriteData(data interface{}) error { - defer cc.mutex.Unlock() cc.mutex.Lock() + defer cc.mutex.Unlock() + stats.msgOutInc() err := cc.WriteJSON(data) if err != nil { - return fmt.Errorf("Error writing data to %s: %v", cc.Host(), err) + if operr, ok := err.(*net.OpError); ok { + fmt.Println("OpError: " + operr.Err.Error()) + } + return fmt.Errorf("Error writing data to %s %s: %v", cc.clientName, cc.Host(), err) } return nil } diff --git a/main.go b/main.go index 121d75b..39b105c 100644 --- a/main.go +++ b/main.go @@ -56,12 +56,12 @@ func main() { fmt.Println("Listen and serve ", addr) go startServer() - go startRmptServer() + go startRmtpServer() <-exit } -func startRmptServer() { +func startRmtpServer() { server := &rtmp.Server{ HandlePlay: handlePlay, HandlePublish: handlePublish, diff --git a/static/css/site.css b/static/css/site.css index 7d17dac..b71d119 100644 --- a/static/css/site.css +++ b/static/css/site.css @@ -4,6 +4,7 @@ --var-message-color: #f4f4f4; --var-contrast-color: #1bf7ec; --var-background-color: #0F0F11; + --var-popout-color: #393940; --var-max-height: 98vh; --var-max-width: 98vw; } @@ -111,23 +112,30 @@ span.svmsg { color: #B1B1B1; } -.access-grant { - font-weight: normal; - color: greenyellow; +.contrast { + color: var(--var-contrast-color); } -.access-ignore { - font-weight: normal; - color: red; +.range-div { + margin-bottom: 5px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 5px; + padding: 5px; } -.access-name { - color: #f7b11b; +.range-div>input[type=button] { + flex: 2; } -.access-title { - color: #e5e0e5; - font-weight: bold; +.hiddendiv { + display: none; + color: var(--var-message-color); + background: var(--var-popout-color); + padding: 2em; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; } #videoElement { @@ -154,7 +162,7 @@ span.svmsg { right: 0px; margin: 0px 5px; padding: 3px; - background: #393940; + background: var(--var-popout-color); } #hidden:hover, @@ -262,4 +270,8 @@ span.svmsg { #helpbody { color: #b1b1b1; +} + +#colorSubmit:disabled { + display: none; } \ No newline at end of file diff --git a/static/js/chat.js b/static/js/chat.js index 392a997..8b3503b 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -4,6 +4,9 @@ function setPlaying(title, link) { if (title !== "") { $('#playing').text(title); document.title = "Movie Night | " + title; + } else { + $('#playing').text(""); + document.title = "Movie Night"; } $('#playing').removeAttr('href'); @@ -89,7 +92,11 @@ function join() { } function websocketSend(data) { - ws.send(data); + if (ws.readyState == ws.OPEN) { + ws.send(data); + } else { + console.log("did not send data because websocket is not open", data); + } } function sendChat() { @@ -126,13 +133,57 @@ function help() { sendMessage("/help"); } +function showColors(show) { + $("#hiddencolor").css("display", show ? "block" : ""); +} + +function colorAsHex() { + let r = parseInt($("#colorRed").val()).toString(16).padStart(2, "0"); + let g = parseInt($("#colorGreen").val()).toString(16).padStart(2, "0"); + let b = parseInt($("#colorBlue").val()).toString(16).padStart(2, "0"); + return `#${r}${g}${b}` +} + +function updateColor() { + let r = $("#colorRed").val(); + let g = $("#colorGreen").val(); + let b = $("#colorBlue").val(); + + $("#colorRedLabel").text(r.padStart(3, "0")); + $("#colorGreenLabel").text(g.padStart(3, "0")); + $("#colorBlueLabel").text(b.padStart(3, "0")); + + $("#colorName").css("color", `rgb(${r}, ${g}, ${b})`); + + if (isValidColor(colorAsHex())) { + $("#colorWarning").text(""); + } else { + $("#colorWarning").text("Unreadable Color"); + } +} + +function changeColor() { + if (isValidColor(colorAsHex())) { + sendMessage("/color " + colorAsHex()); + showColors(false); + } +} + + // Get the websocket setup in a function so it can be recalled function setupWebSocket() { ws = new WebSocket(getWsUri()); ws.onmessage = (m) => recieveMessage(m.data); - ws.onopen = (e) => console.log("Websocket Open:", e); - ws.onclose = () => closeChat(); - ws.onerror = (e) => console.log("Websocket Error:", e); + ws.onopen = () => console.log("Websocket Open"); + ws.onclose = () => { + closeChat(); + setNotifyBox("The connection to the server has closed. Please refresh page to connect again."); + $("#joinbox").css("display", "none"); + } + ws.onerror = (e) => { + console.log("Websocket Error:", e); + e.target.close(); + } } function setupEvents() { @@ -159,10 +210,6 @@ function setupEvents() { input: () => processMessage(), }); - $("#hiddenColorPicker").on({ - change: () => sendMessage("/color " + $("#hiddenColorPicker").val()), - }); - $("#send").on({ click: () => $("#msg").focus(), }); @@ -172,6 +219,12 @@ function setupEvents() { ).observe($("#suggestions")[0], { childList: true }); } +function defaultValues() { + $("#colorRed").val(0).trigger("input"); + $("#colorGreen").val(0).trigger("input"); + $("#colorBlue").val(0).trigger("input"); +} + window.addEventListener("onresize", updateSuggestionCss); window.addEventListener("load", () => { @@ -179,7 +232,42 @@ window.addEventListener("load", () => { setupWebSocket(); startGo(); setupEvents(); + defaultValues(); // Make sure name is focused on start $("#name").focus(); }); + +function pleaseremovethis() { + colors = ["aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", + "beige", "bisque", "black", "blanchedalmond", "blue", + "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", + "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", + "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", + "darkgrey", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", + "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", + "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", + "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue", + "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", + "ghostwhite", "gold", "goldenrod", "gray", "grey", + "green", "greenyellow", "honeydew", "hotpink", "indianred", + "indigo", "ivory", "khaki", "lavender", "lavenderblush", + "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan", + "lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen", "lightpink", + "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", + "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", + "magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", + "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", + "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", + "navajowhite", "navy", "oldlace", "olive", "olivedrab", + "orange", "orangered", "orchid", "palegoldenrod", "palegreen", + "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", + "pink", "plum", "powderblue", "purple", "rebeccapurple", + "red", "rosybrown", "royalblue", "saddlebrown", "salmon", + "sandybrown", "seagreen", "seashell", "sienna", "silver", + "skyblue", "slateblue", "slategray", "slategrey", "snow", + "springgreen", "steelblue", "tan", "teal", "thistle", + "tomato", "turquoise", "violet", "wheat", "white", + "whitesmoke", "yellow", "yellowgreen",] + +} \ No newline at end of file diff --git a/static/main.html b/static/main.html index 85d7ef6..3a09f92 100644 --- a/static/main.html +++ b/static/main.html @@ -1,80 +1,84 @@ {{define "header"}} -{{ if .Chat }} +{{if .Chat}} -{{ end }} +{{end}} -{{ if .Video }} +{{if .Video}} -{{ if not .Chat }} +{{if not .Chat}} -{{ end }} -{{ end }} +{{end}} +{{end}} -{{ if and .Video .Chat }} +{{if and .Video .Chat}} -{{ end }} +{{end}} {{end}} {{define "body"}} -{{ if .Video }} +{{if .Video}} -{{ end }} +{{end}} -{{ if .Chat }} +{{if .Chat}}