mirror of
https://github.com/1f349/simplemail.git
synced 2025-03-13 23:43:09 +00:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
81ac6bb21b | |||
fccaef81bc |
@ -22,7 +22,6 @@ func (f *FromAddress) UnmarshalText(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FromAddress) ToMailAddress() *mail.Address {
|
func (f *FromAddress) ToMailAddress() *mail.Address {
|
||||||
m := mail.Address(f)
|
return (*mail.Address)(f)
|
||||||
return &m
|
|
||||||
}
|
}
|
||||||
|
7
go.mod
7
go.mod
@ -3,11 +3,10 @@ module github.com/1f349/simplemail
|
|||||||
go 1.22
|
go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/1f349/overlapfs v0.0.1
|
github.com/emersion/go-message v0.18.2
|
||||||
github.com/emersion/go-message v0.18.1
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6
|
||||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43
|
|
||||||
github.com/emersion/go-smtp v0.21.3
|
github.com/emersion/go-smtp v0.21.3
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.10.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
16
go.sum
16
go.sum
@ -1,20 +1,16 @@
|
|||||||
github.com/1f349/overlapfs v0.0.1 h1:LAxBolrXFAgU0yqZtXg/C/aaPq3eoQSPpBc49BHuTp0=
|
|
||||||
github.com/1f349/overlapfs v0.0.1/go.mod h1:I6aItQycr7nrzplmfNXp/QF9tTmKRSgY3fXmu/7Ky2o=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emersion/go-message v0.18.1 h1:tfTxIoXFSFRwWaZsgnqS1DSZuGpYGzSmCZD8SK3QA2E=
|
github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg=
|
||||||
github.com/emersion/go-message v0.18.1/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
|
github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
|
||||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
|
github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
|
||||||
github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
50
mail.go
50
mail.go
@ -1,14 +1,12 @@
|
|||||||
package simplemail
|
package simplemail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"github.com/emersion/go-message/mail"
|
"github.com/emersion/go-message/mail"
|
||||||
"github.com/emersion/go-sasl"
|
"github.com/emersion/go-sasl"
|
||||||
"github.com/emersion/go-smtp"
|
"github.com/emersion/go-smtp"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mail struct {
|
type Mail struct {
|
||||||
@ -69,51 +67,17 @@ func isLocalhost(host string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mail) SendMail(subject string, to []*mail.Address, htmlBody, textBody io.Reader) error {
|
// SendMail sends a mail message to the provided mail addresses.
|
||||||
// generate the email in this template
|
//
|
||||||
buf := new(bytes.Buffer)
|
// The reader should follow the format of an RFC 822-style email.
|
||||||
|
//
|
||||||
// setup mail headers
|
// See github.com/emersion/go-smtp.SendMail
|
||||||
var h mail.Header
|
func (m *Mail) SendMail(to []*mail.Address, mailMessage io.Reader) error {
|
||||||
h.SetDate(time.Now())
|
|
||||||
h.SetSubject(subject)
|
|
||||||
h.SetAddressList("From", []*mail.Address{m.From.ToMailAddress()})
|
|
||||||
h.SetAddressList("To", to)
|
|
||||||
h.Set("Content-Type", "multipart/alternative")
|
|
||||||
|
|
||||||
// setup html and text alternative headers
|
|
||||||
var hHtml, hTxt mail.InlineHeader
|
|
||||||
hHtml.Set("Content-Type", "text/html; charset=utf-8")
|
|
||||||
hTxt.Set("Content-Type", "text/plain; charset=utf-8")
|
|
||||||
|
|
||||||
createWriter, err := mail.CreateWriter(buf, h)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
inline, err := createWriter.CreateInline()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
partHtml, err := inline.CreatePart(hHtml)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(partHtml, htmlBody); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
partTxt, err := inline.CreatePart(hTxt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(partTxt, textBody); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert all to addresses to strings
|
// convert all to addresses to strings
|
||||||
toStr := make([]string, len(to))
|
toStr := make([]string, len(to))
|
||||||
for i := range toStr {
|
for i := range toStr {
|
||||||
toStr[i] = to[i].String()
|
toStr[i] = to[i].String()
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.mailCall(toStr, buf)
|
return m.mailCall(toStr, mailMessage)
|
||||||
}
|
}
|
||||||
|
62
preparedmail.go
Normal file
62
preparedmail.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package simplemail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/emersion/go-message/mail"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PreparedMail struct {
|
||||||
|
simpleMail *SimpleMail
|
||||||
|
templateName string
|
||||||
|
Header mail.Header
|
||||||
|
rcpt []*mail.Address
|
||||||
|
data map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PreparedMail) SendMail() error {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
var bufHtml, bufTxt bytes.Buffer
|
||||||
|
p.simpleMail.render(&bufHtml, &bufTxt, p.templateName, p.data)
|
||||||
|
|
||||||
|
// setup html and text alternative headers
|
||||||
|
var hHtml, hTxt mail.InlineHeader
|
||||||
|
hHtml.Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
hTxt.Set("Content-Type", "text/plain; charset=utf-8")
|
||||||
|
|
||||||
|
createWriter, err := mail.CreateWriter(buf, p.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
inline, err := createWriter.CreateInline()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = copyToPart(inline, hHtml, &bufHtml)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = copyToPart(inline, hTxt, &bufTxt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = inline.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.simpleMail.mailSender.SendMail(p.rcpt, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyToPart(inline *mail.InlineWriter, header mail.InlineHeader, body io.Reader) error {
|
||||||
|
part, err := inline.CreatePart(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(part, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return part.Close()
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package simplemail
|
package simplemail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"github.com/emersion/go-message/mail"
|
"github.com/emersion/go-message/mail"
|
||||||
htmlTemplate "html/template"
|
htmlTemplate "html/template"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
textTemplate "text/template"
|
textTemplate "text/template"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SimpleMail struct {
|
type SimpleMail struct {
|
||||||
@ -40,8 +40,29 @@ func (m *SimpleMail) render(wrHtml, wrTxt io.Writer, name string, data any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SimpleMail) Send(templateName, subject string, to *mail.Address, data map[string]any) error {
|
// PrepareSingle constructs the headers for sending an email to the provided mail address.
|
||||||
var bufHtml, bufTxt bytes.Buffer
|
func (m *SimpleMail) PrepareSingle(templateName, subject string, to *mail.Address, data map[string]any) *PreparedMail {
|
||||||
m.render(&bufHtml, &bufTxt, templateName, data)
|
return m.PrepareMany(templateName, subject, []*mail.Address{to}, data)
|
||||||
return m.mailSender.SendMail(subject, []*mail.Address{to}, &bufHtml, &bufTxt)
|
}
|
||||||
|
|
||||||
|
// PrepareMany constructs the headers for sending an email to the provided mail addresses.
|
||||||
|
func (m *SimpleMail) PrepareMany(templateName, subject string, to []*mail.Address, data map[string]any) *PreparedMail {
|
||||||
|
p := &PreparedMail{
|
||||||
|
simpleMail: m,
|
||||||
|
templateName: templateName,
|
||||||
|
rcpt: to,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
p.Header.SetDate(time.Now())
|
||||||
|
p.Header.SetSubject(subject)
|
||||||
|
p.Header.SetAddressList("From", []*mail.Address{m.mailSender.From.ToMailAddress()})
|
||||||
|
p.Header.SetAddressList("To", to)
|
||||||
|
p.Header.Set("Content-Type", "multipart/alternative")
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send is a simplified version of PrepareSingle which sends the mail without allowing header modifications.
|
||||||
|
func (m *SimpleMail) Send(templateName, subject string, to *mail.Address, data map[string]any) error {
|
||||||
|
p := m.PrepareSingle(templateName, subject, to, data)
|
||||||
|
return p.SendMail()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user