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) }