2024-06-11 18:50:35 +00:00
|
|
|
package senders
|
2024-02-19 18:20:09 +00:00
|
|
|
|
|
|
|
import (
|
2024-03-28 12:03:41 +00:00
|
|
|
"bytes"
|
2024-06-11 18:50:35 +00:00
|
|
|
_ "embed"
|
2024-09-17 10:34:07 +00:00
|
|
|
"encoding/base64"
|
2024-03-27 15:29:50 +00:00
|
|
|
"encoding/json"
|
2024-02-19 18:20:09 +00:00
|
|
|
"fmt"
|
2024-03-28 12:03:41 +00:00
|
|
|
"golang.org/x/net/html"
|
2024-03-27 15:29:50 +00:00
|
|
|
"html/template"
|
2025-02-27 13:04:47 +00:00
|
|
|
"gitea.pena/SQuiz/common/model"
|
2024-03-28 12:03:41 +00:00
|
|
|
"strings"
|
2024-02-19 18:20:09 +00:00
|
|
|
)
|
|
|
|
|
2024-06-11 18:50:35 +00:00
|
|
|
type LeadSender interface {
|
|
|
|
SendLead(leadData LeadData) error
|
|
|
|
Name() string
|
2024-02-19 18:20:09 +00:00
|
|
|
}
|
|
|
|
|
2024-06-11 18:50:35 +00:00
|
|
|
type LeadData struct {
|
|
|
|
To interface{}
|
|
|
|
Subject string
|
|
|
|
Template string
|
|
|
|
TemplateData TemplateData
|
2024-02-19 18:20:09 +00:00
|
|
|
}
|
|
|
|
|
2024-06-11 18:50:35 +00:00
|
|
|
type TemplateData struct {
|
|
|
|
QuizConfig model.ResultInfo
|
|
|
|
AnswerContent model.ResultContent
|
|
|
|
AllAnswers []model.ResultAnswer
|
|
|
|
QuestionsMap map[uint64]string
|
|
|
|
AnswerTime string
|
2025-05-15 17:21:49 +00:00
|
|
|
QuizID int64
|
2024-02-19 18:20:09 +00:00
|
|
|
}
|
|
|
|
|
2024-06-11 18:50:35 +00:00
|
|
|
func generateTextFromTemplate(data TemplateData, tpl string) (string, error) {
|
2024-06-24 10:31:08 +00:00
|
|
|
t, err := template.New("email").Funcs(tmplFuncs).Parse(tpl)
|
2024-02-19 18:20:09 +00:00
|
|
|
if err != nil {
|
2024-06-11 18:50:35 +00:00
|
|
|
return "", fmt.Errorf("error parsing template: %w", err)
|
2024-02-19 18:20:09 +00:00
|
|
|
}
|
2024-06-11 18:50:35 +00:00
|
|
|
|
|
|
|
var text bytes.Buffer
|
|
|
|
if err := t.Execute(&text, TemplateData{
|
|
|
|
QuizConfig: data.QuizConfig,
|
|
|
|
AnswerContent: data.AnswerContent,
|
|
|
|
AllAnswers: data.AllAnswers,
|
|
|
|
QuestionsMap: data.QuestionsMap,
|
|
|
|
AnswerTime: data.AnswerTime,
|
|
|
|
}); err != nil {
|
|
|
|
return "", fmt.Errorf("error executing template: %w", err)
|
2024-02-19 18:20:09 +00:00
|
|
|
}
|
2024-06-11 18:50:35 +00:00
|
|
|
|
|
|
|
return text.String(), nil
|
2024-02-19 18:20:09 +00:00
|
|
|
}
|
2024-03-27 15:29:50 +00:00
|
|
|
|
|
|
|
var tmplFuncs = template.FuncMap{
|
|
|
|
"renderImage": RenderImage,
|
|
|
|
}
|
|
|
|
|
|
|
|
func RenderImage(content string) template.HTML {
|
2024-09-17 10:34:07 +00:00
|
|
|
contents := strings.Split(content, "`,`")
|
2025-04-01 22:44:59 +00:00
|
|
|
var builder strings.Builder
|
|
|
|
|
|
|
|
for i, cnt := range contents {
|
|
|
|
if i == 0 {
|
|
|
|
cnt = strings.TrimPrefix(cnt, "`")
|
|
|
|
}
|
|
|
|
if i == len(contents)-1 {
|
|
|
|
cnt = strings.TrimSuffix(cnt, "`")
|
|
|
|
}
|
|
|
|
|
|
|
|
var res model.ImageContent
|
|
|
|
err := json.Unmarshal([]byte(cnt), &res)
|
|
|
|
if err != nil {
|
|
|
|
return SplitContent(content)
|
|
|
|
} else {
|
2025-04-02 21:58:43 +00:00
|
|
|
builder.WriteString(fmt.Sprintf("<td>%s<br><img class=\"image\" style=\"width:100%%; max-width:250px; max-height:250px\" src=\"%s\"/></td>", res.Description, strings.Replace(res.Image,"http","https",1)))
|
2025-04-01 22:44:59 +00:00
|
|
|
}
|
2024-03-27 15:29:50 +00:00
|
|
|
}
|
2025-04-01 22:44:59 +00:00
|
|
|
|
|
|
|
return template.HTML(builder.String())
|
2024-03-27 15:29:50 +00:00
|
|
|
}
|
2024-03-28 12:03:41 +00:00
|
|
|
|
2024-03-28 12:28:18 +00:00
|
|
|
func SplitContent(content string) template.HTML {
|
|
|
|
parts := strings.Split(content, "|")
|
|
|
|
if len(parts) == 2 {
|
|
|
|
url := strings.TrimSpace(parts[0])
|
2024-09-17 10:34:07 +00:00
|
|
|
filenameBase64 := strings.TrimSpace(parts[1])
|
|
|
|
filenameBytes, err := base64.StdEncoding.DecodeString(filenameBase64)
|
|
|
|
if err != nil {
|
|
|
|
return template.HTML(fmt.Sprintf(`<a href="%s" download>%s</a>`, url, "invalid filename"))
|
|
|
|
}
|
|
|
|
|
|
|
|
filename := string(filenameBytes)
|
|
|
|
|
2024-03-28 12:28:18 +00:00
|
|
|
return template.HTML(fmt.Sprintf(`<a href="%s" download>%s</a>`, url, filename))
|
|
|
|
}
|
2024-10-21 22:57:19 +00:00
|
|
|
return template.HTML(strings.ReplaceAll(content, "\n", "<br>"))
|
2024-03-28 12:28:18 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 10:31:08 +00:00
|
|
|
func sanitizeHTMLData(data TemplateData) TemplateData {
|
|
|
|
sanitized := TemplateData{
|
2024-03-28 12:03:41 +00:00
|
|
|
QuizConfig: stripHTMLResultInfo(data.QuizConfig),
|
|
|
|
AnswerContent: stripHTMLResultContent(data.AnswerContent),
|
|
|
|
AllAnswers: stripHTMLResultAnswers(data.AllAnswers),
|
|
|
|
QuestionsMap: stripHTMLResultMap(data.QuestionsMap),
|
|
|
|
AnswerTime: StripHTML(data.AnswerTime),
|
2024-10-10 18:48:16 +00:00
|
|
|
QuizID: data.QuizID,
|
2024-03-28 12:03:41 +00:00
|
|
|
}
|
|
|
|
return sanitized
|
|
|
|
}
|
|
|
|
|
|
|
|
func stripHTMLResultInfo(input model.ResultInfo) model.ResultInfo {
|
|
|
|
return model.ResultInfo{
|
|
|
|
When: StripHTML(input.When),
|
|
|
|
Theme: StripHTML(input.Theme),
|
|
|
|
Reply: StripHTML(input.Reply),
|
|
|
|
ReplName: StripHTML(input.ReplName),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func stripHTMLResultContent(input model.ResultContent) model.ResultContent {
|
|
|
|
return model.ResultContent{
|
|
|
|
Text: StripHTML(input.Text),
|
|
|
|
Name: StripHTML(input.Name),
|
|
|
|
Email: StripHTML(input.Email),
|
|
|
|
Phone: StripHTML(input.Phone),
|
|
|
|
Address: StripHTML(input.Address),
|
|
|
|
Telegram: StripHTML(input.Telegram),
|
|
|
|
Wechat: StripHTML(input.Wechat),
|
|
|
|
Viber: StripHTML(input.Viber),
|
|
|
|
Vk: StripHTML(input.Vk),
|
|
|
|
Skype: StripHTML(input.Skype),
|
|
|
|
Whatsup: StripHTML(input.Whatsup),
|
|
|
|
Messenger: StripHTML(input.Messenger),
|
|
|
|
Custom: stripHTMLCustom(input.Custom),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func stripHTMLResultAnswers(answers []model.ResultAnswer) []model.ResultAnswer {
|
|
|
|
sanitized := make([]model.ResultAnswer, len(answers))
|
|
|
|
for i, j := range answers {
|
|
|
|
sanitized[i] = model.ResultAnswer{
|
|
|
|
Content: StripHTML(j.Content),
|
|
|
|
CreatedAt: j.CreatedAt,
|
|
|
|
QuestionID: j.QuestionID,
|
|
|
|
AnswerID: j.AnswerID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sanitized
|
|
|
|
}
|
|
|
|
|
|
|
|
func stripHTMLResultMap(questionsMap map[uint64]string) map[uint64]string {
|
|
|
|
sanitized := make(map[uint64]string)
|
|
|
|
for i, j := range questionsMap {
|
|
|
|
sanitized[i] = StripHTML(j)
|
|
|
|
}
|
|
|
|
return sanitized
|
|
|
|
}
|
|
|
|
|
|
|
|
func stripHTMLCustom(custom map[string]string) map[string]string {
|
|
|
|
sanitized := make(map[string]string)
|
|
|
|
for i, j := range custom {
|
|
|
|
sanitized[i] = StripHTML(j)
|
|
|
|
}
|
|
|
|
return sanitized
|
|
|
|
}
|
|
|
|
|
|
|
|
func StripHTML(htmlString string) string {
|
|
|
|
tokenizer := html.NewTokenizer(bytes.NewBufferString(htmlString))
|
|
|
|
var result bytes.Buffer
|
|
|
|
for {
|
|
|
|
tokenType := tokenizer.Next()
|
|
|
|
switch tokenType {
|
|
|
|
case html.ErrorToken:
|
|
|
|
return strings.TrimSpace(result.String())
|
|
|
|
case html.TextToken:
|
|
|
|
result.WriteString(tokenizer.Token().Data)
|
|
|
|
result.WriteString("\n")
|
|
|
|
case html.StartTagToken, html.EndTagToken:
|
|
|
|
tagName, _ := tokenizer.TagName()
|
|
|
|
if string(tagName) == "a" {
|
|
|
|
_, attrVal, _ := tokenizer.TagAttr()
|
|
|
|
result.WriteString(string(attrVal))
|
|
|
|
result.WriteString("\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|