diff --git a/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js b/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js index 6d90961b0..bd11b0327 100644 --- a/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js +++ b/frontend/src/Settings/Notifications/Notifications/AddNotificationItem.js @@ -1,10 +1,10 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { sizes } from 'Helpers/Props'; import Button from 'Components/Link/Button'; import Link from 'Components/Link/Link'; import Menu from 'Components/Menu/Menu'; import MenuContent from 'Components/Menu/MenuContent'; +import { sizes } from 'Helpers/Props'; import AddNotificationPresetMenuItem from './AddNotificationPresetMenuItem'; import styles from './AddNotificationItem.css'; @@ -90,7 +90,7 @@ class AddNotificationItem extends Component { to={infoLink} size={sizes.SMALL} > - More info + More Info diff --git a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.css b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.css index 5ecf6f5d3..8e1c16507 100644 --- a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.css +++ b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.css @@ -9,12 +9,3 @@ margin-bottom: 30px; } - -.triggers { - margin-top: 3px; -} - -.triggerEvents { - margin-top: 10px; - user-select: none; -} diff --git a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js index e841ca06e..cfef4dc21 100644 --- a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js +++ b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js @@ -1,20 +1,20 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { inputTypes, kinds } from 'Helpers/Props'; import Alert from 'Components/Alert'; +import Form from 'Components/Form/Form'; +import FormGroup from 'Components/Form/FormGroup'; +import FormInputGroup from 'Components/Form/FormInputGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup'; import Button from 'Components/Link/Button'; import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton'; import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -import ModalContent from 'Components/Modal/ModalContent'; -import ModalHeader from 'Components/Modal/ModalHeader'; import ModalBody from 'Components/Modal/ModalBody'; +import ModalContent from 'Components/Modal/ModalContent'; import ModalFooter from 'Components/Modal/ModalFooter'; -import Form from 'Components/Form/Form'; -import FormGroup from 'Components/Form/FormGroup'; -import FormLabel from 'Components/Form/FormLabel'; -import FormInputGroup from 'Components/Form/FormInputGroup'; -import FormInputHelpText from 'Components/Form/FormInputHelpText'; -import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup'; +import ModalHeader from 'Components/Modal/ModalHeader'; +import { inputTypes, kinds } from 'Helpers/Props'; +import NotificationEventItems from './NotificationEventItems'; import styles from './EditNotificationModalContent.css'; function EditNotificationModalContent(props) { @@ -39,23 +39,6 @@ function EditNotificationModalContent(props) { id, implementationName, name, - onGrab, - onDownload, - onUpgrade, - onRename, - onSeriesDelete, - onEpisodeFileDelete, - onEpisodeFileDeleteForUpgrade, - onHealthIssue, - supportsOnGrab, - supportsOnDownload, - supportsOnUpgrade, - supportsOnRename, - supportsOnSeriesDelete, - supportsOnEpisodeFileDelete, - supportsOnEpisodeFileDeleteForUpgrade, - supportsOnHealthIssue, - includeHealthWarnings, tags, fields, message @@ -75,7 +58,9 @@ function EditNotificationModalContent(props) { { !isFetching && !!error && -
Unable to add a new notification, please try again.
+
+ Unable to add a new notification, please try again. +
} { @@ -102,111 +87,10 @@ function EditNotificationModalContent(props) { /> - - Triggers - -
- - -
- - - - - { - onDownload.value ? - : - null - } - - - - - - - - { - onEpisodeFileDelete.value ? - : - null - } - - - - { - onHealthIssue.value ? - : - null - } -
-
-
+ Tags diff --git a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContentConnector.js b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContentConnector.js index 104f1897a..504f18663 100644 --- a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContentConnector.js +++ b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContentConnector.js @@ -2,8 +2,8 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; +import { saveNotification, setNotificationFieldValue, setNotificationValue, testNotification } from 'Store/Actions/settingsActions'; import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector'; -import { setNotificationValue, setNotificationFieldValue, saveNotification, testNotification } from 'Store/Actions/settingsActions'; import EditNotificationModalContent from './EditNotificationModalContent'; function createMapStateToProps() { diff --git a/frontend/src/Settings/Notifications/Notifications/Notification.js b/frontend/src/Settings/Notifications/Notifications/Notification.js index 7b317d313..89108bd23 100644 --- a/frontend/src/Settings/Notifications/Notifications/Notification.js +++ b/frontend/src/Settings/Notifications/Notifications/Notification.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { kinds } from 'Helpers/Props'; import Card from 'Components/Card'; import Label from 'Components/Label'; import ConfirmModal from 'Components/Modal/ConfirmModal'; +import { kinds } from 'Helpers/Props'; import EditNotificationModalConnector from './EditNotificationModalConnector'; import styles from './Notification.css'; @@ -39,7 +39,7 @@ class Notification extends Component { }); } - onDeleteNotificationModalClose= () => { + onDeleteNotificationModalClose = () => { this.setState({ isDeleteNotificationModalOpen: false }); } @@ -62,6 +62,7 @@ class Notification extends Component { onEpisodeFileDelete, onEpisodeFileDeleteForUpgrade, onHealthIssue, + onApplicationUpdate, supportsOnGrab, supportsOnDownload, supportsOnUpgrade, @@ -69,7 +70,8 @@ class Notification extends Component { supportsOnSeriesDelete, supportsOnEpisodeFileDelete, supportsOnEpisodeFileDeleteForUpgrade, - supportsOnHealthIssue + supportsOnHealthIssue, + supportsOnApplicationUpdate } = this.props; return ( @@ -122,6 +124,14 @@ class Notification extends Component { null } + { + supportsOnApplicationUpdate && onApplicationUpdate ? + : + null + } + { supportsOnSeriesDelete && onSeriesDelete ? + ); +} + +NotificationEventItems.propTypes = { + item: PropTypes.object.isRequired, + onInputChange: PropTypes.func.isRequired +}; + +export default NotificationEventItems; diff --git a/frontend/src/Settings/Notifications/Notifications/Notifications.js b/frontend/src/Settings/Notifications/Notifications/Notifications.js index 77a7c11e8..870a66e2a 100644 --- a/frontend/src/Settings/Notifications/Notifications/Notifications.js +++ b/frontend/src/Settings/Notifications/Notifications/Notifications.js @@ -1,13 +1,13 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { icons } from 'Helpers/Props'; -import FieldSet from 'Components/FieldSet'; import Card from 'Components/Card'; +import FieldSet from 'Components/FieldSet'; import Icon from 'Components/Icon'; import PageSectionContent from 'Components/Page/PageSectionContent'; -import Notification from './Notification'; +import { icons } from 'Helpers/Props'; import AddNotificationModal from './AddNotificationModal'; import EditNotificationModalConnector from './EditNotificationModalConnector'; +import Notification from './Notification'; import styles from './Notifications.css'; class Notifications extends Component { diff --git a/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js b/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js index 6f4138627..6191d76f8 100644 --- a/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js +++ b/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js @@ -2,9 +2,9 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import sortByName from 'Utilities/Array/sortByName'; +import { deleteNotification, fetchNotifications } from 'Store/Actions/settingsActions'; import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector'; -import { fetchNotifications, deleteNotification } from 'Store/Actions/settingsActions'; +import sortByName from 'Utilities/Array/sortByName'; import Notifications from './Notifications'; function createMapStateToProps() { diff --git a/frontend/src/Store/Actions/Settings/notifications.js b/frontend/src/Store/Actions/Settings/notifications.js index 191fd95ef..b7f97abdb 100644 --- a/frontend/src/Store/Actions/Settings/notifications.js +++ b/frontend/src/Store/Actions/Settings/notifications.js @@ -109,6 +109,7 @@ export default { selectedSchema.onSeriesDelete = selectedSchema.supportsOnSeriesDelete; selectedSchema.onEpisodeFileDelete = selectedSchema.supportsOnEpisodeFileDelete; selectedSchema.onEpisodeFileDeleteForUpgrade = selectedSchema.supportsOnEpisodeFileDeleteForUpgrade; + selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate; return selectedSchema; }); diff --git a/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs b/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs index 67327a852..6beb3e805 100644 --- a/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs +++ b/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs @@ -79,6 +79,11 @@ namespace NzbDrone.Core.Test.NotificationTests TestLogger.Info("OnHealthIssue was called"); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + TestLogger.Info("OnApplicationUpdate was called"); + } + } class TestNotificationWithNoEvents : NotificationBase @@ -92,7 +97,7 @@ namespace NzbDrone.Core.Test.NotificationTests throw new NotImplementedException(); } - + } [Test] @@ -120,6 +125,7 @@ namespace NzbDrone.Core.Test.NotificationTests notification.SupportsOnEpisodeFileDelete.Should().BeTrue(); notification.SupportsOnEpisodeFileDeleteForUpgrade.Should().BeTrue(); notification.SupportsOnHealthIssue.Should().BeTrue(); + notification.SupportsOnApplicationUpdate.Should().BeTrue(); } @@ -136,6 +142,7 @@ namespace NzbDrone.Core.Test.NotificationTests notification.SupportsOnEpisodeFileDelete.Should().BeFalse(); notification.SupportsOnEpisodeFileDeleteForUpgrade.Should().BeFalse(); notification.SupportsOnHealthIssue.Should().BeFalse(); + notification.SupportsOnApplicationUpdate.Should().BeFalse(); } } diff --git a/src/NzbDrone.Core/Datastore/Migration/165_add_on_update_to_notifications.cs b/src/NzbDrone.Core/Datastore/Migration/165_add_on_update_to_notifications.cs new file mode 100644 index 000000000..338e4efc1 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/165_add_on_update_to_notifications.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(165)] + public class add_on_update_to_notifications : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Notifications").AddColumn("OnApplicationUpdate").AsBoolean().WithDefaultValue(0); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 5a0de021c..4825a5760 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -76,7 +76,8 @@ namespace NzbDrone.Core.Datastore .Ignore(i => i.SupportsOnSeriesDelete) .Ignore(i => i.SupportsOnEpisodeFileDelete) .Ignore(i => i.SupportsOnEpisodeFileDeleteForUpgrade) - .Ignore(i => i.SupportsOnHealthIssue); + .Ignore(i => i.SupportsOnHealthIssue) + .Ignore(i => i.SupportsOnApplicationUpdate); Mapper.Entity().RegisterDefinition("Metadata") .Ignore(d => d.Tags); diff --git a/src/NzbDrone.Core/Notifications/ApplicationUpdateMessage.cs b/src/NzbDrone.Core/Notifications/ApplicationUpdateMessage.cs new file mode 100644 index 000000000..1819ad423 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/ApplicationUpdateMessage.cs @@ -0,0 +1,16 @@ +using System; + +namespace NzbDrone.Core.Notifications +{ + public class ApplicationUpdateMessage + { + public string Message { get; set; } + public Version PreviousVersion { get; set; } + public Version NewVersion { get; set; } + + public override string ToString() + { + return NewVersion.ToString(); + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Boxcar/Boxcar.cs b/src/NzbDrone.Core/Notifications/Boxcar/Boxcar.cs index 326bc85af..ba4b5b739 100644 --- a/src/NzbDrone.Core/Notifications/Boxcar/Boxcar.cs +++ b/src/NzbDrone.Core/Notifications/Boxcar/Boxcar.cs @@ -23,7 +23,7 @@ namespace NzbDrone.Core.Notifications.Boxcar public override void OnDownload(DownloadMessage message) { - _proxy.SendNotification(EPISODE_DOWNLOADED_TITLE , message.Message, Settings); + _proxy.SendNotification(EPISODE_DOWNLOADED_TITLE, message.Message, Settings); } public override void OnEpisodeFileDelete(EpisodeDeleteMessage deleteMessage) @@ -41,6 +41,10 @@ namespace NzbDrone.Core.Notifications.Boxcar _proxy.SendNotification(HEALTH_ISSUE_TITLE, message.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage message) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE, message.Message, Settings); + } public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs index e70d861b1..01e4f3dba 100644 --- a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs @@ -198,6 +198,18 @@ namespace NzbDrone.Core.Notifications.CustomScript ExecuteScript(environmentVariables); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + var environmentVariables = new StringDictionary(); + + environmentVariables.Add("Sonarr_EventType", "ApplicationUpdate"); + environmentVariables.Add("Sonarr_Update_Message", updateMessage.Message); + environmentVariables.Add("Sonarr_Update_NewVersion", updateMessage.NewVersion.ToString()); + environmentVariables.Add("Sonarr_Update_PreviousVersion", updateMessage.PreviousVersion.ToString()); + + ExecuteScript(environmentVariables); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Discord/Discord.cs b/src/NzbDrone.Core/Notifications/Discord/Discord.cs index e3af799c6..fb382c508 100644 --- a/src/NzbDrone.Core/Notifications/Discord/Discord.cs +++ b/src/NzbDrone.Core/Notifications/Discord/Discord.cs @@ -295,6 +295,40 @@ namespace NzbDrone.Core.Notifications.Discord _proxy.SendPayload(payload, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + var attachments = new List + { + new Embed + { + Author = new DiscordAuthor + { + Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author, + IconUrl = "https://raw.githubusercontent.com/Sonarr/Sonarr/develop/Logo/256.png" + }, + Title = APPLICATION_UPDATE_TITLE, + Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), + Color = (int)DiscordColors.Standard, + Fields = new List() + { + new DiscordField() + { + Name = "Previous Version", + Value = updateMessage.PreviousVersion.ToString() + }, + new DiscordField() + { + Name = "New Version", + Value = updateMessage.NewVersion.ToString() + } + }, + } + }; + + var payload = CreatePayload(null, attachments); + + _proxy.SendPayload(payload, Settings); + } public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Email/Email.cs b/src/NzbDrone.Core/Notifications/Email/Email.cs index 033c42455..a58e17264 100644 --- a/src/NzbDrone.Core/Notifications/Email/Email.cs +++ b/src/NzbDrone.Core/Notifications/Email/Email.cs @@ -57,6 +57,12 @@ namespace NzbDrone.Core.Notifications.Email SendEmail(Settings, HEALTH_ISSUE_TITLE_BRANDED, message.Message); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + var body = $"{updateMessage.Message}"; + + SendEmail(Settings, APPLICATION_UPDATE_TITLE_BRANDED, body); + } public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs b/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs index 146b19696..7de40ce31 100644 --- a/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs +++ b/src/NzbDrone.Core/Notifications/Gotify/Gotify.cs @@ -44,6 +44,11 @@ namespace NzbDrone.Core.Notifications.Gotify _proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/INotification.cs b/src/NzbDrone.Core/Notifications/INotification.cs index f9639d897..92035f3f2 100644 --- a/src/NzbDrone.Core/Notifications/INotification.cs +++ b/src/NzbDrone.Core/Notifications/INotification.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.Notifications void OnEpisodeFileDelete(EpisodeDeleteMessage deleteMessage); void OnSeriesDelete(SeriesDeleteMessage deleteMessage); void OnHealthIssue(HealthCheck.HealthCheck healthCheck); + void OnApplicationUpdate(ApplicationUpdateMessage updateMessage); void ProcessQueue(); bool SupportsOnGrab { get; } bool SupportsOnDownload { get; } @@ -24,5 +25,6 @@ namespace NzbDrone.Core.Notifications bool SupportsOnEpisodeFileDelete { get; } bool SupportsOnEpisodeFileDeleteForUpgrade { get; } bool SupportsOnHealthIssue { get; } + bool SupportsOnApplicationUpdate { get; } } } diff --git a/src/NzbDrone.Core/Notifications/Join/Join.cs b/src/NzbDrone.Core/Notifications/Join/Join.cs index a81c4d123..2f9df88e6 100644 --- a/src/NzbDrone.Core/Notifications/Join/Join.cs +++ b/src/NzbDrone.Core/Notifications/Join/Join.cs @@ -41,6 +41,11 @@ namespace NzbDrone.Core.Notifications.Join _proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, message.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs b/src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs index 2c865654b..44651262c 100644 --- a/src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs +++ b/src/NzbDrone.Core/Notifications/Mailgun/Mailgun.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.Notifications.Mailgun { private readonly IMailgunProxy _proxy; private readonly Logger _logger; - + public MailGun(IMailgunProxy proxy, Logger logger) { _proxy = proxy; @@ -48,6 +48,10 @@ namespace NzbDrone.Core.Notifications.Mailgun _proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheckMessage.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); + } public override ValidationResult Test() { @@ -66,7 +70,7 @@ namespace NzbDrone.Core.Notifications.Mailgun _logger.Error(ex, "Unable to send test message though Mailgun."); failures.Add(new ValidationFailure("", "Unable to send test message though Mailgun.")); } - + return new ValidationResult(failures); } } diff --git a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs index 27347431c..41d9af18f 100644 --- a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs +++ b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs @@ -82,6 +82,14 @@ namespace NzbDrone.Core.Notifications.Emby } } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + if (Settings.Notify) + { + _mediaBrowserService.Notify(Settings, APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message); + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/NotificationBase.cs b/src/NzbDrone.Core/Notifications/NotificationBase.cs index 81a5ac767..8cffdea17 100644 --- a/src/NzbDrone.Core/Notifications/NotificationBase.cs +++ b/src/NzbDrone.Core/Notifications/NotificationBase.cs @@ -15,12 +15,14 @@ namespace NzbDrone.Core.Notifications protected const string EPISODE_DELETED_TITLE = "Episode Deleted"; protected const string SERIES_DELETED_TITLE = "Series Deleted"; protected const string HEALTH_ISSUE_TITLE = "Health Check Failure"; + protected const string APPLICATION_UPDATE_TITLE = "Application Updated"; protected const string EPISODE_GRABBED_TITLE_BRANDED = "Sonarr - " + EPISODE_GRABBED_TITLE; protected const string EPISODE_DOWNLOADED_TITLE_BRANDED = "Sonarr - " + EPISODE_DOWNLOADED_TITLE; protected const string EPISODE_DELETED_TITLE_BRANDED = "Sonarr - " + EPISODE_DELETED_TITLE; protected const string SERIES_DELETED_TITLE_BRANDED = "Sonarr - " + SERIES_DELETED_TITLE; protected const string HEALTH_ISSUE_TITLE_BRANDED = "Sonarr - " + HEALTH_ISSUE_TITLE; + protected const string APPLICATION_UPDATE_TITLE_BRANDED = "Sonarr - " + APPLICATION_UPDATE_TITLE; public abstract string Name { get; } @@ -65,6 +67,10 @@ namespace NzbDrone.Core.Notifications } + public virtual void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + } + public virtual void ProcessQueue() { @@ -78,6 +84,7 @@ namespace NzbDrone.Core.Notifications public bool SupportsOnEpisodeFileDelete => HasConcreteImplementation("OnEpisodeFileDelete"); public bool SupportsOnEpisodeFileDeleteForUpgrade => SupportsOnEpisodeFileDelete; public bool SupportsOnHealthIssue => HasConcreteImplementation("OnHealthIssue"); + public bool SupportsOnApplicationUpdate => HasConcreteImplementation("OnApplicationUpdate"); protected TSettings Settings => (TSettings)Definition.Settings; diff --git a/src/NzbDrone.Core/Notifications/NotificationDefinition.cs b/src/NzbDrone.Core/Notifications/NotificationDefinition.cs index c5d9f23c7..3e929fbd1 100644 --- a/src/NzbDrone.Core/Notifications/NotificationDefinition.cs +++ b/src/NzbDrone.Core/Notifications/NotificationDefinition.cs @@ -12,6 +12,7 @@ namespace NzbDrone.Core.Notifications public bool OnEpisodeFileDelete { get; set; } public bool OnEpisodeFileDeleteForUpgrade { get; set; } public bool OnHealthIssue { get; set; } + public bool OnApplicationUpdate { get; set; } public bool SupportsOnGrab { get; set; } public bool SupportsOnDownload { get; set; } public bool SupportsOnUpgrade { get; set; } @@ -21,7 +22,8 @@ namespace NzbDrone.Core.Notifications public bool SupportsOnEpisodeFileDeleteForUpgrade { get; set; } public bool SupportsOnHealthIssue { get; set; } public bool IncludeHealthWarnings { get; set; } + public bool SupportsOnApplicationUpdate { get; set; } - public override bool Enable => OnGrab || OnDownload || (OnDownload && OnUpgrade) || OnSeriesDelete || OnEpisodeFileDelete || OnEpisodeFileDeleteForUpgrade || OnHealthIssue; + public override bool Enable => OnGrab || OnDownload || (OnDownload && OnUpgrade) || OnSeriesDelete || OnEpisodeFileDelete || OnEpisodeFileDeleteForUpgrade || OnHealthIssue || OnApplicationUpdate; } } diff --git a/src/NzbDrone.Core/Notifications/NotificationFactory.cs b/src/NzbDrone.Core/Notifications/NotificationFactory.cs index 5591ea4dc..b4b366a94 100644 --- a/src/NzbDrone.Core/Notifications/NotificationFactory.cs +++ b/src/NzbDrone.Core/Notifications/NotificationFactory.cs @@ -17,6 +17,7 @@ namespace NzbDrone.Core.Notifications List OnEpisodeFileDeleteEnabled(); List OnEpisodeFileDeleteForUpgradeEnabled(); List OnHealthIssueEnabled(); + List OnApplicationUpdateEnabled(); } public class NotificationFactory : ProviderFactory, INotificationFactory @@ -65,6 +66,11 @@ namespace NzbDrone.Core.Notifications return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnHealthIssue).ToList(); } + public List OnApplicationUpdateEnabled() + { + return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnApplicationUpdate).ToList(); + } + public override void SetProviderCharacteristics(INotification provider, NotificationDefinition definition) { base.SetProviderCharacteristics(provider, definition); @@ -77,6 +83,7 @@ namespace NzbDrone.Core.Notifications definition.SupportsOnEpisodeFileDelete = provider.SupportsOnEpisodeFileDelete; definition.SupportsOnEpisodeFileDeleteForUpgrade = provider.SupportsOnEpisodeFileDeleteForUpgrade; definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue; + definition.SupportsOnApplicationUpdate = provider.SupportsOnApplicationUpdate; } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Notifications/NotificationService.cs b/src/NzbDrone.Core/Notifications/NotificationService.cs index df520c1b8..8e469204c 100644 --- a/src/NzbDrone.Core/Notifications/NotificationService.cs +++ b/src/NzbDrone.Core/Notifications/NotificationService.cs @@ -11,6 +11,7 @@ using NzbDrone.Core.Qualities; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Events; +using NzbDrone.Core.Update.History.Events; namespace NzbDrone.Core.Notifications { @@ -21,6 +22,7 @@ namespace NzbDrone.Core.Notifications IHandle, IHandle, IHandle, + IHandle, IHandleAsync, IHandleAsync, IHandleAsync, @@ -193,6 +195,26 @@ namespace NzbDrone.Core.Notifications } } + public void Handle(UpdateInstalledEvent message) + { + var updateMessage = new ApplicationUpdateMessage(); + updateMessage.Message = $"Sonarr updated from {message.PreviousVerison.ToString()} to {message.NewVersion.ToString()}"; + updateMessage.PreviousVersion = message.PreviousVerison; + updateMessage.NewVersion = message.NewVersion; + + foreach (var notification in _notificationFactory.OnApplicationUpdateEnabled()) + { + try + { + notification.OnApplicationUpdate(updateMessage); + } + catch (Exception ex) + { + _logger.Warn(ex, "Unable to send OnApplicationUpdate notification to: " + notification.Definition.Name); + } + } + } + public void Handle(EpisodeFileDeletedEvent message) { if (message.EpisodeFile.Episodes.Value.Empty()) @@ -229,7 +251,7 @@ namespace NzbDrone.Core.Notifications public void Handle(SeriesDeletedEvent message) { - var deleteMessage = new SeriesDeleteMessage(message.Series,message.DeleteFiles); + var deleteMessage = new SeriesDeleteMessage(message.Series, message.DeleteFiles); foreach (var notification in _notificationFactory.OnSeriesDeleteEnabled()) { diff --git a/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs b/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs index acc1b0255..ee8a8fa8a 100644 --- a/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs +++ b/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs @@ -41,6 +41,11 @@ namespace NzbDrone.Core.Notifications.Prowl _prowlProxy.SendNotification(HEALTH_ISSUE_TITLE, message.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _prowlProxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs b/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs index b97771e48..e5eb34a29 100644 --- a/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs +++ b/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs @@ -43,6 +43,11 @@ namespace NzbDrone.Core.Notifications.PushBullet _proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings); + } + public override ValidationResult Test() { var failures = new List(); @@ -60,23 +65,23 @@ namespace NzbDrone.Core.Notifications.PushBullet if (Settings.ApiKey.IsNullOrWhiteSpace()) { return new - { - devices = new List() - }; + { + devices = new List() + }; } Settings.Validate().Filter("ApiKey").ThrowOnError(); var devices = _proxy.GetDevices(Settings); return new - { - options = devices.Where(d => d.Nickname.IsNotNullOrWhiteSpace()) + { + options = devices.Where(d => d.Nickname.IsNotNullOrWhiteSpace()) .OrderBy(d => d.Nickname, StringComparer.InvariantCultureIgnoreCase) .Select(d => new - { - id = d.Id, - name = d.Nickname - }) + { + id = d.Id, + name = d.Nickname + }) }; } diff --git a/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs b/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs index 5f481ad28..c640e3841 100644 --- a/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs +++ b/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.Notifications.Pushover public class Pushover : NotificationBase { private readonly IPushoverProxy _proxy; - + public Pushover(IPushoverProxy proxy) { _proxy = proxy; @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Notifications.Pushover { _proxy.SendNotification(EPISODE_DELETED_TITLE, deleteMessage.Message, Settings); } - + public override void OnSeriesDelete(SeriesDeleteMessage deleteMessage) { _proxy.SendNotification(SERIES_DELETED_TITLE, deleteMessage.Message, Settings); @@ -41,6 +41,11 @@ namespace NzbDrone.Core.Notifications.Pushover _proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/SendGrid/SendGrid.cs b/src/NzbDrone.Core/Notifications/SendGrid/SendGrid.cs index 0969c8894..265f4d1be 100644 --- a/src/NzbDrone.Core/Notifications/SendGrid/SendGrid.cs +++ b/src/NzbDrone.Core/Notifications/SendGrid/SendGrid.cs @@ -43,6 +43,11 @@ namespace NzbDrone.Core.Notifications.SendGrid _proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Slack/Slack.cs b/src/NzbDrone.Core/Notifications/Slack/Slack.cs index 49efa3f55..210a22dcc 100644 --- a/src/NzbDrone.Core/Notifications/Slack/Slack.cs +++ b/src/NzbDrone.Core/Notifications/Slack/Slack.cs @@ -120,6 +120,23 @@ namespace NzbDrone.Core.Notifications.Slack _proxy.SendPayload(payload, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + var attachments = new List + { + new Attachment + { + Title = Environment.MachineName, + Text = updateMessage.Message, + Color = "good" + } + }; + + var payload = CreatePayload("Application Updated", attachments); + + _proxy.SendPayload(payload, Settings); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Telegram/Telegram.cs b/src/NzbDrone.Core/Notifications/Telegram/Telegram.cs index 3cd5109ee..616cc1342 100644 --- a/src/NzbDrone.Core/Notifications/Telegram/Telegram.cs +++ b/src/NzbDrone.Core/Notifications/Telegram/Telegram.cs @@ -41,6 +41,11 @@ namespace NzbDrone.Core.Notifications.Telegram _proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Twitter/Twitter.cs b/src/NzbDrone.Core/Notifications/Twitter/Twitter.cs index 71c108c2f..1774b25fd 100644 --- a/src/NzbDrone.Core/Notifications/Twitter/Twitter.cs +++ b/src/NzbDrone.Core/Notifications/Twitter/Twitter.cs @@ -44,6 +44,11 @@ namespace NzbDrone.Core.Notifications.Twitter _twitterService.SendNotification($"Health Issue: {healthCheck.Message}", Settings); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + _twitterService.SendNotification($"Application Updated: {updateMessage.Message}", Settings); + } + public override object RequestAction(string action, IDictionary query) { if (action == "startOAuth") diff --git a/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs b/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs index 8ae6cdb57..ea5f11ac4 100644 --- a/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs @@ -56,10 +56,10 @@ namespace NzbDrone.Core.Notifications.Webhook if (message.OldFiles.Any()) { payload.DeletedFiles = message.OldFiles.ConvertAll(x => new WebhookEpisodeFile(x) - { - Path = Path.Combine(message.Series.Path, + { + Path = Path.Combine(message.Series.Path, x.RelativePath) - } + } ); } @@ -86,7 +86,7 @@ namespace NzbDrone.Core.Notifications.Webhook Series = new WebhookSeries(deleteMessage.Series), Episodes = deleteMessage.EpisodeFile.Episodes.Value.ConvertAll(x => new WebhookEpisode(x)), EpisodeFile = deleteMessage.EpisodeFile, - DeleteReason = deleteMessage.Reason + DeleteReason = deleteMessage.Reason }; _proxy.SendWebhook(payload, Settings); @@ -107,13 +107,26 @@ namespace NzbDrone.Core.Notifications.Webhook public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck) { var payload = new WebhookHealthPayload - { - EventType = WebhookEventType.Health, - Level = healthCheck.Type, - Message = healthCheck.Message, - Type = healthCheck.Source.Name, - WikiUrl = healthCheck.WikiUrl?.ToString() - }; + { + EventType = WebhookEventType.Health, + Level = healthCheck.Type, + Message = healthCheck.Message, + Type = healthCheck.Source.Name, + WikiUrl = healthCheck.WikiUrl?.ToString() + }; + + _proxy.SendWebhook(payload, Settings); + } + + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + var payload = new WebhookApplicationUpdatePayload + { + EventType = WebhookEventType.ApplicationUpdate, + Message = updateMessage.Message, + PreviousVersion = updateMessage.PreviousVersion.ToString(), + NewVersion = updateMessage.NewVersion.ToString() + }; _proxy.SendWebhook(payload, Settings); } @@ -134,16 +147,16 @@ namespace NzbDrone.Core.Notifications.Webhook try { var payload = new WebhookGrabPayload + { + EventType = WebhookEventType.Test, + Series = new WebhookSeries() { - EventType = WebhookEventType.Test, - Series = new WebhookSeries() - { - Id = 1, - Title = "Test Title", - Path = "C:\\testpath", - TvdbId = 1234 - }, - Episodes = new List() { + Id = 1, + Title = "Test Title", + Path = "C:\\testpath", + TvdbId = 1234 + }, + Episodes = new List() { new WebhookEpisode() { Id = 123, @@ -152,7 +165,7 @@ namespace NzbDrone.Core.Notifications.Webhook Title = "Test title" } } - }; + }; _proxy.SendWebhook(payload, Settings); } diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookApplicationUpdatePayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookApplicationUpdatePayload.cs new file mode 100644 index 000000000..e05be69bc --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookApplicationUpdatePayload.cs @@ -0,0 +1,11 @@ +using NzbDrone.Core.HealthCheck; + +namespace NzbDrone.Core.Notifications.Webhook +{ + public class WebhookApplicationUpdatePayload : WebhookPayload + { + public string Message { get; set; } + public string PreviousVersion { get; set; } + public string NewVersion { get; set; } + } +} diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookEventType.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookEventType.cs index 492fafc9a..008dc5ee8 100644 --- a/src/NzbDrone.Core/Notifications/Webhook/WebhookEventType.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookEventType.cs @@ -14,6 +14,7 @@ namespace NzbDrone.Core.Notifications.Webhook Rename, SeriesDelete, EpisodeFileDelete, - Health + Health, + ApplicationUpdate } } diff --git a/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs b/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs index 762ad6e1c..5e2ee5413 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs @@ -66,6 +66,11 @@ namespace NzbDrone.Core.Notifications.Xbmc Notify(Settings, HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message); } + public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage) + { + Notify(Settings, APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message); + } + public override string Name => "Kodi"; public override ValidationResult Test() diff --git a/src/Sonarr.Api.V3/Notifications/NotificationResource.cs b/src/Sonarr.Api.V3/Notifications/NotificationResource.cs index 14126750c..5e47ee68b 100644 --- a/src/Sonarr.Api.V3/Notifications/NotificationResource.cs +++ b/src/Sonarr.Api.V3/Notifications/NotificationResource.cs @@ -13,6 +13,7 @@ namespace Sonarr.Api.V3.Notifications public bool OnEpisodeFileDelete { get; set; } public bool OnEpisodeFileDeleteForUpgrade { get; set; } public bool OnHealthIssue { get; set; } + public bool OnApplicationUpdate { get; set; } public bool SupportsOnGrab { get; set; } public bool SupportsOnDownload { get; set; } public bool SupportsOnUpgrade { get; set; } @@ -21,6 +22,7 @@ namespace Sonarr.Api.V3.Notifications public bool SupportsOnEpisodeFileDelete { get; set; } public bool SupportsOnEpisodeFileDeleteForUpgrade { get; set; } public bool SupportsOnHealthIssue { get; set; } + public bool SupportsOnApplicationUpdate { get; set; } public bool IncludeHealthWarnings { get; set; } public string TestCommand { get; set; } } @@ -41,6 +43,7 @@ namespace Sonarr.Api.V3.Notifications resource.OnEpisodeFileDelete = definition.OnEpisodeFileDelete; resource.OnEpisodeFileDeleteForUpgrade = definition.OnEpisodeFileDeleteForUpgrade; resource.OnHealthIssue = definition.OnHealthIssue; + resource.OnApplicationUpdate = definition.OnApplicationUpdate; resource.SupportsOnGrab = definition.SupportsOnGrab; resource.SupportsOnDownload = definition.SupportsOnDownload; resource.SupportsOnUpgrade = definition.SupportsOnUpgrade; @@ -50,6 +53,7 @@ namespace Sonarr.Api.V3.Notifications resource.SupportsOnEpisodeFileDeleteForUpgrade = definition.SupportsOnEpisodeFileDeleteForUpgrade; resource.SupportsOnHealthIssue = definition.SupportsOnHealthIssue; resource.IncludeHealthWarnings = definition.IncludeHealthWarnings; + resource.SupportsOnApplicationUpdate = definition.SupportsOnApplicationUpdate; return resource; } @@ -68,6 +72,7 @@ namespace Sonarr.Api.V3.Notifications definition.OnEpisodeFileDelete = resource.OnEpisodeFileDelete; definition.OnEpisodeFileDeleteForUpgrade = resource.OnEpisodeFileDeleteForUpgrade; definition.OnHealthIssue = resource.OnHealthIssue; + definition.OnApplicationUpdate = resource.OnApplicationUpdate; definition.SupportsOnGrab = resource.SupportsOnGrab; definition.SupportsOnDownload = resource.SupportsOnDownload; definition.SupportsOnUpgrade = resource.SupportsOnUpgrade; @@ -77,6 +82,7 @@ namespace Sonarr.Api.V3.Notifications definition.SupportsOnEpisodeFileDeleteForUpgrade = resource.SupportsOnEpisodeFileDeleteForUpgrade; definition.SupportsOnHealthIssue = resource.SupportsOnHealthIssue; definition.IncludeHealthWarnings = resource.IncludeHealthWarnings; + definition.SupportsOnApplicationUpdate = resource.SupportsOnApplicationUpdate; return definition; }