066765d2dc
* Add chat moderation rules to the website, so admins can apply selective rules to problematic users. Available rules are: * redcam: user's camera is always NSFW. * nobroadcast: user can not broadcast their camera. * novideo: user can not broadcast OR watch any video. * noimage: user can not share OR see any shared image on chat. * The page to manage a user's active rules is available on their admin card of their profile page. When the user has rules active, a yellow counter is shown by the link to manage their rules. * Only chat moderator admins have access to the page or can see the yellow counter to know whether rules are active. * "Shy Accounts" are now permitted on the chat room! With some moderation rules automatically applied to them: novideo,noimage. * Update the Shy Account FAQ and messaging on the chat landing page. * Update the auto-kick from chat behavior regarding shy accounts: * They are kicked from chat only when an update to their profile settings will transition then FROM a non-shy into a shy account. * For example: when saving their profile settings (going private) or when editing or deleting a photo (if they will have no more public photos left)
225 lines
6.4 KiB
Go
225 lines
6.4 KiB
Go
package chat
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/config"
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
"code.nonshy.com/nonshy/website/pkg/models"
|
|
)
|
|
|
|
// MaybeDisconnectUser may send a DisconnectUserNow to BareRTC if the user should not be allowed in the chat room.
|
|
//
|
|
// For example, they have set their profile to private and become a shy account, or they deactivated or got banned.
|
|
//
|
|
// If the user is presently in the chat room, they will be removed and given an appropriate ChatServer message.
|
|
//
|
|
// Returns a boolean OK (they were online in chat, and were removed) with the error only returning in case of a
|
|
// communication or JSON encode error with BareRTC. If they were online and removed, an admin feedback notice is
|
|
// also generated for visibility and confirmation of success.
|
|
func MaybeDisconnectUser(user *models.User) (bool, error) {
|
|
// What reason to remove them? If a message is provided, the DisconnectUserNow API will be called.
|
|
var because = "You have been signed out of chat because "
|
|
var reasons = []struct {
|
|
If bool
|
|
Message string
|
|
}{
|
|
{
|
|
If: !user.Certified,
|
|
Message: because + "your nonshy account is not Certified, or its Certified status has been revoked.",
|
|
},
|
|
{
|
|
If: user.IsShy(),
|
|
Message: because + "you had updated your nonshy profile to become too private, and now are considered to have a 'Shy Account.'<br><br>" +
|
|
"You may <strong>refresh</strong> the page to log back into chat as a Shy Account, where your ability to use webcams and share photos " +
|
|
"will be restricted. To regain full access to the chat room, please edit your profile settings to make sure that at least one 'public' " +
|
|
"photo is viewable to other members of the website.<br><br>" +
|
|
"Please see the <a href=\"https://www.nonshy.com/faq#shy-faqs\">Shy Account FAQ</a> for more information.",
|
|
},
|
|
{
|
|
If: user.Status == models.UserStatusDisabled,
|
|
Message: because + "you have deactivated your nonshy account.",
|
|
},
|
|
{
|
|
If: user.Status == models.UserStatusBanned,
|
|
Message: because + "your nonshy account has been banned.",
|
|
},
|
|
{
|
|
// Catch-all for any non-active user status.
|
|
If: user.Status != models.UserStatusActive,
|
|
Message: because + "your nonshy account is no longer eligible to remain in the chat room.",
|
|
},
|
|
}
|
|
|
|
for _, reason := range reasons {
|
|
if reason.If {
|
|
i, err := DisconnectUserNow(user, reason.Message)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
// Were they online and were removed? Notify the admin for visibility.
|
|
if i > 0 {
|
|
fb := &models.Feedback{
|
|
Intent: "report",
|
|
Subject: "Auto-Disconnect from Chat",
|
|
UserID: user.ID,
|
|
TableName: "users",
|
|
TableID: user.ID,
|
|
Message: fmt.Sprintf(
|
|
"A user was automatically disconnected from the chat room!\n\n"+
|
|
"* Username: %s\n"+
|
|
"* Number of users removed: %d\n"+
|
|
"* Message sent to them: %s\n\n"+
|
|
"Note: this is an informative message only. Users are expected to be removed from "+
|
|
"chat when they do things such as deactivate their account, or private their profile "+
|
|
"or pictures, and thus become ineligible to remain in the chat room.",
|
|
user.Username,
|
|
i,
|
|
reason.Message,
|
|
),
|
|
}
|
|
|
|
// Save the feedback.
|
|
if err := models.CreateFeedback(fb); err != nil {
|
|
log.Error("Couldn't save feedback from user updating their DOB: %s", err)
|
|
}
|
|
}
|
|
|
|
// First removal reason wins.
|
|
break
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// DisconnectUserNow tells the chat room to remove the user now if they are presently online.
|
|
func DisconnectUserNow(user *models.User, message string) (int, error) {
|
|
// API request struct for BareRTC /api/block/now endpoint.
|
|
var request = struct {
|
|
APIKey string
|
|
Usernames []string
|
|
Message string
|
|
Kick bool
|
|
}{
|
|
APIKey: config.Current.CronAPIKey,
|
|
Usernames: []string{
|
|
user.Username,
|
|
},
|
|
Message: message,
|
|
Kick: false,
|
|
}
|
|
|
|
type response struct {
|
|
OK bool
|
|
Removed int
|
|
Error string `json:",omitempty"`
|
|
}
|
|
|
|
// JSON request body.
|
|
jsonStr, err := json.Marshal(request)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Make the API request to BareRTC.
|
|
var url = strings.TrimSuffix(config.Current.BareRTC.URL, "/") + "/api/disconnect/now"
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Ingest the JSON response to see the count and error.
|
|
var (
|
|
result response
|
|
body, _ = io.ReadAll(resp.Body)
|
|
)
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK || !result.OK {
|
|
log.Error("DisconnectUserNow: error from BareRTC: status %d body %s", resp.StatusCode, body)
|
|
return result.Removed, errors.New(result.Error)
|
|
}
|
|
|
|
return result.Removed, nil
|
|
}
|
|
|
|
// EraseChatHistory tells the chat room to clear DMs history for this user.
|
|
func EraseChatHistory(username string) (int, error) {
|
|
// API request struct for BareRTC /api/message/clear endpoint.
|
|
var request = struct {
|
|
APIKey string
|
|
Username string
|
|
}{
|
|
APIKey: config.Current.CronAPIKey,
|
|
Username: username,
|
|
}
|
|
|
|
type response struct {
|
|
OK bool
|
|
MessagesErased int
|
|
Error string `json:",omitempty"`
|
|
}
|
|
|
|
// JSON request body.
|
|
jsonStr, err := json.Marshal(request)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Make the API request to BareRTC.
|
|
var url = strings.TrimSuffix(config.Current.BareRTC.URL, "/") + "/api/message/clear"
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Ingest the JSON response to see the count and error.
|
|
var (
|
|
result response
|
|
body, _ = io.ReadAll(resp.Body)
|
|
)
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK || !result.OK {
|
|
log.Error("EraseChatHistory: error from BareRTC: status %d body %s", resp.StatusCode, body)
|
|
return result.MessagesErased, errors.New(result.Error)
|
|
}
|
|
|
|
return result.MessagesErased, nil
|
|
}
|