website/pkg/mail/mail.go

91 lines
2.2 KiB
Go

// Package mail provides e-mail sending faculties.
package mail
import (
"bytes"
"errors"
"fmt"
"html/template"
"strings"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/log"
"github.com/microcosm-cc/bluemonday"
"gopkg.in/gomail.v2"
)
// Message configuration.
type Message struct {
To string
ReplyTo string
Subject string
Template string // path relative to the templates dir, e.g. "email/verify_email.html"
Data map[string]interface{}
}
// Send an email.
func Send(msg Message) error {
conf := config.Current.Mail
// Verify configuration.
if !conf.Enabled {
return errors.New(
"Email sending is not configured for this app. Please contact the website administrator about this error.",
)
} else if conf.Host == "" || conf.Port == 0 || conf.From == "" {
return errors.New(
"Email settings are misconfigured for this app. Please contact the website administrator about this error.",
)
}
// Get and render the template to HTML.
var html bytes.Buffer
tmpl, err := template.New(msg.Template).ParseFiles(config.TemplatePath + "/" + msg.Template)
if err != nil {
return err
}
// Execute the template.
err = tmpl.ExecuteTemplate(&html, "content", msg)
if err != nil {
return fmt.Errorf("Mail template execute error: %s", err)
}
// Condense the HTML down into the plaintext version.
rawLines := strings.Split(
bluemonday.StrictPolicy().Sanitize(html.String()),
"\n",
)
var lines []string
for _, line := range rawLines {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
lines = append(lines, line)
}
plaintext := strings.Join(lines, "\n\n")
// Prepare the e-mail!
m := gomail.NewMessage()
m.SetHeader("From", fmt.Sprintf("%s <%s>", config.Title, conf.From))
m.SetHeader("To", msg.To)
if msg.ReplyTo != "" {
m.SetHeader("Reply-To", msg.ReplyTo)
}
m.SetHeader("Subject", msg.Subject)
m.SetBody("text/plain", plaintext)
m.AddAlternative("text/html", html.String())
// Deliver asynchronously.
log.Info("mail.Send: %s (%s) to %s", msg.Subject, msg.Template, msg.To)
d := gomail.NewDialer(conf.Host, conf.Port, conf.Username, conf.Password)
go func() {
if err := d.DialAndSend(m); err != nil {
log.Error("mail.Send: %s", err.Error())
}
}()
return nil
}