diff --git a/Correo.sln b/Correo.sln
index 6c2f4c1..bf05160 100644
--- a/Correo.sln
+++ b/Correo.sln
@@ -32,6 +32,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Correo.MailKit", "src\Corre
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Correo.SendGrid", "src\Correo.SendGrid\Correo.SendGrid.csproj", "{1457426A-CD47-4201-BE3C-A40B38FCB1FA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Correo.Mailgun", "src\Correo.Mailgun\Correo.Mailgun.csproj", "{78DC392D-B24A-43CA-B632-6124CD2C35B1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -70,6 +72,10 @@ Global
{1457426A-CD47-4201-BE3C-A40B38FCB1FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1457426A-CD47-4201-BE3C-A40B38FCB1FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1457426A-CD47-4201-BE3C-A40B38FCB1FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {78DC392D-B24A-43CA-B632-6124CD2C35B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {78DC392D-B24A-43CA-B632-6124CD2C35B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {78DC392D-B24A-43CA-B632-6124CD2C35B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {78DC392D-B24A-43CA-B632-6124CD2C35B1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -83,6 +89,7 @@ Global
{ED8048DC-8509-4085-97F0-F9D9A59A689A} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
{325B77E1-D752-4578-8BF7-793905C38DCD} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
{1457426A-CD47-4201-BE3C-A40B38FCB1FA} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
+ {78DC392D-B24A-43CA-B632-6124CD2C35B1} = {245E2FBE-DFDF-40B4-94B7-5DDA216E58AD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {86FCF989-26FC-41E9-8A23-9485606D619D}
diff --git a/dependencies.props b/dependencies.props
index 2480ea9..7180720 100644
--- a/dependencies.props
+++ b/dependencies.props
@@ -10,5 +10,6 @@
6.0.30
3.4.3
9.28.1
+ 108.0.3
\ No newline at end of file
diff --git a/src/Correo.Mailgun/Correo.Mailgun.csproj b/src/Correo.Mailgun/Correo.Mailgun.csproj
new file mode 100644
index 0000000..b496ce3
--- /dev/null
+++ b/src/Correo.Mailgun/Correo.Mailgun.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Correo.Mailgun/DependencyInjectionExtensions.cs b/src/Correo.Mailgun/DependencyInjectionExtensions.cs
new file mode 100644
index 0000000..81d34a5
--- /dev/null
+++ b/src/Correo.Mailgun/DependencyInjectionExtensions.cs
@@ -0,0 +1,16 @@
+using Correo.Abstractions;
+using Correo.Mailgun.Models;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Correo.Mailgun
+{
+ public static class DependencyInjectionExtensions
+ {
+ public static void AddMailgunService(this IServiceCollection services, IConfigurationSection configuration)
+ {
+ services.Configure(configuration);
+ services.AddTransient();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Correo.Mailgun/Extensions/ModelExtensions.cs b/src/Correo.Mailgun/Extensions/ModelExtensions.cs
new file mode 100644
index 0000000..ca29b08
--- /dev/null
+++ b/src/Correo.Mailgun/Extensions/ModelExtensions.cs
@@ -0,0 +1,20 @@
+using Correo.Abstractions;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Correo.Mailgun.Extensions
+{
+ internal static class ModelExtensions
+ {
+ public static string ToMailgunAddress(this EmailMessage.MailAddress address)
+ => $"{address.DisplayName} <{address.Address}>";
+
+ public static string ToMailgunAddresses(this IEnumerable addresses)
+ {
+ if (addresses == null || !addresses.Any())
+ return null;
+
+ return string.Join(',', addresses.Select(z => z.ToMailgunAddress()));
+ }
+ }
+}
diff --git a/src/Correo.Mailgun/MailgunService.cs b/src/Correo.Mailgun/MailgunService.cs
new file mode 100644
index 0000000..4534780
--- /dev/null
+++ b/src/Correo.Mailgun/MailgunService.cs
@@ -0,0 +1,79 @@
+using Correo.Abstractions;
+using Correo.Abstractions.Extensions;
+using Correo.Mailgun.Extensions;
+using Correo.Mailgun.Models;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using RestSharp;
+using RestSharp.Authenticators;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Correo.Mailgun
+{
+ internal class MailgunService : IMailer, IDisposable
+ {
+ private readonly IOptions _optionsAccessor;
+ private readonly RestClient _client;
+ private readonly ILogger _logger;
+
+ public MailgunService(IOptions optionsAccessor, ILogger logger)
+ {
+ _optionsAccessor = optionsAccessor;
+ _logger = logger;
+ _client = new RestClient("https://api.mailgun.net/v3")
+ {
+ Authenticator = new HttpBasicAuthenticator("api", _optionsAccessor.Value.ApiKey)
+ };
+ }
+
+ public void Dispose()
+ {
+ _client.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
+ public void SendEmail(EmailMessage message)
+ {
+ SendEmailAsync(message).GetAwaiter().GetResult();
+ }
+
+ public async Task SendEmailAsync(EmailMessage message, CancellationToken token = default)
+ {
+ var request = new RestRequest();
+ request.AddParameter("domain", _optionsAccessor.Value.Domain, ParameterType.UrlSegment);
+ request.Resource = "{domain}/messages";
+
+ request.AddParameter("subject", message.Subject);
+ var contentKey = message.IsBodyHtml ? "html" : "text";
+ request.AddParameter(contentKey, message.Body);
+
+ request.AddParameter("from", message.From.ToMailgunAddress());
+ request.AddParameter("to", message.To.ToMailgunAddresses());
+
+ var cc = message.Cc.ToMailgunAddresses();
+ if (cc != null)
+ request.AddParameter("cc", cc);
+
+ var bcc = message.Bcc.ToMailgunAddresses();
+ if (bcc != null)
+ request.AddParameter("bcc", bcc);
+
+ request.Method = Method.Post;
+ var response = await _client.ExecuteAsync(request, token);
+ if (response.IsSuccessful)
+ {
+ _logger.LogInformation($"Mailgun response: {response.Data.Message} {response.Data.Id}");
+ Log(message);
+ }
+ else
+ {
+ throw new Exception($"Mailgun error: {response.Data.Message}");
+ }
+ }
+
+ private void Log(EmailMessage message)
+ => _logger.LogInformation($"Email sent: Subject: {message.Subject}; From: {message.From.Address}; To: {message.To.Log()}; Cc: {message.Cc.Log()}; Bcc: {message.Bcc.Log()};");
+ }
+}
diff --git a/src/Correo.Mailgun/Models/MailgunOptions.cs b/src/Correo.Mailgun/Models/MailgunOptions.cs
new file mode 100644
index 0000000..0ba1ccf
--- /dev/null
+++ b/src/Correo.Mailgun/Models/MailgunOptions.cs
@@ -0,0 +1,8 @@
+namespace Correo.Mailgun.Models
+{
+ internal record MailgunOptions
+ {
+ public string ApiKey { get; init; }
+ public string Domain { get; init; }
+ }
+}
diff --git a/src/Correo.Mailgun/Models/MailgunResponse.cs b/src/Correo.Mailgun/Models/MailgunResponse.cs
new file mode 100644
index 0000000..6a4dbba
--- /dev/null
+++ b/src/Correo.Mailgun/Models/MailgunResponse.cs
@@ -0,0 +1,8 @@
+namespace Correo.Mailgun.Models
+{
+ public record MailgunResponse
+ {
+ public string Id { get; init; }
+ public string Message { get; init; }
+ }
+}
diff --git a/src/Correo.Mailgun/readme.txt b/src/Correo.Mailgun/readme.txt
new file mode 100644
index 0000000..661b241
--- /dev/null
+++ b/src/Correo.Mailgun/readme.txt
@@ -0,0 +1 @@
+Docs: https://documentation.mailgun.com/en/latest/api-sending.html#sending
\ No newline at end of file
diff --git a/src/Correo.SendGrid/SendGridService.cs b/src/Correo.SendGrid/SendGridService.cs
index e111fdb..2c67d9d 100644
--- a/src/Correo.SendGrid/SendGridService.cs
+++ b/src/Correo.SendGrid/SendGridService.cs
@@ -28,9 +28,7 @@ namespace Correo.SendGrid
public void SendEmail(EmailMessage message)
{
- var mailMessage = message.ToSendGridMessage();
- _client.SendEmailAsync(mailMessage).GetAwaiter().GetResult();
- Log(message);
+ SendEmailAsync(message).GetAwaiter().GetResult();
}
public async Task SendEmailAsync(EmailMessage message, CancellationToken token = default)
diff --git a/src/Correo/Correo.csproj b/src/Correo/Correo.csproj
index d51adaa..aae0fe1 100644
--- a/src/Correo/Correo.csproj
+++ b/src/Correo/Correo.csproj
@@ -16,6 +16,7 @@
+
diff --git a/src/Correo/Extensions/SmtpExtensions.cs b/src/Correo/Extensions/SmtpExtensions.cs
index cee12ee..d1eae84 100644
--- a/src/Correo/Extensions/SmtpExtensions.cs
+++ b/src/Correo/Extensions/SmtpExtensions.cs
@@ -1,4 +1,5 @@
-using Correo.MailKit;
+using Correo.Mailgun;
+using Correo.MailKit;
using Correo.NetSmtpClient;
using Correo.SendGrid;
using Microsoft.Extensions.Configuration;
@@ -22,7 +23,11 @@ namespace Correo.Extensions
}
else if (mediator.Equals("SendGrid", StringComparison.InvariantCultureIgnoreCase))
{
- services.AddSendGridService(configuration.GetSection("SmtpService"));
+ services.AddSendGridService(configuration.GetSection("SendGrid"));
+ }
+ else if (mediator.Equals("Mailgun", StringComparison.InvariantCultureIgnoreCase))
+ {
+ services.AddMailgunService(configuration.GetSection("Mailgun"));
}
else
{
diff --git a/src/Correo/appsettings.json b/src/Correo/appsettings.json
index 7341fcb..9955318 100644
--- a/src/Correo/appsettings.json
+++ b/src/Correo/appsettings.json
@@ -29,7 +29,7 @@
"Address": "noreply@homelab.com",
"Name": "HomeLab"
},
- "SmtpMediator": ".NET", //.NET, MailKit, SendGrid
+ "SmtpMediator": ".NET", //.NET, MailKit, SendGrid, Mailgun
"SmtpClient": {
"Server": "smtp.gmail.com",
"Port": "587",
@@ -42,7 +42,11 @@
"EnableSsl": true,
"TrustServer": false
},
- "SmtpService": {
+ "SendGrid": {
"ApiKey": "xxxxxxxxxxx"
+ },
+ "Mailgun": {
+ "ApiKey": "xxxxxxxxxxx",
+ "Domain": "xxxxxxxxxxx"
}
}
\ No newline at end of file