moved email-tester from gitlab to gitlab tools
This commit is contained in:
parent
4a2f581a40
commit
e9043bc198
1
email-tester/.gitignore
vendored
Normal file
1
email-tester/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.idea/
|
11
email-tester/README.md
Normal file
11
email-tester/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# email-tester
|
||||||
|
|
||||||
|
нужен для тестирования текущей логики отправки формы, все что нужно это запустить в терминале проект, вписать тип события - toClient или reminder, а также что config.yaml находящийся в проекте в той же директории заполенен и все выходящие из него штуки, таки как шаблоны и файл json должны быть заполены
|
||||||
|
|
||||||
|
процесс работы:
|
||||||
|
- установить конфигурационный файл, пример в проекте
|
||||||
|
- поставить шаблоны, пример в проекте
|
||||||
|
- при запуске указать тип работы, toClient или reminder
|
||||||
|
- после этого произойдет логика работы с шаблоном и проведется отправка на почту данных
|
||||||
|
- далее информация в консоль выведется, если произойдет ошибка в консоли выведется
|
||||||
|
- в конце нужно прописать exit чтобы выйти из текущего сеанса и консоли, далее запуск снова и т.д.
|
121
email-tester/client/mail.go
Normal file
121
email-tester/client/mail.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"mime/multipart"
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Deps struct {
|
||||||
|
SmtpApiUrl string
|
||||||
|
SmtpSender string
|
||||||
|
ApiKey string
|
||||||
|
FiberClient *fiber.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type SmtpClient struct {
|
||||||
|
smtpApiUrl string
|
||||||
|
smtpSender string
|
||||||
|
apiKey string
|
||||||
|
fiberClient *fiber.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSmtpClient(deps Deps) *SmtpClient {
|
||||||
|
if deps.FiberClient == nil {
|
||||||
|
deps.FiberClient = fiber.AcquireClient()
|
||||||
|
}
|
||||||
|
return &SmtpClient{
|
||||||
|
smtpApiUrl: deps.SmtpApiUrl,
|
||||||
|
smtpSender: deps.SmtpSender,
|
||||||
|
apiKey: deps.ApiKey,
|
||||||
|
fiberClient: deps.FiberClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
To string
|
||||||
|
Subject string
|
||||||
|
HtmlBody string
|
||||||
|
Attachments []Attachment
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attachment struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
// data в base64 это файл
|
||||||
|
Data string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TemplateData struct {
|
||||||
|
QuizConfig model.ResultInfo
|
||||||
|
AnswerContent model.ResultContent
|
||||||
|
AllAnswers []model.ResultAnswer
|
||||||
|
QuestionsMap map[uint64]string
|
||||||
|
AnswerTime string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SmtpClient) MailSender(data Message) error {
|
||||||
|
form := new(bytes.Buffer)
|
||||||
|
writer := multipart.NewWriter(form)
|
||||||
|
|
||||||
|
fields := map[string]string{
|
||||||
|
"from": m.smtpSender,
|
||||||
|
"to": data.To,
|
||||||
|
"subject": data.Subject,
|
||||||
|
"html": data.HtmlBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Attachments != nil && len(data.Attachments) > 0 {
|
||||||
|
attachmentJson, err := json.Marshal(data.Attachments)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fields["files"] = string(attachmentJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range fields {
|
||||||
|
if err := writer.WriteField(key, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := m.fiberClient.Post(m.smtpApiUrl).Body(form.Bytes()).ContentType(writer.FormDataContentType())
|
||||||
|
if m.apiKey != "" {
|
||||||
|
req.Set("Authorization", m.apiKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
statusCode, body, errs := req.Bytes()
|
||||||
|
if errs != nil {
|
||||||
|
return errs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusCode != fiber.StatusOK {
|
||||||
|
err := fmt.Errorf("the SMTP service returned an error: %d Response body: %s", statusCode, body)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SmtpClient) SendMailWithAttachment(recipient, subject string, emailTemplate string, data TemplateData, attachments []Attachment) error {
|
||||||
|
sanitizedData := sanitizeHTMLData(data)
|
||||||
|
text, err := generateTextFromTemplate(sanitizedData, emailTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := Message{
|
||||||
|
To: recipient,
|
||||||
|
Subject: subject,
|
||||||
|
HtmlBody: text,
|
||||||
|
Attachments: attachments,
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.MailSender(msg)
|
||||||
|
}
|
144
email-tester/client/tools.go
Normal file
144
email-tester/client/tools.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
"html/template"
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/model"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
var res model.ImageContent
|
||||||
|
err := json.Unmarshal([]byte(content), &res)
|
||||||
|
if err != nil {
|
||||||
|
return SplitContent(content)
|
||||||
|
}
|
||||||
|
tpl := template.HTML(fmt.Sprintf("<td>%s<br><img class=\"image\" style=\"width:100%%; max-width:250px; max-height:250px\" src=\"%s\"/></td>", res.Description, res.Image))
|
||||||
|
return tpl
|
||||||
|
}
|
||||||
|
|
||||||
|
func SplitContent(content string) template.HTML {
|
||||||
|
parts := strings.Split(content, "|")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
url := strings.TrimSpace(parts[0])
|
||||||
|
filename := strings.TrimSpace(parts[1])
|
||||||
|
return template.HTML(fmt.Sprintf(`<a href="%s" download>%s</a>`, url, filename))
|
||||||
|
}
|
||||||
|
return template.HTML(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
email-tester/config.yaml
Normal file
10
email-tester/config.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
apiUrl: "https://api.smtp.bz/v1/smtp/send"
|
||||||
|
apiKey: "P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev"
|
||||||
|
subject: "test"
|
||||||
|
sender: "noreply@mailing.pena.digital"
|
||||||
|
to: "pashamullin2001@gmail.com"
|
||||||
|
pathToTemplateData: "template/templateData.json"
|
||||||
|
pathToReminderTemplate: "template/reminder_base.tmpl"
|
||||||
|
pathToToClientTemplate: "template/to_client_base.tmpl"
|
||||||
|
|
||||||
|
|
23
email-tester/go.mod
Normal file
23
email-tester/go.mod
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
module penahub.gitlab.yandexcloud.net/pena-services/email-tester
|
||||||
|
|
||||||
|
go 1.22.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.5
|
||||||
|
golang.org/x/net v0.17.0
|
||||||
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
|
github.com/google/uuid v1.5.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||||
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
|
)
|
33
email-tester/go.sum
Normal file
33
email-tester/go.sum
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||||
|
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
||||||
|
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||||
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
81
email-tester/main.go
Normal file
81
email-tester/main.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/model"
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/senders"
|
||||||
|
)
|
||||||
|
|
||||||
|
type event string
|
||||||
|
|
||||||
|
var toClient event = "toClient"
|
||||||
|
var reminder event = "reminder"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Need event type 'toClient' or 'reminder':")
|
||||||
|
var eventFlag string
|
||||||
|
fmt.Scanln(&eventFlag)
|
||||||
|
defer func() {
|
||||||
|
fmt.Println("if you want exit write 'exit'")
|
||||||
|
var exit string
|
||||||
|
fmt.Scanln(&exit)
|
||||||
|
if exit == "exit" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
config, err := loadConfig("config.yaml")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = config.Validate()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed validate config:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch eventFlag {
|
||||||
|
case string(toClient):
|
||||||
|
fmt.Println("Processing 'toClient' event")
|
||||||
|
err = senders.ToClientTesting(config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error process task for toClient test:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("OK check mailbox")
|
||||||
|
case string(reminder):
|
||||||
|
fmt.Println("Processing 'reminder' event")
|
||||||
|
err = senders.ReminderTesting(config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error process task for reminder test:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("OK check mailbox")
|
||||||
|
default:
|
||||||
|
fmt.Println("No event type or unknown event type specified need 'toClient' or 'reminder'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(filename string) (model.Config, error) {
|
||||||
|
var config model.Config
|
||||||
|
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(data, &config)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
81
email-tester/model/model.go
Normal file
81
email-tester/model/model.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ApiURL string `yaml:"apiUrl"`
|
||||||
|
ApiKey string `yaml:"apiKey"`
|
||||||
|
Subject string `yaml:"subject"`
|
||||||
|
Sender string `yaml:"sender"`
|
||||||
|
PathToTemplateData string `yaml:"pathToTemplateData"`
|
||||||
|
PathToReminderTemplate string `yaml:"pathToReminderTemplate"`
|
||||||
|
PathToToClientTemplate string `yaml:"pathToToClientTemplate"`
|
||||||
|
To string `yaml:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Validate() error {
|
||||||
|
if c.ApiURL == "" {
|
||||||
|
return errors.New("ApiURL dont be nil")
|
||||||
|
}
|
||||||
|
if c.ApiKey == "" {
|
||||||
|
return errors.New("ApiKey dont be nil")
|
||||||
|
}
|
||||||
|
if c.Subject == "" {
|
||||||
|
return errors.New("Subject dont be nil")
|
||||||
|
}
|
||||||
|
if c.Sender == "" {
|
||||||
|
return errors.New("Sender dont be nil")
|
||||||
|
}
|
||||||
|
if c.PathToTemplateData == "" {
|
||||||
|
return errors.New("PathToTemplateData dont be nil")
|
||||||
|
}
|
||||||
|
if c.PathToReminderTemplate == "" {
|
||||||
|
return errors.New("PathToReminderTemplate dont be nil")
|
||||||
|
}
|
||||||
|
if c.PathToToClientTemplate == "" {
|
||||||
|
return errors.New("PathToToClientTemplate dont be nil")
|
||||||
|
}
|
||||||
|
if c.To == "" {
|
||||||
|
return errors.New("To dont be nil")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultInfo struct {
|
||||||
|
When string `json:"when"` // before|after|email
|
||||||
|
Theme string `json:"theme"` // тема письма
|
||||||
|
Reply string `json:"reply"` // email для ответов, указывается в создании письма
|
||||||
|
ReplName string `json:"repl_name"` // имя отправителя
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultContent struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Telegram string `json:"telegram"`
|
||||||
|
Wechat string `json:"wechat"`
|
||||||
|
Viber string `json:"viber"`
|
||||||
|
Vk string `json:"vk"`
|
||||||
|
Skype string `json:"skype"`
|
||||||
|
Whatsup string `json:"whatsup"`
|
||||||
|
Messenger string `json:"messenger"`
|
||||||
|
Custom map[string]string `json:"customs"`
|
||||||
|
Start bool `json:"start"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultAnswer struct {
|
||||||
|
Content string
|
||||||
|
CreatedAt time.Time
|
||||||
|
QuestionID uint64
|
||||||
|
AnswerID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageContent struct {
|
||||||
|
Description string
|
||||||
|
Image string
|
||||||
|
}
|
30
email-tester/senders/reminder.go
Normal file
30
email-tester/senders/reminder.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package senders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/client"
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReminderTesting(config model.Config) error {
|
||||||
|
smtpClient := client.NewSmtpClient(client.Deps{
|
||||||
|
SmtpApiUrl: config.ApiURL,
|
||||||
|
SmtpSender: config.Sender,
|
||||||
|
ApiKey: config.ApiKey,
|
||||||
|
})
|
||||||
|
|
||||||
|
templateData, err := parseTemplateData(config.PathToTemplateData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := loadTemplate(config.PathToReminderTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = smtpClient.SendMailWithAttachment(config.To, config.Subject, body, templateData, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
56
email-tester/senders/toClient.go
Normal file
56
email-tester/senders/toClient.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package senders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/client"
|
||||||
|
"penahub.gitlab.yandexcloud.net/pena-services/email-tester/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToClientTesting(config model.Config) error {
|
||||||
|
smtpClient := client.NewSmtpClient(client.Deps{
|
||||||
|
SmtpApiUrl: config.ApiURL,
|
||||||
|
SmtpSender: config.Sender,
|
||||||
|
ApiKey: config.ApiKey,
|
||||||
|
})
|
||||||
|
|
||||||
|
templateData, err := parseTemplateData(config.PathToTemplateData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := loadTemplate(config.PathToToClientTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = smtpClient.SendMailWithAttachment(config.To, config.Subject, body, templateData, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTemplateData(filePath string) (client.TemplateData, error) {
|
||||||
|
var data client.TemplateData
|
||||||
|
|
||||||
|
file, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(file, &data)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadTemplate(filePath string) (string, error) {
|
||||||
|
fileContent, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(fileContent), nil
|
||||||
|
}
|
191
email-tester/template/reminder_base.tmpl
Normal file
191
email-tester/template/reminder_base.tmpl
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
/* Сброс стилей */
|
||||||
|
body,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
p,
|
||||||
|
div,
|
||||||
|
img,
|
||||||
|
button,
|
||||||
|
table,
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #f2f2f7;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 25px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance {
|
||||||
|
font-size: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
max-width: 223px;
|
||||||
|
height: 208px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="background-color: #f2f2f7; font-family: Arial, sans-serif">
|
||||||
|
<table style="width: 100%; padding: 16px">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img class="image" style="width: 103px; height: 40px" src="https://storage.yandexcloud.net/squizimages/logo-email-squiz.png" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p style="text-align: end; color: #9a9aaf; font-size: 14px">Квиз для вашего бизнеса</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<h1
|
||||||
|
style="
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 13px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 50px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Поступила новая заявка с квиза “{{ .QuizConfig.Theme }}”!
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<p class="balance" style="color: #4d4d4d; font-size: 20px; margin-bottom: 30px">
|
||||||
|
Но у вас закончились средства на балансе :(
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="text-align: center; padding: 0">
|
||||||
|
<img class="image" style="width: 100%; max-width: 440px; height: 280px; margin-bottom: 40px" src="https://storage.yandexcloud.net/squizimages/img_wallet.png" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<h1
|
||||||
|
style="font-size: 25px; font-weight: 600; margin-bottom: 15px; width: 100%; margin: 0; margin-bottom: 13px"
|
||||||
|
>
|
||||||
|
Аккаунт
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="padding: 0">
|
||||||
|
<table
|
||||||
|
style="
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: left;
|
||||||
|
max-width: 480px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
</th>
|
||||||
|
<td style="word-break: break-word">
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #7e2aea;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .QuizConfig.Reply }}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<p class="balance" style="color: #9a9aaf; font-size: 20px; margin-bottom: 30px; text-align: center">
|
||||||
|
Пополните баланс и посмотрите заявку в личном кабинете:
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%; text-align: center">
|
||||||
|
<a
|
||||||
|
style="
|
||||||
|
max-width: 312px;
|
||||||
|
color: #f2f3f7;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #7e2aea;
|
||||||
|
background: #7e2aea;
|
||||||
|
padding: 10px 43px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Посмотреть в личном кабинете
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="text-align: center; padding: 30px 0 0 0">
|
||||||
|
<hr style="border-top: 2px solid rgba(126, 42, 234, 0.2); margin: 0 0 10px" />
|
||||||
|
<a style="color: #7e2aea; font-size: 20px; font-style: normal; font-weight: 400; line-height: normal">
|
||||||
|
quiz.pena.digital
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
57
email-tester/template/templateData.json
Normal file
57
email-tester/template/templateData.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"QuizConfig": {
|
||||||
|
"when": "test@example.com",
|
||||||
|
"theme": "Taemplste Quiz",
|
||||||
|
"reply": "test@example.com",
|
||||||
|
"repl_name": "test@example.com"
|
||||||
|
},
|
||||||
|
"AnswerContent": {
|
||||||
|
"text": "",
|
||||||
|
"name": "test",
|
||||||
|
"email": "test@example.com",
|
||||||
|
"phone": "+723456789",
|
||||||
|
"address": "",
|
||||||
|
"telegram": "@test",
|
||||||
|
"wechat": "test_wechat",
|
||||||
|
"viber": "+723456789",
|
||||||
|
"vk": "test_vk",
|
||||||
|
"skype": "test_skype",
|
||||||
|
"whatsup": "test_whatsup",
|
||||||
|
"messenger": "test_messenger",
|
||||||
|
"customs": {},
|
||||||
|
"start": false
|
||||||
|
},
|
||||||
|
"AllAnswers": [
|
||||||
|
{
|
||||||
|
"Content": "https://www.google.com/search?q=ku,n",
|
||||||
|
"CreatedAt": "2024-07-05T18:54:37Z",
|
||||||
|
"QuestionID": 1,
|
||||||
|
"AnswerID": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Content": "From a friend",
|
||||||
|
"CreatedAt": "2024-07-05T18:54:37Z",
|
||||||
|
"QuestionID": 2,
|
||||||
|
"AnswerID": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Content": "From a friend",
|
||||||
|
"CreatedAt": "2024-07-05T18:54:37Z",
|
||||||
|
"QuestionID": 3,
|
||||||
|
"AnswerID": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Content": "{\"Image\": \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTZvq8kZl7QhYC_7f0jMlepfnLkr8Y94tJY1g&s\", \"Description\": \"Gekon\"}",
|
||||||
|
"CreatedAt": "2024-07-05T18:54:37Z",
|
||||||
|
"QuestionID": 4,
|
||||||
|
"AnswerID": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"QuestionsMap": {
|
||||||
|
"1": "?",
|
||||||
|
"2": "How did you hear about us?",
|
||||||
|
"3": "que 3",
|
||||||
|
"4": "que 4"
|
||||||
|
},
|
||||||
|
"AnswerTime": "2024-07-05T18:54:37Z"
|
||||||
|
}
|
537
email-tester/template/to_client_base.tmpl
Normal file
537
email-tester/template/to_client_base.tmpl
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
/* Сброс стилей */
|
||||||
|
body,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
p,
|
||||||
|
div,
|
||||||
|
img,
|
||||||
|
button,
|
||||||
|
table,
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #f2f2f7;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 25px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 20px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="background-color: #f2f2f7; font-family: Arial, sans-serif">
|
||||||
|
<table style="width: 100%; padding: 16px">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img class="image" style="width: 103px; height: 40px" src="https://storage.yandexcloud.net/squizimages/logo-email-squiz.png" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p style="text-align: end; color: #9a9aaf; font-size: 14px">Квиз для вашего бизнеса</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<h1
|
||||||
|
style="
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 13px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 13px;
|
||||||
|
margin-top: 50px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Поступила новая заявка с квиза “{{.QuizConfig.Theme}}”!
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<p style="color: #9a9aaf; font-size: 20px; margin-bottom: 50px">
|
||||||
|
Время заявки: {{ .AnswerTime }}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<a
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
color: #f2f3f7;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #7e2aea;
|
||||||
|
background: #7e2aea;
|
||||||
|
padding: 10px 43px;
|
||||||
|
max-height: 63px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Посмотреть в личном кабинете
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<h1
|
||||||
|
style="font-size: 25px; font-weight: 600; margin-bottom: 15px; width: 100%; margin: 0; margin-bottom: 13px"
|
||||||
|
>
|
||||||
|
Контакты
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="padding: 0">
|
||||||
|
<table
|
||||||
|
style="
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: left;
|
||||||
|
max-width: 480px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Имя
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Name}}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ if .AnswerContent.Email }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
</th>
|
||||||
|
<td style="word-break: break-word">
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #7e2aea;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Email }}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Phone }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Телефон
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Phone }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if .AnswerContent.Telegram }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Telegram
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Telegram }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Wechat }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Wechat
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Wechat }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Viber }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Viber
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Viber }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Vk }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Vk
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Vk }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Skype }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Skype
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Skype }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Whatsup }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Whatsup
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Whatsup }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Messenger }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Messenger
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Messenger }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .AnswerContent.Address }}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Адрес
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ .AnswerContent.Address }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ range $key, $value := .AnswerContent.Custom }}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #9a9aaf;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ $key }}
|
||||||
|
</th>
|
||||||
|
<td
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ $value }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="height: 100%">
|
||||||
|
<h1
|
||||||
|
style="font-size: 25px; font-weight: 600; margin-bottom: 15px; width: 100%; margin: 0; margin-bottom: 13px"
|
||||||
|
>
|
||||||
|
Ответы
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{ range .AllAnswers }}
|
||||||
|
{{ if index $.QuestionsMap .AnswerID }}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="padding: 0">
|
||||||
|
<table
|
||||||
|
style="
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: left;
|
||||||
|
max-width: 480px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
text-align: start;
|
||||||
|
color: #4d4d4d;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ index $.QuestionsMap .AnswerID }}
|
||||||
|
</p>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="color: #9a9aaf; font-size: 20px; font-style: normal; font-weight: 400; line-height: normal">
|
||||||
|
{{ renderImage .Content }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" style="text-align: center; padding: 0">
|
||||||
|
<a style="color: #7e2aea; font-size: 20px; font-style: normal; font-weight: 400; line-height: normal">
|
||||||
|
quiz.pena.digital
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user