feat(notifications): Added Partially Available Notifications

pull/4340/head
tidusjar 3 years ago
parent 6fdeeea87b
commit 1ef45dc44c

@ -14,5 +14,6 @@
IssueResolved = 9,
IssueComment = 10,
Newsletter = 11,
PartiallyAvailable = 12
}
}

@ -213,7 +213,51 @@ namespace Ombi.Notifications.Tests
[Test]
public void TvNotificationTests()
{
var notificationOptions = new NotificationOptions();
var notificationOptions = new NotificationOptions
{
NotificationType = Helpers.NotificationType.PartiallyAvailable
};
var req = F.Build<ChildRequests>()
.With(x => x.RequestType, RequestType.TvShow)
.With(x => x.Available, true)
.Create();
var customization = new CustomizationSettings
{
ApplicationUrl = "url",
ApplicationName = "name"
};
var userPrefs = new UserNotificationPreferences();
sut.Setup(notificationOptions, req, customization, userPrefs);
Assert.That(req.Id.ToString(), Is.EqualTo(sut.RequestId));
Assert.That(req.ParentRequest.ExternalProviderId.ToString(), Is.EqualTo(sut.ProviderId));
Assert.That(req.ParentRequest.Title.ToString(), Is.EqualTo(sut.Title));
Assert.That(req.RequestedUser.UserName, Is.EqualTo(sut.RequestedUser));
Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.Alias));
Assert.That(req.RequestedDate.ToString("D"), Is.EqualTo(sut.RequestedDate));
Assert.That("TV Show", Is.EqualTo(sut.Type));
Assert.That(req.ParentRequest.Overview, Is.EqualTo(sut.Overview));
Assert.That(req.ParentRequest.ReleaseDate.Year.ToString(), Is.EqualTo(sut.Year));
Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason));
Assert.That(req.MarkedAsAvailable?.ToString("D"), Is.EqualTo(sut.AvailableDate));
Assert.That("https://image.tmdb.org/t/p/w300/" + req.ParentRequest.PosterPath, Is.EqualTo(sut.PosterImage));
Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason));
Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.UserPreference));
Assert.That(null, Is.EqualTo(sut.AdditionalInformation));
Assert.That("Available", Is.EqualTo(sut.RequestStatus));
Assert.That("url", Is.EqualTo(sut.ApplicationUrl));
Assert.That("name", Is.EqualTo(sut.ApplicationName));
}
[Test]
public void TvNotificationPartialAvailablilityTests()
{
var notificationOptions = new NotificationOptions {
NotificationType = Helpers.NotificationType.PartiallyAvailable
};
notificationOptions.Substitutes.Add("Season", "1");
notificationOptions.Substitutes.Add("Episodes", "1, 2");
var req = F.Build<ChildRequests>()
.With(x => x.RequestType, RequestType.TvShow)
.With(x => x.Available, true)
@ -244,6 +288,8 @@ namespace Ombi.Notifications.Tests
Assert.That("Available", Is.EqualTo(sut.RequestStatus));
Assert.That("url", Is.EqualTo(sut.ApplicationUrl));
Assert.That("name", Is.EqualTo(sut.ApplicationName));
Assert.That(sut.PartiallyAvailableEpisodeNumbers, Is.EqualTo("1, 2"));
Assert.That(sut.PartiallyAvailableSeasonNumber, Is.EqualTo("1"));
}
[Test]

@ -94,6 +94,10 @@ namespace Ombi.Notifications.Agents
{
await Run(model, settings, NotificationType.RequestAvailable);
}
protected override async Task PartiallyAvailable(NotificationOptions model, DiscordNotificationSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
protected override async Task Send(NotificationMessage model, DiscordNotificationSettings settings)
{
@ -166,8 +170,6 @@ namespace Ombi.Notifications.Agents
author.name = appName;
}
var embed = new DiscordEmbeds
{
fields = fields,

@ -219,6 +219,25 @@ namespace Ombi.Notifications.Agents
}
protected override async Task PartiallyAvailable(NotificationOptions model, EmailNotificationSettings settings)
{
var message = await LoadTemplate(NotificationType.PartiallyAvailable, model, settings);
if (message == null)
{
return;
}
var plaintext = await LoadPlainTextMessage(NotificationType.PartiallyAvailable, model, settings);
message.Other.Add("PlainTextBody", plaintext);
await SendToSubscribers(settings, message);
message.To = model.RequestType == RequestType.Movie
? MovieRequest.RequestedUser.Email
: TvRequest.RequestedUser.Email;
await Send(message, settings);
}
protected override async Task RequestApproved(NotificationOptions model, EmailNotificationSettings settings)
{
var message = await LoadTemplate(NotificationType.RequestApproved, model, settings);

@ -112,5 +112,10 @@ namespace Ombi.Notifications.Agents
await Send(notification, settings);
}
protected override async Task PartiallyAvailable(NotificationOptions model, GotifySettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
}
}

@ -316,5 +316,25 @@ namespace Ombi.Notifications.Agents
}
}
}
protected override async Task PartiallyAvailable(NotificationOptions model, MobileNotificationSettings settings)
{
var parsed = await LoadTemplate(NotificationAgent.Mobile, NotificationType.PartiallyAvailable, model);
if (parsed.Disabled)
{
_logger.LogInformation($"Template {NotificationType.PartiallyAvailable} is disabled for {NotificationAgent.Mobile}");
return;
}
var notification = new NotificationMessage
{
Message = parsed.Message,
};
// Send to user
var playerIds = GetUsers(model, NotificationType.PartiallyAvailable);
await AddSubscribedUsers(playerIds);
await Send(playerIds, notification, settings, model);
}
}
}

@ -90,6 +90,10 @@ namespace Ombi.Notifications.Agents
{
await Run(model, settings, NotificationType.RequestAvailable);
}
protected override async Task PartiallyAvailable(NotificationOptions model, MattermostNotificationSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
protected override async Task Send(NotificationMessage model, MattermostNotificationSettings settings)
{

@ -359,5 +359,26 @@ namespace Ombi.Notifications.Agents
}
}
}
protected override async Task PartiallyAvailable(NotificationOptions model, MobileNotificationSettings settings)
{
var parsed = await LoadTemplate(NotificationAgent.Mobile, NotificationType.PartiallyAvailable, model);
if (parsed.Disabled)
{
_logger.LogInformation($"Template {NotificationType.PartiallyAvailable} is disabled for {NotificationAgent.Mobile}");
return;
}
var notification = new NotificationMessage
{
Message = parsed.Message,
Subject = "New Request",
Data = GetNotificationData(parsed, NotificationType.PartiallyAvailable)
};
// Get admin devices
var playerIds = await GetAdmins(NotificationType.PartiallyAvailable);
await Send(playerIds, notification, settings, model, true);
}
}
}

@ -121,5 +121,10 @@ namespace Ombi.Notifications.Agents
await Send(notification, settings);
}
protected override async Task PartiallyAvailable(NotificationOptions model, PushbulletSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
}
}

@ -121,5 +121,10 @@ namespace Ombi.Notifications.Agents
};
await Send(notification, settings);
}
protected override async Task PartiallyAvailable(NotificationOptions model, PushoverSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
}
}

@ -138,5 +138,10 @@ namespace Ombi.Notifications.Agents
notification.Other.Add("image", parsed.Image);
await Send(notification, settings);
}
protected override async Task PartiallyAvailable(NotificationOptions model, SlackNotificationSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
}
}

@ -115,5 +115,10 @@ namespace Ombi.Notifications.Agents
};
await Send(notification, settings);
}
protected override async Task PartiallyAvailable(NotificationOptions model, TelegramSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
}
}

@ -119,5 +119,10 @@ namespace Ombi.Notifications.Agents
await Send(notification, settings);
}
protected override async Task PartiallyAvailable(NotificationOptions model, WebhookSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
}
}

@ -123,5 +123,10 @@ namespace Ombi.Notifications.Agents
};
await Send(notification, settings);
}
protected override async Task PartiallyAvailable(NotificationOptions model, TwilioSettings settings)
{
await Run(model, settings, NotificationType.PartiallyAvailable);
}
}
}

@ -110,6 +110,15 @@ namespace Ombi.Notifications
case NotificationType.IssueComment:
await IssueComment(model, notificationSettings);
break;
case NotificationType.AdminNote:
break;
case NotificationType.WelcomeEmail:
break;
case NotificationType.Newsletter:
break;
case NotificationType.PartiallyAvailable:
await PartiallyAvailable(model, notificationSettings);
break;
default:
throw new ArgumentOutOfRangeException();
}
@ -236,6 +245,7 @@ namespace Ombi.Notifications
protected abstract Task RequestDeclined(NotificationOptions model, T settings);
protected abstract Task RequestApproved(NotificationOptions model, T settings);
protected abstract Task AvailableRequest(NotificationOptions model, T settings);
protected abstract Task PartiallyAvailable(NotificationOptions model, T settings);
protected abstract Task Send(NotificationMessage model, T settings);
protected abstract Task Test(NotificationOptions model, T settings);
}

@ -33,7 +33,7 @@ namespace Ombi.Notifications
UserNotificationPreferences pref)
{
LoadIssues(opts);
LoadCommon(req, s, pref);
LoadCommon(req, s, pref, opts);
LoadTitle(opts, req);
ProviderId = req?.TheMovieDbId.ToString() ?? string.Empty;
Year = req?.ReleaseDate.Year.ToString();
@ -47,7 +47,7 @@ namespace Ombi.Notifications
UserNotificationPreferences pref)
{
LoadIssues(opts);
LoadCommon(req, s, pref);
LoadCommon(req, s, pref, opts);
LoadTitle(opts, req);
ProviderId = req?.ParentRequest?.ExternalProviderId.ToString() ?? string.Empty;
Year = req?.ParentRequest?.ReleaseDate.Year.ToString();
@ -83,7 +83,7 @@ namespace Ombi.Notifications
UserNotificationPreferences pref)
{
LoadIssues(opts);
LoadCommon(req, s, pref);
LoadCommon(req, s, pref, opts);
LoadTitle(opts, req);
ProviderId = req?.ForeignArtistId ?? string.Empty;
Year = req?.ReleaseDate.Year.ToString();
@ -106,7 +106,7 @@ namespace Ombi.Notifications
: string.Empty;
}
private void LoadCommon(BaseRequest req, CustomizationSettings s, UserNotificationPreferences pref)
private void LoadCommon(BaseRequest req, CustomizationSettings s, UserNotificationPreferences pref, NotificationOptions opts)
{
ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s.ApplicationName;
ApplicationUrl = s?.ApplicationUrl.HasValue() ?? false ? s.ApplicationUrl : string.Empty;
@ -137,6 +137,18 @@ namespace Ombi.Notifications
{
UserPreference = pref.Value.HasValue() ? pref.Value : Alias;
}
if (opts.NotificationType == NotificationType.PartiallyAvailable)
{
if (opts.Substitutes.TryGetValue("Season", out var sNumber))
{
PartiallyAvailableSeasonNumber = sNumber;
}
if (opts.Substitutes.TryGetValue("Episodes", out var epNumber))
{
PartiallyAvailableEpisodeNumbers = epNumber;
}
}
}
private static string HumanizeReturnType(RequestType? requestType)
@ -220,6 +232,8 @@ namespace Ombi.Notifications
public string AvailableDate { get; set; }
public string RequestStatus { get; set; }
public string ProviderId { get; set; }
public string PartiallyAvailableEpisodeNumbers { get; set; }
public string PartiallyAvailableSeasonNumber { get; set; }
// System Defined
private string LongDate => DateTime.Now.ToString("D");
@ -259,6 +273,8 @@ namespace Ombi.Notifications
{ nameof(AvailableDate), AvailableDate },
{ nameof(RequestStatus), RequestStatus },
{ nameof(ProviderId), ProviderId },
{ nameof(PartiallyAvailableEpisodeNumbers), PartiallyAvailableEpisodeNumbers },
{ nameof(PartiallyAvailableSeasonNumber), PartiallyAvailableSeasonNumber },
};
}
}

@ -154,24 +154,19 @@ namespace Ombi.Schedule.Jobs.Radarr
{
availableEpisode.Add(new AvailabilityModel
{
Id = episode.Id
Id = episode.Id,
EpisodeNumber = episode.EpisodeNumber,
SeasonNumber = episode.Season.SeasonNumber
});
episode.Available = true;
}
}
}
//TODO Partial avilability notifications here
if (availableEpisode.Any())
{
//await _hub.Clients.Clients(NotificationHub.AdminConnectionIds)
// .SendAsync(NotificationHub.NotificationEvent, "Sonarr Availability Checker found some new available episodes!");
await _tvRequest.Save();
}
//foreach(var c in availableEpisode)
//{
// await _tvRepo.MarkEpisodeAsAvailable(c.Id);
//}
// Check to see if all of the episodes in all seasons are available for this request
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
@ -193,6 +188,20 @@ namespace Ombi.Schedule.Jobs.Radarr
Recipient = child.RequestedUser.Email
});
}
else if (availableEpisode.Any())
{
var notification = new NotificationOptions
{
DateTime = DateTime.Now,
NotificationType = NotificationType.PartiallyAvailable,
RequestId = child.Id,
RequestType = RequestType.TvShow,
Recipient = child.RequestedUser.Email,
};
notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString());
notification.Substitutes.Add("Episodes", string.Join(", ", availableEpisode.Select(x => x.EpisodeNumber)));
await _notification.Notify(notification);
}
}
await _tvRequest.Save();

@ -1,10 +1,10 @@
using Ombi.Store.Entities;
namespace Ombi.Schedule.Jobs.Plex.Models
namespace Ombi.Schedule.Jobs
{
public class AvailabilityModel
{
public int Id { get; set; }
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
public string RequestedUser { get; set; }
}
}

@ -1,31 +1,5 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: EmbyAvaliabilityCheker.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
@ -173,6 +147,7 @@ namespace Ombi.Schedule.Jobs.Emby
x.Series.Title == child.Title);
}
var availableEpisode = new List<AvailabilityModel>();
foreach (var season in child.SeasonRequests)
{
foreach (var episode in season.Episodes)
@ -188,11 +163,22 @@ namespace Ombi.Schedule.Jobs.Emby
if (foundEp != null)
{
availableEpisode.Add(new AvailabilityModel
{
Id = episode.Id,
EpisodeNumber = episode.EpisodeNumber,
SeasonNumber = episode.Season.SeasonNumber
});
episode.Available = true;
}
}
}
if (availableEpisode.Any())
{
await _tvRepo.Save();
}
// Check to see if all of the episodes in all seasons are available for this request
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
if (allAvailable)
@ -209,6 +195,20 @@ namespace Ombi.Schedule.Jobs.Emby
Recipient = child.RequestedUser.Email
});
}
else if (availableEpisode.Any())
{
var notification = new NotificationOptions
{
DateTime = DateTime.Now,
NotificationType = NotificationType.PartiallyAvailable,
RequestId = child.Id,
RequestType = RequestType.TvShow,
Recipient = child.RequestedUser.Email,
};
notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString());
notification.Substitutes.Add("Episodes", string.Join(", ", availableEpisode.Select(x => x.EpisodeNumber)));
await _notificationService.Notify(notification);
}
}
await _tvRepo.Save();

@ -26,6 +26,7 @@
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
@ -173,6 +174,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin
x.Series.Title == child.Title);
}
var availableEpisode = new List<AvailabilityModel>();
foreach (var season in child.SeasonRequests)
{
foreach (var episode in season.Episodes)
@ -188,11 +190,22 @@ namespace Ombi.Schedule.Jobs.Jellyfin
if (foundEp != null)
{
availableEpisode.Add(new AvailabilityModel
{
Id = episode.Id,
EpisodeNumber = episode.EpisodeNumber,
SeasonNumber = episode.Season.SeasonNumber
});
episode.Available = true;
}
}
}
if (availableEpisode.Any())
{
await _tvRepo.Save();
}
// Check to see if all of the episodes in all seasons are available for this request
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
if (allAvailable)
@ -209,6 +222,20 @@ namespace Ombi.Schedule.Jobs.Jellyfin
Recipient = child.RequestedUser.Email
});
}
else if (availableEpisode.Any())
{
var notification = new NotificationOptions
{
DateTime = DateTime.Now,
NotificationType = NotificationType.PartiallyAvailable,
RequestId = child.Id,
RequestType = RequestType.TvShow,
Recipient = child.RequestedUser.Email,
};
notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString());
notification.Substitutes.Add("Episodes", string.Join(", ", availableEpisode.Select(x => x.EpisodeNumber)));
await _notificationService.Notify(notification);
}
}
await _tvRepo.Save();

@ -127,22 +127,19 @@ namespace Ombi.Schedule.Jobs.Plex
{
availableEpisode.Add(new AvailabilityModel
{
Id = episode.Id
Id = episode.Id,
EpisodeNumber = episode.EpisodeNumber,
SeasonNumber = episode.Season.SeasonNumber
});
episode.Available = true;
}
}
}
//TODO Partial avilability notifications here
if (availableEpisode.Any())
{
await _tvRepo.Save();
}
//foreach(var c in availableEpisode)
//{
// await _tvRepo.MarkEpisodeAsAvailable(c.Id);
//}
// Check to see if all of the episodes in all seasons are available for this request
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
@ -162,6 +159,20 @@ namespace Ombi.Schedule.Jobs.Plex
Recipient = child.RequestedUser.Email
});
}
else if (availableEpisode.Any())
{
var notification = new NotificationOptions
{
DateTime = DateTime.Now,
NotificationType = NotificationType.PartiallyAvailable,
RequestId = child.Id,
RequestType = RequestType.TvShow,
Recipient = child.RequestedUser.Email,
};
notification.Substitutes.Add("Season", availableEpisode.First().SeasonNumber.ToString());
notification.Substitutes.Add("Episodes", string.Join(", " ,availableEpisode.Select(x => x.EpisodeNumber)));
await _notificationService.Notify(notification);
}
}
await _tvRepo.Save();

@ -208,6 +208,16 @@ namespace Ombi.Store.Context
Enabled = true,
};
break;
case NotificationType.PartiallyAvailable:
notificationToAdd = new NotificationTemplates
{
NotificationType = notificationType,
Message = "Your TV request is now partially available! Season {PartiallyAvailableSeasonNumber} Episodes {PartiallyAvailableEpisodeNumbers}!",
Subject = "{ApplicationName}: Partially Available Request!",
Agent = agent,
Enabled = true,
};
break;
default:
throw new ArgumentOutOfRangeException();
}

@ -13,6 +13,7 @@
"discord.enabled": true,
"conventionalCommits.scopes": [
"discover",
"request-limits"
"request-limits",
"notifications"
]
}

@ -52,7 +52,7 @@ export enum NotificationType {
IssueResolved = 9,
IssueComment = 10,
Newsletter = 11,
WhatsApp = 12,
PartiallyAvailable = 12,
}
export interface IDiscordNotifcationSettings extends INotificationSettings {

Loading…
Cancel
Save