dev: add email sender
This commit is contained in:
parent
a8e7881a8b
commit
57e6cb33dc
191
emailsender/emailsender.go
Normal file
191
emailsender/emailsender.go
Normal file
@ -0,0 +1,191 @@
|
||||
package emailsender
|
||||
|
||||
import (
|
||||
"net/mail"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"qoobing.com/gomod/email"
|
||||
"qoobing.com/gomod/email/smtp"
|
||||
"qoobing.com/gomod/log"
|
||||
)
|
||||
|
||||
// ///////////////////////////////////////
|
||||
// 发送器接口
|
||||
// ///////////////////////////////////////
|
||||
type Sender interface {
|
||||
Send(to string, name, body string) error
|
||||
SenderType() string
|
||||
SenderSupportAddressType() string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Id string `toml:"id"` //id
|
||||
Type string `toml:"type"` //必选,发送器类型
|
||||
Smtp string `toml:"smtp" ` //必选,SMTP服务器地址(含端口)
|
||||
From string `toml:"from"` //必选,发件人
|
||||
DryRun bool `toml:"dryrun"` //可选,只执行发送流程,但不真实发送
|
||||
SmtpUser string `toml:"smtpuser"` //可选,SMTP授权人地址,默认时从from中取
|
||||
SmtpPass string `toml:"smtppass"` //可选,SMTP授权人密码,默认时从from中取
|
||||
SmtpProxy string `toml:"smtpproxy"` //可选,SMTP代理地址,默认时为空
|
||||
WhiteList []string `toml:"whitelist"` //可选,发送白名单,默认为空
|
||||
BlackList []string `toml:"blacklist"` //可选,发送黑名单,默认为空
|
||||
}
|
||||
|
||||
func (cfg Config) GetId() string {
|
||||
return cfg.Id
|
||||
}
|
||||
|
||||
func (cfg Config) NewSender() (Sender, error) {
|
||||
return New(cfg), nil
|
||||
}
|
||||
|
||||
type EmailPlainAuthSender struct {
|
||||
cfg Config
|
||||
send func(to string, name, body string) error
|
||||
}
|
||||
|
||||
func New(cfg Config) *EmailPlainAuthSender {
|
||||
var (
|
||||
arrFrom = strings.Split(cfg.From, "/")
|
||||
fromName = arrFrom[0]
|
||||
fromAddr = arrFrom[1]
|
||||
fromPass = arrFrom[2]
|
||||
arrSmtp = strings.Split(cfg.Smtp, ":")
|
||||
smtpAddr = arrSmtp[0]
|
||||
smtpPort = arrSmtp[1]
|
||||
smtpUser = cfg.SmtpUser
|
||||
smtpPass = cfg.SmtpPass
|
||||
)
|
||||
if smtpUser == "" {
|
||||
smtpUser = fromAddr
|
||||
}
|
||||
if smtpPass == "" {
|
||||
smtpPass = fromPass
|
||||
}
|
||||
|
||||
type emailItem struct {
|
||||
to string
|
||||
name string
|
||||
body string
|
||||
}
|
||||
|
||||
var whiteListRegs = []*regexp.Regexp{}
|
||||
for _, r := range cfg.WhiteList {
|
||||
whiteListRegs = append(whiteListRegs, regexp.MustCompile(r))
|
||||
}
|
||||
var blackListRegs = []*regexp.Regexp{}
|
||||
for _, r := range cfg.BlackList {
|
||||
blackListRegs = append(blackListRegs, regexp.MustCompile(r))
|
||||
}
|
||||
var addressInWhiteList = func(address string) bool {
|
||||
for _, reg := range blackListRegs {
|
||||
if reg.Match([]byte(address)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(whiteListRegs) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, reg := range whiteListRegs {
|
||||
if reg.Match([]byte(address)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var emailQueue = make(chan emailItem, 1024)
|
||||
var emailSendFunc = func(to string, name, body string) (err error) {
|
||||
eml := emailItem{to, name, body}
|
||||
emailQueue <- eml
|
||||
return nil
|
||||
}
|
||||
var emailSendFuncInner = func() {
|
||||
defer func() {
|
||||
if re := recover(); re != nil {
|
||||
log.Errorf("email inner sender exit with recover: %v", re)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
eml, ok := <-emailQueue
|
||||
if !ok {
|
||||
log.Infof("emailQueue closed")
|
||||
return
|
||||
}
|
||||
log.Infof("start send email to [%s]", eml.to)
|
||||
if !addressInWhiteList(eml.to) {
|
||||
// white-blck-list
|
||||
log.Infof("send email to [%s] ignored for not in white list.", eml.to)
|
||||
continue
|
||||
} else if cfg.DryRun {
|
||||
// dryrun mode
|
||||
log.Infof("end send email to [%s]: ignored for dryrun mode.", eml.to)
|
||||
continue
|
||||
}
|
||||
// compose the message
|
||||
m := email.NewHTMLMessage(eml.name, eml.body)
|
||||
m.To = []string{eml.to}
|
||||
m.From = mail.Address{Name: fromName, Address: fromAddr}
|
||||
// send it to smtp server
|
||||
auth := smtp.PlainAuth("", smtpUser, smtpPass, smtpAddr)
|
||||
addr := smtpAddr + ":" + smtpPort
|
||||
if cfg.SmtpProxy != "" {
|
||||
addr += "|" + cfg.SmtpProxy
|
||||
}
|
||||
if err := email.Send(addr, auth, m); err != nil {
|
||||
log.Errorf("failed send email to [%s]", err.Error())
|
||||
log.Infof("to[%v],from[%s]", m.To, m.From)
|
||||
log.Infof("smtpUser[%s],smtpPass[%s],smtpAddr[%s:%s],smtpProxy[%s]",
|
||||
smtpUser, secLogPass(smtpPass), smtpAddr, smtpPort, secLogProxy(cfg.SmtpProxy))
|
||||
continue
|
||||
}
|
||||
log.Infof("success send email to [%s]", eml.to)
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
emailSendFuncInner()
|
||||
log.Infof("email inner sender exit abnormal, restart it 5s later...")
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}()
|
||||
var sender = &EmailPlainAuthSender{
|
||||
cfg: cfg,
|
||||
send: emailSendFunc,
|
||||
}
|
||||
return sender
|
||||
}
|
||||
|
||||
func (sender *EmailPlainAuthSender) Send(to string, name, body string) error {
|
||||
return sender.send(to, name, body)
|
||||
}
|
||||
|
||||
func (sender *EmailPlainAuthSender) SenderType() string {
|
||||
return "EMAIL" // SENDER_TYPE_EMAIL
|
||||
}
|
||||
|
||||
func (sender *EmailPlainAuthSender) SenderSupportAddressType() string {
|
||||
return "EMAIL_ADDRESS" // SENDER_SUPPORT_ADDRESS_TYPE_EMAIL_ADDRESS
|
||||
}
|
||||
|
||||
func secLogPass(password string) string {
|
||||
n := len(password)
|
||||
if n < 3 {
|
||||
return "*****"
|
||||
} else if n < 8 {
|
||||
return "***" + password[n-2:]
|
||||
}
|
||||
return "" + password[:1] + "**" + password[n-2:]
|
||||
}
|
||||
|
||||
func secLogProxy(proxy string) string {
|
||||
if strings.HasPrefix(proxy, "socks5://") && strings.Contains(proxy, "@") {
|
||||
arr := strings.Split(proxy, "@")
|
||||
if len(arr) == 2 {
|
||||
return arr[1]
|
||||
}
|
||||
}
|
||||
return proxy
|
||||
}
|
@ -3,10 +3,12 @@ package email
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"qoobing.com/gomod/email"
|
||||
)
|
||||
|
||||
func TestAttachment(t *testing.T) {
|
||||
m := NewMessage("Hi", "this is the body")
|
||||
m := email.NewMessage("Hi", "this is the body")
|
||||
|
||||
if err := m.AttachBuffer("test.ics", []byte("test"), false); err != nil {
|
||||
t.Fatal(err)
|
@ -22,7 +22,7 @@ func main1() {
|
||||
m.To = []string{toAddr}
|
||||
|
||||
// add attachments
|
||||
if err := m.Attach("aaa.txt"); err != nil {
|
||||
if err := m.Attach("attachment.file"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ func main2() {
|
||||
m.To = []string{toAddr}
|
||||
|
||||
// add attachments
|
||||
if err := m.Attach("aaa.txt"); err != nil {
|
||||
if err := m.Attach("attachment.file"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ func main3() {
|
||||
m.To = []string{toAddr}
|
||||
|
||||
// add attachments
|
||||
if err := m.Attach("aaa.txt"); err != nil {
|
||||
if err := m.Attach("attachment.file"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user