Merge pull request #946 from tidusjar/dev

Discord notifications
pull/1028/head
Jamie 8 years ago committed by GitHub
commit f7a1f34b2f

@ -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);
}
}

@ -31,6 +31,6 @@ namespace Ombi.Api.Interfaces
{
public interface INetflixApi
{
NetflixMovieResult GetMovies(string movieName, string year = null);
NetflixMovieResult CheckNetflix(string title, string year = null);
}
}

@ -47,6 +47,7 @@
<ItemGroup>
<Compile Include="IApiRequest.cs" />
<Compile Include="ICouchPotatoApi.cs" />
<Compile Include="IDiscordApi.cs" />
<Compile Include="IHeadphonesApi.cs" />
<Compile Include="IMusicBrainzApi.cs" />
<Compile Include="INetflixApi.cs" />

@ -58,5 +58,12 @@ namespace Ombi.Api.Models.Netflix
public string Mediatype { get; set; }
[JsonProperty(PropertyName = "runtime")]
public string Runtime { get; set; }
// For errors
[JsonProperty(PropertyName = "errorcode")]
public int ErrorCode { get; set; }
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
}
}

@ -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; }
}
}

@ -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\MusicBrainzSearchResults.cs" />
<Compile Include="Netflix\NetflixMovieResult.cs" />
<Compile Include="Notifications\DiscordWebhookRequest.cs" />
<Compile Include="Notifications\DiscordWebhookResponse.cs" />
<Compile Include="Notifications\PushbulletPush.cs" />
<Compile Include="Notifications\PushbulletResponse.cs" />
<Compile Include="Notifications\PushoverResponse.cs" />

@ -0,0 +1,115 @@
#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}",
Method = Method.POST
};
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}",
Method = Method.POST
};
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);
}
}
}

@ -26,6 +26,7 @@
#endregion
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ombi.Api.Interfaces;
using Ombi.Api.Models.Netflix;
@ -43,10 +44,10 @@ namespace Ombi.Api
private IApiRequest Api { get; }
private Uri Endpoint => new Uri("http://netflixroulette.net/api/api.php");
public NetflixMovieResult GetMovies(string movieName, string year = null)
public NetflixMovieResult CheckNetflix(string title, string year = null)
{
var request = new RestRequest();
request.AddQueryParameter("title", movieName);
request.AddQueryParameter("title", title);
if (!string.IsNullOrEmpty(year))
{
request.AddQueryParameter("year", year);

@ -69,6 +69,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ApiRequest.cs" />
<Compile Include="DiscordApi.cs" />
<Compile Include="NetflixRouletteApi.cs" />
<Compile Include="WatcherApi.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
/// </summary>
/// <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);
}
}

@ -122,6 +122,7 @@
<Compile Include="Queue\TransientFaultQueue.cs" />
<Compile Include="SecurityExtensions.cs" />
<Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\DiscordNotificationSettings.cs" />
<Compile Include="SettingModels\WatcherSettings.cs" />
<Compile Include="SettingModels\ExternalSettings.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
/// </summary>
/// <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)
{
var plexUser = PlexUsers.GetUserByUsername(username);
if (plexUser != null)
{
if (!string.IsNullOrEmpty(plexUser.UserAlias))
{
return plexUser.UserAlias;
}
else
{
return plexUser.Username;
}
return !string.IsNullOrEmpty(plexUser.UserAlias) ? plexUser.UserAlias : plexUser.Username;
}
var dbUser = UserRepository.GetUserByUsername(username);
if (dbUser != null)
{
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(dbUser.UserProperties);
if (!string.IsNullOrEmpty(userProps.UserAlias))
{
return userProps.UserAlias;
}
else
{
return dbUser.UserName;
}
return !string.IsNullOrEmpty(userProps.UserAlias) ? userProps.UserAlias : dbUser.UserName;
}
// could be a local user

@ -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];
}
}
}

@ -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\PushoverNotification.cs" />
<Compile Include="Notification\PushbulletNotification.cs" />
<Compile Include="Notification\DiscordNotification.cs" />
<Compile Include="Notification\SlackNotification.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

@ -304,6 +304,8 @@ $(function () {
var html = searchTemplate(context);
$("#movieList").append(html);
checkNetflix(context.title, context.id);
});
}
else {
@ -334,6 +336,9 @@ $(function () {
var context = buildTvShowContext(result);
var html = searchTemplate(context);
$("#tvList").append(html);
checkNetflix(context.title, context.id);
});
}
else {
@ -343,6 +348,19 @@ $(function () {
});
};
function checkNetflix(title, id) {
var url = createBaseUrl(base, '/searchextension/netflix/' + title);
$.ajax(url).success(function (results) {
if (results.result) {
// It's on Netflix
$('#' + id + 'netflixTab')
.html("<span class='label label-success'>Avaialble on Netflix</span>");
}
});
}
function resetTvShows() {
$("#tvList").html("");
}

@ -93,6 +93,8 @@ namespace Ombi.UI.Modules.Admin
private IAnalytics Analytics { get; }
private IRecentlyAdded RecentlyAdded { get; }
private ISettingsService<NotificationSettingsV2> NotifySettings { get; }
private ISettingsService<DiscordNotificationSettings> DiscordSettings { get; }
private IDiscordApi DiscordApi { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
public AdminModule(ISettingsService<PlexRequestSettings> prService,
@ -118,7 +120,9 @@ namespace Ombi.UI.Modules.Admin
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics,
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded,
ISettingsService<WatcherSettings> watcherSettings
ISettingsService<WatcherSettings> watcherSettings ,
ISettingsService<DiscordNotificationSettings> discord,
IDiscordApi discordapi
, ISecurityExtensions security) : base("admin", prService, security)
{
PrService = prService;
@ -150,6 +154,8 @@ namespace Ombi.UI.Modules.Admin
NotifySettings = notifyService;
RecentlyAdded = recentlyAdded;
WatcherSettings = watcherSettings;
DiscordSettings = discord;
DiscordApi = discordapi;
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
@ -208,10 +214,13 @@ namespace Ombi.UI.Modules.Admin
Post["/testslacknotification", true] = async (x, ct) => await TestSlackNotification();
Get["/slacknotification"] = _ => SlackNotifications();
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();
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." });
}
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()
{
var settings = await LandingSettings.GetSettingsAsync();

@ -115,7 +115,7 @@ namespace Ombi.UI.Modules
var username = Security.GetUsername(User.UserName, Session);
if (string.IsNullOrEmpty(username))
{
return Session[SessionKeys.UsernameKey].ToString();
return "Unknown User";
}
_username = username;
}

@ -0,0 +1,62 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2017 Jamie Rees
// File: SearchExtensionModule.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 Nancy;
using Ombi.Api.Interfaces;
using Ombi.Core;
using Ombi.Core.SettingModels;
namespace Ombi.UI.Modules
{
public class SearchExtensionModule : BaseAuthModule
{
public SearchExtensionModule(ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security, INetflixApi netflix) : base("searchextension",pr, security)
{
NetflixApi = netflix;
Get["/netflix/{searchTerm}", true] = async (x, ctx) => await Netflix(x.searchTerm);
}
private INetflixApi NetflixApi { get; }
public async Task<Response> Netflix(string title)
{
var result = NetflixApi.CheckNetflix(title);
if (!string.IsNullOrEmpty(result.Message))
{
return Response.AsJson(new { Result = false });
}
return Response.AsJson(new { Result = true });
}
}
}

@ -46,6 +46,8 @@ namespace Ombi.UI.NinjectModules
Bind<ISlackApi>().To<SlackApi>();
Bind<IApiRequest>().To<ApiRequest>();
Bind<IWatcherApi>().To<WatcherApi>();
Bind<INetflixApi>().To<NetflixRouletteApi>();
Bind<IDiscordApi>().To<DiscordApi>();
}
}
}

@ -273,6 +273,7 @@
<Compile Include="Modules\IssuesModule.cs" />
<Compile Include="Modules\LandingPageModule.cs" />
<Compile Include="Modules\LayoutModule.cs" />
<Compile Include="Modules\SearchExtensionModule.cs" />
<Compile Include="Modules\UserWizardModule.cs" />
<Compile Include="NinjectModules\ApiModule.cs" />
<Compile Include="NinjectModules\ConfigurationModule.cs" />
@ -788,6 +789,9 @@
<Content Include="Views\UserLogin\Password.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\Admin\DiscordNotification.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Web.Debug.config">
<DependentUpon>web.config</DependentUpon>
</None>

@ -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>

@ -107,7 +107,7 @@
}
<!-- Movie and TV Results template -->
<script id="search-template" type="text/x-handlebars-template">
<script id="search-template" type="text/x-handlebars-template">
<div class="row">
<div class="col-sm-2">
@ -134,7 +134,6 @@
<h4>{{title}} ({{year}})</h4>
</a>
{{/if_eq}}
{{#if_eq type "tv"}}
{{#if available}}
<span class="label label-success">@UI.Search_Available_on_plex</span>
{{else}}
@ -146,9 +145,11 @@
<span class="label label-danger">@UI.Search_Not_Requested_Yet</span>
{{/if}}
{{/if}}
<span id="{{id}}netflixTab"></span>
<br />
<br />
{{/if_eq}}
</div>
<p>{{overview}}</p>
</div>

@ -19,6 +19,7 @@
@Html.GetSidebarUrl(Context, "/admin/pushbulletnotification", "Pushbullet Notifications")
@Html.GetSidebarUrl(Context, "/admin/pushovernotification", "Pushover 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/status", "Status")
@Html.GetSidebarUrl(Context, "/admin/scheduledjobs", "Scheduled Jobs")

Loading…
Cancel
Save