package common import ( "bytes" "encoding/json" "errors" "fmt" "strings" "text/template" ) type DataInterface interface { HTML() string } type ChatData struct { Type DataType Data DataInterface } func (c ChatData) ToJSON() (ChatDataJSON, error) { rawData, err := json.Marshal(c.Data) return ChatDataJSON{ Type: c.Type, Data: rawData, }, err } type ChatDataJSON struct { Type DataType Data json.RawMessage } func (c ChatDataJSON) ToData() (ChatData, error) { data, err := c.GetData() return ChatData{ Type: c.Type, Data: data, }, err } func (c ChatDataJSON) 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 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 case DTHidden: d := HiddenMessage{} err = json.Unmarshal(c.Data, &d) data = d default: err = fmt.Errorf("unhandled data type: %d", c.Type) } return data, err } type ClientData struct { Type ClientDataType Message string } func (c ClientData) HTML() string { // Client data is for client to server communication only, so clients should not see this return `The developer messed up. You should not be seeing this.` } type DataMessage struct { From string Color string Message string Level CommandLevel Type MessageType } var ( cmdme = template.Must(template.New("cmdme").Parse(`{{.From}} {{.Message}}`)) announcement = template.Must(template.New("announcement").Parse(`{{.Message}}`)) errormsg = template.Must(template.New("error").Parse(`{{.Message}}`)) notice = template.Must(template.New("notice").Parse(`{{.Message}}`)) command = template.Must(template.New("command").Parse(`{{.Message}}`)) commanderror = template.Must(template.New("commanderror").Parse(`{{.Message}}`)) cmdlMod = template.Must(template.New("cmdlMod").Parse(`{{.From}}: {{.Message}}`)) cmdlAdmin = template.Must(template.New("CmdlAdmin").Parse(`{{.From}}: {{.Message}}`)) defaultMsg = template.Must(template.New("defaultMsg").Parse(`{{.From}}: {{.Message}}`)) ) // TODO: Read this HTML from a template somewhere func (dc DataMessage) HTML() string { buf := &bytes.Buffer{} switch dc.Type { case MsgAction: cmdme.Execute(buf, dc) return buf.String() case MsgServer: announcement.Execute(buf, dc) return buf.String() case MsgError: errormsg.Execute(buf, dc) return buf.String() case MsgNotice: notice.Execute(buf, dc) return buf.String() case MsgCommandResponse: command.Execute(buf, dc) return buf.String() case MsgCommandError: commanderror.Execute(buf, dc) return buf.String() default: switch dc.Level { case CmdlMod: cmdlMod.Execute(buf, dc) case CmdlAdmin: cmdlAdmin.Execute(buf, dc) default: defaultMsg.Execute(buf, dc) } return buf.String() } } func NewChatMessage(name, color, msg string, lvl CommandLevel, msgtype MessageType) ChatData { return ChatData{ Type: DTChat, Data: DataMessage{ From: name, Color: color, Message: msg, Type: msgtype, Level: lvl, }, } } type DataCommand struct { Command CommandType Arguments []string } func (de DataCommand) HTML() string { switch de.Command { case CmdPurgeChat: return `Chat has been purged by a moderator.` default: return "" } } func NewChatCommand(command CommandType, args []string) ChatData { return ChatData{ Type: DTCommand, Data: DataCommand{ Command: command, Arguments: args, }, } } type DataEvent struct { Event EventType User string Color string Users []string } var ( evKick = template.Must(template.New("evKick").Parse(`{{.User}} has been kicked.`)) evLeave = template.Must(template.New("evLeave").Parse(`{{.User}} has left the chat.`)) evBan = template.Must(template.New("evBan").Parse(`{{.User}} has been banned.`)) evJoin = template.Must(template.New("evJoin").Parse(`{{.User}} has joined the chat.`)) evNameChangeWC = template.Must(template.New("evNameChangeWC").Parse(`Somebody changed their name, but IDK who {{.}}.`)) evNameChange = template.Must(template.New("evNameChange").Parse(`{{index .Users 0}} has changed their name to {{index .Users 1}}.`)) evNameChangeForced = template.Must(template.New("evNameChangeForced").Parse(`{{index .Users 0}} has had their name changed to {{index .Users 1}} by an admin.`)) evNameChangeForcedWC = template.Must(template.New("evNameChangeForcedWC").Parse(`An admin changed somebody's name, but IDK who {{.}}.`)) ) func (de DataEvent) HTML() string { buf := &bytes.Buffer{} switch de.Event { case EvKick: evKick.Execute(buf, de) return buf.String() case EvLeave: evLeave.Execute(buf, de) return buf.String() case EvBan: evBan.Execute(buf, de) return buf.String() case EvJoin: evJoin.Execute(buf, de) return buf.String() case EvNameChange: de.Users = strings.Split(de.User, ":") if len(de.Users) < 2 { evNameChangeWC.Execute(buf, ParseEmotes("Jebaited")) } else { evNameChange.Execute(buf, de) } return buf.String() case EvNameChangeForced: de.Users = strings.Split(de.User, ":") if len(de.Users) < 2 { evNameChangeForcedWC.Execute(buf, ParseEmotes("Jebaited")) } else { evNameChangeForced.Execute(buf, de) } return buf.String() } return "" } func NewChatEvent(event EventType, name, color string) ChatData { return ChatData{ Type: DTEvent, Data: DataEvent{ Event: event, User: name, Color: color, }, } } // DataHidden is for the server to send instructions and data // to the client without the purpose of outputting it on the chat type HiddenMessage struct { Type ClientDataType Data interface{} } func (h HiddenMessage) HTML() string { return "" } func NewChatHiddenMessage(clientType ClientDataType, data interface{}) ChatData { return ChatData{ Type: DTHidden, Data: HiddenMessage{ Type: clientType, Data: data, }, } } func DecodeData(rawjson string) (ChatDataJSON, error) { var data ChatDataJSON err := json.Unmarshal([]byte(rawjson), &data) return data, err } type JoinData struct { Name string Color string }