mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-16 02:02:55 -07:00
Initial impliementation of #739
This commit is contained in:
parent
88c0651b1e
commit
973688393f
18 changed files with 624 additions and 21 deletions
38
Ombi.Api.Interfaces/IDiscordApi.cs
Normal file
38
Ombi.Api.Interfaces/IDiscordApi.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2017 Jamie Rees
|
||||||
|
// File: IDiscordApi.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Api.Models.Notifications;
|
||||||
|
|
||||||
|
namespace Ombi.Api.Interfaces
|
||||||
|
{
|
||||||
|
public interface IDiscordApi
|
||||||
|
{
|
||||||
|
void SendMessage(string message, string webhookId, string webhookToken, string username = null);
|
||||||
|
Task SendMessageAsync(string message, string webhookId, string webhookToken, string username = null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="IApiRequest.cs" />
|
<Compile Include="IApiRequest.cs" />
|
||||||
<Compile Include="ICouchPotatoApi.cs" />
|
<Compile Include="ICouchPotatoApi.cs" />
|
||||||
|
<Compile Include="IDiscordApi.cs" />
|
||||||
<Compile Include="IHeadphonesApi.cs" />
|
<Compile Include="IHeadphonesApi.cs" />
|
||||||
<Compile Include="IMusicBrainzApi.cs" />
|
<Compile Include="IMusicBrainzApi.cs" />
|
||||||
<Compile Include="INetflixApi.cs" />
|
<Compile Include="INetflixApi.cs" />
|
||||||
|
|
34
Ombi.Api.Models/Notifications/DiscordWebhookRequest.cs
Normal file
34
Ombi.Api.Models/Notifications/DiscordWebhookRequest.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2017 Jamie Rees
|
||||||
|
// File: DiscordWebhookRequest.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
namespace Ombi.Api.Models.Notifications
|
||||||
|
{
|
||||||
|
public class DiscordWebhookRequest
|
||||||
|
{
|
||||||
|
public string content { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
47
Ombi.Api.Models/Notifications/DiscordWebhookResponse.cs
Normal file
47
Ombi.Api.Models/Notifications/DiscordWebhookResponse.cs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2017 Jamie Rees
|
||||||
|
// File: DiscordWebhookResponse.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ombi.Api.Models.Notifications
|
||||||
|
{
|
||||||
|
public class DiscordWebhookResponse
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "channel_id")]
|
||||||
|
public string channelid { get; set; }
|
||||||
|
|
||||||
|
public string token { get; set; }
|
||||||
|
public string avatar { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "guild_id")]
|
||||||
|
public string guildid { get; set; }
|
||||||
|
|
||||||
|
public string id { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,6 +62,8 @@
|
||||||
<Compile Include="Music\MusicBrainzReleaseInfo.cs" />
|
<Compile Include="Music\MusicBrainzReleaseInfo.cs" />
|
||||||
<Compile Include="Music\MusicBrainzSearchResults.cs" />
|
<Compile Include="Music\MusicBrainzSearchResults.cs" />
|
||||||
<Compile Include="Netflix\NetflixMovieResult.cs" />
|
<Compile Include="Netflix\NetflixMovieResult.cs" />
|
||||||
|
<Compile Include="Notifications\DiscordWebhookRequest.cs" />
|
||||||
|
<Compile Include="Notifications\DiscordWebhookResponse.cs" />
|
||||||
<Compile Include="Notifications\PushbulletPush.cs" />
|
<Compile Include="Notifications\PushbulletPush.cs" />
|
||||||
<Compile Include="Notifications\PushbulletResponse.cs" />
|
<Compile Include="Notifications\PushbulletResponse.cs" />
|
||||||
<Compile Include="Notifications\PushoverResponse.cs" />
|
<Compile Include="Notifications\PushoverResponse.cs" />
|
||||||
|
|
113
Ombi.Api/DiscordApi.cs
Normal file
113
Ombi.Api/DiscordApi.cs
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2017 Jamie Rees
|
||||||
|
// File: NetflixRouletteApi.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ombi.Api.Interfaces;
|
||||||
|
using Ombi.Api.Models.Netflix;
|
||||||
|
using Ombi.Api.Models.Notifications;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace Ombi.Api
|
||||||
|
{
|
||||||
|
public class DiscordApi : IDiscordApi
|
||||||
|
{
|
||||||
|
public DiscordApi(IApiRequest req)
|
||||||
|
{
|
||||||
|
Api = req;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IApiRequest Api { get; }
|
||||||
|
private Uri Endpoint => new Uri("https://discordapp.com/api/"); //webhooks/270828242636636161/lLysOMhJ96AFO1kvev0bSqP-WCZxKUh1UwfubhIcLkpS0DtM3cg4Pgeraw3waoTXbZii
|
||||||
|
|
||||||
|
|
||||||
|
public void SendMessage(string message, string webhookId, string webhookToken, string username = null)
|
||||||
|
{
|
||||||
|
var request = new RestRequest
|
||||||
|
{
|
||||||
|
Resource = "webhooks/{webhookId}/{webhookToken}"
|
||||||
|
};
|
||||||
|
|
||||||
|
request.AddUrlSegment("webhookId", webhookId);
|
||||||
|
request.AddUrlSegment("webhookToken", webhookToken);
|
||||||
|
|
||||||
|
var body = new DiscordWebhookRequest
|
||||||
|
{
|
||||||
|
content = message,
|
||||||
|
username = username
|
||||||
|
};
|
||||||
|
request.AddJsonBody(body);
|
||||||
|
|
||||||
|
request.AddHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
Api.Execute(request, Endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendMessageAsync(string message, string webhookId, string webhookToken, string username = null)
|
||||||
|
{
|
||||||
|
var request = new RestRequest
|
||||||
|
{
|
||||||
|
Resource = "webhooks/{webhookId}/{webhookToken}"
|
||||||
|
};
|
||||||
|
|
||||||
|
request.AddUrlSegment("webhookId", webhookId);
|
||||||
|
request.AddUrlSegment("webhookToken", webhookToken);
|
||||||
|
|
||||||
|
var body = new DiscordWebhookRequest
|
||||||
|
{
|
||||||
|
content = message,
|
||||||
|
username = username
|
||||||
|
};
|
||||||
|
request.AddJsonBody(body);
|
||||||
|
|
||||||
|
request.AddHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
await Task.Run(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
Api.Execute(request, Endpoint);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public NetflixMovieResult CheckNetflix(string title, string year = null)
|
||||||
|
{
|
||||||
|
var request = new RestRequest();
|
||||||
|
request.AddQueryParameter("title", title);
|
||||||
|
if (!string.IsNullOrEmpty(year))
|
||||||
|
{
|
||||||
|
request.AddQueryParameter("year", year);
|
||||||
|
}
|
||||||
|
var result = Api.Execute(request, Endpoint);
|
||||||
|
|
||||||
|
return JsonConvert.DeserializeObject<NetflixMovieResult>(result.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,6 +69,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ApiRequest.cs" />
|
<Compile Include="ApiRequest.cs" />
|
||||||
|
<Compile Include="DiscordApi.cs" />
|
||||||
<Compile Include="NetflixRouletteApi.cs" />
|
<Compile Include="NetflixRouletteApi.cs" />
|
||||||
<Compile Include="WatcherApi.cs" />
|
<Compile Include="WatcherApi.cs" />
|
||||||
<Compile Include="MusicBrainzApi.cs" />
|
<Compile Include="MusicBrainzApi.cs" />
|
||||||
|
|
|
@ -29,7 +29,10 @@ namespace Ombi.Core
|
||||||
/// Gets the username this could be the alias! We should always use this method when getting the username
|
/// Gets the username this could be the alias! We should always use this method when getting the username
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="username">The username.</param>
|
/// <param name="username">The username.</param>
|
||||||
/// <returns><c>null</c> if we cannot find a user</returns>
|
/// <param name="session">The session.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>null</c> if we cannot find a user
|
||||||
|
/// </returns>
|
||||||
string GetUsername(string username, ISession session);
|
string GetUsername(string username, ISession session);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -122,6 +122,7 @@
|
||||||
<Compile Include="Queue\TransientFaultQueue.cs" />
|
<Compile Include="Queue\TransientFaultQueue.cs" />
|
||||||
<Compile Include="SecurityExtensions.cs" />
|
<Compile Include="SecurityExtensions.cs" />
|
||||||
<Compile Include="SettingModels\AuthenticationSettings.cs" />
|
<Compile Include="SettingModels\AuthenticationSettings.cs" />
|
||||||
|
<Compile Include="SettingModels\DiscordNotificationSettings.cs" />
|
||||||
<Compile Include="SettingModels\WatcherSettings.cs" />
|
<Compile Include="SettingModels\WatcherSettings.cs" />
|
||||||
<Compile Include="SettingModels\ExternalSettings.cs" />
|
<Compile Include="SettingModels\ExternalSettings.cs" />
|
||||||
<Compile Include="SettingModels\HeadphonesSettings.cs" />
|
<Compile Include="SettingModels\HeadphonesSettings.cs" />
|
||||||
|
|
|
@ -94,34 +94,23 @@ namespace Ombi.Core
|
||||||
/// Gets the username this could be the alias! We should always use this method when getting the username
|
/// Gets the username this could be the alias! We should always use this method when getting the username
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="username">The username.</param>
|
/// <param name="username">The username.</param>
|
||||||
/// <returns><c>null</c> if we cannot find a user</returns>
|
/// <param name="session"></param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>null</c> if we cannot find a user
|
||||||
|
/// </returns>
|
||||||
public string GetUsername(string username, ISession session)
|
public string GetUsername(string username, ISession session)
|
||||||
{
|
{
|
||||||
var plexUser = PlexUsers.GetUserByUsername(username);
|
var plexUser = PlexUsers.GetUserByUsername(username);
|
||||||
if (plexUser != null)
|
if (plexUser != null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(plexUser.UserAlias))
|
return !string.IsNullOrEmpty(plexUser.UserAlias) ? plexUser.UserAlias : plexUser.Username;
|
||||||
{
|
|
||||||
return plexUser.UserAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return plexUser.Username;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbUser = UserRepository.GetUserByUsername(username);
|
var dbUser = UserRepository.GetUserByUsername(username);
|
||||||
if (dbUser != null)
|
if (dbUser != null)
|
||||||
{
|
{
|
||||||
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(dbUser.UserProperties);
|
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(dbUser.UserProperties);
|
||||||
if (!string.IsNullOrEmpty(userProps.UserAlias))
|
return !string.IsNullOrEmpty(userProps.UserAlias) ? userProps.UserAlias : dbUser.UserName;
|
||||||
{
|
|
||||||
return userProps.UserAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return dbUser.UserName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// could be a local user
|
// could be a local user
|
||||||
|
|
31
Ombi.Core/SettingModels/DiscordNotificationSettings.cs
Normal file
31
Ombi.Core/SettingModels/DiscordNotificationSettings.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Ombi.Core.SettingModels
|
||||||
|
{
|
||||||
|
public sealed class DiscordNotificationSettings : NotificationSettings
|
||||||
|
{
|
||||||
|
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.InvariantCulture))
|
||||||
|
{
|
||||||
|
WebhookUrl = "https://" + WebhookUrl;
|
||||||
|
}
|
||||||
|
var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
return split.Length < index
|
||||||
|
? string.Empty
|
||||||
|
: split[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
165
Ombi.Services/Notification/DiscordNotification.cs
Normal file
165
Ombi.Services/Notification/DiscordNotification.cs
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2016 Jamie Rees
|
||||||
|
// File: SlackNotification.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using Ombi.Api.Interfaces;
|
||||||
|
using Ombi.Api.Models.Notifications;
|
||||||
|
using Ombi.Core;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
using Ombi.Core.SettingModels;
|
||||||
|
using Ombi.Services.Interfaces;
|
||||||
|
|
||||||
|
namespace Ombi.Services.Notification
|
||||||
|
{
|
||||||
|
public class DiscordNotification : INotification
|
||||||
|
{
|
||||||
|
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn)
|
||||||
|
{
|
||||||
|
Api = api;
|
||||||
|
Settings = sn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NotificationName => "DiscordNotification";
|
||||||
|
|
||||||
|
private IDiscordApi Api { get; }
|
||||||
|
private ISettingsService<DiscordNotificationSettings> Settings { get; }
|
||||||
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
|
||||||
|
public async Task NotifyAsync(NotificationModel model)
|
||||||
|
{
|
||||||
|
var settings = Settings.GetSettings();
|
||||||
|
|
||||||
|
await NotifyAsync(model, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task NotifyAsync(NotificationModel model, Settings settings)
|
||||||
|
{
|
||||||
|
if (settings == null) await NotifyAsync(model);
|
||||||
|
|
||||||
|
var pushSettings = (DiscordNotificationSettings)settings;
|
||||||
|
if (!ValidateConfiguration(pushSettings))
|
||||||
|
{
|
||||||
|
Log.Error("Settings for Slack was not correct, we cannot push a notification");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (model.NotificationType)
|
||||||
|
{
|
||||||
|
case NotificationType.NewRequest:
|
||||||
|
await PushNewRequestAsync(model, pushSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.Issue:
|
||||||
|
await PushIssueAsync(model, pushSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestAvailable:
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestApproved:
|
||||||
|
break;
|
||||||
|
case NotificationType.AdminNote:
|
||||||
|
break;
|
||||||
|
case NotificationType.Test:
|
||||||
|
await PushTest(pushSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.RequestDeclined:
|
||||||
|
await PushRequestDeclinedAsync(model, pushSettings);
|
||||||
|
break;
|
||||||
|
case NotificationType.ItemAddedToFaultQueue:
|
||||||
|
await PushFaultQueue(model, pushSettings);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushNewRequestAsync(NotificationModel model, DiscordNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var message = $"{model.Title} has been requested by user: {model.User}";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushRequestDeclinedAsync(NotificationModel model, DiscordNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var message = $"Hello! Your request for {model.Title} has been declined, Sorry!";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushIssueAsync(NotificationModel model, DiscordNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushTest(DiscordNotificationSettings settings)
|
||||||
|
{
|
||||||
|
var message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PushFaultQueue(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";
|
||||||
|
await Push(settings, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Push(DiscordNotificationSettings config, string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Api.SendMessageAsync(message, config.WebookId, config.Token, config.Username);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -126,6 +126,7 @@
|
||||||
<Compile Include="Notification\NotificationService.cs" />
|
<Compile Include="Notification\NotificationService.cs" />
|
||||||
<Compile Include="Notification\PushoverNotification.cs" />
|
<Compile Include="Notification\PushoverNotification.cs" />
|
||||||
<Compile Include="Notification\PushbulletNotification.cs" />
|
<Compile Include="Notification\PushbulletNotification.cs" />
|
||||||
|
<Compile Include="Notification\DiscordNotification.cs" />
|
||||||
<Compile Include="Notification\SlackNotification.cs" />
|
<Compile Include="Notification\SlackNotification.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -93,6 +93,8 @@ namespace Ombi.UI.Modules.Admin
|
||||||
private IAnalytics Analytics { get; }
|
private IAnalytics Analytics { get; }
|
||||||
private IRecentlyAdded RecentlyAdded { get; }
|
private IRecentlyAdded RecentlyAdded { get; }
|
||||||
private ISettingsService<NotificationSettingsV2> NotifySettings { get; }
|
private ISettingsService<NotificationSettingsV2> NotifySettings { get; }
|
||||||
|
private ISettingsService<DiscordNotificationSettings> DiscordSettings { get; }
|
||||||
|
private IDiscordApi DiscordApi { get; }
|
||||||
|
|
||||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
public AdminModule(ISettingsService<PlexRequestSettings> prService,
|
public AdminModule(ISettingsService<PlexRequestSettings> prService,
|
||||||
|
@ -118,7 +120,9 @@ namespace Ombi.UI.Modules.Admin
|
||||||
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
|
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
|
||||||
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics,
|
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics,
|
||||||
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded,
|
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded,
|
||||||
ISettingsService<WatcherSettings> watcherSettings
|
ISettingsService<WatcherSettings> watcherSettings ,
|
||||||
|
ISettingsService<DiscordNotificationSettings> discord,
|
||||||
|
IDiscordApi discordapi
|
||||||
, ISecurityExtensions security) : base("admin", prService, security)
|
, ISecurityExtensions security) : base("admin", prService, security)
|
||||||
{
|
{
|
||||||
PrService = prService;
|
PrService = prService;
|
||||||
|
@ -150,6 +154,8 @@ namespace Ombi.UI.Modules.Admin
|
||||||
NotifySettings = notifyService;
|
NotifySettings = notifyService;
|
||||||
RecentlyAdded = recentlyAdded;
|
RecentlyAdded = recentlyAdded;
|
||||||
WatcherSettings = watcherSettings;
|
WatcherSettings = watcherSettings;
|
||||||
|
DiscordSettings = discord;
|
||||||
|
DiscordApi = discordapi;
|
||||||
|
|
||||||
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
|
||||||
|
|
||||||
|
@ -208,10 +214,13 @@ namespace Ombi.UI.Modules.Admin
|
||||||
|
|
||||||
|
|
||||||
Post["/testslacknotification", true] = async (x, ct) => await TestSlackNotification();
|
Post["/testslacknotification", true] = async (x, ct) => await TestSlackNotification();
|
||||||
|
|
||||||
Get["/slacknotification"] = _ => SlackNotifications();
|
Get["/slacknotification"] = _ => SlackNotifications();
|
||||||
Post["/slacknotification"] = _ => SaveSlackNotifications();
|
Post["/slacknotification"] = _ => SaveSlackNotifications();
|
||||||
|
|
||||||
|
Post["/testdiscordnotification", true] = async (x, ct) => await TestDiscordNotification();
|
||||||
|
Get["/discordnotification", true] = async (x, ct) => await DiscordNotification();
|
||||||
|
Post["/discordnotification", true] = async (x, ct) => await SaveDiscordNotifications();
|
||||||
|
|
||||||
Get["/landingpage", true] = async (x, ct) => await LandingPage();
|
Get["/landingpage", true] = async (x, ct) => await LandingPage();
|
||||||
Post["/landingpage", true] = async (x, ct) => await SaveLandingPage();
|
Post["/landingpage", true] = async (x, ct) => await SaveLandingPage();
|
||||||
|
|
||||||
|
@ -918,6 +927,71 @@ namespace Ombi.UI.Modules.Admin
|
||||||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Negotiator> DiscordNotification()
|
||||||
|
{
|
||||||
|
var settings = await DiscordSettings.GetSettingsAsync();
|
||||||
|
return View["DiscordNotification", settings];
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Response> TestDiscordNotification()
|
||||||
|
{
|
||||||
|
var settings = this.BindAndValidate<DiscordNotificationSettings>();
|
||||||
|
if (!ModelValidationResult.IsValid)
|
||||||
|
{
|
||||||
|
return Response.AsJson(ModelValidationResult.SendJsonError());
|
||||||
|
}
|
||||||
|
var notificationModel = new NotificationModel
|
||||||
|
{
|
||||||
|
NotificationType = NotificationType.Test,
|
||||||
|
DateTime = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
var currentDicordSettings = await DiscordSettings.GetSettingsAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||||
|
settings.Enabled = true;
|
||||||
|
await NotificationService.Publish(notificationModel, settings);
|
||||||
|
Log.Info("Sent Discord notification test");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Failed to subscribe and publish test Discord Notification");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!currentDicordSettings.Enabled)
|
||||||
|
{
|
||||||
|
NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Discord Notification! If you do not receive it please check the logs." });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Response> SaveDiscordNotifications()
|
||||||
|
{
|
||||||
|
var settings = this.BindAndValidate<DiscordNotificationSettings>();
|
||||||
|
if (!ModelValidationResult.IsValid)
|
||||||
|
{
|
||||||
|
return Response.AsJson(ModelValidationResult.SendJsonError());
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await DiscordSettings.SaveSettingsAsync(settings);
|
||||||
|
if (settings.Enabled)
|
||||||
|
{
|
||||||
|
NotificationService.Subscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NotificationService.UnSubscribe(new DiscordNotification(DiscordApi, DiscordSettings));
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info("Saved discord settings, result: {0}", result);
|
||||||
|
return Response.AsJson(result
|
||||||
|
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Discord Notifications!" }
|
||||||
|
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<Negotiator> LandingPage()
|
private async Task<Negotiator> LandingPage()
|
||||||
{
|
{
|
||||||
var settings = await LandingSettings.GetSettingsAsync();
|
var settings = await LandingSettings.GetSettingsAsync();
|
||||||
|
|
|
@ -115,7 +115,7 @@ namespace Ombi.UI.Modules
|
||||||
var username = Security.GetUsername(User.UserName, Session);
|
var username = Security.GetUsername(User.UserName, Session);
|
||||||
if (string.IsNullOrEmpty(username))
|
if (string.IsNullOrEmpty(username))
|
||||||
{
|
{
|
||||||
return Session[SessionKeys.UsernameKey].ToString();
|
return "Unknown User";
|
||||||
}
|
}
|
||||||
_username = username;
|
_username = username;
|
||||||
}
|
}
|
||||||
|
|
|
@ -789,6 +789,9 @@
|
||||||
<Content Include="Views\UserLogin\Password.cshtml">
|
<Content Include="Views\UserLogin\Password.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="Views\Admin\DiscordNotification.cshtml">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<None Include="Web.Debug.config">
|
<None Include="Web.Debug.config">
|
||||||
<DependentUpon>web.config</DependentUpon>
|
<DependentUpon>web.config</DependentUpon>
|
||||||
</None>
|
</None>
|
||||||
|
|
99
Ombi.UI/Views/Admin/DiscordNotification.cshtml
Normal file
99
Ombi.UI/Views/Admin/DiscordNotification.cshtml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
@using Ombi.UI.Helpers
|
||||||
|
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<Ombi.Core.SettingModels.DiscordNotificationSettings>
|
||||||
|
@Html.Partial("Shared/Partial/_Sidebar")
|
||||||
|
|
||||||
|
<div class="col-sm-8 col-sm-push-1">
|
||||||
|
<form class="form-horizontal" method="POST" id="mainForm">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Discord Notifications</legend>
|
||||||
|
|
||||||
|
@Html.Checkbox(Model.Enabled, "Enabled", "Enabled")
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="WebhookUrl" class="control-label">Webhook Url</label>
|
||||||
|
<small class="control-label">This is the full webhook url.</small>
|
||||||
|
<small class="control-label"> Discord > Edit Channel > Webhooks > Create Webook > Copy the Webhook Url > Press Save</small>
|
||||||
|
<div class="">
|
||||||
|
<input type="text" class="form-control form-control-custom " id="WebhookUrl" name="WebhookUrl" value="@Model.WebhookUrl">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="Username" class="control-label">Username Override</label>
|
||||||
|
<small class="control-label">You can override the default username</small>
|
||||||
|
<div class="">
|
||||||
|
<input type="text" class="form-control form-control-custom " id="Username" name="Username" value="@Model.Username">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button id="testDiscord" type="submit" class="btn btn-primary-outline">Test <div id="spinner"></div></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
var base = '@Html.GetBaseUrl()';
|
||||||
|
$('#save').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$('#spinner').attr("class", "fa fa-spinner fa-spin");
|
||||||
|
var $form = $("#mainForm");
|
||||||
|
$.ajax({
|
||||||
|
type: $form.prop("method"),
|
||||||
|
data: $form.serialize(),
|
||||||
|
url: $form.prop("action"),
|
||||||
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
|
if (response.result === true) {
|
||||||
|
generateNotify(response.message, "success");
|
||||||
|
$('#spinner').attr("class", "fa fa-check");
|
||||||
|
} else {
|
||||||
|
generateNotify(response.message, "warning");
|
||||||
|
$('#spinner').attr("class", "fa fa-times");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
|
$('#spinner').attr("class", "fa fa-times");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#testDiscord').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var url = createBaseUrl(base, '/admin/testdiscordnotification');
|
||||||
|
var $form = $("#mainForm");
|
||||||
|
$.ajax({
|
||||||
|
type: $form.prop("method"),
|
||||||
|
data: $form.serialize(),
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
success: function (response) {
|
||||||
|
if (response.result === true) {
|
||||||
|
generateNotify(response.message, "success");
|
||||||
|
} else {
|
||||||
|
generateNotify(response.message, "warning");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e) {
|
||||||
|
console.log(e);
|
||||||
|
generateNotify("Something went wrong!", "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -19,6 +19,7 @@
|
||||||
@Html.GetSidebarUrl(Context, "/admin/pushbulletnotification", "Pushbullet Notifications")
|
@Html.GetSidebarUrl(Context, "/admin/pushbulletnotification", "Pushbullet Notifications")
|
||||||
@Html.GetSidebarUrl(Context, "/admin/pushovernotification", "Pushover Notifications")
|
@Html.GetSidebarUrl(Context, "/admin/pushovernotification", "Pushover Notifications")
|
||||||
@Html.GetSidebarUrl(Context, "/admin/slacknotification", "Slack Notifications")
|
@Html.GetSidebarUrl(Context, "/admin/slacknotification", "Slack Notifications")
|
||||||
|
@Html.GetSidebarUrl(Context, "/admin/discordnotification", "Discord Notifications")
|
||||||
@Html.GetSidebarUrl(Context, "/admin/logs", "Logs")
|
@Html.GetSidebarUrl(Context, "/admin/logs", "Logs")
|
||||||
@Html.GetSidebarUrl(Context, "/admin/status", "Status")
|
@Html.GetSidebarUrl(Context, "/admin/status", "Status")
|
||||||
@Html.GetSidebarUrl(Context, "/admin/scheduledjobs", "Scheduled Jobs")
|
@Html.GetSidebarUrl(Context, "/admin/scheduledjobs", "Scheduled Jobs")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue