123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- package chat
- import (
- "fmt"
- "net/http"
- "sort"
- "github.com/owncast/owncast/models"
- "github.com/owncast/owncast/storage/chatrepository"
- "github.com/owncast/owncast/storage/configrepository"
- "github.com/owncast/owncast/storage/userrepository"
- log "github.com/sirupsen/logrus"
- )
- type Chat struct {
- getStatus func() *models.Status
- server *Server
- configRepository *configrepository.SqlConfigRepository
- emojis *emojis
- }
- func New() *Chat {
- return &Chat{
- configRepository: configrepository.Get(),
- emojis: newEmojis(),
- }
- }
- var temporaryGlobalInstance *Chat
- // GetConfig returns the temporary global instance.
- // Remove this after dependency injection is implemented.
- func Get() *Chat {
- if temporaryGlobalInstance == nil {
- temporaryGlobalInstance = New()
- }
- return temporaryGlobalInstance
- }
- // Start begins the chat server.
- func (c *Chat) Start(getStatusFunc func() *models.Status) error {
- c.getStatus = getStatusFunc
- c.server = NewChat()
- go c.server.Run()
- log.Traceln("Chat server started with max connection count of", c.server.maxSocketConnectionLimit)
- return nil
- }
- // FindClientByID will return a single connected client by ID.
- func (c *Chat) FindClientByID(clientID uint) (*Client, bool) {
- client, found := c.server.clients[clientID]
- return client, found
- }
- // GetClients will return all the current chat clients connected.
- func (c *Chat) GetClients() []*Client {
- clients := []*Client{}
- if c.server == nil {
- return clients
- }
- // Convert the keyed map to a slice.
- for _, client := range c.server.clients {
- clients = append(clients, client)
- }
- sort.Slice(clients, func(i, j int) bool {
- return clients[i].ConnectedAt.Before(clients[j].ConnectedAt)
- })
- return clients
- }
- // SendSystemMessage will send a message string as a system message to all clients.
- func (c *Chat) SendSystemMessage(text string, ephemeral bool) error {
- message := SystemMessageEvent{
- MessageEvent: MessageEvent{
- Body: text,
- },
- }
- message.SetDefaults()
- message.RenderBody()
- message.DisplayName = c.configRepository.GetServerName()
- if err := c.Broadcast(&message); err != nil {
- log.Errorln("error sending system message", err)
- }
- if !ephemeral {
- cr := chatrepository.Get()
- cr.SaveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
- }
- return nil
- }
- // SendFediverseAction will send a message indicating some Fediverse engagement took place.
- func (c *Chat) SendFediverseAction(eventType string, userAccountName string, image *string, body string, link string) error {
- message := FediverseEngagementEvent{
- Event: models.Event{
- Type: eventType,
- },
- MessageEvent: MessageEvent{
- Body: body,
- },
- UserAccountName: userAccountName,
- Image: image,
- Link: link,
- }
- message.SetDefaults()
- message.RenderBody()
- if err := c.Broadcast(&message); err != nil {
- log.Errorln("error sending system message", err)
- return err
- }
- cr := chatrepository.Get()
- cr.SaveFederatedAction(message)
- return nil
- }
- // SendSystemAction will send a system action string as an action event to all clients.
- func (c *Chat) SendSystemAction(text string, ephemeral bool) error {
- message := ActionEvent{
- MessageEvent: MessageEvent{
- Body: text,
- },
- }
- message.SetDefaults()
- message.RenderBody()
- if err := c.Broadcast(&message); err != nil {
- log.Errorln("error sending system chat action")
- }
- if !ephemeral {
- cr := chatrepository.Get()
- cr.SaveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
- }
- return nil
- }
- // SendAllWelcomeMessage will send the chat message to all connected clients.
- func (c *Chat) SendAllWelcomeMessage() {
- c.server.sendAllWelcomeMessage()
- }
- // SendSystemMessageToClient will send a single message to a single connected chat client.
- func (c *Chat) SendSystemMessageToClient(clientID uint, text string) {
- if client, foundClient := c.FindClientByID(clientID); foundClient {
- c.server.sendSystemMessageToClient(client, text)
- }
- }
- // Broadcast will send all connected clients the outbound object provided.
- func (c *Chat) Broadcast(event OutboundEvent) error {
- return c.server.Broadcast(event.GetBroadcastPayload())
- }
- // HandleClientConnection handles a single inbound websocket connection.
- func (c *Chat) HandleClientConnection(w http.ResponseWriter, r *http.Request) {
- c.server.HandleClientConnection(w, r)
- }
- // DisconnectClients will forcefully disconnect all clients belonging to a user by ID.
- func (c *Chat) DisconnectClients(clients []*Client) {
- c.server.DisconnectClients(clients)
- }
- func (c *Chat) GetClientsForUser(userID string) ([]*Client, error) {
- return c.server.GetClientsForUser(userID)
- }
- // SendConnectedClientInfoToUser will find all the connected clients assigned to a user
- // and re-send each the connected client info.
- func (c *Chat) SendConnectedClientInfoToUser(userID string) error {
- clients, err := c.server.GetClientsForUser(userID)
- if err != nil {
- return err
- }
- userRepository := userrepository.Get()
- // Get an updated reference to the user.
- user := userRepository.GetUserByID(userID)
- if user == nil {
- return fmt.Errorf("user not found")
- }
- if err != nil {
- return err
- }
- for _, client := range clients {
- // Update the client's reference to its user.
- client.User = user
- // Send the update to the client.
- client.sendConnectedClientInfo()
- }
- return nil
- }
- // SendActionToUser will send system action text to all connected clients
- // assigned to a user ID.
- func (c *Chat) SendActionToUser(userID string, text string) error {
- clients, err := c.server.GetClientsForUser(userID)
- if err != nil {
- return err
- }
- for _, client := range clients {
- c.server.sendActionToClient(client, text)
- }
- return nil
- }
|