From 4b1576398c392de5e4d10915a26fd3faa9abf761 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 13 Nov 2019 08:39:03 +0000 Subject: [PATCH 01/32] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6febb6683..8e5971c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## (unreleased) +## v3.0.4880 (2019-11-13) ### **New Features** From f0bd5c737609be685bf3f03a87d95d784b760b83 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 13 Nov 2019 22:28:52 +0000 Subject: [PATCH 02/32] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5971c06..e8b58c9c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v3.0.4887 (2019-11-13) + +### **Fixes** + +- Fixed issues with the RequestId Migration #3244 #3253 #3249 + ## v3.0.4880 (2019-11-13) ### **New Features** From 2dbac003acf4d7b8a1a8840f6dd7329ee247f11c Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 16 Nov 2019 00:34:06 +0000 Subject: [PATCH 03/32] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8b58c9c5..1984a6898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v3.0.4892 (2019-11-16) + +- Fixed an issue where some users couldn't start Ombi + ## v3.0.4887 (2019-11-13) ### **Fixes** From e08fbb5298d0aef2b2b977bf32dc97909ba04672 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 16 Nov 2019 00:34:17 +0000 Subject: [PATCH 04/32] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1984a6898..b5e9481d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## v3.0.4892 (2019-11-16) +### **Fixes** + - Fixed an issue where some users couldn't start Ombi ## v3.0.4887 (2019-11-13) From 102d6ea1395777facb3a956ed86e3f36c1041edf Mon Sep 17 00:00:00 2001 From: Javier Pastor Date: Sat, 16 Nov 2019 13:02:40 +0100 Subject: [PATCH 05/32] Update GenericEmailProvider.cs Fix #3267 --- src/Ombi.Notifications/GenericEmailProvider.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Notifications/GenericEmailProvider.cs b/src/Ombi.Notifications/GenericEmailProvider.cs index 15f17af92..49bdb1fa9 100644 --- a/src/Ombi.Notifications/GenericEmailProvider.cs +++ b/src/Ombi.Notifications/GenericEmailProvider.cs @@ -96,7 +96,21 @@ namespace Ombi.Notifications client.Authenticate(settings.Username, settings.Password); } _log.LogDebug("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated); - await client.SendAsync(message); + try + { + await client.SendAsync(message); + } + catch (MailKit.Net.Smtp.SmtpCommandException e) when (e.ErrorCode.Equals(MailKit.Net.Smtp.SmtpErrorCode.RecipientNotAccepted)) + { + if (e.StatusCode.Equals(MailKit.Net.Smtp.SmtpStatusCode.MailboxUnavailable)) + { + _log.LogError("Could not send email '{0}', address <{1}> does not exist.", message.Subject, model.To); + } + else + { + throw; + } + } await client.DisconnectAsync(true); } } @@ -178,4 +192,4 @@ namespace Ombi.Notifications } } } -} \ No newline at end of file +} From 9a4386e28efa449d53e7d64bc994c867959aef3d Mon Sep 17 00:00:00 2001 From: Javier Pastor Date: Sat, 16 Nov 2019 22:05:16 +0100 Subject: [PATCH 06/32] Update GenericEmailProvider.cs Add the two checks in when inside catch. --- src/Ombi.Notifications/GenericEmailProvider.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Ombi.Notifications/GenericEmailProvider.cs b/src/Ombi.Notifications/GenericEmailProvider.cs index 49bdb1fa9..90033e385 100644 --- a/src/Ombi.Notifications/GenericEmailProvider.cs +++ b/src/Ombi.Notifications/GenericEmailProvider.cs @@ -100,16 +100,9 @@ namespace Ombi.Notifications { await client.SendAsync(message); } - catch (MailKit.Net.Smtp.SmtpCommandException e) when (e.ErrorCode.Equals(MailKit.Net.Smtp.SmtpErrorCode.RecipientNotAccepted)) + catch (MailKit.Net.Smtp.SmtpCommandException e) when (e.ErrorCode.Equals(MailKit.Net.Smtp.SmtpErrorCode.RecipientNotAccepted) && e.StatusCode.Equals(MailKit.Net.Smtp.SmtpStatusCode.MailboxUnavailable)) { - if (e.StatusCode.Equals(MailKit.Net.Smtp.SmtpStatusCode.MailboxUnavailable)) - { - _log.LogError("Could not send email '{0}', address <{1}> does not exist.", message.Subject, model.To); - } - else - { - throw; - } + _log.LogError("Could not send email '{0}', address <{1}> does not exist.", message.Subject, model.To); } await client.DisconnectAsync(true); } From faba87cdc62f3867ae78ee304d150a6c00b3785b Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 20 Nov 2019 21:35:04 +0000 Subject: [PATCH 07/32] Fix for #3277 --- src/Ombi.Helpers.Tests/PlexHelperTests.cs | 2 + src/Ombi.Helpers/PlexHelper.cs | 92 ++++++----------------- 2 files changed, 23 insertions(+), 71 deletions(-) diff --git a/src/Ombi.Helpers.Tests/PlexHelperTests.cs b/src/Ombi.Helpers.Tests/PlexHelperTests.cs index 8ecb3fa0a..ef1119f9f 100644 --- a/src/Ombi.Helpers.Tests/PlexHelperTests.cs +++ b/src/Ombi.Helpers.Tests/PlexHelperTests.cs @@ -39,6 +39,8 @@ namespace Ombi.Helpers.Tests yield return new TestCaseData("com.plexapp.agents.agent47://tt2543456?lang=en", ProviderIdType.Imdb).Returns("tt2543456").SetName("Unknown IMDB agent"); yield return new TestCaseData("com.plexapp.agents.agent47://456822/1/1?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent"); yield return new TestCaseData("com.plexapp.agents.agent47://456822/999/999?lang=en", ProviderIdType.TvDb).Returns("456822").SetName("Unknown TvDb agent, large episode and season"); + yield return new TestCaseData("com.plexapp.agents.xbmcnfotv://153021/2/1?lang=xn", ProviderIdType.TvDb).Returns("153021").SetName("xmbc agent, tv episode"); + yield return new TestCaseData("com.plexapp.agents.xbmcnfotv://153021?lang=xn", ProviderIdType.TvDb).Returns("153021").SetName("xmbc agent, tv show"); } } diff --git a/src/Ombi.Helpers/PlexHelper.cs b/src/Ombi.Helpers/PlexHelper.cs index de61b8740..a25c3b14a 100644 --- a/src/Ombi.Helpers/PlexHelper.cs +++ b/src/Ombi.Helpers/PlexHelper.cs @@ -33,14 +33,15 @@ namespace Ombi.Helpers { public class PlexHelper { - private const string ImdbMatchExpression = "tt([0-9]{1,10})"; - private const string TvDbIdMatchExpression = "//[0-9]+/([0-9]{1,3})/([0-9]{1,3})"; + private const string ImdbMatchExpression = "tt([0-9]{1,10})"; + private const string TvDbIdMatchExpression = "//[0-9]+/?([0-9]{1,3})/?([0-9]{1,3})"; public static ProviderId GetProviderIdFromPlexGuid(string guid) { //com.plexapp.agents.thetvdb://269586/2/8?lang=en //com.plexapp.agents.themoviedb://390043?lang=en //com.plexapp.agents.imdb://tt2543164?lang=en + // https://github.com/tidusjar/Ombi/issues/3277 if (string.IsNullOrEmpty(guid)) { return new ProviderId(); @@ -55,7 +56,7 @@ namespace Ombi.Helpers { TheTvDb = guidSplit[1] }; - } else + } if (guid.Contains("themoviedb", CompareOptions.IgnoreCase)) { return new ProviderId @@ -63,7 +64,6 @@ namespace Ombi.Helpers TheMovieDb = guidSplit[1] }; } - else if (guid.Contains("imdb", CompareOptions.IgnoreCase)) { return new ProviderId @@ -71,74 +71,31 @@ namespace Ombi.Helpers ImdbId = guidSplit[1] }; } - else + + var imdbRegex = new Regex(ImdbMatchExpression, RegexOptions.Compiled); + var tvdbRegex = new Regex(TvDbIdMatchExpression, RegexOptions.Compiled); + var imdbMatch = imdbRegex.IsMatch(guid); + if (imdbMatch) { - var imdbRegex = new Regex(ImdbMatchExpression, RegexOptions.Compiled); - var tvdbRegex = new Regex(TvDbIdMatchExpression, RegexOptions.Compiled); - var imdbMatch = imdbRegex.IsMatch(guid); - if (imdbMatch) + return new ProviderId { - return new ProviderId - { - ImdbId = guidSplit[1] - }; - } - else + ImdbId = guidSplit[1] + }; + } + + // Check if it matches the TvDb pattern + var tvdbMatch = tvdbRegex.IsMatch(guid); + if (tvdbMatch) + { + return new ProviderId { - // Check if it matches the TvDb pattern - var tvdbMatch = tvdbRegex.IsMatch(guid); - if (tvdbMatch) - { - return new ProviderId - { - TheTvDb = guidSplit[1] - }; - } - } + TheTvDb = guidSplit[1] + }; } } return new ProviderId(); } - public static EpisodeModelHelper GetSeasonsAndEpisodesFromPlexGuid(string guid) - { - var ep = new EpisodeModelHelper(); - //com.plexapp.agents.thetvdb://269586/2/8?lang=en - //com.plexapp.agents.themoviedb://390043?lang=en - //com.plexapp.agents.imdb://tt2543164?lang=en - if (string.IsNullOrEmpty(guid)) - return null; - try - { - var guidSplit = guid.Split(new[] { '/', '?' }, StringSplitOptions.RemoveEmptyEntries); - if (guidSplit.Length > 2) - { - if (guid.Contains("thetvdb", CompareOptions.IgnoreCase)) - { - ep.ProviderId = new ProviderId {TheTvDb = guidSplit[1]}; - } - if (guid.Contains("themoviedb", CompareOptions.IgnoreCase)) - { - ep.ProviderId = new ProviderId { TheMovieDb = guidSplit[1] }; - - } - if (guid.Contains("imdb", CompareOptions.IgnoreCase)) - { - ep.ProviderId = new ProviderId { ImdbId = guidSplit[1] }; - - } - ep.SeasonNumber = int.Parse(guidSplit[2]); - ep.EpisodeNumber = int.Parse(guidSplit[3]); - } - return ep; - - } - catch (Exception) - { - return ep; - } - } - public static string GetPlexMediaUrl(string machineId, int mediaId) { var url = @@ -147,13 +104,6 @@ namespace Ombi.Helpers } } - public class EpisodeModelHelper - { - public ProviderId ProviderId { get; set; } - public int SeasonNumber { get; set; } - public int EpisodeNumber { get; set; } - } - public class ProviderId { public string TheTvDb { get; set; } From cdc002ecd73c1ac939c009981b0b96d6ac9dc157 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Fri, 29 Nov 2019 16:05:14 +0100 Subject: [PATCH 08/32] Add webhook notification and API logic --- src/Ombi.Api.Webhook/IWebhookApi.cs | 10 ++ src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj | 15 +++ src/Ombi.Api.Webhook/WebhookApi.cs | 36 ++++++ .../Models/UI/WebhookNotificationViewModel.cs | 15 +++ .../Agents/Interfaces/IWebhookNotification.cs | 6 + .../Agents/WebhookNotification.cs | 116 ++++++++++++++++++ .../Models/Notifications/WebhookSettings.cs | 9 ++ src/Ombi.sln | 11 +- 8 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 src/Ombi.Api.Webhook/IWebhookApi.cs create mode 100644 src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj create mode 100644 src/Ombi.Api.Webhook/WebhookApi.cs create mode 100644 src/Ombi.Core/Models/UI/WebhookNotificationViewModel.cs create mode 100644 src/Ombi.Notifications/Agents/Interfaces/IWebhookNotification.cs create mode 100644 src/Ombi.Notifications/Agents/WebhookNotification.cs create mode 100644 src/Ombi.Settings/Settings/Models/Notifications/WebhookSettings.cs diff --git a/src/Ombi.Api.Webhook/IWebhookApi.cs b/src/Ombi.Api.Webhook/IWebhookApi.cs new file mode 100644 index 000000000..755c7c789 --- /dev/null +++ b/src/Ombi.Api.Webhook/IWebhookApi.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Ombi.Api.Webhook +{ + public interface IWebhookApi + { + Task PushAsync(string endpoint, string accessToken, IReadOnlyDictionary parameters); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj b/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj new file mode 100644 index 000000000..321c1f333 --- /dev/null +++ b/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + 3.0.0.0 + 3.0.0.0 + + + + + + + + + \ No newline at end of file diff --git a/src/Ombi.Api.Webhook/WebhookApi.cs b/src/Ombi.Api.Webhook/WebhookApi.cs new file mode 100644 index 000000000..509de2622 --- /dev/null +++ b/src/Ombi.Api.Webhook/WebhookApi.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ombi.Api.Webhook +{ + public class WebhookApi : IWebhookApi + { + private static readonly CamelCasePropertyNamesContractResolver _nameResolver = new CamelCasePropertyNamesContractResolver(); + + public WebhookApi(IApi api) + { + _api = api; + } + + private readonly IApi _api; + + public async Task PushAsync(string baseUrl, string accessToken, IReadOnlyDictionary parameters) + { + var request = new Request("/", baseUrl, HttpMethod.Post); + request.AddHeader("Access-Token", accessToken); + request.ApplicationJsonContentType(); + + var body = parameters.ToDictionary( + x => _nameResolver.GetResolvedPropertyName(x.Key), + x => x.Value + ); + + request.AddJsonBody(body); + + await _api.Request(request); + } + } +} diff --git a/src/Ombi.Core/Models/UI/WebhookNotificationViewModel.cs b/src/Ombi.Core/Models/UI/WebhookNotificationViewModel.cs new file mode 100644 index 000000000..533de1d6b --- /dev/null +++ b/src/Ombi.Core/Models/UI/WebhookNotificationViewModel.cs @@ -0,0 +1,15 @@ + +using System.Collections.Generic; +using Ombi.Settings.Settings.Models.Notifications; +using Ombi.Store.Entities; + +namespace Ombi.Core.Models.UI +{ + /// + /// The view model for the notification settings page + /// + /// + public class WebhookNotificationViewModel : WebhookSettings + { + } +} diff --git a/src/Ombi.Notifications/Agents/Interfaces/IWebhookNotification.cs b/src/Ombi.Notifications/Agents/Interfaces/IWebhookNotification.cs new file mode 100644 index 000000000..303229878 --- /dev/null +++ b/src/Ombi.Notifications/Agents/Interfaces/IWebhookNotification.cs @@ -0,0 +1,6 @@ +namespace Ombi.Notifications.Agents +{ + public interface IWebhookNotification : INotification + { + } +} \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/WebhookNotification.cs b/src/Ombi.Notifications/Agents/WebhookNotification.cs new file mode 100644 index 000000000..fbbb1d352 --- /dev/null +++ b/src/Ombi.Notifications/Agents/WebhookNotification.cs @@ -0,0 +1,116 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Ombi.Api.Webhook; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Notifications.Models; +using Ombi.Settings.Settings.Models; +using Ombi.Settings.Settings.Models.Notifications; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Notifications.Agents +{ + public class WebhookNotification : BaseNotification, IWebhookNotification + { + public WebhookNotification(IWebhookApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, + ISettingsService s, IRepository sub, IMusicRequestRepository music, + IRepository userPref) : base(sn, r, m, t, s, log, sub, music, userPref) + { + Api = api; + Logger = log; + } + + public override string NotificationName => "WebhookNotification"; + + private IWebhookApi Api { get; } + private ILogger Logger { get; } + + protected override bool ValidateConfiguration(WebhookSettings settings) + { + return settings.Enabled && !string.IsNullOrEmpty(settings.WebhookUrl); + } + + protected override async Task NewRequest(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.NewRequest); + } + + + protected override async Task NewIssue(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.Issue); + } + + protected override async Task IssueComment(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.IssueComment); + } + + protected override async Task IssueResolved(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.IssueResolved); + } + + protected override async Task AddedToRequestQueue(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.ItemAddedToFaultQueue); + } + + protected override async Task RequestDeclined(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.RequestDeclined); + } + + protected override async Task RequestApproved(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.RequestApproved); + } + + protected override async Task AvailableRequest(NotificationOptions model, WebhookSettings settings) + { + await Run(model, settings, NotificationType.RequestAvailable); + } + + protected override async Task Send(NotificationMessage model, WebhookSettings settings) + { + try + { + await Api.PushAsync(settings.WebhookUrl, settings.ApplicationToken, model.Data); + } + catch (Exception e) + { + Logger.LogError(LoggingEvents.WebhookNotification, e, "Failed to send webhook notification"); + } + } + + protected override async Task Test(NotificationOptions model, WebhookSettings settings) + { + var message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!"; + var notification = new NotificationMessage + { + Message = message, + }; + await Send(notification, settings); + } + + private async Task Run(NotificationOptions model, WebhookSettings settings, NotificationType type) + { + var parsed = await LoadTemplate(NotificationAgent.Webhook, type, model); + if (parsed.Disabled) + { + Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Webhook}"); + return; + } + + var notification = new NotificationMessage + { + Data = parsed.Data, + }; + + await Send(notification, settings); + } + } +} diff --git a/src/Ombi.Settings/Settings/Models/Notifications/WebhookSettings.cs b/src/Ombi.Settings/Settings/Models/Notifications/WebhookSettings.cs new file mode 100644 index 000000000..1c304e84c --- /dev/null +++ b/src/Ombi.Settings/Settings/Models/Notifications/WebhookSettings.cs @@ -0,0 +1,9 @@ +namespace Ombi.Settings.Settings.Models.Notifications +{ + public class WebhookSettings : Settings + { + public bool Enabled { get; set; } + public string WebhookUrl { get; set; } + public string ApplicationToken { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.sln b/src/Ombi.sln index f4f683c11..e71c1a30c 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi\Ombi.csproj", "{C987AA67-AFE1-468F-ACD3-EAD5A48E1F6A}" EndProject @@ -100,6 +100,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Helpers.Tests", "Ombi. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Gotify", "Ombi.Api.Gotify\Ombi.Api.Gotify.csproj", "{105EA346-766E-45B8-928B-DE6991DCB7EB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Webhook", "Ombi.Api.Webhook\Ombi.Api.Webhook.csproj", "{E2186FDA-D827-4781-8663-130AC382F12C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -262,6 +264,10 @@ Global {105EA346-766E-45B8-928B-DE6991DCB7EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {105EA346-766E-45B8-928B-DE6991DCB7EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {105EA346-766E-45B8-928B-DE6991DCB7EB}.Release|Any CPU.Build.0 = Release|Any CPU + {E2186FDA-D827-4781-8663-130AC382F12C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2186FDA-D827-4781-8663-130AC382F12C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2186FDA-D827-4781-8663-130AC382F12C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2186FDA-D827-4781-8663-130AC382F12C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -300,6 +306,7 @@ Global {4FA21A20-92F4-462C-B929-2C517A88CC56} = {9293CA11-360A-4C20-A674-B9E794431BF5} {CC8CEFCD-0CB6-45BB-845F-508BCAB5BDC3} = {6F42AB98-9196-44C4-B888-D5E409F415A1} {105EA346-766E-45B8-928B-DE6991DCB7EB} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {E2186FDA-D827-4781-8663-130AC382F12C} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869} From 88f3f0f9f09b86efbb7e4475127c7d34854f08cc Mon Sep 17 00:00:00 2001 From: Namaneo Date: Fri, 29 Nov 2019 16:06:51 +0100 Subject: [PATCH 09/32] Integrate webhooks into existing notification mechanism --- src/Ombi.DependencyInjection/IocExtensions.cs | 3 ++ .../Ombi.DependencyInjection.csproj | 1 + src/Ombi.Helpers/LoggingEvents.cs | 1 + src/Ombi.Helpers/NotificationAgent.cs | 1 + src/Ombi.Mapping/Profiles/SettingsProfile.cs | 1 + .../Models/NotificationMessage.cs | 1 + .../NotificationMessageContent.cs | 5 +++- .../NotificationMessageResolver.cs | 2 +- .../Ombi.Notifications.csproj | 1 + .../Controllers/External/TesterController.cs | 28 ++++++++++++++++++- src/Ombi/Controllers/SettingsController.cs | 27 ++++++++++++++++++ 11 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index cec6bf4e3..f50417189 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -32,6 +32,7 @@ using Ombi.Api.DogNzb; using Ombi.Api.FanartTv; using Ombi.Api.Github; using Ombi.Api.Gotify; +using Ombi.Api.Webhook; using Ombi.Api.Lidarr; using Ombi.Api.Mattermost; using Ombi.Api.Notifications; @@ -122,6 +123,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -173,6 +175,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index ec905e718..0d3f8652b 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -35,6 +35,7 @@ + diff --git a/src/Ombi.Helpers/LoggingEvents.cs b/src/Ombi.Helpers/LoggingEvents.cs index 0723800ab..bfa452c13 100644 --- a/src/Ombi.Helpers/LoggingEvents.cs +++ b/src/Ombi.Helpers/LoggingEvents.cs @@ -33,6 +33,7 @@ namespace Ombi.Helpers public static EventId PushoverNotification => new EventId(4005); public static EventId TelegramNotifcation => new EventId(4006); public static EventId GotifyNotification => new EventId(4007); + public static EventId WebhookNotification => new EventId(4008); public static EventId TvSender => new EventId(5000); public static EventId SonarrSender => new EventId(5001); diff --git a/src/Ombi.Helpers/NotificationAgent.cs b/src/Ombi.Helpers/NotificationAgent.cs index 18f28105a..7544d033d 100644 --- a/src/Ombi.Helpers/NotificationAgent.cs +++ b/src/Ombi.Helpers/NotificationAgent.cs @@ -11,5 +11,6 @@ Mattermost = 6, Mobile = 7, Gotify = 8, + Webhook = 9, } } \ No newline at end of file diff --git a/src/Ombi.Mapping/Profiles/SettingsProfile.cs b/src/Ombi.Mapping/Profiles/SettingsProfile.cs index f460ce78b..3562b8ac2 100644 --- a/src/Ombi.Mapping/Profiles/SettingsProfile.cs +++ b/src/Ombi.Mapping/Profiles/SettingsProfile.cs @@ -20,6 +20,7 @@ namespace Ombi.Mapping.Profiles CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } } } \ No newline at end of file diff --git a/src/Ombi.Notifications/Models/NotificationMessage.cs b/src/Ombi.Notifications/Models/NotificationMessage.cs index f14604d3f..d336d830e 100644 --- a/src/Ombi.Notifications/Models/NotificationMessage.cs +++ b/src/Ombi.Notifications/Models/NotificationMessage.cs @@ -9,5 +9,6 @@ namespace Ombi.Notifications.Models public string To { get; set; } public Dictionary Other { get; set; } = new Dictionary(); + public IReadOnlyDictionary Data { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Notifications/NotificationMessageContent.cs b/src/Ombi.Notifications/NotificationMessageContent.cs index 37f7504e9..901b3bcb2 100644 --- a/src/Ombi.Notifications/NotificationMessageContent.cs +++ b/src/Ombi.Notifications/NotificationMessageContent.cs @@ -1,4 +1,6 @@ -namespace Ombi.Notifications +using System.Collections.Generic; + +namespace Ombi.Notifications { public class NotificationMessageContent { @@ -6,5 +8,6 @@ public string Subject { get; set; } public string Message { get; set; } public string Image { get; set; } + public IReadOnlyDictionary Data { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Notifications/NotificationMessageResolver.cs b/src/Ombi.Notifications/NotificationMessageResolver.cs index 451ef1b55..fe6102eda 100644 --- a/src/Ombi.Notifications/NotificationMessageResolver.cs +++ b/src/Ombi.Notifications/NotificationMessageResolver.cs @@ -47,7 +47,7 @@ namespace Ombi.Notifications body = ReplaceFields(bodyFields, parameters, body); subject = ReplaceFields(subjectFields, parameters, subject); - return new NotificationMessageContent { Message = body ?? string.Empty, Subject = subject ?? string.Empty}; + return new NotificationMessageContent { Message = body ?? string.Empty, Subject = subject ?? string.Empty, Data = parameters }; } public IEnumerable ProcessConditions(IEnumerable conditionalFields, IReadOnlyDictionary parameters) diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index 3fa4b4830..5d63fbc1a 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Ombi/Controllers/External/TesterController.cs b/src/Ombi/Controllers/External/TesterController.cs index 2894542f6..48a8e89db 100644 --- a/src/Ombi/Controllers/External/TesterController.cs +++ b/src/Ombi/Controllers/External/TesterController.cs @@ -40,7 +40,7 @@ namespace Ombi.Controllers.External IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm, IPlexApi plex, IEmbyApi emby, IRadarrApi radarr, ISonarrApi sonarr, ILogger log, IEmailProvider provider, ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, IMobileNotification mobileNotification, - ILidarrApi lidarrApi, IGotifyNotification gotifyNotification) + ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWebhookNotification webhookNotification) { Service = service; DiscordNotification = notification; @@ -62,6 +62,7 @@ namespace Ombi.Controllers.External MobileNotification = mobileNotification; LidarrApi = lidarrApi; GotifyNotification = gotifyNotification; + WebhookNotification = webhookNotification; } private INotificationService Service { get; } @@ -71,6 +72,7 @@ namespace Ombi.Controllers.External private ISlackNotification SlackNotification { get; } private IPushoverNotification PushoverNotification { get; } private IGotifyNotification GotifyNotification { get; } + private IWebhookNotification WebhookNotification { get; } private IMattermostNotification MattermostNotification { get; } private IPlexApi PlexApi { get; } private IRadarrApi RadarrApi { get; } @@ -181,6 +183,30 @@ namespace Ombi.Controllers.External } + /// + /// Sends a test message to configured webhook using the provided settings + /// + /// The settings. + /// + [HttpPost("webhook")] + public bool Webhook([FromBody] WebhookSettings settings) + { + try + { + settings.Enabled = true; + WebhookNotification.NotifyAsync( + new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings); + + return true; + } + catch (Exception e) + { + Log.LogError(LoggingEvents.Api, e, "Could not test your webhook"); + return false; + } + + } + /// /// Sends a test message to mattermost using the provided settings /// diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index ed246806a..df480865f 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -1007,6 +1007,33 @@ namespace Ombi.Controllers return model; } + /// + /// Saves the webhook notification settings. + /// + /// The model. + /// + [HttpPost("notifications/webhook")] + public async Task WebhookNotificationSettings([FromBody] WebhookNotificationViewModel model) + { + var settings = Mapper.Map(model); + var result = await Save(settings); + + return result; + } + + /// + /// Gets the webhook notification settings. + /// + /// + [HttpGet("notifications/webhook")] + public async Task WebhookNotificationSettings() + { + var settings = await Get(); + var model = Mapper.Map(settings); + + return model; + } + /// /// Saves the Newsletter notification settings. /// From c8cdf0056756947615a752c6482dc2ea8c82c3f3 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Fri, 29 Nov 2019 16:07:35 +0100 Subject: [PATCH 10/32] Add settings view for webhook configuration --- README.md | 1 + .../app/interfaces/INotificationSettings.ts | 5 ++ .../services/applications/tester.service.ts | 5 ++ .../app/services/settings.service.ts | 9 +++ .../notifications/webhook.component.html | 47 ++++++++++++++ .../notifications/webhook.component.ts | 64 +++++++++++++++++++ .../ClientApp/app/settings/settings.module.ts | 3 + .../app/settings/settingsmenu.component.html | 1 + 8 files changed, 135 insertions(+) create mode 100644 src/Ombi/ClientApp/app/settings/notifications/webhook.component.html create mode 100644 src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts diff --git a/README.md b/README.md index e8e269e7f..e9415432d 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ Supported notifications: * Pushover * Mattermost * Telegram +* Webhook ### The difference between Version 3 and 2 diff --git a/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts b/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts index 5472a6c7c..bda18c4ed 100644 --- a/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts @@ -101,6 +101,11 @@ export interface IGotifyNotificationSettings extends INotificationSettings { priority: number; } +export interface IWebhookNotificationSettings extends INotificationSettings { + webhookUrl: string; + applicationToken: string; +} + export interface IMattermostNotifcationSettings extends INotificationSettings { webhookUrl: string; username: string; diff --git a/src/Ombi/ClientApp/app/services/applications/tester.service.ts b/src/Ombi/ClientApp/app/services/applications/tester.service.ts index af619d583..bf6801e66 100644 --- a/src/Ombi/ClientApp/app/services/applications/tester.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/tester.service.ts @@ -12,6 +12,7 @@ import { IEmailNotificationSettings, IEmbyServer, IGotifyNotificationSettings, + IWebhookNotificationSettings, ILidarrSettings, IMattermostNotifcationSettings, IMobileNotificationTestSettings, @@ -48,6 +49,10 @@ export class TesterService extends ServiceHelpers { return this.http.post(`${this.url}gotify`, JSON.stringify(settings), { headers: this.headers }); } + public webhookTest(settings: IWebhookNotificationSettings): Observable { + return this.http.post(`${this.url}webhook`, JSON.stringify(settings), { headers: this.headers }); + } + public mattermostTest(settings: IMattermostNotifcationSettings): Observable { return this.http.post(`${this.url}mattermost`, JSON.stringify(settings), {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/services/settings.service.ts b/src/Ombi/ClientApp/app/services/settings.service.ts index 8c7787b6d..dbf0d129c 100644 --- a/src/Ombi/ClientApp/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/app/services/settings.service.ts @@ -15,6 +15,7 @@ import { IEmailNotificationSettings, IEmbySettings, IGotifyNotificationSettings, + IWebhookNotificationSettings, IIssueSettings, IJobSettings, IJobSettingsViewModel, @@ -192,6 +193,14 @@ export class SettingsService extends ServiceHelpers { .post(`${this.url}/notifications/gotify`, JSON.stringify(settings), { headers: this.headers }); } + public getWebhookNotificationSettings(): Observable { + return this.http.get(`${this.url}/notifications/webhook`, { headers: this.headers }); + } + public saveWebhookNotificationSettings(settings: IWebhookNotificationSettings): Observable { + return this.http + .post(`${this.url}/notifications/webhook`, JSON.stringify(settings), { headers: this.headers }); + } + public getSlackNotificationSettings(): Observable { return this.http.get(`${this.url}/notifications/slack`, {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/settings/notifications/webhook.component.html b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.html new file mode 100644 index 000000000..6773886f8 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.html @@ -0,0 +1,47 @@ + + +
+
+ Webhook Notifications +
+
+ +
+
+ + +
+
+ +
+ + + + The Webhook URL is required +
+ +
+ + + + The Application Token is required +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts new file mode 100644 index 000000000..c72c59ed8 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; + +import { IWebhookNotificationSettings, INotificationTemplates, NotificationType } from "../../interfaces"; +import { TesterService } from "../../services"; +import { NotificationService } from "../../services"; +import { SettingsService } from "../../services"; + +@Component({ + templateUrl: "./webhook.component.html", +}) +export class WebhookComponent implements OnInit { + public NotificationType = NotificationType; + public templates: INotificationTemplates[]; + public form: FormGroup; + + constructor(private settingsService: SettingsService, + private notificationService: NotificationService, + private fb: FormBuilder, + private testerService: TesterService) { } + + public ngOnInit() { + this.settingsService.getWebhookNotificationSettings().subscribe(x => { + this.form = this.fb.group({ + enabled: [x.enabled], + webhookUrl: [x.webhookUrl, [Validators.required]], + applicationToken: [x.applicationToken], + }); + }); + } + + public onSubmit(form: FormGroup) { + if (form.invalid) { + this.notificationService.error("Please check your entered values"); + return; + } + + const settings = form.value; + + this.settingsService.saveWebhookNotificationSettings(settings).subscribe(x => { + if (x) { + this.notificationService.success("Successfully saved the Webhook settings"); + } else { + this.notificationService.success("There was an error when saving the Webhook settings"); + } + }); + + } + + public test(form: FormGroup) { + if (form.invalid) { + this.notificationService.error("Please check your entered values"); + return; + } + + this.testerService.webhookTest(form.value).subscribe(x => { + if (x) { + this.notificationService.success("Successfully sent a Webhook message"); + } else { + this.notificationService.error("There was an error when sending the Webhook message. Please check your settings"); + } + }); + } +} diff --git a/src/Ombi/ClientApp/app/settings/settings.module.ts b/src/Ombi/ClientApp/app/settings/settings.module.ts index 377756e56..6fe8c7b30 100644 --- a/src/Ombi/ClientApp/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/app/settings/settings.module.ts @@ -29,6 +29,7 @@ import { MassEmailComponent } from "./massemail/massemail.component"; import { DiscordComponent } from "./notifications/discord.component"; import { EmailNotificationComponent } from "./notifications/emailnotification.component"; import { GotifyComponent } from "./notifications/gotify.component"; +import { WebhookComponent } from "./notifications/webhook.component"; import { MattermostComponent } from "./notifications/mattermost.component"; import { MobileComponent } from "./notifications/mobile.component"; import { NewsletterComponent } from "./notifications/newsletter.component"; @@ -67,6 +68,7 @@ const routes: Routes = [ { path: "Pushover", component: PushoverComponent, canActivate: [AuthGuard] }, { path: "Pushbullet", component: PushbulletComponent, canActivate: [AuthGuard] }, { path: "Gotify", component: GotifyComponent, canActivate: [AuthGuard] }, + { path: "Webhook", component: WebhookComponent, canActivate: [AuthGuard] }, { path: "Mattermost", component: MattermostComponent, canActivate: [AuthGuard] }, { path: "UserManagement", component: UserManagementComponent, canActivate: [AuthGuard] }, { path: "Update", component: UpdateComponent, canActivate: [AuthGuard] }, @@ -124,6 +126,7 @@ const routes: Routes = [ MattermostComponent, PushbulletComponent, GotifyComponent, + WebhookComponent, UserManagementComponent, UpdateComponent, AboutComponent, diff --git a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html index 686f4c020..eb0f3f4bb 100644 --- a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html @@ -76,6 +76,7 @@
  • Mattermost
  • Telegram
  • Gotify
  • +
  • Webhook
  • From 78ac3984cb2c812a872521986b02c9a370609017 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sat, 30 Nov 2019 18:13:42 +0100 Subject: [PATCH 11/32] Fix lint issues on UI --- src/Ombi/ClientApp/app/services/applications/tester.service.ts | 2 +- src/Ombi/ClientApp/app/services/settings.service.ts | 2 +- .../ClientApp/app/settings/notifications/webhook.component.ts | 2 +- src/Ombi/ClientApp/app/settings/settings.module.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ombi/ClientApp/app/services/applications/tester.service.ts b/src/Ombi/ClientApp/app/services/applications/tester.service.ts index bf6801e66..5b7a4cd63 100644 --- a/src/Ombi/ClientApp/app/services/applications/tester.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/tester.service.ts @@ -12,7 +12,6 @@ import { IEmailNotificationSettings, IEmbyServer, IGotifyNotificationSettings, - IWebhookNotificationSettings, ILidarrSettings, IMattermostNotifcationSettings, IMobileNotificationTestSettings, @@ -25,6 +24,7 @@ import { ISlackNotificationSettings, ISonarrSettings, ITelegramNotifcationSettings, + IWebhookNotificationSettings, } from "../../interfaces"; @Injectable() diff --git a/src/Ombi/ClientApp/app/services/settings.service.ts b/src/Ombi/ClientApp/app/services/settings.service.ts index dbf0d129c..17ba342aa 100644 --- a/src/Ombi/ClientApp/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/app/services/settings.service.ts @@ -15,7 +15,6 @@ import { IEmailNotificationSettings, IEmbySettings, IGotifyNotificationSettings, - IWebhookNotificationSettings, IIssueSettings, IJobSettings, IJobSettingsViewModel, @@ -37,6 +36,7 @@ import { IUpdateSettings, IUserManagementSettings, IVoteSettings, + IWebhookNotificationSettings, } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; diff --git a/src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts index c72c59ed8..d410b2b44 100644 --- a/src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts +++ b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder, FormGroup, Validators } from "@angular/forms"; -import { IWebhookNotificationSettings, INotificationTemplates, NotificationType } from "../../interfaces"; +import { INotificationTemplates, IWebhookNotificationSettings, NotificationType } from "../../interfaces"; import { TesterService } from "../../services"; import { NotificationService } from "../../services"; import { SettingsService } from "../../services"; diff --git a/src/Ombi/ClientApp/app/settings/settings.module.ts b/src/Ombi/ClientApp/app/settings/settings.module.ts index 6fe8c7b30..3f0979ea0 100644 --- a/src/Ombi/ClientApp/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/app/settings/settings.module.ts @@ -29,7 +29,6 @@ import { MassEmailComponent } from "./massemail/massemail.component"; import { DiscordComponent } from "./notifications/discord.component"; import { EmailNotificationComponent } from "./notifications/emailnotification.component"; import { GotifyComponent } from "./notifications/gotify.component"; -import { WebhookComponent } from "./notifications/webhook.component"; import { MattermostComponent } from "./notifications/mattermost.component"; import { MobileComponent } from "./notifications/mobile.component"; import { NewsletterComponent } from "./notifications/newsletter.component"; @@ -38,6 +37,7 @@ import { PushbulletComponent } from "./notifications/pushbullet.component"; import { PushoverComponent } from "./notifications/pushover.component"; import { SlackComponent } from "./notifications/slack.component"; import { TelegramComponent } from "./notifications/telegram.component"; +import { WebhookComponent } from "./notifications/webhook.component"; import { OmbiComponent } from "./ombi/ombi.component"; import { PlexComponent } from "./plex/plex.component"; import { RadarrComponent } from "./radarr/radarr.component"; From 8ae98c91c1d944b01de1259455ff8c2372b67739 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sat, 30 Nov 2019 18:18:33 +0100 Subject: [PATCH 12/32] Minor updates related to Github's comments * Access-Token Header is not sent if it has not been configured (or left blank) * Access token is now also sent in URI string with "token" as key * NotificationType is manually added to the notification data (original data is cloned before to avoid conflict in case of re-usage by other notification handlers) --- src/Ombi.Api.Webhook/IWebhookApi.cs | 2 +- src/Ombi.Api.Webhook/WebhookApi.cs | 11 ++++++++--- src/Ombi.Notifications/Agents/WebhookNotification.cs | 5 ++++- src/Ombi.Notifications/Models/NotificationMessage.cs | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Ombi.Api.Webhook/IWebhookApi.cs b/src/Ombi.Api.Webhook/IWebhookApi.cs index 755c7c789..6a22a8b02 100644 --- a/src/Ombi.Api.Webhook/IWebhookApi.cs +++ b/src/Ombi.Api.Webhook/IWebhookApi.cs @@ -5,6 +5,6 @@ namespace Ombi.Api.Webhook { public interface IWebhookApi { - Task PushAsync(string endpoint, string accessToken, IReadOnlyDictionary parameters); + Task PushAsync(string endpoint, string accessToken, IDictionary parameters); } } \ No newline at end of file diff --git a/src/Ombi.Api.Webhook/WebhookApi.cs b/src/Ombi.Api.Webhook/WebhookApi.cs index 509de2622..b794c3d2f 100644 --- a/src/Ombi.Api.Webhook/WebhookApi.cs +++ b/src/Ombi.Api.Webhook/WebhookApi.cs @@ -17,17 +17,22 @@ namespace Ombi.Api.Webhook private readonly IApi _api; - public async Task PushAsync(string baseUrl, string accessToken, IReadOnlyDictionary parameters) + public async Task PushAsync(string baseUrl, string accessToken, IDictionary parameters) { var request = new Request("/", baseUrl, HttpMethod.Post); - request.AddHeader("Access-Token", accessToken); - request.ApplicationJsonContentType(); + + if (!string.IsNullOrWhiteSpace(accessToken)) + { + request.AddQueryString("token", accessToken); + request.AddHeader("Access-Token", accessToken); + } var body = parameters.ToDictionary( x => _nameResolver.GetResolvedPropertyName(x.Key), x => x.Value ); + request.ApplicationJsonContentType(); request.AddJsonBody(body); await _api.Request(request); diff --git a/src/Ombi.Notifications/Agents/WebhookNotification.cs b/src/Ombi.Notifications/Agents/WebhookNotification.cs index fbbb1d352..05a9c3763 100644 --- a/src/Ombi.Notifications/Agents/WebhookNotification.cs +++ b/src/Ombi.Notifications/Agents/WebhookNotification.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Ombi.Api.Webhook; @@ -105,9 +106,11 @@ namespace Ombi.Notifications.Agents return; } + var notificationData = parsed.Data.ToDictionary(x => x.Key, x => x.Value); + notificationData[nameof(NotificationType)] = type.ToString(); var notification = new NotificationMessage { - Data = parsed.Data, + Data = notificationData, }; await Send(notification, settings); diff --git a/src/Ombi.Notifications/Models/NotificationMessage.cs b/src/Ombi.Notifications/Models/NotificationMessage.cs index d336d830e..7ffd48e63 100644 --- a/src/Ombi.Notifications/Models/NotificationMessage.cs +++ b/src/Ombi.Notifications/Models/NotificationMessage.cs @@ -9,6 +9,6 @@ namespace Ombi.Notifications.Models public string To { get; set; } public Dictionary Other { get; set; } = new Dictionary(); - public IReadOnlyDictionary Data { get; set; } + public IDictionary Data { get; set; } } } \ No newline at end of file From 1176be7f20f92e67fd8ee9b5f7e81b34f882e1bc Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sat, 30 Nov 2019 19:06:55 +0100 Subject: [PATCH 13/32] Add explanation tooltip on webhook settings view Also remove token from URI string: should be added by user in settings view --- src/Ombi.Api.Webhook/WebhookApi.cs | 1 - .../app/settings/notifications/webhook.component.html | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Api.Webhook/WebhookApi.cs b/src/Ombi.Api.Webhook/WebhookApi.cs index b794c3d2f..8b6b35ca0 100644 --- a/src/Ombi.Api.Webhook/WebhookApi.cs +++ b/src/Ombi.Api.Webhook/WebhookApi.cs @@ -23,7 +23,6 @@ namespace Ombi.Api.Webhook if (!string.IsNullOrWhiteSpace(accessToken)) { - request.AddQueryString("token", accessToken); request.AddHeader("Access-Token", accessToken); } diff --git a/src/Ombi/ClientApp/app/settings/notifications/webhook.component.html b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.html index 6773886f8..ed6eb43b9 100644 --- a/src/Ombi/ClientApp/app/settings/notifications/webhook.component.html +++ b/src/Ombi/ClientApp/app/settings/notifications/webhook.component.html @@ -21,7 +21,9 @@
    - + The Application Token is required From 1ac37cd9ecceacaa434f4dfd3ed3f4e410e06c60 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sun, 1 Dec 2019 11:45:42 +0100 Subject: [PATCH 14/32] Improve webhook test to use more accurate payload --- src/Ombi.Notifications/Agents/WebhookNotification.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Notifications/Agents/WebhookNotification.cs b/src/Ombi.Notifications/Agents/WebhookNotification.cs index 05a9c3763..2339b6abf 100644 --- a/src/Ombi.Notifications/Agents/WebhookNotification.cs +++ b/src/Ombi.Notifications/Agents/WebhookNotification.cs @@ -89,11 +89,15 @@ namespace Ombi.Notifications.Agents protected override async Task Test(NotificationOptions model, WebhookSettings settings) { - var message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!"; + var c = new NotificationMessageCurlys(); + + var testData = c.Curlys.ToDictionary(x => x.Key, x => x.Value); + testData[nameof(NotificationType)] = NotificationType.Test.ToString(); var notification = new NotificationMessage { - Message = message, + Data = testData, }; + await Send(notification, settings); } From e672533aaa4c0f51d1789e010c331f9588eb9f9b Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 15 Dec 2019 13:14:48 +0000 Subject: [PATCH 15/32] New translations en.json (Hungarian) --- src/Ombi/wwwroot/translations/hu.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/wwwroot/translations/hu.json b/src/Ombi/wwwroot/translations/hu.json index 1414f0ea0..050c21e64 100644 --- a/src/Ombi/wwwroot/translations/hu.json +++ b/src/Ombi/wwwroot/translations/hu.json @@ -75,7 +75,7 @@ "RequestAdded": "Kérés sikeresen leadva erre: {{title}}", "Similar": "Hasonló", "Refine": "Finomítás", - "SearchBarPlaceholder": "Type Here to Search", + "SearchBarPlaceholder": "A kereséshez írj be valamit", "Movies": { "PopularMovies": "Népszerű filmek", "UpcomingMovies": "Közelgő filmek", From 1069ddf9cf63fce667342441dbdfaa97cadc5a39 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 17 Dec 2019 13:11:45 +0000 Subject: [PATCH 16/32] Fixed the multiple notifications (I think) --- .../Jobs/Plex/Models/AvailabilityModel.cs | 10 +++++ .../Jobs/Plex/PlexAvailabilityChecker.cs | 44 ++++++++++++++----- src/Ombi.Store/Context/IOmbiContext.cs | 2 + src/Ombi.Store/Context/OmbiContext.cs | 2 + .../Requests/IMovieRequestRepository.cs | 1 + .../Requests/ITvRequestRepository.cs | 2 + .../Requests/MovieRequestRepository.cs | 8 ++++ .../Requests/TvRequestRepository.cs | 24 ++++++++-- 8 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 src/Ombi.Schedule/Jobs/Plex/Models/AvailabilityModel.cs diff --git a/src/Ombi.Schedule/Jobs/Plex/Models/AvailabilityModel.cs b/src/Ombi.Schedule/Jobs/Plex/Models/AvailabilityModel.cs new file mode 100644 index 000000000..44d018404 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Plex/Models/AvailabilityModel.cs @@ -0,0 +1,10 @@ +using Ombi.Store.Entities; + +namespace Ombi.Schedule.Jobs.Plex.Models +{ + public class AvailabilityModel + { + public int Id { get; set; } + public string RequestedUser { get; set; } + } +} diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index 72d7f5a2c..6b556bc0e 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; using Ombi.Notifications.Models; +using Ombi.Schedule.Jobs.Plex.Models; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; @@ -99,6 +100,7 @@ namespace Ombi.Schedule.Jobs.Plex } + var availableEpisode = new List(); foreach (var season in child.SeasonRequests) { foreach (var episode in season.Episodes) @@ -113,19 +115,28 @@ namespace Ombi.Schedule.Jobs.Plex if (foundEp != null) { + availableEpisode.Add(new AvailabilityModel + { + Id = episode.Id + }); episode.Available = true; } } } + //TODO Partial avilability notifications here + 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)); if (allAvailable) { _log.LogInformation("[PAC] - Child request {0} is now available, sending notification", $"{child.Title} - {child.Id}"); // We have ful-fulled this request! - child.Available = true; - child.MarkedAsAvailable = DateTime.Now; + await _tvRepo.MarkChildAsAvailable(child.Id); await _notificationService.Publish(new NotificationOptions { DateTime = DateTime.Now, @@ -144,9 +155,15 @@ namespace Ombi.Schedule.Jobs.Plex { // Get all non available var movies = _movieRepo.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available); + var itemsForAvailbility = new List(); foreach (var movie in movies) { + if (movie.Available) + { + return; + } + PlexServerContent item = null; if (movie.ImdbId.HasValue()) { @@ -164,24 +181,29 @@ namespace Ombi.Schedule.Jobs.Plex // We don't yet have this continue; } - - movie.Available = true; - movie.MarkedAsAvailable = DateTime.Now; - item.RequestId = movie.Id; - + _log.LogInformation("[PAC] - Movie request {0} is now available, sending notification", $"{movie.Title} - {movie.Id}"); + itemsForAvailbility.Add(new AvailabilityModel + { + Id = movie.Id, + RequestedUser = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty + }); + } + + foreach (var i in itemsForAvailbility) + { + await _movieRepo.MarkAsAvailable(i.Id); + await _notificationService.Publish(new NotificationOptions { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, - RequestId = movie.Id, + RequestId = i.Id, RequestType = RequestType.Movie, - Recipient = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty + Recipient = i.RequestedUser }); - } - await _movieRepo.Save(); await _repo.SaveChangesAsync(); } diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index a7fb3b47d..b93c16be9 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Infrastructure; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository.Requests; namespace Ombi.Store.Context { @@ -27,6 +28,7 @@ namespace Ombi.Store.Context DbSet AlbumRequests { get; set; } DbSet TvRequests { get; set; } DbSet ChildRequests { get; set; } + DbSet EpisodeRequests { get; set; } DbSet Issues { get; set; } DbSet IssueCategories { get; set; } DbSet Tokens { get; set; } diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 28b27107e..f7ce09aa4 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Ombi.Helpers; using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository.Requests; namespace Ombi.Store.Context { @@ -38,6 +39,7 @@ namespace Ombi.Store.Context public DbSet AlbumRequests { get; set; } public DbSet TvRequests { get; set; } public DbSet ChildRequests { get; set; } + public DbSet EpisodeRequests { get; set; } public DbSet Issues { get; set; } public DbSet IssueCategories { get; set; } diff --git a/src/Ombi.Store/Repository/Requests/IMovieRequestRepository.cs b/src/Ombi.Store/Repository/Requests/IMovieRequestRepository.cs index 275937360..5c44b2d6c 100644 --- a/src/Ombi.Store/Repository/Requests/IMovieRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/IMovieRequestRepository.cs @@ -10,6 +10,7 @@ namespace Ombi.Store.Repository.Requests MovieRequests GetRequest(int theMovieDbId); Task Update(MovieRequests request); Task Save(); + Task MarkAsAvailable(int id); IQueryable GetWithUser(); IQueryable GetWithUser(string userId); IQueryable GetAll(string userId); diff --git a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs index f08f7812f..10c7ace68 100644 --- a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs @@ -23,6 +23,8 @@ namespace Ombi.Store.Repository.Requests Task UpdateChild(ChildRequests request); IQueryable GetChild(); IQueryable GetChild(string userId); + Task MarkEpisodeAsAvailable(int id); + Task MarkChildAsAvailable(int id); Task Save(); Task DeleteChildRange(IEnumerable request); } diff --git a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs index 2cea81200..0b826c1fc 100644 --- a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs @@ -54,6 +54,14 @@ namespace Ombi.Store.Repository.Requests .AsQueryable(); } + public async Task MarkAsAvailable(int id) + { + var movieRequest = new MovieRequests{ Id = id, Available = true, MarkedAsAvailable = DateTime.UtcNow}; + var attached = Db.MovieRequests.Attach(movieRequest); + attached.Property(x => x.Available).IsModified = true; + attached.Property(x => x.MarkedAsAvailable).IsModified = true; + await Db.SaveChangesAsync(); + } public IQueryable GetWithUser(string userId) { diff --git a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs index 6528f0969..7cf727414 100644 --- a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -100,6 +101,23 @@ namespace Ombi.Store.Repository.Requests .AsQueryable(); } + public async Task MarkChildAsAvailable(int id) + { + var request = new ChildRequests { Id = id, Available = true, MarkedAsAvailable = DateTime.UtcNow }; + var attached = Db.ChildRequests.Attach(request); + attached.Property(x => x.Available).IsModified = true; + attached.Property(x => x.MarkedAsAvailable).IsModified = true; + await Db.SaveChangesAsync(); + } + + public async Task MarkEpisodeAsAvailable(int id) + { + var request = new EpisodeRequests { Id = id, Available = true }; + var attached = Db.EpisodeRequests.Attach(request); + attached.Property(x => x.Available).IsModified = true; + await Db.SaveChangesAsync(); + } + public async Task Save() { await InternalSaveChanges(); @@ -141,10 +159,10 @@ namespace Ombi.Store.Repository.Requests public async Task Update(TvRequests request) { Db.Update(request); - + await InternalSaveChanges(); } - + public async Task UpdateChild(ChildRequests request) { Db.Update(request); From d08b68ec250bb659fa6525b05e8365231608312a Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 17 Dec 2019 13:14:46 +0000 Subject: [PATCH 17/32] merge --- src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index a1bd29443..e3448635e 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -133,7 +134,7 @@ namespace Ombi.Schedule.Jobs.Plex _log.LogInformation("[PAC] - Child request {0} is now available, sending notification", $"{child.Title} - {child.Id}"); // We have ful-fulled this request! await _tvRepo.MarkChildAsAvailable(child.Id); - await _notificationService.Publish(new NotificationOptions + await _notificationService.Notify(new NotificationOptions { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, @@ -190,7 +191,7 @@ namespace Ombi.Schedule.Jobs.Plex { await _movieRepo.MarkAsAvailable(i.Id); - await _notificationService.Publish(new NotificationOptions + await _notificationService.Notify(new NotificationOptions { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, From e883fe7030f32671993a230672e6e3fd19aa20b6 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 19 Dec 2019 22:31:38 +0000 Subject: [PATCH 18/32] Fix #3315 --- src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index e3448635e..827a0bfba 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -49,13 +49,13 @@ namespace Ombi.Schedule.Jobs.Plex private Task ProcessTv() { - var tv = _tvRepo.GetChild().Where(x => !x.Available); + var tv = _tvRepo.GetChild().Where(x => !x.Available).AsNoTracking(); return ProcessTv(tv); } private async Task ProcessTv(IQueryable tv) { - var plexEpisodes = _repo.GetAllEpisodes().Include(x => x.Series); + var plexEpisodes = _repo.GetAllEpisodes().Include(x => x.Series).AsNoTracking(); foreach (var child in tv) { @@ -151,7 +151,7 @@ namespace Ombi.Schedule.Jobs.Plex private async Task ProcessMovies() { // Get all non available - var movies = _movieRepo.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available); + var movies = _movieRepo.GetAll().Include(x => x.RequestedUser).Where(x => !x.Available).AsNoTracking(); var itemsForAvailbility = new List(); foreach (var movie in movies) From aeccd35f6dfae9c38de09cde98d82dfdbf0a62fa Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 26 Dec 2019 11:41:22 +0000 Subject: [PATCH 19/32] New translations en.json (Russian) --- src/Ombi/wwwroot/translations/ru.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/wwwroot/translations/ru.json b/src/Ombi/wwwroot/translations/ru.json index 092689584..1657fdcff 100644 --- a/src/Ombi/wwwroot/translations/ru.json +++ b/src/Ombi/wwwroot/translations/ru.json @@ -3,7 +3,7 @@ "SignInButton": "Войти", "UsernamePlaceholder": "Имя пользователя", "PasswordPlaceholder": "Пароль", - "RememberMe": "Запомнить Меня", + "RememberMe": "Запомнить меня", "ForgottenPassword": "Забыли пароль?", "Errors": { "IncorrectCredentials": "Неверное имя пользователя или пароль" From f73d2fa52bd35cdbadae0cd0fa0647da5ba23677 Mon Sep 17 00:00:00 2001 From: zobe123 Date: Mon, 30 Dec 2019 10:08:15 +0100 Subject: [PATCH 20/32] Update README.md Update integration section --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e8e269e7f..7c75a12e8 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,13 @@ Here are some of the features Ombi V3 has: ### Integration We integrate with the following applications: * Plex Media Server -* Emby +* Emby/Jellyfin Media Server * Sonarr * Radarr * Lidarr * DogNzb * Couch Potato +* SickRage/SickChill ### Notifications From 8eb29ab9054ef8eb1c770a1efe39acb0ae341899 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sat, 11 Jan 2020 22:36:30 +0100 Subject: [PATCH 21/32] Add RequestId variable in notification's data --- src/Ombi.Notifications/NotificationMessageCurlys.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index cedda3735..c0ad1b49d 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -18,6 +18,8 @@ namespace Ombi.Notifications { LoadIssues(opts); + RequestId = req?.Id.ToString(); + string title; if (req == null) { @@ -68,6 +70,8 @@ namespace Ombi.Notifications { LoadIssues(opts); + RequestId = req?.Id.ToString(); + string title; if (req == null) { @@ -114,6 +118,9 @@ namespace Ombi.Notifications public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref) { LoadIssues(opts); + + RequestId = req?.Id.ToString(); + string title; if (req == null) { @@ -216,6 +223,7 @@ namespace Ombi.Notifications } // User Defined + public string RequestId { get; set; } public string RequestedUser { get; set; } public string UserName { get; set; } public string IssueUser => UserName; @@ -248,6 +256,7 @@ namespace Ombi.Notifications public Dictionary Curlys => new Dictionary { + {nameof(RequestId), RequestId }, {nameof(RequestedUser), RequestedUser }, {nameof(Title), Title }, {nameof(RequestedDate), RequestedDate }, From 56948035f93ae4f54a851da9b1658dd1fd8a139e Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sun, 12 Jan 2020 00:31:11 +0100 Subject: [PATCH 22/32] Call abstract ProcessResult from generic one while searching TV This especially allows to execute search rules, and therefor to fill properties like requested, approved and available statuses --- .../Engine/Demo/DemoTvSearchEngine.cs | 4 ++-- src/Ombi.Core/Engine/TvSearchEngine.cs | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs index edf9c430d..6c3a78a15 100644 --- a/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs +++ b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs @@ -56,7 +56,7 @@ namespace Ombi.Core.Engine.Demo { continue; } - retVal.Add(ProcessResult(tvMazeSearch)); + retVal.Add(await ProcessResult(tvMazeSearch)); } return retVal; } @@ -78,7 +78,7 @@ namespace Ombi.Core.Engine.Demo } var movieResult = await TvMazeApi.ShowLookup(tv); - responses.Add(ProcessResult(movieResult)); + responses.Add(await ProcessResult(movieResult)); } return responses; diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs index 3a1fead18..0b27da5c1 100644 --- a/src/Ombi.Core/Engine/TvSearchEngine.cs +++ b/src/Ombi.Core/Engine/TvSearchEngine.cs @@ -61,7 +61,7 @@ namespace Ombi.Core.Engine { continue; } - retVal.Add(ProcessResult(tvMazeSearch)); + retVal.Add(await ProcessResult(tvMazeSearch)); } return retVal; } @@ -123,7 +123,7 @@ namespace Ombi.Core.Engine public async Task> Popular() { var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12)); - var processed = ProcessResults(result); + var processed = await ProcessResults(result); return processed; } @@ -131,37 +131,38 @@ namespace Ombi.Core.Engine { var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12)); - var processed = ProcessResults(result); + var processed = await ProcessResults(result); return processed; } public async Task> MostWatches() { var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12)); - var processed = ProcessResults(result); + var processed = await ProcessResults(result); return processed; } public async Task> Trending() { var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12)); - var processed = ProcessResults(result); + var processed = await ProcessResults(result); return processed; } - protected IEnumerable ProcessResults(IEnumerable items) + protected async Task> ProcessResults(IEnumerable items) { var retVal = new List(); foreach (var tvMazeSearch in items) { - retVal.Add(ProcessResult(tvMazeSearch)); + retVal.Add(await ProcessResult(tvMazeSearch)); } return retVal; } - protected SearchTvShowViewModel ProcessResult(T tvMazeSearch) + protected async Task ProcessResult(T tvMazeSearch) { - return Mapper.Map(tvMazeSearch); + var viewTv = Mapper.Map(tvMazeSearch); + return await ProcessResult(viewTv); } private async Task ProcessResult(SearchTvShowViewModel item) From f8fcc9bea95d0e3fb4185954c1aa741e987ed239 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sun, 12 Jan 2020 00:31:33 +0100 Subject: [PATCH 23/32] Add Denied and DeniedReason on the SearchViewModel --- src/Ombi.Core/Models/Search/SearchViewModel.cs | 2 ++ src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/Ombi.Core/Models/Search/SearchViewModel.cs b/src/Ombi.Core/Models/Search/SearchViewModel.cs index a388ccfff..a96700553 100644 --- a/src/Ombi.Core/Models/Search/SearchViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchViewModel.cs @@ -7,6 +7,8 @@ namespace Ombi.Core.Models.Search { public int Id { get; set; } public bool Approved { get; set; } + public bool? Denied { get; set; } + public string DeniedReason { get; set; } public bool Requested { get; set; } public int RequestId { get; set; } public bool Available { get; set; } diff --git a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs index 2d4482ba9..37d9fddd7 100644 --- a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs @@ -34,6 +34,8 @@ namespace Ombi.Core.Rule.Rules.Search obj.Requested = true; obj.RequestId = movieRequests.Id; obj.Approved = movieRequests.Approved; + obj.Denied = movieRequests.Denied ?? false; + obj.DeniedReason = movieRequests.DeniedReason; obj.Available = movieRequests.Available; return Success(); @@ -60,6 +62,8 @@ namespace Ombi.Core.Rule.Rules.Search request.Requested = true; request.Approved = tvRequests.ChildRequests.Any(x => x.Approved); + request.Denied = tvRequests.ChildRequests.Any(x => x.Denied ?? false); + request.DeniedReason = tvRequests.ChildRequests.FirstOrDefault(x => x.DeniedReason != null)?.DeniedReason; // Let's modify the seasonsrequested to reflect what we have requested... foreach (var season in request.SeasonRequests) @@ -108,6 +112,8 @@ namespace Ombi.Core.Rule.Rules.Search obj.Requested = true; obj.RequestId = albumRequest.Id; obj.Approved = albumRequest.Approved; + obj.Denied = albumRequest.Denied; + obj.DeniedReason = albumRequest.DeniedReason; obj.Available = albumRequest.Available; return Success(); From 230c22abe1f90ef5d5273b1a42c296e9999a8822 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Mon, 20 Jan 2020 16:13:01 +0100 Subject: [PATCH 24/32] During a TvShow request, notify using the ParentRequest Id if available --- src/Ombi.Notifications/NotificationMessageCurlys.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index c0ad1b49d..4efc948a5 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -119,7 +119,7 @@ namespace Ombi.Notifications { LoadIssues(opts); - RequestId = req?.Id.ToString(); + RequestId = req?.ParentRequestId.ToString(); string title; if (req == null) From 373d28361e30af6995fc07095fa6809392478da5 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 23 Jan 2020 21:47:27 +0000 Subject: [PATCH 25/32] Fixed #3348 --- src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs index 2ba054aeb..f7d5fa941 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs @@ -55,7 +55,7 @@ namespace Ombi.Schedule.Jobs.Plex _log.LogError(LoggingEvents.Cacher, e, "Caching Episodes Failed"); } - //await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System"); + await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System"); await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex"); } @@ -217,4 +217,4 @@ namespace Ombi.Schedule.Jobs.Plex GC.SuppressFinalize(this); } } -} \ No newline at end of file +} From 7d53050fbaeb1684ba8f24b99f8b036664f90fb6 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 25 Jan 2020 23:46:13 +0000 Subject: [PATCH 26/32] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7c75a12e8..b33146f90 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ We also now have merch up on Teespring! ___ -| Service | Stable | Develop | -|----------|:---------------------------:|:----------------------------:| -| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/master?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/develop?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) | -| Download |[![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/Ombi/releases) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | +| Service | Stable | Develop | V4 | +|----------|:---------------------------:|:----------------------------:|:----------------------------:| +| Build Status | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/master?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/develop?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) | [![Build Status](https://dev.azure.com/tidusjar/Ombi/_apis/build/status/Ombi%20CI%20Build?branchName=feature%2Fv4)](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=15&branchName=feature%2Fv4) +| Download |[![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/Ombi/releases) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/ombi.releases/releases) | # Features Here are some of the features Ombi V3 has: * Now working without crashes on Linux. From 100c3f813dec88e9277fc3291f8bd634dfd6a383 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Tue, 28 Jan 2020 11:52:15 +0100 Subject: [PATCH 27/32] Remove DeniedReason from top-level TV search model --- src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs index 37d9fddd7..e5c890450 100644 --- a/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/ExistingRule.cs @@ -63,7 +63,6 @@ namespace Ombi.Core.Rule.Rules.Search request.Requested = true; request.Approved = tvRequests.ChildRequests.Any(x => x.Approved); request.Denied = tvRequests.ChildRequests.Any(x => x.Denied ?? false); - request.DeniedReason = tvRequests.ChildRequests.FirstOrDefault(x => x.DeniedReason != null)?.DeniedReason; // Let's modify the seasonsrequested to reflect what we have requested... foreach (var season in request.SeasonRequests) From eb9e77542d334a4492e880ebe214f9e0c310924b Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 22 Feb 2020 22:56:49 +0000 Subject: [PATCH 28/32] Delete test.workflow --- .github/workflows/test.workflow | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .github/workflows/test.workflow diff --git a/.github/workflows/test.workflow b/.github/workflows/test.workflow deleted file mode 100644 index 7c88813d1..000000000 --- a/.github/workflows/test.workflow +++ /dev/null @@ -1,9 +0,0 @@ -workflow "New workflow" { - on = "push" - resolves = [".NET Core CLI"] -} - -action ".NET Core CLI" { - uses = "baruchiro/github-actions@0.0.1" - args = "build src/Ombi.sln" -} From fc108fcba4a55415c541f308bbb3eba84665aa92 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 22 Feb 2020 22:56:54 +0000 Subject: [PATCH 29/32] Delete aspnetcore.yml --- .github/workflows/aspnetcore.yml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .github/workflows/aspnetcore.yml diff --git a/.github/workflows/aspnetcore.yml b/.github/workflows/aspnetcore.yml deleted file mode 100644 index e562216cc..000000000 --- a/.github/workflows/aspnetcore.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: ASP.NET Core CI - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 2.2.108 - - - name: Build Backend - run: ./build.sh --settings_skipverification=true From 76144d3ee22a2fc2f1de7703236099c0ba8f130b Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 13 Mar 2020 09:33:42 +0000 Subject: [PATCH 30/32] Fixed the exception in the newsletter job #3421 --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 32254f09a..6893cb585 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -730,7 +730,7 @@ namespace Ombi.Schedule.Jobs.Ombi finalsb.Append("
    "); } - var summary = info.summary; + var summary = info?.summary ?? string.Empty; if (summary.Length > 280) { summary = summary.Remove(280); @@ -946,4 +946,4 @@ namespace Ombi.Schedule.Jobs.Ombi GC.SuppressFinalize(this); } } -} \ No newline at end of file +} From a110be83fe99cc2c4ead8753ca629b241606b78a Mon Sep 17 00:00:00 2001 From: stcbus Date: Sat, 21 Mar 2020 18:22:31 -0400 Subject: [PATCH 31/32] minor language and grammar changes. --- src/Ombi.Store/Context/OmbiContext.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 4694f1151..c1f2e9251 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -116,7 +116,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello! Your request for {Title} on {ApplicationName}! This is now available! :)", + Message = "Hello! Your request for {Title} on {ApplicationName} is now available.", Subject = "{ApplicationName}: {Title} is now available!", Agent = agent, Enabled = true, @@ -138,7 +138,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello! Your request for {Title} has been declined, Sorry!", + Message = "Hello! Your request for {Title} has been declined.", Subject = "{ApplicationName}: your request has been declined", Agent = agent, Enabled = true, @@ -148,7 +148,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello! The user '{UserName}' has requested {Title} but it could not be added. This has been added into the requests queue and will keep retrying", + Message = "Hello! The user '{UserName}' has requested {Title} but it could not be added. This has been added into the requests queue and it will keep retrying", Subject = "Item Added To Retry Queue", Agent = agent, Enabled = true, @@ -158,7 +158,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello! You have been invited to use {ApplicationName}! You can login here: {ApplicationUrl}", + Message = "Hello! You have been invited to use {ApplicationName}. You can login here: {ApplicationUrl}", Subject = "Invite to {ApplicationName}", Agent = agent, Enabled = true, @@ -168,7 +168,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello {UserName} Your issue for {Title} has now been resolved.", + Message = "Hello {UserName}, your issue for {Title} has now been resolved.", Subject = "{ApplicationName}: Issue has been resolved for {Title}!", Agent = agent, Enabled = true, @@ -179,7 +179,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello, There is a new comment on your issue {IssueSubject}, The comment is: {NewIssueComment}", + Message = "Hello, There is a new comment on your issue {IssueSubject}. The comment is: {NewIssueComment}", Subject = "{ApplicationName}: New comment on issue {IssueSubject}!", Agent = agent, Enabled = true, @@ -191,7 +191,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Here is a list of Movies and TV Shows that have recently been added!", + Message = "Here is a list of Movies and TV Shows that have recently been added:", Subject = "{ApplicationName}: Recently Added Content!", Agent = agent, Enabled = true, @@ -215,4 +215,4 @@ namespace Ombi.Store.Context } } } -} \ No newline at end of file +} From 2c788cc0c3e024baeaff949d8261545a18053507 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 31 Mar 2020 15:56:37 +0100 Subject: [PATCH 32/32] Fixed #3455 --- src/Ombi.Helpers.Tests/EmbyHelperTests.cs | 46 +++++++++++++++++++++++ src/Ombi.Helpers/EmbyHelper.cs | 12 ++++-- 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/Ombi.Helpers.Tests/EmbyHelperTests.cs diff --git a/src/Ombi.Helpers.Tests/EmbyHelperTests.cs b/src/Ombi.Helpers.Tests/EmbyHelperTests.cs new file mode 100644 index 000000000..746bd1d5f --- /dev/null +++ b/src/Ombi.Helpers.Tests/EmbyHelperTests.cs @@ -0,0 +1,46 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ombi.Helpers.Tests +{ + [TestFixture] + public class EmbyHelperTests + { + [TestCaseSource(nameof(UrlData))] + public string TestUrl(string mediaId, string url) + { + return EmbyHelper.GetEmbyMediaUrl(mediaId, url); + } + + [TestCaseSource(nameof(JellyfinUrlData))] + public string TestJellyfinUrl(string mediaId, string url) + { + return EmbyHelper.GetEmbyMediaUrl(mediaId, url, true); + } + + public static IEnumerable UrlData + { + get + { + var mediaId = 1; + yield return new TestCaseData(mediaId.ToString(), "http://google.com").Returns($"http://google.com/#!/item?id={mediaId}").SetName("EmbyHelper_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash"); + yield return new TestCaseData(mediaId.ToString(), "http://google.com/").Returns($"http://google.com/#!/item?id={mediaId}").SetName("EmbyHelper_GetMediaUrl_WithCustomDomain"); + yield return new TestCaseData(mediaId.ToString(), "https://google.com/").Returns($"https://google.com/#!/item?id={mediaId}").SetName("EmbyHelper_GetMediaUrl_WithCustomDomain_Https"); + yield return new TestCaseData(mediaId.ToString(), string.Empty).Returns($"https://app.emby.media/#!/item?id={mediaId}").SetName("EmbyHelper_GetMediaUrl_WithOutCustomDomain"); + } + } + + public static IEnumerable JellyfinUrlData + { + get + { + var mediaId = 1; + yield return new TestCaseData(mediaId.ToString(), "http://google.com").Returns($"http://google.com/#!/itemdetails.html?id={mediaId}").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash"); + yield return new TestCaseData(mediaId.ToString(), "http://google.com/").Returns($"http://google.com/#!/itemdetails.html?id={mediaId}").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain"); + yield return new TestCaseData(mediaId.ToString(), "https://google.com/").Returns($"https://google.com/#!/itemdetails.html?id={mediaId}").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_Https"); + } + } + } +} diff --git a/src/Ombi.Helpers/EmbyHelper.cs b/src/Ombi.Helpers/EmbyHelper.cs index e22bb1d99..61716cd4b 100644 --- a/src/Ombi.Helpers/EmbyHelper.cs +++ b/src/Ombi.Helpers/EmbyHelper.cs @@ -4,18 +4,22 @@ { public static string GetEmbyMediaUrl(string mediaId, string customerServerUrl = null, bool isJellyfin = false) { - string path = "item/item"; + string path = "item"; if (isJellyfin) { - path = "itemdetails"; + path = "itemdetails.html"; } if (customerServerUrl.HasValue()) { - return $"{customerServerUrl}#!/{path}.html?id={mediaId}"; + if (!customerServerUrl.EndsWith("/")) + { + return $"{customerServerUrl}/#!/{path}?id={mediaId}"; + } + return $"{customerServerUrl}#!/{path}?id={mediaId}"; } else { - return $"https://app.emby.media/#!/{path}.html?id={mediaId}"; + return $"https://app.emby.media/#!/{path}?id={mediaId}"; } } }