package moderation import ( "regexp" "strings" "github.com/mozillazg/go-pinyin" "github.com/tinode/chat/server/store/types" ) func matchLocalWords(text, scene string, words *types.SensitiveWordSet) *ProviderResult { normalized := strings.ToLower(strings.TrimSpace(text)) if normalized == "" || words == nil { return nil } for _, rule := range words.Items { if rule == nil || strings.TrimSpace(rule.Word) == "" || !sceneEnabled(rule.Scenes, scene) { continue } matched := false switch strings.ToLower(strings.TrimSpace(rule.MatchType)) { case "exact": matched = normalized == strings.ToLower(strings.TrimSpace(rule.Word)) case "regex": pattern, err := regexp.Compile(rule.Word) matched = err == nil && pattern.MatchString(text) case "pinyin": args := pinyin.NewArgs() args.Style = pinyin.Normal textPinyin := strings.Join(pinyin.LazyConvert(text, &args), "") wordPinyin := strings.Join(pinyin.LazyConvert(rule.Word, &args), "") if wordPinyin == "" { wordPinyin = strings.ToLower(strings.TrimSpace(rule.Word)) } matched = wordPinyin != "" && strings.Contains(strings.ToLower(textPinyin), strings.ToLower(wordPinyin)) default: matched = strings.Contains(normalized, strings.ToLower(strings.TrimSpace(rule.Word))) } if matched { return &ProviderResult{RiskLevel: normalizedRisk(rule.RiskLevel), RiskLabels: []string{"local_sensitive_word"}, Summary: normalizedAction(rule.Action)} } } return nil } func sceneEnabled(scenes []string, scene string) bool { if len(scenes) == 0 { return true } for _, value := range scenes { if value == "*" || value == scene { return true } } return false } func normalizedRisk(value string) string { switch strings.ToLower(strings.TrimSpace(value)) { case "high", "medium", "low": return strings.ToLower(strings.TrimSpace(value)) default: return "none" } } func normalizedAction(value string) string { switch strings.ToLower(strings.TrimSpace(value)) { case ActionBlock, ActionAuditOnly: return strings.ToLower(strings.TrimSpace(value)) default: return ActionAllow } }