package service import ( "context" "fmt" "net/mail" "net/smtp" "os" "strconv" "strings" "github.com/gochat/gochat/internal/model" ) const contactExportCompleteSubject = "Your contact's export file is available to download." // ContactExportMailer delivers the administrator notification emitted by // Chatwoot's Account::ContactsExportJob after the CSV artifact is attached. type ContactExportMailer interface { SendContactExportComplete(ctx context.Context, account *model.Account, user *model.User, export *model.ContactExport) error } type SMTPContactExportMailer struct { Address string Port int Username string Password string From string FrontendURL string } func NewEnvContactExportMailer() *SMTPContactExportMailer { return &SMTPContactExportMailer{ Address: strings.TrimSpace(os.Getenv("SMTP_ADDRESS")), Port: envInt("SMTP_PORT", 587), Username: firstEnv("SMTP_USERNAME", "SMTP_LOGIN"), Password: os.Getenv("SMTP_PASSWORD"), From: firstEnv("MAILER_SENDER_EMAIL", "SMTP_FROM"), FrontendURL: strings.TrimRight(os.Getenv("FRONTEND_URL"), "/"), } } func (m *SMTPContactExportMailer) SendContactExportComplete(ctx context.Context, account *model.Account, user *model.User, export *model.ContactExport) error { _ = ctx if m == nil || strings.TrimSpace(m.Address) == "" || user == nil || strings.TrimSpace(user.Email) == "" || export == nil { return nil } fromHeader := strings.TrimSpace(m.From) if fromHeader == "" { fromHeader = "Chatwoot " } fromAddress := fromHeader if parsed, err := mail.ParseAddress(fromHeader); err == nil { fromAddress = parsed.Address } to := strings.TrimSpace(user.Email) body := contactExportEmailBody(account, export, m.FrontendURL) message := smtpMessage(fromHeader, to, contactExportCompleteSubject, body) addr := fmt.Sprintf("%s:%d", strings.TrimSpace(m.Address), m.Port) var auth smtp.Auth if strings.TrimSpace(m.Username) != "" { auth = smtp.PlainAuth("", strings.TrimSpace(m.Username), m.Password, strings.TrimSpace(m.Address)) } return smtp.SendMail(addr, auth, fromAddress, []string{to}, []byte(message)) } func contactExportEmailBody(account *model.Account, export *model.ContactExport, frontendURL string) string { accountName := "your account" if account != nil && strings.TrimSpace(account.Name) != "" { accountName = account.Name } fileURL := export.FileURL if frontendURL != "" && strings.HasPrefix(fileURL, "/") { fileURL = frontendURL + fileURL } return fmt.Sprintf("The contact export for %s is ready.\n\nDownload: %s\n", accountName, fileURL) } func smtpMessage(from, to, subject, body string) string { return strings.Join([]string{ "From: " + from, "To: " + to, "Subject: " + subject, "MIME-Version: 1.0", "Content-Type: text/plain; charset=UTF-8", "", body, }, "\r\n") } func firstEnv(keys ...string) string { for _, key := range keys { if value := strings.TrimSpace(os.Getenv(key)); value != "" { return value } } return "" } func envInt(key string, fallback int) int { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback } parsed, err := strconv.Atoi(value) if err != nil || parsed <= 0 { return fallback } return parsed }