persona-community-3/pkg/email/plaintext.go
jordan f53b908499
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/manual/woodpecker Pipeline was successful
Initialize project from skeleton template
2026-02-23 11:10:35 +00:00

81 lines
1.9 KiB
Go

package email
import (
"strings"
"github.com/PuerkitoBio/goquery"
)
// htmlToPlainText derives a plain-text representation from rendered HTML.
// The text version is for email clients that cannot render HTML. The HTML
// version is always preferred when the client supports it.
func htmlToPlainText(htmlStr string) string {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlStr))
if err != nil {
return htmlStr
}
// Remove elements that should not appear in plain text.
doc.Find("style, script, head, .preheader").Remove()
// Replace links with "text (URL)" format.
doc.Find("a[href]").Each(func(_ int, s *goquery.Selection) {
href, _ := s.Attr("href")
text := strings.TrimSpace(s.Text())
if href != "" && href != text && !strings.HasPrefix(href, "mailto:") {
s.ReplaceWithHtml(text + "\n" + href)
}
})
// Walk the DOM and collect text with structural newlines.
var lines []string
var walk func(*goquery.Selection)
walk = func(sel *goquery.Selection) {
sel.Contents().Each(func(_ int, n *goquery.Selection) {
tag := goquery.NodeName(n)
switch tag {
case "#text":
t := strings.TrimSpace(n.Text())
if t != "" {
lines = append(lines, t)
}
case "br":
lines = append(lines, "")
case "p", "div", "section", "article", "main", "h1", "h2", "h3", "h4", "h5", "h6", "li":
walk(n)
lines = append(lines, "")
case "tr":
walk(n)
lines = append(lines, "")
case "hr":
lines = append(lines, "---")
default:
walk(n)
}
})
}
body := doc.Find("body")
if body.Length() == 0 {
body = doc.Selection
}
walk(body)
// Collapse consecutive blank lines to a single blank line.
result := make([]string, 0, len(lines))
prevBlank := false
for _, line := range lines {
if line == "" {
if !prevBlank {
result = append(result, "")
}
prevBlank = true
} else {
result = append(result, line)
prevBlank = false
}
}
return strings.TrimSpace(strings.Join(result, "\n"))
}