github.com/dhax/go-base@v0.0.0-20231004214136-8be7e5c1972b/email/email.go (about) 1 // Package email provides email sending functionality. 2 package email 3 4 import ( 5 "bytes" 6 "fmt" 7 "html/template" 8 "log" 9 "os" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/go-mail/mail" 16 "github.com/jaytaylor/html2text" 17 "github.com/spf13/viper" 18 "github.com/vanng822/go-premailer/premailer" 19 ) 20 21 var ( 22 debug bool 23 templates *template.Template 24 ) 25 26 // Mailer is a SMTP mailer. 27 type Mailer struct { 28 client *mail.Dialer 29 from Email 30 } 31 32 // NewMailer returns a configured SMTP Mailer. 33 func NewMailer() (*Mailer, error) { 34 if err := parseTemplates(); err != nil { 35 return nil, err 36 } 37 38 smtp := struct { 39 Host string 40 Port int 41 User string 42 Password string 43 }{ 44 viper.GetString("email_smtp_host"), 45 viper.GetInt("email_smtp_port"), 46 viper.GetString("email_smtp_user"), 47 viper.GetString("email_smtp_password"), 48 } 49 50 s := &Mailer{ 51 client: mail.NewPlainDialer(smtp.Host, smtp.Port, smtp.User, smtp.Password), 52 from: NewEmail(viper.GetString("email_from_name"), viper.GetString("email_from_address")), 53 } 54 55 if smtp.Host == "" { 56 log.Println("SMTP host not set => printing emails to stdout") 57 debug = true 58 return s, nil 59 } 60 61 d, err := s.client.Dial() 62 if err == nil { 63 d.Close() 64 return s, nil 65 } 66 return nil, err 67 } 68 69 // Send sends the mail via smtp. 70 func (m *Mailer) Send(email *message) error { 71 if debug { 72 log.Println("To:", email.to.Address) 73 log.Println("Subject:", email.subject) 74 log.Println(email.text) 75 return nil 76 } 77 78 msg := mail.NewMessage() 79 msg.SetAddressHeader("From", email.from.Address, email.from.Name) 80 msg.SetAddressHeader("To", email.to.Address, email.to.Name) 81 msg.SetHeader("Subject", email.subject) 82 msg.SetBody("text/plain", email.text) 83 msg.AddAlternative("text/html", email.html) 84 85 return m.client.DialAndSend(msg) 86 } 87 88 // message struct holds all parts of a specific email message. 89 type message struct { 90 from Email 91 to Email 92 subject string 93 template string 94 content interface{} 95 html string 96 text string 97 } 98 99 // parse parses the corrsponding template and content 100 func (m *message) parse() error { 101 buf := new(bytes.Buffer) 102 if err := templates.ExecuteTemplate(buf, m.template, m.content); err != nil { 103 return err 104 } 105 prem, err := premailer.NewPremailerFromString(buf.String(), premailer.NewOptions()) 106 if err != nil { 107 return err 108 } 109 110 html, err := prem.Transform() 111 if err != nil { 112 return err 113 } 114 m.html = html 115 116 text, err := html2text.FromString(html, html2text.Options{PrettyTables: true}) 117 if err != nil { 118 return err 119 } 120 m.text = text 121 return nil 122 } 123 124 // Email struct holds email address and recipient name. 125 type Email struct { 126 Name string 127 Address string 128 } 129 130 // NewEmail returns an email address. 131 func NewEmail(name string, address string) Email { 132 return Email{ 133 Name: name, 134 Address: address, 135 } 136 } 137 138 func parseTemplates() error { 139 templates = template.New("").Funcs(fMap) 140 return filepath.Walk("./templates", func(path string, info os.FileInfo, err error) error { 141 if strings.Contains(path, ".html") { 142 _, err = templates.ParseFiles(path) 143 return err 144 } 145 return err 146 }) 147 } 148 149 var fMap = template.FuncMap{ 150 "formatAsDate": formatAsDate, 151 "formatAsDuration": formatAsDuration, 152 } 153 154 func formatAsDate(t time.Time) string { 155 year, month, day := t.Date() 156 return fmt.Sprintf("%d.%d.%d", day, month, year) 157 } 158 159 func formatAsDuration(t time.Time) string { 160 dur := t.Sub(time.Now()) 161 hours := int(dur.Hours()) 162 mins := int(dur.Minutes()) 163 164 v := "" 165 if hours != 0 { 166 v += strconv.Itoa(hours) + " hours and " 167 } 168 v += strconv.Itoa(mins) + " minutes" 169 return v 170 }