-
-
Notifications
You must be signed in to change notification settings - Fork 54
Bulk Mailer Example
In this example we create a small bulk mailer for sending out the same mail to a bigger list of recipients. It is
important for us to address the recipient directly in the mail, therefore we will make use of Go's html/template
and
text/template
system together with placeholders.
Test on play.go.dev
package main
import (
"fmt"
ht "html/template"
"log"
"math/rand"
"os"
tt "text/template"
"time"
"github.com/wneessen/go-mail"
)
// User is a simple type allowing us to set a firstname, lastname and mail address
type User struct {
Firstname string
Lastname string
EmailAddr string
}
// Our sender information that will be used in the FROM address field
const (
senderName = "ACME Inc."
senderAddr = "[email protected]"
)
const (
textBodyTemplate = `Hi {{.Firstname}},
we are writing your to let you know that this week we have an amazing offer for you.
Using the coupon code "GOMAIL" you will get a 20% discount on all our products in our
online shop.
Check out our latest offer on https://acme.com and use your discount code today!
Your marketing team
at ACME Inc.`
htmlBodyTemplate = `<p>Hi {{.Firstname}},</p>
<p>we are writing your to let you know that this week we have an amazing offer for you.
Using the coupon code "<strong>GOMAIL</strong>" you will get a 20% discount on all
our products in our online shop.</p>
<p>Check out our latest offer on <a href="https://acme.com" target="_blank">https://acme.com</a>
and use your discount code today!</p>
<p>Your marketing team<br />
at ACME Inc.</p>`
)
func main() {
// Define a list of users we want to mail to
userList := []User{
{"Toni", "Tester", "[email protected]"},
{"Tina", "Tester", "[email protected]"},
{"John", "Doe", "[email protected]"},
}
// Prepare the different templates
textTpl, err := tt.New("texttpl").Parse(textBodyTemplate)
if err != nil {
log.Fatalf("failed to parse text template: %s", err)
}
htmlTpl, err := ht.New("htmltpl").Parse(htmlBodyTemplate)
if err != nil {
log.Fatalf("failed to parse text template: %s", err)
}
var messages []*mail.Msg
random := rand.New(rand.NewSource(time.Now().UnixNano()))
for _, user := range userList {
randNum := random.Int31()
message := mail.NewMsg()
if err := message.EnvelopeFrom(fmt.Sprintf("noreply+%[email protected]", randNum)); err != nil {
log.Fatalf("failed to set ENVELOPE FROM address: %s", err)
}
if err := message.FromFormat(senderName, senderAddr); err != nil {
log.Fatalf("failed to set formatted FROM address: %s", err)
}
if err := message.AddToFormat(fmt.Sprintf("%s %s", user.Firstname, user.Lastname), user.EmailAddr); err != nil {
log.Fatalf("failed to set formatted TO address: %s", err)
}
message.SetMessageID()
message.SetDate()
message.SetBulk()
message.Subject(fmt.Sprintf("%s, we have a great offer for you!", user.Firstname))
if err := message.SetBodyTextTemplate(textTpl, user); err != nil {
log.Fatalf("failed to add text template to mail body: %s", err)
}
if err := message.AddAlternativeHTMLTemplate(htmlTpl, user); err != nil {
log.Fatalf("failed to add HTML template to mail body: %s", err)
}
messages = append(messages, message)
}
// Deliver the mails via SMTP
client, err := mail.NewClient("smtp.example.com",
mail.WithSMTPAuth(mail.SMTPAuthPlain), mail.WithTLSPortPolicy(mail.TLSMandatory),
mail.WithUsername(os.Getenv("SMTP_USER")), mail.WithPassword(os.Getenv("SMTP_PASS")),
)
if err := client.DialAndSend(messages...); err != nil {
log.Fatalf("failed to deliver mail: %s", err)
}
log.Printf("Bulk mailing successfully delivered.")
}
Let's take the example apart to look at some details...
At first, in line 15, we define a new type for our users that we want to address. This is totally optional and is only done so we can easily work with a list of users and address them later on in our text template. How you handle this, is totally up to you and not mandatory for this to work.
In line 28 thru 48 we set up a simple text and HTML template mail body with placeholders
that can be used with Go's html/template
and text/template
.
Next we set up a list of users, we want to send our great bulk mailing to. Line 52 uses the User
type for this. With the preparation work done, we will start looping over all of our users in line 70.
For each user we create a new *mail.Msg
.
For bulk mailings it is common that the ENVELOPE FROM
and the MAIL FROM
differ, so that bounce mails are sent
to some system that can mark those bounces in the local system as bounced. Therefore we set both of those from
addresses in line 73 and line 76. The lines 79 to 85 should be
of no surprise to you, if you already used go-mail before.
One more interesting thing happens in lines 86 thru 91 in which we use our
prepared html/template
and text/template
templates and apply it to our mail message using
m.SetBodyTextTemplate
and m.AddAlternativeHTMLTemplate
. We provide the whole user struct as data to that
methods, so that html/template
and text/template
can take care of replacing placeholders in the mail body.
Go-mail will take care of all the bells and whistles with the template handling for you. With our mail message
now complete, we append it to our mail message slice in line 93.
Finally we create a new Client and send out all of our prepared messages in one go by
providing the whole slice of messages to Client.DialAndSend
.
Socials: go-mail on Mastodon | #go-mail on Discord | #go-mail on Slack