Add infrastructure for the client to send formatted data to the server
This commit is contained in:
parent
6a2cb3f906
commit
60dd98a1e7
@ -53,7 +53,8 @@ func ParseEmotes(msg string) string {
|
||||
}
|
||||
|
||||
//Client has a new message to broadcast
|
||||
func (cl *Client) NewMsg(msg string) {
|
||||
func (cl *Client) NewMsg(data common.ClientData) {
|
||||
msg := data.Message
|
||||
msg = html.EscapeString(msg)
|
||||
msg = removeDumbSpaces(msg)
|
||||
msg = strings.Trim(msg, " ")
|
||||
|
52
common/chatcommands.go
Normal file
52
common/chatcommands.go
Normal file
@ -0,0 +1,52 @@
|
||||
package common
|
||||
|
||||
import "strings"
|
||||
|
||||
const CommandNameSeparator = ","
|
||||
|
||||
type ChatCommandNames []string
|
||||
|
||||
func (c ChatCommandNames) String() string {
|
||||
return strings.Join(c, CommandNameSeparator)
|
||||
}
|
||||
|
||||
// Names for commands
|
||||
var (
|
||||
// User Commands
|
||||
CNMe ChatCommandNames = []string{"me"}
|
||||
CNHelp ChatCommandNames = []string{"help"}
|
||||
CNCount ChatCommandNames = []string{"count"}
|
||||
CNColor ChatCommandNames = []string{"color", "colour"}
|
||||
CNWhoAmI ChatCommandNames = []string{"w", "whoami"}
|
||||
CNAuth ChatCommandNames = []string{"auth"}
|
||||
CNUsers ChatCommandNames = []string{"users"}
|
||||
// Mod Commands
|
||||
CNSv ChatCommandNames = []string{"sv"}
|
||||
CNPlaying ChatCommandNames = []string{"playing"}
|
||||
CNUnmod ChatCommandNames = []string{"unmod"}
|
||||
CNKick ChatCommandNames = []string{"kick"}
|
||||
CNBan ChatCommandNames = []string{"ban"}
|
||||
CNUnban ChatCommandNames = []string{"unban"}
|
||||
// Admin Commands
|
||||
CNMod ChatCommandNames = []string{"mod"}
|
||||
CNReloadPlayer ChatCommandNames = []string{"reloadplayer"}
|
||||
CNReloadEmotes ChatCommandNames = []string{"reloademotes"}
|
||||
CNModpass ChatCommandNames = []string{"modpass"}
|
||||
)
|
||||
|
||||
var ChatCommands = []ChatCommandNames{
|
||||
CNMe, CNHelp, CNCount, CNColor, CNWhoAmI, CNAuth, CNUsers,
|
||||
CNSv, CNPlaying, CNUnmod, CNKick, CNBan, CNUnban,
|
||||
CNMod, CNReloadPlayer, CNReloadEmotes, CNModpass,
|
||||
}
|
||||
|
||||
func GetFullChatCommand(c string) string {
|
||||
for _, names := range ChatCommands {
|
||||
for _, n := range names {
|
||||
if c == n {
|
||||
return names.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
@ -2,58 +2,60 @@ package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const CommandNameSeparator = ","
|
||||
|
||||
type ChatCommandNames []string
|
||||
|
||||
func (c ChatCommandNames) String() string {
|
||||
return strings.Join(c, CommandNameSeparator)
|
||||
type DataInterface interface {
|
||||
HTML() string
|
||||
}
|
||||
|
||||
// Names for commands
|
||||
var (
|
||||
// User Commands
|
||||
CNMe ChatCommandNames = []string{"me"}
|
||||
CNHelp ChatCommandNames = []string{"help"}
|
||||
CNCount ChatCommandNames = []string{"count"}
|
||||
CNColor ChatCommandNames = []string{"color", "colour"}
|
||||
CNWhoAmI ChatCommandNames = []string{"w", "whoami"}
|
||||
CNAuth ChatCommandNames = []string{"auth"}
|
||||
CNUsers ChatCommandNames = []string{"users"}
|
||||
// Mod Commands
|
||||
CNSv ChatCommandNames = []string{"sv"}
|
||||
CNPlaying ChatCommandNames = []string{"playing"}
|
||||
CNUnmod ChatCommandNames = []string{"unmod"}
|
||||
CNKick ChatCommandNames = []string{"kick"}
|
||||
CNBan ChatCommandNames = []string{"ban"}
|
||||
CNUnban ChatCommandNames = []string{"unban"}
|
||||
// Admin Commands
|
||||
CNMod ChatCommandNames = []string{"mod"}
|
||||
CNReloadPlayer ChatCommandNames = []string{"reloadplayer"}
|
||||
CNReloadEmotes ChatCommandNames = []string{"reloademotes"}
|
||||
CNModpass ChatCommandNames = []string{"modpass"}
|
||||
)
|
||||
|
||||
var ChatCommands = []ChatCommandNames{
|
||||
CNMe, CNHelp, CNCount, CNColor, CNWhoAmI, CNAuth, CNUsers,
|
||||
CNSv, CNPlaying, CNUnmod, CNKick, CNBan, CNUnban,
|
||||
CNMod, CNReloadPlayer, CNReloadEmotes, CNModpass,
|
||||
type ChatData struct {
|
||||
Hidden bool
|
||||
Type DataType
|
||||
Data json.RawMessage
|
||||
}
|
||||
|
||||
func GetFullChatCommand(c string) string {
|
||||
for _, names := range ChatCommands {
|
||||
for _, n := range names {
|
||||
if c == n {
|
||||
return names.String()
|
||||
}
|
||||
}
|
||||
func (c ChatData) GetData() (DataInterface, error) {
|
||||
var data DataInterface
|
||||
var err error
|
||||
|
||||
switch c.Type {
|
||||
case DTInvalid:
|
||||
return nil, errors.New("data type is invalid")
|
||||
case DTChat:
|
||||
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)
|
||||
data = d
|
||||
case DTEvent:
|
||||
d := DataEvent{}
|
||||
err = json.Unmarshal(c.Data, &d)
|
||||
data = d
|
||||
case DTClient:
|
||||
d := ClientData{}
|
||||
err = json.Unmarshal(c.Data, &d)
|
||||
data = d
|
||||
}
|
||||
return ""
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
func newChatData(hidden bool, dtype DataType, d DataInterface) (ChatData, error) {
|
||||
rawData, err := json.Marshal(d)
|
||||
return ChatData{
|
||||
Hidden: hidden,
|
||||
Type: dtype,
|
||||
Data: rawData,
|
||||
}, err
|
||||
}
|
||||
|
||||
type ClientData struct {
|
||||
@ -61,11 +63,6 @@ type ClientData struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
type ChatData struct {
|
||||
Type DataType
|
||||
Data DataInterface
|
||||
}
|
||||
|
||||
type DataError struct {
|
||||
Message string
|
||||
}
|
||||
@ -88,31 +85,6 @@ type DataEvent struct {
|
||||
Color string
|
||||
}
|
||||
|
||||
type DataInterface interface {
|
||||
GetType() DataType
|
||||
HTML() string
|
||||
}
|
||||
|
||||
func (c ClientData) GetType() DataType {
|
||||
return DTClient
|
||||
}
|
||||
|
||||
func (d DataMessage) GetType() DataType {
|
||||
return DTChat
|
||||
}
|
||||
|
||||
func (d DataError) GetType() DataType {
|
||||
return DTError
|
||||
}
|
||||
|
||||
func (d DataCommand) GetType() DataType {
|
||||
return DTCommand
|
||||
}
|
||||
|
||||
func (d DataEvent) GetType() DataType {
|
||||
return DTEvent
|
||||
}
|
||||
|
||||
type ClientDataType int
|
||||
|
||||
const (
|
||||
@ -136,8 +108,7 @@ 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 DTInvalid, err
|
||||
return DTInvalid, fmt.Errorf("invalid data type value: %q\n", d)
|
||||
}
|
||||
return DataType(val), nil
|
||||
}
|
||||
@ -224,46 +195,45 @@ func (de DataCommand) HTML() string {
|
||||
}
|
||||
|
||||
func EncodeMessage(name, color, msg string, msgtype MessageType) (string, error) {
|
||||
d := ChatData{
|
||||
Type: DTChat,
|
||||
Data: DataMessage{
|
||||
From: name,
|
||||
Color: color,
|
||||
Message: msg,
|
||||
Type: msgtype,
|
||||
},
|
||||
d, err := newChatData(false, DTChat, DataMessage{
|
||||
From: name,
|
||||
Color: color,
|
||||
Message: msg,
|
||||
Type: msgtype,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
j, err := jsonifyChatData(d)
|
||||
return j, err
|
||||
return jsonifyChatData(d)
|
||||
}
|
||||
|
||||
func EncodeError(message string) (string, error) {
|
||||
d := ChatData{
|
||||
Type: DTError,
|
||||
Data: DataError{Message: message},
|
||||
d, err := newChatData(false, DTError, DataError{Message: message})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return jsonifyChatData(d)
|
||||
}
|
||||
|
||||
func EncodeCommand(command CommandType, args []string) (string, error) {
|
||||
d := ChatData{
|
||||
Type: DTCommand,
|
||||
Data: DataCommand{
|
||||
Command: command,
|
||||
Arguments: args,
|
||||
},
|
||||
d, err := newChatData(false, DTCommand, DataCommand{
|
||||
Command: command,
|
||||
Arguments: args,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return jsonifyChatData(d)
|
||||
}
|
||||
|
||||
func EncodeEvent(event EventType, name, color string) (string, error) {
|
||||
d := ChatData{
|
||||
Type: DTEvent,
|
||||
Data: DataEvent{
|
||||
Event: event,
|
||||
User: name,
|
||||
Color: color,
|
||||
},
|
||||
d, err := newChatData(false, DTEvent, DataEvent{
|
||||
Event: event,
|
||||
User: name,
|
||||
Color: color,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return jsonifyChatData(d)
|
||||
}
|
||||
@ -276,64 +246,8 @@ func jsonifyChatData(data ChatData) (string, error) {
|
||||
return string(raw), nil
|
||||
}
|
||||
|
||||
func DecodeData(rawjson string) (DataInterface, error) {
|
||||
data := ChatData{}
|
||||
decoder := json.NewDecoder(strings.NewReader(rawjson))
|
||||
|
||||
// Open bracket
|
||||
_, err := decoder.Token()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Open bracket token error: %s", err)
|
||||
}
|
||||
|
||||
for decoder.More() {
|
||||
key, err := decoder.Token()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error decoding token: %s", err)
|
||||
}
|
||||
|
||||
if key.(string) == "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)
|
||||
}
|
||||
} else {
|
||||
|
||||
switch DataType(data.Type) {
|
||||
case DTChat:
|
||||
d := DataMessage{}
|
||||
if err := decoder.Decode(&d); err != nil {
|
||||
return nil, fmt.Errorf("Unable to decode DataMessage: %s", err)
|
||||
}
|
||||
return d, nil
|
||||
case DTError:
|
||||
d := DataError{}
|
||||
if err := decoder.Decode(&d); err != nil {
|
||||
return nil, fmt.Errorf("Unable to decode DataError: %s", err)
|
||||
}
|
||||
return d, nil
|
||||
case DTCommand:
|
||||
d := DataCommand{}
|
||||
if err := decoder.Decode(&d); err != nil {
|
||||
return nil, fmt.Errorf("Unable to decode DataCommand: %s", err)
|
||||
}
|
||||
return d, nil
|
||||
case DTEvent:
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Incomplete data")
|
||||
func DecodeData(rawjson string) (ChatData, error) {
|
||||
var data ChatData
|
||||
err := json.Unmarshal([]byte(rawjson), &data)
|
||||
return data, err
|
||||
}
|
||||
|
16
handlers.go
16
handlers.go
@ -9,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/zorchenhimer/MovieNight/common"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/nareix/joy4/av/avutil"
|
||||
"github.com/nareix/joy4/av/pubsub"
|
||||
@ -106,15 +108,15 @@ func wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
//first message has to be the name
|
||||
// loop through name since websocket is opened once
|
||||
for client == nil {
|
||||
_, msg, err := conn.ReadMessage()
|
||||
var data common.ClientData
|
||||
err := conn.ReadJSON(&data)
|
||||
if err != nil {
|
||||
fmt.Printf("[handler] Client closed connection: %s\n", conn.RemoteAddr().String())
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
name := string(msg)
|
||||
client, err = chat.Join(name, uid)
|
||||
client, err = chat.Join(data.Message, uid)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case UserFormatError, UserTakenError:
|
||||
@ -133,12 +135,13 @@ func wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
//then watch for incoming messages
|
||||
for {
|
||||
_, msg, err := conn.ReadMessage()
|
||||
var data common.ClientData
|
||||
err := conn.ReadJSON(&data)
|
||||
if err != nil { //if error then assuming that the connection is closed
|
||||
client.Exit()
|
||||
return
|
||||
}
|
||||
client.NewMsg(string(msg))
|
||||
client.NewMsg(data)
|
||||
}
|
||||
|
||||
}()
|
||||
@ -173,6 +176,9 @@ func handleIndexTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
data.Title += " - video"
|
||||
}
|
||||
|
||||
// Force browser to replace cache since file was not changed
|
||||
w.Header().Set("Cache-Control", "no-cache, must-revalidate")
|
||||
|
||||
err = t.Execute(w, data)
|
||||
if err != nil {
|
||||
fmt.Printf("[ERR] could not execute file, %v", err)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -10,20 +11,31 @@ import (
|
||||
|
||||
func recieve(v []js.Value) {
|
||||
if len(v) == 0 {
|
||||
fmt.Printf("No data received")
|
||||
fmt.Println("No data received")
|
||||
return
|
||||
}
|
||||
|
||||
data, err := common.DecodeData(v[0].String())
|
||||
chat, err := common.DecodeData(v[0].String())
|
||||
if err != nil {
|
||||
fmt.Printf("Error decoding data: %s\n", err)
|
||||
js.Call("appendMessages", v)
|
||||
return
|
||||
}
|
||||
|
||||
switch data.GetType() {
|
||||
case common.DTChat, common.DTError, common.DTEvent:
|
||||
js.Call("appendMessages", data.HTML())
|
||||
data, err := chat.GetData()
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing DataInterface: %v", err)
|
||||
js.Call("appendMessages", v)
|
||||
}
|
||||
|
||||
switch chat.Type {
|
||||
case common.DTEvent:
|
||||
// on join or leave, update list of possible user names
|
||||
fallthrough
|
||||
case common.DTChat, common.DTError:
|
||||
if !chat.Hidden {
|
||||
js.Call("appendMessages", data.HTML())
|
||||
}
|
||||
case common.DTCommand:
|
||||
dc := data.(common.DataCommand)
|
||||
|
||||
@ -43,25 +55,52 @@ func recieve(v []js.Value) {
|
||||
case common.CmdPurgeChat:
|
||||
fmt.Println("//TODO: chat purge command received.")
|
||||
case common.CmdHelp:
|
||||
js.Call("appendMesages", data.HTML())
|
||||
if !chat.Hidden {
|
||||
js.Call("appendMessages", data.HTML())
|
||||
}
|
||||
// TODO: open window
|
||||
//js.Call("")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func send(v []js.Value) {
|
||||
if len(v) != 1 {
|
||||
fmt.Printf("expected 1 parameter, got %d", len(v))
|
||||
return
|
||||
func websocketSend(msg string, dataType common.ClientDataType) error {
|
||||
data, err := json.Marshal(common.ClientData{
|
||||
Type: dataType,
|
||||
Message: msg,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not marshal data: %v", err)
|
||||
}
|
||||
|
||||
js.Call("websocketSend", string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func send(this js.Value, v []js.Value) interface{} {
|
||||
if len(v) != 1 {
|
||||
showSendError(fmt.Errorf("expected 1 parameter, got %d", len(v)))
|
||||
return false
|
||||
}
|
||||
|
||||
err := websocketSend(v[0].String(), common.CdMessage)
|
||||
if err != nil {
|
||||
showSendError(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func showSendError(err error) {
|
||||
if err != nil {
|
||||
fmt.Printf("Could not send: %v\n", err)
|
||||
js.Call("appendMessages", `<div><span style="color: red;">Could not send message</span></div>`)
|
||||
}
|
||||
js.Call("websocketSend", v)
|
||||
}
|
||||
|
||||
func main() {
|
||||
js.Set("recieveMessage", js.CallbackOf(recieve))
|
||||
js.Set("sendMessage", js.CallbackOf(send))
|
||||
js.Set("sendMessage", js.FuncOf(send))
|
||||
|
||||
// This is needed so the goroutine does not end
|
||||
for {
|
||||
|
Loading…
Reference in New Issue
Block a user