Add VIP expiration notification toggle

This commit is contained in:
Neureka 2026-04-23 12:57:21 -07:00
parent 46ce8e2701
commit 0883715351
No known key found for this signature in database
11 changed files with 109 additions and 7 deletions

View file

@ -59,10 +59,12 @@ class Notification extends Component {
onGrab,
onHealthIssue,
onHealthRestored,
onVipExpiration,
onApplicationUpdate,
supportsOnGrab,
supportsOnHealthIssue,
supportsOnHealthRestored,
supportsOnVipExpiration,
supportsOnApplicationUpdate,
tags,
tagList
@ -102,6 +104,14 @@ class Notification extends Component {
null
}
{
supportsOnVipExpiration && onVipExpiration ?
<Label kind={kinds.SUCCESS}>
{translate('OnVipExpiration')}
</Label> :
null
}
{
supportsOnApplicationUpdate && onApplicationUpdate ?
<Label kind={kinds.SUCCESS}>
@ -111,7 +121,7 @@ class Notification extends Component {
}
{
!onGrab && !onHealthIssue && !onHealthRestored && !onApplicationUpdate ?
!onGrab && !onHealthIssue && !onHealthRestored && !onVipExpiration && !onApplicationUpdate ?
<Label
kind={kinds.DISABLED}
outline={true}
@ -153,10 +163,12 @@ Notification.propTypes = {
onGrab: PropTypes.bool.isRequired,
onHealthIssue: PropTypes.bool.isRequired,
onHealthRestored: PropTypes.bool.isRequired,
onVipExpiration: PropTypes.bool.isRequired,
onApplicationUpdate: PropTypes.bool.isRequired,
supportsOnGrab: PropTypes.bool.isRequired,
supportsOnHealthIssue: PropTypes.bool.isRequired,
supportsOnHealthRestored: PropTypes.bool.isRequired,
supportsOnVipExpiration: PropTypes.bool.isRequired,
supportsOnApplicationUpdate: PropTypes.bool.isRequired,
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,

View file

@ -18,11 +18,13 @@ function NotificationEventItems(props) {
onGrab,
onHealthIssue,
onHealthRestored,
onVipExpiration,
onApplicationUpdate,
supportsOnGrab,
includeManualGrabs,
supportsOnHealthIssue,
supportsOnHealthRestored,
supportsOnVipExpiration,
includeHealthWarnings,
supportsOnApplicationUpdate
} = item;
@ -83,6 +85,17 @@ function NotificationEventItems(props) {
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onVipExpiration"
helpText={translate('OnVipExpirationHelpText')}
isDisabled={!supportsOnVipExpiration.value}
{...onVipExpiration}
onChange={onInputChange}
/>
</div>
{
(onHealthIssue.value || onHealthRestored.value) &&
<div>

View file

@ -16,11 +16,13 @@ interface Notification extends ModelBase {
onGrab: boolean;
onHealthIssue: boolean;
onHealthRestored: boolean;
onVipExpiration: boolean;
includeHealthWarnings: boolean;
onApplicationUpdate: boolean;
supportsOnGrab: boolean;
supportsOnHealthIssue: boolean;
supportsOnHealthRestored: boolean;
supportsOnVipExpiration: boolean;
supportsOnApplicationUpdate: boolean;
fields: Field[];
implementationName: string;

View file

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(044)]
public class vip_expiration_notification : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Notifications").AddColumn("OnVipExpiration").AsBoolean().NotNullable().WithDefaultValue(false);
}
}
}

View file

@ -70,6 +70,7 @@ public static void Map()
.Ignore(i => i.SupportsOnGrab)
.Ignore(i => i.SupportsOnHealthIssue)
.Ignore(i => i.SupportsOnHealthRestored)
.Ignore(i => i.SupportsOnVipExpiration)
.Ignore(i => i.SupportsOnApplicationUpdate);
Mapper.Entity<IndexerProxyDefinition>("IndexerProxies").RegisterModel()

View file

@ -548,6 +548,8 @@
"OnHealthRestored": "On Health Restored",
"OnHealthRestoredHelpText": "On Health Restored",
"OnLatestVersion": "The latest version of {appName} is already installed",
"OnVipExpiration": "On VIP Expiration",
"OnVipExpirationHelpText": "On VIP Expiration",
"Open": "Open",
"OpenBrowserOnStart": "Open browser on start",
"OpenThisModal": "Open This Modal",

View file

@ -6,15 +6,17 @@ public class NotificationDefinition : ProviderDefinition
{
public bool OnHealthIssue { get; set; }
public bool OnHealthRestored { get; set; }
public bool OnVipExpiration { get; set; }
public bool OnApplicationUpdate { get; set; }
public bool OnGrab { get; set; }
public bool SupportsOnGrab { get; set; }
public bool IncludeManualGrabs { get; set; }
public bool SupportsOnHealthIssue { get; set; }
public bool SupportsOnHealthRestored { get; set; }
public bool SupportsOnVipExpiration { get; set; }
public bool IncludeHealthWarnings { get; set; }
public bool SupportsOnApplicationUpdate { get; set; }
public override bool Enable => OnHealthIssue || OnHealthRestored || OnApplicationUpdate || OnGrab;
public override bool Enable => OnHealthIssue || OnHealthRestored || OnVipExpiration || OnApplicationUpdate || OnGrab;
}
}

View file

@ -13,6 +13,7 @@ public interface INotificationFactory : IProviderFactory<INotification, Notifica
List<INotification> OnGrabEnabled(bool filterBlockedNotifications = true);
List<INotification> OnHealthIssueEnabled(bool filterBlockedNotifications = true);
List<INotification> OnHealthRestoredEnabled(bool filterBlockedNotifications = true);
List<INotification> OnVipExpirationEnabled(bool filterBlockedNotifications = true);
List<INotification> OnApplicationUpdateEnabled(bool filterBlockedNotifications = true);
}
@ -63,6 +64,16 @@ public List<INotification> OnHealthRestoredEnabled(bool filterBlockedNotificatio
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnHealthRestored).ToList();
}
public List<INotification> OnVipExpirationEnabled(bool filterBlockedNotifications = true)
{
if (filterBlockedNotifications)
{
return FilterBlockedNotifications(GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnVipExpiration)).ToList();
}
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnVipExpiration).ToList();
}
public List<INotification> OnApplicationUpdateEnabled(bool filterBlockedNotifications = true)
{
if (filterBlockedNotifications)
@ -96,6 +107,7 @@ public override void SetProviderCharacteristics(INotification provider, Notifica
definition.SupportsOnGrab = provider.SupportsOnGrab;
definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue;
definition.SupportsOnHealthRestored = provider.SupportsOnHealthRestored;
definition.SupportsOnVipExpiration = provider.SupportsOnHealthIssue;
definition.SupportsOnApplicationUpdate = provider.SupportsOnApplicationUpdate;
}

View file

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Events;
using NzbDrone.Core.Messaging.Events;
@ -40,6 +42,18 @@ private bool ShouldHandleHealthFailure(HealthCheck.HealthCheck healthCheck, bool
};
}
private bool ShouldHandleVipExpiration(HealthCheck.HealthCheck healthCheck)
{
return IsVipExpirationHealthCheck(healthCheck) &&
ShouldHandleHealthFailure(healthCheck, true);
}
private bool IsVipExpirationHealthCheck(HealthCheck.HealthCheck healthCheck)
{
return healthCheck.Source == typeof(IndexerVIPCheck) ||
healthCheck.Source == typeof(IndexerVIPExpiredCheck);
}
private bool ShouldHandleOnGrab(GrabMessage message, bool includeManual)
{
return message.GrabTrigger switch
@ -81,15 +95,33 @@ public void Handle(HealthCheckFailedEvent message)
return;
}
foreach (var notification in _notificationFactory.OnHealthIssueEnabled())
var notified = new HashSet<int>();
var notifications = _notificationFactory.OnHealthIssueEnabled()
.Concat(_notificationFactory.OnVipExpirationEnabled());
foreach (var notification in notifications)
{
try
{
if (ShouldHandleHealthFailure(message.HealthCheck, ((NotificationDefinition)notification.Definition).IncludeHealthWarnings))
var definition = (NotificationDefinition)notification.Definition;
var shouldHandleHealthIssue = definition.OnHealthIssue &&
ShouldHandleHealthFailure(message.HealthCheck, definition.IncludeHealthWarnings);
var shouldHandleVipExpiration = definition.OnVipExpiration &&
ShouldHandleVipExpiration(message.HealthCheck);
if (!shouldHandleHealthIssue && !shouldHandleVipExpiration)
{
notification.OnHealthIssue(message.HealthCheck);
_notificationStatusService.RecordSuccess(notification.Definition.Id);
continue;
}
if (!notified.Add(definition.Id))
{
continue;
}
notification.OnHealthIssue(message.HealthCheck);
_notificationStatusService.RecordSuccess(notification.Definition.Id);
}
catch (Exception ex)
{

View file

@ -8,11 +8,13 @@ public class NotificationResource : ProviderResource<NotificationResource>
public bool OnGrab { get; set; }
public bool OnHealthIssue { get; set; }
public bool OnHealthRestored { get; set; }
public bool OnVipExpiration { get; set; }
public bool OnApplicationUpdate { get; set; }
public bool SupportsOnGrab { get; set; }
public bool IncludeManualGrabs { get; set; }
public bool SupportsOnHealthIssue { get; set; }
public bool SupportsOnHealthRestored { get; set; }
public bool SupportsOnVipExpiration { get; set; }
public bool IncludeHealthWarnings { get; set; }
public bool SupportsOnApplicationUpdate { get; set; }
public string TestCommand { get; set; }
@ -34,8 +36,10 @@ public override NotificationResource ToResource(NotificationDefinition definitio
resource.IncludeManualGrabs = definition.IncludeManualGrabs;
resource.OnHealthIssue = definition.OnHealthIssue;
resource.OnHealthRestored = definition.OnHealthRestored;
resource.OnVipExpiration = definition.OnVipExpiration;
resource.SupportsOnHealthIssue = definition.SupportsOnHealthIssue;
resource.SupportsOnHealthRestored = definition.SupportsOnHealthRestored;
resource.SupportsOnVipExpiration = definition.SupportsOnVipExpiration;
resource.IncludeHealthWarnings = definition.IncludeHealthWarnings;
resource.OnApplicationUpdate = definition.OnApplicationUpdate;
resource.SupportsOnApplicationUpdate = definition.SupportsOnApplicationUpdate;
@ -57,8 +61,10 @@ public override NotificationDefinition ToModel(NotificationResource resource, No
definition.IncludeManualGrabs = resource.IncludeManualGrabs;
definition.OnHealthIssue = resource.OnHealthIssue;
definition.OnHealthRestored = resource.OnHealthRestored;
definition.OnVipExpiration = resource.OnVipExpiration;
definition.SupportsOnHealthIssue = resource.SupportsOnHealthIssue;
definition.SupportsOnHealthRestored = resource.SupportsOnHealthRestored;
definition.SupportsOnVipExpiration = resource.SupportsOnVipExpiration;
definition.IncludeHealthWarnings = resource.IncludeHealthWarnings;
definition.OnApplicationUpdate = resource.OnApplicationUpdate;
definition.SupportsOnApplicationUpdate = resource.SupportsOnApplicationUpdate;

View file

@ -5683,6 +5683,9 @@
"onHealthRestored": {
"type": "boolean"
},
"onVipExpiration": {
"type": "boolean"
},
"onApplicationUpdate": {
"type": "boolean"
},
@ -5698,6 +5701,9 @@
"supportsOnHealthRestored": {
"type": "boolean"
},
"supportsOnVipExpiration": {
"type": "boolean"
},
"includeHealthWarnings": {
"type": "boolean"
},
@ -6353,4 +6359,4 @@
"apikey": [ ]
}
]
}
}