2023-02-11 06:46:39 +00:00
package barertc
2023-03-14 04:26:37 +00:00
import (
2023-07-01 01:41:06 +00:00
"fmt"
"os"
2023-03-14 04:26:37 +00:00
"strconv"
"time"
2023-06-10 19:27:27 +00:00
2023-07-01 01:41:06 +00:00
"git.kirsle.net/apps/barertc/pkg/config"
2023-06-10 19:27:27 +00:00
"git.kirsle.net/apps/barertc/pkg/log"
"github.com/mattn/go-shellwords"
2023-03-14 04:26:37 +00:00
)
2023-02-11 06:46:39 +00:00
// ProcessCommand parses a chat message for "/commands"
func ( s * Server ) ProcessCommand ( sub * Subscriber , msg Message ) bool {
if len ( msg . Message ) == 0 || msg . Message [ 0 ] != '/' {
return false
}
// Line begins with a slash, parse it apart.
2023-06-10 19:27:27 +00:00
words , err := shellwords . Parse ( msg . Message )
if err != nil {
log . Error ( "ProcessCommands: parsing shell words: %s" , err )
return false
} else if len ( words ) == 0 {
2023-02-11 06:46:39 +00:00
return false
}
// Moderator commands.
if sub . JWTClaims != nil && sub . JWTClaims . IsAdmin {
switch words [ 0 ] {
case "/kick" :
2023-03-14 04:26:37 +00:00
s . KickCommand ( words , sub )
return true
case "/ban" :
s . BanCommand ( words , sub )
2023-02-11 06:46:39 +00:00
return true
case "/nsfw" :
if len ( words ) == 1 {
sub . ChatServer ( "Usage: `/nsfw username` to add the NSFW flag to their camera." )
}
username := words [ 1 ]
other , err := s . GetSubscriber ( username )
if err != nil {
sub . ChatServer ( "/nsfw: username not found: %s" , username )
} else {
other . ChatServer ( "Your camera has been marked as NSFW by %s" , sub . Username )
2023-07-01 01:41:06 +00:00
other . VideoStatus |= VideoFlagNSFW
2023-02-11 06:46:39 +00:00
other . SendMe ( )
s . SendWhoList ( )
sub . ChatServer ( "%s has their camera marked as NSFW" , username )
}
return true
case "/help" :
sub . ChatServer ( RenderMarkdown ( "Moderator commands are:\n\n" +
"* `/kick <username>` to kick from chat\n" +
"* `/nsfw <username>` to mark their camera NSFW\n" +
2023-07-01 01:41:06 +00:00
"* `/shutdown` to gracefully shut down (reboot) the chat server\n" +
"* `/kickall` to kick EVERYBODY off and force them to log back in\n" +
2023-06-10 19:27:27 +00:00
"* `/help` to show this message\n\n" +
"Note: shell-style quoting is supported, if a username has a space in it, quote the whole username, e.g.: `/kick \"username 2\"`" ,
2023-02-11 06:46:39 +00:00
) )
return true
2023-07-01 01:41:06 +00:00
case "/shutdown" :
s . Broadcast ( Message {
Action : ActionError ,
Username : "ChatServer" ,
Message : "The chat server is going down for a reboot NOW!" ,
} )
os . Exit ( 1 )
case "/kickall" :
s . KickAllCommand ( )
2023-02-11 06:46:39 +00:00
}
2023-07-01 01:41:06 +00:00
2023-02-11 06:46:39 +00:00
}
// Not handled.
return false
}
2023-03-14 04:26:37 +00:00
// KickCommand handles the `/kick` operator command.
func ( s * Server ) KickCommand ( words [ ] string , sub * Subscriber ) {
if len ( words ) == 1 {
2023-06-10 19:27:27 +00:00
sub . ChatServer ( RenderMarkdown (
"Usage: `/kick username` to remove the user from the chat room.\n\nNote: if the username has spaces in it, quote the name (shell style), `/kick \"username 2\"`" ,
) )
return
2023-03-14 04:26:37 +00:00
}
username := words [ 1 ]
other , err := s . GetSubscriber ( username )
if err != nil {
sub . ChatServer ( "/kick: username not found: %s" , username )
2023-06-10 19:27:27 +00:00
} else if other . Username == sub . Username {
sub . ChatServer ( "/kick: did you really mean to kick yourself?" )
2023-03-14 04:26:37 +00:00
} else {
other . ChatServer ( "You have been kicked from the chat room by %s" , sub . Username )
other . SendJSON ( Message {
Action : ActionKick ,
} )
s . DeleteSubscriber ( other )
sub . ChatServer ( "%s has been kicked from the room" , username )
}
}
2023-07-01 01:41:06 +00:00
// KickAllCommand kicks everybody out of the room.
func ( s * Server ) KickAllCommand ( ) {
// If we have JWT enabled and a landing page, link users to it.
if config . Current . JWT . Enabled && config . Current . JWT . LandingPageURL != "" {
s . Broadcast ( Message {
Action : ActionError ,
Username : "ChatServer" ,
Message : fmt . Sprintf (
"<strong>Notice:</strong> The chat operator has requested that you log back in to the chat room. " +
"Probably, this is because a new feature was launched that needs you to reload the page. " +
"You may refresh the tab or <a href=\"%s\">click here</a> to re-enter the room." ,
config . Current . JWT . LandingPageURL ,
) ,
} )
} else {
s . Broadcast ( Message {
Action : ActionError ,
Username : "ChatServer" ,
Message : "<strong>Notice:</strong> The chat operator has kicked everybody from the room. Usually, this " +
"may mean a new feature of the chat has been launched and you need to reload the page for it " +
"to function correctly." ,
} )
}
// Kick everyone off.
s . Broadcast ( Message {
Action : ActionKick ,
} )
// Disconnect everybody.
s . subscribersMu . RLock ( )
defer s . subscribersMu . RUnlock ( )
for _ , sub := range s . IterSubscribers ( true ) {
if ! sub . authenticated {
continue
}
s . DeleteSubscriber ( sub )
}
}
2023-03-14 04:26:37 +00:00
// BanCommand handles the `/ban` operator command.
func ( s * Server ) BanCommand ( words [ ] string , sub * Subscriber ) {
if len ( words ) == 1 {
2023-06-10 19:27:27 +00:00
sub . ChatServer ( RenderMarkdown (
2023-03-14 04:26:37 +00:00
"Usage: `/ban username` to remove the user from the chat room for 24 hours (default).\n\n" +
"Set another duration (in hours, fractions supported) like: `/ban username 0.5` for a 30-minute ban." ,
2023-06-10 19:27:27 +00:00
) )
return
2023-03-14 04:26:37 +00:00
}
// Parse the command.
var (
username = words [ 1 ]
duration = 24 * time . Hour
)
if len ( words ) >= 3 {
if dur , err := strconv . ParseFloat ( words [ 2 ] , 64 ) ; err == nil {
if dur < 1 {
duration = time . Duration ( dur * 60 ) * time . Second
} else {
duration = time . Duration ( dur ) * time . Hour
}
}
}
// TODO: banning, for now it just kicks.
_ = duration
other , err := s . GetSubscriber ( username )
if err != nil {
sub . ChatServer ( "/ban: username not found: %s" , username )
} else {
other . ChatServer ( "You have been kicked from the chat room by %s" , sub . Username )
other . SendJSON ( Message {
Action : ActionKick ,
} )
s . DeleteSubscriber ( other )
sub . ChatServer ( "%s has been kicked from the room" , username )
}
}