mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 11:36:36 +08:00
148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
// Package service — Email(SMTP) 通知 Provider。
|
||
package service
|
||
|
||
import (
|
||
"context"
|
||
"crypto/tls"
|
||
"fmt"
|
||
"net/mail"
|
||
"net/smtp"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
// EmailProvider 通过 SMTP 发送邮件通知。
|
||
type EmailProvider struct{}
|
||
|
||
// Send 通过 SMTP 发送邮件。
|
||
func (p *EmailProvider) Send(ctx context.Context, cfg map[string]string, event NotifyEvent) error {
|
||
smtpHost := cfg["smtp_host"]
|
||
smtpPortStr := cfg["smtp_port"]
|
||
username := cfg["username"]
|
||
password := cfg["password"]
|
||
from := cfg["from"]
|
||
to := cfg["to"]
|
||
tlsStr := cfg["tls"]
|
||
|
||
if smtpHost == "" || smtpPortStr == "" || username == "" || from == "" || to == "" {
|
||
return fmt.Errorf("email: smtp_host, smtp_port, username, from, and to are required")
|
||
}
|
||
|
||
smtpPort, err := strconv.Atoi(smtpPortStr)
|
||
if err != nil {
|
||
return fmt.Errorf("email: invalid smtp_port: %s", smtpPortStr)
|
||
}
|
||
|
||
useTLS := true
|
||
if tlsStr == "false" || tlsStr == "0" || tlsStr == "no" {
|
||
useTLS = false
|
||
}
|
||
|
||
// 构建邮件内容
|
||
subject := fmt.Sprintf("[MediaStationGo] %s", event.Title)
|
||
body := event.Message
|
||
if len(event.Data) > 0 {
|
||
body += "\n\n---\n详细信息:\n"
|
||
for k, v := range event.Data {
|
||
body += fmt.Sprintf(" %s: %v\n", k, v)
|
||
}
|
||
}
|
||
|
||
recipients := strings.Split(to, ",")
|
||
for i, r := range recipients {
|
||
recipients[i] = strings.TrimSpace(r)
|
||
}
|
||
|
||
// 构建邮件
|
||
fromAddr := mail.Address{Name: "MediaStationGo", Address: from}
|
||
toAddrs := make([]mail.Address, 0, len(recipients))
|
||
for _, r := range recipients {
|
||
toAddrs = append(toAddrs, mail.Address{Address: r})
|
||
}
|
||
|
||
msg := "From: " + fromAddr.String() + "\r\n"
|
||
msg += "To: "
|
||
for i, addr := range toAddrs {
|
||
if i > 0 {
|
||
msg += ", "
|
||
}
|
||
msg += addr.String()
|
||
}
|
||
msg += "\r\n"
|
||
msg += "Subject: " + subject + "\r\n"
|
||
msg += "MIME-Version: 1.0\r\n"
|
||
msg += "Content-Type: text/plain; charset=\"utf-8\"\r\n"
|
||
msg += "Content-Transfer-Encoding: base64\r\n"
|
||
msg += "\r\n"
|
||
msg += body
|
||
|
||
addr := fmt.Sprintf("%s:%d", smtpHost, smtpPort)
|
||
auth := smtp.PlainAuth("", username, password, smtpHost)
|
||
|
||
if useTLS {
|
||
// 使用 TLS 连接
|
||
tlsConfig := &tls.Config{
|
||
ServerName: smtpHost,
|
||
MinVersion: tls.VersionTLS12,
|
||
}
|
||
conn, err := tls.Dial("tcp", addr, tlsConfig)
|
||
if err != nil {
|
||
return fmt.Errorf("email tls dial: %w", err)
|
||
}
|
||
client, err := smtp.NewClient(conn, smtpHost)
|
||
if err != nil {
|
||
return fmt.Errorf("email smtp client: %w", err)
|
||
}
|
||
defer client.Close()
|
||
|
||
if err = client.Auth(auth); err != nil {
|
||
return fmt.Errorf("email auth: %w", err)
|
||
}
|
||
if err = client.Mail(from); err != nil {
|
||
return fmt.Errorf("email mail from: %w", err)
|
||
}
|
||
for _, r := range recipients {
|
||
if err = client.Rcpt(r); err != nil {
|
||
return fmt.Errorf("email rcpt to: %w", err)
|
||
}
|
||
}
|
||
w, err := client.Data()
|
||
if err != nil {
|
||
return fmt.Errorf("email data: %w", err)
|
||
}
|
||
if _, err = w.Write([]byte(msg)); err != nil {
|
||
return fmt.Errorf("email write: %w", err)
|
||
}
|
||
if err = w.Close(); err != nil {
|
||
return fmt.Errorf("email close: %w", err)
|
||
}
|
||
return client.Quit()
|
||
}
|
||
|
||
// 不使用 TLS(STARTTLS 或明文)
|
||
return smtp.SendMail(addr, auth, from, recipients, []byte(msg))
|
||
}
|
||
|
||
// ValidateConfig 验证 Email 配置。
|
||
func (p *EmailProvider) ValidateConfig(cfg map[string]string) error {
|
||
if cfg["smtp_host"] == "" {
|
||
return fmt.Errorf("email: smtp_host is required")
|
||
}
|
||
if cfg["smtp_port"] == "" {
|
||
return fmt.Errorf("email: smtp_port is required")
|
||
}
|
||
if cfg["username"] == "" {
|
||
return fmt.Errorf("email: username is required")
|
||
}
|
||
if cfg["from"] == "" {
|
||
return fmt.Errorf("email: from is required")
|
||
}
|
||
if cfg["to"] == "" {
|
||
return fmt.Errorf("email: to is required")
|
||
}
|
||
if _, err := strconv.Atoi(cfg["smtp_port"]); err != nil {
|
||
return fmt.Errorf("email: invalid smtp_port")
|
||
}
|
||
return nil
|
||
}
|