Add Notifiarr support (#52)

This commit is contained in:
Marius Nechifor
2025-02-02 20:45:50 +02:00
parent 1713d0fd1e
commit 19b3675701
46 changed files with 834 additions and 25 deletions
@@ -0,0 +1,6 @@
namespace Infrastructure.Verticals.Notifications.Notifiarr;
public interface INotifiarrProxy
{
Task SendNotification(NotifiarrPayload payload, NotifiarrConfig config);
}
@@ -0,0 +1,30 @@
using Common.Configuration.Notification;
using Microsoft.Extensions.Configuration;
namespace Infrastructure.Verticals.Notifications.Notifiarr;
public sealed record NotifiarrConfig : NotificationConfig
{
public const string SectionName = "Notifiarr";
[ConfigurationKeyName("API_KEY")]
public string? ApiKey { get; init; }
[ConfigurationKeyName("CHANNEL_ID")]
public string? ChannelId { get; init; }
public override bool IsValid()
{
if (string.IsNullOrEmpty(ApiKey?.Trim()))
{
return false;
}
if (string.IsNullOrEmpty(ChannelId?.Trim()))
{
return false;
}
return true;
}
}
@@ -0,0 +1,12 @@
namespace Infrastructure.Verticals.Notifications.Notifiarr;
public class NotifiarrException : Exception
{
public NotifiarrException(string message) : base(message)
{
}
public NotifiarrException(string message, Exception innerException) : base(message, innerException)
{
}
}
@@ -0,0 +1,57 @@
namespace Infrastructure.Verticals.Notifications.Notifiarr;
public class NotifiarrPayload
{
public NotifiarrNotification Notification { get; set; } = new NotifiarrNotification();
public Discord Discord { get; set; }
}
public class NotifiarrNotification
{
public bool Update { get; set; }
public string Name => "Cleanuperr";
public int? Event { get; set; }
}
public class Discord
{
public string Color { get; set; } = string.Empty;
public Ping Ping { get; set; }
public Images Images { get; set; }
public Text Text { get; set; }
public Ids Ids { get; set; }
}
public class Ping
{
public string PingUser { get; set; }
public string PingRole { get; set; }
}
public class Images
{
public Uri? Thumbnail { get; set; }
public Uri? Image { get; set; }
}
public class Text
{
public string Title { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public List<Field> Fields { get; set; } = new List<Field>();
public string Footer { get; set; } = string.Empty;
}
public class Field
{
public string Title { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
public bool Inline { get; set; }
}
public class Ids
{
public string Channel { get; set; }
}
@@ -0,0 +1,75 @@
using Domain.Enums;
using Infrastructure.Verticals.Notifications.Models;
using Mapster;
using Microsoft.Extensions.Options;
namespace Infrastructure.Verticals.Notifications.Notifiarr;
public class NotifiarrProvider : NotificationProvider
{
private readonly NotifiarrConfig _config;
private readonly INotifiarrProxy _proxy;
private const string WarningColor = "f0ad4e";
private const string ImportantColor = "bb2124";
public NotifiarrProvider(IOptions<NotifiarrConfig> config, INotifiarrProxy proxy)
: base(config)
{
_config = config.Value;
_proxy = proxy;
}
public override string Name => "Notifiarr";
public override async Task OnFailedImportStrike(FailedImportStrikeNotification notification)
{
await _proxy.SendNotification(BuildPayload(notification, WarningColor), _config);
}
public override async Task OnStalledStrike(StalledStrikeNotification notification)
{
await _proxy.SendNotification(BuildPayload(notification, WarningColor), _config);
}
public override async Task OnQueueItemDelete(QueueItemDeleteNotification notification)
{
await _proxy.SendNotification(BuildPayload(notification, ImportantColor), _config);
}
private NotifiarrPayload BuildPayload(Notification notification, string color)
{
NotifiarrPayload payload = new()
{
Discord = new()
{
Color = color,
Text = new()
{
Title = notification.Title,
Icon = "https://github.com/flmorg/cleanuperr/blob/main/Logo/48.png?raw=true",
Description = notification.Description,
Fields = new()
{
new() { Title = "Instance type", Text = notification.InstanceType.ToString() },
new() { Title = "Url", Text = notification.InstanceUrl.ToString() },
new() { Title = "Download hash", Text = notification.Hash }
}
},
Ids = new Ids
{
Channel = _config.ChannelId
},
Images = new()
{
Thumbnail = new Uri("https://github.com/flmorg/cleanuperr/blob/main/Logo/48.png?raw=true"),
Image = notification.Image
}
}
};
payload.Discord.Text.Fields.AddRange(notification.Fields?.Adapt<List<Field>>() ?? []);
return payload;
}
}
@@ -0,0 +1,55 @@
using System.Text;
using Common.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Infrastructure.Verticals.Notifications.Notifiarr;
public class NotifiarrProxy : INotifiarrProxy
{
private readonly HttpClient _httpClient;
private const string Url = "https://notifiarr.com/api/v1/notification/passthrough/";
public NotifiarrProxy(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient(Constants.HttpClientWithRetryName);
}
public async Task SendNotification(NotifiarrPayload payload, NotifiarrConfig config)
{
try
{
string content = JsonConvert.SerializeObject(payload, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"{Url}{config.ApiKey}");
request.Method = HttpMethod.Post;
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
using HttpResponseMessage response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException exception)
{
if (exception.StatusCode is null)
{
throw new NotifiarrException("unable to send notification", exception);
}
switch ((int)exception.StatusCode)
{
case 401:
throw new NotifiarrException("unable to send notification | API key is invalid");
case 502:
case 503:
case 504:
throw new NotifiarrException("unable to send notification | service unavailable", exception);
default:
throw new NotifiarrException("unable to send notification", exception);
}
}
}
}