Converting from conn.WriteMessage to conn.WriteJson

This takes away some of the work on the developers side to worry about parsing the object as a json string.
Backend work for issue #7
This commit is contained in:
joeyak 2019-03-15 17:28:29 -04:00
parent 28d43a726a
commit a717c6ef38
7 changed files with 110 additions and 131 deletions

View File

@ -11,10 +11,6 @@ import (
"github.com/zorchenhimer/MovieNight/common" "github.com/zorchenhimer/MovieNight/common"
) )
func connSend(s string, c *websocket.Conn) {
c.WriteMessage(websocket.TextMessage, []byte(s))
}
type Client struct { type Client struct {
name string // Display name name string // Display name
conn *websocket.Conn conn *websocket.Conn
@ -56,12 +52,19 @@ func (cl *Client) NewMsg(data common.ClientData) {
switch data.Type { switch data.Type {
case common.CdUsers: case common.CdUsers:
fmt.Printf("[chat|hidden] <%s> get list of users\n", cl.name) fmt.Printf("[chat|hidden] <%s> get list of users\n", cl.name)
s, err := common.EncodeHiddenMessage(data.Type, chat.GetNames())
if err != nil { names := chat.GetNames()
fmt.Printf("[ERR] could not encode user list, %v", err) idx := -1
return for i := range names {
if names[i] == cl.name {
idx = i
}
}
err := cl.SendChatData(common.NewChatHiddenMessage(data.Type, append(names[:idx], names[idx+1:]...)))
if err != nil {
fmt.Printf("Error sending chat data: %v\n", err)
} }
cl.Send(s)
case common.CdMessage: case common.CdMessage:
msg := html.EscapeString(data.Message) msg := html.EscapeString(data.Message)
msg = removeDumbSpaces(msg) msg = removeDumbSpaces(msg)
@ -81,7 +84,10 @@ func (cl *Client) NewMsg(data common.ClientData) {
response := commands.RunCommand(cmd, args, cl) response := commands.RunCommand(cmd, args, cl)
if response != "" { if response != "" {
cl.ServerMessage(response) err := cl.SendServerMessage(ParseEmotes(msg))
if err != nil {
fmt.Printf("Error command results %v\n", err)
}
return return
} }
@ -103,6 +109,30 @@ func (cl *Client) NewMsg(data common.ClientData) {
} }
} }
func (cl *Client) SendChatData(newData common.NewChatDataFunc) error {
cd, err := newData()
if err != nil {
return fmt.Errorf("could not create chatdata of type %d: %v", cd.Type, err)
}
return cl.Send(cd)
}
func (cl *Client) Send(data common.ChatData) error {
err := cl.conn.WriteJSON(data)
if err != nil {
return fmt.Errorf("could not send message: %v", err)
}
return nil
}
func (cl *Client) SendServerMessage(s string) error {
err := cl.SendChatData(common.NewChatMessage("", ColorServerMessage, s, common.MsgServer))
if err != nil {
return fmt.Errorf("could send server message to %s: %s; Message: %s\n", cl.name, err, s)
}
return nil
}
// Make links clickable // Make links clickable
func formatLinks(input string) string { func formatLinks(input string) string {
newMsg := []string{} newMsg := []string{}
@ -121,22 +151,6 @@ func (cl *Client) Exit() {
cl.belongsTo.Leave(cl.name, cl.color) cl.belongsTo.Leave(cl.name, cl.color)
} }
//Sending message block to the client
func (cl *Client) Send(s string) {
connSend(s, cl.conn)
}
// Send server message to this client
func (cl *Client) ServerMessage(msg string) {
msg = ParseEmotes(msg)
encoded, err := common.EncodeMessage("", "#ea6260", msg, common.MsgError)
if err != nil {
fmt.Printf("[ERR] could not server message to %s: %s; Message: %s\n", cl.name, err, msg)
return
}
cl.Send(encoded)
}
// Outgoing messages // Outgoing messages
func (cl *Client) Message(msg string) { func (cl *Client) Message(msg string) {
msg = ParseEmotes(msg) msg = ParseEmotes(msg)

View File

@ -229,7 +229,7 @@ var commands = &CommandControl{
common.CNReloadEmotes.String(): Command{ common.CNReloadEmotes.String(): Command{
HelpText: "Reload the emotes on the server.", HelpText: "Reload the emotes on the server.",
Function: func(cl *Client, args []string) string { Function: func(cl *Client, args []string) string {
cl.ServerMessage("Reloading emotes") cl.SendServerMessage("Reloading emotes")
num, err := LoadEmotes() num, err := LoadEmotes()
if err != nil { if err != nil {
fmt.Printf("Unbale to reload emotes: %s\n", err) fmt.Printf("Unbale to reload emotes: %s\n", err)

View File

@ -18,8 +18,9 @@ import (
) )
const ( const (
UsernameMaxLength int = 36 UsernameMaxLength int = 36
UsernameMinLength int = 3 UsernameMinLength int = 3
ColorServerMessage string = "#ea6260"
) )
var re_username *regexp.Regexp = regexp.MustCompile(`^[0-9a-zA-Z_-]+$`) var re_username *regexp.Regexp = regexp.MustCompile(`^[0-9a-zA-Z_-]+$`)
@ -29,8 +30,8 @@ type ChatRoom struct {
clientsMtx sync.Mutex clientsMtx sync.Mutex
tempConn map[string]*websocket.Conn tempConn map[string]*websocket.Conn
queue chan string queue chan common.ChatData
modqueue chan string // mod and admin broadcast messages modqueue chan common.ChatData // mod and admin broadcast messages
playing string playing string
playingLink string playingLink string
@ -42,8 +43,8 @@ type ChatRoom struct {
//initializing the chatroom //initializing the chatroom
func newChatRoom() (*ChatRoom, error) { func newChatRoom() (*ChatRoom, error) {
cr := &ChatRoom{ cr := &ChatRoom{
queue: make(chan string, 100), queue: make(chan common.ChatData, 100),
modqueue: make(chan string, 100), modqueue: make(chan common.ChatData, 100),
clients: make(map[string]*Client), clients: make(map[string]*Client),
tempConn: make(map[string]*websocket.Conn), tempConn: make(map[string]*websocket.Conn),
} }
@ -159,7 +160,7 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) {
delete(cr.tempConn, uid) delete(cr.tempConn, uid)
fmt.Printf("[join] %s %s\n", host, name) fmt.Printf("[join] %s %s\n", host, name)
playingCommand, err := common.EncodeCommand(common.CmdPlaying, []string{cr.playing, cr.playingLink}) playingCommand, err := common.NewChatCommand(common.CmdPlaying, []string{cr.playing, cr.playingLink})()
if err != nil { if err != nil {
fmt.Printf("Unable to encode playing command on join: %s\n", err) fmt.Printf("Unable to encode playing command on join: %s\n", err)
} else { } else {
@ -265,15 +266,9 @@ func (cr *ChatRoom) AddMsg(from *Client, isAction, isServer bool, msg string) {
t = common.MsgServer t = common.MsgServer
} }
data, err := common.EncodeMessage( data, err := common.NewChatMessage(from.name, from.color, msg, t)()
from.name,
from.color,
msg,
t)
if err != nil { if err != nil {
fmt.Printf("Error encoding chat message: %s", err) fmt.Printf("Error encoding chat message: %s", err)
cr.queue <- msg
return return
} }
@ -285,8 +280,7 @@ func (cr *ChatRoom) AddMsg(from *Client, isAction, isServer bool, msg string) {
} }
func (cr *ChatRoom) AddCmdMsg(command common.CommandType, args []string) { func (cr *ChatRoom) AddCmdMsg(command common.CommandType, args []string) {
data, err := common.EncodeCommand(command, args) data, err := common.NewChatCommand(command, args)()
if err != nil { if err != nil {
fmt.Printf("Error encoding command: %s", err) fmt.Printf("Error encoding command: %s", err)
return return
@ -300,7 +294,7 @@ func (cr *ChatRoom) AddCmdMsg(command common.CommandType, args []string) {
} }
func (cr *ChatRoom) AddModNotice(message string) { func (cr *ChatRoom) AddModNotice(message string) {
data, err := common.EncodeMessage("", "", message, common.MsgNotice) data, err := common.NewChatMessage("", "", message, common.MsgNotice)()
if err != nil { if err != nil {
fmt.Printf("Error encoding notice: %v", err) fmt.Printf("Error encoding notice: %v", err)
return return
@ -314,7 +308,7 @@ func (cr *ChatRoom) AddModNotice(message string) {
} }
func (cr *ChatRoom) AddEventMsg(event common.EventType, name, color string) { func (cr *ChatRoom) AddEventMsg(event common.EventType, name, color string) {
data, err := common.EncodeEvent(event, name, color) data, err := common.NewChatEvent(event, name, color)()
if err != nil { if err != nil {
fmt.Printf("Error encoding command: %s", err) fmt.Printf("Error encoding command: %s", err)
@ -338,7 +332,7 @@ func (cr *ChatRoom) Unmod(name string) error {
} }
client.Unmod() client.Unmod()
client.ServerMessage(`You have been unmodded.`) client.SendServerMessage(`You have been unmodded.`)
return nil return nil
} }
@ -352,7 +346,7 @@ func (cr *ChatRoom) Mod(name string) error {
} }
client.IsMod = true client.IsMod = true
client.ServerMessage(`You have been modded.`) client.SendServerMessage(`You have been modded.`)
return nil return nil
} }
@ -378,17 +372,14 @@ func (cr *ChatRoom) UserCount() int {
func (cr *ChatRoom) BroadCast() { func (cr *ChatRoom) BroadCast() {
running := true running := true
for running { for running {
cr.clientsMtx.Lock()
select { select {
case msg := <-cr.queue: case msg := <-cr.queue:
if len(msg) > 0 { for _, client := range cr.clients {
cr.clientsMtx.Lock() client.Send(msg)
for _, client := range cr.clients { }
client.Send(msg) for _, conn := range cr.tempConn {
} conn.WriteJSON(msg)
for _, conn := range cr.tempConn {
connSend(msg, conn)
}
cr.clientsMtx.Unlock()
} }
default: default:
// No messages to send // No messages to send
@ -399,18 +390,15 @@ func (cr *ChatRoom) BroadCast() {
// Mod queue // Mod queue
select { select {
case msg := <-cr.modqueue: case msg := <-cr.modqueue:
if len(msg) > 0 { for _, client := range cr.clients {
cr.clientsMtx.Lock() if client.IsMod || client.IsAdmin {
for _, client := range cr.clients { client.Send(msg)
if client.IsMod || client.IsAdmin {
client.Send(msg)
}
} }
cr.clientsMtx.Unlock()
} }
default: default:
running = false running = false
} }
cr.clientsMtx.Unlock()
} }
} }

View File

@ -6,6 +6,8 @@ import (
"fmt" "fmt"
) )
type NewChatDataFunc func() (ChatData, error)
type DataInterface interface { type DataInterface interface {
HTML() string HTML() string
} }
@ -26,10 +28,6 @@ func (c ChatData) GetData() (DataInterface, error) {
d := DataMessage{} d := DataMessage{}
err = json.Unmarshal(c.Data, &d) err = json.Unmarshal(c.Data, &d)
data = d data = d
case DTError:
d := DataError{}
err = json.Unmarshal(c.Data, &d)
data = d
case DTCommand: case DTCommand:
d := DataCommand{} d := DataCommand{}
err = json.Unmarshal(c.Data, &d) err = json.Unmarshal(c.Data, &d)
@ -71,22 +69,6 @@ func (c ClientData) HTML() string {
return `<div style="color: red;"><span>The developer messed up. You should not be seeing this.</span></div>` return `<div style="color: red;"><span>The developer messed up. You should not be seeing this.</span></div>`
} }
type DataError struct {
Message string
}
func (de DataError) HTML() string {
return `<div class="svmsg"><b>Error</b>: ` + de.Message + `</div>`
}
func EncodeError(message string) (string, error) {
d, err := newChatData(DTError, DataError{Message: message})
if err != nil {
return "", err
}
return jsonifyChatData(d)
}
type DataMessage struct { type DataMessage struct {
From string From string
Color string Color string
@ -116,17 +98,15 @@ func (dc DataMessage) HTML() string {
} }
} }
func EncodeMessage(name, color, msg string, msgtype MessageType) (string, error) { func NewChatMessage(name, color, msg string, msgtype MessageType) NewChatDataFunc {
d, err := newChatData(DTChat, DataMessage{ return func() (ChatData, error) {
From: name, return newChatData(DTChat, DataMessage{
Color: color, From: name,
Message: msg, Color: color,
Type: msgtype, Message: msg,
}) Type: msgtype,
if err != nil { })
return "", err
} }
return jsonifyChatData(d)
} }
type DataCommand struct { type DataCommand struct {
@ -138,15 +118,13 @@ func (de DataCommand) HTML() string {
return "" return ""
} }
func EncodeCommand(command CommandType, args []string) (string, error) { func NewChatCommand(command CommandType, args []string) NewChatDataFunc {
d, err := newChatData(DTCommand, DataCommand{ return func() (ChatData, error) {
Command: command, return newChatData(DTCommand, DataCommand{
Arguments: args, Command: command,
}) Arguments: args,
if err != nil { })
return "", err
} }
return jsonifyChatData(d)
} }
type DataEvent struct { type DataEvent struct {
@ -173,16 +151,14 @@ func (de DataEvent) HTML() string {
return "" return ""
} }
func EncodeEvent(event EventType, name, color string) (string, error) { func NewChatEvent(event EventType, name, color string) NewChatDataFunc {
d, err := newChatData(DTEvent, DataEvent{ return func() (ChatData, error) {
Event: event, return newChatData(DTEvent, DataEvent{
User: name, Event: event,
Color: color, User: name,
}) Color: color,
if err != nil { })
return "", err
} }
return jsonifyChatData(d)
} }
// DataHidden is for the server to send instructions and data // DataHidden is for the server to send instructions and data
@ -196,15 +172,13 @@ func (h HiddenMessage) HTML() string {
return "" return ""
} }
func EncodeHiddenMessage(clientType ClientDataType, data interface{}) (string, error) { func NewChatHiddenMessage(clientType ClientDataType, data interface{}) NewChatDataFunc {
d, err := newChatData(DTHidden, HiddenMessage{ return func() (ChatData, error) {
Type: clientType, return newChatData(DTHidden, HiddenMessage{
Data: data, Type: clientType,
}) Data: data,
if err != nil { })
return "", err
} }
return jsonifyChatData(d)
} }
func jsonifyChatData(data ChatData) (string, error) { func jsonifyChatData(data ChatData) (string, error) {

View File

@ -13,7 +13,6 @@ type DataType int
const ( const (
DTInvalid DataType = iota DTInvalid DataType = iota
DTChat // chat message DTChat // chat message
DTError // something went wrong with the previous request
DTCommand // non-chat function DTCommand // non-chat function
DTEvent // join/leave/kick/ban events DTEvent // join/leave/kick/ban events
DTClient // a message coming from the client DTClient // a message coming from the client
@ -48,6 +47,6 @@ const (
MsgChat MessageType = iota // standard chat MsgChat MessageType = iota // standard chat
MsgAction // /me command MsgAction // /me command
MsgServer // server message MsgServer // server message
MsgError MsgError // something went wrong
MsgNotice // Like MsgServer, but for mods and admins only. MsgNotice // Like MsgServer, but for mods and admins only.
) )

View File

@ -149,7 +149,7 @@ func wsHandler(w http.ResponseWriter, r *http.Request) {
func handleIndexTemplate(w http.ResponseWriter, r *http.Request) { func handleIndexTemplate(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles("./static/index.html") t, err := template.ParseFiles("./static/index.html")
if err != nil { if err != nil {
fmt.Printf("[ERR] could not parse template file, %v\n", err) fmt.Printf("Error parsing template file, %v\n", err)
return return
} }
@ -180,7 +180,7 @@ func handleIndexTemplate(w http.ResponseWriter, r *http.Request) {
err = t.Execute(w, data) err = t.Execute(w, data)
if err != nil { if err != nil {
fmt.Printf("[ERR] could not execute file, %v", err) fmt.Printf("Error executing file, %v", err)
} }
} }

View File

@ -3,14 +3,15 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
"time" "time"
"github.com/dennwc/dom/js" "github.com/dennwc/dom/js"
"github.com/zorchenhimer/MovieNight/common" "github.com/zorchenhimer/MovieNight/common"
) )
var names []string var (
names []js.Value
)
func recieve(v []js.Value) { func recieve(v []js.Value) {
if len(v) == 0 { if len(v) == 0 {
@ -37,19 +38,22 @@ func recieve(v []js.Value) {
h := data.(common.HiddenMessage) h := data.(common.HiddenMessage)
switch h.Type { switch h.Type {
case common.CdUsers: case common.CdUsers:
names = nil var names []string
for _, i := range h.Data.([]interface{}) { for _, i := range h.Data.([]interface{}) {
names = append(names, i.(string)) names = append(names, i.(string))
} }
} }
case common.DTEvent: case common.DTEvent:
d := data.(common.DataEvent) d := data.(common.DataEvent)
if d.Event == common.EvJoin { if d.Event == common.EvJoin ||
d.Event == common.EvBan ||
d.Event == common.EvKick ||
d.Event == common.EvLeave {
websocketSend("", common.CdUsers) websocketSend("", common.CdUsers)
} }
// on join or leave, update list of possible user names // on join or leave, update list of possible user names
fallthrough fallthrough
case common.DTChat, common.DTError: case common.DTChat:
js.Call("appendMessages", data.HTML()) js.Call("appendMessages", data.HTML())
case common.DTCommand: case common.DTCommand:
d := data.(common.DataCommand) d := data.(common.DataCommand)
@ -113,9 +117,9 @@ func showSendError(err error) {
func main() { func main() {
js.Set("recieveMessage", js.CallbackOf(recieve)) js.Set("recieveMessage", js.CallbackOf(recieve))
js.Set("sendMessage", js.FuncOf(send)) js.Set("sendMessage", js.FuncOf(send))
js.Set("getNames", js.FuncOf(func(_ js.Value, v []js.Value) interface{} {
return strings.Join(names, ",") // Get names on first run
})) websocketSend("", common.CdUsers)
// This is needed so the goroutine does not end // This is needed so the goroutine does not end
for { for {