diff --git a/go.mod b/go.mod index eeca4fb..2af1e59 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,9 @@ require ( github.com/anacrolix/missinggo/v2 v2.7.2 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect + github.com/containrrr/shoutrrr v0.8.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fatih/color v1.16.0 // indirect github.com/gdm85/go-rencode v0.1.8 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -76,7 +78,7 @@ require ( github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect diff --git a/go.sum b/go.sum index 0e783af..fa73327 100644 --- a/go.sum +++ b/go.sum @@ -121,6 +121,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containrrr/shoutrrr v0.8.0 h1:mfG2ATzIS7NR2Ec6XL+xyoHzN97H8WPjir8aYzJUSec= +github.com/containrrr/shoutrrr v0.8.0/go.mod h1:ioyQAyu1LJY6sILuNyKaQaw+9Ttik5QePU8atnAdO2o= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cytec/releaseparser v0.0.0-20200706155913-2341b265c370 h1:g9q5BGfDdhcXn4EmVZD8UydPXrvhSvgz3FRBn7zAJNs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -145,6 +147,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ergochat/irc-go v0.4.0 h1:0YibCKfAAtwxQdNjLQd9xpIEPisLcJ45f8FNsMHAuZc= github.com/ergochat/irc-go v0.4.0/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -309,6 +313,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= diff --git a/internal/domain/notification.go b/internal/domain/notification.go index cb45006..db3e234 100644 --- a/internal/domain/notification.go +++ b/internal/domain/notification.go @@ -82,6 +82,7 @@ const ( NotificationTypeGotify NotificationType = "GOTIFY" NotificationTypeNtfy NotificationType = "NTFY" NotificationTypeLunaSea NotificationType = "LUNASEA" + NotificationTypeShoutrrr NotificationType = "SHOUTRRR" ) type NotificationEvent string diff --git a/internal/notification/service.go b/internal/notification/service.go index 56649b2..f274753 100644 --- a/internal/notification/service.go +++ b/internal/notification/service.go @@ -124,16 +124,20 @@ func (s *service) registerSenders() { switch n.Type { case domain.NotificationTypeDiscord: s.senders = append(s.senders, NewDiscordSender(s.log, n)) - case domain.NotificationTypeNotifiarr: - s.senders = append(s.senders, NewNotifiarrSender(s.log, n)) - case domain.NotificationTypeTelegram: - s.senders = append(s.senders, NewTelegramSender(s.log, n)) - case domain.NotificationTypePushover: - s.senders = append(s.senders, NewPushoverSender(s.log, n)) case domain.NotificationTypeGotify: s.senders = append(s.senders, NewGotifySender(s.log, n)) case domain.NotificationTypeLunaSea: s.senders = append(s.senders, NewLunaSeaSender(s.log, n)) + case domain.NotificationTypeNotifiarr: + s.senders = append(s.senders, NewNotifiarrSender(s.log, n)) + case domain.NotificationTypeNtfy: + s.senders = append(s.senders, NewNtfySender(s.log, n)) + case domain.NotificationTypePushover: + s.senders = append(s.senders, NewPushoverSender(s.log, n)) + case domain.NotificationTypeShoutrrr: + s.senders = append(s.senders, NewShoutrrrSender(s.log, n)) + case domain.NotificationTypeTelegram: + s.senders = append(s.senders, NewTelegramSender(s.log, n)) } } } @@ -241,18 +245,20 @@ func (s *service) Test(ctx context.Context, notification domain.Notification) er switch notification.Type { case domain.NotificationTypeDiscord: agent = NewDiscordSender(s.log, notification) - case domain.NotificationTypeNotifiarr: - agent = NewNotifiarrSender(s.log, notification) - case domain.NotificationTypeTelegram: - agent = NewTelegramSender(s.log, notification) - case domain.NotificationTypePushover: - agent = NewPushoverSender(s.log, notification) case domain.NotificationTypeGotify: agent = NewGotifySender(s.log, notification) - case domain.NotificationTypeNtfy: - agent = NewNtfySender(s.log, notification) case domain.NotificationTypeLunaSea: agent = NewLunaSeaSender(s.log, notification) + case domain.NotificationTypeNotifiarr: + agent = NewNotifiarrSender(s.log, notification) + case domain.NotificationTypeNtfy: + agent = NewNtfySender(s.log, notification) + case domain.NotificationTypePushover: + agent = NewPushoverSender(s.log, notification) + case domain.NotificationTypeShoutrrr: + agent = NewShoutrrrSender(s.log, notification) + case domain.NotificationTypeTelegram: + agent = NewTelegramSender(s.log, notification) default: s.log.Error().Msgf("unsupported notification type: %v", notification.Type) return errors.New("unsupported notification type") diff --git a/internal/notification/shoutrrr.go b/internal/notification/shoutrrr.go new file mode 100644 index 0000000..bab1f44 --- /dev/null +++ b/internal/notification/shoutrrr.go @@ -0,0 +1,69 @@ +package notification + +import ( + "github.com/autobrr/autobrr/internal/domain" + + "github.com/containrrr/shoutrrr" + "github.com/rs/zerolog" +) + +type shoutrrrSender struct { + log zerolog.Logger + Settings domain.Notification + builder NotificationBuilderPlainText +} + +func NewShoutrrrSender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender { + return &shoutrrrSender{ + log: log.With().Str("sender", "shoutrrr").Logger(), + Settings: settings, + builder: NotificationBuilderPlainText{}, + } +} + +func (s *shoutrrrSender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error { + message := s.builder.BuildBody(payload) + + if err := shoutrrr.Send(s.Settings.Host, message); err != nil { + return err + } + + s.log.Debug().Msg("notification successfully sent to via shoutrrr") + + return nil +} + +func (s *shoutrrrSender) CanSend(event domain.NotificationEvent) bool { + if s.isEnabled() && s.isEnabledEvent(event) { + return true + } + return false +} + +func (s *shoutrrrSender) isEnabled() bool { + if s.Settings.Enabled { + if s.Settings.Host == "" { + s.log.Warn().Msg("shoutrrr missing host") + return false + } + + if s.Settings.Token == "" { + s.log.Warn().Msg("shoutrrr missing application token") + return false + } + + return true + } + + return false +} + +func (s *shoutrrrSender) isEnabledEvent(event domain.NotificationEvent) bool { + for _, e := range s.Settings.Events { + if e == string(event) { + return true + } + } + + return false +} diff --git a/web/src/domain/constants.ts b/web/src/domain/constants.ts index 990e92b..d2679f7 100644 --- a/web/src/domain/constants.ts +++ b/web/src/domain/constants.ts @@ -390,30 +390,34 @@ export const NotificationTypeOptions: OptionBasicTyped[] = [ label: "Discord", value: "DISCORD" }, - { - label: "Notifiarr", - value: "NOTIFIARR" - }, - { - label: "Telegram", - value: "TELEGRAM" - }, - { - label: "Pushover", - value: "PUSHOVER" - }, { label: "Gotify", value: "GOTIFY" }, + { + label: "LunaSea", + value: "LUNASEA" + }, + { + label: "Notifiarr", + value: "NOTIFIARR" + }, { label: "Ntfy", value: "NTFY" }, { - label: "LunaSea", - value: "LUNASEA" - } + label: "Pushover", + value: "PUSHOVER" + }, + { + label: "Shoutrrr", + value: "SHOUTRRR" + }, + { + label: "Telegram", + value: "TELEGRAM" + }, ]; export const IrcAuthMechanismTypeOptions: OptionBasicTyped[] = [ diff --git a/web/src/forms/settings/NotificationForms.tsx b/web/src/forms/settings/NotificationForms.tsx index a2aaa37..509eb30 100644 --- a/web/src/forms/settings/NotificationForms.tsx +++ b/web/src/forms/settings/NotificationForms.tsx @@ -239,6 +239,34 @@ function FormFieldsNtfy() { ); } +function FormFieldsShoutrrr() { + return ( +
+
+ Settings +
+ +

See full documentation

+ + Services + +
+ } + placeholder="smtp://username:password@host:port/?from=fromAddress&to=recipient1" + required={true} + /> + + ); +} + const componentMap: componentMapType = { DISCORD: , NOTIFIARR: , @@ -246,6 +274,7 @@ const componentMap: componentMapType = { PUSHOVER: , GOTIFY: , NTFY: , + SHOUTRRR: , LUNASEA: }; diff --git a/web/src/screens/settings/Notifications.tsx b/web/src/screens/settings/Notifications.tsx index cd4007e..8d132e2 100644 --- a/web/src/screens/settings/Notifications.tsx +++ b/web/src/screens/settings/Notifications.tsx @@ -77,6 +77,7 @@ const iconComponentMap: componentMapType = { PUSHOVER: Pushover, GOTIFY: Gotify, NTFY: ntfy, + SHOUTRRR: Shoutrrr, LUNASEA: LunaSea }; diff --git a/web/src/types/Notification.d.ts b/web/src/types/Notification.d.ts index 0384937..3c5259f 100644 --- a/web/src/types/Notification.d.ts +++ b/web/src/types/Notification.d.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -type NotificationType = "DISCORD" | "NOTIFIARR" | "TELEGRAM" | "PUSHOVER" | "GOTIFY" | "NTFY" | "LUNASEA"; +type NotificationType = "DISCORD" | "NOTIFIARR" | "TELEGRAM" | "PUSHOVER" | "GOTIFY" | "NTFY" | "LUNASEA" | "SHOUTRRR"; type NotificationEvent = "PUSH_APPROVED" | "PUSH_REJECTED"