Files
tamabox/internal/mail/mail.go
T
2026-09-08 10:10:44 +08:00

139 lines
5.4 KiB
Go
Raw 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.
// Copyright 2022 E99p1ant. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package mail
import (
"bytes"
"crypto/tls"
"embed"
"fmt"
"html/template"
"strings"
"time"
"github.com/pkg/errors"
"gopkg.in/gomail.v2"
"github.com/tamakyi/TamaBox/internal/conf"
"github.com/tamakyi/TamaBox/templates"
)
func SendNewQuestionMail(email, domain string, questionID uint, questionContent string, token string) error {
params := map[string]string{
"link": fmt.Sprintf("%s/_/%s/%d?t=%s", conf.App.ExternalURL, domain, questionID, token),
"question": questionContent,
}
return sendTemplateMail(email, "【狼的提问箱】您有一个新的提问", templates.FS, "mail/new-question.html", params)
}
func SendNewQuestionMailToUser(email, domain string, questionID uint, questionContent string, token string) error {
params := map[string]string{
"link": fmt.Sprintf("%s/_/%s/%d?t=%s", conf.App.ExternalURL, domain, questionID, token),
"question": questionContent,
}
return sendTemplateMail(email, "【狼的提问箱】您发送了一个新的提问", templates.FS, "mail/new-question-to-user.html", params)
}
func SendNewAnswerMail(email, domain string, questionID uint, question, answer string, token string) error {
params := map[string]string{
"link": fmt.Sprintf("%s/_/%s/%d?t=%s", conf.App.ExternalURL, domain, questionID, token),
"question": question,
"answer": answer,
}
return sendTemplateMail(email, "【狼的提问箱】您的提问有了回复", templates.FS, "mail/new-answer.html", params)
}
func SendPasswordRecoveryMail(email, code string) error {
params := map[string]string{
"link": fmt.Sprintf("%s/recover-password?code=%s", conf.App.ExternalURL, code),
"email": email,
}
return sendTemplateMail(email, "【狼的提问箱】账号密码找回", templates.FS, "mail/password-recovery.html", params)
}
// SendNewFollowUpMail 通知提问箱主人:有人对某条已回答的提问进行了追问。
// 链接无需 token,主人登录后即可查看。
func SendNewFollowUpMail(email, domain string, questionID uint, questionContent string, followUpContent string) error {
params := map[string]string{
"link": fmt.Sprintf("%s/_/%s/%d", conf.App.ExternalURL, domain, questionID),
"question": questionContent,
"followup": followUpContent,
}
return sendTemplateMail(email, "【狼的提问箱】您的提问有了追问", templates.FS, "mail/new-follow-up.html", params)
}
// SendNewConversationMail 通知有新私聊发起(使用 token 而非 conversationID)
func SendNewConversationMail(email, askerName, domain string, token string, firstMessage string) error {
params := map[string]string{
"link": fmt.Sprintf("%s/_/%s/chat/%s", conf.App.ExternalURL, domain, token),
"asker": askerName,
"content": firstMessage,
}
return sendTemplateMail(email, "【狼的提问箱】有人发起了私聊", templates.FS, "mail/new-conversation.html", params)
}
// SendNewMessageMail 通知私聊中有新消息(使用 token 而非 conversationID)
func SendNewMessageMail(email, senderName, domain string, token string, content string) error {
params := map[string]string{
"link": fmt.Sprintf("%s/_/%s/chat/%s", conf.App.ExternalURL, domain, token),
"sender": senderName,
"content": content,
}
return sendTemplateMail(email, "【狼的提问箱】私聊新消息", templates.FS, "mail/new-message.html", params)
}
// SendNewVoteSubmissionMail 通知箱主:表单投票收到新回复(链接指向管理后台结果页)。
func SendNewVoteSubmissionMail(email, voteTitle string, voteID uint) error {
params := map[string]string{
"link": fmt.Sprintf("%s/user/votes/%d/results", conf.App.ExternalURL, voteID),
"title": voteTitle,
}
return sendTemplateMail(email, "【狼的提问箱】您的投票收到了新回复", templates.FS, "mail/new-vote.html", params)
}
// SendSecurityAlertMail 发送安全告警:检测到疑似批量扫描/探测并触发自动封禁时通知管理员。
func SendSecurityAlertMail(ip string, samplePaths []string, durationMinutes int) error {
params := map[string]string{
"ip": ip,
"paths": strings.Join(samplePaths, "、"),
"duration": fmt.Sprintf("%d", durationMinutes),
"time": time.Now().Format("2006-01-02 15:04:05"),
}
return sendTemplateMail(conf.Security.SecurityAlertEmail, "【狼的提问箱】检测到疑似批量扫描,已自动封禁 IP", templates.FS, "mail/security-alert.html", params)
}
func sendTemplateMail(email, title string, templateFS embed.FS, templatePath string, params map[string]string) error {
var content bytes.Buffer
t, err := template.ParseFS(templateFS, templatePath)
if err != nil {
return errors.Wrap(err, "parse template file")
}
params["year"] = time.Now().Format("2006")
if err := t.Execute(&content, params); err != nil {
return errors.Wrap(err, "execute template")
}
return sendMail(email, title, content.String())
}
func sendMail(to, title, content string) error {
m := gomail.NewMessage()
m.SetHeader("From", fmt.Sprintf("TamaBox <%s>", conf.Mail.Account))
m.SetHeader("To", to)
m.SetHeader("Subject", title)
m.SetBody("text/html", content)
d := gomail.NewDialer(
conf.Mail.SMTP,
conf.Mail.Port,
conf.Mail.Account,
conf.Mail.Password,
)
// 默认校验证书;仅当配置了 skip_tls_verify=true(自签名/内网 SMTP)时跳过。
if conf.Mail.SkipTLSVerify {
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
return d.DialAndSend(m)
}