diff --git a/pkg/commands.go b/pkg/commands.go index d5ccd3c..485e6a5 100644 --- a/pkg/commands.go +++ b/pkg/commands.go @@ -62,7 +62,6 @@ func (s *Server) ProcessCommand(sub *Subscriber, msg messages.Message) bool { "* `/bans` to list current banned users and their expiration date\n" + "* `/nsfw ` to mark their camera NSFW\n" + "* `/cut ` to make them turn off their camera\n" + - "* `/unmute-all` to lift all mutes on your side\n" + "* `/help` to show this message\n" + "* `/help-advanced` to show advanced admin commands\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\"`", @@ -189,7 +188,9 @@ func (s *Server) CutCommand(words []string, sub *Subscriber) { func (s *Server) UnmuteAllCommand(words []string, sub *Subscriber) { count := len(sub.muted) sub.muted = map[string]struct{}{} + sub.unblockable = true sub.ChatServer("Your mute on %d users has been lifted.", count) + s.SendWhoList() } // KickCommand handles the `/kick` operator command. diff --git a/pkg/config/config.go b/pkg/config/config.go index 44359ab..aa2e328 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -13,7 +13,7 @@ import ( // Version of the config format - when new fields are added, it will attempt // to write the settings.toml to disk so new defaults populate. -var currentVersion = 13 +var currentVersion = 14 // Config for your BareRTC app. type Config struct { @@ -30,9 +30,10 @@ type Config struct { Branding string WebsiteURL string - CORSHosts []string - AdminAPIKey string - PermitNSFW bool + CORSHosts []string + AdminAPIKey string + PermitNSFW bool + BlockableAdmins bool UseXForwardedFor bool diff --git a/pkg/handlers.go b/pkg/handlers.go index 4245eca..c1c5fb1 100644 --- a/pkg/handlers.go +++ b/pkg/handlers.go @@ -126,7 +126,7 @@ func (s *Server) OnMessage(sub *Subscriber, msg messages.Message) { log.Info("[%s to #%s] %s", sub.Username, msg.Channel, msg.Message) } - if sub.Username == "" { + if sub.Username == "" || !sub.authenticated { sub.ChatServer("You must log in first.") return } @@ -180,11 +180,8 @@ func (s *Server) OnMessage(sub *Subscriber, msg messages.Message) { // If the user is OP, just tell them we would. if sub.IsAdmin() { sub.ChatServer("Your recent chat context would have been reported to your main website.") - return - } - - // Send the report to the main website. - if err := s.reportFilteredMessage(sub, msg); err != nil { + } else if err := s.reportFilteredMessage(sub, msg); err != nil { + // Send the report to the main website. log.Error("Reporting filtered message: %s", err) } } @@ -204,17 +201,17 @@ func (s *Server) OnMessage(sub *Subscriber, msg messages.Message) { // Don't deliver it if the receiver has muted us. Note: admin users, even if muted, // can still deliver a DM to the one who muted them. rcpt, err := s.GetSubscriber(strings.TrimPrefix(msg.Channel, "@")) - if err == nil && rcpt.Mutes(sub.Username) && !sub.IsAdmin() { - log.Debug("Do not send message to %s: they have muted or booted %s", rcpt.Username, sub.Username) - return - } else if err != nil { + if err != nil { // Recipient was no longer online: the message won't be sent. sub.ChatServer("Could not deliver your message: %s appears not to be online.", msg.Channel) return + } else if rcpt.Mutes(sub.Username) && !sub.IsAdmin() { + log.Debug("Do not send message to %s: they have muted or booted %s", rcpt.Username, sub.Username) + return } // If the sender already mutes the recipient, reply back with the error. - if err == nil && sub.Mutes(rcpt.Username) && !sub.IsAdmin() { + if sub.Mutes(rcpt.Username) && !sub.IsAdmin() { sub.ChatServer("You have muted %s and so your message has not been sent.", rcpt.Username) return } diff --git a/pkg/websocket.go b/pkg/websocket.go index 9c97d3b..e8ec33d 100644 --- a/pkg/websocket.go +++ b/pkg/websocket.go @@ -50,6 +50,12 @@ type Subscriber struct { blocked map[string]struct{} // usernames you have blocked muted map[string]struct{} // usernames you muted + // Admin "unblockable" override command, e.g. especially for your chatbot so it can + // still moderate the chat even if users had blocked it. The /unmute-all admin command + // will toggle this setting: then the admin chatbot will appear in the Who's Online list + // as normal and it can see user messages in chat. + unblockable bool + // Record which message IDs belong to this user. midMu sync.Mutex messageIDs map[int64]struct{} @@ -535,7 +541,7 @@ func (s *Server) SendWhoList() { // If this person's VideoFlag is set to VIP Only, force their camera to "off" // except when the person looking has the VIP status. - if (user.VideoStatus&messages.VideoFlagOnlyVIP == messages.VideoFlagOnlyVIP) && (!sub.IsVIP() && !sub.IsAdmin()) { + if (user.VideoStatus&messages.VideoFlagOnlyVIP == messages.VideoFlagOnlyVIP) && !sub.IsVIP() { who.Video = 0 } } @@ -590,9 +596,21 @@ func (s *Subscriber) Blocks(other *Subscriber) bool { return false } - // If either side is an admin, blocking is not allowed. - if s.IsAdmin() || other.IsAdmin() { + // Admin blocking behavior: by default, admins are NOT blockable by users and retain visibility on + // chat, especially to moderate webcams (messages may still be muted between blocked users). + // + // If your chat server allows admins to be blockable: + if !config.Current.BlockableAdmins && (s.IsAdmin() || other.IsAdmin()) { return false + } else { + // Admins are blockable, unless they have the unblockable flag - e.g. if you have an admin chatbot on + // your server it will send the `/unmute-all` command to still retain visibility into user messages for + // auto-moderation. The `/unmute-all` sets the unblockable flag, so your admin chatbot still appears + // on the Who's Online list as well. + unblockable := (s.IsAdmin() && s.unblockable) || (other.IsAdmin() && other.unblockable) + if unblockable { + return false + } } s.muteMu.RLock() diff --git a/src/App.vue b/src/App.vue index ffc3543..65d4b25 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1332,11 +1332,7 @@ export default { if (!window.confirm( `Do you want to remove your mute on ${username}? If you un-mute them, you ` + `will be able to see their chat messages or DMs going forward, but most importantly, ` + - `they may be able to watch your webcam now if you are broadcasting!\n\n` + - `Note: currently you can only re-mute them the next time you see one of their ` + - `chat messages, or you can only boot them off your cam after they have already ` + - `opened it. If you are concerned about this, click Cancel and do not remove ` + - `the mute on ${username}.` + `they may be able to watch your webcam now if you are broadcasting!`, )) { return; }