From c50e2bb644a6fcf62418eb54b35eb82c0b40f70f Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 25 Apr 2016 16:41:18 +0100 Subject: [PATCH] Added the actual notification part of #27 --- .../Interfaces/INotification.cs | 7 +- .../Interfaces/INotificationService.cs | 11 +++ .../Jobs/PlexAvailabilityChecker.cs | 81 ++++++++++++++----- .../Notification/EmailMessageNotification.cs | 68 +++++++--------- .../Notification/NotificationModel.cs | 1 + .../Notification/NotificationService.cs | 11 +++ .../Notification/NotificationType.cs | 3 +- .../Notification/PushbulletNotification.cs | 34 ++------ .../Notification/PushoverNotification.cs | 32 ++------ 9 files changed, 135 insertions(+), 113 deletions(-) diff --git a/PlexRequests.Services/Interfaces/INotification.cs b/PlexRequests.Services/Interfaces/INotification.cs index 2e4e55ea4..ea3488cfd 100644 --- a/PlexRequests.Services/Interfaces/INotification.cs +++ b/PlexRequests.Services/Interfaces/INotification.cs @@ -36,7 +36,12 @@ namespace PlexRequests.Services.Interfaces string NotificationName { get; } Task NotifyAsync(NotificationModel model); - + /// + /// Sends a notification to the user, this is usually for testing the settings. + /// + /// The model. + /// The settings. + /// Task NotifyAsync(NotificationModel model, Settings settings); } } \ No newline at end of file diff --git a/PlexRequests.Services/Interfaces/INotificationService.cs b/PlexRequests.Services/Interfaces/INotificationService.cs index 91563c6de..66f0853cd 100644 --- a/PlexRequests.Services/Interfaces/INotificationService.cs +++ b/PlexRequests.Services/Interfaces/INotificationService.cs @@ -33,7 +33,18 @@ namespace PlexRequests.Services.Interfaces { public interface INotificationService { + /// + /// Sends a notification to the user. This one is used in normal notification scenarios + /// + /// The model. + /// Task Publish(NotificationModel model); + /// + /// Sends a notification to the user, this is usually for testing the settings. + /// + /// The model. + /// The settings. + /// Task Publish(NotificationModel model, Settings settings); void Subscribe(INotification notification); void UnSubscribe(INotification notification); diff --git a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs index b47651123..54feb6262 100644 --- a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs +++ b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs @@ -37,6 +37,7 @@ using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Services.Interfaces; using PlexRequests.Services.Models; +using PlexRequests.Services.Notification; using PlexRequests.Store; using Quartz; @@ -45,13 +46,15 @@ namespace PlexRequests.Services.Jobs { public class PlexAvailabilityChecker : IJob, IAvailabilityChecker { - public PlexAvailabilityChecker(ISettingsService plexSettings, ISettingsService auth, IRequestService request, IPlexApi plex, ICacheProvider cache) + public PlexAvailabilityChecker(ISettingsService plexSettings, ISettingsService auth, IRequestService request, IPlexApi plex, ICacheProvider cache, + INotificationService notify) { Plex = plexSettings; Auth = auth; RequestService = request; PlexApi = plex; Cache = cache; + Notification = notify; } private ISettingsService Plex { get; } @@ -60,6 +63,7 @@ namespace PlexRequests.Services.Jobs private static Logger Log = LogManager.GetCurrentClassLogger(); private IPlexApi PlexApi { get; } private ICacheProvider Cache { get; } + private INotificationService Notification { get; } public void CheckAndUpdateAll() { @@ -67,7 +71,7 @@ namespace PlexRequests.Services.Jobs var plexSettings = Plex.GetSettings(); var authSettings = Auth.GetSettings(); Log.Trace("Getting all the requests"); - + if (!ValidateSettings(plexSettings, authSettings)) { Log.Info("Validation of the plex settings failed."); @@ -95,22 +99,12 @@ namespace PlexRequests.Services.Jobs Log.Info("There are no requests to check."); return; } - + var modifiedModel = new List(); foreach (var r in requestedModels) { Log.Trace("We are going to see if Plex has the following title: {0}", r.Title); - if (libraries == null) - { - libraries = new List { PlexApi.SearchContent(authSettings.PlexAuthToken, r.Title, plexSettings.FullUri) }; - if (libraries.Count == 0) - { - Log.Trace("Could not find any matching result for this title."); - continue; - } - } - Log.Trace("Search results from Plex for the following request: {0}", r.Title); var releaseDate = r.ReleaseDate == DateTime.MinValue ? string.Empty : r.ReleaseDate.ToString("yyyy"); @@ -130,7 +124,7 @@ namespace PlexRequests.Services.Jobs default: throw new ArgumentOutOfRangeException(); } - + if (matchResult) { r.Available = true; @@ -146,8 +140,10 @@ namespace PlexRequests.Services.Jobs if (modifiedModel.Any()) { + NotifyUsers(modifiedModel, authSettings.PlexAuthToken); RequestService.BatchUpdate(modifiedModel); } + } public List GetPlexMovies() @@ -205,8 +201,8 @@ namespace PlexRequests.Services.Jobs public bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year) { - return plexShows.Any(x => - (x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) || x.Title.StartsWith(title, StringComparison.CurrentCultureIgnoreCase)) && + return plexShows.Any(x => + (x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) || x.Title.StartsWith(title, StringComparison.CurrentCultureIgnoreCase)) && x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase)); } @@ -237,8 +233,8 @@ namespace PlexRequests.Services.Jobs public bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist) { - return plexAlbums.Any(x => - x.Title.Contains(title) && + return plexAlbums.Any(x => + x.Title.Contains(title) && //x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase) && x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase)); } @@ -271,7 +267,8 @@ namespace PlexRequests.Services.Jobs else { Log.Trace("Plex Lib GetSet Call"); - results = Cache.GetOrSet(CacheKeys.PlexLibaries, () => { + results = Cache.GetOrSet(CacheKeys.PlexLibaries, () => + { Log.Trace("Plex Lib API Call (inside getset)"); return GetLibraries(authSettings, plexSettings); }, CacheKeys.TimeFrameMinutes.SchedulerCaching); @@ -281,7 +278,7 @@ namespace PlexRequests.Services.Jobs { Log.Error(ex, "Failed to obtain Plex libraries"); } - + return results; } @@ -305,7 +302,7 @@ namespace PlexRequests.Services.Jobs Log.Trace("Returning Plex Libs"); return libs; - } + } private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth) { @@ -317,9 +314,49 @@ namespace PlexRequests.Services.Jobs return true; } + private void NotifyUsers(IEnumerable modelChanged, string apiKey) + { + try + { + var plexUser = PlexApi.GetUsers(apiKey); + if (plexUser?.User == null || plexUser.User.Length == 0) + { + return; + } + + foreach (var model in modelChanged) + { + var usersToNotify = model.UsersToNotify; // Users that selected the notification button when requesting a movie/tv show + foreach (var user in usersToNotify) + { + var email = plexUser.User.FirstOrDefault(x => x.Username == user); + if (email == null) + { + // We do not have a plex user that requested this! + continue; + } + var notificationModel = new NotificationModel + { + User = email.Username, + UserEmail = email.Email, + NotificationType = NotificationType.RequestAvailable, + Title = model.Title + }; + + // Send the notification to the user. + Notification.Publish(notificationModel); + } + } + } + catch (Exception e) + { + Log.Error(e); + } + } + public void Execute(IJobExecutionContext context) { - CheckAndUpdateAll(); + CheckAndUpdateAll(); } } } \ No newline at end of file diff --git a/PlexRequests.Services/Notification/EmailMessageNotification.cs b/PlexRequests.Services/Notification/EmailMessageNotification.cs index 4a359fb23..cd4c6b0a2 100644 --- a/PlexRequests.Services/Notification/EmailMessageNotification.cs +++ b/PlexRequests.Services/Notification/EmailMessageNotification.cs @@ -71,8 +71,8 @@ namespace PlexRequests.Services.Notification await EmailIssue(model, emailSettings); break; case NotificationType.RequestAvailable: - throw new NotImplementedException(); - + await EmailAvailableRequest(model, emailSettings); + break; case NotificationType.RequestApproved: throw new NotImplementedException(); @@ -120,23 +120,7 @@ namespace PlexRequests.Services.Notification Subject = $"Plex Requests: New request for {model.Title}!" }; - try - { - using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort)) - { - smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword); - smtp.EnableSsl = settings.Ssl; - await smtp.SendMailAsync(message).ConfigureAwait(false); - } - } - catch (SmtpException smtp) - { - Log.Error(smtp); - } - catch (Exception e) - { - Log.Error(e); - } + await Send(message, settings); } private async Task EmailIssue(NotificationModel model, EmailNotificationSettings settings) @@ -146,10 +130,34 @@ namespace PlexRequests.Services.Notification IsBodyHtml = true, To = { new MailAddress(settings.RecipientEmail) }, Body = $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!", - From = new MailAddress(settings.RecipientEmail), + From = new MailAddress(settings.EmailSender), Subject = $"Plex Requests: New issue for {model.Title}!" }; + await Send(message, settings); + } + + private async Task EmailAvailableRequest(NotificationModel model, EmailNotificationSettings settings) + { + if (!settings.EnableUserEmailNotifications) + { + await Task.FromResult(false); + } + + var message = new MailMessage + { + IsBodyHtml = true, + To = { new MailAddress(model.UserEmail) }, + Body = $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)", + From = new MailAddress(settings.EmailSender), + Subject = $"Plex Requests: {model.Title} is now available!" + }; + + await Send(message, settings); + } + + private async Task Send(MailMessage message, EmailNotificationSettings settings) + { try { using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort)) @@ -176,27 +184,11 @@ namespace PlexRequests.Services.Notification IsBodyHtml = true, To = { new MailAddress(settings.RecipientEmail) }, Body = "This is just a test! Success!", - From = new MailAddress(settings.RecipientEmail), + From = new MailAddress(settings.EmailSender), Subject = "Plex Requests: Test Message!" }; - try - { - using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort)) - { - smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword); - smtp.EnableSsl = settings.Ssl; - await smtp.SendMailAsync(message).ConfigureAwait(false); - } - } - catch (SmtpException smtp) - { - Log.Error(smtp); - } - catch (Exception e) - { - Log.Error(e); - } + await Send(message, settings); } } } \ No newline at end of file diff --git a/PlexRequests.Services/Notification/NotificationModel.cs b/PlexRequests.Services/Notification/NotificationModel.cs index 264d3e609..2cc4e7fdb 100644 --- a/PlexRequests.Services/Notification/NotificationModel.cs +++ b/PlexRequests.Services/Notification/NotificationModel.cs @@ -35,5 +35,6 @@ namespace PlexRequests.Services.Notification public DateTime DateTime { get; set; } public NotificationType NotificationType { get; set; } public string User { get; set; } + public string UserEmail { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Services/Notification/NotificationService.cs b/PlexRequests.Services/Notification/NotificationService.cs index 35e52fd7d..3e52e43fa 100644 --- a/PlexRequests.Services/Notification/NotificationService.cs +++ b/PlexRequests.Services/Notification/NotificationService.cs @@ -41,6 +41,11 @@ namespace PlexRequests.Services.Notification private static Logger Log = LogManager.GetCurrentClassLogger(); public ConcurrentDictionary Observers { get; } = new ConcurrentDictionary(); + /// + /// Sends a notification to the user. This one is used in normal notification scenarios + /// + /// The model. + /// public async Task Publish(NotificationModel model) { var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model)); @@ -48,6 +53,12 @@ namespace PlexRequests.Services.Notification await Task.WhenAll(notificationTasks).ConfigureAwait(false); } + /// + /// Sends a notification to the user, this is usually for testing the settings. + /// + /// The model. + /// The settings. + /// public async Task Publish(NotificationModel model, Settings settings) { var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model, settings)); diff --git a/PlexRequests.Services/Notification/NotificationType.cs b/PlexRequests.Services/Notification/NotificationType.cs index 22d0d29b1..5e7b32370 100644 --- a/PlexRequests.Services/Notification/NotificationType.cs +++ b/PlexRequests.Services/Notification/NotificationType.cs @@ -33,6 +33,7 @@ namespace PlexRequests.Services.Notification RequestAvailable, RequestApproved, AdminNote, - Test + Test, + } } diff --git a/PlexRequests.Services/Notification/PushbulletNotification.cs b/PlexRequests.Services/Notification/PushbulletNotification.cs index 521855dca..f4d6f802e 100644 --- a/PlexRequests.Services/Notification/PushbulletNotification.cs +++ b/PlexRequests.Services/Notification/PushbulletNotification.cs @@ -45,7 +45,6 @@ namespace PlexRequests.Services.Notification } private IPushbulletApi PushbulletApi { get; } private ISettingsService SettingsService { get; } - private PushbulletNotificationSettings Settings => GetSettings(); private static Logger Log = LogManager.GetCurrentClassLogger(); public string NotificationName => "PushbulletNotification"; @@ -107,45 +106,28 @@ namespace PlexRequests.Services.Notification { var message = $"{model.Title} has been requested by user: {model.User}"; var pushTitle = $"Plex Requests: {model.Title} has been requested!"; - try - { - var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier); - if (result == null) - { - Log.Error("Pushbullet api returned a null value, the notification did not get pushed"); - } - } - catch (Exception e) - { - Log.Error(e); - } + await Push(settings, message, pushTitle); } private async Task PushIssueAsync(NotificationModel model, PushbulletNotificationSettings settings) { var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}"; var pushTitle = $"Plex Requests: A new issue has been reported for {model.Title}"; - try - { - var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier); - if (result != null) - { - Log.Error("Pushbullet api returned a null value, the notification did not get pushed"); - } - } - catch (Exception e) - { - Log.Error(e); - } + await Push(settings, message, pushTitle); } private async Task PushTestAsync(NotificationModel model, PushbulletNotificationSettings settings) { var message = "This is just a test! Success!"; var pushTitle = "Plex Requests: Test Message!"; + await Push(settings, message, pushTitle); + } + + private async Task Push(PushbulletNotificationSettings settings, string message, string title) + { try { - var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier); + var result = await PushbulletApi.PushAsync(settings.AccessToken, title, message, settings.DeviceIdentifier); if (result != null) { Log.Error("Pushbullet api returned a null value, the notification did not get pushed"); diff --git a/PlexRequests.Services/Notification/PushoverNotification.cs b/PlexRequests.Services/Notification/PushoverNotification.cs index 47854b1d5..d39492563 100644 --- a/PlexRequests.Services/Notification/PushoverNotification.cs +++ b/PlexRequests.Services/Notification/PushoverNotification.cs @@ -45,7 +45,6 @@ namespace PlexRequests.Services.Notification } private IPushoverApi PushoverApi { get; } private ISettingsService SettingsService { get; } - private PushoverNotificationSettings Settings => GetSettings(); private static Logger Log = LogManager.GetCurrentClassLogger(); public string NotificationName => "PushoverNotification"; @@ -106,40 +105,23 @@ namespace PlexRequests.Services.Notification private async Task PushNewRequestAsync(NotificationModel model, PushoverNotificationSettings settings) { var message = $"Plex Requests: {model.Title} has been requested by user: {model.User}"; - try - { - var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken); - if (result?.status != 1) - { - Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed"); - } - } - catch (Exception e) - { - Log.Error(e); - } + await Push(settings, message); } private async Task PushIssueAsync(NotificationModel model, PushoverNotificationSettings settings) { var message = $"Plex Requests: A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}"; - try - { - var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken); - if (result?.status != 1) - { - Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed"); - } - } - catch (Exception e) - { - Log.Error(e); - } + await Push(settings, message); } private async Task PushTestAsync(NotificationModel model, PushoverNotificationSettings settings) { var message = $"Plex Requests: Test Message!"; + await Push(settings, message); + } + + private async Task Push(PushoverNotificationSettings settings, string message) + { try { var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);