Files
geo/server/internal/tenant/app/article_url_sanitizer.go
T
root 52997e36fe fix(content-gen): strip URLs and site-list sections from generated content
Forbid URLs/domains/markdown links across prompt rules (runtime, platform
templates, outline/imitation/title flows) and rename "官网" placeholder to
"官网信息" with explicit instructions that links are background only.
Add sanitizeGeneratedArticleMarkdown to strip URLs, HTML images, and
"网站列表/官网列表" labels from LLM output, wired into article, prompt-rule,
template, and outline generation. Drop the site_list outline section seed
and filter blocked outline keys in the wizard so stored drafts cannot
resurrect it.
2026-05-24 22:19:43 +08:00

120 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"regexp"
"strings"
)
const (
generatedArticleSchemeURLPattern = `https?://[^\s<>"',。;、?!\])}]+`
generatedArticleDomainPattern = `(?:www\.)?[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*\.(?:com|cn|net|org|io|ai|co|cc|top|xyz|shop|site|online|tech|app|gov|edu|info|biz|me|tv)(?:/[^\s<>"',。;、?!\])}]*)?`
generatedArticleURLPattern = `(?:` + generatedArticleSchemeURLPattern + `|` + generatedArticleDomainPattern + `)`
)
var (
generatedArticleMarkdownLinkPattern = regexp.MustCompile(`!?\[([^\]\n]*)\]\(([^)\s]+)\)`)
generatedArticleHTMLImageParagraph = regexp.MustCompile(`(?is)<p\b[^>]*>\s*<img\b[^>]*\bsrc\s*=\s*["']?` + generatedArticleURLPattern + `["']?[^>]*>\s*</p>`)
generatedArticleHTMLImagePattern = regexp.MustCompile(`(?is)<img\b[^>]*\bsrc\s*=\s*["']?` + generatedArticleURLPattern + `["']?[^>]*>`)
generatedArticleURLLabelPattern = regexp.MustCompile(`(?i)(?:官网|官方网站|网站|网址|链接|URL|website|official website)\s*[:]?\s*` + generatedArticleURLPattern)
generatedArticleBareURLPattern = regexp.MustCompile(`(?i)\b` + generatedArticleURLPattern)
generatedArticleDanglingURLLabelPattern = regexp.MustCompile(`(?i)(?:官网|官方网站|网站|网址|链接|URL|website|official website)\s*[:]?\s*$`)
generatedArticleBlankLinePattern = regexp.MustCompile(`\n{3,}`)
generatedArticlePunctuationGapPattern = regexp.MustCompile(`\s+([,。;、?!,.;!?])`)
generatedArticleEmptyPunctuationPattern = regexp.MustCompile(`[:]\s*([,。;、?!,.;!?])`)
generatedArticleRepeatedSpacePattern = regexp.MustCompile(`[ \t]{2,}`)
)
func sanitizeGeneratedArticleMarkdown(markdown string) string {
text := strings.TrimSpace(markdown)
if text == "" {
return ""
}
text = generatedArticleMarkdownLinkPattern.ReplaceAllStringFunc(text, func(match string) string {
parts := generatedArticleMarkdownLinkPattern.FindStringSubmatch(match)
if len(parts) != 3 || !looksLikeGeneratedArticleURL(parts[2]) {
return match
}
if strings.HasPrefix(match, "![") {
return ""
}
label := strings.TrimSpace(parts[1])
if label == "" || looksLikeGeneratedArticleURL(label) {
return ""
}
return label
})
text = generatedArticleHTMLImageParagraph.ReplaceAllString(text, "")
text = generatedArticleHTMLImagePattern.ReplaceAllString(text, "")
text = generatedArticleURLLabelPattern.ReplaceAllString(text, "")
text = generatedArticleBareURLPattern.ReplaceAllString(text, "")
text = cleanGeneratedArticleURLArtifacts(text)
return strings.TrimSpace(text)
}
func sanitizeGeneratedOutlineText(text string) string {
text = sanitizeGeneratedArticleMarkdown(text)
replacer := strings.NewReplacer(
"网站列表", "品牌信息整理",
"官网列表", "品牌信息整理",
"网址列表", "品牌信息整理",
"官方网站", "公开信息",
"官方网址", "公开信息",
)
return strings.TrimSpace(replacer.Replace(text))
}
func looksLikeGeneratedArticleURL(text string) bool {
text = strings.TrimSpace(text)
if text == "" {
return false
}
return generatedArticleBareURLPattern.MatchString(text)
}
func cleanGeneratedArticleURLArtifacts(text string) string {
lines := strings.Split(text, "\n")
cleaned := make([]string, 0, len(lines))
for _, line := range lines {
originalLine := line
line = strings.TrimRight(line, " \t")
line = generatedArticleDanglingURLLabelPattern.ReplaceAllString(line, "")
line = strings.ReplaceAll(line, "网站列表", "品牌信息整理")
line = strings.ReplaceAll(line, "官网列表", "品牌信息整理")
line = strings.ReplaceAll(line, "网址列表", "品牌信息整理")
line = generatedArticleEmptyPunctuationPattern.ReplaceAllString(line, "$1")
line = generatedArticlePunctuationGapPattern.ReplaceAllString(line, "$1")
line = generatedArticleRepeatedSpacePattern.ReplaceAllString(line, " ")
if line != originalLine {
line = strings.TrimRight(line, " \t:,;")
}
if strings.TrimSpace(line) == "" {
if strings.TrimSpace(originalLine) == "" {
cleaned = append(cleaned, line)
}
continue
}
if isGeneratedArticleURLOnlyLine(line) {
continue
}
cleaned = append(cleaned, line)
}
return generatedArticleBlankLinePattern.ReplaceAllString(strings.Join(cleaned, "\n"), "\n\n")
}
func isGeneratedArticleURLOnlyLine(line string) bool {
trimmed := strings.TrimSpace(line)
trimmed = strings.TrimLeft(trimmed, "#-*+> \t")
trimmed = strings.Trim(trimmed, " \t:,;;。")
if trimmed == "" {
return true
}
switch strings.ToLower(trimmed) {
case "官网", "官方网站", "官方网址", "网站", "网址", "链接", "url", "website", "official website":
return true
default:
return false
}
}