package main import ( "bytes" "embed" "html/template" "log" "time" "github.com/vanng822/go-premailer/premailer" mail "github.com/xhit/go-simple-mail/v2" ) //go:embed templates var emailTemplateFS embed.FS type Mail struct { Domain string Host string Port int Username string Password string Encryption string FromAddr string FromName string } type Message struct { From string FromName string To string Subject string Attachments []string Data any DataMap map[string]any } func (m *Mail) SendSMTPMessage(msg Message) error { if msg.From == "" { msg.From = m.FromAddr } if msg.FromName == "" { msg.FromName = m.FromName } data := map[string]any{ "message": msg.Data, } msg.DataMap = data formattedMessage, err := m.buildHTMLMessage(msg) if err != nil { return err } plainMessage, err := m.buildPlainTextMessage(msg) if err != nil { return err } server := mail.NewSMTPClient() server.Host = m.Host server.Port = m.Port server.Username = m.Username server.Password = m.Password server.Encryption = m.getEncryption(m.Encryption) server.KeepAlive = false server.ConnectTimeout = 10 * time.Second server.SendTimeout = 10 * time.Second log.Println(server.Host, server.Port) smtpClient, err := server.Connect() if err != nil { return err } email := mail.NewMSG() email.SetFrom(msg.From).AddTo(msg.To).SetSubject(msg.Subject) email.SetBody(mail.TextPlain, plainMessage) email.AddAlternative(mail.TextHTML, formattedMessage) if len(msg.Attachments) > 0 { for _, x := range msg.Attachments { email.AddAttachment(x) } } err = email.Send(smtpClient) if err != nil { return err } return nil } func (m *Mail) buildPlainTextMessage(msg Message) (string, error) { templateToRender := "templates/mail.plain.gohtml" t, err := template.New("email-plain").ParseFS(emailTemplateFS, templateToRender) if err != nil { return "", err } var tpl bytes.Buffer if err = t.ExecuteTemplate(&tpl, "body", msg.DataMap); err != nil { return "", err } formattedMessage := tpl.String() return formattedMessage, nil } func (m *Mail) buildHTMLMessage(msg Message) (string, error) { templateToRender := "templates/mail.html.gohtml" t, err := template.New("email-html").ParseFS(emailTemplateFS, templateToRender) if err != nil { return "", err } var tpl bytes.Buffer if err = t.ExecuteTemplate(&tpl, "body", msg.DataMap); err != nil { return "", err } formattedMessage := tpl.String() formattedMessage, err = m.inlineCSS(formattedMessage) if err != nil { return "", err } return formattedMessage, nil } func (m *Mail) inlineCSS(s string) (string, error) { options := premailer.Options{ RemoveClasses: false, CssToAttributes: false, KeepBangImportant: true, } prem, err := premailer.NewPremailerFromString(s, &options) if err != nil { return "", err } html, err := prem.Transform() if err != nil { return "", err } return html, nil } func (m *Mail) getEncryption(s string) mail.Encryption { switch s { case "tls": return mail.EncryptionSTARTTLS case "ssl": return mail.EncryptionSSLTLS case "none": return mail.EncryptionNone default: return mail.EncryptionNone } }