Some checks failed
CI / test (push) Failing after 4s
Watches issue/PR comments and pushes for flagged phrases; warns commenters via reply and opens a redacted correction PR for flagged file content, never rewriting history or deleting content directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
51 lines
1.3 KiB
Go
51 lines
1.3 KiB
Go
// 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
|
|
}
|