mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-12 08:16:05 -07:00
Added Discord notification #865
This commit is contained in:
parent
4c797733ca
commit
43dbe854a6
18 changed files with 336 additions and 26 deletions
33
src/Ombi.Api.Discord/DiscordApi.cs
Normal file
33
src/Ombi.Api.Discord/DiscordApi.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Discord.Models;
|
||||
|
||||
namespace Ombi.Api.Discord
|
||||
{
|
||||
public class DiscordApi : IDiscordApi
|
||||
{
|
||||
public DiscordApi()
|
||||
{
|
||||
Api = new Api();
|
||||
}
|
||||
|
||||
private string Endpoint => "https://discordapp.com/api/"; //webhooks/270828242636636161/lLysOMhJ96AFO1kvev0bSqP-WCZxKUh1UwfubhIcLkpS0DtM3cg4Pgeraw3waoTXbZii
|
||||
private Api Api { get; }
|
||||
|
||||
public async Task SendMessage(string message, string webhookId, string webhookToken, string username = null)
|
||||
{
|
||||
var request = new Request(Endpoint, $"webhooks/{webhookId}/{webhookToken}", HttpMethod.Post);
|
||||
|
||||
var body = new DiscordWebhookBody
|
||||
{
|
||||
content = message,
|
||||
username = username
|
||||
};
|
||||
request.AddJsonBody(body);
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
await Api.Request(request);
|
||||
}
|
||||
}
|
||||
}
|
9
src/Ombi.Api.Discord/IDiscordApi.cs
Normal file
9
src/Ombi.Api.Discord/IDiscordApi.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Discord
|
||||
{
|
||||
public interface IDiscordApi
|
||||
{
|
||||
Task SendMessage(string message, string webhookId, string webhookToken, string username = null);
|
||||
}
|
||||
}
|
8
src/Ombi.Api.Discord/Models/DiscordWebhookBody.cs
Normal file
8
src/Ombi.Api.Discord/Models/DiscordWebhookBody.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Api.Discord.Models
|
||||
{
|
||||
public class DiscordWebhookBody
|
||||
{
|
||||
public string content { get; set; }
|
||||
public string username { get; set; }
|
||||
}
|
||||
}
|
11
src/Ombi.Api.Discord/Ombi.Api.Discord.csproj
Normal file
11
src/Ombi.Api.Discord/Ombi.Api.Discord.csproj
Normal file
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -82,7 +82,7 @@ namespace Ombi.Api.Radarr
|
|||
|
||||
try
|
||||
{
|
||||
var response = await Api.Request(request);
|
||||
var response = await Api.RequestContent(request);
|
||||
if (response.Contains("\"message\":"))
|
||||
{
|
||||
var error = JsonConvert.DeserializeObject<RadarrError>(response);
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace Ombi.Api
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<string> Request(Request request)
|
||||
public async Task<string> RequestContent(Request request)
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
|
@ -93,5 +93,34 @@ namespace Ombi.Api
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Request(Request request)
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri))
|
||||
{
|
||||
// Add the Json Body
|
||||
if (request.JsonBody != null)
|
||||
{
|
||||
httpRequestMessage.Content = new JsonContent(request.JsonBody);
|
||||
}
|
||||
|
||||
// Add headers
|
||||
foreach (var header in request.Headers)
|
||||
{
|
||||
httpRequestMessage.Headers.Add(header.Key, header.Value);
|
||||
|
||||
}
|
||||
using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage))
|
||||
{
|
||||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
// Logging
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Ombi.Api
|
|||
public List<KeyValuePair<string, string>> Headers { get; } = new List<KeyValuePair<string, string>>();
|
||||
public List<KeyValuePair<string, string>> ContentHeaders { get; } = new List<KeyValuePair<string, string>>();
|
||||
|
||||
public object JsonBody { get; set; }
|
||||
public object JsonBody { get; private set; }
|
||||
|
||||
public bool IsValidUrl
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using Ombi.Api.Discord;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Radarr;
|
||||
|
@ -60,6 +62,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ITvMazeApi, TvMazeApi>();
|
||||
services.AddTransient<ITraktApi, TraktApi>();
|
||||
services.AddTransient<IRadarrApi, RadarrApi>();
|
||||
services.AddTransient<IDiscordApi, DiscordApi>();
|
||||
}
|
||||
|
||||
public static void RegisterStore(this IServiceCollection services)
|
||||
|
|
|
@ -6,8 +6,13 @@ namespace Ombi.Helpers
|
|||
{
|
||||
public static EventId ApiException => new EventId(1000);
|
||||
public static EventId RadarrApiException => new EventId(1001);
|
||||
|
||||
public static EventId CacherException => new EventId(2000);
|
||||
public static EventId RadarrCacherException => new EventId(2001);
|
||||
|
||||
public static EventId MovieSender => new EventId(3000);
|
||||
|
||||
public static EventId Notification => new EventId(4000);
|
||||
public static EventId DiscordNotification => new EventId(4001);
|
||||
}
|
||||
}
|
131
src/Ombi.Notification.Discord/Class1.cs
Normal file
131
src/Ombi.Notification.Discord/Class1.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Discord;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
|
||||
namespace Ombi.Notification.Discord
|
||||
{
|
||||
public class DiscordNotification : BaseNotification<DiscordNotificationSettings>
|
||||
{
|
||||
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn, ILogger<DiscordNotification> log) : base(sn)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
}
|
||||
|
||||
public override string NotificationName => "DiscordNotification";
|
||||
|
||||
private IDiscordApi Api { get; }
|
||||
private ILogger<DiscordNotification> Logger { get; }
|
||||
|
||||
protected override bool ValidateConfiguration(DiscordNotificationSettings settings)
|
||||
{
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (string.IsNullOrEmpty(settings.WebhookUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
var a = settings.Token;
|
||||
var b = settings.WebookId;
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override async Task NewRequest(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"{model.Title} has been requested by user: {model.User}";
|
||||
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task Issue(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! Your request for {model.Title} has been declined, Sorry!";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! The request for {model.Title} has now been approved!";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationModel model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! The request for {model.Title} is now available!";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task Send(NotificationMessage model, DiscordNotificationSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Api.SendMessage(model.Message, settings.WebookId, settings.Token, settings.Username);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(LoggingEvents.DiscordNotification, e, "Failed to send Discord Notification");
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task Test(NotificationModel model, DiscordNotificationSettings 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -4,9 +4,9 @@ using MailKit.Net.Smtp;
|
|||
using MimeKit;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.Notifications;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Notifications.Templates;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
|
||||
namespace Ombi.Notifications.Email
|
||||
{
|
||||
|
@ -20,6 +20,10 @@ namespace Ombi.Notifications.Email
|
|||
|
||||
protected override bool ValidateConfiguration(EmailNotificationSettings settings)
|
||||
{
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (settings.Authentication)
|
||||
{
|
||||
if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
|
||||
|
|
|
@ -7,12 +7,8 @@ namespace Ombi.Notifications
|
|||
{
|
||||
public interface INotificationService
|
||||
{
|
||||
ConcurrentDictionary<string, INotification> Observers { get; }
|
||||
|
||||
Task Publish(NotificationModel model);
|
||||
Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings);
|
||||
Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type);
|
||||
void Subscribe(INotification notification);
|
||||
void UnSubscribe(INotification notification);
|
||||
}
|
||||
}
|
|
@ -1,15 +1,47 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Settings.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
|
||||
namespace Ombi.Notifications
|
||||
{
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
public ConcurrentDictionary<string, INotification> Observers { get; } = new ConcurrentDictionary<string, INotification>();
|
||||
public NotificationService(IServiceProvider provider, ILogger<NotificationService> log)
|
||||
{
|
||||
Log = log;
|
||||
NotificationAgents = new List<INotification>();
|
||||
|
||||
var baseSearchType = typeof(BaseNotification<>).FullName;
|
||||
|
||||
var ass = typeof(NotificationService).GetTypeInfo().Assembly;
|
||||
|
||||
foreach (var ti in ass.DefinedTypes)
|
||||
{
|
||||
if (ti?.BaseType?.FullName == baseSearchType)
|
||||
{
|
||||
var type = ti?.AsType();
|
||||
var ctors = type.GetConstructors();
|
||||
var ctor = ctors.FirstOrDefault();
|
||||
|
||||
var services = new List<object>();
|
||||
foreach (var param in ctor.GetParameters())
|
||||
{
|
||||
services.Add(provider.GetService(param.ParameterType));
|
||||
}
|
||||
|
||||
var item = Activator.CreateInstance(type, services.ToArray());
|
||||
NotificationAgents.Add((INotification)item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<INotification> NotificationAgents { get; }
|
||||
private ILogger<NotificationService> Log { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sends a notification to the user. This one is used in normal notification scenarios
|
||||
|
@ -18,7 +50,7 @@ namespace Ombi.Notifications
|
|||
/// <returns></returns>
|
||||
public async Task Publish(NotificationModel model)
|
||||
{
|
||||
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model));
|
||||
var notificationTasks = NotificationAgents.Select(notification => NotifyAsync(notification, model));
|
||||
|
||||
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -31,20 +63,11 @@ namespace Ombi.Notifications
|
|||
/// <returns></returns>
|
||||
public async Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings)
|
||||
{
|
||||
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model, settings));
|
||||
var notificationTasks = NotificationAgents.Select(notification => NotifyAsync(notification, model, settings));
|
||||
|
||||
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Subscribe(INotification notification)
|
||||
{
|
||||
Observers.TryAdd(notification.NotificationName, notification);
|
||||
}
|
||||
|
||||
public void UnSubscribe(INotification notification)
|
||||
{
|
||||
Observers.TryRemove(notification.NotificationName, out notification);
|
||||
}
|
||||
|
||||
private async Task NotifyAsync(INotification notification, NotificationModel model)
|
||||
{
|
||||
|
@ -54,6 +77,7 @@ namespace Ombi.Notifications
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogError(LoggingEvents.Notification, ex, "Failed to notify for notification: {@notification}", notification);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Settings.Settings.Models.Notifications
|
||||
{
|
||||
public class DiscordNotificationSettings : Settings
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string WebhookUrl { get; set; }
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string WebookId => SplitWebUrl(4);
|
||||
|
||||
[JsonIgnore]
|
||||
public string Token => SplitWebUrl(5);
|
||||
|
||||
private string SplitWebUrl(int index)
|
||||
{
|
||||
if (!WebhookUrl.StartsWith("http", StringComparison.CurrentCulture))
|
||||
{
|
||||
WebhookUrl = "https://" + WebhookUrl;
|
||||
}
|
||||
var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
return split.Length < index
|
||||
? string.Empty
|
||||
: split[index];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
namespace Ombi.Core.Settings.Models.Notifications
|
||||
namespace Ombi.Settings.Settings.Models.Notifications
|
||||
{
|
||||
public sealed class EmailNotificationSettings : Ombi.Settings.Settings.Models.Settings
|
||||
public sealed class EmailNotificationSettings : Settings
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string EmailHost { get; set; }
|
||||
|
|
14
src/Ombi.sln
14
src/Ombi.sln
|
@ -61,6 +61,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Core.Tests", "Ombi.Cor
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Radarr", "Ombi.Api.Radarr\Ombi.Api.Radarr.csproj", "{94D04C1F-E35A-499C-B0A0-9FADEBDF8336}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notification.Discord", "Ombi.Notification.Discord\Ombi.Notification.Discord.csproj", "{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -147,6 +151,14 @@ Global
|
|||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -166,5 +178,7 @@ Global
|
|||
{3880375C-1A7E-4D75-96EC-63B954C42FEA} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{D7B05CF2-E6B3-4BE5-8A02-6698CC0DCE9A} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue