diff --git a/Massmailer.Shared/Events/MailSentEvent.cs b/Massmailer.Shared/Events/MailSentEvent.cs new file mode 100644 index 0000000..f1f5604 --- /dev/null +++ b/Massmailer.Shared/Events/MailSentEvent.cs @@ -0,0 +1,17 @@ +using System; + +namespace Massmailer.Shared.Events +{ + public class MailSentEvent + { + public MailSentEvent(string recipient, DateTime timestamp) + { + this.Recipient = recipient; + this.Timestamp = timestamp; + } + + public string Recipient { get; private set; } + + public DateTime Timestamp { get; private set; } + } +} diff --git a/Massmailer.Shared/IMailer.cs b/Massmailer.Shared/IMailer.cs index 47fdc9f..b41c6b0 100644 --- a/Massmailer.Shared/IMailer.cs +++ b/Massmailer.Shared/IMailer.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace Massmailer.Shared { diff --git a/Massmailer.Shared/Logic/Mailer.cs b/Massmailer.Shared/Logic/Mailer.cs index f7ec320..e72bb59 100644 --- a/Massmailer.Shared/Logic/Mailer.cs +++ b/Massmailer.Shared/Logic/Mailer.cs @@ -1,7 +1,11 @@ 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 @@ -9,36 +13,99 @@ 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) + 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 async Task SendMailAsync(string email, string subject, string body) + 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 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 { - var message = new MimeMessage(); - message.From.Add(new MailboxAddress(this.smtpSettings.SenderName, this.smtpSettings.SenderEmail)); - message.To.Add(new MailboxAddress(email, email)); - message.Subject = subject; - message.Body = new TextPart("html") { Text = body }; + await this.smtpClient.ConnectAsync(this.smtpSettings.Server, this.smtpSettings.Port, this.smtpSettings.UseSsl); + await this.smtpClient.AuthenticateAsync(this.smtpSettings.Username, this.smtpSettings.Password); - using (var client = new SmtpClient()) + foreach (var message in messages) { - client.ServerCertificateValidationCallback = (s, c, h, e) => true; - - await client.ConnectAsync(this.smtpSettings.Server, this.smtpSettings.Port, this.smtpSettings.UseSsl); - await client.AuthenticateAsync(this.smtpSettings.Username, this.smtpSettings.Password); - await client.SendAsync(message); - await client.DisconnectAsync(true); + 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(ex.Message); + 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); + } + }); + } } } diff --git a/Massmailer.Shared/Massmailer.Shared.csproj b/Massmailer.Shared/Massmailer.Shared.csproj index 0165452..a043f07 100644 --- a/Massmailer.Shared/Massmailer.Shared.csproj +++ b/Massmailer.Shared/Massmailer.Shared.csproj @@ -6,6 +6,7 @@ + diff --git a/Massmailer.Shared/Model/MassmailProject.cs b/Massmailer.Shared/Model/MassmailProject.cs new file mode 100644 index 0000000..95d699e --- /dev/null +++ b/Massmailer.Shared/Model/MassmailProject.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Massmailer.Shared.Model +{ + public class MassmailProject + { + public List Recipient { get; set; } + + public string Subject { get; set; } + + public string Body { get; set; } + } +} diff --git a/Massmailer.Shared/Model/MassmailRecipient.cs b/Massmailer.Shared/Model/MassmailRecipient.cs new file mode 100644 index 0000000..b15bf44 --- /dev/null +++ b/Massmailer.Shared/Model/MassmailRecipient.cs @@ -0,0 +1,13 @@ +using System; + +namespace Massmailer.Shared.Model +{ + public class MassmailRecipient + { + public string Recipient { get; set; } + + public bool IsSent { get; set; } + + public DateTime SentDate { get; set; } + } +} diff --git a/Massmailer.Shared/Model/SmtpSettings.cs b/Massmailer.Shared/Model/SmtpSettings.cs index 715bbd3..d094021 100644 --- a/Massmailer.Shared/Model/SmtpSettings.cs +++ b/Massmailer.Shared/Model/SmtpSettings.cs @@ -21,5 +21,7 @@ namespace Massmailer.Shared.Model public string Password { get; set; } public bool UseSsl { get; set; } + + public int MaxSendRate { get; set; } } } diff --git a/Massmailer.UI/App.xaml b/Massmailer.UI/App.xaml index 14a333c..06b12f9 100644 --- a/Massmailer.UI/App.xaml +++ b/Massmailer.UI/App.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Massmailer.UI" - StartupUri="MainWindow.xaml"> + StartupUri="Views\MainView.xaml"> diff --git a/Massmailer.UI/MainWindow.xaml.cs b/Massmailer.UI/MainWindow.xaml.cs deleted file mode 100644 index bfe6286..0000000 --- a/Massmailer.UI/MainWindow.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace Massmailer.UI -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - public MainWindow() - { - InitializeComponent(); - } - } -} diff --git a/Massmailer.UI/Massmailer.UI.csproj b/Massmailer.UI/Massmailer.UI.csproj index 07b623a..bfdb509 100644 --- a/Massmailer.UI/Massmailer.UI.csproj +++ b/Massmailer.UI/Massmailer.UI.csproj @@ -6,6 +6,10 @@ true + + + + diff --git a/Massmailer.UI/MainWindow.xaml b/Massmailer.UI/Views/MainView.xaml similarity index 79% rename from Massmailer.UI/MainWindow.xaml rename to Massmailer.UI/Views/MainView.xaml index 15edc3a..de66884 100644 --- a/Massmailer.UI/MainWindow.xaml +++ b/Massmailer.UI/Views/MainView.xaml @@ -1,4 +1,4 @@ - - +