2023-08-13 04:37:11 +00:00
|
|
|
package barertc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2023-10-07 20:24:07 +00:00
|
|
|
"strings"
|
2023-10-29 19:35:00 +00:00
|
|
|
"time"
|
2023-08-13 04:37:11 +00:00
|
|
|
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/config"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/controller/api"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/models"
|
2023-10-29 19:35:00 +00:00
|
|
|
"code.nonshy.com/nonshy/website/pkg/templates"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/utility"
|
2023-08-13 04:37:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// WebhookRequest is a JSON request wrapper around all webhook messages.
|
|
|
|
type WebhookRequest struct {
|
|
|
|
Action string
|
|
|
|
APIKey string
|
|
|
|
|
|
|
|
// Relevant body per request.
|
|
|
|
Report WebhookRequestReport `json:",omitempty"`
|
2023-10-07 20:24:07 +00:00
|
|
|
|
|
|
|
// Optional params per request.
|
|
|
|
Username string `json:",omitempty"` // e.g. for profile webhook
|
2023-08-13 04:37:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WebhookRequestReport is the body for 'report' webhook messages.
|
|
|
|
type WebhookRequestReport struct {
|
|
|
|
FromUsername string
|
|
|
|
AboutUsername string
|
|
|
|
Channel string
|
|
|
|
Timestamp string
|
|
|
|
Reason string
|
|
|
|
Message string
|
|
|
|
Comment string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Report webhook controller.
|
|
|
|
func Report() http.HandlerFunc {
|
|
|
|
|
|
|
|
// Response JSON schema.
|
|
|
|
type Response struct {
|
2023-10-07 20:24:07 +00:00
|
|
|
OK bool
|
|
|
|
Error string `json:",omitempty"`
|
2023-08-13 04:37:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
api.SendJSON(w, http.StatusNotAcceptable, Response{
|
|
|
|
Error: "POST method only",
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse request payload.
|
|
|
|
var req WebhookRequest
|
|
|
|
if err := api.ParseJSON(r, &req); err != nil {
|
|
|
|
api.SendJSON(w, http.StatusBadRequest, Response{
|
|
|
|
Error: fmt.Sprintf("Error with request payload: %s", err),
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the AdminAPIKey.
|
|
|
|
if req.APIKey != config.Current.CronAPIKey {
|
|
|
|
api.SendJSON(w, http.StatusForbidden, Response{
|
|
|
|
Error: "Invalid API Key",
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the report out.
|
|
|
|
report := req.Report
|
2023-08-13 04:47:33 +00:00
|
|
|
if report.Comment == "" {
|
|
|
|
report.Comment = "(no comment)"
|
|
|
|
}
|
2023-08-13 04:37:11 +00:00
|
|
|
|
|
|
|
log.Debug("Got chat report: %+v", report)
|
|
|
|
|
2024-10-18 02:21:18 +00:00
|
|
|
// Make a clickable profile link for the channel ID (other user).
|
|
|
|
otherUsername := strings.TrimPrefix(report.Channel, "@")
|
|
|
|
|
2023-08-13 04:37:11 +00:00
|
|
|
// Create an admin Feedback model.
|
|
|
|
fb := &models.Feedback{
|
|
|
|
Intent: "report",
|
|
|
|
Subject: "report.chat",
|
|
|
|
Message: fmt.Sprintf(
|
|
|
|
"A message was reported on the chat room!\n\n"+
|
|
|
|
"* From username: [%s](/u/%s)\n"+
|
|
|
|
"* About username: [%s](/u/%s)\n"+
|
2024-10-18 02:21:18 +00:00
|
|
|
"* Channel: [**%s**](/u/%s)\n"+
|
2023-08-13 04:37:11 +00:00
|
|
|
"* Timestamp: %s\n"+
|
|
|
|
"* Classification: %s\n"+
|
|
|
|
"* User comment: %s\n\n"+
|
|
|
|
"- - - - -\n\n"+
|
|
|
|
"The reported message on chat was:\n\n%s",
|
|
|
|
report.FromUsername, report.FromUsername,
|
|
|
|
report.AboutUsername, report.AboutUsername,
|
2024-10-18 02:21:18 +00:00
|
|
|
report.Channel, otherUsername,
|
2023-08-13 04:37:11 +00:00
|
|
|
report.Timestamp,
|
|
|
|
report.Reason,
|
|
|
|
report.Comment,
|
|
|
|
report.Message,
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the Reply-To user if possible.
|
2023-08-13 04:47:33 +00:00
|
|
|
currentUser, err := models.FindUser(report.FromUsername)
|
|
|
|
if err == nil {
|
|
|
|
fb.UserID = currentUser.ID
|
|
|
|
} else {
|
|
|
|
currentUser = nil
|
2023-08-13 04:37:11 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 00:30:39 +00:00
|
|
|
// Look up the AboutUser ID if possible.
|
|
|
|
targetUser, err := models.FindUser(report.AboutUsername)
|
|
|
|
if err == nil {
|
|
|
|
fb.TableName = "users"
|
|
|
|
fb.TableID = targetUser.ID
|
2024-10-18 02:21:18 +00:00
|
|
|
fb.AboutUserID = targetUser.ID
|
2023-08-17 00:30:39 +00:00
|
|
|
} else {
|
|
|
|
log.Error("BareRTC Chat Feedback: couldn't find user ID for AboutUsername=%s: %s", report.AboutUsername, err)
|
|
|
|
}
|
|
|
|
|
2023-08-13 04:37:11 +00:00
|
|
|
// Save the feedback.
|
|
|
|
if err := models.CreateFeedback(fb); err != nil {
|
|
|
|
log.Error("Couldn't save feedback from BareRTC report endpoint: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send success response.
|
|
|
|
api.SendJSON(w, http.StatusOK, Response{
|
|
|
|
OK: true,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2023-10-07 20:24:07 +00:00
|
|
|
|
|
|
|
// Profile webhook controller to fetch more profile details for a user.
|
|
|
|
func Profile() http.HandlerFunc {
|
|
|
|
|
|
|
|
// Response JSON schema.
|
|
|
|
type ProfileField struct {
|
|
|
|
Name string
|
|
|
|
Value string
|
|
|
|
}
|
|
|
|
type Response struct {
|
|
|
|
OK bool
|
|
|
|
Error string `json:",omitempty"`
|
|
|
|
ProfileFields []ProfileField
|
|
|
|
}
|
|
|
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
api.SendJSON(w, http.StatusNotAcceptable, Response{
|
|
|
|
Error: "POST method only",
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse request payload.
|
|
|
|
var req WebhookRequest
|
|
|
|
if err := api.ParseJSON(r, &req); err != nil {
|
|
|
|
api.SendJSON(w, http.StatusBadRequest, Response{
|
|
|
|
Error: fmt.Sprintf("Error with request payload: %s", err),
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the AdminAPIKey.
|
|
|
|
if req.APIKey != config.Current.CronAPIKey {
|
|
|
|
api.SendJSON(w, http.StatusForbidden, Response{
|
|
|
|
Error: "Invalid API Key",
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the Reply-To user if possible.
|
|
|
|
currentUser, err := models.FindUser(req.Username)
|
|
|
|
if err != nil {
|
|
|
|
api.SendJSON(w, http.StatusForbidden, Response{
|
|
|
|
Error: "Username Not Found",
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate their profile fields.
|
|
|
|
var maritalStatus = currentUser.GetProfileFieldOr("marital_status", "n/a")
|
|
|
|
if relationshipType := currentUser.GetProfileField("relationship_type"); relationshipType != "" {
|
|
|
|
maritalStatus += fmt.Sprintf(" (%s)", relationshipType)
|
|
|
|
}
|
|
|
|
|
|
|
|
var gender = currentUser.GetProfileFieldOr("gender", "n/a")
|
|
|
|
if pronouns := currentUser.GetProfileField("pronouns"); pronouns != "" {
|
|
|
|
gender += fmt.Sprintf(" (%s)", pronouns)
|
|
|
|
}
|
|
|
|
|
2023-10-29 19:35:00 +00:00
|
|
|
var photoCount = models.CountPublicPhotos(currentUser.ID)
|
|
|
|
|
2024-08-24 06:09:27 +00:00
|
|
|
// Member Since date.
|
|
|
|
var memberSinceDate = currentUser.CreatedAt
|
|
|
|
if currentUser.Certified {
|
|
|
|
if dt, err := currentUser.CertifiedSince(); err == nil {
|
|
|
|
memberSinceDate = dt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-07 20:24:07 +00:00
|
|
|
var resp = Response{
|
|
|
|
OK: true,
|
|
|
|
ProfileFields: []ProfileField{
|
2023-10-29 19:35:00 +00:00
|
|
|
{
|
2024-08-24 06:09:27 +00:00
|
|
|
Name: "Certified since",
|
|
|
|
Value: fmt.Sprintf("%s ago", utility.FormatDurationCoarse(time.Since(memberSinceDate))),
|
2023-10-29 19:35:00 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "📸 Gallery",
|
|
|
|
Value: fmt.Sprintf("At least %d photo%s", photoCount, templates.Pluralize(photoCount)),
|
|
|
|
},
|
2023-10-07 20:24:07 +00:00
|
|
|
{
|
|
|
|
Name: "Age",
|
|
|
|
Value: currentUser.GetDisplayAge(),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Gender",
|
|
|
|
Value: gender,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "City",
|
|
|
|
Value: currentUser.GetProfileFieldOr("city", "n/a"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Job",
|
|
|
|
Value: currentUser.GetProfileFieldOr("job", "n/a"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Marital status",
|
|
|
|
Value: maritalStatus,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Orientation",
|
|
|
|
Value: currentUser.GetProfileFieldOr("orientation", "n/a"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Here for",
|
|
|
|
Value: strings.Join(strings.Split(currentUser.GetProfileFieldOr("here_for", "n/a"), ","), ", "),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send success response.
|
|
|
|
api.SendJSON(w, http.StatusOK, resp)
|
|
|
|
})
|
|
|
|
}
|