2020-06-23 23:11:01 +03:00
|
|
|
package chat
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2021-07-20 05:22:29 +03:00
|
|
|
"net/http"
|
|
|
|
"sort"
|
2020-06-23 23:11:01 +03:00
|
|
|
|
2022-03-07 04:26:52 +03:00
|
|
|
"github.com/owncast/owncast/config"
|
2021-07-20 05:22:29 +03:00
|
|
|
"github.com/owncast/owncast/core/chat/events"
|
2022-03-07 04:26:52 +03:00
|
|
|
"github.com/owncast/owncast/core/data"
|
2020-10-05 20:07:09 +03:00
|
|
|
"github.com/owncast/owncast/models"
|
2022-03-07 04:26:52 +03:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
2021-07-20 05:22:29 +03:00
|
|
|
log "github.com/sirupsen/logrus"
|
2020-06-23 23:11:01 +03:00
|
|
|
)
|
|
|
|
|
2022-03-07 04:26:52 +03:00
|
|
|
var (
|
|
|
|
getStatus func() models.Status
|
|
|
|
chatMessagesSentCounter prometheus.Gauge
|
|
|
|
)
|
2021-07-20 05:22:29 +03:00
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// Start begins the chat server.
|
2021-07-20 05:22:29 +03:00
|
|
|
func Start(getStatusFunc func() models.Status) error {
|
2020-07-16 03:20:47 +03:00
|
|
|
setupPersistence()
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
getStatus = getStatusFunc
|
|
|
|
_server = NewChat()
|
|
|
|
|
|
|
|
go _server.Run()
|
|
|
|
|
2021-08-14 01:26:44 +03:00
|
|
|
log.Traceln("Chat server started with max connection count of", _server.maxSocketConnectionLimit)
|
2021-07-20 05:22:29 +03:00
|
|
|
|
2022-03-07 04:26:52 +03:00
|
|
|
chatMessagesSentCounter = promauto.NewGauge(prometheus.GaugeOpts{
|
|
|
|
Name: "total_chat_message_count",
|
|
|
|
Help: "The number of chat messages incremented over time.",
|
|
|
|
ConstLabels: map[string]string{
|
|
|
|
"version": config.VersionNumber,
|
|
|
|
"host": data.GetServerURL(),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
return nil
|
2020-06-23 23:11:01 +03:00
|
|
|
}
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
// GetClientsForUser will return chat connections that are owned by a specific user.
|
2021-09-12 10:18:15 +03:00
|
|
|
func GetClientsForUser(userID string) ([]*Client, error) {
|
2022-03-07 07:34:49 +03:00
|
|
|
_server.mu.Lock()
|
|
|
|
defer _server.mu.Unlock()
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
clients := map[string][]*Client{}
|
2020-06-23 23:11:01 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
for _, client := range _server.clients {
|
2021-09-12 10:18:15 +03:00
|
|
|
clients[client.User.ID] = append(clients[client.User.ID], client)
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|
2020-06-23 23:11:01 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if _, exists := clients[userID]; !exists {
|
|
|
|
return nil, errors.New("no connections for user found")
|
|
|
|
}
|
2020-06-23 23:11:01 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
return clients[userID], nil
|
2020-06-23 23:11:01 +03:00
|
|
|
}
|
|
|
|
|
2021-09-13 11:26:28 +03:00
|
|
|
// FindClientByID will return a single connected client by ID.
|
|
|
|
func FindClientByID(clientID uint) (*Client, bool) {
|
|
|
|
client, found := _server.clients[clientID]
|
|
|
|
return client, found
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// GetClients will return all the current chat clients connected.
|
|
|
|
func GetClients() []*Client {
|
|
|
|
clients := []*Client{}
|
2021-07-20 05:22:29 +03:00
|
|
|
|
2022-03-07 04:26:52 +03:00
|
|
|
if _server == nil {
|
|
|
|
return clients
|
|
|
|
}
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
// Convert the keyed map to a slice.
|
|
|
|
for _, client := range _server.clients {
|
|
|
|
clients = append(clients, client)
|
2020-06-23 23:11:01 +03:00
|
|
|
}
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
sort.Slice(clients, func(i, j int) bool {
|
|
|
|
return clients[i].ConnectedAt.Before(clients[j].ConnectedAt)
|
|
|
|
})
|
|
|
|
|
|
|
|
return clients
|
2020-06-23 23:11:01 +03:00
|
|
|
}
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// SendSystemMessage will send a message string as a system message to all clients.
|
2021-07-20 05:22:29 +03:00
|
|
|
func SendSystemMessage(text string, ephemeral bool) error {
|
|
|
|
message := events.SystemMessageEvent{
|
|
|
|
MessageEvent: events.MessageEvent{
|
|
|
|
Body: text,
|
|
|
|
},
|
2020-06-23 23:11:01 +03:00
|
|
|
}
|
2021-07-20 05:22:29 +03:00
|
|
|
message.SetDefaults()
|
|
|
|
message.RenderBody()
|
2020-06-23 23:11:01 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if err := Broadcast(&message); err != nil {
|
|
|
|
log.Errorln("error sending system message", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ephemeral {
|
2022-01-13 00:53:10 +03:00
|
|
|
saveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|
2021-02-19 10:05:52 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
return nil
|
2020-06-23 23:11:01 +03:00
|
|
|
}
|
2020-10-07 09:14:33 +03:00
|
|
|
|
2022-01-13 00:53:10 +03:00
|
|
|
// SendFediverseAction will send a message indicating some Fediverse engagement took place.
|
|
|
|
func SendFediverseAction(eventType string, userAccountName string, image *string, body string, link string) error {
|
|
|
|
message := events.FediverseEngagementEvent{
|
|
|
|
Event: events.Event{
|
|
|
|
Type: eventType,
|
|
|
|
},
|
|
|
|
MessageEvent: events.MessageEvent{
|
|
|
|
Body: body,
|
|
|
|
},
|
|
|
|
UserAccountName: userAccountName,
|
|
|
|
Image: image,
|
|
|
|
Link: link,
|
|
|
|
}
|
|
|
|
|
|
|
|
message.SetDefaults()
|
|
|
|
message.RenderBody()
|
|
|
|
|
|
|
|
if err := Broadcast(&message); err != nil {
|
|
|
|
log.Errorln("error sending system message", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
saveFederatedAction(message)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// SendSystemAction will send a system action string as an action event to all clients.
|
2021-07-20 05:22:29 +03:00
|
|
|
func SendSystemAction(text string, ephemeral bool) error {
|
|
|
|
message := events.ActionEvent{
|
|
|
|
MessageEvent: events.MessageEvent{
|
|
|
|
Body: text,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
message.SetDefaults()
|
|
|
|
message.RenderBody()
|
2021-03-04 07:44:13 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if err := Broadcast(&message); err != nil {
|
|
|
|
log.Errorln("error sending system chat action")
|
2020-10-07 09:14:33 +03:00
|
|
|
}
|
2021-07-20 05:22:29 +03:00
|
|
|
|
|
|
|
if !ephemeral {
|
2022-01-13 00:53:10 +03:00
|
|
|
saveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|
|
|
|
|
2020-10-07 09:14:33 +03:00
|
|
|
return nil
|
|
|
|
}
|
2021-07-20 05:22:29 +03:00
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// SendAllWelcomeMessage will send the chat message to all connected clients.
|
2021-07-20 05:22:29 +03:00
|
|
|
func SendAllWelcomeMessage() {
|
|
|
|
_server.sendAllWelcomeMessage()
|
|
|
|
}
|
|
|
|
|
2021-09-13 11:26:28 +03:00
|
|
|
// SendSystemMessageToClient will send a single message to a single connected chat client.
|
|
|
|
func SendSystemMessageToClient(clientID uint, text string) {
|
|
|
|
if client, foundClient := FindClientByID(clientID); foundClient {
|
|
|
|
_server.sendSystemMessageToClient(client, text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// Broadcast will send all connected clients the outbound object provided.
|
2021-07-20 05:22:29 +03:00
|
|
|
func Broadcast(event events.OutboundEvent) error {
|
|
|
|
return _server.Broadcast(event.GetBroadcastPayload())
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// HandleClientConnection handles a single inbound websocket connection.
|
2021-07-20 05:22:29 +03:00
|
|
|
func HandleClientConnection(w http.ResponseWriter, r *http.Request) {
|
|
|
|
_server.HandleClientConnection(w, r)
|
|
|
|
}
|
|
|
|
|
2022-03-07 07:34:49 +03:00
|
|
|
// DisconnectClients will forcefully disconnect all clients belonging to a user by ID.
|
|
|
|
func DisconnectClients(clients []*Client) {
|
|
|
|
_server.DisconnectClients(clients)
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|