email/emailsender/emailsender.go
2025-06-18 18:34:51 +08:00

192 lines
4.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}