2021-07-20 05:22:29 +03:00
package chat
import (
"encoding/json"
"fmt"
"strings"
2021-08-13 08:40:10 +03:00
"time"
2021-07-20 05:22:29 +03:00
2022-08-10 05:56:45 +03:00
"github.com/owncast/owncast/config"
2021-07-20 05:22:29 +03:00
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks"
2024-07-02 04:58:50 +03:00
"github.com/owncast/owncast/persistence/userrepository"
2022-12-29 08:30:06 +03:00
"github.com/owncast/owncast/utils"
2021-07-20 05:22:29 +03:00
log "github.com/sirupsen/logrus"
)
2021-09-12 10:18:15 +03:00
func ( s * Server ) userNameChanged ( eventData chatClientEvent ) {
2021-07-20 05:22:29 +03:00
var receivedEvent events . NameChangeEvent
if err := json . Unmarshal ( eventData . data , & receivedEvent ) ; err != nil {
log . Errorln ( "error unmarshalling to NameChangeEvent" , err )
return
}
proposedUsername := receivedEvent . NewName
2022-04-22 00:55:26 +03:00
// Check if name is on the blocklist
2021-07-20 05:22:29 +03:00
blocklist := data . GetForbiddenUsernameList ( )
2022-08-15 04:09:16 +03:00
// Names have a max length
2022-12-29 08:30:06 +03:00
proposedUsername = utils . MakeSafeStringOfLength ( proposedUsername , config . MaxChatDisplayNameLength )
2022-08-15 04:09:16 +03:00
2021-07-20 05:22:29 +03:00
for _ , blockedName := range blocklist {
normalizedName := strings . TrimSpace ( blockedName )
normalizedName = strings . ToLower ( normalizedName )
if strings . Contains ( normalizedName , proposedUsername ) {
// Denied.
2022-09-21 20:03:16 +03:00
log . Debugln ( logSanitize ( eventData . client . User . DisplayName ) , "blocked from changing name to" , logSanitize ( proposedUsername ) , "due to blocked name" , normalizedName )
2021-07-20 05:22:29 +03:00
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
}
}
2024-07-02 04:58:50 +03:00
userRepository := userrepository . Get ( )
2022-04-22 00:55:26 +03:00
// Check if the name is not already assigned to a registered user.
2024-07-02 04:58:50 +03:00
if available , err := userRepository . IsDisplayNameAvailable ( proposedUsername ) ; err != nil {
2022-04-22 00:55:26 +03:00
log . Errorln ( "error checking if name is available" , err )
return
} else if ! available {
2022-05-16 23:05:00 +03:00
message := fmt . Sprintf ( "The name **%s** has been already registered. If this is your name, please authenticate." , proposedUsername )
2022-04-22 00:55:26 +03:00
s . sendActionToClient ( eventData . client , message )
// Resend the client's user so their username is in sync.
eventData . client . sendConnectedClientInfo ( )
return
}
2024-07-02 04:58:50 +03:00
savedUser := userRepository . GetUserByToken ( eventData . client . accessToken )
2021-07-20 05:22:29 +03:00
oldName := savedUser . DisplayName
2023-07-11 09:16:36 +03:00
// Check that the new name is different from old.
if proposedUsername == oldName {
eventData . client . sendConnectedClientInfo ( )
return
}
2021-07-20 05:22:29 +03:00
// Save the new name
2024-07-02 04:58:50 +03:00
if err := userRepository . ChangeUsername ( eventData . client . User . ID , proposedUsername ) ; err != nil {
2022-04-22 00:55:26 +03:00
log . Errorln ( "error changing username" , err )
}
2021-07-20 05:22:29 +03:00
// Update the connected clients associated user with the new name
2021-08-13 10:05:13 +03:00
now := time . Now ( )
2021-07-20 05:22:29 +03:00
eventData . client . User = savedUser
2021-08-13 10:05:13 +03:00
eventData . client . User . NameChangedAt = & now
2021-07-20 05:22:29 +03:00
// Send chat event letting everyone about about the name change
2022-08-15 04:09:16 +03:00
savedUser . DisplayName = proposedUsername
2021-07-20 05:22:29 +03:00
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
2022-12-14 06:17:32 +03:00
receivedEvent . ClientID = eventData . client . Id
2021-07-20 05:22:29 +03:00
webhooks . SendChatEventUsernameChanged ( receivedEvent )
2022-05-26 23:52:04 +03:00
// Resend the client's user so their username is in sync.
eventData . client . sendConnectedClientInfo ( )
2021-07-20 05:22:29 +03:00
}
2022-08-10 05:56:45 +03:00
func ( s * Server ) userColorChanged ( eventData chatClientEvent ) {
2024-07-02 04:58:50 +03:00
userRepository := userrepository . Get ( )
2022-08-10 05:56:45 +03:00
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
2024-07-02 04:58:50 +03:00
if err := userRepository . ChangeUserColor ( eventData . client . User . ID , receivedEvent . NewColor ) ; err != nil {
2022-08-10 05:56:45 +03:00
log . Errorln ( "error changing user display color" , err )
}
2022-11-23 21:52:39 +03:00
// 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 ( )
2022-08-10 05:56:45 +03:00
}
2021-09-12 10:18:15 +03:00
func ( s * Server ) userMessageSent ( eventData chatClientEvent ) {
2024-07-02 04:58:50 +03:00
userRepository := userrepository . Get ( )
2021-07-20 05:22:29 +03:00
var event events . UserMessageEvent
if err := json . Unmarshal ( eventData . data , & event ) ; err != nil {
log . Errorln ( "error unmarshalling to UserMessageEvent" , err )
return
}
event . SetDefaults ( )
2022-12-14 06:17:32 +03:00
event . ClientID = eventData . client . Id
2021-07-20 05:22:29 +03:00
// Ignore empty messages
if event . Empty ( ) {
return
}
2021-08-13 08:40:10 +03:00
// 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
}
2021-07-27 20:42:05 +03:00
}
2024-07-02 04:58:50 +03:00
event . User = userRepository . GetUserByToken ( eventData . client . accessToken )
2021-07-20 05:22:29 +03:00
// 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 )
2022-03-07 04:26:52 +03:00
chatMessagesSentCounter . Inc ( )
2021-07-20 05:22:29 +03:00
SaveUserMessage ( event )
2021-10-25 10:31:45 +03:00
eventData . client . MessageCount ++
2021-07-20 05:22:29 +03:00
}
2022-09-21 20:03:16 +03:00
func logSanitize ( userValue string ) string {
// strip carriage return and newline from user-submitted values to prevent log injection
2022-09-21 20:24:16 +03:00
sanitizedValue := strings . ReplaceAll ( userValue , "\n" , "" )
sanitizedValue = strings . ReplaceAll ( sanitizedValue , "\r" , "" )
2022-09-21 20:03:16 +03:00
return fmt . Sprintf ( "userSuppliedValue(%s)" , sanitizedValue )
}