Added Pushbullet notifications #1459 #865

pull/1488/head
Jamie.Rees 7 years ago
parent 457ea103d8
commit da5a4b0641

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Ombi.Api.Pushbullet
{
public interface IPushbulletApi
{
Task Push(string accessToken, string subject, string body, string channelTag);
}
}

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

@ -0,0 +1,36 @@
using System.Net.Http;
using System.Threading.Tasks;
namespace Ombi.Api.Pushbullet
{
public class PushbulletApi : IPushbulletApi
{
public PushbulletApi(IApi api)
{
Api = api;
}
private IApi Api { get; }
private const string BaseUrl = "https://api.pushbullet.com/v2";
public async Task Push(string accessToken, string subject, string body, string channelTag)
{
var request = new Request("/pushes", BaseUrl, HttpMethod.Post);
request.AddHeader("Access-Token", accessToken);
request.ApplicationJsonContentType();
var jsonBody = new
{
type = "note",
body = body,
title = subject,
channel_tag = channelTag
};
request.AddJsonBody(jsonBody);
await Api.Request(request);
}
}
}

@ -78,6 +78,11 @@ namespace Ombi.Api
ContentHeaders.Add(new KeyValuePair<string, string>(key, value));
}
public void ApplicationJsonContentType()
{
AddContentHeader("Content-Type", "application/json");
}
public void AddQueryString(string key, string value)
{
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) return;

@ -0,0 +1,23 @@

using System.Collections.Generic;
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities;
namespace Ombi.Core.Models.UI
{
/// <summary>
/// The view model for the notification settings page
/// </summary>
/// <seealso cref="DiscordNotificationSettings" />
public class PushbulletNotificationViewModel : PushbulletSettings
{
/// <summary>
/// Gets or sets the notification templates.
/// </summary>
/// <value>
/// The notification templates.
/// </value>
public List<NotificationTemplates> NotificationTemplates { get; set; }
}
}

@ -29,6 +29,7 @@ using Ombi.Notifications.Agents;
using Ombi.Schedule.Jobs.Radarr;
using Ombi.Api;
using Ombi.Api.FanartTv;
using Ombi.Api.Pushbullet;
using Ombi.Api.Service;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Senders;
@ -72,6 +73,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<ITraktApi, TraktApi>();
services.AddTransient<IRadarrApi, RadarrApi>();
services.AddTransient<IDiscordApi, DiscordApi>();
services.AddTransient<IPushbulletApi, PushbulletApi>();
services.AddTransient<IOmbiService, OmbiService>();
services.AddTransient<IFanartTvApi, FanartTvApi>();
}
@ -82,7 +84,6 @@ namespace Ombi.DependencyInjection
services.AddScoped<IOmbiContext, OmbiContext>();
services.AddTransient<ISettingsRepository, SettingsJsonRepository>();
//services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ISettingsResolver, SettingsResolver>();
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>();
@ -91,7 +92,6 @@ namespace Ombi.DependencyInjection
services.AddTransient<IMovieRequestRepository, MovieRequestRepository>();
services.AddTransient<IAuditRepository, AuditRepository>();
services.AddTransient<IApplicationConfigRepository, ApplicationConfigRepository>();
//services.AddTransient<ITokenRepository, TokenRepository>();
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsService<>));
}
public static void RegisterServices(this IServiceCollection services)
@ -104,6 +104,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IDiscordNotification, DiscordNotification>();
services.AddTransient<IEmailNotification, EmailNotification>();
services.AddTransient<IPushbulletNotification, PushbulletNotification>();
}
public static void RegisterJobs(this IServiceCollection services)

@ -15,6 +15,7 @@
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
<ProjectReference Include="..\Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj" />
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
<ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" />
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
<ProjectReference Include="..\Ombi.Api.Service\Ombi.Api.Service.csproj" />
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />

@ -10,6 +10,7 @@ namespace Ombi.Mapping.Profiles
{
CreateMap<EmailNotificationsViewModel, EmailNotificationSettings>().ReverseMap();
CreateMap<DiscordNotificationsViewModel, DiscordNotificationSettings>().ReverseMap();
CreateMap<PushbulletNotificationViewModel, PushbulletSettings>().ReverseMap();
}
}
}

@ -0,0 +1,6 @@
namespace Ombi.Notifications.Agents
{
public interface IPushbulletNotification : INotification
{
}
}

@ -0,0 +1,144 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.Pushbullet;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Notifications.Interfaces;
using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
namespace Ombi.Notifications.Agents
{
public class PushbulletNotification : BaseNotification<PushbulletSettings>, IPushbulletNotification
{
public PushbulletNotification(IPushbulletApi api, ISettingsService<PushbulletSettings> sn, ILogger<PushbulletNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t) : base(sn, r, m, t)
{
Api = api;
Logger = log;
}
public override string NotificationName => "PushbulletNotification";
private IPushbulletApi Api { get; }
private ILogger<PushbulletNotification> Logger { get; }
protected override bool ValidateConfiguration(PushbulletSettings settings)
{
if (!settings.Enabled)
{
return false;
}
if (string.IsNullOrEmpty(settings.AccessToken))
{
return false;
}
return true;
}
protected override async Task NewRequest(NotificationOptions model, PushbulletSettings settings)
{
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.NewRequest, model);
var notification = new NotificationMessage
{
Message = parsed.Message,
};
await Send(notification, settings);
}
protected override async Task Issue(NotificationOptions model, PushbulletSettings settings)
{
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.Issue, model);
var notification = new NotificationMessage
{
Message = parsed.Message,
};
await Send(notification, settings);
}
protected override async Task AddedToRequestQueue(NotificationOptions model, PushbulletSettings settings)
{
string user;
string title;
if (model.RequestType == RequestType.Movie)
{
user = MovieRequest.RequestedUser.UserAlias;
title = MovieRequest.Title;
}
else
{
user = TvRequest.RequestedUser.UserAlias;
title = TvRequest.ParentRequest.Title;
}
var message = $"Hello! The user '{user}' has requested {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(NotificationOptions model, PushbulletSettings settings)
{
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestDeclined, model);
var notification = new NotificationMessage
{
Message = parsed.Message,
};
await Send(notification, settings);
}
protected override async Task RequestApproved(NotificationOptions model, PushbulletSettings settings)
{
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestApproved, model);
var notification = new NotificationMessage
{
Message = parsed.Message,
};
await Send(notification, settings);
}
protected override async Task AvailableRequest(NotificationOptions model, PushbulletSettings settings)
{
var parsed = await LoadTemplate(NotificationAgent.Discord, NotificationType.RequestAvailable, model);
var notification = new NotificationMessage
{
Message = parsed.Message,
};
await Send(notification, settings);
}
protected override async Task Send(NotificationMessage model, PushbulletSettings settings)
{
try
{
await Api.Push(settings.AccessToken, model.Subject, model.Message, settings.ChannelTag);
}
catch (Exception e)
{
Logger.LogError(LoggingEvents.DiscordNotification, e, "Failed to send Pushbullet Notification");
}
}
protected override async Task Test(NotificationOptions model, PushbulletSettings 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);
}
}
}

@ -10,6 +10,7 @@
<ItemGroup>
<ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" />
<ProjectReference Include="..\Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj" />
<ProjectReference Include="..\Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj" />
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />

@ -0,0 +1,12 @@
using System;
using Newtonsoft.Json;
namespace Ombi.Settings.Settings.Models.Notifications
{
public class PushbulletSettings : Settings
{
public bool Enabled { get; set; }
public string AccessToken { get; set; }
public string ChannelTag { get; set; }
}
}

@ -67,7 +67,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Notifications.Tests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Service", "Ombi.Api.Service\Ombi.Api.Service.csproj", "{A0892896-F5BD-47E2-823E-DFCE82514EEC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.FanartTv", "Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj", "{FD947E63-A0D2-4878-8378-2005D5E9AB8A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.FanartTv", "Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj", "{FD947E63-A0D2-4878-8378-2005D5E9AB8A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Pushbullet", "Ombi.Api.Pushbullet\Ombi.Api.Pushbullet.csproj", "{E237CDF6-D044-437D-B157-E9A3CC0BCF53}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -171,6 +173,10 @@ Global
{FD947E63-A0D2-4878-8378-2005D5E9AB8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD947E63-A0D2-4878-8378-2005D5E9AB8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD947E63-A0D2-4878-8378-2005D5E9AB8A}.Release|Any CPU.Build.0 = Release|Any CPU
{E237CDF6-D044-437D-B157-E9A3CC0BCF53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E237CDF6-D044-437D-B157-E9A3CC0BCF53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E237CDF6-D044-437D-B157-E9A3CC0BCF53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E237CDF6-D044-437D-B157-E9A3CC0BCF53}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -194,5 +200,6 @@ Global
{2C7836E7-B120-40A6-B641-DDAA02FBAE23} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
{A0892896-F5BD-47E2-823E-DFCE82514EEC} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{FD947E63-A0D2-4878-8378-2005D5E9AB8A} = {9293CA11-360A-4C20-A674-B9E794431BF5}
{E237CDF6-D044-437D-B157-E9A3CC0BCF53} = {9293CA11-360A-4C20-A674-B9E794431BF5}
EndGlobalSection
EndGlobal

@ -48,4 +48,10 @@ export interface IDiscordNotifcationSettings extends INotificationSettings{
webhookUrl : string,
username: string,
notificationTemplates: INotificationTemplates[],
}
export interface IPushbulletNotificationSettings extends INotificationSettings {
accessToken: string,
notificationTemplates: INotificationTemplates[],
channelTag: string;
}

@ -4,7 +4,7 @@ import { Observable } from 'rxjs/Rx';
import { ServiceAuthHelpers } from '../service.helpers';
import { IDiscordNotifcationSettings, IEmailNotificationSettings } from '../../interfaces/INotifcationSettings'
import { IDiscordNotifcationSettings, IEmailNotificationSettings, IPushbulletNotificationSettings } from '../../interfaces/INotifcationSettings'
@Injectable()
@ -17,6 +17,10 @@ export class TesterService extends ServiceAuthHelpers {
return this.http.post(`${this.url}discord`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
}
pushbulletTest(settings: IPushbulletNotificationSettings): Observable<boolean> {
return this.http.post(`${this.url}pushbullet`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
}
emailTest(settings: IEmailNotificationSettings): Observable<boolean> {
return this.http.post(`${this.url}email`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
}

@ -13,7 +13,7 @@ import {
ICustomizationSettings,
IRadarrSettings
} from '../interfaces/ISettings';
import { IEmailNotificationSettings, IDiscordNotifcationSettings } from '../interfaces/INotifcationSettings';
import { IEmailNotificationSettings, IDiscordNotifcationSettings, IPushbulletNotificationSettings } from '../interfaces/INotifcationSettings';
@Injectable()
export class SettingsService extends ServiceAuthHelpers {
@ -96,4 +96,11 @@ export class SettingsService extends ServiceAuthHelpers {
saveDiscordNotificationSettings(settings: IDiscordNotifcationSettings): Observable<boolean> {
return this.httpAuth.post(`${this.url}/notifications/discord`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData).catch(this.handleError)
}
getPushbulletNotificationSettings(): Observable<IPushbulletNotificationSettings> {
return this.httpAuth.get(`${this.url}/notifications/pushbullet`).map(this.extractData).catch(this.handleError)
}
savePushbulletNotificationSettings(settings: IPushbulletNotificationSettings): Observable<boolean> {
return this.httpAuth.post(`${this.url}/notifications/pushbullet`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData).catch(this.handleError)
}
}

@ -0,0 +1,60 @@

<settings-menu></settings-menu>
<div *ngIf="form">
<fieldset>
<legend>Pushbyllet Notifications</legend>
<div class="col-md-6">
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="enable" formControlName="enabled">
<label for="enable">Enabled</label>
</div>
</div>
<div *ngIf="form.invalid && form.dirty" class="alert alert-danger">
<div *ngIf="form.get('accessToken').hasError('required')">The Access Token is required</div>
</div>
<small>You can find this here: <a href="https://www.pushbullet.com/#settings/account">https://www.pushbullet.com/#settings/account </a></small>
<div class="form-group">
<label for="accessToken" class="control-label">Access Token</label>
<div>
<input type="text" class="form-control form-control-custom " id="accessToken" name="accessToken" formControlName="accessToken">
</div>
</div>
<div class="form-group">
<label for="channelTag" class="control-label">Channel Tag</label>
<div>
<input type="text" class="form-control form-control-custom " id="channelTag" name="channelTag" formControlName="channelTag" pTooltip="Optional, this is if you want to send a message to everyone subscribed to a channel">
</div>
</div>
<div class="form-group">
<div>
<button [disabled]="form.invalid" type="button" (click)="test(form)" class="btn btn-primary-outline">
Test
<div id="spinner"></div>
</button>
</div>
</div>
<div class="form-group">
<div>
<button [disabled]="form.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
</div>
</div>
</form>
</div>
<div class="col-md-6">
<notification-templates [templates]="templates"></notification-templates>
</div>
</fieldset>
</div>

@ -0,0 +1,69 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { INotificationTemplates, IPushbulletNotificationSettings, NotificationType } from '../../interfaces/INotifcationSettings';
import { SettingsService } from '../../services/settings.service';
import { NotificationService } from "../../services/notification.service";
import { TesterService } from "../../services/applications/tester.service";
@Component({
templateUrl: './pushbullet.component.html',
})
export class PushbulletComponent implements OnInit {
constructor(private settingsService: SettingsService,
private notificationService: NotificationService,
private fb: FormBuilder,
private testerService : TesterService) { }
NotificationType = NotificationType;
templates: INotificationTemplates[];
form: FormGroup;
ngOnInit(): void {
this.settingsService.getPushbulletNotificationSettings().subscribe(x => {
this.templates = x.notificationTemplates;
this.form = this.fb.group({
enabled: [x.enabled],
channelTag: [x.channelTag],
accessToken: [x.accessToken, [Validators.required]],
});
});
}
onSubmit(form: FormGroup) {
if (form.invalid) {
this.notificationService.error("Validation", "Please check your entered values");
return
}
var settings = <IPushbulletNotificationSettings>form.value;
settings.notificationTemplates = this.templates;
this.settingsService.savePushbulletNotificationSettings(settings).subscribe(x => {
if (x) {
this.notificationService.success("Settings Saved", "Successfully saved the Pushbullet settings");
} else {
this.notificationService.success("Settings Saved", "There was an error when saving the Pushbullet settings");
}
});
}
test(form: FormGroup) {
if (form.invalid) {
this.notificationService.error("Validation", "Please check your entered values");
return
}
this.testerService.pushbulletTest(form.value).subscribe(x => {
if (x) {
this.notificationService.success("Successful", "Successfully sent a Pushbullet message, please check the discord channel");
} else {
this.notificationService.success("Error", "There was an error when sending the Pushbullet message. Please check your settings");
}
})
}
}

@ -24,16 +24,20 @@ namespace Ombi.Controllers.External
/// <param name="service">The service.</param>
/// <param name="notification">The notification.</param>
/// <param name="emailN">The notification.</param>
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN)
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN,
IPushbulletNotification pushbullet)
{
Service = service;
DiscordNotification = notification;
EmailNotification = emailN;
PushbulletNotification = pushbullet;
}
private INotificationService Service { get; }
private IDiscordNotification DiscordNotification { get; }
private IEmailNotification EmailNotification { get; }
private IPushbulletNotification PushbulletNotification { get; }
/// <summary>
/// Sends a test message to discord using the provided settings
@ -45,7 +49,22 @@ namespace Ombi.Controllers.External
{
settings.Enabled = true;
DiscordNotification.NotifyAsync(
new NotificationOptions {NotificationType = NotificationType.Test, RequestId = -1}, settings);
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
return true;
}
/// <summary>
/// Sends a test message to discord using the provided settings
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns></returns>
[HttpPost("pushbullet")]
public bool Pushbullet([FromBody] PushbulletSettings settings)
{
settings.Enabled = true;
PushbulletNotification.NotifyAsync(
new NotificationOptions { NotificationType = NotificationType.Test, RequestId = -1 }, settings);
return true;
}

@ -261,6 +261,40 @@ namespace Ombi.Controllers
return model;
}
/// <summary>
/// Saves the pushbullet notification settings.
/// </summary>
/// <param name="model">The model.</param>
/// <returns></returns>
[HttpPost("notifications/pushbullet")]
public async Task<bool> PushbulletNotificationSettings([FromBody] PushbulletNotificationViewModel model)
{
// Save the email settings
var settings = Mapper.Map<PushbulletSettings>(model);
var result = await Save(settings);
// Save the templates
await TemplateRepository.UpdateRange(model.NotificationTemplates);
return result;
}
/// <summary>
/// Gets the pushbullet Notification Settings.
/// </summary>
/// <returns></returns>
[HttpGet("notifications/pushbullet")]
public async Task<PushbulletNotificationViewModel> PushbulletNotificationSettings()
{
var settings = await Get<PushbulletSettings>();
var model = Mapper.Map<PushbulletNotificationViewModel>(settings);
// Lookup to see if we have any templates saved
model.NotificationTemplates = await BuildTemplates(NotificationAgent.Pushbullet);
return model;
}
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
{
var templates = await TemplateRepository.GetAllTemplates(agent);

Loading…
Cancel
Save