From a0320714c48c90668204aa873d868a3f8b373df1 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sat, 13 Jul 2024 12:05:36 -0700 Subject: [PATCH] Search terms and admin features --- pkg/controller/account/search.go | 34 +++++++++++++++- pkg/controller/admin/user_actions.go | 28 ++++++++++++++ pkg/spam/spam.go | 56 +++++++++++++++++++++++++++ web/templates/account/profile.html | 6 +++ web/templates/account/search.html | 12 ++++++ web/templates/account/user_notes.html | 8 ++++ web/templates/admin/user_actions.html | 38 ++++++++++++++++++ 7 files changed, 181 insertions(+), 1 deletion(-) diff --git a/pkg/controller/account/search.go b/pkg/controller/account/search.go index 854e505..ae03e38 100644 --- a/pkg/controller/account/search.go +++ b/pkg/controller/account/search.go @@ -1,6 +1,7 @@ package account import ( + "fmt" "net/http" "strconv" @@ -9,6 +10,7 @@ import ( "code.nonshy.com/nonshy/website/pkg/log" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/session" + "code.nonshy.com/nonshy/website/pkg/spam" "code.nonshy.com/nonshy/website/pkg/templates" "code.nonshy.com/nonshy/website/pkg/worker" ) @@ -50,7 +52,8 @@ func Search() http.HandlerFunc { ageMin, ageMax = ageMax, ageMin } - search := models.ParseSearchString(searchTerm) + rawSearch := models.ParseSearchString(searchTerm) + search, restricted := spam.RestrictSearchTerms(rawSearch) // Get current user. currentUser, err := session.CurrentUser(r) @@ -60,6 +63,32 @@ func Search() http.HandlerFunc { return } + // Report when search terms are restricted. + if restricted != nil { + // Admin users: allow the search anyway. + if currentUser.IsAdmin { + search = rawSearch + } else { + fb := &models.Feedback{ + Intent: "report", + Subject: "Search Keyword Blacklist", + UserID: currentUser.ID, + TableName: "users", + TableID: currentUser.ID, + Message: fmt.Sprintf( + "A user has run a search on the Member Directory using search terms which are prohibited.\n\n"+ + "Their search query was: %s", + searchTerm, + ), + } + + // Save the feedback. + if err := models.CreateFeedback(fb); err != nil { + log.Error("Couldn't save feedback from user updating their DOB: %s", err) + } + } + } + // Geolocation/Who's Nearby: if the current user uses GeoIP, update // their coordinates now. myLocation, err := models.RefreshGeoIP(currentUser.ID, r) @@ -160,6 +189,9 @@ func Search() http.HandlerFunc { "LikedSearch": likedSearch, "Sort": sort, + // Restricted Search errors. + "RestrictedSearchError": restricted, + // Photo counts mapped to users "PhotoCountMap": models.MapPhotoCounts(users), diff --git a/pkg/controller/admin/user_actions.go b/pkg/controller/admin/user_actions.go index c568cac..d113a10 100644 --- a/pkg/controller/admin/user_actions.go +++ b/pkg/controller/admin/user_actions.go @@ -122,6 +122,34 @@ func UserActions() http.HandlerFunc { session.FlashError(w, r, "Error getting blocklist insights: %s", err) } vars["BlocklistInsights"] = insights + case "essays": + // Edit their profile essays easily. + if !currentUser.HasAdminScope(config.ScopePhotoModerator) { + session.FlashError(w, r, "Missing admin scope: %s", config.ScopePhotoModerator) + templates.Redirect(w, "/admin") + return + } + + if r.Method == http.MethodPost { + var ( + about = r.PostFormValue("about_me") + interests = r.PostFormValue("interests") + musicMovies = r.PostFormValue("music_movies") + ) + + user.SetProfileField("about_me", about) + user.SetProfileField("interests", interests) + user.SetProfileField("music_movies", musicMovies) + + if err := user.Save(); err != nil { + session.FlashError(w, r, "Error saving the user: %s", err) + } else { + session.Flash(w, r, "Their profile text has been updated!") + } + + templates.Redirect(w, "/u/"+user.Username) + return + } case "impersonate": // Scope check. if !currentUser.HasAdminScope(config.ScopeUserImpersonate) { diff --git a/pkg/spam/spam.go b/pkg/spam/spam.go index 3ac48a2..16ab82a 100644 --- a/pkg/spam/spam.go +++ b/pkg/spam/spam.go @@ -3,6 +3,8 @@ package spam import ( "errors" "strings" + + "code.nonshy.com/nonshy/website/pkg/models" ) // SpamWebsites to third-party video hosting apps: we already have our own chat room, and third-party links shared in @@ -30,3 +32,57 @@ func DetectSpamMessage(message string) error { return nil } + +// RestrictSearchTerm can remove/replace search words to exclude blacklisted terms. +func RestrictSearchTerms(terms *models.Search) (*models.Search, error) { + var ( + m = map[string]interface{}{} + result = &models.Search{} + r1, r2 int + ) + + // Map the blacklist for easy lookup. + for _, term := range restrictedSearchTerms { + m[term] = nil + } + + // Filter the includes+excludes. + result.Includes, r1 = restrictTermsFrom(terms.Includes, m) + result.Excludes, r2 = restrictTermsFrom(terms.Excludes, m) + + // If we have excluded everything down to zero. + if len(terms.Includes) > 0 && len(result.Includes) == 0 { + result.Includes = append(result.Includes, "36c49b88-dd0c-4b9f-a4e1-f0c73c976ce2") + } + + // Were there restrictions? + if r1+r2 > 0 { + return result, errors.New("some search terms were restricted") + } + + return result, nil +} + +// Restricted search terms. +var restrictedSearchTerms = []string{ + "open", "mind", "minded", "taboo", "tabboo", "perv", "pervy", "grew", "raised", "raise", + "children", "kid", "kids", "dad", "mom", "underage", "yng", "ynger", "family", "families", + "18", "shota", "shotacon", "loli", "lolicon", "jailbait", "incest", +} + +func restrictTermsFrom(terms []string, blacklist map[string]interface{}) ([]string, int) { + var ( + result []string + count int + ) + + for _, term := range terms { + if _, ok := blacklist[strings.ToLower(term)]; ok { + count++ + continue + } + result = append(result, term) + } + + return result, count +} diff --git a/web/templates/account/profile.html b/web/templates/account/profile.html index 3ad0895..a21f967 100644 --- a/web/templates/account/profile.html +++ b/web/templates/account/profile.html @@ -497,6 +497,12 @@ +
  • + + + Edit their profile text + +
  • diff --git a/web/templates/account/search.html b/web/templates/account/search.html index c2b3e32..bbd5bea 100644 --- a/web/templates/account/search.html +++ b/web/templates/account/search.html @@ -69,6 +69,18 @@ {{end}} + + {{if .RestrictedSearchError}} + + {{end}} +
    diff --git a/web/templates/account/user_notes.html b/web/templates/account/user_notes.html index 8b798ab..df8d821 100644 --- a/web/templates/account/user_notes.html +++ b/web/templates/account/user_notes.html @@ -264,6 +264,14 @@ {{end}} + + + Date: + + + {{.CreatedAt.Format "2006-01-02 15:04:05 MST"}} + +
    diff --git a/web/templates/admin/user_actions.html b/web/templates/admin/user_actions.html index 31306ca..9d4fc73 100644 --- a/web/templates/admin/user_actions.html +++ b/web/templates/admin/user_actions.html @@ -22,6 +22,9 @@ {{if eq .Intent "impersonate"}} Impersonate User + {{else if eq .Intent "essays"}} + + Edit Profile Text {{else if eq .Intent "ban"}} Ban User @@ -145,6 +148,41 @@
    + {{else if eq .Intent "essays"}} +
    +

    + You may use this page to edit the essay texts (e.g. About Me) section of a user's profile page. + The main use cases may be to remove Onlyfans spammy links or that sort of thing, so that you + don't need to fully impersonate their account to do so. +

    +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    {{else if eq .Intent "impersonate"}}

    With great power...