1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-04 10:02:40 +01:00

Notifications can be tested

Notification ImplementationType was added for showing in UI (Humanized/Title cased of Implementation)
This commit is contained in:
Mark McDowall 2013-06-12 23:41:26 -07:00
parent 1f4cf0034e
commit 8cac7ed1cd
34 changed files with 418 additions and 328 deletions

View File

@ -8,6 +8,7 @@ namespace NzbDrone.Api.Notifications
public class NotificationResource : RestResource
{
public String Name { get; set; }
public String ImplementationName { get; set; }
public Boolean OnGrab { get; set; }
public Boolean OnDownload { get; set; }
public List<Field> Fields { get; set; }

View File

@ -42,8 +42,7 @@ public void GetSectionKeys_should_return_single_section_key_when_only_one_show_s
Mocker.GetMock<IHttpProvider>().Setup(s => s.DownloadStream("http://localhost:32400/library/sections", null))
.Returns(stream);
var result = Mocker.Resolve<PlexProvider>().GetSectionKeys("localhost:32400");
var result = Mocker.Resolve<PlexService>().GetSectionKeys(new PlexServerSettings { Host = "localhost", Port = 32400 });
result.Should().HaveCount(1);
@ -62,7 +61,7 @@ public void GetSectionKeys_should_return_single_section_key_when_only_one_show_s
.Returns(stream);
var result = Mocker.Resolve<PlexProvider>().GetSectionKeys("localhost:32400");
var result = Mocker.Resolve<PlexService>().GetSectionKeys(new PlexServerSettings { Host = "localhost", Port = 32400 });
result.Should().HaveCount(1);
@ -81,7 +80,7 @@ public void GetSectionKeys_should_return_multiple_section_keys_when_there_are_mu
.Returns(stream);
var result = Mocker.Resolve<PlexProvider>().GetSectionKeys("localhost:32400");
var result = Mocker.Resolve<PlexService>().GetSectionKeys(new PlexServerSettings { Host = "localhost", Port = 32400 });
result.Should().HaveCount(2);
@ -101,7 +100,7 @@ public void UpdateSection_should_update_section()
.Returns(response);
Mocker.Resolve<PlexProvider>().UpdateSection("localhost:32400", 5);
Mocker.Resolve<PlexService>().UpdateSection(new PlexServerSettings { Host = "localhost", Port = 32400 }, 5);
@ -121,7 +120,7 @@ public void Notify_should_send_notification()
.Returns("ok");
Mocker.Resolve<PlexProvider>().Notify(_clientSettings, header, message);
Mocker.Resolve<PlexService>().Notify(_clientSettings, header, message);
fakeHttp.Verify(v => v.DownloadString(expectedUrl), Times.Once());
@ -142,7 +141,7 @@ public void Notify_should_send_notification_with_credentials_when_configured()
.Returns("ok");
Mocker.Resolve<PlexProvider>().Notify(_clientSettings, header, message);
Mocker.Resolve<PlexService>().Notify(_clientSettings, header, message);
fakeHttp.Verify(v => v.DownloadString(expectedUrl, "plex", "plex"), Times.Once());

View File

@ -9,130 +9,40 @@ namespace NzbDrone.Core.Test.NotificationTests
{
[Explicit]
[TestFixture]
public class ProwlProviderTest : CoreTest
public class ProwlProviderTest : CoreTest<ProwlService>
{
private const string _apiKey = "c3bdc0f48168f72d546cc6872925b160f5cbffc1";
private const string _apiKey2 = "46a710a46b111b0b8633819b0d8a1e0272a3affa";
private const string _apiKey = "66e9f688b512152eb2688f0486ae542c76e564a2";
private const string _badApiKey = "1234567890abcdefghijklmnopqrstuvwxyz1234";
[Test]
public void Verify_should_return_true_for_a_valid_apiKey()
public void Verify_should_not_throw_for_a_valid_apiKey()
{
var result = Mocker.Resolve<ProwlProvider>().Verify(_apiKey);
result.Should().BeTrue();
Subject.Verify(_apiKey);
ExceptionVerification.ExpectedWarns(0);
}
[Test]
public void Verify_should_return_false_for_an_invalid_apiKey()
public void Verify_should_throw_for_an_invalid_apiKey()
{
var result = Mocker.Resolve<ProwlProvider>().Verify(_badApiKey);
Assert.Throws<InvalidApiKeyException>(() => Subject.Verify(_badApiKey));
ExceptionVerification.ExpectedWarns(1);
result.Should().BeFalse();
}
[Test]
public void SendNotification_should_return_true_for_a_valid_apiKey()
public void SendNotification_should_not_throw_for_a_valid_apiKey()
{
var result = Mocker.Resolve<ProwlProvider>().SendNotification("NzbDrone Test", "This is a test message from NzbDrone", _apiKey);
result.Should().BeTrue();
Subject.SendNotification("NzbDrone Test", "This is a test message from NzbDrone", _apiKey);
ExceptionVerification.ExpectedWarns(0);
}
[Test]
public void SendNotification_should_return_false_for_an_invalid_apiKey()
public void SendNotification_should_log_a_warning_for_an_invalid_apiKey()
{
var result = Mocker.Resolve<ProwlProvider>().SendNotification("NzbDrone Test", "This is a test message from NzbDrone", _badApiKey);
Subject.SendNotification("NzbDrone Test", "This is a test message from NzbDrone", _badApiKey);
ExceptionVerification.ExpectedWarns(1);
result.Should().BeFalse();
}
[Test]
public void SendNotification_should_alert_with_high_priority()
{
var result = Mocker.Resolve<ProwlProvider>().SendNotification("NzbDrone Test", "This is a test message from NzbDrone (High)", _apiKey, NotificationPriority.High);
result.Should().BeTrue();
}
[Test]
public void SendNotification_should_alert_with_VeryLow_priority()
{
var result = Mocker.Resolve<ProwlProvider>().SendNotification("NzbDrone Test", "This is a test message from NzbDrone (VeryLow)", _apiKey, NotificationPriority.VeryLow);
result.Should().BeTrue();
}
[Test]
public void SendNotification_should_have_a_call_back_url()
{
var result = Mocker.Resolve<ProwlProvider>().SendNotification("NzbDrone Test", "This is a test message from NzbDrone", _apiKey, NotificationPriority.Normal, "http://www.nzbdrone.com");
result.Should().BeTrue();
}
[Test]
public void SendNotification_should_return_true_for_two_valid_apiKey()
{
var result = Mocker.Resolve<ProwlProvider>().SendNotification("NzbDrone Test", "This is a test message from NzbDrone", _apiKey + ", " + _apiKey2);
result.Should().BeTrue();
}
[Test]
public void SendNotification_should_return_true_for_valid_apiKey_with_bad_apiKey()
{
var result = Mocker.Resolve<ProwlProvider>().SendNotification("NzbDrone Test", "This is a test message from NzbDrone", _apiKey + ", " + _badApiKey);
result.Should().BeTrue();
}
}
}

View File

@ -185,7 +185,6 @@
<Compile Include="DecisionEngineTests\QualityUpgradableSpecificationFixture.cs" />
<Compile Include="ProviderTests\DiskProviderTests\FreeDiskSpaceTest.cs" />
<Compile Include="NotificationTests\ProwlProviderTest.cs" />
<Compile Include="NotificationTests\GrowlProviderTest.cs" />
<Compile Include="ProviderTests\DiskProviderTests\ExtractArchiveFixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\DropFolderImportServiceFixture.cs" />
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />

View File

@ -5,9 +5,9 @@ namespace NzbDrone.Core.Notifications.Email
{
public class Email : NotificationBase<EmailSettings>
{
private readonly EmailProvider _smtpProvider;
private readonly IEmailService _smtpProvider;
public Email(EmailProvider smtpProvider)
public Email(IEmailService smtpProvider)
{
_smtpProvider = smtpProvider;
}
@ -17,6 +17,11 @@ public override string Name
get { return "Email"; }
}
public override string ImplementationName
{
get { return "Email"; }
}
public override void OnGrab(string message)
{
const string subject = "NzbDrone [TV] - Grabbed";

View File

@ -2,19 +2,26 @@
using System.Net;
using System.Net.Mail;
using NLog;
using NzbDrone.Common.Messaging;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Notifications.Email
{
public class EmailProvider
public interface IEmailService
{
void SendEmail(EmailSettings settings, string subject, string body, bool htmlBody = false);
}
public class EmailService : IEmailService, IExecute<TestEmailCommand>
{
private readonly Logger _logger;
public EmailProvider(Logger logger)
public EmailService(Logger logger)
{
_logger = logger;
}
public virtual void SendEmail(EmailSettings settings, string subject, string body, bool htmlBody = false)
public void SendEmail(EmailSettings settings, string subject, string body, bool htmlBody = false)
{
var email = new MailMessage();
email.From = new MailAddress(settings.From);
@ -32,7 +39,7 @@ public virtual void SendEmail(EmailSettings settings, string subject, string bod
try
{
Send(email, settings.Server, settings.Port, settings.UseSsl, credentials);
Send(email, settings.Server, settings.Port, settings.Ssl, credentials);
}
catch(Exception ex)
{
@ -41,7 +48,7 @@ public virtual void SendEmail(EmailSettings settings, string subject, string bod
}
}
public virtual void Send(MailMessage email, string server, int port, bool ssl, NetworkCredential credentials)
private void Send(MailMessage email, string server, int port, bool ssl, NetworkCredential credentials)
{
try
{
@ -60,5 +67,15 @@ public virtual void Send(MailMessage email, string server, int port, bool ssl, N
throw;
}
}
public void Execute(TestEmailCommand message)
{
var settings = new EmailSettings();
settings.InjectFrom(message);
var body = "Success! You have properly configured your email notification settings";
SendEmail(settings, "NzbDrone - Test Notification", body);
}
}
}

View File

@ -11,16 +11,16 @@ public class EmailSettings : INotifcationSettings
[FieldDefinition(1, Label = "Port")]
public Int32 Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", HelpText = "Does your Email server use SSL?")]
public Boolean UseSsl { get; set; }
[FieldDefinition(2, Label = "SSL", Type = FieldType.Checkbox)]
public Boolean Ssl { get; set; }
[FieldDefinition(3, Label = "Username")]
public String Username { get; set; }
[FieldDefinition(4, Label = "Password")]
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public String Password { get; set; }
[FieldDefinition(5, Label = "Sender Address")]
[FieldDefinition(5, Label = "From Address")]
public String From { get; set; }
[FieldDefinition(6, Label = "Recipient Address")]

View File

@ -0,0 +1,15 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Notifications.Email
{
public class TestEmailCommand : ICommand
{
public string Server { get; set; }
public int Port { get; set; }
public bool Ssl { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string From { get; set; }
public string To { get; set; }
}
}

View File

@ -7,9 +7,9 @@ namespace NzbDrone.Core.Notifications.Growl
{
public class Growl : NotificationBase<GrowlSettings>
{
private readonly GrowlProvider _growlProvider;
private readonly IGrowlService _growlProvider;
public Growl(GrowlProvider growlProvider)
public Growl(IGrowlService growlProvider)
{
_growlProvider = growlProvider;
}
@ -19,6 +19,11 @@ public override string Name
get { return "Growl"; }
}
public override string ImplementationName
{
get { return "Growl"; }
}
public override void OnGrab(string message)
{
const string title = "Episode Grabbed";

View File

@ -3,43 +3,33 @@
using System.Linq;
using Growl.Connector;
using NLog;
using NzbDrone.Common.Messaging;
using GrowlNotification = Growl.Connector.Notification;
namespace NzbDrone.Core.Notifications.Growl
{
public class GrowlProvider
public interface IGrowlService
{
void SendNotification(string title, string message, string notificationTypeName, string hostname, int port, string password);
}
public class GrowlService : IGrowlService, IExecute<TestGrowlCommand>
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly Application _growlApplication = new Application("NzbDrone");
private GrowlConnector _growlConnector;
private List<NotificationType> _notificationTypes;
private readonly List<NotificationType> _notificationTypes;
public GrowlProvider()
public GrowlService()
{
_notificationTypes = GetNotificationTypes();
_growlApplication.Icon = "https://github.com/NzbDrone/NzbDrone/raw/master/NzbDrone.Core/NzbDrone.jpg";
}
public virtual void Register(string hostname, int port, string password)
{
Logger.Trace("Registering NzbDrone with Growl host: {0}:{1}", hostname, port);
_growlConnector = new GrowlConnector(password, hostname, port);
_growlConnector.Register(_growlApplication, _notificationTypes.ToArray());
}
public virtual void TestNotification(string hostname, int port, string password)
{
const string title = "Test Notification";
const string message = "This is a test message from NzbDrone";
SendNotification(title, message, "TEST", hostname, port, password);
}
public virtual void SendNotification(string title, string message, string notificationTypeName, string hostname, int port, string password)
public void SendNotification(string title, string message, string notificationTypeName, string hostname, int port, string password)
{
var notificationType = _notificationTypes.Single(n => n.Name == notificationTypeName);
var notification = new GrowlNotification("NzbDrone", notificationType.Name, DateTime.Now.Ticks.ToString(), title, message);
_growlConnector = new GrowlConnector(password, hostname, port);
@ -48,6 +38,13 @@ public virtual void SendNotification(string title, string message, string notifi
_growlConnector.Notify(notification);
}
private void Register(string host, int port, string password)
{
Logger.Trace("Registering NzbDrone with Growl host: {0}:{1}", host, port);
_growlConnector = new GrowlConnector(password, host, port);
_growlConnector.Register(_growlApplication, _notificationTypes.ToArray());
}
private List<NotificationType> GetNotificationTypes()
{
var notificationTypes = new List<NotificationType>();
@ -57,5 +54,15 @@ private List<NotificationType> GetNotificationTypes()
return notificationTypes;
}
public void Execute(TestGrowlCommand message)
{
Register(message.Host, message.Port, message.Password);
const string title = "Test Notification";
const string body = "This is a test message from NzbDrone";
SendNotification(title, body, "TEST", message.Host, message.Port, message.Password);
}
}
}

View File

@ -0,0 +1,11 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Notifications.Growl
{
public class TestGrowlCommand : ICommand
{
public string Host { get; set; }
public int Port { get; set; }
public string Password { get; set; }
}
}

View File

@ -9,6 +9,7 @@ namespace NzbDrone.Core.Notifications
public interface INotification
{
string Name { get; }
string ImplementationName { get; }
NotificationDefinition InstanceDefinition { get; set; }

View File

@ -9,6 +9,7 @@ public class Notification
{
public int Id { get; set; }
public string Name { get; set; }
public string ImplementationName { get; set; }
public bool OnGrab { get; set; }
public bool OnDownload { get; set; }
public INotifcationSettings Settings { get; set; }

View File

@ -8,6 +8,7 @@ namespace NzbDrone.Core.Notifications
public abstract class NotificationBase<TSetting> : INotification where TSetting : class, INotifcationSettings, new()
{
public abstract string Name { get; }
public abstract string ImplementationName { get; }
public NotificationDefinition InstanceDefinition { get; set; }

View File

@ -69,7 +69,7 @@ public List<Notification> Schema()
var newNotification = new Notification();
newNotification.Instance = (INotification)_container.Resolve(type);
newNotification.Id = i;
newNotification.Name = notification.Name;
newNotification.ImplementationName = notification.ImplementationName;
var instanceType = newNotification.Instance.GetType();
var baseGenArgs = instanceType.BaseType.GetGenericArguments();
@ -120,6 +120,7 @@ private Notification ToNotification(NotificationDefinition definition)
notification.Instance = GetInstance(definition);
notification.Name = definition.Name;
notification.Implementation = definition.Implementation;
notification.ImplementationName = notification.Instance.ImplementationName;
notification.Settings = ((dynamic)notification.Instance).ImportSettingsFromJson(definition.Settings);
return notification;

View File

@ -6,9 +6,9 @@ namespace NzbDrone.Core.Notifications.Plex
{
public class PlexClient : NotificationBase<PlexClientSettings>
{
private readonly PlexProvider _plexProvider;
private readonly IPlexService _plexProvider;
public PlexClient(PlexProvider plexProvider)
public PlexClient(IPlexService plexProvider)
{
_plexProvider = plexProvider;
}
@ -18,6 +18,11 @@ public override string Name
get { return "Plex Client"; }
}
public override string ImplementationName
{
get { return "Plex Client"; }
}
public override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";

View File

@ -1,88 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.Notifications.Plex
{
public class PlexProvider
{
private readonly IHttpProvider _httpProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public PlexProvider(IHttpProvider httpProvider)
{
_httpProvider = httpProvider;
}
public virtual void Notify(PlexClientSettings settings, string header, string message)
{
try
{
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", header, message);
SendCommand(settings.Host, settings.Port, command, settings.Username, settings.Password);
}
catch(Exception ex)
{
logger.WarnException("Failed to send notification to Plex Client: " + settings.Host, ex);
}
}
public virtual void UpdateLibrary(string host)
{
try
{
logger.Trace("Sending Update Request to Plex Server");
var sections = GetSectionKeys(host);
sections.ForEach(s => UpdateSection(host, s));
}
catch(Exception ex)
{
logger.WarnException("Failed to Update Plex host: " + host, ex);
throw;
}
}
public List<int> GetSectionKeys(string host)
{
logger.Trace("Getting sections from Plex host: {0}", host);
var url = String.Format("http://{0}/library/sections", host);
var xmlStream = _httpProvider.DownloadStream(url, null);
var xDoc = XDocument.Load(xmlStream);
var mediaContainer = xDoc.Descendants("MediaContainer").FirstOrDefault();
var directories = mediaContainer.Descendants("Directory").Where(x => x.Attribute("type").Value == "show");
return directories.Select(d => Int32.Parse(d.Attribute("key").Value)).ToList();
}
public void UpdateSection(string host, int key)
{
logger.Trace("Updating Plex host: {0}, Section: {1}", host, key);
var url = String.Format("http://{0}/library/sections/{1}/refresh", host, key);
_httpProvider.DownloadString(url);
}
public virtual string SendCommand(string host, int port, string command, string username, string password)
{
var url = String.Format("http://{0}:{1}/xbmcCmds/xbmcHttp?command={2}", host, port, command);
if (!String.IsNullOrEmpty(username))
{
return _httpProvider.DownloadString(url, username, password);
}
return _httpProvider.DownloadString(url);
}
public virtual void TestNotification(string host, int port, string username, string password)
{
logger.Trace("Sending Test Notifcation to XBMC Host: {0}", host);
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", "Test Notification", "Success! Notifications are setup correctly");
SendCommand(host, port, command, username, password);
}
}
}

View File

@ -6,9 +6,9 @@ namespace NzbDrone.Core.Notifications.Plex
{
public class PlexServer : NotificationBase<PlexServerSettings>
{
private readonly PlexProvider _plexProvider;
private readonly IPlexService _plexProvider;
public PlexServer(PlexProvider plexProvider)
public PlexServer(IPlexService plexProvider)
{
_plexProvider = plexProvider;
}
@ -18,6 +18,11 @@ public override string Name
get { return "Plex Server"; }
}
public override string ImplementationName
{
get { return "Plex Server"; }
}
public override void OnGrab(string message)
{
}
@ -36,7 +41,7 @@ private void UpdateIfEnabled()
{
if (Settings.UpdateLibrary)
{
_plexProvider.UpdateLibrary(Settings.Host);
_plexProvider.UpdateLibrary(Settings);
}
}
}

View File

@ -11,7 +11,10 @@ public class PlexServerSettings : INotifcationSettings
[FieldDefinition(0, Label = "Host", HelpText = "Plex Server Host (IP or Hostname)")]
public String Host { get; set; }
[FieldDefinition(1, Label = "Update Library", HelpText = "Update Library on Download/Rename")]
[FieldDefinition(1, Label = "Port")]
public Int32 Port { get; set; }
[FieldDefinition(2, Label = "Update Library")]
public Boolean UpdateLibrary { get; set; }
public bool IsValid

View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.Notifications.Plex
{
public interface IPlexService
{
void Notify(PlexClientSettings settings, string header, string message);
void UpdateLibrary(PlexServerSettings settings);
}
public class PlexService : IPlexService, IExecute<TestPlexClientCommand>, IExecute<TestPlexServerCommand>
{
private readonly IHttpProvider _httpProvider;
private readonly Logger _logger;
public PlexService(IHttpProvider httpProvider, Logger logger)
{
_httpProvider = httpProvider;
_logger = logger;
}
public void Notify(PlexClientSettings settings, string header, string message)
{
try
{
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", header, message);
SendCommand(settings.Host, settings.Port, command, settings.Username, settings.Password);
}
catch(Exception ex)
{
_logger.WarnException("Failed to send notification to Plex Client: " + settings.Host, ex);
}
}
public void UpdateLibrary(PlexServerSettings settings)
{
try
{
_logger.Trace("Sending Update Request to Plex Server");
var sections = GetSectionKeys(settings);
sections.ForEach(s => UpdateSection(settings, s));
}
catch(Exception ex)
{
_logger.WarnException("Failed to Update Plex host: " + settings.Host, ex);
throw;
}
}
public List<int> GetSectionKeys(PlexServerSettings settings)
{
_logger.Trace("Getting sections from Plex host: {0}", settings.Host);
var url = String.Format("http://{0}:{1}/library/sections", settings.Host, settings.Port);
var xmlStream = _httpProvider.DownloadStream(url, null);
var xDoc = XDocument.Load(xmlStream);
var mediaContainer = xDoc.Descendants("MediaContainer").FirstOrDefault();
var directories = mediaContainer.Descendants("Directory").Where(x => x.Attribute("type").Value == "show");
return directories.Select(d => Int32.Parse(d.Attribute("key").Value)).ToList();
}
public void UpdateSection(PlexServerSettings settings, int key)
{
_logger.Trace("Updating Plex host: {0}, Section: {1}", settings.Host, key);
var url = String.Format("http://{0}:{1}/library/sections/{2}/refresh", settings.Host, settings.Port, key);
_httpProvider.DownloadString(url);
}
public string SendCommand(string host, int port, string command, string username, string password)
{
var url = String.Format("http://{0}:{1}/xbmcCmds/xbmcHttp?command={2}", host, port, command);
if (!String.IsNullOrEmpty(username))
{
return _httpProvider.DownloadString(url, username, password);
}
return _httpProvider.DownloadString(url);
}
public void Execute(TestPlexClientCommand message)
{
_logger.Trace("Sending Test Notifcation to Plex Client: {0}", message.Host);
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", "Test Notification", "Success! Notifications are setup correctly");
var result = SendCommand(message.Host, message.Port, command, message.Username, message.Password);
if (String.IsNullOrWhiteSpace(result) ||
result.IndexOf("error", StringComparison.InvariantCultureIgnoreCase) > -1)
{
throw new Exception("Unable to connect to Plex Client");
}
}
public void Execute(TestPlexServerCommand message)
{
if (!GetSectionKeys(new PlexServerSettings {Host = message.Host, Port = message.Port}).Any())
{
throw new Exception("Unable to connect to Plex Server");
}
}
}
}

View File

@ -0,0 +1,12 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Notifications.Plex
{
public class TestPlexClientCommand : ICommand
{
public string Host { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Notifications.Plex
{
public class TestPlexServerCommand : ICommand
{
public string Host { get; set; }
public int Port { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Notifications.Prowl
{
public class InvalidApiKeyException : Exception
{
public InvalidApiKeyException()
{
}
public InvalidApiKeyException(string message) : base(message)
{
}
}
}

View File

@ -5,9 +5,9 @@ namespace NzbDrone.Core.Notifications.Prowl
{
public class Prowl : NotificationBase<ProwlSettings>
{
private readonly ProwlProvider _prowlProvider;
private readonly IProwlService _prowlProvider;
public Prowl(ProwlProvider prowlProvider)
public Prowl(IProwlService prowlProvider)
{
_prowlProvider = prowlProvider;
}
@ -17,6 +17,11 @@ public override string Name
get { return "Prowl"; }
}
public override string ImplementationName
{
get { return "Prowl"; }
}
public override void OnGrab(string message)
{
const string title = "Episode Grabbed";

View File

@ -1,78 +0,0 @@
using System;
using NLog;
using Prowlin;
namespace NzbDrone.Core.Notifications.Prowl
{
public class ProwlProvider
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public virtual bool Verify(string apiKey)
{
try
{
var verificationRequest = new Verification();
verificationRequest.ApiKey = apiKey;
var client = new ProwlClient();
Logger.Trace("Verifying API Key: {0}", apiKey);
var verificationResult = client.SendVerification(verificationRequest);
if (String.IsNullOrWhiteSpace(verificationResult.ErrorMessage) && verificationResult.ResultCode == "200")
return true;
}
catch (Exception ex)
{
Logger.TraceException(ex.Message, ex);
Logger.Warn("Invalid API Key: {0}", apiKey);
}
return false;
}
public virtual bool SendNotification(string title, string message, string apiKey, NotificationPriority priority = NotificationPriority.Normal, string url = null)
{
try
{
var notification = new Prowlin.Notification
{
Application = "NzbDrone",
Description = message,
Event = title,
Priority = priority,
Url = url
};
notification.AddApiKey(apiKey.Trim());
var client = new ProwlClient();
Logger.Trace("Sending Prowl Notification");
var notificationResult = client.SendNotification(notification);
if (String.IsNullOrWhiteSpace(notificationResult.ErrorMessage))
return true;
}
catch (Exception ex)
{
Logger.TraceException(ex.Message, ex);
Logger.Warn("Invalid API Key: {0}", apiKey);
}
return false;
}
public virtual void TestNotification(string apiKeys)
{
const string title = "Test Notification";
const string message = "This is a test message from NzbDrone";
SendNotification(title, message, apiKeys);
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using NLog;
using NzbDrone.Common.Messaging;
using Prowlin;
namespace NzbDrone.Core.Notifications.Prowl
{
public interface IProwlService
{
void SendNotification(string title, string message, string apiKey, NotificationPriority priority = NotificationPriority.Normal, string url = null);
}
public class ProwlService : IProwlService, IExecute<TestProwlCommand>
{
private readonly Logger _logger;
public ProwlService(Logger logger)
{
_logger = logger;
}
public void SendNotification(string title, string message, string apiKey, NotificationPriority priority = NotificationPriority.Normal, string url = null)
{
try
{
var notification = new Prowlin.Notification
{
Application = "NzbDrone",
Description = message,
Event = title,
Priority = priority,
Url = url
};
notification.AddApiKey(apiKey.Trim());
var client = new ProwlClient();
_logger.Trace("Sending Prowl Notification");
var notificationResult = client.SendNotification(notification);
if (!String.IsNullOrWhiteSpace(notificationResult.ErrorMessage))
{
throw new InvalidApiKeyException("API Key: " + apiKey + " is invalid");
}
}
catch (Exception ex)
{
_logger.TraceException(ex.Message, ex);
_logger.Warn("Invalid API Key: {0}", apiKey);
}
}
public void Verify(string apiKey)
{
try
{
var verificationRequest = new Verification();
verificationRequest.ApiKey = apiKey;
var client = new ProwlClient();
_logger.Trace("Verifying API Key: {0}", apiKey);
var verificationResult = client.SendVerification(verificationRequest);
if (!String.IsNullOrWhiteSpace(verificationResult.ErrorMessage) &&
verificationResult.ResultCode != "200")
{
throw new InvalidApiKeyException("API Key: " + apiKey + " is invalid");
}
}
catch (Exception ex)
{
_logger.TraceException(ex.Message, ex);
_logger.Warn("Invalid API Key: {0}", apiKey);
throw new InvalidApiKeyException("API Key: " + apiKey + " is invalid");
}
}
public void Execute(TestProwlCommand message)
{
Verify(message.ApiKey);
const string title = "Test Notification";
const string body = "This is a test message from NzbDrone";
SendNotification(title, body, message.ApiKey);
}
}
}

View File

@ -0,0 +1,10 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Notifications.Prowl
{
public class TestProwlCommand : ICommand
{
public string ApiKey { get; set; }
public int Priority { get; set; }
}
}

View File

@ -17,6 +17,11 @@ public override string Name
get { return "XBMC"; }
}
public override string ImplementationName
{
get { return "XBMC"; }
}
public override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";

View File

@ -266,7 +266,9 @@
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
<Compile Include="Download\EpisodeGrabbedEvent.cs" />
<Compile Include="Download\SeriesRenamedEvent.cs" />
<Compile Include="Notifications\Email\TestEmailCommand.cs" />
<Compile Include="Notifications\Growl\GrowlSettings.cs" />
<Compile Include="Notifications\Growl\TestGrowlCommand.cs" />
<Compile Include="Notifications\NotificationSettingsProvider.cs" />
<Compile Include="Notifications\INotification.cs" />
<Compile Include="Notifications\Notification.cs" />
@ -324,11 +326,15 @@
<Compile Include="MetadataSource\Trakt\Season.cs" />
<Compile Include="MetadataSource\Trakt\Show.cs" />
<Compile Include="Notifications\INotifcationSettings.cs" />
<Compile Include="Notifications\Plex\TestPlexServerCommand.cs" />
<Compile Include="Notifications\Plex\PlexServer.cs" />
<Compile Include="Notifications\Plex\PlexClientSettings.cs" />
<Compile Include="Notifications\Plex\PlexServerSettings.cs" />
<Compile Include="Notifications\Plex\TestPlexClientCommand.cs" />
<Compile Include="Notifications\Prowl\InvalidApiKeyException.cs" />
<Compile Include="Notifications\Prowl\ProwlSettings.cs" />
<Compile Include="Notifications\Email\EmailSettings.cs" />
<Compile Include="Notifications\Prowl\TestProwlCommand.cs" />
<Compile Include="Notifications\Xbmc\HttpApiProvider.cs" />
<Compile Include="Notifications\Xbmc\IApiProvider.cs" />
<Compile Include="Notifications\Xbmc\InvalidXbmcVersionException.cs" />
@ -454,7 +460,7 @@
<Compile Include="Notifications\Xbmc\Xbmc.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Notifications\Growl\GrowlProvider.cs">
<Compile Include="Notifications\Growl\GrowlService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="History\HistoryService.cs">
@ -472,11 +478,11 @@
<Compile Include="MediaFiles\MediaFileService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Notifications\Plex\PlexProvider.cs" />
<Compile Include="Notifications\Plex\PlexService.cs" />
<Compile Include="MediaFiles\DownloadedEpisodesImportService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Notifications\Prowl\ProwlProvider.cs">
<Compile Include="Notifications\Prowl\ProwlService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Qualities\QualityProfileService.cs">
@ -492,7 +498,7 @@
<Compile Include="Tv\SeriesService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Notifications\Email\EmailProvider.cs">
<Compile Include="Notifications\Email\EmailService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Notifications\Xbmc\XbmcService.cs">

View File

@ -1,7 +1,7 @@
<div class="add-notification-item span3">
<div class="row">
<div class="span3">
{{name}}
{{implementationName}}
</div>
</div>
</div>

View File

@ -20,7 +20,6 @@ define([
addNotification: function () {
this.model.set('id', undefined);
this.model.set('name', '');
var view = new NzbDrone.Settings.Notifications.EditView({ model: this.model, notificationCollection: this.notificationCollection });
NzbDrone.modalRegion.show(view);
}

View File

@ -2,6 +2,7 @@
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>On Grab</th>
<th>On Download</th>
<th>Controls</th>

View File

@ -1,9 +1,9 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
{{#if id}}
<h3>Edit</h3>
<h3>Edit - {{implementationName}}</h3>
{{else}}
<h3>Add</h3>
<h3>Add - {{implementationName}}</h3>
{{/if}}
</div>
<div class="modal-body">

View File

@ -1,4 +1,5 @@
<td name="name"></td>
<td name="implementationName"></td>
<td name="onGrab"></td>
<td name="onDownload"></td>
<td name="cutoff.name"></td>