mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-06 08:01:24 +00:00
86 lines
3.2 KiB
Go
86 lines
3.2 KiB
Go
package tasks
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"github.com/warmbly/warmbly/internal/models"
|
|
)
|
|
|
|
// GetCampaignSequences returns the sequences for a campaign ordered by position
|
|
func (s *tasksService) GetCampaignSequences(ctx context.Context, campaignID uuid.UUID) ([]models.Sequence, error) {
|
|
return s.campaignRepo.GetSequencesByCampaignID(ctx, campaignID)
|
|
}
|
|
|
|
// testContact stands in when no real contact is chosen for a test send.
|
|
func testContact(recipient string) models.Contact {
|
|
return models.Contact{
|
|
ID: uuid.New(),
|
|
FirstName: "Test",
|
|
LastName: "Recipient",
|
|
Email: recipient,
|
|
Company: "Test Company",
|
|
}
|
|
}
|
|
|
|
// SendTestEmail renders a campaign step as the send path would and mails it to
|
|
// recipient through one of the organization's mailboxes. contact, when given,
|
|
// is the real contact the copy is rendered for; nil uses a placeholder. The
|
|
// message carries the campaign's attachments, the mailbox signature and the
|
|
// opt-out footer, so what lands in the tester's inbox is what a lead gets.
|
|
func (s *tasksService) SendTestEmail(ctx context.Context, orgID uuid.UUID, accountID uuid.UUID, recipient string, campaign *models.Campaign, sequence *models.Sequence, contact *models.Contact) *errx.Error {
|
|
// Any member allowed to send may test from any of the organization's
|
|
// mailboxes, not only the ones they connected. GetByID is the full row
|
|
// (the org-scoped Get omits worker_id, which the send needs).
|
|
account, err := s.emailRepo.GetByID(ctx, accountID)
|
|
if err != nil || account == nil || account.OrganizationID == nil || *account.OrganizationID != orgID {
|
|
return errx.New(errx.NotFound, "email account not found")
|
|
}
|
|
|
|
renderFor := testContact(recipient)
|
|
if contact != nil {
|
|
renderFor = *contact
|
|
}
|
|
|
|
// A test send carries the real opt-out footer and header so the sender
|
|
// sees exactly what a recipient will, but its link names no contact
|
|
// (uuid.Nil), so clicking it can never suppress anyone.
|
|
optOut := s.resolveOptOut(ctx, orgID, campaign)
|
|
var unsubscribeURL string
|
|
if s.unsubLinks != nil && s.unsubLinks.Enabled() {
|
|
unsubscribeURL = s.unsubLinks.URL(orgID, campaign.ID, uuid.Nil, time.Now())
|
|
}
|
|
|
|
rendered := previewTemplatesWith(sequence.Subject, sequence.BodyHTML, sequence.BodyPlain, renderFor, unsubscribeURL)
|
|
bodyHTML, bodyPlain := finishBody(rendered.BodyHTML, rendered.BodyPlain, campaign.TextOnly, account, &optOut, unsubscribeURL)
|
|
subject := "[TEST] " + rendered.Subject
|
|
|
|
headerURL := ""
|
|
if campaign.UnsubscribeHeader {
|
|
headerURL = unsubscribeURL
|
|
}
|
|
|
|
// Tracking is deliberately off: a test open or click must not count.
|
|
emailMsg := EmailMessage{
|
|
From: account.Email,
|
|
To: []string{recipient},
|
|
Subject: subject,
|
|
BodyHTML: bodyHTML,
|
|
BodyPlain: bodyPlain,
|
|
MessageID: generateMessageID(account.Email),
|
|
IsWarmup: false,
|
|
UnsubscribeURL: headerURL,
|
|
Attachments: s.campaignAttachmentRefs(ctx, campaign.ID, sequence.ID),
|
|
}
|
|
|
|
taskID := uuid.New()
|
|
if err := s.emailSender.Send(ctx, taskID, emailMsg, *account); err != nil {
|
|
return errx.New(errx.Internal, fmt.Sprintf("failed to send test email: %v", err))
|
|
}
|
|
|
|
return nil
|
|
}
|