package senders import ( "bytes" _ "embed" "encoding/base64" "encoding/json" "fmt" "golang.org/x/net/html" "html/template" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "strings" ) type LeadSender interface { SendLead(leadData LeadData) error Name() string } type LeadData struct { To interface{} Subject string Template string TemplateData TemplateData } type TemplateData struct { QuizConfig model.ResultInfo AnswerContent model.ResultContent AllAnswers []model.ResultAnswer QuestionsMap map[uint64]string AnswerTime string } func generateTextFromTemplate(data TemplateData, tpl string) (string, error) { t, err := template.New("email").Funcs(tmplFuncs).Parse(tpl) if err != nil { return "", fmt.Errorf("error parsing template: %w", err) } 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) } return text.String(), nil } var tmplFuncs = template.FuncMap{ "renderImage": RenderImage, } func RenderImage(content string) template.HTML { contents := strings.Split(content, "`,`") 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 { builder.WriteString(fmt.Sprintf("%s
", res.Description, res.Image)) } } return template.HTML(builder.String()) } func SplitContent(content string) template.HTML { parts := strings.Split(content, "|") if len(parts) == 2 { url := strings.TrimSpace(parts[0]) filenameBase64 := strings.TrimSpace(parts[1]) filenameBytes, err := base64.StdEncoding.DecodeString(filenameBase64) if err != nil { return template.HTML(fmt.Sprintf(`%s`, url, "invalid filename")) } filename := string(filenameBytes) return template.HTML(fmt.Sprintf(`%s`, url, filename)) } return template.HTML(strings.ReplaceAll(content, "\n", "
")) } func sanitizeHTMLData(data TemplateData) TemplateData { sanitized := TemplateData{ QuizConfig: stripHTMLResultInfo(data.QuizConfig), AnswerContent: stripHTMLResultContent(data.AnswerContent), AllAnswers: stripHTMLResultAnswers(data.AllAnswers), QuestionsMap: stripHTMLResultMap(data.QuestionsMap), AnswerTime: StripHTML(data.AnswerTime), } 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") } } } }