From 7528882adf71bbb4074877ee352b67cb8ec476ef Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 15 Sep 2024 22:21:25 +0300 Subject: [PATCH] Gotify notification updates New: Option to include links for Gotify notifications New: Include images and links for Android (cherry picked from commit 3c857135c59029635b0972f959f9a8255bcff21f) Closes #10433 Fixes #10410 --- src/NzbDrone.Core/Localization/Core/en.json | 4 + .../Notifications/Gotify/Gotify.cs | 89 +++++++++++++++---- .../Notifications/Gotify/GotifyMessage.cs | 31 +++++++ .../Notifications/Gotify/GotifySettings.cs | 36 ++++++++ .../Notifications/MetadataLinkType.cs | 16 ++++ 5 files changed, 159 insertions(+), 17 deletions(-) create mode 100644 src/NzbDrone.Core/Notifications/MetadataLinkType.cs diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 962800c3e..c144575bb 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -1099,6 +1099,10 @@ "NotificationsGotifySettingsAppToken": "App Token", "NotificationsGotifySettingsAppTokenHelpText": "The Application Token generated by Gotify", "NotificationsGotifySettingsPriorityHelpText": "Priority of the notification", + "NotificationsGotifySettingsMetadataLinks": "Metadata Links", + "NotificationsGotifySettingsMetadataLinksMovieHelpText": "Add a links to movie metadata when sending notifications", + "NotificationsGotifySettingsPreferredMetadataLink": "Preferred Metadata Link", + "NotificationsGotifySettingsPreferredMetadataLinkHelpText": "Metadata link for clients that only support a single link", "NotificationsGotifySettingsServer": "Gotify Server", "NotificationsGotifySettingsServerHelpText": "Gotify server URL, including http(s):// and port if needed", "NotificationsJoinSettingsApiKeyHelpText": "The API Key from your Join account settings (click Join API button).", diff --git a/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs b/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs index 05ce83963..ca889e1c7 100644 --- a/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs +++ b/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using FluentValidation.Results; using NLog; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Localization; using NzbDrone.Core.MediaCover; using NzbDrone.Core.Movies; @@ -12,6 +13,8 @@ namespace NzbDrone.Core.Notifications.Gotify { public class Gotify : NotificationBase { + private const string RadarrImageUrl = "https://raw.githubusercontent.com/Radarr/Radarr/develop/Logo/128.png"; + private readonly IGotifyProxy _proxy; private readonly ILocalizationService _localizationService; private readonly Logger _logger; @@ -83,20 +86,30 @@ namespace NzbDrone.Core.Notifications.Gotify var sb = new StringBuilder(); sb.AppendLine("This is a test message from Radarr"); + var payload = new GotifyMessage + { + Title = title, + Priority = Settings.Priority + }; + if (Settings.IncludeMoviePoster) { isMarkdown = true; - sb.AppendLine("\r![](https://raw.githubusercontent.com/Radarr/Radarr/develop/Logo/128.png)"); + sb.AppendLine($"\r![]({RadarrImageUrl})"); + payload.SetImage(RadarrImageUrl); } - var payload = new GotifyMessage + if (Settings.MetadataLinks.Any()) { - Title = title, - Message = sb.ToString(), - Priority = Settings.Priority - }; + isMarkdown = true; + sb.AppendLine(""); + sb.AppendLine("[Radarr.video](https://radarr.video)"); + payload.SetClickUrl("https://radarr.video"); + } + + payload.Message = sb.ToString(); payload.SetContentType(isMarkdown); _proxy.SendNotification(payload, Settings); @@ -117,24 +130,66 @@ namespace NzbDrone.Core.Notifications.Gotify sb.AppendLine(message); - if (Settings.IncludeMoviePoster && movie != null) + var payload = new GotifyMessage { - var poster = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.RemoteUrl; + Title = title, + Priority = Settings.Priority + }; + + if (movie != null) + { + if (Settings.IncludeMoviePoster) + { + var poster = movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.RemoteUrl; + + if (poster != null) + { + isMarkdown = true; + sb.AppendLine($"\r![]({poster})"); + payload.SetImage(poster); + } + } - if (poster != null) + if (Settings.MetadataLinks.Any()) { isMarkdown = true; - sb.AppendLine($"\r![]({poster})"); + sb.AppendLine(""); + + foreach (var link in Settings.MetadataLinks) + { + var linkType = (MetadataLinkType)link; + var linkText = ""; + var linkUrl = ""; + + if (linkType == MetadataLinkType.Tmdb && movie.TmdbId > 0) + { + linkText = "TMDb"; + linkUrl = $"https://www.themoviedb.org/movie/{movie.TmdbId}"; + } + + if (linkType == MetadataLinkType.Imdb && movie.ImdbId.IsNotNullOrWhiteSpace()) + { + linkText = "IMDb"; + linkUrl = $"https://www.imdb.com/title/{movie.ImdbId}"; + } + + if (linkType == MetadataLinkType.Trakt && movie.TmdbId > 0) + { + linkText = "Trakt"; + linkUrl = $"https://trakt.tv/search/tmdb/{movie.TmdbId}?id_type=movie"; + } + + sb.AppendLine($"[{linkText}]({linkUrl})"); + + if (link == Settings.PreferredMetadataLink) + { + payload.SetClickUrl(linkUrl); + } + } } } - var payload = new GotifyMessage - { - Title = title, - Message = sb.ToString(), - Priority = Settings.Priority - }; - + payload.Message = sb.ToString(); payload.SetContentType(isMarkdown); _proxy.SendNotification(payload, Settings); diff --git a/src/NzbDrone.Core/Notifications/Gotify/GotifyMessage.cs b/src/NzbDrone.Core/Notifications/Gotify/GotifyMessage.cs index 170ce1367..576294946 100644 --- a/src/NzbDrone.Core/Notifications/Gotify/GotifyMessage.cs +++ b/src/NzbDrone.Core/Notifications/Gotify/GotifyMessage.cs @@ -20,12 +20,27 @@ namespace NzbDrone.Core.Notifications.Gotify Extras.ClientDisplay = new GotifyClientDisplay(contentType); } + + public void SetImage(string imageUrl) + { + Extras.ClientNotification ??= new GotifyClientNotification(); + Extras.ClientNotification.BigImageUrl = imageUrl; + } + + public void SetClickUrl(string url) + { + Extras.ClientNotification ??= new GotifyClientNotification(); + Extras.ClientNotification.Click = new GotifyClientNotificationClick(url); + } } public class GotifyExtras { [JsonProperty("client::display")] public GotifyClientDisplay ClientDisplay { get; set; } + + [JsonProperty("client::notification")] + public GotifyClientNotification ClientNotification { get; set; } } public class GotifyClientDisplay @@ -37,4 +52,20 @@ namespace NzbDrone.Core.Notifications.Gotify ContentType = contentType; } } + + public class GotifyClientNotification + { + public string BigImageUrl { get; set; } + public GotifyClientNotificationClick Click { get; set; } + } + + public class GotifyClientNotificationClick + { + public string Url { get; set; } + + public GotifyClientNotificationClick(string url) + { + Url = url; + } + } } diff --git a/src/NzbDrone.Core/Notifications/Gotify/GotifySettings.cs b/src/NzbDrone.Core/Notifications/Gotify/GotifySettings.cs index 0c49e78fe..9920fe9fd 100644 --- a/src/NzbDrone.Core/Notifications/Gotify/GotifySettings.cs +++ b/src/NzbDrone.Core/Notifications/Gotify/GotifySettings.cs @@ -1,4 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Linq; using FluentValidation; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.Validation; @@ -10,6 +14,30 @@ namespace NzbDrone.Core.Notifications.Gotify { RuleFor(c => c.Server).IsValidUrl(); RuleFor(c => c.AppToken).NotEmpty(); + + RuleFor(c => c.MetadataLinks).Custom((links, context) => + { + foreach (var link in links) + { + if (!Enum.IsDefined(typeof(MetadataLinkType), link)) + { + context.AddFailure("MetadataLinks", $"MetadataLink is not valid: {link}"); + } + } + }); + + RuleFor(c => c).Custom((c, context) => + { + if (c.MetadataLinks.Empty()) + { + return; + } + + if (!c.MetadataLinks.Contains(c.PreferredMetadataLink)) + { + context.AddFailure("PreferredMetadataLink", "Must be a selected link"); + } + }); } } @@ -20,6 +48,8 @@ namespace NzbDrone.Core.Notifications.Gotify public GotifySettings() { Priority = 5; + MetadataLinks = Enumerable.Empty(); + PreferredMetadataLink = (int)MetadataLinkType.Tmdb; } [FieldDefinition(0, Label = "NotificationsGotifySettingsServer", HelpText = "NotificationsGotifySettingsServerHelpText")] @@ -34,6 +64,12 @@ namespace NzbDrone.Core.Notifications.Gotify [FieldDefinition(3, Label = "NotificationsGotifySettingIncludeMoviePoster", Type = FieldType.Checkbox, HelpText = "NotificationsGotifySettingIncludeMoviePosterHelpText")] public bool IncludeMoviePoster { get; set; } + [FieldDefinition(4, Label = "NotificationsGotifySettingsMetadataLinks", Type = FieldType.Select, SelectOptions = typeof(MetadataLinkType), HelpText = "NotificationsGotifySettingsMetadataLinksMovieHelpText")] + public IEnumerable MetadataLinks { get; set; } + + [FieldDefinition(5, Label = "NotificationsGotifySettingsPreferredMetadataLink", Type = FieldType.Select, SelectOptions = typeof(MetadataLinkType), HelpText = "NotificationsGotifySettingsPreferredMetadataLinkHelpText")] + public int PreferredMetadataLink { get; set; } + public override NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); diff --git a/src/NzbDrone.Core/Notifications/MetadataLinkType.cs b/src/NzbDrone.Core/Notifications/MetadataLinkType.cs new file mode 100644 index 000000000..12f1afa0c --- /dev/null +++ b/src/NzbDrone.Core/Notifications/MetadataLinkType.cs @@ -0,0 +1,16 @@ +using NzbDrone.Core.Annotations; + +namespace NzbDrone.Core.Notifications +{ + public enum MetadataLinkType + { + [FieldOption(Label = "TMDb")] + Tmdb = 0, + + [FieldOption(Label = "IMDb")] + Imdb = 1, + + [FieldOption(Label = "Trakt")] + Trakt = 2 + } +}