profanity

master
Denis Ranneft 1 month ago
parent 6bbdc217de
commit de8e6bc892

@ -3,10 +3,12 @@ module github.com/denisovdennis/autohero
go 1.23 go 1.23
require ( require (
github.com/TwiN/go-away v1.6.12
github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/chi/v5 v5.1.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/jackc/pgx/v5 v5.7.1 github.com/jackc/pgx/v5 v5.7.1
github.com/redis/go-redis/v9 v9.7.0 github.com/redis/go-redis/v9 v9.7.0
golang.org/x/text v0.18.0
) )
require ( require (
@ -17,5 +19,4 @@ require (
github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect
golang.org/x/crypto v0.27.0 // indirect golang.org/x/crypto v0.27.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
) )

@ -1,3 +1,5 @@
github.com/TwiN/go-away v1.6.12 h1:80AjDyeTjfQaSFYbALzRcDKMAmxKW0a5PoxwXKZlW2A=
github.com/TwiN/go-away v1.6.12/go.mod h1:MpvIC9Li3minq+CGgbgUDvQ9tDaeW35k5IXZrF9MVas=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=

@ -11,12 +11,15 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
"unicode/utf8"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"golang.org/x/text/unicode/norm"
"github.com/denisovdennis/autohero/internal/changelog" "github.com/denisovdennis/autohero/internal/changelog"
"github.com/denisovdennis/autohero/internal/game" "github.com/denisovdennis/autohero/internal/game"
"github.com/denisovdennis/autohero/internal/model" "github.com/denisovdennis/autohero/internal/model"
"github.com/denisovdennis/autohero/internal/profanity"
"github.com/denisovdennis/autohero/internal/storage" "github.com/denisovdennis/autohero/internal/storage"
"github.com/denisovdennis/autohero/internal/tuning" "github.com/denisovdennis/autohero/internal/tuning"
"github.com/denisovdennis/autohero/internal/version" "github.com/denisovdennis/autohero/internal/version"
@ -1035,13 +1038,14 @@ type heroNameRequest struct {
Name string `json:"name"` Name string `json:"name"`
} }
// isValidHeroName checks that a name is 2-16 chars, only latin/cyrillic letters and digits, // isValidHeroName checks that a name is 2-16 Unicode characters, only latin/cyrillic letters and digits,
// no leading/trailing spaces. // no leading/trailing spaces. Expects trimmed, NFC-normalized input (see SetHeroName).
func isValidHeroName(name string) bool { func isValidHeroName(name string) bool {
if len(name) < 2 || len(name) > 16 { n := utf8.RuneCountInString(name)
if n < 2 || n > 16 {
return false return false
} }
if name[0] == ' ' || name[len(name)-1] == ' ' { if strings.TrimSpace(name) != name {
return false return false
} }
for _, r := range name { for _, r := range name {
@ -1079,12 +1083,20 @@ func (h *GameHandler) SetHeroName(w http.ResponseWriter, r *http.Request) {
return return
} }
req.Name = norm.NFC.String(strings.TrimSpace(req.Name))
if !isValidHeroName(req.Name) { if !isValidHeroName(req.Name) {
writeJSON(w, http.StatusBadRequest, map[string]string{ writeJSON(w, http.StatusBadRequest, map[string]string{
"error": "invalid name: must be 2-16 characters, letters (latin/cyrillic) and digits only", "error": "invalid name: must be 2-16 characters, letters (latin/cyrillic) and digits only",
}) })
return return
} }
if profanity.HeroNameIsProfane(req.Name) {
writeJSON(w, http.StatusBadRequest, map[string]string{
"error": "invalid name: inappropriate language",
})
return
}
hero, err := h.store.GetByTelegramID(r.Context(), telegramID) hero, err := h.store.GetByTelegramID(r.Context(), telegramID)
if err != nil { if err != nil {

@ -125,7 +125,12 @@ export function NameEntryScreen({ onNameSet }: NameEntryScreenProps) {
} else if (err.status === 400) { } else if (err.status === 400) {
try { try {
const j = JSON.parse(err.body) as { error?: string }; const j = JSON.parse(err.body) as { error?: string };
setError(j.error ?? tr.invalidName); const errMsg = j.error ?? '';
if (errMsg === 'invalid name: inappropriate language') {
setError(tr.invalidName);
} else {
setError(errMsg || tr.invalidName);
}
} catch { } catch {
setError(tr.invalidName); setError(tr.invalidName);
} }

Loading…
Cancel
Save