From cdc002ecd73c1ac939c009981b0b96d6ac9dc157 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Fri, 29 Nov 2019 16:05:14 +0100 Subject: [PATCH 01/18] 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 02/18] 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 03/18] 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 04/18] 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 05/18] 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 06/18] 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 07/18] 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 08/18] 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 09/18] 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 10/18] 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 11/18] 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 12/18] 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 8eb29ab9054ef8eb1c770a1efe39acb0ae341899 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Sat, 11 Jan 2020 22:36:30 +0100 Subject: [PATCH 13/18] 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 14/18] 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 15/18] 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 16/18] 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 17/18] 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 100c3f813dec88e9277fc3291f8bd634dfd6a383 Mon Sep 17 00:00:00 2001 From: Namaneo Date: Tue, 28 Jan 2020 11:52:15 +0100 Subject: [PATCH 18/18] 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)