|
|
package profanity
|
|
|
|
|
|
import (
|
|
|
"strings"
|
|
|
|
|
|
goaway "github.com/TwiN/go-away"
|
|
|
)
|
|
|
|
|
|
var detector *goaway.ProfanityDetector
|
|
|
|
|
|
func init() {
|
|
|
combined := mergeUniqueWords(
|
|
|
goaway.DefaultProfanities,
|
|
|
russianProfanityFullWords,
|
|
|
latinRussianProfanityFull,
|
|
|
)
|
|
|
detector = goaway.NewProfanityDetector().WithCustomDictionary(
|
|
|
combined,
|
|
|
goaway.DefaultFalsePositives,
|
|
|
goaway.DefaultFalseNegatives,
|
|
|
)
|
|
|
}
|
|
|
|
|
|
func mergeUniqueWords(parts ...[]string) []string {
|
|
|
seen := make(map[string]struct{}, 768)
|
|
|
out := make([]string, 0, 768)
|
|
|
for _, p := range parts {
|
|
|
for _, w := range p {
|
|
|
if w == "" {
|
|
|
continue
|
|
|
}
|
|
|
if _, ok := seen[w]; ok {
|
|
|
continue
|
|
|
}
|
|
|
seen[w] = struct{}{}
|
|
|
out = append(out, w)
|
|
|
}
|
|
|
}
|
|
|
return out
|
|
|
}
|
|
|
|
|
|
// foldYoToE maps ё→е so dictionary entries using «е» still match nicknames typed with «ё».
|
|
|
func foldYoToE(s string) string {
|
|
|
var b strings.Builder
|
|
|
b.Grow(len(s))
|
|
|
for _, r := range s {
|
|
|
switch r {
|
|
|
case 'ё', 'Ё':
|
|
|
b.WriteRune('е')
|
|
|
default:
|
|
|
b.WriteRune(r)
|
|
|
}
|
|
|
}
|
|
|
return b.String()
|
|
|
}
|
|
|
|
|
|
// HeroNameIsProfane: go-away (англ. + leet) + полные русские и латинские формы; две нормализации омоглифов.
|
|
|
func HeroNameIsProfane(name string) bool {
|
|
|
folded := foldYoToE(name)
|
|
|
if detector.IsProfane(folded) {
|
|
|
return true
|
|
|
}
|
|
|
if detector.IsProfane(cyrillicHomoglyphsToLatin(folded)) {
|
|
|
return true
|
|
|
}
|
|
|
if hasCyrillicLetter(folded) {
|
|
|
if detector.IsProfane(latinAndDigitHomoglyphsToCyrillic(folded)) {
|
|
|
return true
|
|
|
}
|
|
|
}
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
// ChatMessageIsProfane checks free-text chat lines (shorter than hero names may allow).
|
|
|
func ChatMessageIsProfane(s string) bool {
|
|
|
if s == "" {
|
|
|
return false
|
|
|
}
|
|
|
folded := foldYoToE(s)
|
|
|
if detector.IsProfane(folded) {
|
|
|
return true
|
|
|
}
|
|
|
if detector.IsProfane(cyrillicHomoglyphsToLatin(folded)) {
|
|
|
return true
|
|
|
}
|
|
|
if hasCyrillicLetter(folded) {
|
|
|
if detector.IsProfane(latinAndDigitHomoglyphsToCyrillic(folded)) {
|
|
|
return true
|
|
|
}
|
|
|
}
|
|
|
return false
|
|
|
}
|