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 }