From 1acbee2a57ee9eb1aacacb099e14dfd9b0d4aba4 Mon Sep 17 00:00:00 2001 From: Qstick Date: Sat, 7 Jan 2023 13:14:00 -0600 Subject: [PATCH] New: (Notification) Mailgun Fixes #1297 --- .../Notifications/Mailgun/Mailgun.cs | 53 +++++++++++++++ .../Notifications/Mailgun/MailgunException.cs | 18 +++++ .../Notifications/Mailgun/MailgunProxy.cs | 68 +++++++++++++++++++ .../Notifications/Mailgun/MailgunSettings.cs | 49 +++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs create mode 100644 src/NzbDrone.Core/Notifications/Mailgun/MailgunException.cs create mode 100644 src/NzbDrone.Core/Notifications/Mailgun/MailgunProxy.cs create mode 100644 src/NzbDrone.Core/Notifications/Mailgun/MailgunSettings.cs diff --git a/src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs b/src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs new file mode 100644 index 000000000..30c5297b4 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using FluentValidation.Results; +using NLog; + +namespace NzbDrone.Core.Notifications.Mailgun +{ + public class MailGun : NotificationBase + { + private readonly IMailgunProxy _proxy; + private readonly Logger _logger; + + public MailGun(IMailgunProxy proxy, Logger logger) + { + _proxy = proxy; + _logger = logger; + } + + public override string Name => "Mailgun"; + public override string Link => "https://mailgun.com"; + + public override void OnHealthIssue(HealthCheck.HealthCheck healthCheckMessage) + { + _proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheckMessage.Message, Settings); + } + + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); + } + + public override ValidationResult Test() + { + var failures = new List(); + + try + { + const string title = "Test Notification"; + const string body = "This is a test message from Prowlarr, though Mailgun."; + + _proxy.SendNotification(title, body, Settings); + _logger.Info("Successsfully sent email though Mailgun."); + } + catch (Exception ex) + { + _logger.Error(ex, "Unable to send test message though Mailgun."); + failures.Add(new ValidationFailure("", "Unable to send test message though Mailgun.")); + } + + return new ValidationResult(failures); + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Mailgun/MailgunException.cs b/src/NzbDrone.Core/Notifications/Mailgun/MailgunException.cs new file mode 100644 index 000000000..4ddd53814 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Mailgun/MailgunException.cs @@ -0,0 +1,18 @@ +using System; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Notifications.Mailgun +{ + public class MailgunException : NzbDroneException + { + public MailgunException(string message) + : base(message) + { + } + + public MailgunException(string message, Exception innerException, params object[] args) + : base(message, innerException, args) + { + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Mailgun/MailgunProxy.cs b/src/NzbDrone.Core/Notifications/Mailgun/MailgunProxy.cs new file mode 100644 index 000000000..f8b970f15 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Mailgun/MailgunProxy.cs @@ -0,0 +1,68 @@ +using System.Net; +using System.Net.Http; +using NLog; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.Notifications.Mailgun +{ + public interface IMailgunProxy + { + void SendNotification(string title, string message, MailgunSettings settings); + } + + public class MailgunProxy : IMailgunProxy + { + private const string BaseUrlEu = "https://api.eu.mailgun.net/v3"; + private const string BaseUrlUs = "https://api.mailgun.net/v3"; + private readonly IHttpClient _httpClient; + private readonly Logger _logger; + + public MailgunProxy(IHttpClient httpClient, Logger logger) + { + _httpClient = httpClient; + _logger = logger; + } + + public void SendNotification(string title, string message, MailgunSettings settings) + { + try + { + var request = BuildRequest(settings, $"{settings.SenderDomain}/messages", HttpMethod.Post, title, message).Build(); + _httpClient.Execute(request); + } + catch (HttpException ex) + { + if (ex.Response.StatusCode == HttpStatusCode.Unauthorized) + { + _logger.Error("Unathorized - ApiKey is invalid"); + throw new MailgunException("Unauthorized - ApiKey is invalid"); + } + + _logger.Error(ex, "Unable to connect to Mailgun. Status code: " + ex.Message); + throw new MailgunException("Unable to connect to Mailgun. Status code: {0}", ex); + } + } + + private HttpRequestBuilder BuildRequest(MailgunSettings settings, string resource, HttpMethod method, string messageSubject, string messageBody) + { + var loginCredentials = new NetworkCredential("api", settings.ApiKey); + var url = settings.UseEuEndpoint ? BaseUrlEu : BaseUrlUs; + var requestBuilder = new HttpRequestBuilder(url).Resource(resource); + + requestBuilder.Method = method; + requestBuilder.NetworkCredential = loginCredentials; + + requestBuilder.AddFormParameter("from", $"{settings.From}"); + + foreach (var recipient in settings.Recipients) + { + requestBuilder.AddFormParameter("to", $"{recipient}"); + } + + requestBuilder.AddFormParameter("subject", $"{messageSubject}"); + requestBuilder.AddFormParameter("text", $"{messageBody}"); + + return requestBuilder; + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Mailgun/MailgunSettings.cs b/src/NzbDrone.Core/Notifications/Mailgun/MailgunSettings.cs new file mode 100644 index 000000000..896239ef5 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Mailgun/MailgunSettings.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.Notifications.Mailgun +{ + public class MailGunSettingsValidator : AbstractValidator + { + public MailGunSettingsValidator() + { + RuleFor(c => c.ApiKey).NotEmpty(); + RuleFor(c => c.From).NotEmpty(); + RuleFor(c => c.Recipients).NotEmpty(); + } + } + + public class MailgunSettings : IProviderConfig + { + private static readonly MailGunSettingsValidator Validator = new MailGunSettingsValidator(); + + public MailgunSettings() + { + Recipients = Array.Empty(); + } + + [FieldDefinition(0, Label = "API Key", HelpText = "The API key generated from MailGun")] + public string ApiKey { get; set; } + + [FieldDefinition(1, Label = "Use EU Endpoint?", HelpText = "Use the EU MailGun endpoint", Type = FieldType.Checkbox)] + public bool UseEuEndpoint { get; set; } + + [FieldDefinition(2, Label = "From Address")] + public string From { get; set; } + + [FieldDefinition(3, Label = "Sender Domain")] + public string SenderDomain { get; set; } + + [FieldDefinition(4, Label = "Recipient Address(es)", Type = FieldType.Tag, Placeholder = "example@email.com,example1@email.com")] + public IEnumerable Recipients { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +}