parent
c0e54773e2
commit
5f09f2b25f
@ -0,0 +1,74 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Pushcut
|
||||||
|
{
|
||||||
|
public class Pushcut : NotificationBase<PushcutSettings>
|
||||||
|
{
|
||||||
|
private readonly IPushcutProxy _proxy;
|
||||||
|
|
||||||
|
public Pushcut(IPushcutProxy proxy)
|
||||||
|
{
|
||||||
|
_proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "Pushcut";
|
||||||
|
|
||||||
|
public override string Link => "https://www.pushcut.io";
|
||||||
|
|
||||||
|
public override ValidationResult Test()
|
||||||
|
{
|
||||||
|
var failures = new List<ValidationFailure>();
|
||||||
|
|
||||||
|
failures.AddIfNotNull(_proxy.Test(Settings));
|
||||||
|
|
||||||
|
return new ValidationResult(failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGrab(GrabMessage grabMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(EPISODE_GRABBED_TITLE, grabMessage?.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDownload(DownloadMessage downloadMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(EPISODE_DOWNLOADED_TITLE, downloadMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEpisodeFileDelete(EpisodeDeleteMessage deleteMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(EPISODE_DELETED_TITLE, deleteMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSeriesAdd(SeriesAddMessage seriesAddMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(SERIES_ADDED_TITLE, $"{seriesAddMessage.Series.Title} added to library", Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSeriesDelete(SeriesDeleteMessage deleteMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(SERIES_DELETED_TITLE, deleteMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnHealthRestored(HealthCheck.HealthCheck previousCheck)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(HEALTH_RESTORED_TITLE_BRANDED, $"The following issue is now resolved: {previousCheck.Message}", Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage manualInteractionRequiredMessage)
|
||||||
|
{
|
||||||
|
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED, manualInteractionRequiredMessage.Message, Settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using NzbDrone.Common.Exceptions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Pushcut
|
||||||
|
{
|
||||||
|
public class PushcutException : NzbDroneException
|
||||||
|
{
|
||||||
|
public PushcutException(string message, params object[] args)
|
||||||
|
: base(message, args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PushcutException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PushcutException(string message, Exception innerException, params object[] args)
|
||||||
|
: base(message, innerException, args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PushcutException(string message, Exception innerException)
|
||||||
|
: base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace NzbDrone.Core.Notifications.Pushcut
|
||||||
|
{
|
||||||
|
public class PushcutPayload
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
public bool? IsTimeSensitive { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Pushcut
|
||||||
|
{
|
||||||
|
public interface IPushcutProxy
|
||||||
|
{
|
||||||
|
void SendNotification(string title, string message, PushcutSettings settings);
|
||||||
|
ValidationFailure Test(PushcutSettings settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PushcutProxy : IPushcutProxy
|
||||||
|
{
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public PushcutProxy(IHttpClient httpClient, Logger logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendNotification(string title, string message, PushcutSettings settings)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestBuilder("https://api.pushcut.io/v1/notifications/{notificationName}")
|
||||||
|
.SetSegment("notificationName", settings?.NotificationName)
|
||||||
|
.SetHeader("API-Key", settings?.ApiKey)
|
||||||
|
.Accept(HttpAccept.Json)
|
||||||
|
.Build();
|
||||||
|
var payload = new PushcutPayload
|
||||||
|
{
|
||||||
|
Title = title,
|
||||||
|
Text = message,
|
||||||
|
IsTimeSensitive = settings?.TimeSensitive
|
||||||
|
};
|
||||||
|
|
||||||
|
request.Method = HttpMethod.Post;
|
||||||
|
request.Headers.ContentType = "application/json";
|
||||||
|
request.SetContent(payload.ToJson());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.Execute(request);
|
||||||
|
}
|
||||||
|
catch (HttpException exception)
|
||||||
|
{
|
||||||
|
_logger.Error(exception, "Unable to send Pushcut notification: {0}", exception.Message);
|
||||||
|
throw new PushcutException("Unable to send Pushcut notification: {0}", exception.Message, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationFailure Test(PushcutSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const string title = "Sonarr Test Title";
|
||||||
|
const string message = "Success! You have properly configured your Pushcut notification settings.";
|
||||||
|
SendNotification(title, message, settings);
|
||||||
|
}
|
||||||
|
catch (PushcutException pushcutException) when (pushcutException.InnerException is HttpException httpException)
|
||||||
|
{
|
||||||
|
if (httpException.Response.StatusCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
_logger.Error(pushcutException, "API Key is invalid: {0}", pushcutException.Message);
|
||||||
|
return new ValidationFailure("API Key", $"API Key is invalid: {pushcutException.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (httpException.Response.Content.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
var response = Json.Deserialize<PushcutResponse>(httpException.Response.Content);
|
||||||
|
|
||||||
|
_logger.Error(pushcutException, "Unable to send test notification. Response from Pushcut: {0}", response.Error);
|
||||||
|
return new ValidationFailure("Url", $"Unable to send test notification. Response from Pushcut: {response.Error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Error(pushcutException, "Unable to connect to Pushcut API. Server connection failed: ({0}) {1}", httpException.Response.StatusCode, pushcutException.Message);
|
||||||
|
return new ValidationFailure("Host", $"Unable to connect to Pushcut API. Server connection failed: ({httpException.Response.StatusCode}) {pushcutException.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace NzbDrone.Core.Notifications.Pushcut
|
||||||
|
{
|
||||||
|
public class PushcutResponse
|
||||||
|
{
|
||||||
|
public string Error { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Pushcut
|
||||||
|
{
|
||||||
|
public class PushcutSettingsValidator : AbstractValidator<PushcutSettings>
|
||||||
|
{
|
||||||
|
public PushcutSettingsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(settings => settings.ApiKey).NotEmpty();
|
||||||
|
RuleFor(settings => settings.NotificationName).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PushcutSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
private static readonly PushcutSettingsValidator Validator = new ();
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Notification name", Type = FieldType.Textbox, HelpText = "Notification name from Notifications tab of the Pushcut app.")]
|
||||||
|
public string NotificationName { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "API Key", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey, HelpText = "API Keys can be managed in the Account view of the Pushcut app.")]
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Time sensitive", Type = FieldType.Checkbox, HelpText = "Check to mark the notification as \"Time-Sensitive\"")]
|
||||||
|
public bool TimeSensitive { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pushcut/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sonarr/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sonarr/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=tvdb/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=tvdb/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
Loading…
Reference in new issue