archive
This commit is contained in:
205
MovieNight/chatclient.go
Executable file
205
MovieNight/chatclient.go
Executable file
@@ -0,0 +1,205 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/zorchenhimer/MovieNight/common"
|
||||
)
|
||||
|
||||
var (
|
||||
regexSpoiler = regexp.MustCompile(`\|\|(.*?)\|\|`)
|
||||
spoilerStart = `<span class="spoiler" onclick='$(this).removeClass("spoiler").addClass("spoiler-active")'>`
|
||||
spoilerEnd = `</span>`
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
name string // Display name
|
||||
conn *chatConnection
|
||||
belongsTo *ChatRoom
|
||||
color string
|
||||
CmdLevel common.CommandLevel
|
||||
IsColorForced bool
|
||||
IsNameForced bool
|
||||
regexName *regexp.Regexp
|
||||
|
||||
// Times since last event. use time.Duration.Since()
|
||||
nextChat time.Time // rate limit chat messages
|
||||
nextNick time.Time // rate limit nickname changes
|
||||
nextColor time.Time // rate limit color changes
|
||||
nextAuth time.Time // rate limit failed auth attempts. Sould prolly have a backoff policy.
|
||||
authTries int // number of failed auth attempts
|
||||
|
||||
nextDuplicate time.Time
|
||||
lastMsg string
|
||||
}
|
||||
|
||||
func NewClient(connection *chatConnection, room *ChatRoom, name, color string) (*Client, error) {
|
||||
c := &Client{
|
||||
conn: connection,
|
||||
belongsTo: room,
|
||||
color: color,
|
||||
}
|
||||
|
||||
if err := c.setName(name); err != nil {
|
||||
return nil, fmt.Errorf("could not set client name to %#v: %v", name, err)
|
||||
}
|
||||
|
||||
// Set initial vaules to their rate limit duration in the past.
|
||||
c.nextChat = time.Now()
|
||||
c.nextNick = time.Now()
|
||||
c.nextColor = time.Now()
|
||||
c.nextAuth = time.Now()
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
//Client has a new message to broadcast
|
||||
func (cl *Client) NewMsg(data common.ClientData) {
|
||||
return
|
||||
}
|
||||
|
||||
func (cl *Client) SendChatData(data common.ChatData) error {
|
||||
// Don't send chat or event data to clients that have not fully joined the
|
||||
// chatroom (ie, they have not set a name).
|
||||
if cl.name == "" && (data.Type == common.DTChat || data.Type == common.DTEvent) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Colorize name on chat messages
|
||||
if data.Type == common.DTChat {
|
||||
var err error
|
||||
data = cl.replaceColorizedName(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not colorize name: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
cd, err := data.ToJSON()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create ChatDataJSON of type %d: %v", data.Type, err)
|
||||
}
|
||||
return cl.Send(cd)
|
||||
}
|
||||
|
||||
func (cl *Client) Send(data common.ChatDataJSON) error {
|
||||
err := cl.conn.WriteData(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.CmdlUser, common.MsgServer))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could send server message to %s: message - %#v: %v", cl.name, s, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make links clickable
|
||||
func formatLinks(input string) string {
|
||||
newMsg := []string{}
|
||||
for _, word := range strings.Split(input, " ") {
|
||||
if strings.HasPrefix(word, "http://") || strings.HasPrefix(word, "https://") {
|
||||
word = html.UnescapeString(word)
|
||||
word = fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, word, word)
|
||||
}
|
||||
newMsg = append(newMsg, word)
|
||||
}
|
||||
return strings.Join(newMsg, " ")
|
||||
}
|
||||
|
||||
//Exiting out
|
||||
func (cl *Client) Exit() {
|
||||
cl.belongsTo.Leave(cl.name, cl.color)
|
||||
}
|
||||
|
||||
// Outgoing messages
|
||||
func (cl *Client) Message(msg string) {
|
||||
msg = common.ParseEmotes(msg)
|
||||
cl.belongsTo.AddMsg(cl, false, false, msg)
|
||||
}
|
||||
|
||||
// Outgoing /me command
|
||||
func (cl *Client) Me(msg string) {
|
||||
msg = common.ParseEmotes(msg)
|
||||
cl.belongsTo.AddMsg(cl, true, false, msg)
|
||||
}
|
||||
|
||||
func (cl *Client) Mod() {
|
||||
if cl.CmdLevel < common.CmdlMod {
|
||||
cl.CmdLevel = common.CmdlMod
|
||||
}
|
||||
}
|
||||
|
||||
func (cl *Client) Unmod() {
|
||||
cl.CmdLevel = common.CmdlUser
|
||||
}
|
||||
|
||||
func (cl *Client) Host() string {
|
||||
return cl.conn.Host()
|
||||
}
|
||||
|
||||
func (cl *Client) setName(s string) error {
|
||||
cl.name = s
|
||||
if cl.conn != nil {
|
||||
cl.conn.clientName = s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cl *Client) setColor(s string) error {
|
||||
cl.color = s
|
||||
return cl.SendChatData(common.NewChatHiddenMessage(common.CdColor, cl.color))
|
||||
}
|
||||
|
||||
func (cl *Client) replaceColorizedName(chatData common.ChatData) common.ChatData {
|
||||
data := chatData.Data.(common.DataMessage)
|
||||
words := strings.Split(data.Message, " ")
|
||||
newWords := []string{}
|
||||
|
||||
for _, word := range words {
|
||||
if strings.ToLower(word) == strings.ToLower(cl.name) || strings.ToLower(word) == strings.ToLower("@"+cl.name) {
|
||||
newWords = append(newWords, `<span class="mention">`+word+`</span>`)
|
||||
} else {
|
||||
newWords = append(newWords, word)
|
||||
}
|
||||
}
|
||||
|
||||
data.Message = strings.Join(newWords, " ")
|
||||
chatData.Data = data
|
||||
return chatData
|
||||
}
|
||||
|
||||
var dumbSpaces = []string{
|
||||
"\n",
|
||||
"\t",
|
||||
"\r",
|
||||
"\u200b",
|
||||
}
|
||||
|
||||
func removeDumbSpaces(msg string) string {
|
||||
for _, ds := range dumbSpaces {
|
||||
msg = strings.ReplaceAll(msg, ds, " ")
|
||||
}
|
||||
|
||||
newMsg := ""
|
||||
for _, r := range msg {
|
||||
if unicode.IsSpace(r) {
|
||||
newMsg += " "
|
||||
} else {
|
||||
newMsg += string(r)
|
||||
}
|
||||
}
|
||||
return newMsg
|
||||
}
|
||||
|
||||
func addSpoilerTags(msg string) string {
|
||||
return regexSpoiler.ReplaceAllString(msg, fmt.Sprintf(`%s$1%s`, spoilerStart, spoilerEnd))
|
||||
}
|
||||
Reference in New Issue
Block a user