Files
vantage-shared/mail/announcement.go
T

70 lines
2.1 KiB
Go

package mail
import (
"bytes"
"fmt"
htmltmpl "html/template"
"strings"
"github.com/yuin/goldmark"
)
// Announcement is one product email to one person: a feature launch, a guide,
// an offer. vantage-admin's announce package builds it per recipient, because
// the unsubscribe links carry that person's token.
type Announcement struct {
CategoryLabel string // "New features"
Subject string
BodyMarkdown string
UnsubscribeURL string // RFC 8058 one-click target for this category
PreferencesURL string // the no-login preferences page
PostalAddress string // optional footer line
}
type announcementData struct {
Announcement
BodyHTML htmltmpl.HTML
}
// md renders with goldmark's defaults, which omit raw HTML: a pasted <script>
// never reaches an inbox.
var md = goldmark.New()
func announcementMessage(a Announcement, publicURL string) (message, error) {
var buf bytes.Buffer
if err := md.Convert([]byte(a.BodyMarkdown), &buf); err != nil {
return message{}, fmt.Errorf("mail: markdown: %w", err)
}
return render("announcement", announcementData{Announcement: a, BodyHTML: htmltmpl.HTML(buf.String())}, publicURL)
}
// RenderAnnouncement is the staff preview: exactly what a recipient gets.
func RenderAnnouncement(a Announcement, publicURL string) (subject, html, text string, err error) {
m, err := announcementMessage(a, publicURL)
if err != nil {
return "", "", "", err
}
return m.Subject, m.HTML, m.Text, nil
}
// SendAnnouncement sends one announcement to one address, with the
// List-Unsubscribe pair Gmail and Yahoo require of bulk senders.
func (s Sender) SendAnnouncement(to string, a Announcement) error {
if strings.Contains(to, ",") {
return fmt.Errorf("mail: an announcement goes to exactly one address")
}
if a.UnsubscribeURL == "" {
return fmt.Errorf("mail: announcement without an unsubscribe URL")
}
m, err := announcementMessage(a, s.PublicURL)
if err != nil {
return err
}
m.To = to
m.Headers = map[string]string{
"List-Unsubscribe": "<" + a.UnsubscribeURL + ">",
"List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
}
return s.send(m)
}