feat: New Verbosity setting for notifications

Fixes #354
pull/360/head
Robert Dailey 3 months ago
parent 89d04c365f
commit 2bde784d62

@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Notifications: New `verbosity` setting for Notifications to control the frequency and content of
notifications sent after sync operations.
## [7.3.0] - 2024-10-28
### Added

@ -37,6 +37,25 @@
}
}
}
},
"verbosity": {
"description": "The verbosity level for notifications",
"type": "string",
"default": "normal",
"oneOf": [
{
"const": "normal",
"description": "Includes errors, warnings, and informational (changes). This is the default."
},
{
"const": "detailed",
"description": "Everything in normal, plus empty messages (no changes)."
},
{
"const": "minimal",
"description": "Only errors and warnings."
}
]
}
}
}

@ -173,6 +173,16 @@ public class CoreAutofacModule : Module
.Keyed<IAppriseNotificationApiService>(AppriseMode.Stateless);
builder.RegisterType<AppriseRequestBuilder>().As<IAppriseRequestBuilder>();
// Verbosity Strategies
builder.RegisterType<MinimalVerbosityStrategy>().Keyed<IVerbosityStrategy>(NotificationVerbosity.Minimal);
builder.RegisterType<NormalVerbosityStrategy>().Keyed<IVerbosityStrategy>(NotificationVerbosity.Normal);
builder.RegisterType<DetailedVerbosityStrategy>().Keyed<IVerbosityStrategy>(NotificationVerbosity.Detailed);
builder.Register(c =>
{
var settings = c.Resolve<ISettings<NotificationSettings>>().Value;
return c.ResolveKeyed<IVerbosityStrategy>(settings.Verbosity);
});
}
private static void RegisterPlatform(ContainerBuilder builder)

@ -4,7 +4,7 @@ using Recyclarr.Notifications.Events;
namespace Recyclarr.Notifications;
public class NotificationEmitter
public class NotificationEmitter(IVerbosityStrategy verbosity)
{
private readonly Subject<IPresentableNotification> _notifications = new();
@ -12,24 +12,36 @@ public class NotificationEmitter
public void SendStatistic(string description)
{
_notifications.OnNext(new InformationEvent(description));
if (verbosity.ShouldSendInformation())
{
_notifications.OnNext(new InformationEvent(description));
}
}
public void SendStatistic<T>(string description, T stat) where T : notnull
{
_notifications.OnNext(new InformationEvent(description)
if (verbosity.ShouldSendInformation())
{
Statistic = stat.ToString() ?? "!STAT ERROR!"
});
_notifications.OnNext(new InformationEvent(description)
{
Statistic = stat.ToString() ?? "!STAT ERROR!"
});
}
}
public void SendError(string message)
{
_notifications.OnNext(new ErrorEvent(message));
if (verbosity.ShouldSendError())
{
_notifications.OnNext(new ErrorEvent(message));
}
}
public void SendWarning(string message)
{
_notifications.OnNext(new WarningEvent(message));
if (verbosity.ShouldSendWarning())
{
_notifications.OnNext(new WarningEvent(message));
}
}
}

@ -14,7 +14,8 @@ public sealed class NotificationService(
ILogger log,
IIndex<AppriseMode, IAppriseNotificationApiService> apiFactory,
ISettings<NotificationSettings> settings,
NotificationEmitter notificationEmitter)
NotificationEmitter notificationEmitter,
IVerbosityStrategy verbosity)
: IDisposable
{
private const string NoInstance = "[no instance]";
@ -65,6 +66,16 @@ public sealed class NotificationService(
private async Task SendAppriseNotification(bool succeeded, string body, AppriseMessageType messageType)
{
if (string.IsNullOrEmpty(body) && !verbosity.ShouldSendEmpty())
{
log.Debug("Skipping notification because the body is empty");
return;
}
// Apprise doesn't like empty bodies, so the hyphens are there in case there are no notifications to render.
// This also doesn't look too bad because it creates some separation between the title and the content.
body = "---\n" + body.Trim();
try
{
var api = apiFactory[_settings!.Mode!.Value];
@ -72,7 +83,7 @@ public sealed class NotificationService(
await api.Notify(_settings!, payload => payload with
{
Title = $"Recyclarr Sync {(succeeded ? "Completed" : "Failed")}",
Body = body.Trim(),
Body = body,
Type = messageType,
Format = AppriseMessageFormat.Markdown
});
@ -85,9 +96,7 @@ public sealed class NotificationService(
private string BuildNotificationBody()
{
// Apprise doesn't like empty bodies, so the hyphens are there in case there are no notifications to render.
// This also doesn't look too bad because it creates some separation between the title and the content.
var body = new StringBuilder("---\n");
var body = new StringBuilder();
foreach (var (instanceName, notifications) in _events)
{

@ -0,0 +1,33 @@
namespace Recyclarr.Notifications;
public interface IVerbosityStrategy
{
bool ShouldSendInformation();
bool ShouldSendError();
bool ShouldSendWarning();
bool ShouldSendEmpty();
}
public class MinimalVerbosityStrategy : IVerbosityStrategy
{
public bool ShouldSendInformation() => false;
public bool ShouldSendError() => true;
public bool ShouldSendWarning() => true;
public bool ShouldSendEmpty() => false;
}
public class NormalVerbosityStrategy : IVerbosityStrategy
{
public bool ShouldSendInformation() => true;
public bool ShouldSendError() => true;
public bool ShouldSendWarning() => true;
public bool ShouldSendEmpty() => false;
}
public class DetailedVerbosityStrategy : IVerbosityStrategy
{
public bool ShouldSendInformation() => true;
public bool ShouldSendError() => true;
public bool ShouldSendWarning() => true;
public bool ShouldSendEmpty() => true;
}

@ -38,9 +38,17 @@ public record RecyclarrSettings
public record NotificationSettings
{
public NotificationVerbosity Verbosity { get; [UsedImplicitly] init; } = NotificationVerbosity.Normal;
public AppriseNotificationSettings? Apprise { get; [UsedImplicitly] init; }
}
public enum NotificationVerbosity
{
Minimal,
Normal,
Detailed
}
public record AppriseNotificationSettings
{
public AppriseMode? Mode { get; [UsedImplicitly] init; }

Loading…
Cancel
Save