Exemplo n.º 1
0
        public async Task <DirectEmailHistory> SendDirectAsync(DirectEmailDetails directEmailDetails)
        {
            if (directEmailDetails == null)
            {
                throw new ArgumentNullException(nameof(directEmailDetails));
            }

            string toAddress;
            string toName;
            int    languageId;
            Site   site;

            if (directEmailDetails.ToUserId.HasValue)
            {
                var user = await _userRepository.GetByIdAsync(directEmailDetails.ToUserId
                                                              ?? directEmailDetails.SendingUserId);

                if (string.IsNullOrEmpty(user.Email))
                {
                    _logger.LogError("Unable to send email to user id {UserId}: no email address configured.",
                                     directEmailDetails.ToUserId);
                    throw new GraException($"User id {directEmailDetails.ToUserId} does not have an email address configured.");
                }
                site = await _siteLookupService.GetByIdAsync(user.SiteId);

                toAddress  = user.Email;
                toName     = user.FullName;
                languageId = directEmailDetails.LanguageId ?? (string.IsNullOrEmpty(user.Culture)
                        ? await _languageService.GetDefaultLanguageIdAsync()
                        : await _languageService.GetLanguageIdAsync(user.Culture));
            }
            else
            {
                var user = await _userRepository.GetByIdAsync(directEmailDetails.SendingUserId);

                site = await _siteLookupService.GetByIdAsync(user.SiteId);

                toAddress  = directEmailDetails.ToAddress;
                toName     = directEmailDetails.ToName;
                languageId = directEmailDetails.LanguageId
                             ?? await _languageService.GetLanguageIdAsync(CultureInfo.CurrentCulture.Name);
            }

            if (!SiteCanSendMail(site))
            {
                throw new GraException("Unable to send mail, please ensure from name, from email, and outgoing mail server are configured in Site Management -> Configuration.");
            }

            var history = new DirectEmailHistory
            {
                CreatedBy              = directEmailDetails.SendingUserId,
                FromEmailAddress       = site.FromEmailAddress,
                FromName               = site.FromEmailName,
                IsBulk                 = directEmailDetails.IsBulk,
                LanguageId             = languageId,
                OverrideToEmailAddress = string
                                         .IsNullOrWhiteSpace(_config[ConfigurationKey.EmailOverride])
                    ? null
                    : _config[ConfigurationKey.EmailOverride],
                ToEmailAddress = toAddress,
                ToName         = toName
            };

            if (directEmailDetails.ToUserId.HasValue)
            {
                history.UserId = directEmailDetails.ToUserId.Value;
            }

            DirectEmailTemplate directEmailTemplate
                = await GetDirectEmailTemplateAsync(directEmailDetails.DirectEmailSystemId,
                                                    directEmailDetails.DirectEmailTemplateId,
                                                    history.LanguageId);

            if (directEmailTemplate == null || directEmailTemplate.DirectEmailTemplateText == null)
            {
                // not available in the requested language, use the default language
                history.LanguageId = await _languageService.GetDefaultLanguageIdAsync();

                directEmailTemplate
                    = await GetDirectEmailTemplateAsync(directEmailDetails.DirectEmailSystemId,
                                                        directEmailDetails.DirectEmailTemplateId,
                                                        history.LanguageId);
            }

            history.DirectEmailTemplateId = directEmailTemplate.Id;
            history.EmailBaseId           = directEmailTemplate.EmailBaseId;

            var stubble = new StubbleBuilder().Build();

            history.Subject = await stubble
                              .RenderAsync(directEmailTemplate.DirectEmailTemplateText.Subject,
                                           directEmailDetails.Tags);

            history.BodyText = await stubble
                               .RenderAsync(directEmailTemplate.DirectEmailTemplateText.BodyCommonMark,
                                            directEmailDetails.Tags);

            history.BodyHtml = CommonMark.CommonMarkConverter.Convert(history.BodyText);

            string preview = await stubble
                             .RenderAsync(directEmailTemplate.DirectEmailTemplateText.Preview,
                                          directEmailDetails.Tags);

            string title = await stubble
                           .RenderAsync(directEmailTemplate.DirectEmailTemplateText.Title,
                                        directEmailDetails.Tags);

            string footer = CommonMark.CommonMarkConverter.Convert(await stubble
                                                                   .RenderAsync(directEmailTemplate.DirectEmailTemplateText.Footer,
                                                                                directEmailDetails.Tags));

            history = await InternalSendDirectAsync(site,
                                                    history,
                                                    new Dictionary <string, string>
            {
                { "Footer", footer },
                { "Preview", preview },
                { "Title", title },
                { "BodyHtml", history.BodyHtml },
                { "BodyText", history.BodyText }
            });

            if (directEmailDetails.IsBulk && !directEmailDetails.IsTest)
            {
                if (directEmailDetails.ToUserId.HasValue)
                {
                    history.BodyHtml = null;
                    history.BodyText = null;
                    await _directEmailHistoryRepository.AddSaveNoAuditAsync(history);
                }
                if (!directEmailTemplate.SentBulk)
                {
                    await _directEmailTemplateRepository
                    .UpdateSentBulkAsync(directEmailTemplate.Id);
                }
            }
            else
            {
                if (!directEmailDetails.IsTest)
                {
                    await _directEmailHistoryRepository.AddSaveNoAuditAsync(history);
                }
                await IncrementSentCountAsync(directEmailTemplate.Id);
            }

            return(history);
        }
Exemplo n.º 2
0
        private async Task <JobStatus> SendBulkListAsync(int userId,
                                                         int jobId,
                                                         IProgress <JobStatus> progress,
                                                         JobDetailsSendBulkEmails jobDetails,
                                                         CancellationToken token)
        {
            var sw = System.Diagnostics.Stopwatch.StartNew();

            int emailsSent    = 0;
            int emailsSkipped = 0;
            int userCounter   = 0;

            int addSentCounter = 0;

            var problemEmails = new HashSet <string>();

            var filter = new EmailReminderFilter
            {
                MailingList = jobDetails.MailingList,
                Take        = 30
            };

            var subscribers = await _emailReminderService.GetSubscribersWithCountAsync(filter);

            var subscribedCount = subscribers.Count;

            var subscribed = subscribers.Data;

            _logger.LogInformation("Email job {JobId}: found {Count} subscribed users, processing first batch of {BatchCount}",
                                   jobId,
                                   subscribedCount,
                                   subscribed.Count);

            token.Register(() =>
            {
                _logger.LogWarning("Email job {JobId} for user {UserId} was cancelled after {EmailsSent} sent, {EmailsSkipped} skipped of {SubscribedUsersCount} total in {TimeElapsed}.",
                                   jobId,
                                   userId,
                                   emailsSent,
                                   emailsSkipped,
                                   subscribedCount,
                                   sw.Elapsed.ToString(SpanFormat, CultureInfo.InvariantCulture));
            });

            if (subscribed.Count > 0)
            {
                var site = await _siteLookupService.GetByIdAsync(GetCurrentSiteId());

                var emailDetails = new DirectEmailDetails(site.Name)
                {
                    IsBulk                = true,
                    SendingUserId         = userId,
                    DirectEmailTemplateId = jobDetails.EmailTemplateId
                };

                var elapsedStatus         = sw.Elapsed;
                var elapsedUpdateDbStatus = sw.Elapsed;
                var elapsedLogInfoStatus  = sw.Elapsed;
                var elapsedLogInfoPercent = 0;

                progress.Report(new JobStatus
                {
                    PercentComplete = 0,
                    Title           = $"Sending email...",
                    Status          = $"Preparing to email {subscribed.Count} participants...",
                    Error           = false
                });

                while (subscribed.Count > 0)
                {
                    foreach (var emailReminder in subscribed)
                    {
                        if (problemEmails.Contains(emailReminder.Email))
                        {
                            emailsSkipped++;
                            continue;
                        }

                        bool clearToSend = true;

                        var isParticipant = await _userService
                                            .IsEmailSubscribedAsync(emailReminder.Email);

                        if (isParticipant)
                        {
                            clearToSend = false;
                        }

                        if (emailReminder.SentAt != null || !clearToSend)
                        {
                            // send email
                            _logger.LogTrace("Email job {JobId}: skipping email {Count}/{Total} to {Email}: {Message}",
                                             jobId,
                                             userCounter + 1,
                                             subscribedCount,
                                             emailReminder.Email,
                                             emailReminder.SentAt != null
                                    ? " already sent at " + emailReminder.SentAt
                                    : " is a subscribed participant");

                            emailsSkipped++;
                        }
                        else
                        {
                            // send email
                            _logger.LogTrace("Email job {JobId}: sending email {Count}/{Total} to {Email} with template {EmailTemplate}",
                                             jobId,
                                             userCounter + 1,
                                             subscribedCount,
                                             emailReminder.Email,
                                             jobDetails.EmailTemplateId);

                            // send email to user
                            try
                            {
                                emailDetails.ToAddress  = emailReminder.Email;
                                emailDetails.LanguageId = emailReminder.LanguageId;
                                emailDetails.ClearTags();
                                emailDetails.SetTag("Email", emailReminder.Email);

                                DirectEmailHistory result = null;
                                try
                                {
                                    result = await _emailService.SendDirectAsync(emailDetails);

                                    await _emailReminderService.UpdateSentDateAsync(emailReminder.Id);
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogWarning("Unable to email {ToAddress}: {ErrorMessage}",
                                                       emailDetails.ToAddress,
                                                       ex.Message);
                                }

                                if (result?.Successful == true)
                                {
                                    addSentCounter++;
                                    emailsSent++;
                                }
                                else
                                {
                                    problemEmails.Add(emailReminder.Email);
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.LogError(ex,
                                                 "Email job {JobId}: Send failed to {UserId} at {Email}: {ErrorMessage}",
                                                 jobId,
                                                 emailReminder.Id,
                                                 emailReminder.Email,
                                                 ex.Message);

                                problemEmails.Add(emailReminder.Email);
                            }
                        }

                        userCounter++;

                        if (token.IsCancellationRequested)
                        {
                            break;
                        }

                        if (sw.Elapsed.TotalSeconds - elapsedStatus.TotalSeconds > 5 ||
                            userCounter == 1)
                        {
                            elapsedStatus = sw.Elapsed;

                            var remaining = TimeSpan
                                            .FromMilliseconds(elapsedStatus.TotalMilliseconds / userCounter
                                                              * (subscribedCount - userCounter))
                                            .ToString(SpanFormat, CultureInfo.InvariantCulture);

                            var status = new JobStatus
                            {
                                PercentComplete = userCounter * 100 / subscribedCount,
                                Status          = $"Sent {emailsSent}, skipped {emailsSkipped} of {subscribedCount}; {elapsedStatus.ToString(SpanFormat, CultureInfo.InvariantCulture)}, remaining: {remaining}, problems: {problemEmails.Count}",
                                Error           = false
                            };

                            progress.Report(status);

                            if (sw.Elapsed.TotalSeconds - elapsedUpdateDbStatus.TotalSeconds > 60 ||
                                userCounter == 1)
                            {
                                elapsedUpdateDbStatus = sw.Elapsed;

                                if (addSentCounter > 0)
                                {
                                    await _emailService.IncrementSentCountAsync(
                                        jobDetails.EmailTemplateId,
                                        addSentCounter);

                                    addSentCounter = 0;
                                }

                                var dbStatusText = string.Format(CultureInfo.InvariantCulture,
                                                                 "{0}%: {1}",
                                                                 status.PercentComplete,
                                                                 status.Status);

                                await _jobRepository.UpdateStatusAsync(jobId,
                                                                       dbStatusText[..Math.Min(dbStatusText.Length, 255)]);
Exemplo n.º 3
0
        private async Task <DirectEmailHistory> InternalSendDirectAsync(Site site,
                                                                        DirectEmailHistory history,
                                                                        IDictionary <string, string> tags)
        {
            if (history == null)
            {
                throw new ArgumentNullException(nameof(history));
            }

            var emailBase = await GetEmailBase(history.EmailBaseId, history.LanguageId);

            if (emailBase.EmailBaseText == null)
            {
                emailBase = await GetEmailBase(history.EmailBaseId,
                                               await _languageService.GetDefaultLanguageIdAsync());
            }

            var stubble = new StubbleBuilder().Build();

            using var message = new MimeMessage
                  {
                      Subject = history.Subject,
                      Body    = new BodyBuilder
                      {
                          TextBody = await stubble
                                     .RenderAsync(emailBase.EmailBaseText.TemplateText, tags),
                          HtmlBody = await stubble
                                     .RenderAsync(emailBase.EmailBaseText.TemplateHtml,
                                                  tags,
                                                  new Stubble.Core.Settings.RenderSettings { SkipHtmlEncoding = true })
                      }.ToMessageBody()
                  };

            message.From.Add(new MailboxAddress(history.FromName, history.FromEmailAddress));

            try
            {
                if (!string.IsNullOrEmpty(history.OverrideToEmailAddress))
                {
                    message.To.Add(MailboxAddress.Parse(history.OverrideToEmailAddress));
                }
                else
                {
                    message.To.Add(new MailboxAddress(history.ToName, history.ToEmailAddress));
                }
            }
            catch (ParseException ex)
            {
                _logger.LogError("Unable to parse email address: {EmailAddress}",
                                 history.ToEmailAddress);
                throw new GraException($"Unable to parse email address: {history.ToEmailAddress}", ex);
            }

            using var client = new SmtpClient
                  {
                      // accept any STARTTLS certificate
                      ServerCertificateValidationCallback = (_, __, ___, ____) => true
                  };

            client.MessageSent += (_, e) =>
            {
                history.SentResponse = e.Response?.Length > 255
                    ? e.Response[..255]