// Package detector implements plain substring matching against a configured // phrase list (the "goroku" list). Entries are literal fixed phrases rather // than regexes because the source material is a closed set of copypasta // lines, not a pattern language. package detector import "strings" type Detector struct { phrases []string } func New(phrases []string) *Detector { uniq := make([]string, 0, len(phrases)) seen := make(map[string]struct{}, len(phrases)) for _, p := range phrases { p = strings.TrimSpace(p) if p == "" { continue } if _, ok := seen[p]; ok { continue } seen[p] = struct{}{} uniq = append(uniq, p) } return &Detector{phrases: uniq} } // Detect returns every registered phrase found in text, in registration order. func (d *Detector) Detect(text string) []string { var found []string for _, p := range d.phrases { if strings.Contains(text, p) { found = append(found, p) } } return found } // Redact returns text with every matched phrase replaced by a same-width // mask, along with the list of phrases that were matched. func (d *Detector) Redact(text string) (string, []string) { matched := d.Detect(text) redacted := text for _, p := range matched { mask := strings.Repeat("○", len([]rune(p))) redacted = strings.ReplaceAll(redacted, p, mask) } return redacted, matched }