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"
)
func connSend(s string, c *websocket.Conn) {
c.WriteMessage(websocket.TextMessage, []byte(s))
}
type Client struct {
name string // Display name
conn *websocket.Conn
@ -56,12 +52,19 @@ func (cl *Client) NewMsg(data common.ClientData) {
switch data.Type {
case common.CdUsers:
fmt.Printf("[chat|hidden] <%s> get list of users\n", cl.name)
s, err := common.EncodeHiddenMessage(data.Type, chat.GetNames())
if err != nil {
fmt.Printf("[ERR] could not encode user list, %v", err)
return
names := chat.GetNames()
idx := -1
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:
msg := html.EscapeString(data.Message)
msg = removeDumbSpaces(msg)
@ -81,7 +84,10 @@ func (cl *Client) NewMsg(data common.ClientData) {
response := commands.RunCommand(cmd, args, cl)
if response != "" {
cl.ServerMessage(response)
err := cl.SendServerMessage(ParseEmotes(msg))
if err != nil {
fmt.Printf("Error command results %v\n", err)
}
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
func formatLinks(input string) string {
newMsg := []string{}
@ -121,22 +151,6 @@ func (cl *Client) Exit() {
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
func (cl *Client) Message(msg string) {
msg = ParseEmotes(msg)

View File

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

View File

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

View File

@ -6,6 +6,8 @@ import (
"fmt"
)
type NewChatDataFunc func() (ChatData, error)
type DataInterface interface {
HTML() string
}
@ -26,10 +28,6 @@ func (c ChatData) GetData() (DataInterface, error) {
d := DataMessage{}
err = json.Unmarshal(c.Data, &d)
data = d
case DTError:
d := DataError{}
err = json.Unmarshal(c.Data, &d)
data = d
case DTCommand:
d := DataCommand{}
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>`
}
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 {
From string
Color string
@ -116,17 +98,15 @@ func (dc DataMessage) HTML() string {
}
}
func EncodeMessage(name, color, msg string, msgtype MessageType) (string, error) {
d, err := newChatData(DTChat, DataMessage{
From: name,
Color: color,
Message: msg,
Type: msgtype,
})
if err != nil {
return "", err
func NewChatMessage(name, color, msg string, msgtype MessageType) NewChatDataFunc {
return func() (ChatData, error) {
return newChatData(DTChat, DataMessage{
From: name,
Color: color,
Message: msg,
Type: msgtype,
})
}
return jsonifyChatData(d)
}
type DataCommand struct {
@ -138,15 +118,13 @@ func (de DataCommand) HTML() string {
return ""
}
func EncodeCommand(command CommandType, args []string) (string, error) {
d, err := newChatData(DTCommand, DataCommand{
Command: command,
Arguments: args,
})
if err != nil {
return "", err
func NewChatCommand(command CommandType, args []string) NewChatDataFunc {
return func() (ChatData, error) {
return newChatData(DTCommand, DataCommand{
Command: command,
Arguments: args,
})
}
return jsonifyChatData(d)
}
type DataEvent struct {
@ -173,16 +151,14 @@ func (de DataEvent) HTML() string {
return ""
}
func EncodeEvent(event EventType, name, color string) (string, error) {
d, err := newChatData(DTEvent, DataEvent{
Event: event,
User: name,
Color: color,
})
if err != nil {
return "", err
func NewChatEvent(event EventType, name, color string) NewChatDataFunc {
return func() (ChatData, error) {
return newChatData(DTEvent, DataEvent{
Event: event,
User: name,
Color: color,
})
}
return jsonifyChatData(d)
}
// DataHidden is for the server to send instructions and data
@ -196,15 +172,13 @@ func (h HiddenMessage) HTML() string {
return ""
}
func EncodeHiddenMessage(clientType ClientDataType, data interface{}) (string, error) {
d, err := newChatData(DTHidden, HiddenMessage{
Type: clientType,
Data: data,
})
if err != nil {
return "", err
func NewChatHiddenMessage(clientType ClientDataType, data interface{}) NewChatDataFunc {
return func() (ChatData, error) {
return newChatData(DTHidden, HiddenMessage{
Type: clientType,
Data: data,
})
}
return jsonifyChatData(d)
}
func jsonifyChatData(data ChatData) (string, error) {

View File

@ -13,7 +13,6 @@ type DataType int
const (
DTInvalid DataType = iota
DTChat // chat message
DTError // something went wrong with the previous request
DTCommand // non-chat function
DTEvent // join/leave/kick/ban events
DTClient // a message coming from the client
@ -48,6 +47,6 @@ const (
MsgChat MessageType = iota // standard chat
MsgAction // /me command
MsgServer // server message
MsgError
MsgNotice // Like MsgServer, but for mods and admins only.
MsgError // something went wrong
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) {
t, err := template.ParseFiles("./static/index.html")
if err != nil {
fmt.Printf("[ERR] could not parse template file, %v\n", err)
fmt.Printf("Error parsing template file, %v\n", err)
return
}
@ -180,7 +180,7 @@ func handleIndexTemplate(w http.ResponseWriter, r *http.Request) {
err = t.Execute(w, data)
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 (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/dennwc/dom/js"
"github.com/zorchenhimer/MovieNight/common"
)
var names []string
var (
names []js.Value
)
func recieve(v []js.Value) {
if len(v) == 0 {
@ -37,19 +38,22 @@ func recieve(v []js.Value) {
h := data.(common.HiddenMessage)
switch h.Type {
case common.CdUsers:
names = nil
var names []string
for _, i := range h.Data.([]interface{}) {
names = append(names, i.(string))
}
}
case common.DTEvent:
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)
}
// on join or leave, update list of possible user names
fallthrough
case common.DTChat, common.DTError:
case common.DTChat:
js.Call("appendMessages", data.HTML())
case common.DTCommand:
d := data.(common.DataCommand)
@ -113,9 +117,9 @@ func showSendError(err error) {
func main() {
js.Set("recieveMessage", js.CallbackOf(recieve))
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
for {