package emailing
import (
"bytes"
"fmt"
"html/template"
"github.com/spf13/viper"
"github.com/wneessen/go-mail"
)
type Mailer struct {
TemplatesDir string
TemplatesConfig *viper.Viper
SMTPConfig
}
type SMTPConfig struct {
Host string
Port int
Username string
Password string
}
type Option func(*mail.Msg, []mail.Option) ([]mail.Option, error)
func NewMailer(templates_dir string, tplcfg *viper.Viper, smtpconfig *viper.Viper) (*Mailer, error) {
return &Mailer{
TemplatesDir: templates_dir,
TemplatesConfig: tplcfg,
SMTPConfig: SMTPConfig{
Host: smtpconfig.GetString("host"),
Port: smtpconfig.GetInt("port"),
Username: smtpconfig.GetString("username"),
Password: smtpconfig.GetString("password"),
},
}, nil
}
func (m *Mailer) Send(emailcfg string, to string, data any, opts ...Option) error {
cfg := m.TemplatesConfig.Sub(emailcfg)
files := cfg.GetStringSlice("files")
prefixed_files := []string{}
for _, f := range files {
prefixed_files = append(prefixed_files, m.TemplatesDir+f)
}
t := template.New("email").Funcs(
template.FuncMap{
"unescapeHTML": UnescapeHTML,
},
)
t = template.Must(t.ParseFiles(prefixed_files...))
buf := new(bytes.Buffer)
if err := t.ExecuteTemplate(buf, "main", data); err != nil {
return fmt.Errorf("failed execute mail template : %w", err)
}
body := buf.String()
message := mail.NewMsg()
if err := message.From(m.SMTPConfig.Username); err != nil {
return fmt.Errorf("failed to set From header : %w", err)
}
if err := message.To(to); err != nil {
return fmt.Errorf("failed to set To header : %w", err)
}
message.Subject(cfg.GetString("subject"))
message.SetBodyString(mail.TypeTextHTML, body)
dialOptions := []mail.Option{
//mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover),
mail.WithSMTPAuth(mail.SMTPAuthNoAuth),
mail.WithUsername(m.SMTPConfig.Username),
mail.WithPassword(m.SMTPConfig.Password),
}
fmt.Println(dialOptions)
for _, opt := range opts {
no, err := opt(message, dialOptions)
if err != nil {
return fmt.Errorf("failed to set option : %w", err)
}
dialOptions = no
}
fmt.Println(dialOptions)
client, err := mail.NewClient(m.SMTPConfig.Host, dialOptions...)
if err != nil {
return fmt.Errorf("failed to create mail client : %w", err)
}
if err := client.DialAndSend(message); err != nil {
return fmt.Errorf("failed to send message : %w", err)
}
return nil
}
func WithReplyTo(email string) Option {
return func(m *mail.Msg, opts []mail.Option) ([]mail.Option, error) {
return opts, m.ReplyTo(email)
}
}
func WithTLSOpportunistic() Option {
return func(m *mail.Msg, opts []mail.Option) ([]mail.Option, error) {
opts = append(opts, mail.WithTLSPolicy(mail.TLSOpportunistic))
return opts, nil
}
}
func UnescapeHTML(s string) template.HTML {
return template.HTML(s)
}