123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- package chat
- import (
- "encoding/json"
- "fmt"
- "strings"
- "time"
- "github.com/owncast/owncast/config"
- "github.com/owncast/owncast/core/chat/events"
- "github.com/owncast/owncast/core/data"
- "github.com/owncast/owncast/core/webhooks"
- "github.com/owncast/owncast/persistence/userrepository"
- "github.com/owncast/owncast/utils"
- log "github.com/sirupsen/logrus"
- )
- func (s *Server) userNameChanged(eventData chatClientEvent) {
- var receivedEvent events.NameChangeEvent
- if err := json.Unmarshal(eventData.data, &receivedEvent); err != nil {
- log.Errorln("error unmarshalling to NameChangeEvent", err)
- return
- }
- proposedUsername := receivedEvent.NewName
- // Check if name is on the blocklist
- blocklist := data.GetForbiddenUsernameList()
- // Names have a max length
- proposedUsername = utils.MakeSafeStringOfLength(proposedUsername, config.MaxChatDisplayNameLength)
- for _, blockedName := range blocklist {
- normalizedName := strings.TrimSpace(blockedName)
- normalizedName = strings.ToLower(normalizedName)
- if strings.Contains(normalizedName, proposedUsername) {
- // Denied.
- log.Debugln(logSanitize(eventData.client.User.DisplayName), "blocked from changing name to", logSanitize(proposedUsername), "due to blocked name", normalizedName)
- message := fmt.Sprintf("You cannot change your name to **%s**.", proposedUsername)
- s.sendActionToClient(eventData.client, message)
- // Resend the client's user so their username is in sync.
- eventData.client.sendConnectedClientInfo()
- return
- }
- }
- userRepository := userrepository.Get()
- // Check if the name is not already assigned to a registered user.
- if available, err := userRepository.IsDisplayNameAvailable(proposedUsername); err != nil {
- log.Errorln("error checking if name is available", err)
- return
- } else if !available {
- message := fmt.Sprintf("The name **%s** has already been registered. If this is your name, please authenticate.", proposedUsername)
- s.sendActionToClient(eventData.client, message)
- // Resend the client's user so their username is in sync.
- eventData.client.sendConnectedClientInfo()
- return
- }
- savedUser := userRepository.GetUserByToken(eventData.client.accessToken)
- oldName := savedUser.DisplayName
- // Check that the new name is different from old.
- if proposedUsername == oldName {
- eventData.client.sendConnectedClientInfo()
- return
- }
- // Save the new name
- if err := userRepository.ChangeUsername(eventData.client.User.ID, proposedUsername); err != nil {
- log.Errorln("error changing username", err)
- }
- // Update the connected clients associated user with the new name
- now := time.Now()
- eventData.client.User = savedUser
- eventData.client.User.NameChangedAt = &now
- // Send chat event letting everyone about about the name change
- savedUser.DisplayName = proposedUsername
- broadcastEvent := events.NameChangeBroadcast{
- Oldname: oldName,
- }
- broadcastEvent.User = savedUser
- broadcastEvent.SetDefaults()
- payload := broadcastEvent.GetBroadcastPayload()
- if err := s.Broadcast(payload); err != nil {
- log.Errorln("error broadcasting NameChangeEvent", err)
- return
- }
- // Send chat user name changed webhook
- receivedEvent.User = savedUser
- receivedEvent.ClientID = eventData.client.Id
- webhooks.SendChatEventUsernameChanged(receivedEvent)
- // Resend the client's user so their username is in sync.
- eventData.client.sendConnectedClientInfo()
- }
- func (s *Server) userColorChanged(eventData chatClientEvent) {
- userRepository := userrepository.Get()
- var receivedEvent events.ColorChangeEvent
- if err := json.Unmarshal(eventData.data, &receivedEvent); err != nil {
- log.Errorln("error unmarshalling to ColorChangeEvent", err)
- return
- }
- // Verify this color is valid
- if receivedEvent.NewColor > config.MaxUserColor {
- log.Errorln("invalid color requested when changing user display color")
- return
- }
- // Save the new color
- if err := userRepository.ChangeUserColor(eventData.client.User.ID, receivedEvent.NewColor); err != nil {
- log.Errorln("error changing user display color", err)
- }
- // Resend client's user info with new color, otherwise the name change dialog would still show the old color
- eventData.client.User.DisplayColor = receivedEvent.NewColor
- eventData.client.sendConnectedClientInfo()
- }
- func (s *Server) userMessageSent(eventData chatClientEvent) {
- userRepository := userrepository.Get()
- var event events.UserMessageEvent
- if err := json.Unmarshal(eventData.data, &event); err != nil {
- log.Errorln("error unmarshalling to UserMessageEvent", err)
- return
- }
- event.SetDefaults()
- event.ClientID = eventData.client.Id
- // Ignore empty messages
- if event.Empty() {
- return
- }
- // Ignore if the stream has been offline
- if !getStatus().Online && getStatus().LastDisconnectTime != nil {
- disconnectedTime := getStatus().LastDisconnectTime.Time
- if time.Since(disconnectedTime) > 5*time.Minute {
- return
- }
- }
- event.User = userRepository.GetUserByToken(eventData.client.accessToken)
- // Guard against nil users
- if event.User == nil {
- return
- }
- payload := event.GetBroadcastPayload()
- if err := s.Broadcast(payload); err != nil {
- log.Errorln("error broadcasting UserMessageEvent payload", err)
- return
- }
- // Send chat message sent webhook
- webhooks.SendChatEvent(&event)
- chatMessagesSentCounter.Inc()
- SaveUserMessage(event)
- eventData.client.MessageCount++
- }
- func logSanitize(userValue string) string {
- // strip carriage return and newline from user-submitted values to prevent log injection
- sanitizedValue := strings.ReplaceAll(userValue, "\n", "")
- sanitizedValue = strings.ReplaceAll(sanitizedValue, "\r", "")
- return fmt.Sprintf("userSuppliedValue(%s)", sanitizedValue)
- }
|