-
-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(cherry picked from commit 59dd3b11271a63ea16f0e32a596dba8e9b9d1096)
- Loading branch information
1 parent
4734cad
commit e199e40
Showing
6 changed files
with
273 additions
and
0 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
src/NzbDrone.Core/Notifications/Signal/InvalidResponseException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System; | ||
|
||
namespace NzbDrone.Core.Notifications.Signal | ||
{ | ||
public class SignalInvalidResponseException : Exception | ||
{ | ||
public SignalInvalidResponseException() | ||
{ | ||
} | ||
|
||
public SignalInvalidResponseException(string message) | ||
: base(message) | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using System.Collections.Generic; | ||
using FluentValidation.Results; | ||
using NzbDrone.Common.Extensions; | ||
|
||
namespace NzbDrone.Core.Notifications.Signal | ||
{ | ||
public class Signal : NotificationBase<SignalSettings> | ||
{ | ||
private readonly ISignalProxy _proxy; | ||
|
||
public Signal(ISignalProxy proxy) | ||
{ | ||
_proxy = proxy; | ||
} | ||
|
||
public override string Name => "Signal"; | ||
public override string Link => "https://signal.org/"; | ||
|
||
public override void OnGrab(GrabMessage grabMessage) | ||
{ | ||
_proxy.SendNotification(BOOK_GRABBED_TITLE, grabMessage.Message, Settings); | ||
} | ||
|
||
public override void OnReleaseImport(BookDownloadMessage message) | ||
{ | ||
_proxy.SendNotification(BOOK_DOWNLOADED_TITLE, message.Message, Settings); | ||
} | ||
|
||
public override void OnAuthorDelete(AuthorDeleteMessage deleteMessage) | ||
{ | ||
_proxy.SendNotification(AUTHOR_DELETED_TITLE, deleteMessage.Message, Settings); | ||
} | ||
|
||
public override void OnBookDelete(BookDeleteMessage deleteMessage) | ||
{ | ||
_proxy.SendNotification(BOOK_DELETED_TITLE, deleteMessage.Message, Settings); | ||
} | ||
|
||
public override void OnBookFileDelete(BookFileDeleteMessage deleteMessage) | ||
{ | ||
_proxy.SendNotification(BOOK_FILE_DELETED_TITLE, deleteMessage.Message, Settings); | ||
} | ||
|
||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck) | ||
{ | ||
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings); | ||
} | ||
|
||
public override void OnDownloadFailure(DownloadFailedMessage message) | ||
{ | ||
_proxy.SendNotification(DOWNLOAD_FAILURE_TITLE, message.Message, Settings); | ||
} | ||
|
||
public override void OnImportFailure(BookDownloadMessage message) | ||
{ | ||
_proxy.SendNotification(IMPORT_FAILURE_TITLE, message.Message, Settings); | ||
} | ||
|
||
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) | ||
{ | ||
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); | ||
} | ||
|
||
public override ValidationResult Test() | ||
{ | ||
var failures = new List<ValidationFailure>(); | ||
|
||
failures.AddIfNotNull(_proxy.Test(Settings)); | ||
|
||
return new ValidationResult(failures); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace NzbDrone.Core.Notifications.Signal | ||
{ | ||
public class SignalError | ||
{ | ||
public string Error { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace NzbDrone.Core.Notifications.Signal | ||
{ | ||
public class SignalPayload | ||
{ | ||
public string Message { get; set; } | ||
public string Number { get; set; } | ||
public string[] Recipients { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
using System; | ||
using System.Net; | ||
using System.Text; | ||
using FluentValidation.Results; | ||
using NLog; | ||
using NzbDrone.Common.Extensions; | ||
using NzbDrone.Common.Http; | ||
using NzbDrone.Common.Serializer; | ||
|
||
namespace NzbDrone.Core.Notifications.Signal | ||
{ | ||
public interface ISignalProxy | ||
{ | ||
void SendNotification(string title, string message, SignalSettings settings); | ||
ValidationFailure Test(SignalSettings settings); | ||
} | ||
|
||
public class SignalProxy : ISignalProxy | ||
{ | ||
private readonly IHttpClient _httpClient; | ||
private readonly Logger _logger; | ||
|
||
public SignalProxy(IHttpClient httpClient, Logger logger) | ||
{ | ||
_httpClient = httpClient; | ||
_logger = logger; | ||
} | ||
|
||
public void SendNotification(string title, string message, SignalSettings settings) | ||
{ | ||
var text = new StringBuilder(); | ||
text.AppendLine(title); | ||
text.AppendLine(message); | ||
|
||
var urlSignalAPI = HttpRequestBuilder.BuildBaseUrl( | ||
settings.UseSsl, | ||
settings.Host, | ||
settings.Port, | ||
"/v2/send"); | ||
|
||
var requestBuilder = new HttpRequestBuilder(urlSignalAPI).Post(); | ||
|
||
if (settings.AuthUsername.IsNotNullOrWhiteSpace() && settings.AuthPassword.IsNotNullOrWhiteSpace()) | ||
{ | ||
requestBuilder.NetworkCredential = new BasicNetworkCredential(settings.AuthUsername, settings.AuthPassword); | ||
} | ||
|
||
var request = requestBuilder.Build(); | ||
|
||
request.Headers.ContentType = "application/json"; | ||
|
||
var payload = new SignalPayload | ||
{ | ||
Message = text.ToString(), | ||
Number = settings.SenderNumber, | ||
Recipients = new[] { settings.ReceiverId } | ||
}; | ||
request.SetContent(payload.ToJson()); | ||
_httpClient.Post(request); | ||
} | ||
|
||
public ValidationFailure Test(SignalSettings settings) | ||
{ | ||
try | ||
{ | ||
const string title = "Test Notification"; | ||
const string body = "This is a test message from Readarr"; | ||
|
||
SendNotification(title, body, settings); | ||
} | ||
catch (WebException ex) | ||
{ | ||
_logger.Error(ex, "Unable to send test message: {0}", ex.Message); | ||
return new ValidationFailure("Host", $"Unable to send test message: {ex.Message}"); | ||
} | ||
catch (HttpException ex) | ||
{ | ||
_logger.Error(ex, "Unable to send test message: {0}", ex.Message); | ||
|
||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest) | ||
{ | ||
if (ex.Response.Content.ContainsIgnoreCase("400 The plain HTTP request was sent to HTTPS port")) | ||
{ | ||
return new ValidationFailure("UseSsl", "SSL seems to be required"); | ||
} | ||
|
||
var error = Json.Deserialize<SignalError>(ex.Response.Content); | ||
|
||
var property = "Host"; | ||
|
||
if (error.Error.ContainsIgnoreCase("Invalid group id")) | ||
{ | ||
property = "ReceiverId"; | ||
} | ||
else if (error.Error.ContainsIgnoreCase("Invalid account")) | ||
{ | ||
property = "SenderNumber"; | ||
} | ||
|
||
return new ValidationFailure(property, $"Unable to send test message: {error.Error}"); | ||
} | ||
|
||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized) | ||
{ | ||
return new ValidationFailure("AuthUsername", "Login/Password invalid"); | ||
} | ||
|
||
return new ValidationFailure("Host", $"Unable to send test message: {ex.Message}"); | ||
} | ||
catch (Exception ex) | ||
{ | ||
_logger.Error(ex, "Unable to send test message: {0}", ex.Message); | ||
return new ValidationFailure("Host", $"Unable to send test message: {ex.Message}"); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using FluentValidation; | ||
using NzbDrone.Core.Annotations; | ||
using NzbDrone.Core.ThingiProvider; | ||
using NzbDrone.Core.Validation; | ||
|
||
namespace NzbDrone.Core.Notifications.Signal | ||
{ | ||
public class SignalSettingsValidator : AbstractValidator<SignalSettings> | ||
{ | ||
public SignalSettingsValidator() | ||
{ | ||
RuleFor(c => c.Host).NotEmpty(); | ||
RuleFor(c => c.Port).NotEmpty(); | ||
RuleFor(c => c.SenderNumber).NotEmpty(); | ||
RuleFor(c => c.ReceiverId).NotEmpty(); | ||
} | ||
} | ||
|
||
public class SignalSettings : IProviderConfig | ||
{ | ||
private static readonly SignalSettingsValidator Validator = new (); | ||
|
||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "localhost")] | ||
public string Host { get; set; } | ||
|
||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "8080")] | ||
public int Port { get; set; } | ||
|
||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection.")] | ||
public bool UseSsl { get; set; } | ||
|
||
[FieldDefinition(3, Label = "Sender Number", Privacy = PrivacyLevel.ApiKey, HelpText = "Phone number of the sender register in signal-api")] | ||
public string SenderNumber { get; set; } | ||
|
||
[FieldDefinition(4, Label = "Group ID / PhoneNumber", HelpText = "GroupID / PhoneNumber of the receiver")] | ||
public string ReceiverId { get; set; } | ||
|
||
[FieldDefinition(5, Label = "Login", Privacy = PrivacyLevel.UserName, HelpText = "Username used to authenticate requests toward signal-api")] | ||
public string AuthUsername { get; set; } | ||
|
||
[FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "Password used to authenticate requests toward signal-api")] | ||
public string AuthPassword { get; set; } | ||
|
||
public NzbDroneValidationResult Validate() | ||
{ | ||
return new NzbDroneValidationResult(Validator.Validate(this)); | ||
} | ||
} | ||
} |