Start convert to JSON data b/t client and server

Chat and events should work.  Commands do not work yet.  Everything
also needs to be tested and cleaned up a bunch more.
This commit is contained in:
Zorchenhimer 2019-03-13 01:09:24 -04:00
parent 382b03b430
commit 9c80269943
6 changed files with 393 additions and 30 deletions

View File

@ -2,13 +2,13 @@
all: vet fmt MovieNight MovieNight.exe static/main.wasm
MovieNight.exe: *.go
MovieNight.exe: *.go common/*.go
GOOS=windows GOARCH=amd64 go build -o MovieNight.exe
MovieNight: *.go
MovieNight: *.go common/*.go
GOOS=linux GOARCH=386 go build -o MovieNight
static/main.wasm: wasm/*.go
static/main.wasm: wasm/*.go common/*.go
GOOS=js GOARCH=wasm go build -o ./static/main.wasm wasm/*.go
clean:
@ -23,3 +23,6 @@ vet:
sync:
scp -i /c/movienight/movienight-deploy.key -r . zorchenhimer@movienight.zorchenhimer.com:/home/zorchenhimer/movienight
run: all
./MovieNight.exe

View File

@ -8,6 +8,7 @@ import (
"unicode"
"github.com/gorilla/websocket"
//"github.com/zorchenhimer/MovieNight/common"
)
type Client struct {
@ -22,8 +23,7 @@ type Client struct {
var emotes map[string]string
func ParseEmotes(msg string) string {
words := strings.Split(msg, " ")
func ParseEmotesArray(words []string) []string {
newWords := []string{}
for _, word := range words {
word = strings.Trim(word, "[]")
@ -40,7 +40,12 @@ func ParseEmotes(msg string) string {
newWords = append(newWords, word)
}
}
return strings.Join(newWords, " ")
return newWords
}
func ParseEmotes(msg string) string {
words := ParseEmotesArray(strings.Split(msg, " "))
return strings.Join(words, " ")
}
//Client has a new message to broadcast
@ -116,15 +121,16 @@ func (cl *Client) ServerMessage(msg string) {
// Outgoing messages
func (cl *Client) Message(msg string) {
msg = ParseEmotes(msg)
cl.belongsTo.AddMsg(
`<span class="name" style="color:` + cl.color + `">` + cl.name +
`</span><b>:</b> <span class="msg">` + msg + `</span><br />`)
cl.belongsTo.AddMsg(cl, false, false, msg)
//`<span class="name" style="color:` + cl.color + `">` + cl.name +
// `</span><b>:</b> <span class="msg">` + msg + `</span><br />`)
}
// Outgoing /me command
func (cl *Client) Me(msg string) {
msg = ParseEmotes(msg)
cl.belongsTo.AddMsg(fmt.Sprintf(`<span style="color:%s"><span class="name">%s</span> <span class="cmdme">%s</span><br />`, cl.color, cl.name, msg))
cl.belongsTo.AddMsg(cl, true, false, msg)
//cl.belongsTo.AddMsg(fmt.Sprintf(`<span style="color:%s"><span class="name">%s</span> <span class="cmdme">%s</span><br />`, cl.color, cl.name, msg))
}
func (cl *Client) Mod() {

View File

@ -5,6 +5,8 @@ import (
"html"
"regexp"
"strings"
"github.com/zorchenhimer/MovieNight/common"
)
var commands *CommandControl
@ -91,8 +93,9 @@ func init() {
if len(args) == 0 {
return "Missing message"
}
svmsg := formatLinks(ParseEmotes(strings.Join(args, " ")))
cl.belongsTo.AddCmdMsg(fmt.Sprintf(`<div class="announcement">%s</div>`, svmsg))
svmsg := formatLinks(strings.Join(ParseEmotesArray(args), " "))
//cl.belongsTo.AddCmdMsg(fmt.Sprintf(`<div class="announcement">%s</div>`, svmsg))
cl.belongsTo.AddMsg(cl, false, true, svmsg)
return ""
},
},
@ -198,7 +201,8 @@ func init() {
"reloadplayer": Command{
HelpText: "Reload the stream player for everybody in chat.",
Function: func(cl *Client, args []string) string {
cl.belongsTo.AddCmdMsg(`<span class="svmsg">[SERVER] Video player reload forced.</span><script>initPlayer();</script><br />`)
//cl.belongsTo.AddCmdMsg(`<span class="svmsg">[SERVER] Video player reload forced.</span><script>initPlayer();</script><br />`)
cl.belongsTo.AddCmdMsg(common.CMD_REFRESHPLAYER, nil)
return "Reloading player for all chatters."
},
},

View File

@ -12,6 +12,7 @@ import (
"time"
"github.com/gorilla/websocket"
"github.com/zorchenhimer/MovieNight/common"
)
const (
@ -117,8 +118,15 @@ func (cr *ChatRoom) Join(name string, conn *websocket.Conn) (*Client, error) {
cr.clients[strings.ToLower(name)] = client
fmt.Printf("[join] %s %s\n", host, name)
client.Send(cr.GetPlayingString())
cr.AddMsg(fmt.Sprintf("<i><b style=\"color:%s\">%s</b> has joined the chat.</i><br />", client.color, name))
//client.Send(cr.GetPlayingString())
playingCommand, err := common.EncodeCommand(common.CMD_PLAYING, []string{cr.playing, cr.playingLink})
if err != nil {
fmt.Printf("Unable to encode playing command on join: %s\n", err)
} else {
client.Send(playingCommand)
}
//cr.AddMsg(fmt.Sprintf("<i><b style=\"color:%s\">%s</b> has joined the chat.</i><br />", client.color, name))
cr.AddEventMsg(common.EV_JOIN, name, client.color)
return client, nil
}
@ -136,7 +144,8 @@ func (cr *ChatRoom) Leave(name, color string) {
client.conn.Close()
cr.delClient(name)
cr.AddMsg(fmt.Sprintf("<i><b style=\"color:%s\">%s</b> has left the chat.</i><br />", color, name))
//cr.AddMsg(fmt.Sprintf("<i><b style=\"color:%s\">%s</b> has left the chat.</i><br />", color, name))
cr.AddEventMsg(common.EV_LEAVE, name, color)
fmt.Printf("[leave] %s %s\n", client.Host(), client.name)
}
@ -158,11 +167,13 @@ func (cr *ChatRoom) Kick(name string) string {
return "Jebaited No."
}
color := client.color
host := client.Host()
client.conn.Close()
cr.delClient(name)
cr.AddMsg(fmt.Sprintf("<i><b>%s</b> has been kicked.</i><br />", name))
//cr.AddMsg(fmt.Sprintf("<i><b>%s</b> has been kicked.</i><br />", name))
cr.AddEventMsg(common.EV_KICK, name, color)
fmt.Printf("[kick] %s %s has been kicked\n", host, name)
return ""
}
@ -179,6 +190,7 @@ func (cr *ChatRoom) Ban(name string) string {
names := []string{}
host := client.Host()
color := client.color
client.conn.Close()
cr.delClient(name)
@ -197,20 +209,56 @@ func (cr *ChatRoom) Ban(name string) string {
err = settings.AddBan(host, names)
if err != nil {
fmt.Printf("[BAN] Error banning %q: %s\n", name, err)
cr.AddMsg(fmt.Sprintf("<i><b>%s</b> has been kicked.</i><br />", name))
//cr.AddMsg(fmt.Sprintf("<i><b>%s</b> has been kicked.</i><br />", name))
cr.AddEventMsg(common.EV_KICK, name, color)
} else {
cr.AddMsg(fmt.Sprintf("<i><b>%s</b> has been banned.</i><br />", name))
//cr.AddMsg(fmt.Sprintf("<i><b>%s</b> has been banned.</i><br />", name))
cr.AddEventMsg(common.EV_BAN, name, color)
}
return ""
}
//adding message to queue
func (cr *ChatRoom) AddMsg(msg string) {
cr.queue <- msg
// Add a chat message from a viewer
func (cr *ChatRoom) AddMsg(from *Client, isAction, isServer bool, msg string) {
data, err := common.EncodeChat(
from.name,
from.color,
msg,
isAction,
isServer)
if err != nil {
fmt.Printf("Error encoding chat message: %s", err)
cr.queue <- msg
return
}
fmt.Println("Chat encoded OK")
cr.queue <- data
}
func (cr *ChatRoom) AddCmdMsg(msg string) {
cr.queue <- msg
func (cr *ChatRoom) AddCmdMsg(command common.CommandType, args []string) {
data, err := common.EncodeCommand(command, args)
if err != nil {
fmt.Printf("Error encoding command: %s", err)
//cr.queue <- msg
return
}
cr.queue <- data
}
func (cr *ChatRoom) AddEventMsg(event common.EventType, name, color string) {
data, err := common.EncodeEvent(event, name, color)
if err != nil {
fmt.Printf("Error encoding command: %s", err)
//cr.queue <- msg
return
}
cr.queue <- data
}
func (cr *ChatRoom) Unmod(name string) error {
@ -281,18 +329,20 @@ infLoop:
func (cr *ChatRoom) ClearPlaying() {
cr.playing = ""
cr.playingLink = ""
cr.AddCmdMsg(`<script>setPlaying("","");</script>`)
//cr.AddCmdMsg(`<script>setPlaying("","");</script>`)
cr.AddCmdMsg(common.CMD_PLAYING, []string{"", ""})
}
func (cr *ChatRoom) SetPlaying(title, link string) {
cr.playing = title
cr.playingLink = link
cr.AddCmdMsg(cr.GetPlayingString())
//cr.AddCmdMsg(cr.GetPlayingString())
cr.AddCmdMsg(common.CMD_PLAYING, []string{title, link})
}
func (cr *ChatRoom) GetPlayingString() string {
return fmt.Sprintf(`<script>setPlaying("%s","%s");</script>`, cr.playing, cr.playingLink)
}
//func (cr *ChatRoom) GetPlayingString() string {
// return fmt.Sprintf(`<script>setPlaying("%s","%s");</script>`, cr.playing, cr.playingLink)
//}
func (cr *ChatRoom) GetNames() []string {
names := []string{}

272
common/chatdata.go Normal file
View File

@ -0,0 +1,272 @@
package common
import (
"encoding/json"
"fmt"
"strconv"
"strings"
)
type ChatData struct {
Type DataType
Data DataInterface
}
type DataError struct {
Message string
}
type DataChat struct {
From string
Color string
Message string
IsAction bool
IsServer bool // server message?
}
type DataCommand struct {
Command CommandType
Arguments []string
}
type DataEvent struct {
Event EventType
User string
Color string
}
type DataInterface interface {
GetType() DataType
HTML() string
}
type VisibleData interface {
HTML() string
}
func (dc DataChat) GetType() DataType {
return DT_CHAT
}
func (de DataError) GetType() DataType {
return DT_ERROR
}
func (dc DataCommand) GetType() DataType {
return DT_COMMAND
}
func (de DataEvent) GetType() DataType {
return DT_EVENT
}
type DataType int
const (
DT_INVALID DataType = iota
DT_CHAT // chat message
DT_ERROR // something went wrong with the previous request
DT_COMMAND // non-chat function
DT_EVENT // join/leave/kick/ban events
)
func ParseDataType(token json.Token) (DataType, error) {
d := fmt.Sprintf("%.0f", token)
val, err := strconv.ParseInt(d, 10, 32)
if err != nil {
fmt.Printf("Invalid data type value: %q\n", d)
return DT_INVALID, err
}
return DataType(val), nil
}
type CommandType int
const (
CMD_PLAYING CommandType = iota
CMD_REFRESHPLAYER
CMD_PURGECHAT
)
type EventType int
const (
EV_JOIN EventType = iota
EV_LEAVE
EV_KICK
EV_BAN
EV_SERVERMESSAGE
)
// TODO: Read this HTML from a template somewhere
func (dc DataChat) HTML() string {
if dc.IsAction {
return `<span style="color:` + dc.Color + `"><span class="name">` + dc.From +
`</span> <span class="cmdme">` + dc.Message + `</span><br />`
}
if dc.IsServer {
return `<div class="announcement">` + dc.Message + `</div>`
}
return `<span class="name" style="color:` + dc.Color + `">` + dc.From +
`</span><b>:</b> <span class="msg">` + dc.Message + `</span><br />`
}
func (de DataEvent) HTML() string {
switch de.Event {
case EV_KICK:
return `<span class="event"><span class="name" style="color:` + de.Color + `">` +
de.User + `</b> has been kicked.</span><br />`
case EV_LEAVE:
return `<span class="event"><span class="name" style="color:` + de.Color + `">` +
de.User + `</b> has left the chat.</span><br />`
case EV_BAN:
return `<span class="event"><span class="name" style="color:` + de.Color + `">` +
de.User + `</b> has been banned.</span><br />`
case EV_JOIN:
return `<span class="event"><span class="name" style="color:` + de.Color + `">` +
de.User + `</b> has joined the chat.</span><br />`
}
return ""
}
func (de DataError) HTML() string {
return `<span class="svmsg"><b>Error</b>: ` + de.Message + `</span><br />`
}
func (de DataCommand) HTML() string {
return ""
}
func EncodeChat(name, color, msg string, isAction, isServer bool) (string, error) {
d := ChatData{
Type: DT_CHAT,
Data: DataChat{
From: name,
Color: color,
IsAction: isAction,
IsServer: isServer,
Message: msg,
},
}
j, err := jsonifyChatData(d)
fmt.Printf("Err: %s; data: %s\n", err, j)
return j, err
}
func EncodeError(message string) (string, error) {
d := ChatData{
Type: DT_ERROR,
Data: DataError{Message: message},
}
return jsonifyChatData(d)
}
func EncodeCommand(command CommandType, args []string) (string, error) {
d := ChatData{
Type: DT_COMMAND,
Data: DataCommand{
Command: command,
Arguments: args,
},
}
return jsonifyChatData(d)
}
func EncodeEvent(event EventType, name, color string) (string, error) {
d := ChatData{
Type: DT_EVENT,
Data: DataEvent{
Event: event,
User: name,
Color: color,
},
}
return jsonifyChatData(d)
}
func jsonifyChatData(data ChatData) (string, error) {
raw, err := json.Marshal(data)
if err != nil {
return "", err
}
return string(raw), nil
}
func DecodeData(rawjson string) (DataInterface, error) {
data := ChatData{}
decoder := json.NewDecoder(strings.NewReader(rawjson))
// Open bracket
t, err := decoder.Token()
if err != nil {
return nil, fmt.Errorf("Open bracket token error: %s", err)
}
fmt.Printf("Token: %q\n", t)
for decoder.More() {
key, err := decoder.Token()
if err != nil {
return nil, fmt.Errorf("Error decoding token: %s", err)
}
fmt.Printf("Key: %q\n", key)
if fmt.Sprintf("%s", key) == "Type" {
value, err := decoder.Token()
if err != nil {
return nil, fmt.Errorf("Error decoding data value: %q", err)
}
data.Type, err = ParseDataType(value)
if err != nil {
return nil, fmt.Errorf("Error parsing data type: %d", data.Type)
}
fmt.Printf("data.Type: %d\n", data.Type)
} else {
fmt.Printf("Key: %q\n", key)
//value, err := decoder.Token()
//if err != nil {
// return nil, fmt.Errorf("Error decoding value token: %s", err)
//}
//fmt.Printf("Value: %q", value)
switch DataType(data.Type) {
case DT_CHAT:
d := DataChat{}
if err := decoder.Decode(&d); err != nil {
return nil, fmt.Errorf("Unable to decode DataChat: %s", err)
}
return d, nil
case DT_ERROR:
d := DataError{}
if err := decoder.Decode(&d); err != nil {
return nil, fmt.Errorf("Unable to decode DataError: %s", err)
}
return d, nil
case DT_COMMAND:
d := DataCommand{}
if err := decoder.Decode(&d); err != nil {
return nil, fmt.Errorf("Unable to decode DataCommand: %s", err)
}
return d, nil
case DT_EVENT:
d := DataEvent{}
if err := decoder.Decode(&d); err != nil {
return nil, fmt.Errorf("Unable to decode DataEvent: %s", err)
}
return d, nil
default:
return nil, fmt.Errorf("Invalid data type: %d", data.Type)
}
//fmt.Printf("di.GetType: %q\n", di.GetType())
//err = decoder.Decode(&di)
//if err != nil {
// return nil, fmt.Errorf("Unable to decode data into interface: %s", err)
//}
//return di, nil
}
}
return nil, fmt.Errorf("Incomplete data")
}

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/dennwc/dom/js"
"github.com/zorchenhimer/MovieNight/common"
)
func log(s string) {
@ -12,7 +13,34 @@ func log(s string) {
}
func recieve(v []js.Value) {
js.Call("appendMessages", v)
if len(v) == 0 {
fmt.Printf("No data received")
return
}
fmt.Printf("Received: %s\n", v[0])
data, err := common.DecodeData(fmt.Sprintf("%s", v[0]))
if err != nil {
fmt.Printf("Error decoding data: %s\n", err)
js.Call("appendMessages", v)
return
}
//dt, err := common.ParseDataType(*data.Type)
//if err != nil {
// fmt.Printf("Error decoding type: %s\n", err)
// js.Call("appendMessages", v)
// return
//}
switch data.GetType() {
case common.DT_CHAT, common.DT_EVENT, common.DT_ERROR:
fmt.Printf("data raw: %q\n", data)
dc := common.VisibleData(data)
js.Call("appendMessages", dc.HTML())
case common.DT_COMMAND:
return
}
}
func send(v []js.Value) {