2020-06-23 03:11:56 +02:00
|
|
|
package chat
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2020-12-22 04:42:47 +01:00
|
|
|
"sync"
|
2020-06-23 03:11:56 +02:00
|
|
|
"time"
|
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"golang.org/x/net/websocket"
|
2020-06-23 08:52:50 +02:00
|
|
|
|
2020-10-05 19:07:09 +02:00
|
|
|
"github.com/owncast/owncast/config"
|
|
|
|
"github.com/owncast/owncast/models"
|
2020-06-23 03:11:56 +02:00
|
|
|
)
|
|
|
|
|
2020-06-23 22:11:01 +02:00
|
|
|
var (
|
|
|
|
_server *server
|
|
|
|
)
|
|
|
|
|
2020-12-22 04:42:47 +01:00
|
|
|
var l = sync.Mutex{}
|
|
|
|
|
2020-11-13 00:14:59 +01:00
|
|
|
// Server represents the server which handles the chat.
|
2020-06-23 22:11:01 +02:00
|
|
|
type server struct {
|
2020-08-06 06:01:06 +02:00
|
|
|
Clients map[string]*Client
|
2020-06-23 03:11:56 +02:00
|
|
|
|
2020-06-23 22:11:01 +02:00
|
|
|
pattern string
|
|
|
|
listener models.ChatListener
|
|
|
|
|
2020-06-23 03:11:56 +02:00
|
|
|
addCh chan *Client
|
|
|
|
delCh chan *Client
|
2020-12-29 22:35:33 +01:00
|
|
|
sendAllCh chan models.ChatEvent
|
2020-06-23 03:11:56 +02:00
|
|
|
pingCh chan models.PingMessage
|
|
|
|
doneCh chan bool
|
|
|
|
errCh chan error
|
|
|
|
}
|
|
|
|
|
2020-11-13 00:14:59 +01:00
|
|
|
// Add adds a client to the server.
|
2020-06-23 22:11:01 +02:00
|
|
|
func (s *server) add(c *Client) {
|
2020-06-23 03:11:56 +02:00
|
|
|
s.addCh <- c
|
|
|
|
}
|
|
|
|
|
2020-11-13 00:14:59 +01:00
|
|
|
// Remove removes a client from the server.
|
2020-06-23 22:11:01 +02:00
|
|
|
func (s *server) remove(c *Client) {
|
2020-06-23 03:11:56 +02:00
|
|
|
s.delCh <- c
|
|
|
|
}
|
|
|
|
|
2020-11-13 00:14:59 +01:00
|
|
|
// SendToAll sends a message to all of the connected clients.
|
2020-12-29 22:35:33 +01:00
|
|
|
func (s *server) SendToAll(msg models.ChatEvent) {
|
2020-06-23 03:11:56 +02:00
|
|
|
s.sendAllCh <- msg
|
|
|
|
}
|
|
|
|
|
2020-11-13 00:14:59 +01:00
|
|
|
// Err handles an error.
|
2020-06-23 22:11:01 +02:00
|
|
|
func (s *server) err(err error) {
|
2020-06-23 03:11:56 +02:00
|
|
|
s.errCh <- err
|
|
|
|
}
|
|
|
|
|
2020-12-29 22:35:33 +01:00
|
|
|
func (s *server) sendAll(msg models.ChatEvent) {
|
2020-06-23 03:11:56 +02:00
|
|
|
for _, c := range s.Clients {
|
2021-01-02 00:39:41 +01:00
|
|
|
c.write(msg)
|
2020-06-23 03:11:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-23 22:11:01 +02:00
|
|
|
func (s *server) ping() {
|
2020-07-29 06:30:03 +02:00
|
|
|
ping := models.PingMessage{MessageType: PING}
|
2020-06-23 03:11:56 +02:00
|
|
|
for _, c := range s.Clients {
|
|
|
|
c.pingch <- ping
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 06:30:03 +02:00
|
|
|
func (s *server) usernameChanged(msg models.NameChangeEvent) {
|
|
|
|
for _, c := range s.Clients {
|
|
|
|
c.usernameChangeChannel <- msg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-23 22:11:01 +02:00
|
|
|
func (s *server) onConnection(ws *websocket.Conn) {
|
|
|
|
client := NewClient(ws)
|
2020-06-23 08:52:50 +02:00
|
|
|
|
|
|
|
defer func() {
|
2020-12-22 04:42:47 +01:00
|
|
|
s.removeClient(client)
|
2020-06-23 08:52:50 +02:00
|
|
|
|
|
|
|
if err := ws.Close(); err != nil {
|
|
|
|
s.errCh <- err
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-06-23 22:11:01 +02:00
|
|
|
s.add(client)
|
2021-01-02 00:39:41 +01:00
|
|
|
client.listen()
|
2020-06-23 08:52:50 +02:00
|
|
|
}
|
|
|
|
|
2020-06-23 03:11:56 +02:00
|
|
|
// Listen and serve.
|
|
|
|
// It serves client connection and broadcast request.
|
2020-06-23 22:11:01 +02:00
|
|
|
func (s *server) Listen() {
|
2020-06-23 08:52:50 +02:00
|
|
|
http.Handle(s.pattern, websocket.Handler(s.onConnection))
|
2020-06-23 03:11:56 +02:00
|
|
|
|
2020-07-07 06:27:31 +02:00
|
|
|
log.Tracef("Starting the websocket listener on: %s", s.pattern)
|
2020-06-23 03:11:56 +02:00
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// add new a client
|
|
|
|
case c := <-s.addCh:
|
2020-12-22 04:42:47 +01:00
|
|
|
l.Lock()
|
2020-07-29 06:30:03 +02:00
|
|
|
s.Clients[c.socketID] = c
|
2020-12-22 04:42:47 +01:00
|
|
|
l.Unlock()
|
|
|
|
|
2020-10-07 08:14:33 +02:00
|
|
|
s.listener.ClientAdded(c.GetViewerClientFromChatClient())
|
2020-07-20 00:14:51 +02:00
|
|
|
s.sendWelcomeMessageToClient(c)
|
2020-06-23 03:11:56 +02:00
|
|
|
|
|
|
|
// remove a client
|
|
|
|
case c := <-s.delCh:
|
2020-12-22 04:42:47 +01:00
|
|
|
s.removeClient(c)
|
|
|
|
case msg := <-s.sendAllCh:
|
2020-11-12 23:57:24 +01:00
|
|
|
// message was received from a client and should be sanitized, validated
|
2020-10-14 01:45:52 +02:00
|
|
|
// and distributed to other clients.
|
2020-12-22 04:42:47 +01:00
|
|
|
//
|
2020-10-14 01:45:52 +02:00
|
|
|
// Will turn markdown into html, sanitize user-supplied raw html
|
|
|
|
// and standardize this message into something safe we can send everyone else.
|
|
|
|
msg.RenderAndSanitizeMessageBody()
|
|
|
|
|
2020-06-23 22:11:01 +02:00
|
|
|
s.listener.MessageSent(msg)
|
2020-06-23 03:11:56 +02:00
|
|
|
s.sendAll(msg)
|
2020-10-14 01:45:52 +02:00
|
|
|
|
|
|
|
// Store in the message history
|
2020-07-12 23:53:33 +02:00
|
|
|
addMessage(msg)
|
2020-06-23 03:11:56 +02:00
|
|
|
case ping := <-s.pingCh:
|
|
|
|
fmt.Println("PING?", ping)
|
|
|
|
|
|
|
|
case err := <-s.errCh:
|
2020-07-07 06:27:31 +02:00
|
|
|
log.Error("Error:", err.Error())
|
2020-06-23 03:11:56 +02:00
|
|
|
|
|
|
|
case <-s.doneCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-20 00:14:51 +02:00
|
|
|
|
2020-12-22 04:42:47 +01:00
|
|
|
func (s *server) removeClient(c *Client) {
|
|
|
|
l.Lock()
|
|
|
|
|
|
|
|
if _, ok := s.Clients[c.socketID]; ok {
|
|
|
|
delete(s.Clients, c.socketID)
|
|
|
|
|
|
|
|
s.listener.ClientRemoved(c.socketID)
|
|
|
|
|
|
|
|
log.Tracef("The client was connected for %s and sent %d messages (%s)", time.Since(c.ConnectedAt), c.MessageCount, c.ClientID)
|
|
|
|
}
|
|
|
|
l.Unlock()
|
|
|
|
}
|
|
|
|
|
2020-07-20 00:14:51 +02:00
|
|
|
func (s *server) sendWelcomeMessageToClient(c *Client) {
|
|
|
|
go func() {
|
2020-07-20 09:22:32 +02:00
|
|
|
// Add an artificial delay so people notice this message come in.
|
|
|
|
time.Sleep(7 * time.Second)
|
2020-07-20 00:14:51 +02:00
|
|
|
|
|
|
|
initialChatMessageText := fmt.Sprintf("Welcome to %s! %s", config.Config.InstanceDetails.Title, config.Config.InstanceDetails.Summary)
|
2020-12-29 22:35:33 +01:00
|
|
|
initialMessage := models.ChatEvent{ClientID: "owncast-server", Author: config.Config.InstanceDetails.Name, Body: initialChatMessageText, ID: "initial-message-1", MessageType: "SYSTEM", Visible: true, Timestamp: time.Now()}
|
2021-01-02 00:39:41 +01:00
|
|
|
c.write(initialMessage)
|
2020-07-20 00:14:51 +02:00
|
|
|
}()
|
2020-10-07 08:14:33 +02:00
|
|
|
}
|