Merge branch 'staging' into gigachat
This commit is contained in:
commit
b3bdc9b0f7
24
.gitea/workflows/deploy.yml
Normal file
24
.gitea/workflows/deploy.yml
Normal file
@ -0,0 +1,24 @@
|
||||
name: Deploy
|
||||
run-name: ${{ gitea.actor }} build image and push to container registry
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'staging'
|
||||
|
||||
jobs:
|
||||
CreateImage:
|
||||
runs-on: [squizstaging]
|
||||
uses: http://gitea.pena/PenaDevops/actions.git/.gitea/workflows/build-image.yml@v1.1.6-p
|
||||
with:
|
||||
runner: squizstaging
|
||||
secrets:
|
||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
DeployService:
|
||||
runs-on: [squizstaging]
|
||||
needs: CreateImage
|
||||
uses: http://gitea.pena/PenaDevops/actions.git/.gitea/workflows/deploy.yml@v1.1.7
|
||||
with:
|
||||
runner: squizstaging
|
@ -9,6 +9,6 @@ on:
|
||||
jobs:
|
||||
Lint:
|
||||
runs-on: [hubstaging]
|
||||
uses: http://gitea.pena/PenaDevops/actions.git/.gitea/workflows/lint.yml@v1.1.0
|
||||
uses: http://gitea.pena/PenaDevops/actions.git/.gitea/workflows/lint.yml@v1.1.2
|
||||
with:
|
||||
runner: hubstaging
|
||||
|
@ -1,375 +0,0 @@
|
||||
package answerwc
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"gitea.pena/SQuiz/worker/clients/customer"
|
||||
"gitea.pena/SQuiz/worker/clients/mailclient"
|
||||
"gitea.pena/SQuiz/worker/wctools"
|
||||
"github.com/themakers/hlog"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
type DepsSendToClient struct {
|
||||
Redis *redis.Client
|
||||
Dal *dal.DAL
|
||||
MailClient *mailclient.Client
|
||||
CustomerService customer.CustomerServiceClient
|
||||
}
|
||||
|
||||
type SendToClient struct {
|
||||
deps DepsSendToClient
|
||||
logger hlog.Logger
|
||||
errChan chan<- error
|
||||
}
|
||||
|
||||
type PendingTasks struct {
|
||||
Count int64
|
||||
QuizConfig model.QuizConfig
|
||||
}
|
||||
|
||||
//go:embed mail/to_client.tmpl
|
||||
var toClientTemplate string
|
||||
|
||||
//go:embed mail/reminder.tmpl
|
||||
var reminderTemplate string
|
||||
|
||||
func NewSendToClient(deps DepsSendToClient, logger hlog.Logger, errChan chan<- error) *SendToClient {
|
||||
return &SendToClient{
|
||||
deps: deps,
|
||||
logger: logger,
|
||||
errChan: errChan,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) Start(ctx context.Context) {
|
||||
answerTicker := time.NewTicker(30 * time.Second)
|
||||
defer answerTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-answerTicker.C:
|
||||
w.processPendingAnswer(ctx)
|
||||
|
||||
case <-ctx.Done():
|
||||
w.logger.Module("To client worker terminated")
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) processPendingAnswer(ctx context.Context) {
|
||||
pendingAnswers, err := w.deps.Redis.Keys(ctx, "answer:*").Result()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting keys from redis")
|
||||
w.errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("ANS")
|
||||
|
||||
for _, key := range pendingAnswers {
|
||||
func() {
|
||||
fmt.Println("ANS1", key)
|
||||
answerJSON, err := w.deps.Redis.GetDel(ctx, key).Result()
|
||||
if err == redis.Nil {
|
||||
return
|
||||
} else if err != nil {
|
||||
w.reportError(err, "Error getting and deleting data from redis")
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
w.reportError(nil, fmt.Sprintf("recovering from panic or error setting redis value %v", r))
|
||||
fmt.Println("ANS1ERRR", r)
|
||||
_ = w.deps.Redis.Set(ctx, key, answerJSON, 0).Err()
|
||||
}
|
||||
}()
|
||||
|
||||
var answer model.Answer
|
||||
err = json.Unmarshal([]byte(answerJSON), &answer)
|
||||
fmt.Println("ANS2", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error unmarshal answer")
|
||||
return
|
||||
}
|
||||
|
||||
answerContent, err := wctools.ProcessAnswer(answer.Content)
|
||||
fmt.Println("ANS3", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error unmarshal answer content")
|
||||
return
|
||||
}
|
||||
|
||||
allAnswers, err := w.deps.Dal.WorkerAnsRepo.GetAllAnswersByQuizID(ctx, answer.Session)
|
||||
fmt.Println("ANS4", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting all answers by quizID")
|
||||
return
|
||||
}
|
||||
|
||||
questionsMap, sortedallAnswers, err := w.deps.Dal.QuestionRepo.GetMapQuestions(ctx, allAnswers)
|
||||
fmt.Println("ANS5", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting questionsMap")
|
||||
return
|
||||
}
|
||||
|
||||
if answer.QuizId == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
quizConfig, accountId, err := w.deps.Dal.QuizRepo.GetQuizConfig(ctx, answer.QuizId)
|
||||
fmt.Println("ANS6", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting quiz config")
|
||||
return
|
||||
}
|
||||
|
||||
quiz, err := w.deps.Dal.QuizRepo.GetQuizById(ctx, accountId, answer.QuizId)
|
||||
fmt.Println("ANS60", err, accountId, answer.QuizId)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting quiz")
|
||||
return
|
||||
}
|
||||
|
||||
quizConfig.Mailing.Reply = quiz.Name
|
||||
|
||||
if quizConfig.Mailing.Theme == "" {
|
||||
quizConfig.Mailing.Theme = quiz.Name
|
||||
}
|
||||
|
||||
account, privileges, err := w.deps.Dal.AccountRepo.GetAccAndPrivilegeByEmail(ctx, accountId)
|
||||
fmt.Println("ANS7", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting account and privileges by email")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := w.processAnswerWithPrivileges(ctx, quiz.Name, quizConfig, questionsMap, privileges, account, sortedallAnswers, answerContent, answer.CreatedAt, answer.QuizId)
|
||||
fmt.Println("ANS8", err, result, privileges)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error process answer with privileges")
|
||||
return
|
||||
}
|
||||
if !result {
|
||||
err = w.deps.Redis.Set(ctx, fmt.Sprintf("%s:%s", account.ID, key), answerJSON, 0).Err()
|
||||
if err != nil {
|
||||
w.reportError(err, "Error setting redis value")
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) processAnswerWithPrivileges(ctx context.Context, quizName string, quizConfig model.QuizConfig,
|
||||
questionsMap map[uint64]string, privileges []model.ShortPrivilege, account model.Account, allAnswers []model.ResultAnswer,
|
||||
answerContent model.ResultContent, answerTime time.Time, quizID uint64) (bool, error) {
|
||||
|
||||
err := w.notificationCustomer(account, privileges)
|
||||
fmt.Println("ANS81", err)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if wctools.HasUnlimitedPrivilege(privileges) {
|
||||
err := w.ProcessMessageToClient(quizConfig, questionsMap, account, allAnswers, answerContent, answerTime, quizID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
privilege := wctools.HasQuizCntPrivilege(privileges)
|
||||
if privilege != nil {
|
||||
err := w.ProcessMessageToClient(quizConfig, questionsMap, account, allAnswers, answerContent, answerTime, quizID)
|
||||
fmt.Println("PMC", err)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
privilege.Amount--
|
||||
err = w.deps.Dal.AccountRepo.UpdatePrivilegeAmount(ctx, privilege.ID, privilege.Amount)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
} else {
|
||||
w.checkAndSendTaskReminders(ctx, sendTaskRemindersDeps{
|
||||
email: account.Email,
|
||||
theme: quizName,
|
||||
config: model.QuizConfig{
|
||||
Mailing: model.ResultInfo{
|
||||
When: "email",
|
||||
Theme: fmt.Sprintf("не удалось отправить заявку по опросу\"%s\"", quizName),
|
||||
Reply: "noreply@pena.digital",
|
||||
ReplName: "Reminder",
|
||||
},
|
||||
},
|
||||
})
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) recordPendingTasks(ctx context.Context, Email string, quizConfig model.QuizConfig) error {
|
||||
key := fmt.Sprintf("pending_tasks:%s", Email)
|
||||
|
||||
var pendingTasks PendingTasks
|
||||
val, err := w.deps.Redis.HGet(ctx, key, "data").Result()
|
||||
if err == nil {
|
||||
err := json.Unmarshal([]byte(val), &pendingTasks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pendingTasks.Count++
|
||||
} else {
|
||||
pendingTasks = PendingTasks{
|
||||
Count: 1,
|
||||
QuizConfig: quizConfig,
|
||||
}
|
||||
}
|
||||
|
||||
pendingTasksJSON, err := json.Marshal(pendingTasks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.deps.Redis.HSet(ctx, key, "data", string(pendingTasksJSON)).Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type sendTaskRemindersDeps struct {
|
||||
email, theme string
|
||||
config model.QuizConfig
|
||||
}
|
||||
|
||||
func (w *SendToClient) checkAndSendTaskReminders(ctx context.Context, deps sendTaskRemindersDeps) {
|
||||
err := w.processReminderToClient(deps.email, deps.config)
|
||||
fmt.Println("PMC1", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error sending tasks reminder email")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) notificationCustomer(account model.Account, privileges []model.ShortPrivilege) error {
|
||||
for _, privilege := range privileges {
|
||||
fmt.Println("NOTIFIC", privilege.PrivilegeID, privilege.Amount, !wctools.IsPrivilegeExpired(privilege))
|
||||
if privilege.PrivilegeID == "quizUnlimTime" && !wctools.IsPrivilegeExpired(privilege) {
|
||||
rawDetail, err := wctools.ToJSON(privilege)
|
||||
historyData := &customer.History{
|
||||
UserID: account.UserID,
|
||||
Comment: fmt.Sprintf("Привилегия %s просрочена", privilege.PrivilegeID),
|
||||
Key: "privilege_expired",
|
||||
RawDetails: rawDetail,
|
||||
}
|
||||
|
||||
_, err = w.deps.CustomerService.InsertHistory(context.Background(), historyData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if privilege.PrivilegeID == "quizCnt" && privilege.Amount == 0 {
|
||||
rawDetail, err := wctools.ToJSON(privilege)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
historyData := &customer.History{
|
||||
UserID: account.UserID,
|
||||
Comment: fmt.Sprintf("У привилегии %s истек amount", privilege.PrivilegeID),
|
||||
Key: "privilege_expired",
|
||||
RawDetails: rawDetail,
|
||||
}
|
||||
|
||||
_, err = w.deps.CustomerService.InsertHistory(context.Background(), historyData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// сделал экспортируемым для теста
|
||||
func (w *SendToClient) ProcessMessageToClient(quizConfig model.QuizConfig, questionsMap map[uint64]string, account model.Account, allAnswers []model.ResultAnswer, answerContent model.ResultContent, answerTime time.Time, quizID uint64) error {
|
||||
theme := quizConfig.Mailing.Theme
|
||||
quizConfig.Mailing.Theme = quizConfig.Mailing.Reply
|
||||
|
||||
data := mailclient.EmailTemplateData{
|
||||
QuizConfig: quizConfig.Mailing,
|
||||
AnswerContent: answerContent,
|
||||
AllAnswers: allAnswers,
|
||||
QuestionsMap: questionsMap,
|
||||
QuizID: quizID,
|
||||
}
|
||||
|
||||
dayOfWeek := wctools.DaysOfWeek[answerTime.Format("Monday")]
|
||||
monthOfYear := wctools.MonthsOfYear[answerTime.Format("January")]
|
||||
|
||||
formattedTime := fmt.Sprintf("%s, %d %s %d г., %02d:%02d (UTC%s)",
|
||||
dayOfWeek,
|
||||
answerTime.Day(),
|
||||
monthOfYear,
|
||||
answerTime.Year(),
|
||||
answerTime.Hour(),
|
||||
answerTime.Minute(),
|
||||
answerTime.Format("-07:00"),
|
||||
)
|
||||
|
||||
data.AnswerTime = formattedTime
|
||||
|
||||
fmt.Println("SUBJECT", theme, account.Email)
|
||||
|
||||
err := w.deps.MailClient.SendMailWithAttachment(account.Email, theme, toClientTemplate, data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *SendToClient) processReminderToClient(email string, quizConfig model.QuizConfig) error {
|
||||
|
||||
data := mailclient.EmailTemplateData{
|
||||
QuizConfig: model.ResultInfo{
|
||||
When: quizConfig.Mailing.When,
|
||||
Theme: quizConfig.Mailing.Theme,
|
||||
Reply: email,
|
||||
ReplName: quizConfig.Mailing.ReplName,
|
||||
},
|
||||
AnswerContent: model.ResultContent{},
|
||||
AllAnswers: []model.ResultAnswer{},
|
||||
QuestionsMap: nil,
|
||||
}
|
||||
|
||||
fmt.Println("PRTC", data, email, quizConfig)
|
||||
|
||||
err := w.deps.MailClient.SendMailWithAttachment(email, quizConfig.Mailing.Theme, reminderTemplate, data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *SendToClient) reportError(err error, message string) {
|
||||
if err != nil {
|
||||
fmt.Println(message + ": " + err.Error())
|
||||
w.errChan <- err
|
||||
}
|
||||
}
|
252
app/app.go
252
app/app.go
@ -1,252 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"gitea.pena/SQuiz/worker/answerwc"
|
||||
"gitea.pena/SQuiz/worker/clients/customer"
|
||||
"gitea.pena/SQuiz/worker/clients/gigachat"
|
||||
"gitea.pena/SQuiz/worker/clients/mailclient"
|
||||
"gitea.pena/SQuiz/worker/gigachatwc"
|
||||
"gitea.pena/SQuiz/worker/privilegewc"
|
||||
"gitea.pena/SQuiz/worker/workers/shortstat"
|
||||
"gitea.pena/SQuiz/worker/workers/timeout"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/skeris/appInit"
|
||||
"github.com/themakers/hlog"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"time"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
logger *zap.Logger
|
||||
err chan error
|
||||
}
|
||||
|
||||
func (a App) GetLogger() *zap.Logger {
|
||||
return a.logger
|
||||
}
|
||||
|
||||
func (a App) GetErr() chan error {
|
||||
return a.err
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidOptions = errors.New("invalid options")
|
||||
)
|
||||
|
||||
var zapOptions = []zap.Option{
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(2),
|
||||
zap.AddStacktrace(zap.ErrorLevel),
|
||||
}
|
||||
|
||||
var _ appInit.CommonApp = (*App)(nil)
|
||||
|
||||
type Options struct {
|
||||
ServiceName string `env:"SERVICE_NAME" default:"squiz"`
|
||||
KafkaBroker string `env:"KAFKA_BROKER"`
|
||||
KafkaTopic string `env:"KAFKA_TOPIC"`
|
||||
PrivilegeID string `env:"QUIZ_ID"`
|
||||
Amount uint64 `env:"AMOUNT"`
|
||||
UnlimID string `env:"UNLIM_ID"`
|
||||
LoggerProdMode bool `env:"IS_PROD_LOG" default:"false"`
|
||||
IsProd bool `env:"IS_PROD" default:"false"`
|
||||
MinioEP string `env:"MINIO_EP" default:"localhost:3002"`
|
||||
MinioAK string `env:"MINIO_AK" default:"minio"`
|
||||
MinioSK string `env:"MINIO_SK" default:"miniostorage"`
|
||||
PostgresCredentials string `env:"PG_CRED" default:"host=localhost port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"`
|
||||
RedisHost string `env:"REDIS_HOST" default:"localhost:6379"`
|
||||
RedisPassword string `env:"REDIS_PASSWORD"`
|
||||
RedisDB uint64 `env:"REDIS_DB" default:"2"`
|
||||
SmtpHost string `env:"SMTP_HOST" default:"connect.mailclient.bz"`
|
||||
SmtpPort string `env:"SMTP_PORT" default:"587"`
|
||||
SmtpSender string `env:"SMTP_SENDER" default:"noreply@mailing.pena.digital"`
|
||||
SmtpUsername string `env:"SMTP_USERNAME" default:"kotilion.95@gmail.com"`
|
||||
SmtpPassword string `env:"SMTP_PASSWORD" default:"vWwbCSg4bf0p"`
|
||||
SmtpApiKey string `env:"SMTP_API_KEY" default:"P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev"`
|
||||
SmtpApiUrl string `env:"SMTP_API_URL" default:"https://api.smtp.bz/v1/smtp/send"`
|
||||
CustomerServiceAddress string `env:"CUSTOMER_SERVICE_ADDRESS"`
|
||||
KafkaTopicGigaChat string `env:"KAFKA_TOPIC_GIGA_CHAT"`
|
||||
KafkaGroupGigaChat string `env:"KAFKA_GROUP_GIGA_CHAT" default:"gigachat"`
|
||||
GigaChatApiBaseURL string `env:"GIGA_CHAT_API_BASE_URL"`
|
||||
GigaChatApiAuthKey string `env:"GIGA_CHAT_API_AUTH_KEY"`
|
||||
}
|
||||
|
||||
func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.CommonApp, error) {
|
||||
var (
|
||||
err, workerErr error
|
||||
zapLogger *zap.Logger
|
||||
errChan = make(chan error)
|
||||
options Options
|
||||
ok bool
|
||||
)
|
||||
|
||||
if options, ok = opts.(Options); !ok {
|
||||
return App{}, errInvalidOptions
|
||||
}
|
||||
|
||||
if options.LoggerProdMode {
|
||||
zapLogger, err = zap.NewProduction(zapOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
zapLogger, err = zap.NewDevelopment(zapOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
zapLogger = zapLogger.With(
|
||||
zap.String("SvcCommit", ver.Commit),
|
||||
zap.String("SvcVersion", ver.Release),
|
||||
zap.String("SvcBuildTime", ver.BuildTime),
|
||||
)
|
||||
|
||||
logger := hlog.New(zapLogger)
|
||||
logger.Emit(InfoSvcStarted{})
|
||||
zapLogger.Info("config", zap.Any("options", options))
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case err := <-errChan:
|
||||
zapLogger.Error("Ошибка при работе воркера", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
//init redis
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: options.RedisHost,
|
||||
Password: options.RedisPassword,
|
||||
DB: int(options.RedisDB),
|
||||
})
|
||||
|
||||
smtpData := mailclient.ClientDeps{
|
||||
Host: options.SmtpHost,
|
||||
Port: options.SmtpPort,
|
||||
Sender: options.SmtpSender,
|
||||
ApiKey: options.SmtpApiKey,
|
||||
SmtpApiUrl: options.SmtpApiUrl,
|
||||
}
|
||||
|
||||
mailClient := mailclient.NewClient(smtpData)
|
||||
|
||||
customerServiceConn, err := grpc.Dial(options.CustomerServiceAddress, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
customerServiceClient := customer.NewCustomerServiceClient(customerServiceConn)
|
||||
|
||||
minioClient, err := minio.New(options.MinioEP, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(options.MinioAK, options.MinioSK, ""),
|
||||
Secure: options.IsProd,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("MINIOERR", options.MinioEP, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pgdal, err := dal.New(ctx, options.PostgresCredentials, minioClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gigaChatClient, err := gigachat.NewGigaChatClient(ctx, gigachat.Deps{
|
||||
Logger: zapLogger,
|
||||
Client: resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}),
|
||||
BaseURL: options.GigaChatApiBaseURL,
|
||||
AuthKey: options.GigaChatApiAuthKey,
|
||||
RedisClient: redisClient,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// метод для обновления токенов гигачата
|
||||
go gigaChatClient.TokenResearch(ctx)
|
||||
|
||||
gigaChatWorker, err := gigachatwc.NewGigaChatTaskScheduler(gigachatwc.Deps{
|
||||
KafkaBrokers: options.KafkaBroker,
|
||||
KafkaTopic: options.KafkaTopicGigaChat,
|
||||
KafkaGroup: options.KafkaGroupGigaChat,
|
||||
GigaChatClient: gigaChatClient,
|
||||
Logger: zapLogger,
|
||||
Dal: pgdal,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go gigaChatWorker.Start(ctx)
|
||||
|
||||
kafkaWorker, err := privilegewc.NewKafkaConsumerWorker(privilegewc.Config{
|
||||
KafkaBroker: options.KafkaBroker,
|
||||
KafkaTopic: options.KafkaTopic,
|
||||
ServiceKey: options.ServiceName,
|
||||
TickerInterval: time.Second * 10,
|
||||
Logger: logger,
|
||||
ErrChan: errChan,
|
||||
}, redisClient, pgdal)
|
||||
if err != nil {
|
||||
logger.Module("Failed start privilege worker")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
checkWorker := privilegewc.NewCheckWorker(privilegewc.CheckWorkerConfig{
|
||||
DefaultData: model.DefaultData{
|
||||
PrivilegeID: options.PrivilegeID,
|
||||
Amount: options.Amount,
|
||||
UnlimID: options.UnlimID,
|
||||
},
|
||||
TickerInterval: time.Minute,
|
||||
Logger: logger,
|
||||
ErrChan: errChan,
|
||||
}, pgdal)
|
||||
|
||||
go kafkaWorker.Start(ctx)
|
||||
go checkWorker.Start(ctx)
|
||||
toClientWorker := answerwc.NewSendToClient(answerwc.DepsSendToClient{
|
||||
Redis: redisClient,
|
||||
Dal: pgdal,
|
||||
MailClient: mailClient,
|
||||
CustomerService: customerServiceClient,
|
||||
}, logger, errChan)
|
||||
|
||||
toRespWorker := answerwc.NewRespWorker(answerwc.DepsRespWorker{
|
||||
Redis: redisClient,
|
||||
Dal: pgdal,
|
||||
MailClient: mailClient,
|
||||
}, logger, errChan)
|
||||
|
||||
go toClientWorker.Start(ctx)
|
||||
go toRespWorker.Start(ctx)
|
||||
|
||||
tow := timeout.New(pgdal, time.Minute)
|
||||
statW := shortstat.New(pgdal, 5*time.Minute)
|
||||
tow.ExposeErr(ctx, &workerErr)
|
||||
statW.ExposeErr(ctx, &workerErr)
|
||||
go tow.Start(ctx)
|
||||
go func() {
|
||||
// defer pgdal.CloseWorker()
|
||||
statW.Start(ctx)
|
||||
}()
|
||||
|
||||
logger.Emit(InfoSvcReady{})
|
||||
// todo implement helper func for service app type. such as server preparing, logger preparing, healthchecks and etc.
|
||||
return &App{
|
||||
logger: zapLogger,
|
||||
err: make(chan error),
|
||||
}, err
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc (unknown)
|
||||
// source: customer/service.proto
|
||||
|
||||
package customer
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type History struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
UserID string `protobuf:"bytes,1,opt,name=UserID,proto3" json:"UserID,omitempty"`
|
||||
Comment string `protobuf:"bytes,2,opt,name=Comment,proto3" json:"Comment,omitempty"`
|
||||
Key string `protobuf:"bytes,3,opt,name=Key,proto3" json:"Key,omitempty"`
|
||||
RawDetails string `protobuf:"bytes,4,opt,name=RawDetails,proto3" json:"RawDetails,omitempty"`
|
||||
}
|
||||
|
||||
func (x *History) Reset() {
|
||||
*x = History{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_customer_service_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *History) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*History) ProtoMessage() {}
|
||||
|
||||
func (x *History) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_customer_service_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use History.ProtoReflect.Descriptor instead.
|
||||
func (*History) Descriptor() ([]byte, []int) {
|
||||
return file_customer_service_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *History) GetUserID() string {
|
||||
if x != nil {
|
||||
return x.UserID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *History) GetComment() string {
|
||||
if x != nil {
|
||||
return x.Comment
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *History) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *History) GetRawDetails() string {
|
||||
if x != nil {
|
||||
return x.RawDetails
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_customer_service_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_customer_service_proto_rawDesc = []byte{
|
||||
0x0a, 0x16, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
|
||||
0x65, 0x72, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
|
||||
0x6d, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73,
|
||||
0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x73, 0x65, 0x72,
|
||||
0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x1e,
|
||||
0x0a, 0x0a, 0x52, 0x61, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0a, 0x52, 0x61, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x32, 0x4f,
|
||||
0x0a, 0x0f, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x79, 0x12, 0x11, 0x2e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x2e, 0x48, 0x69,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42,
|
||||
0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_customer_service_proto_rawDescOnce sync.Once
|
||||
file_customer_service_proto_rawDescData = file_customer_service_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_customer_service_proto_rawDescGZIP() []byte {
|
||||
file_customer_service_proto_rawDescOnce.Do(func() {
|
||||
file_customer_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_customer_service_proto_rawDescData)
|
||||
})
|
||||
return file_customer_service_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_customer_service_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_customer_service_proto_goTypes = []interface{}{
|
||||
(*History)(nil), // 0: customer.History
|
||||
(*emptypb.Empty)(nil), // 1: google.protobuf.Empty
|
||||
}
|
||||
var file_customer_service_proto_depIdxs = []int32{
|
||||
0, // 0: customer.CustomerService.InsertHistory:input_type -> customer.History
|
||||
1, // 1: customer.CustomerService.InsertHistory:output_type -> google.protobuf.Empty
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_customer_service_proto_init() }
|
||||
func file_customer_service_proto_init() {
|
||||
if File_customer_service_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_customer_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*History); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_customer_service_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_customer_service_proto_goTypes,
|
||||
DependencyIndexes: file_customer_service_proto_depIdxs,
|
||||
MessageInfos: file_customer_service_proto_msgTypes,
|
||||
}.Build()
|
||||
File_customer_service_proto = out.File
|
||||
file_customer_service_proto_rawDesc = nil
|
||||
file_customer_service_proto_goTypes = nil
|
||||
file_customer_service_proto_depIdxs = nil
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc (unknown)
|
||||
// source: customer/service.proto
|
||||
|
||||
package customer
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
CustomerService_InsertHistory_FullMethodName = "/customer.CustomerService/InsertHistory"
|
||||
)
|
||||
|
||||
// CustomerServiceClient is the client API for CustomerService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type CustomerServiceClient interface {
|
||||
InsertHistory(ctx context.Context, in *History, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
type customerServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewCustomerServiceClient(cc grpc.ClientConnInterface) CustomerServiceClient {
|
||||
return &customerServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *customerServiceClient) InsertHistory(ctx context.Context, in *History, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, CustomerService_InsertHistory_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CustomerServiceServer is the server API for CustomerService service.
|
||||
// All implementations should embed UnimplementedCustomerServiceServer
|
||||
// for forward compatibility
|
||||
type CustomerServiceServer interface {
|
||||
InsertHistory(context.Context, *History) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
// UnimplementedCustomerServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedCustomerServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedCustomerServiceServer) InsertHistory(context.Context, *History) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method InsertHistory not implemented")
|
||||
}
|
||||
|
||||
// UnsafeCustomerServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to CustomerServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeCustomerServiceServer interface {
|
||||
mustEmbedUnimplementedCustomerServiceServer()
|
||||
}
|
||||
|
||||
func RegisterCustomerServiceServer(s grpc.ServiceRegistrar, srv CustomerServiceServer) {
|
||||
s.RegisterService(&CustomerService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _CustomerService_InsertHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(History)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CustomerServiceServer).InsertHistory(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CustomerService_InsertHistory_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CustomerServiceServer).InsertHistory(ctx, req.(*History))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// CustomerService_ServiceDesc is the grpc.ServiceDesc for CustomerService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var CustomerService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "customer.CustomerService",
|
||||
HandlerType: (*CustomerServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "InsertHistory",
|
||||
Handler: _CustomerService_InsertHistory_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "customer/service.proto",
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
package mailclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"html/template"
|
||||
"mime/multipart"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ClientDeps struct {
|
||||
Host string
|
||||
Port string
|
||||
Sender string
|
||||
ApiKey string
|
||||
SmtpApiUrl string
|
||||
FiberClient *fiber.Client
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
deps ClientDeps
|
||||
}
|
||||
|
||||
type EmailTemplateData struct {
|
||||
QuizConfig model.ResultInfo
|
||||
QuizID uint64
|
||||
AnswerContent model.ResultContent
|
||||
AllAnswers []model.ResultAnswer
|
||||
QuestionsMap map[uint64]string
|
||||
AnswerTime string
|
||||
}
|
||||
|
||||
func NewClient(deps ClientDeps) *Client {
|
||||
if deps.FiberClient == nil {
|
||||
deps.FiberClient = fiber.AcquireClient()
|
||||
}
|
||||
return &Client{
|
||||
deps: deps,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) SendMailWithAttachment(recipient, subject string, emailTemplate string, data EmailTemplateData, attachments []Attachment) error {
|
||||
sanitizedData := sanitizeHTMLData(data)
|
||||
|
||||
text, err := generateTextFromTemplate(sanitizedData, emailTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := &Message{
|
||||
From: c.deps.Sender,
|
||||
To: []string{recipient},
|
||||
Subject: subject,
|
||||
Body: text,
|
||||
Attachments: attachments,
|
||||
}
|
||||
|
||||
return c.Send(msg)
|
||||
}
|
||||
|
||||
func (c *Client) Send(msg *Message) error {
|
||||
form := new(bytes.Buffer)
|
||||
writer := multipart.NewWriter(form)
|
||||
defer writer.Close()
|
||||
|
||||
fields := map[string]string{
|
||||
"from": msg.From,
|
||||
"to": strings.Join(msg.To, ","),
|
||||
"subject": msg.Subject,
|
||||
"html": msg.Body,
|
||||
}
|
||||
for key, value := range fields {
|
||||
if err := writer.WriteField(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// пока не используется так как Attachments из воркеров = nil, нужен ли будет потом
|
||||
for _, attachment := range msg.Attachments {
|
||||
part, err := writer.CreateFormFile("attachments", attachment.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = part.Write(attachment.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := writer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
agent := c.deps.FiberClient.Post(c.deps.SmtpApiUrl).Body(form.Bytes()).ContentType(writer.FormDataContentType())
|
||||
if c.deps.ApiKey != "" {
|
||||
agent.Set("Authorization", c.deps.ApiKey)
|
||||
}
|
||||
|
||||
statusCode, body, errs := agent.Bytes()
|
||||
if errs != nil {
|
||||
return errs[0]
|
||||
}
|
||||
|
||||
if statusCode != fiber.StatusOK {
|
||||
return fmt.Errorf("SMTP service returned error: %d, Response body: %s", statusCode, body)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateTextFromTemplate(data EmailTemplateData, 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, EmailTemplateData{
|
||||
QuizConfig: data.QuizConfig,
|
||||
AnswerContent: data.AnswerContent,
|
||||
AllAnswers: data.AllAnswers,
|
||||
QuestionsMap: data.QuestionsMap,
|
||||
AnswerTime: data.AnswerTime,
|
||||
QuizID: data.QuizID,
|
||||
}); err != nil {
|
||||
return "", fmt.Errorf("error executing template: %w", err)
|
||||
}
|
||||
|
||||
return text.String(), nil
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package mailclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
To []string
|
||||
From string
|
||||
Subject string
|
||||
Body string
|
||||
Attachments []Attachment
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
Name string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func NewMessage(subject, body string) *Message {
|
||||
if subject == "" {
|
||||
subject = "Вам пришла заявка с PenaQuiz"
|
||||
}
|
||||
return &Message{Subject: subject, Body: body, Attachments: []Attachment{}}
|
||||
}
|
||||
|
||||
func (m *Message) AttachFile(src string) error {
|
||||
data, err := os.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, filename := filepath.Split(src)
|
||||
m.Attachments = append(m.Attachments, Attachment{Name: filename, Data: data})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) AttachBytesFile(filename string, data []byte) {
|
||||
m.Attachments = append(m.Attachments, Attachment{Name: filename, Data: data})
|
||||
}
|
||||
|
||||
func (m *Message) ToBytes() []byte {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
buf.WriteString("MIME-Version: 1.0\r\n")
|
||||
fmt.Fprintf(buf, "From: %s\r\n", m.From)
|
||||
fmt.Fprintf(buf, "Subject: %s\r\n", m.Subject)
|
||||
fmt.Fprintf(buf, "To: %s\r\n", strings.Join(m.To, ","))
|
||||
|
||||
boundary := randomBoundary()
|
||||
|
||||
if len(m.Attachments) > 0 {
|
||||
buf.WriteString("Content-Type: multipart/mixed;\r\n")
|
||||
|
||||
fmt.Fprintf(buf, " boundary=\"%s\"\r\n", boundary)
|
||||
|
||||
fmt.Fprintf(buf, "\r\n--%s", boundary)
|
||||
for _, attachment := range m.Attachments {
|
||||
buf.WriteString("\r\n")
|
||||
switch strings.Split(attachment.Name, ".")[1] {
|
||||
case "htmlmsg":
|
||||
|
||||
buf.WriteString("Content-Type: text/html; charset=\"utf-8\"\r\n")
|
||||
buf.WriteString("Content-Transfer-Encoding: base64\r\n")
|
||||
case "docx":
|
||||
|
||||
buf.WriteString("Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document\r\n")
|
||||
buf.WriteString("Content-Transfer-Encoding: base64\r\n")
|
||||
fmt.Fprintf(buf, "Content-Disposition: attachment; filename=\"%s\"\r\n", attachment.Name)
|
||||
default:
|
||||
fmt.Fprintf(buf, "Content-Type: %s\r\n", http.DetectContentType(attachment.Data))
|
||||
buf.WriteString("Content-Transfer-Encoding: base64\r\n")
|
||||
fmt.Fprintf(buf, "Content-Disposition: attachment; filename=\"%s\"\r\n", attachment.Name)
|
||||
}
|
||||
|
||||
buf.WriteString("\r\n")
|
||||
|
||||
b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
|
||||
base64.StdEncoding.Encode(b, attachment.Data)
|
||||
|
||||
writer := NewLineWriter(buf, 76)
|
||||
_, err := writer.Write(b)
|
||||
if err != nil {
|
||||
fmt.Println("mailclient-client err:", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, "\r\n\r\n--%s", boundary)
|
||||
}
|
||||
|
||||
buf.WriteString("--")
|
||||
} else {
|
||||
buf.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
|
||||
buf.WriteString(m.Body)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
35
cmd/main.go
Normal file
35
cmd/main.go
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.uber.org/zap"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"gitea.pena/SQuiz/worker/internal/app"
|
||||
"gitea.pena/SQuiz/worker/internal/initialize"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
commit string = os.Getenv("COMMIT")
|
||||
buildTime string = os.Getenv("BUILD_TIME")
|
||||
version string = os.Getenv("VERSION")
|
||||
)
|
||||
|
||||
func main() {
|
||||
config, err := initialize.LoadConfig()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to load config", zap.Error(err))
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
if err = app.New(ctx, *config, app.Build{
|
||||
Commit: commit,
|
||||
Version: version,
|
||||
}); err != nil {
|
||||
log.Fatal("App exited with error", zap.Error(err))
|
||||
}
|
||||
}
|
82
cmd/validator/main.go
Normal file
82
cmd/validator/main.go
Normal file
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitea.pena/PenaSide/common/validate"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/worker/internal/initialize"
|
||||
"github.com/caarlos0/env/v8"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("error loading config: %v", err)
|
||||
}
|
||||
|
||||
err = validateNotEmpty(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("error validating config: %v", err)
|
||||
}
|
||||
|
||||
err = validate.ValidateKafka([]string{cfg.KafkaBrokers}, cfg.KafkaTopicTariff)
|
||||
if err != nil {
|
||||
log.Fatalf("error validating kafka: %v", err)
|
||||
}
|
||||
|
||||
err = validate.ValidateRedis(cfg.RedisHost, cfg.RedisPassword, int(cfg.RedisDB))
|
||||
if err != nil {
|
||||
log.Fatalf("error validating redis: %v", err)
|
||||
}
|
||||
|
||||
err = validate.ValidateSmtp(cfg.ApiKey)
|
||||
if err != nil {
|
||||
log.Fatalf("error validating smtp: %v", err)
|
||||
}
|
||||
|
||||
_, err = dal.New(context.TODO(), cfg.PostgresURL, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("error connecting to postgres: %v", err)
|
||||
}
|
||||
|
||||
// s3 пока не валидируем пока не слились все ветки
|
||||
}
|
||||
|
||||
func loadConfig() (initialize.Config, error) {
|
||||
var cfg initialize.Config
|
||||
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func validateNotEmpty(cfg initialize.Config) error {
|
||||
if cfg.ServiceName == "" {
|
||||
return errors.New("service name is not be empty")
|
||||
}
|
||||
|
||||
if cfg.CustomerMicroserviceRPCURL == "" {
|
||||
return errors.New("customer microservice rpc url is not be empty")
|
||||
}
|
||||
|
||||
if cfg.TelegramToken == "" {
|
||||
return errors.New("telegram token is not be empty")
|
||||
}
|
||||
|
||||
if cfg.Sender == "" {
|
||||
return errors.New("sender is not be empty")
|
||||
}
|
||||
|
||||
if cfg.ApiKey == "" {
|
||||
return errors.New("api key is not be empty")
|
||||
}
|
||||
|
||||
if cfg.ApiUrl == "" {
|
||||
return errors.New("api url is not be empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,20 +1,19 @@
|
||||
version: "3"
|
||||
services:
|
||||
workerv1.0.0:
|
||||
hostname: squiz-workerv1.0.0
|
||||
container_name: squiz-workerv1.0.0
|
||||
image: $CI_REGISTRY_IMAGE/staging-worker:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID
|
||||
image: gitea.pena/squiz/worker/staging:$GITHUB_RUN_NUMBER
|
||||
tty: true
|
||||
environment:
|
||||
IS_PROD_LOG: 'false'
|
||||
IS_PROD: 'false'
|
||||
PG_CRED: 'host=10.8.0.5 port=5433 user=squiz password=Redalert2 dbname=squiz sslmode=disable'
|
||||
KAFKA_BROKER: '10.8.0.6:9092'
|
||||
PG_CRED: 'host=10.7.0.10 port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable'
|
||||
KAFKA_BROKER: '10.7.0.6:9092'
|
||||
KAFKA_TOPIC: 'tariffs'
|
||||
QUIZ_ID: quizCnt
|
||||
AMOUNT: 10
|
||||
UNLIM_ID: quizUnlimTime
|
||||
REDIS_HOST: '10.8.0.5:6379'
|
||||
REDIS_HOST: '10.7.0.10:6379'
|
||||
REDIS_PASSWORD: 'Redalert2'
|
||||
REDIS_DB: 2
|
||||
SMTP_HOST: 'connect.mailclient.bz'
|
||||
@ -24,7 +23,7 @@ services:
|
||||
SMTP_USERNAME: 'kotilion.95@gmail.com'
|
||||
SMTP_PASSWORD: 'vWwbCSg4bf0p'
|
||||
SMTP_API_KEY: 'P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev'
|
||||
CUSTOMER_SERVICE_ADDRESS: '10.8.0.6:9066'
|
||||
CUSTOMER_SERVICE_ADDRESS: '10.7.0.6:9060'
|
||||
MINIO_EP: s3.timeweb.cloud
|
||||
MINIO_AK: 5CV77KVDUU9H0II9R24M
|
||||
MINIO_SK: 0W0m8DyvdAKRJnsAy6mB5zndQ7RouJBLhqhtThcu
|
||||
|
@ -18,50 +18,50 @@ services:
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
|
||||
# need update!
|
||||
# test-pena-auth-service:
|
||||
# image: penahub.gitlab.yandexcloud.net:5050/pena-services/pena-auth-service:staging.872
|
||||
# container_name: test-pena-auth-service
|
||||
# init: true
|
||||
# env_file: auth.env.test
|
||||
# healthcheck:
|
||||
# test: wget -T1 --spider http://localhost:8000/user
|
||||
# interval: 2s
|
||||
# timeout: 2s
|
||||
# retries: 5
|
||||
# environment:
|
||||
# - DB_HOST=test-pena-auth-db
|
||||
# - DB_PORT=27017
|
||||
# - ENVIRONMENT=staging
|
||||
# - HTTP_HOST=0.0.0.0
|
||||
# - HTTP_PORT=8000
|
||||
# - DB_USERNAME=test
|
||||
# - DB_PASSWORD=test
|
||||
# - DB_NAME=admin
|
||||
# - DB_AUTH=admin
|
||||
# # ports:
|
||||
# # - 8000:8000
|
||||
# depends_on:
|
||||
# - test-pena-auth-db
|
||||
# # - pena-auth-migration
|
||||
# networks:
|
||||
# - penatest
|
||||
#
|
||||
# test-pena-auth-db:
|
||||
# container_name: test-pena-auth-db
|
||||
# init: true
|
||||
# image: "mongo:6.0.3"
|
||||
# command: mongod --quiet --logpath /dev/null
|
||||
# volumes:
|
||||
# - test-mongodb:/data/db
|
||||
# - test-mongoconfdb:/data/configdb
|
||||
# environment:
|
||||
# MONGO_INITDB_ROOT_USERNAME: test
|
||||
# MONGO_INITDB_ROOT_PASSWORD: test
|
||||
# # ports:
|
||||
# # - 27017:27017
|
||||
# networks:
|
||||
# - penatest
|
||||
# need update!
|
||||
# test-pena-auth-service:
|
||||
# image: penahub.gitlab.yandexcloud.net:5050/pena-services/pena-auth-service:staging.872
|
||||
# container_name: test-pena-auth-service
|
||||
# init: true
|
||||
# env_file: auth.env.test
|
||||
# healthcheck:
|
||||
# test: wget -T1 --spider http://localhost:8000/user
|
||||
# interval: 2s
|
||||
# timeout: 2s
|
||||
# retries: 5
|
||||
# environment:
|
||||
# - DB_HOST=test-pena-auth-db
|
||||
# - DB_PORT=27017
|
||||
# - ENVIRONMENT=staging
|
||||
# - HTTP_HOST=0.0.0.0
|
||||
# - HTTP_PORT=8000
|
||||
# - DB_USERNAME=test
|
||||
# - DB_PASSWORD=test
|
||||
# - DB_NAME=admin
|
||||
# - DB_AUTH=admin
|
||||
# # ports:
|
||||
# # - 8000:8000
|
||||
# depends_on:
|
||||
# - test-pena-auth-db
|
||||
# # - pena-auth-migration
|
||||
# networks:
|
||||
# - penatest
|
||||
#
|
||||
# test-pena-auth-db:
|
||||
# container_name: test-pena-auth-db
|
||||
# init: true
|
||||
# image: "mongo:6.0.3"
|
||||
# command: mongod --quiet --logpath /dev/null
|
||||
# volumes:
|
||||
# - test-mongodb:/data/db
|
||||
# - test-mongoconfdb:/data/configdb
|
||||
# environment:
|
||||
# MONGO_INITDB_ROOT_USERNAME: test
|
||||
# MONGO_INITDB_ROOT_PASSWORD: test
|
||||
# # ports:
|
||||
# # - 27017:27017
|
||||
# networks:
|
||||
# - penatest
|
||||
|
||||
test-minio:
|
||||
container_name: test-minio
|
||||
@ -82,8 +82,8 @@ services:
|
||||
depends_on:
|
||||
test-postgres:
|
||||
condition: service_healthy
|
||||
# test-pena-auth-service:
|
||||
# condition: service_healthy
|
||||
# test-pena-auth-service:
|
||||
# condition: service_healthy
|
||||
# volumes:
|
||||
# - ./../..:/app:ro
|
||||
# command: [ "go", "test", "./tests", "-run", "TestFoo" ]
|
||||
|
12
go.mod
12
go.mod
@ -23,10 +23,9 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@ -38,17 +37,18 @@ require (
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/twmb/franz-go/pkg/kmsg v1.9.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.54.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.59.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
|
47
go.sum
47
go.sum
@ -50,14 +50,15 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
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-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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=
|
||||
@ -78,6 +79,8 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
@ -101,10 +104,22 @@ github.com/twmb/franz-go/pkg/kmsg v1.9.0 h1:JojYUph2TKAau6SBtErXpXGC7E3gg4vGZMv9
|
||||
github.com/twmb/franz-go/pkg/kmsg v1.9.0/go.mod h1:CMbfazviCyY6HM0SXuG5t9vOwYDHRCSrJJyBAe5paqg=
|
||||
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.54.0 h1:cCL+ZZR3z3HPLMVfEYVUMtJqVaui0+gu7Lx63unHwS0=
|
||||
github.com/valyala/fasthttp v1.54.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
@ -129,8 +144,6 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
@ -144,17 +157,21 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99 h1:ZSlhAUqC4r8TPzqLXQ0m3upBNZeF+Y8jQ3c4CR3Ujms=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po=
|
||||
gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
@ -4,31 +4,28 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/themakers/hlog"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"gitea.pena/SQuiz/worker/clients/mailclient"
|
||||
"gitea.pena/SQuiz/worker/wctools"
|
||||
senders2 "gitea.pena/SQuiz/worker/internal/senders"
|
||||
"gitea.pena/SQuiz/worker/internal/wctools"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DepsRespWorker struct {
|
||||
Redis *redis.Client
|
||||
Dal *dal.DAL
|
||||
MailClient *mailclient.Client
|
||||
MailClient *senders2.MailLeadSender
|
||||
}
|
||||
|
||||
type RespWorker struct {
|
||||
deps DepsRespWorker
|
||||
logger hlog.Logger
|
||||
errChan chan<- error
|
||||
}
|
||||
|
||||
func NewRespWorker(deps DepsRespWorker, logger hlog.Logger, errChan chan<- error) *RespWorker {
|
||||
func NewRespWorker(deps DepsRespWorker, errChan chan<- error) *RespWorker {
|
||||
return &RespWorker{
|
||||
deps: deps,
|
||||
logger: logger,
|
||||
errChan: errChan,
|
||||
}
|
||||
}
|
||||
@ -43,7 +40,6 @@ func (w *RespWorker) Start(ctx context.Context) {
|
||||
w.processPendingAnswer(ctx)
|
||||
|
||||
case <-ctx.Done():
|
||||
w.logger.Module("To respondent worker terminated")
|
||||
return
|
||||
}
|
||||
|
||||
@ -132,7 +128,7 @@ func (w *RespWorker) processMessageToSMTP(quizConfig model.QuizConfig, questions
|
||||
theme := quizConfig.Mailing.Theme
|
||||
quizConfig.Mailing.Theme = quizConfig.Mailing.Reply
|
||||
|
||||
data := mailclient.EmailTemplateData{
|
||||
data := senders2.TemplateData{
|
||||
QuizConfig: quizConfig.Mailing,
|
||||
AnswerContent: answerContent,
|
||||
AllAnswers: allAnswers,
|
26
internal/answerwc/template/client_tg.tmpl
Normal file
26
internal/answerwc/template/client_tg.tmpl
Normal file
@ -0,0 +1,26 @@
|
||||
*Квиз для вашего бизнеса*
|
||||
|
||||
*Поступила новая заявка с квиза "{{.QuizConfig.Theme}}"!*
|
||||
Время заявки: {{ .AnswerTime }}
|
||||
|
||||
Имя: {{ .AnswerContent.Name }}
|
||||
{{ if .AnswerContent.Email }}Email: {{ .AnswerContent.Email }}{{ end }}
|
||||
{{ if .AnswerContent.Phone }}Телефон: {{ .AnswerContent.Phone }}{{ end }}
|
||||
{{ if .AnswerContent.Telegram }}Telegram: {{ .AnswerContent.Telegram }}{{ end }}
|
||||
{{ if .AnswerContent.Wechat }}Wechat: {{ .AnswerContent.Wechat }}{{ end }}
|
||||
{{ if .AnswerContent.Viber }}Viber: {{ .AnswerContent.Viber }}{{ end }}
|
||||
{{ if .AnswerContent.Vk }}Vk: {{ .AnswerContent.Vk }}{{ end }}
|
||||
{{ if .AnswerContent.Skype }}Skype: {{ .AnswerContent.Skype }}{{ end }}
|
||||
{{ if .AnswerContent.Whatsup }}Whatsup: {{ .AnswerContent.Whatsup }}{{ end }}
|
||||
{{ if .AnswerContent.Messenger }}Messenger: {{ .AnswerContent.Messenger }}{{ end }}
|
||||
{{ if .AnswerContent.Address }}Адрес: {{ .AnswerContent.Address }}{{ end }}
|
||||
{{ range $key, $value := .AnswerContent.Custom }}{{ $key }}: {{ $value }}{{ end }}
|
||||
|
||||
*Ответы:*
|
||||
{{ range .AllAnswers }}
|
||||
{{ if index $.QuestionsMap .AnswerID }}
|
||||
*{{ index $.QuestionsMap .AnswerID }}*
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
0
internal/answerwc/template/client_whatsapp.tmpl
Normal file
0
internal/answerwc/template/client_whatsapp.tmpl
Normal file
9
internal/answerwc/template/reminder_tg.tmpl
Normal file
9
internal/answerwc/template/reminder_tg.tmpl
Normal file
@ -0,0 +1,9 @@
|
||||
# Поступила новая заявка с квиза “{{ .QuizConfig.Theme }}”!
|
||||
Но у вас закончились средства на балансе :(
|
||||

|
||||
## Аккаунт
|
||||
**Email:** {{ .QuizConfig.Reply }}
|
||||
Пополните баланс и посмотрите заявку в личном кабинете:
|
||||
[Посмотреть в личном кабинете](https://quiz.pena.digital)
|
||||
---
|
||||
[quiz.pena.digital](https://quiz.pena.digital)
|
0
internal/answerwc/template/reminder_whatsapp.tmpl
Normal file
0
internal/answerwc/template/reminder_whatsapp.tmpl
Normal file
491
internal/answerwc/to_client.go
Normal file
491
internal/answerwc/to_client.go
Normal file
@ -0,0 +1,491 @@
|
||||
package answerwc
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitea.pena/PenaSide/customer/pkg/customer_clients"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"gitea.pena/SQuiz/common/pj_errors"
|
||||
"gitea.pena/SQuiz/worker/internal/senders"
|
||||
"gitea.pena/SQuiz/worker/internal/wctools"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DepsSendToClient struct {
|
||||
Redis *redis.Client
|
||||
Dal *dal.DAL
|
||||
LeadSenders []senders.LeadSender
|
||||
CustomerService *customer_clients.CustomersClient
|
||||
}
|
||||
|
||||
type SendToClient struct {
|
||||
redis *redis.Client
|
||||
dal *dal.DAL
|
||||
leadSenders []senders.LeadSender
|
||||
customerService *customer_clients.CustomersClient
|
||||
errChan chan<- error
|
||||
toClientTemplates map[model.LeadTargetType]string
|
||||
toReminderTemplates map[model.LeadTargetType]string
|
||||
}
|
||||
|
||||
type PendingTasks struct {
|
||||
Count int64
|
||||
QuizConfig model.QuizConfig
|
||||
}
|
||||
|
||||
//go:embed template/to_client.tmpl
|
||||
var toClientTemplate string
|
||||
|
||||
//go:embed template/reminder.tmpl
|
||||
var reminderTemplate string
|
||||
|
||||
//go:embed template/reminder_tg.tmpl
|
||||
var reminderTgTemplate string
|
||||
|
||||
//go:embed template/reminder_whatsapp.tmpl
|
||||
var reminderWhatsAppTemplate string
|
||||
|
||||
//go:embed template/client_tg.tmpl
|
||||
var toClientTgTemplate string
|
||||
|
||||
//go:embed template/client_whatsapp.tmpl
|
||||
var toClientWhatsAppTemplate string
|
||||
|
||||
func NewSendToClient(deps DepsSendToClient, errChan chan<- error) *SendToClient {
|
||||
toClientTemplates := map[model.LeadTargetType]string{
|
||||
model.LeadTargetEmail: toClientTemplate,
|
||||
model.LeadTargetTg: toClientTgTemplate,
|
||||
model.LeadTargetWhatsapp: toClientWhatsAppTemplate,
|
||||
}
|
||||
|
||||
toReminderTemplates := map[model.LeadTargetType]string{
|
||||
model.LeadTargetEmail: reminderTemplate,
|
||||
model.LeadTargetTg: reminderTgTemplate,
|
||||
model.LeadTargetWhatsapp: reminderWhatsAppTemplate,
|
||||
}
|
||||
|
||||
return &SendToClient{
|
||||
redis: deps.Redis,
|
||||
dal: deps.Dal,
|
||||
customerService: deps.CustomerService,
|
||||
errChan: errChan,
|
||||
leadSenders: deps.LeadSenders,
|
||||
toClientTemplates: toClientTemplates,
|
||||
toReminderTemplates: toReminderTemplates,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) Start(ctx context.Context) {
|
||||
answerTicker := time.NewTicker(30 * time.Second)
|
||||
defer answerTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-answerTicker.C:
|
||||
w.processPendingAnswer(ctx)
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) processPendingAnswer(ctx context.Context) {
|
||||
pendingAnswers, err := w.redis.Keys(ctx, "answer:*").Result()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting keys from redis")
|
||||
w.errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("ANS")
|
||||
|
||||
for _, key := range pendingAnswers {
|
||||
func() {
|
||||
fmt.Println("ANS1", key)
|
||||
answerJSON, err := w.redis.GetDel(ctx, key).Result()
|
||||
if err == redis.Nil {
|
||||
return
|
||||
} else if err != nil {
|
||||
w.reportError(err, "Error getting and deleting data from redis")
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
w.reportError(nil, fmt.Sprintf("recovering from panic or error setting redis value %v", r))
|
||||
fmt.Println("ANS1ERRR", r)
|
||||
_ = w.redis.Set(ctx, key, answerJSON, 0).Err()
|
||||
}
|
||||
}()
|
||||
|
||||
var answer model.Answer
|
||||
err = json.Unmarshal([]byte(answerJSON), &answer)
|
||||
fmt.Println("ANS2", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error unmarshal answer")
|
||||
return
|
||||
}
|
||||
|
||||
answerContent, err := wctools.ProcessAnswer(answer.Content)
|
||||
fmt.Println("ANS3", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error unmarshal answer content")
|
||||
return
|
||||
}
|
||||
|
||||
allAnswersDirty, err := w.dal.AnswerRepo.GetAllAnswersByQuizID(ctx, answer.Session)
|
||||
fmt.Println("ANS4", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting all answers by quizID")
|
||||
return
|
||||
}
|
||||
allAnswers := wctools.CleanNullContent(allAnswersDirty)
|
||||
|
||||
questionsMap, sortedallAnswers, err := w.dal.QuestionRepo.GetMapQuestions(ctx, allAnswers)
|
||||
fmt.Println("ANS5", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting questionsMap")
|
||||
return
|
||||
}
|
||||
|
||||
if answer.QuizId == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
quizConfig, accountId, err := w.dal.QuizRepo.GetQuizConfig(ctx, answer.QuizId)
|
||||
fmt.Println("ANS6", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting quiz config")
|
||||
return
|
||||
}
|
||||
|
||||
quiz, err := w.dal.QuizRepo.GetQuizById(ctx, accountId, answer.QuizId)
|
||||
fmt.Println("ANS60", err, accountId, answer.QuizId)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting quiz")
|
||||
return
|
||||
}
|
||||
|
||||
quizConfig.Mailing.Reply = quiz.Name
|
||||
|
||||
if quizConfig.Mailing.Theme == "" {
|
||||
quizConfig.Mailing.Theme = quiz.Name
|
||||
}
|
||||
|
||||
account, privileges, err := w.dal.AccountRepo.GetAccAndPrivilegeByEmail(ctx, accountId)
|
||||
fmt.Println("ANS7", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error getting account and privileges by email")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := w.processAnswerWithPrivileges(ctx, ProcessAnsWithPriv{
|
||||
quiz: quiz,
|
||||
quizConfig: quizConfig,
|
||||
questionsMap: questionsMap,
|
||||
privileges: privileges,
|
||||
account: account,
|
||||
allAnswers: sortedallAnswers,
|
||||
answerContent: answerContent,
|
||||
answerTime: answer.CreatedAt,
|
||||
})
|
||||
fmt.Println("ANS8", err, result, privileges)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error process answer with privileges")
|
||||
return
|
||||
}
|
||||
if !result {
|
||||
err = w.redis.Set(ctx, fmt.Sprintf("%s:%s", account.ID, key), answerJSON, 0).Err()
|
||||
if err != nil {
|
||||
w.reportError(err, "Error setting redis value")
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
type ProcessAnsWithPriv struct {
|
||||
quiz *model.Quiz
|
||||
quizConfig model.QuizConfig
|
||||
questionsMap map[uint64]string
|
||||
privileges []model.ShortPrivilege
|
||||
account model.Account
|
||||
allAnswers []model.ResultAnswer
|
||||
answerContent model.ResultContent
|
||||
answerTime time.Time
|
||||
}
|
||||
|
||||
func (w *SendToClient) processAnswerWithPrivileges(ctx context.Context, data ProcessAnsWithPriv) (bool, error) {
|
||||
|
||||
err := w.notificationCustomer(ctx, data.account, data.privileges)
|
||||
fmt.Println("ANS81", err)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if wctools.HasUnlimitedPrivilege(data.privileges) {
|
||||
err := w.ProcessMessageToClient(ctx, DepsProcessMsgToClient{
|
||||
QuizConfig: data.quizConfig,
|
||||
QuestionsMap: data.questionsMap,
|
||||
Account: data.account,
|
||||
AllAnswers: data.allAnswers,
|
||||
AnswerContent: data.answerContent,
|
||||
AnswerTime: data.answerTime,
|
||||
Quiz: data.quiz,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
privilege := wctools.HasQuizCntPrivilege(data.privileges)
|
||||
if privilege != nil {
|
||||
err := w.ProcessMessageToClient(ctx, DepsProcessMsgToClient{
|
||||
QuizConfig: data.quizConfig,
|
||||
QuestionsMap: data.questionsMap,
|
||||
Account: data.account,
|
||||
AllAnswers: data.allAnswers,
|
||||
AnswerContent: data.answerContent,
|
||||
AnswerTime: data.answerTime,
|
||||
Quiz: data.quiz,
|
||||
})
|
||||
fmt.Println("PMC", err)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
privilege.Amount--
|
||||
err = w.dal.AccountRepo.UpdatePrivilegeAmount(ctx, privilege.ID, privilege.Amount)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
} else {
|
||||
w.checkAndSendTaskReminders(ctx, sendTaskRemindersDeps{
|
||||
quiz: data.quiz,
|
||||
account: data.account,
|
||||
theme: data.quiz.Name,
|
||||
config: model.QuizConfig{
|
||||
Mailing: model.ResultInfo{
|
||||
When: "email",
|
||||
Theme: fmt.Sprintf("не удалось отправить заявку по опросу\"%s\"", data.quiz.Name),
|
||||
Reply: "noreply@pena.digital",
|
||||
ReplName: "Reminder",
|
||||
},
|
||||
},
|
||||
})
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) recordPendingTasks(ctx context.Context, Email string, quizConfig model.QuizConfig) error {
|
||||
key := fmt.Sprintf("pending_tasks:%s", Email)
|
||||
|
||||
var pendingTasks PendingTasks
|
||||
val, err := w.redis.HGet(ctx, key, "data").Result()
|
||||
if err == nil {
|
||||
err := json.Unmarshal([]byte(val), &pendingTasks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pendingTasks.Count++
|
||||
} else {
|
||||
pendingTasks = PendingTasks{
|
||||
Count: 1,
|
||||
QuizConfig: quizConfig,
|
||||
}
|
||||
}
|
||||
|
||||
pendingTasksJSON, err := json.Marshal(pendingTasks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.redis.HSet(ctx, key, "data", string(pendingTasksJSON)).Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type sendTaskRemindersDeps struct {
|
||||
account model.Account
|
||||
theme string
|
||||
config model.QuizConfig
|
||||
quiz *model.Quiz
|
||||
}
|
||||
|
||||
func (w *SendToClient) checkAndSendTaskReminders(ctx context.Context, data sendTaskRemindersDeps) {
|
||||
err := w.ProcessReminderToClient(ctx, data.account, data.config, data.quiz)
|
||||
fmt.Println("PMC1", err)
|
||||
if err != nil {
|
||||
w.reportError(err, "Error sending tasks reminder email")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SendToClient) notificationCustomer(ctx context.Context, account model.Account, privileges []model.ShortPrivilege) error {
|
||||
for _, privilege := range privileges {
|
||||
fmt.Println("NOTIFIC", privilege.PrivilegeID, privilege.Amount, !wctools.IsPrivilegeExpired(privilege))
|
||||
if privilege.PrivilegeID == "quizUnlimTime" && !wctools.IsPrivilegeExpired(privilege) {
|
||||
historyData := customer_clients.InsertHistoryDeps{
|
||||
UserID: account.UserID,
|
||||
Comment: fmt.Sprintf("%s privilege has expired, it was created at %d", privilege.PrivilegeID, privilege.CreatedAt.Unix()),
|
||||
Key: "privilege_expired",
|
||||
}
|
||||
|
||||
err := w.customerService.InsertHistory(ctx, historyData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if privilege.PrivilegeID == "quizCnt" && privilege.Amount == 0 {
|
||||
historyData := customer_clients.InsertHistoryDeps{
|
||||
UserID: account.UserID,
|
||||
Comment: fmt.Sprintf("%s privilege has expired, it was created at %d", privilege.PrivilegeID, privilege.CreatedAt.Unix()),
|
||||
Key: "privilege_expired",
|
||||
}
|
||||
|
||||
err := w.customerService.InsertHistory(ctx, historyData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DepsProcessMsgToClient struct {
|
||||
QuizConfig model.QuizConfig
|
||||
QuestionsMap map[uint64]string
|
||||
Account model.Account
|
||||
AllAnswers []model.ResultAnswer
|
||||
AnswerContent model.ResultContent
|
||||
AnswerTime time.Time
|
||||
Quiz *model.Quiz
|
||||
}
|
||||
|
||||
// сделал экспортируемым для теста
|
||||
func (w *SendToClient) ProcessMessageToClient(ctx context.Context, constructData DepsProcessMsgToClient) error {
|
||||
leadTargetForAll, err := w.dal.AccountRepo.GetLeadTarget(ctx, constructData.Quiz.AccountId, 0)
|
||||
if err != nil && !errors.Is(err, pj_errors.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
leadTargetForQuiz, err := w.dal.AccountRepo.GetLeadTarget(ctx, constructData.Quiz.AccountId, int32(constructData.Quiz.Id))
|
||||
if err != nil && !errors.Is(err, pj_errors.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
if len(leadTargetForQuiz) > 0 {
|
||||
leadTargetForAll = append(leadTargetForAll, leadTargetForQuiz...)
|
||||
}
|
||||
|
||||
theme := constructData.QuizConfig.Mailing.Theme
|
||||
constructData.QuizConfig.Mailing.Theme = constructData.QuizConfig.Mailing.Reply
|
||||
|
||||
data := senders.TemplateData{
|
||||
QuizConfig: constructData.QuizConfig.Mailing,
|
||||
AnswerContent: constructData.AnswerContent,
|
||||
AllAnswers: constructData.AllAnswers,
|
||||
QuestionsMap: constructData.QuestionsMap,
|
||||
}
|
||||
|
||||
dayOfWeek := wctools.DaysOfWeek[constructData.AnswerTime.Format("Monday")]
|
||||
monthOfYear := wctools.MonthsOfYear[constructData.AnswerTime.Format("January")]
|
||||
|
||||
formattedTime := fmt.Sprintf("%s, %d %s %d г., %02d:%02d (UTC%s)",
|
||||
dayOfWeek,
|
||||
constructData.AnswerTime.Day(),
|
||||
monthOfYear,
|
||||
constructData.AnswerTime.Year(),
|
||||
constructData.AnswerTime.Hour(),
|
||||
constructData.AnswerTime.Minute(),
|
||||
constructData.AnswerTime.Format("-07:00"),
|
||||
)
|
||||
|
||||
data.AnswerTime = formattedTime
|
||||
mapLeadTarget := make(map[string][]senders.LeadData) // ключ имя сендера, модель отправки
|
||||
for _, leadTarget := range leadTargetForAll {
|
||||
mapLeadTarget[string(leadTarget.Type)] = append(mapLeadTarget[string(leadTarget.Type)], senders.LeadData{
|
||||
To: leadTarget.Target,
|
||||
Subject: theme,
|
||||
TemplateData: data,
|
||||
Template: w.toClientTemplates[leadTarget.Type],
|
||||
})
|
||||
}
|
||||
|
||||
for _, sender := range w.leadSenders {
|
||||
for _, sendData := range mapLeadTarget[sender.Name()] {
|
||||
err := sender.SendLead(sendData)
|
||||
if err != nil {
|
||||
w.reportError(err, fmt.Sprintf("Error sending lead through %s", sender.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *SendToClient) ProcessReminderToClient(ctx context.Context, account model.Account, quizConfig model.QuizConfig, quiz *model.Quiz) error {
|
||||
leadTargetForAll, err := w.dal.AccountRepo.GetLeadTarget(ctx, account.UserID, 0)
|
||||
if err != nil && !errors.Is(err, pj_errors.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
leadTargetForQuiz, err := w.dal.AccountRepo.GetLeadTarget(ctx, account.UserID, int32(quiz.Id))
|
||||
if err != nil && !errors.Is(err, pj_errors.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
if len(leadTargetForQuiz) > 0 {
|
||||
leadTargetForAll = append(leadTargetForAll, leadTargetForQuiz...)
|
||||
}
|
||||
mapLeadTarget := make(map[string][]senders.LeadData)
|
||||
for _, leadTarget := range leadTargetForAll {
|
||||
data := senders.TemplateData{
|
||||
QuizConfig: model.ResultInfo{
|
||||
When: quizConfig.Mailing.When,
|
||||
Theme: quizConfig.Mailing.Theme,
|
||||
Reply: leadTarget.Target,
|
||||
ReplName: quizConfig.Mailing.ReplName,
|
||||
},
|
||||
AnswerContent: model.ResultContent{},
|
||||
AllAnswers: []model.ResultAnswer{},
|
||||
QuestionsMap: nil,
|
||||
}
|
||||
|
||||
fmt.Println("PRTC", data, leadTarget.Target, quizConfig)
|
||||
mapLeadTarget[string(leadTarget.Type)] = append(mapLeadTarget[string(leadTarget.Type)], senders.LeadData{
|
||||
To: leadTarget.Target,
|
||||
Subject: quizConfig.Mailing.Theme,
|
||||
Template: w.toReminderTemplates[leadTarget.Type],
|
||||
TemplateData: data,
|
||||
})
|
||||
}
|
||||
for _, sender := range w.leadSenders {
|
||||
for _, sendData := range mapLeadTarget[sender.Name()] {
|
||||
err := sender.SendLead(sendData)
|
||||
if err != nil {
|
||||
w.reportError(err, fmt.Sprintf("Error sending lead through %s", sender.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *SendToClient) reportError(err error, message string) {
|
||||
if err != nil {
|
||||
fmt.Println(message + ": " + err.Error())
|
||||
w.errChan <- err
|
||||
}
|
||||
}
|
163
internal/app/app.go
Normal file
163
internal/app/app.go
Normal file
@ -0,0 +1,163 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitea.pena/PenaSide/hlog"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
answerwc2 "gitea.pena/SQuiz/worker/internal/answerwc"
|
||||
"gitea.pena/SQuiz/worker/internal/initialize"
|
||||
privilegewc2 "gitea.pena/SQuiz/worker/internal/privilegewc"
|
||||
senders2 "gitea.pena/SQuiz/worker/internal/senders"
|
||||
"gitea.pena/SQuiz/worker/internal/workers/shortstat"
|
||||
"gitea.pena/SQuiz/worker/internal/workers/timeout"
|
||||
"gitea.pena/SQuiz/worker/pkg/closer"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
var zapOptions = []zap.Option{
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(2),
|
||||
zap.AddStacktrace(zap.ErrorLevel),
|
||||
}
|
||||
|
||||
type Build struct {
|
||||
Commit string
|
||||
Version string
|
||||
}
|
||||
|
||||
func New(ctx context.Context, cfg initialize.Config, build Build) error {
|
||||
var (
|
||||
err, workerErr error
|
||||
zapLogger *zap.Logger
|
||||
errChan = make(chan error)
|
||||
)
|
||||
|
||||
if cfg.LoggerProdMode {
|
||||
zapLogger, err = zap.NewProduction(zapOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
zapLogger, err = zap.NewDevelopment(zapOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
zapLogger = zapLogger.With(
|
||||
zap.String("SvcCommit", build.Commit),
|
||||
zap.String("SvcVersion", build.Version),
|
||||
zap.String("SvcBuildTime", time.Now().String()),
|
||||
)
|
||||
|
||||
logger := hlog.New(zapLogger)
|
||||
logger.Emit(InfoSvcStarted{})
|
||||
|
||||
shutdownGroup := closer.NewCloserGroup()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case err := <-errChan:
|
||||
zapLogger.Error("Ошибка при работе воркера", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
redisClient, err := initialize.Redis(ctx, cfg)
|
||||
if err != nil {
|
||||
zapLogger.Error("failed init redis", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
minioClient, err := initialize.NewMinio(cfg)
|
||||
if err != nil {
|
||||
zapLogger.Error("failed init minio", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
clients := initialize.NewClients(cfg, zapLogger)
|
||||
|
||||
// tgSender, err := senders.NewTgSender(options.TgToken)
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// return nil, err
|
||||
// }
|
||||
mailSender := senders2.NewMailLeadSender(clients.MailClient)
|
||||
leadSenders := []senders2.LeadSender{mailSender /* , tgSender */}
|
||||
|
||||
pgdal, err := dal.New(ctx, cfg.PostgresURL, minioClient)
|
||||
if err != nil {
|
||||
zapLogger.Error("failed init postgres", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
kafkaWorker, err := privilegewc2.NewKafkaConsumerWorker(privilegewc2.Config{
|
||||
KafkaBroker: cfg.KafkaBrokers,
|
||||
KafkaTopic: cfg.KafkaTopicTariff,
|
||||
ServiceKey: cfg.ServiceName,
|
||||
TickerInterval: time.Second * 10,
|
||||
ErrChan: errChan,
|
||||
}, redisClient, pgdal)
|
||||
if err != nil {
|
||||
zapLogger.Error("Failed start privilege worker", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
checkWorker := privilegewc2.NewCheckWorker(privilegewc2.Deps{
|
||||
PrivilegeIDsDays: []string{"quizUnlimTime", "squizHideBadge"},
|
||||
PrivilegeIDsCount: []string{"quizCnt", "quizManual"},
|
||||
TickerInterval: time.Minute,
|
||||
PrivilegeDAL: pgdal,
|
||||
CustomerClient: clients.CustomerClient,
|
||||
}, errChan)
|
||||
|
||||
go kafkaWorker.Start(ctx)
|
||||
go checkWorker.Start(ctx)
|
||||
|
||||
toClientWorker := answerwc2.NewSendToClient(answerwc2.DepsSendToClient{
|
||||
Redis: redisClient,
|
||||
Dal: pgdal,
|
||||
LeadSenders: leadSenders,
|
||||
CustomerService: clients.CustomerClient,
|
||||
}, errChan)
|
||||
|
||||
toRespWorker := answerwc2.NewRespWorker(answerwc2.DepsRespWorker{
|
||||
Redis: redisClient,
|
||||
Dal: pgdal,
|
||||
MailClient: mailSender,
|
||||
}, errChan)
|
||||
|
||||
go toClientWorker.Start(ctx)
|
||||
go toRespWorker.Start(ctx)
|
||||
|
||||
tow := timeout.New(pgdal, time.Minute)
|
||||
statW := shortstat.New(pgdal, 5*time.Minute)
|
||||
tow.ExposeErr(ctx, &workerErr)
|
||||
statW.ExposeErr(ctx, &workerErr)
|
||||
go tow.Start(ctx)
|
||||
go statW.Start(ctx)
|
||||
|
||||
logger.Emit(InfoSvcReady{})
|
||||
shutdownGroup.Add(closer.CloserFunc(pgdal.Close))
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer timeoutCancel()
|
||||
if err := shutdownGroup.Call(timeoutCtx); err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
zapLogger.Error("Shutdown timed out", zap.Error(err))
|
||||
} else {
|
||||
zapLogger.Error("Failed to shutdown services gracefully", zap.Error(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
zapLogger.Info("Application has stopped")
|
||||
return nil
|
||||
}
|
26
internal/initialize/clients.go
Normal file
26
internal/initialize/clients.go
Normal file
@ -0,0 +1,26 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"gitea.pena/PenaSide/customer/pkg/customer_clients"
|
||||
"gitea.pena/SQuiz/common/clients"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Clients struct {
|
||||
MailClient *clients.SmtpClient
|
||||
CustomerClient *customer_clients.CustomersClient
|
||||
}
|
||||
|
||||
func NewClients(cfg Config, logger *zap.Logger) *Clients {
|
||||
return &Clients{
|
||||
MailClient: clients.NewSmtpClient(clients.Deps{
|
||||
SmtpSender: cfg.Sender,
|
||||
ApiKey: cfg.ApiKey,
|
||||
SmtpApiUrl: cfg.ApiUrl,
|
||||
}),
|
||||
CustomerClient: customer_clients.NewCustomersClient(customer_clients.CustomersClientDeps{
|
||||
Logger: logger,
|
||||
CustomerServiceHost: cfg.CustomerMicroserviceRPCURL,
|
||||
}),
|
||||
}
|
||||
}
|
38
internal/initialize/config.go
Normal file
38
internal/initialize/config.go
Normal file
@ -0,0 +1,38 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/caarlos0/env/v8"
|
||||
"github.com/joho/godotenv"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ServiceName string `env:"SERVICE_NAME" envDefault:"squiz"`
|
||||
KafkaBrokers string `env:"KAFKA_BROKERS" envDefault:"localhost:6379"`
|
||||
KafkaTopicTariff string `env:"KAFKA_TOPIC_TARIFF" envDefault:"test-topic"`
|
||||
LoggerProdMode bool `env:"IS_PROD_LOG" envDefault:"false"`
|
||||
IsProd bool `env:"IS_PROD" envDefault:"false"`
|
||||
S3Endpoint string `env:"S3_ENDPOINT" envDefault:"localhost:3002"`
|
||||
S3AccessKey string `env:"S3_ACCESS_KEY" envDefault:"minio"`
|
||||
S3SecretKey string `env:"S3_SECRET_KEY" envDefault:"miniostorage"`
|
||||
PostgresURL string `env:"POSTGRES_URL" envDefault:"host=localhost port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"`
|
||||
RedisHost string `env:"REDIS_HOST"`
|
||||
RedisPassword string `env:"REDIS_PASSWORD"`
|
||||
RedisDB uint64 `env:"REDIS_DB"`
|
||||
Sender string `env:"MAIL_SENDER" envDefault:"noreply@mailing.pena.digital"`
|
||||
ApiKey string `env:"MAIL_API_KEY" envDefault:"P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev"`
|
||||
ApiUrl string `env:"API_URL" envDefault:"https://api.smtp.bz/v1/smtp/send"`
|
||||
CustomerMicroserviceRPCURL string `env:"CUSTOMER_MICROSERVICE_RPC_URL" envDefault:"localhost:9001"`
|
||||
TelegramToken string `env:"TELEGRAM_TOKEN"`
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Print("No .env file found")
|
||||
}
|
||||
var config Config
|
||||
if err := env.Parse(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
17
internal/initialize/minio.go
Normal file
17
internal/initialize/minio.go
Normal file
@ -0,0 +1,17 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
)
|
||||
|
||||
func NewMinio(cfg Config) (*minio.Client, error) {
|
||||
minioClient, err := minio.New(cfg.S3Endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(cfg.S3AccessKey, cfg.S3SecretKey, ""),
|
||||
Secure: cfg.IsProd,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return minioClient, nil
|
||||
}
|
21
internal/initialize/redis.go
Normal file
21
internal/initialize/redis.go
Normal file
@ -0,0 +1,21 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
func Redis(ctx context.Context, cfg Config) (*redis.Client, error) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: cfg.RedisHost,
|
||||
Password: cfg.RedisPassword,
|
||||
DB: int(cfg.RedisDB),
|
||||
})
|
||||
|
||||
status := rdb.Ping(ctx)
|
||||
if err := status.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rdb, nil
|
||||
}
|
96
internal/privilegewc/check.go
Normal file
96
internal/privilegewc/check.go
Normal file
@ -0,0 +1,96 @@
|
||||
package privilegewc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"gitea.pena/PenaSide/customer/pkg/customer_clients"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
PrivilegeDAL *dal.DAL
|
||||
TickerInterval time.Duration
|
||||
PrivilegeIDsDays []string
|
||||
PrivilegeIDsCount []string
|
||||
CustomerClient *customer_clients.CustomersClient
|
||||
}
|
||||
|
||||
type CheckWorker struct {
|
||||
privilegeDAL *dal.DAL
|
||||
tickerInterval time.Duration
|
||||
errChan chan<- error
|
||||
privilegeIDsDays []string
|
||||
privilegeIDsCount []string
|
||||
customerClient *customer_clients.CustomersClient
|
||||
}
|
||||
|
||||
func NewCheckWorker(deps Deps, errChan chan<- error) *CheckWorker {
|
||||
return &CheckWorker{
|
||||
privilegeDAL: deps.PrivilegeDAL,
|
||||
tickerInterval: deps.TickerInterval,
|
||||
errChan: errChan,
|
||||
privilegeIDsCount: deps.PrivilegeIDsCount,
|
||||
privilegeIDsDays: deps.PrivilegeIDsDays,
|
||||
customerClient: deps.CustomerClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *CheckWorker) Start(ctx context.Context) {
|
||||
ticker := time.NewTicker(w.tickerInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
fmt.Println("CHECK")
|
||||
w.deleteExpired(ctx)
|
||||
case <-ctx.Done():
|
||||
fmt.Println("Check worker terminated")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *CheckWorker) deleteExpired(ctx context.Context) {
|
||||
var toHistory []customer_clients.InsertHistoryDeps
|
||||
var expiredData []model.ExpiredPrivileges
|
||||
|
||||
for _, id := range w.privilegeIDsDays {
|
||||
expired, err := w.privilegeDAL.AccountRepo.GetExpired(ctx, id)
|
||||
if err != nil {
|
||||
w.errChan <- err
|
||||
}
|
||||
expiredData = append(expiredData, expired...)
|
||||
}
|
||||
|
||||
for _, id := range w.privilegeIDsCount {
|
||||
expired, err := w.privilegeDAL.AccountRepo.GetExpiredCount(ctx, id)
|
||||
if err != nil {
|
||||
w.errChan <- err
|
||||
}
|
||||
expiredData = append(expiredData, expired...)
|
||||
}
|
||||
|
||||
for _, data := range expiredData {
|
||||
err := w.privilegeDAL.AccountRepo.DeletePrivilegeByID(ctx, data.Privilege.ID)
|
||||
if err != nil {
|
||||
w.errChan <- err
|
||||
continue
|
||||
}
|
||||
|
||||
toHistory = append(toHistory, customer_clients.InsertHistoryDeps{
|
||||
UserID: data.UserID,
|
||||
Comment: fmt.Sprintf("%s privilege has expired, it was created at %d", data.Privilege.PrivilegeID, data.Privilege.CreatedAt.Unix()),
|
||||
Key: "privilege_expired",
|
||||
})
|
||||
}
|
||||
|
||||
for _, to := range toHistory {
|
||||
err := w.customerClient.InsertHistory(ctx, to)
|
||||
if err != nil {
|
||||
w.errChan <- err
|
||||
}
|
||||
}
|
||||
}
|
@ -3,12 +3,11 @@ package privilegewc
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/themakers/hlog"
|
||||
"github.com/twmb/franz-go/pkg/kgo"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"gitea.pena/SQuiz/worker/wctools"
|
||||
"gitea.pena/SQuiz/worker/internal/wctools"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/twmb/franz-go/pkg/kgo"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -19,7 +18,6 @@ type Config struct {
|
||||
KafkaTopic string
|
||||
ServiceKey string
|
||||
TickerInterval time.Duration
|
||||
Logger hlog.Logger
|
||||
ErrChan chan<- error
|
||||
}
|
||||
|
@ -2,18 +2,18 @@ package privilegewc
|
||||
|
||||
import (
|
||||
"context"
|
||||
dal2 "gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProcessValidMessage(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dal, err := dal2.New(ctx, "host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable", nil)
|
||||
d, err := dal.New(ctx, "host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfg, err := NewKafkaConsumerWorker(Config{}, nil, dal)
|
||||
cfg, err := NewKafkaConsumerWorker(Config{}, nil, d)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package mailclient
|
||||
package senders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
_ "embed"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"golang.org/x/net/html"
|
||||
"html/template"
|
||||
"io"
|
||||
@ -12,60 +14,44 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LineWriter struct {
|
||||
w io.Writer
|
||||
length int
|
||||
type LeadSender interface {
|
||||
SendLead(leadData LeadData) error
|
||||
Name() string
|
||||
}
|
||||
|
||||
func NewLineWriter(w io.Writer, length int) *LineWriter {
|
||||
return &LineWriter{
|
||||
w: w,
|
||||
length: length,
|
||||
}
|
||||
type LeadData struct {
|
||||
To interface{}
|
||||
Subject string
|
||||
Template string
|
||||
TemplateData TemplateData
|
||||
}
|
||||
|
||||
func (r *LineWriter) Write(p []byte) (n int, err error) {
|
||||
for i := 0; i < len(p); i += r.length {
|
||||
end := i + r.length
|
||||
type TemplateData struct {
|
||||
QuizConfig model.ResultInfo
|
||||
AnswerContent model.ResultContent
|
||||
AllAnswers []model.ResultAnswer
|
||||
QuestionsMap map[uint64]string
|
||||
AnswerTime string
|
||||
}
|
||||
|
||||
if end > len(p) {
|
||||
end = len(p) - 1
|
||||
}
|
||||
|
||||
var chunk []byte
|
||||
chunk = append(chunk, p[i:end]...)
|
||||
|
||||
if len(p) >= end+r.length {
|
||||
chunk = append(chunk, []byte("\r\n")...)
|
||||
}
|
||||
|
||||
addN, err := r.w.Write(chunk)
|
||||
func generateTextFromTemplate(data TemplateData, tpl string) (string, error) {
|
||||
t, err := template.New("email").Funcs(tmplFuncs).Parse(tpl)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n += addN
|
||||
return "", fmt.Errorf("error parsing template: %w", err)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (r *LineWriter) WriteString(s string) (n int, err error) {
|
||||
p := []byte(s)
|
||||
return r.Write(p)
|
||||
}
|
||||
|
||||
func (r *LineWriter) WriteFormatString(format string, a ...any) (n int, err error) {
|
||||
p := []byte(fmt.Sprintf(format, a...))
|
||||
return r.Write(p)
|
||||
}
|
||||
|
||||
func randomBoundary() string {
|
||||
var buf [30]byte
|
||||
_, err := io.ReadFull(rand.Reader, buf[:])
|
||||
if err != nil {
|
||||
panic(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 fmt.Sprintf("%x", buf[:])
|
||||
|
||||
return text.String(), nil
|
||||
}
|
||||
|
||||
var tmplFuncs = template.FuncMap{
|
||||
@ -84,11 +70,9 @@ func RenderImage(content string) template.HTML {
|
||||
cnt = strings.TrimSuffix(cnt, "`")
|
||||
}
|
||||
|
||||
fmt.Println("RI1", cnt)
|
||||
var res model.ImageContent
|
||||
err := json.Unmarshal([]byte(cnt), &res)
|
||||
if err != nil {
|
||||
fmt.Println("RI1", content)
|
||||
return SplitContent(content)
|
||||
} else {
|
||||
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)))
|
||||
@ -102,14 +86,21 @@ 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>`, strings.Replace(url,"http","https",1), filename))
|
||||
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"))
|
||||
}
|
||||
return template.HTML(strings.ReplaceAll(strings.ReplaceAll(content,"`,`","<br>"),"`",""))
|
||||
|
||||
filename := string(filenameBytes)
|
||||
|
||||
return template.HTML(fmt.Sprintf(`<a href="%s" download>%s</a>`, url, filename))
|
||||
}
|
||||
return template.HTML(strings.ReplaceAll(content, "\n", "<br>"))
|
||||
}
|
||||
|
||||
func sanitizeHTMLData(data EmailTemplateData) EmailTemplateData {
|
||||
sanitized := EmailTemplateData{
|
||||
func sanitizeHTMLData(data TemplateData) TemplateData {
|
||||
sanitized := TemplateData{
|
||||
QuizConfig: stripHTMLResultInfo(data.QuizConfig),
|
||||
AnswerContent: stripHTMLResultContent(data.AnswerContent),
|
||||
AllAnswers: stripHTMLResultAnswers(data.AllAnswers),
|
43
internal/senders/mail_sender.go
Normal file
43
internal/senders/mail_sender.go
Normal file
@ -0,0 +1,43 @@
|
||||
package senders
|
||||
|
||||
import (
|
||||
"gitea.pena/SQuiz/common/clients"
|
||||
)
|
||||
|
||||
type MailLeadSender struct {
|
||||
client *clients.SmtpClient
|
||||
}
|
||||
|
||||
func NewMailLeadSender(client *clients.SmtpClient) *MailLeadSender {
|
||||
return &MailLeadSender{client: client}
|
||||
}
|
||||
|
||||
func (m *MailLeadSender) SendLead(data LeadData) error {
|
||||
err := m.SendMailWithAttachment(data.To.(string), data.Subject, data.Template, data.TemplateData, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MailLeadSender) SendMailWithAttachment(recipient, subject string, emailTemplate string, data TemplateData, attachments []clients.Attachment) error {
|
||||
sanitizedData := sanitizeHTMLData(data)
|
||||
text, err := generateTextFromTemplate(sanitizedData, emailTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := clients.Message{
|
||||
To: recipient,
|
||||
Subject: subject,
|
||||
HtmlBody: text,
|
||||
Attachments: attachments,
|
||||
}
|
||||
|
||||
return m.client.MailSender(msg)
|
||||
}
|
||||
|
||||
func (m *MailLeadSender) Name() string {
|
||||
return "mail"
|
||||
}
|
51
internal/senders/tg_sender.go
Normal file
51
internal/senders/tg_sender.go
Normal file
@ -0,0 +1,51 @@
|
||||
package senders
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/tucnak/telebot.v2"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TgSender struct {
|
||||
bot *telebot.Bot
|
||||
}
|
||||
|
||||
func NewTgSender(tgToken string) (*TgSender, error) {
|
||||
bot, err := telebot.NewBot(telebot.Settings{
|
||||
Token: tgToken,
|
||||
Poller: &telebot.LongPoller{Timeout: 10 * time.Second},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating Telegram bot: %w", err)
|
||||
}
|
||||
|
||||
return &TgSender{bot: bot}, nil
|
||||
}
|
||||
|
||||
func (tg *TgSender) SendLead(data LeadData) error {
|
||||
text, err := generateTextFromTemplate(data.TemplateData, data.Template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chatStr := data.To.(string)
|
||||
|
||||
chat, err := strconv.ParseInt(chatStr, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tg.bot.Send(telebot.ChatID(chat), text, &telebot.SendOptions{
|
||||
ParseMode: telebot.ModeHTML,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending Telegram message: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tg *TgSender) Name() string {
|
||||
return "telegram"
|
||||
}
|
19
internal/senders/whatsapp_sender.go
Normal file
19
internal/senders/whatsapp_sender.go
Normal file
@ -0,0 +1,19 @@
|
||||
package senders
|
||||
|
||||
import "fmt"
|
||||
|
||||
type WhatsAppSender struct {
|
||||
}
|
||||
|
||||
func NewWhatsAppSender() *WhatsAppSender {
|
||||
return &WhatsAppSender{}
|
||||
}
|
||||
|
||||
func (wa *WhatsAppSender) SendLead(data LeadData) error {
|
||||
fmt.Println("TODO")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wa *WhatsAppSender) Name() string {
|
||||
return "whatsapp"
|
||||
}
|
@ -3,12 +3,12 @@ package wctools
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"fmt"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"gitea.pena/SQuiz/common/model/tariff"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"strings"
|
||||
"time"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var DaysOfWeek = map[string]string{
|
||||
@ -143,3 +143,14 @@ func ToJSON(data interface{}) (string, error) {
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
func CleanNullContent(answers []model.ResultAnswer) []model.ResultAnswer {
|
||||
var results []model.ResultAnswer
|
||||
for _, answer := range answers {
|
||||
answer.Content = strings.ReplaceAll(strings.ReplaceAll(answer.Content, "`,`", "`<br>`"),"\n","<br>")
|
||||
if answer.Content != "" {
|
||||
results = append(results, answer)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/worker/workers"
|
||||
"gitea.pena/SQuiz/worker/internal/workers"
|
||||
"time"
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/worker/workers"
|
||||
"gitea.pena/SQuiz/worker/internal/workers"
|
||||
"time"
|
||||
)
|
||||
|
10
main.go
10
main.go
@ -1,10 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/skeris/appInit"
|
||||
"gitea.pena/SQuiz/worker/app"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appInit.Initialize(app.New, app.Options{})
|
||||
}
|
37
pkg/closer/closer.go
Normal file
37
pkg/closer/closer.go
Normal file
@ -0,0 +1,37 @@
|
||||
package closer
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Closer interface {
|
||||
Close(ctx context.Context) error
|
||||
}
|
||||
|
||||
type CloserFunc func(ctx context.Context) error
|
||||
|
||||
func (cf CloserFunc) Close(ctx context.Context) error {
|
||||
return cf(ctx)
|
||||
}
|
||||
|
||||
type CloserGroup struct {
|
||||
closers []Closer
|
||||
}
|
||||
|
||||
func NewCloserGroup() *CloserGroup {
|
||||
return &CloserGroup{}
|
||||
}
|
||||
|
||||
func (cg *CloserGroup) Add(c Closer) {
|
||||
cg.closers = append(cg.closers, c)
|
||||
}
|
||||
|
||||
func (cg *CloserGroup) Call(ctx context.Context) error {
|
||||
var closeErr error
|
||||
for i := len(cg.closers) - 1; i >= 0; i-- {
|
||||
if err := cg.closers[i].Close(ctx); err != nil && closeErr == nil {
|
||||
closeErr = err
|
||||
}
|
||||
}
|
||||
return closeErr
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
package privilegewc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/themakers/hlog"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CheckWorkerConfig struct {
|
||||
TickerInterval time.Duration
|
||||
DefaultData model.DefaultData
|
||||
Logger hlog.Logger
|
||||
ErrChan chan<- error
|
||||
}
|
||||
|
||||
type CheckWorker struct {
|
||||
config CheckWorkerConfig
|
||||
privilegeDAL *dal.DAL
|
||||
}
|
||||
|
||||
func NewCheckWorker(config CheckWorkerConfig, privilegeDAL *dal.DAL) *CheckWorker {
|
||||
return &CheckWorker{
|
||||
config: config,
|
||||
privilegeDAL: privilegeDAL,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *CheckWorker) Start(ctx context.Context) {
|
||||
ticker := time.NewTicker(w.config.TickerInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
fmt.Println("CHECK")
|
||||
w.performScheduledTasks(ctx)
|
||||
case <-ctx.Done():
|
||||
fmt.Println("Check worker terminated")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Maybe one query?
|
||||
func (w *CheckWorker) performScheduledTasks(ctx context.Context) {
|
||||
fmt.Println("CHEC0")
|
||||
w.deleteExpired(ctx)
|
||||
}
|
||||
|
||||
func (w *CheckWorker) deleteExpired(ctx context.Context) {
|
||||
expiredData, err := w.privilegeDAL.AccountRepo.GetExpired(ctx, w.config.DefaultData.UnlimID)
|
||||
if err != nil {
|
||||
w.config.Logger.Module("Error getting expired quizUnlimTime records")
|
||||
w.config.ErrChan <- err
|
||||
}
|
||||
|
||||
for _, data := range expiredData {
|
||||
err := w.privilegeDAL.AccountRepo.DeletePrivilegeByID(ctx, data.Privilege.ID)
|
||||
if err != nil {
|
||||
w.config.Logger.Module("Error deleting expired quizUnlimTime record")
|
||||
w.config.ErrChan <- err
|
||||
}
|
||||
}
|
||||
}
|
17
tests/img_contents.json
Normal file
17
tests/img_contents.json
Normal file
@ -0,0 +1,17 @@
|
||||
[
|
||||
{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/cq64n9vot84c73evddbg","Description":"Вариант 1"}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/cq64navot84c73evddcg","Description":"Вариант 2"}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmkvot84c739fusn0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmm7ot84c739fuso0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmn7ot84c739fusp0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmo7ot84c739fusq0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmp7ot84c739fusr0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmqfot84c739fuss0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmrnot84c739fust0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalmsnot84c739fusu0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln27ot84c739fusv0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln3fot84c739fut00","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln47ot84c739fut10","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln57ot84c739fut20","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln6fot84c739fut30","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln7fot84c739fut40","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln8fot84c739fut50","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaln9fot84c739fut60","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnafot84c739fut70","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnb7ot84c739fut80","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalncfot84c739fut90","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalndnot84c739futa0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnenot84c739futb0","Description":""},
|
||||
{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnfnot84c739futc0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalngvot84c739futd0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalni7ot84c739fute0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnj7ot84c739futf0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnjvot84c739futg0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnkvot84c739futh0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnlnot84c739futi0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnn7ot84c739futj0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalno7ot84c739futk0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnp7ot84c739futl0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnq7ot84c739futm0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnr7ot84c739futn0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalns7ot84c739futo0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnt7ot84c739futp0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalnu7ot84c739futq0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo07ot84c739futr0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo1fot84c739futs0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo2fot84c739futt0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo37ot84c739futu0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo47ot84c739futv0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo57ot84c739fuu00","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo67ot84c739fuu10","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo6vot84c739fuu20","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo87ot84c739fuu30","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalo97ot84c739fuu40","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaloa7ot84c739fuu50","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalob7ot84c739fuu60","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalobvot84c739fuu70","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalocnot84c739fuu80","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalodfot84c739fuu90","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csaloefot84c739fuua0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalof7ot84c739fuub0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalofvot84c739fuuc0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalogvot84c739fuud0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalohvot84c739fuue0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalot7ot84c739fuuf0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalou7ot84c739fuug0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalouvot84c739fuuh0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp07ot84c739fuui0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp17ot84c739fuuj0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp27ot84c739fuuk0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp37ot84c739fuul0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp4fot84c739fuum0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp5vot84c739fuun0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp6not84c739fuuo0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp7fot84c739fuup0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp8fot84c739fuuq0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalp9vot84c739fuur0","Description":""}
|
||||
,{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpanot84c739fuus0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpbfot84c739fuut0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpc7ot84c739fuuu0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpd7ot84c739fuuv0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpenot84c739fuv00","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpffot84c739fuv10","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpgfot84c739fuv20","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalph7ot84c739fuv30","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalphvot84c739fuv40","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpinot84c739fuv50","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpjnot84c739fuv60","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpkfot84c739fuv70","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpl7ot84c739fuv80","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalplvot84c739fuv90","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpn7ot84c739fuva0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpnvot84c739fuvb0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpofot84c739fuvc0","Description":""},{"Image":"https://s3.timeweb.cloud/3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b/squizimages/33ec973f-b4bd-4215-bb13-b98914161908/csalpp7ot84c739fuvd0","Description":""}]
|
||||
|
||||
|
||||
|
26
tests/mail/client_tg.tmpl
Normal file
26
tests/mail/client_tg.tmpl
Normal file
@ -0,0 +1,26 @@
|
||||
*Квиз для вашего бизнеса*
|
||||
|
||||
*Поступила новая заявка с квиза "{{.QuizConfig.Theme}}"!*
|
||||
Время заявки: {{ .AnswerTime }}
|
||||
|
||||
Имя: {{ .AnswerContent.Name }}
|
||||
{{ if .AnswerContent.Email }}Email: {{ .AnswerContent.Email }}{{ end }}
|
||||
{{ if .AnswerContent.Phone }}Телефон: {{ .AnswerContent.Phone }}{{ end }}
|
||||
{{ if .AnswerContent.Telegram }}Telegram: {{ .AnswerContent.Telegram }}{{ end }}
|
||||
{{ if .AnswerContent.Wechat }}Wechat: {{ .AnswerContent.Wechat }}{{ end }}
|
||||
{{ if .AnswerContent.Viber }}Viber: {{ .AnswerContent.Viber }}{{ end }}
|
||||
{{ if .AnswerContent.Vk }}Vk: {{ .AnswerContent.Vk }}{{ end }}
|
||||
{{ if .AnswerContent.Skype }}Skype: {{ .AnswerContent.Skype }}{{ end }}
|
||||
{{ if .AnswerContent.Whatsup }}Whatsup: {{ .AnswerContent.Whatsup }}{{ end }}
|
||||
{{ if .AnswerContent.Messenger }}Messenger: {{ .AnswerContent.Messenger }}{{ end }}
|
||||
{{ if .AnswerContent.Address }}Адрес: {{ .AnswerContent.Address }}{{ end }}
|
||||
{{ range $key, $value := .AnswerContent.Custom }}{{ $key }}: {{ $value }}{{ end }}
|
||||
|
||||
*Ответы:*
|
||||
{{ range .AllAnswers }}
|
||||
{{ if index $.QuestionsMap .AnswerID }}
|
||||
*{{ index $.QuestionsMap .AnswerID }}*
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
@ -1,7 +1,23 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.pena/SQuiz/common/clients"
|
||||
"gitea.pena/SQuiz/common/dal"
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
"gitea.pena/SQuiz/worker/internal/answerwc"
|
||||
senders2 "gitea.pena/SQuiz/worker/internal/senders"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pioz/faker"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:embed mail/to_client.tmpl
|
||||
@ -10,158 +26,269 @@ var toClientTemplate string
|
||||
//go:embed mail/reminder.tmpl
|
||||
var reminderTemplate string
|
||||
|
||||
//func TestProcessMessageToSMTP(t *testing.T) {
|
||||
// clientDeps := mailclient.ClientDeps{
|
||||
// Host: "connect.mailclient.bz",
|
||||
// Port: "587",
|
||||
// Sender: "skeris@mailing.pena.digital",
|
||||
// ApiKey: "P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev",
|
||||
//go:embed mail/client_tg.tmpl
|
||||
var tgClientTemplate string
|
||||
|
||||
func TestProcessMessageToSMTP(t *testing.T) {
|
||||
clientDeps := clients.Deps{
|
||||
SmtpHost: "connect.mailclient.bz",
|
||||
SmtpPort: "587",
|
||||
SmtpSender: "skeris@mailing.pena.digital",
|
||||
ApiKey: "P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev",
|
||||
SmtpApiUrl: "https://api.smtp.bz/v1/smtp/send",
|
||||
FiberClient: &fiber.Client{},
|
||||
}
|
||||
|
||||
client := clients.NewSmtpClient(clientDeps)
|
||||
|
||||
recipient := "pashamullin2001@gmail.com"
|
||||
subject := "Test"
|
||||
|
||||
data := senders2.TemplateData{
|
||||
QuizConfig: model.ResultInfo{
|
||||
Theme: "<h1>Taemplste Quiz</h1>",
|
||||
},
|
||||
AnswerContent: model.ResultContent{
|
||||
Name: "<a>Pasha</a>",
|
||||
Phone: "<div>+723456789<div",
|
||||
Email: "test@example.com",
|
||||
//Adress: "chtoto tam",
|
||||
Telegram: "<br>@test</br>",
|
||||
Wechat: "<span>test_wechat</span>",
|
||||
Viber: "<span><span>+723456789</span></span>",
|
||||
Vk: "<body>test_vk<body>",
|
||||
Skype: "test_skype",
|
||||
Whatsup: "test_whatsup",
|
||||
Messenger: "test_messenger",
|
||||
},
|
||||
AllAnswers: []model.ResultAnswer{
|
||||
{AnswerID: 1, QuestionID: 1, Content: "https://www.google.com/search?sca_esv=c51a80de1a7d45f0&sxsrf=ACQVn08xG-a0eH1Vds246-fONoSvvjzVMw:1707762485524&q=ku,n&tbm=isch&source=lnms&sa=X&ved=2ahUKEwi7ub2Ct6aEAxVVb_UHHQIQBVoQ0pQJegQIDRAB&biw=1536&bih=703&dpr=1.25#imgrc=0PWwTuuH2uBQ3M|html", CreatedAt: time.Now()},
|
||||
{AnswerID: 2, QuestionID: 2, Content: "From a friend", CreatedAt: time.Now()},
|
||||
{AnswerID: 3, QuestionID: 3, Content: "From a friend", CreatedAt: time.Now()},
|
||||
{AnswerID: 4, QuestionID: 4, Content: `{"Image":"https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg","Description":"Gekon"}`, CreatedAt: time.Now()},
|
||||
},
|
||||
QuestionsMap: map[uint64]string{
|
||||
1: "?",
|
||||
2: "How did you hear about us?",
|
||||
3: "que 3",
|
||||
4: "que 4",
|
||||
},
|
||||
AnswerTime: time.Now().Format("Monday, 2 January 2006 г., 15:04 UTC-07:00"),
|
||||
}
|
||||
|
||||
mailSender := senders2.NewMailLeadSender(client)
|
||||
err := mailSender.SendMailWithAttachment(recipient, subject, toClientTemplate, data, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Error sending email: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProcessReminderToClient(t *testing.T) {
|
||||
clientDeps := clients.Deps{
|
||||
SmtpHost: "connect.mailclient.bz",
|
||||
SmtpPort: "587",
|
||||
SmtpSender: "skeris@mailing.pena.digital",
|
||||
ApiKey: "P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev",
|
||||
FiberClient: &fiber.Client{},
|
||||
}
|
||||
|
||||
client := clients.NewSmtpClient(clientDeps)
|
||||
|
||||
recipient := "mullinp@internet.ru"
|
||||
subject := "Test Reminder"
|
||||
|
||||
quizConfig := model.ResultInfo{
|
||||
ReplName: "Test Quiz",
|
||||
Reply: "mullinp@internet.ru",
|
||||
Theme: "Reminder Theme",
|
||||
}
|
||||
|
||||
mailSender := senders2.NewMailLeadSender(client)
|
||||
|
||||
err := mailSender.SendMailWithAttachment(recipient, subject, reminderTemplate, senders2.TemplateData{
|
||||
QuizConfig: quizConfig,
|
||||
AnswerContent: model.ResultContent{},
|
||||
AllAnswers: []model.ResultAnswer{},
|
||||
QuestionsMap: nil,
|
||||
}, nil)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error sending email: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessMessageToClient(t *testing.T) {
|
||||
smtpData := clients.Deps{
|
||||
SmtpApiUrl: "https://api.smtp.bz/v1/smtp/send",
|
||||
SmtpHost: "connect.mailclient.bz",
|
||||
SmtpPort: "587",
|
||||
SmtpSender: "skeris@mailing.pena.digital",
|
||||
ApiKey: "P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev",
|
||||
FiberClient: &fiber.Client{},
|
||||
}
|
||||
|
||||
mailClient := clients.NewSmtpClient(smtpData)
|
||||
mailSender := senders2.NewMailLeadSender(mailClient)
|
||||
tgSender, err := senders2.NewTgSender("6712573453:AAFqTOsgwe_j48ZQ1GzWKQDT5Nwr-SAWjz8")
|
||||
assert.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
repo, err := dal.New(ctx, "host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
deps := answerwc.DepsSendToClient{
|
||||
Redis: nil,
|
||||
Dal: repo,
|
||||
LeadSenders: []senders2.LeadSender{mailSender, tgSender},
|
||||
CustomerService: nil,
|
||||
}
|
||||
|
||||
errChan := make(chan<- error)
|
||||
|
||||
w := answerwc.NewSendToClient(deps, errChan)
|
||||
|
||||
quizConfig := model.QuizConfig{
|
||||
Mailing: model.ResultInfo{
|
||||
Theme: faker.String(),
|
||||
},
|
||||
}
|
||||
|
||||
questionsMap := map[uint64]string{
|
||||
1: faker.String(),
|
||||
2: faker.String(),
|
||||
}
|
||||
|
||||
account := model.Account{
|
||||
UserID: "64f2cd7a7047f28fdabf6d9e",
|
||||
}
|
||||
|
||||
file, err := os.Open("img_contents.json")
|
||||
if err != nil {
|
||||
fmt.Println("Failed to open file", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
bytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to read file", err)
|
||||
return
|
||||
}
|
||||
|
||||
var items []model.ImageContent
|
||||
err = json.Unmarshal(bytes, &items)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to unmarshal JSON:", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(len(items))
|
||||
|
||||
allAnswers := []model.ResultAnswer{
|
||||
{
|
||||
Content: "",
|
||||
AnswerID: 2,
|
||||
QuestionID: 2,
|
||||
},
|
||||
}
|
||||
|
||||
answersIMG := model.ResultAnswer{
|
||||
Content: "",
|
||||
AnswerID: 1,
|
||||
QuestionID: 1,
|
||||
}
|
||||
|
||||
var imageLinks []string
|
||||
|
||||
for _, item := range items {
|
||||
|
||||
str, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
fmt.Println("error marshal item to str:", err)
|
||||
return
|
||||
}
|
||||
imageLinks = append(imageLinks, fmt.Sprintf("`%s`", string(str)))
|
||||
}
|
||||
|
||||
fmt.Println(len(imageLinks))
|
||||
|
||||
answersIMG.Content = strings.Join(imageLinks, ",")
|
||||
|
||||
allAnswers = append(allAnswers, answersIMG)
|
||||
|
||||
answerContent := model.ResultContent{
|
||||
Name: "Pasha",
|
||||
Phone: "+723456789",
|
||||
Email: "test@example.com",
|
||||
//Adress: "chtoto tam",
|
||||
Telegram: "@test",
|
||||
Wechat: "test_wechat",
|
||||
Viber: "+723456789",
|
||||
Vk: "test_vk",
|
||||
Skype: "test_skype",
|
||||
Whatsup: "test_whatsup",
|
||||
Messenger: "test_messenger",
|
||||
}
|
||||
|
||||
answerTime := time.Now()
|
||||
|
||||
err = w.ProcessMessageToClient(ctx, answerwc.DepsProcessMsgToClient{
|
||||
QuizConfig: quizConfig,
|
||||
QuestionsMap: questionsMap,
|
||||
Account: account,
|
||||
AllAnswers: allAnswers,
|
||||
AnswerContent: answerContent,
|
||||
AnswerTime: answerTime,
|
||||
Quiz: &model.Quiz{
|
||||
Id: 26324,
|
||||
AccountId: "64f2cd7a7047f28fdabf6d9e",
|
||||
},
|
||||
})
|
||||
|
||||
err = w.ProcessReminderToClient(ctx, account, model.QuizConfig{
|
||||
Mailing: model.ResultInfo{
|
||||
When: "email",
|
||||
Theme: fmt.Sprintf("не удалось отправить заявку по опросу\"%s\"", "test"),
|
||||
Reply: "noreply@pena.digital",
|
||||
ReplName: "Reminder",
|
||||
},
|
||||
}, &model.Quiz{
|
||||
Id: 26324,
|
||||
AccountId: "64f2cd7a7047f28fdabf6d9e",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
//func TestManyImg(t *testing.T) {
|
||||
// smtpData := clients.Deps{
|
||||
// SmtpApiUrl: "https://api.smtp.bz/v1/smtp/send",
|
||||
// FiberClient: &fiber.Client{},
|
||||
// }
|
||||
//
|
||||
// client := mailclient.NewClient(clientDeps)
|
||||
//
|
||||
// recipient := "pashamullin2001@gmail.com"
|
||||
// subject := "Test"
|
||||
//
|
||||
// data := mailclient.EmailTemplateData{
|
||||
// QuizConfig: model.ResultInfo{
|
||||
// Theme: "<h1>Taemplste Quiz</h1>",
|
||||
// },
|
||||
// AnswerContent: model.ResultContent{
|
||||
// Name: "<a>Pasha</a>",
|
||||
// Phone: "<div>+723456789<div",
|
||||
// Email: "test@example.com",
|
||||
// //Adress: "chtoto tam",
|
||||
// Telegram: "<br>@test</br>",
|
||||
// Wechat: "<span>test_wechat</span>",
|
||||
// Viber: "<span><span>+723456789</span></span>",
|
||||
// Vk: "<body>test_vk<body>",
|
||||
// Skype: "test_skype",
|
||||
// Whatsup: "test_whatsup",
|
||||
// Messenger: "test_messenger",
|
||||
// },
|
||||
// AllAnswers: []model.ResultAnswer{
|
||||
// {AnswerID: 1, QuestionID: 1, Content: "https://www.google.com/search?sca_esv=c51a80de1a7d45f0&sxsrf=ACQVn08xG-a0eH1Vds246-fONoSvvjzVMw:1707762485524&q=ku,n&tbm=isch&source=lnms&sa=X&ved=2ahUKEwi7ub2Ct6aEAxVVb_UHHQIQBVoQ0pQJegQIDRAB&biw=1536&bih=703&dpr=1.25#imgrc=0PWwTuuH2uBQ3M|html", CreatedAt: time.Now()},
|
||||
// {AnswerID: 2, QuestionID: 2, Content: "From a friend", CreatedAt: time.Now()},
|
||||
// {AnswerID: 3, QuestionID: 3, Content: "From a friend", CreatedAt: time.Now()},
|
||||
// {AnswerID: 4, QuestionID: 4, Content: `{"Image":"https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg","Description":"Gekon"}`, CreatedAt: time.Now()},
|
||||
// },
|
||||
// QuestionsMap: map[uint64]string{
|
||||
// 1: "?",
|
||||
// 2: "How did you hear about us?",
|
||||
// 3: "que 3",
|
||||
// 4: "que 4",
|
||||
// },
|
||||
// AnswerTime: time.Now().Format("Monday, 2 January 2006 г., 15:04 UTC-07:00"),
|
||||
// }
|
||||
//
|
||||
// err := client.SendMailWithAttachment(recipient, subject, toClientTemplate, data, nil)
|
||||
// if err != nil {
|
||||
// t.Errorf("Error sending email: %v", err)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func TestProcessReminderToClient(t *testing.T) {
|
||||
// clientDeps := mailclient.ClientDeps{
|
||||
// Host: "connect.mailclient.bz",
|
||||
// Port: "587",
|
||||
// Sender: "skeris@mailing.pena.digital",
|
||||
// SmtpHost: "connect.mailclient.bz",
|
||||
// SmtpPort: "587",
|
||||
// SmtpSender: "skeris@mailing.pena.digital",
|
||||
// ApiKey: "P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev",
|
||||
// FiberClient: &fiber.Client{},
|
||||
// }
|
||||
//
|
||||
// client := mailclient.NewClient(clientDeps)
|
||||
// mailClient := clients.NewSmtpClient(smtpData)
|
||||
// mailSender := senders.NewMailLeadSender(mailClient)
|
||||
// tgSender, err := senders.NewTgSender("6712573453:AAFqTOsgwe_j48ZQ1GzWKQDT5Nwr-SAWjz8")
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// recipient := "mullinp@internet.ru"
|
||||
// subject := "Test Reminder"
|
||||
// ctx := context.Background()
|
||||
//
|
||||
// quizConfig := model.ResultInfo{
|
||||
// ReplName: "Test Quiz",
|
||||
// Reply: "mullinp@internet.ru",
|
||||
// Theme: "Reminder Theme",
|
||||
// }
|
||||
//
|
||||
// err := client.SendMailWithAttachment(recipient, subject, reminderTemplate, mailclient.EmailTemplateData{
|
||||
// QuizConfig: quizConfig,
|
||||
// AnswerContent: model.ResultContent{},
|
||||
// AllAnswers: []model.ResultAnswer{},
|
||||
// QuestionsMap: nil,
|
||||
// }, nil)
|
||||
//
|
||||
// if err != nil {
|
||||
// t.Errorf("Error sending email: %v", err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestProcessMessageToClient(t *testing.T) {
|
||||
//
|
||||
// smtpData := mailclient.ClientDeps{
|
||||
// Host: "connect.mailclient.bz",
|
||||
// Port: "587",
|
||||
// Sender: "skeris@mailing.pena.digital",
|
||||
// ApiKey: "P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev",
|
||||
// FiberClient: &fiber.Client{},
|
||||
// }
|
||||
//
|
||||
// mailClient := mailclient.NewClient(smtpData)
|
||||
// repo, err := dal.New(ctx, "host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable", nil)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// deps := answerwc.DepsSendToClient{
|
||||
// Redis: nil,
|
||||
// Dal: nil,
|
||||
// MailClient: mailClient,
|
||||
// Dal: repo,
|
||||
// LeadSenders: []senders.LeadSender{mailSender, tgSender},
|
||||
// CustomerService: nil,
|
||||
// }
|
||||
//
|
||||
// errChan := make(chan<- error)
|
||||
//
|
||||
// w := answerwc.NewSendToClient(deps, nil, errChan)
|
||||
//
|
||||
// quizConfig := model.QuizConfig{
|
||||
// Mailing: model.ResultInfo{
|
||||
// Theme: faker.String(),
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// questionsMap := map[uint64]string{
|
||||
// 1: faker.String(),
|
||||
// 2: faker.String(),
|
||||
// }
|
||||
//
|
||||
// account := model.Account{
|
||||
// Email: "pashamullin2001@gmail.com",
|
||||
// }
|
||||
//
|
||||
// allAnswers := []model.ResultAnswer{
|
||||
// {
|
||||
// Content: `{"Image":"https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg","Description":"Gekon"}`,
|
||||
// AnswerID: 1,
|
||||
// QuestionID: 1,
|
||||
// },
|
||||
// {
|
||||
// AnswerID: 2,
|
||||
// QuestionID: 2,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// answerContent := model.ResultContent{
|
||||
// Name: "Pasha",
|
||||
// Phone: "+723456789",
|
||||
// Email: "test@example.com",
|
||||
// //Adress: "chtoto tam",
|
||||
// Telegram: "@test",
|
||||
// Wechat: "test_wechat",
|
||||
// Viber: "+723456789",
|
||||
// Vk: "test_vk",
|
||||
// Skype: "test_skype",
|
||||
// Whatsup: "test_whatsup",
|
||||
// Messenger: "test_messenger",
|
||||
// }
|
||||
//
|
||||
// answerTime := time.Now()
|
||||
//
|
||||
// err := w.ProcessMessageToClient(quizConfig, questionsMap, account, allAnswers, answerContent, answerTime)
|
||||
//
|
||||
// assert.NoError(t, err)
|
||||
// w := answerwc.NewSendToClient(deps, errChan)
|
||||
//}
|
||||
|
48
tests/tg_sender_test.go
Normal file
48
tests/tg_sender_test.go
Normal file
@ -0,0 +1,48 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"gitea.pena/SQuiz/common/model"
|
||||
senders2 "gitea.pena/SQuiz/worker/internal/senders"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Tg_Sender(t *testing.T) {
|
||||
tg_Sender, err := senders2.NewTgSender("6712573453:AAFqTOsgwe_j48ZQ1GzWKQDT5Nwr-SAWjz8")
|
||||
assert.NoError(t, err)
|
||||
err = tg_Sender.SendLead(senders2.LeadData{
|
||||
To: int64(-1002217604546),
|
||||
Subject: "test_TG_Sender",
|
||||
Template: tgClientTemplate,
|
||||
TemplateData: senders2.TemplateData{
|
||||
QuizConfig: model.ResultInfo{
|
||||
Theme: "Taemplste Quiz",
|
||||
},
|
||||
AnswerContent: model.ResultContent{
|
||||
Name: "Pasha",
|
||||
Phone: "+723456789",
|
||||
Email: "test@example.com",
|
||||
//Adress: "chtoto tam",
|
||||
Telegram: "@test",
|
||||
Wechat: "test_wechat",
|
||||
Viber: "+723456789",
|
||||
Vk: "test_vk",
|
||||
Skype: "test_skype",
|
||||
Whatsup: "test_whatsup",
|
||||
Messenger: "test_messenger",
|
||||
},
|
||||
AllAnswers: []model.ResultAnswer{
|
||||
{AnswerID: 1, QuestionID: 1, Content: "Pasha", CreatedAt: time.Now()},
|
||||
{AnswerID: 2, QuestionID: 2, Content: "From a friend", CreatedAt: time.Now()},
|
||||
},
|
||||
QuestionsMap: map[uint64]string{
|
||||
1: "How did you hear about us?",
|
||||
2: "How did you hear about us?",
|
||||
},
|
||||
AnswerTime: time.Now().Format("Monday, 2 January 2006 г., 15:04 UTC-07:00"),
|
||||
},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
Loading…
Reference in New Issue
Block a user