using MailKit.Net.Smtp; using Massmailer.Shared.Events; using Massmailer.Shared.Model; using MimeKit; using PubSub; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Massmailer.Shared.Logic { public class Mailer : IMailer { private readonly SmtpSettings smtpSettings; private readonly SmtpClient smtpClient; private readonly Hub hub; private readonly int minWaitTime = 0; private DateTime lastMailSent = DateTime.MinValue; public Mailer(SmtpSettings smtpSettings, Hub hub) { this.smtpSettings = smtpSettings; this.smtpClient = new SmtpClient(); this.smtpClient.ServerCertificateValidationCallback = (s, c, h, e) => true; this.hub = hub; // Calculate wait time in seconds this.minWaitTime = (int)Math.Ceiling(60d / this.smtpSettings.MaxSendRate); } public MimeMessage CreateMessage(string recipient, string subject, string body) { var message = new MimeMessage(); message.From.Add(new MailboxAddress(this.smtpSettings.SenderName, this.smtpSettings.SenderEmail)); message.To.Add(new MailboxAddress(recipient, recipient)); message.Subject = subject; message.Body = new TextPart("html") { Text = body }; return message; } public async Task SendMailsAsync(MassmailerProject project) { try { await this.smtpClient.ConnectAsync(this.smtpSettings.Server, this.smtpSettings.Port, this.smtpSettings.UseSsl); await this.smtpClient.AuthenticateAsync(this.smtpSettings.Username, this.smtpSettings.Password); foreach (var recipient in project.Recipients) { await this.CheckForWaitTime(); var message = this.CreateMessage(recipient.Address, project.Subject, project.Body); await this.smtpClient.SendAsync(message); this.lastMailSent = DateTime.Now; recipient.IsSent = true; recipient.SentDate = this.lastMailSent; this.hub.Publish(new MailSentEvent(message.To[0].ToString(), this.lastMailSent)); } await this.smtpClient.DisconnectAsync(true); } catch (Exception ex) { throw new InvalidOperationException("Something went wrong while sending many mails", ex); } } public List CreateMessages(List recipients, string subject, string body) { var result = new List(); foreach (var recipient in recipients) { result.Add(this.CreateMessage(recipient, subject, body)); } return result; } public async Task SendMailsAsync(List recipients, string subject, string body) { var messages = this.CreateMessages(recipients, subject, body); await this.SendMailsAsync(messages); } public async Task SendMailsAsync(List messages) { try { await this.smtpClient.ConnectAsync(this.smtpSettings.Server, this.smtpSettings.Port, this.smtpSettings.UseSsl); await this.smtpClient.AuthenticateAsync(this.smtpSettings.Username, this.smtpSettings.Password); foreach (var message in messages) { await this.CheckForWaitTime(); await this.smtpClient.SendAsync(message); this.lastMailSent = DateTime.Now; this.hub.Publish(new MailSentEvent(message.To[0].ToString(), this.lastMailSent)); } await this.smtpClient.DisconnectAsync(true); } catch (Exception ex) { throw new InvalidOperationException("Something went wrong while sending many mails", ex); } } public async Task SendMailAsync(string recipient, string subject, string body) { try { var message = this.CreateMessage(recipient, subject, body); await this.SendMailsAsync(new List { message }); } catch (Exception ex) { throw new InvalidOperationException("Something went wrong while sending a mail", ex); } } private async Task CheckForWaitTime() { await Task.Run(() => { while (true) { if (this.lastMailSent.AddSeconds(this.minWaitTime) < DateTime.Now) { break; } Thread.Sleep(100); } }); } } }