mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
feat(notifications): add Notifiarr support (#464)
This commit is contained in:
parent
f8ace9edbe
commit
63d4c21e54
8 changed files with 326 additions and 18 deletions
168
internal/notification/notifiarr.go
Normal file
168
internal/notification/notifiarr.go
Normal file
|
@ -0,0 +1,168 @@
|
|||
package notification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
"github.com/autobrr/autobrr/pkg/errors"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type notifiarrMessage struct {
|
||||
Event string `json:"event"`
|
||||
Data notifiarrMessageData `json:"data"`
|
||||
}
|
||||
|
||||
type notifiarrMessageData struct {
|
||||
Subject string `json:"subject"`
|
||||
Message string `json:"message"`
|
||||
Event domain.NotificationEvent `json:"event"`
|
||||
ReleaseName *string `json:"release_name,omitempty"`
|
||||
Filter *string `json:"filter,omitempty"`
|
||||
Indexer *string `json:"indexer,omitempty"`
|
||||
InfoHash *string `json:"info_hash,omitempty"`
|
||||
Size *uint64 `json:"size,omitempty"`
|
||||
Status *domain.ReleasePushStatus `json:"status,omitempty"`
|
||||
Action *string `json:"action,omitempty"`
|
||||
ActionType *domain.ActionType `json:"action_type,omitempty"`
|
||||
ActionClient *string `json:"action_client,omitempty"`
|
||||
Rejections []string `json:"rejections,omitempty"`
|
||||
Protocol *domain.ReleaseProtocol `json:"protocol,omitempty"` // torrent
|
||||
Implementation *domain.ReleaseImplementation `json:"implementation,omitempty"` // irc, rss, api
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type notifiarrSender struct {
|
||||
log zerolog.Logger
|
||||
Settings domain.Notification
|
||||
}
|
||||
|
||||
func NewNotifiarrSender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
||||
return ¬ifiarrSender{
|
||||
log: log.With().Str("sender", "notifiarr").Logger(),
|
||||
Settings: settings,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *notifiarrSender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
||||
m := notifiarrMessage{
|
||||
Event: string(event),
|
||||
Data: s.buildMessage(payload),
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("notifiarr client could not marshal data: %v", m)
|
||||
return errors.Wrap(err, "could not marshal data: %+v", m)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("https://notifiarr.com/api/v1/notification/autobrr/%v", s.Settings.APIKey)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("notifiarr client request error: %v", event)
|
||||
return errors.Wrap(err, "could not create request")
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "autobrr")
|
||||
|
||||
t := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
|
||||
client := http.Client{Transport: t, Timeout: 30 * time.Second}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("notifiarr client request error: %v", event)
|
||||
return errors.Wrap(err, "could not make request: %+v", req)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
s.log.Error().Err(err).Msgf("notifiarr client request error: %v", event)
|
||||
return errors.Wrap(err, "could not read data")
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
s.log.Trace().Msgf("notifiarr status: %v response: %v", res.StatusCode, string(body))
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
s.log.Error().Err(err).Msgf("notifiarr client request error: %v", string(body))
|
||||
return errors.New("bad status: %v body: %v", res.StatusCode, string(body))
|
||||
}
|
||||
|
||||
s.log.Debug().Msg("notification successfully sent to notifiarr")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *notifiarrSender) CanSend(event domain.NotificationEvent) bool {
|
||||
if s.isEnabled() && s.isEnabledEvent(event) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *notifiarrSender) isEnabled() bool {
|
||||
if s.Settings.Enabled && s.Settings.Webhook != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *notifiarrSender) isEnabledEvent(event domain.NotificationEvent) bool {
|
||||
for _, e := range s.Settings.Events {
|
||||
if e == string(event) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *notifiarrSender) buildMessage(payload domain.NotificationPayload) notifiarrMessageData {
|
||||
m := notifiarrMessageData{
|
||||
Event: payload.Event,
|
||||
Timestamp: payload.Timestamp,
|
||||
}
|
||||
|
||||
if payload.Subject != "" && payload.Message != "" {
|
||||
m.Subject = payload.Subject
|
||||
m.Message = payload.Message
|
||||
}
|
||||
if payload.ReleaseName != "" {
|
||||
m.ReleaseName = &payload.ReleaseName
|
||||
}
|
||||
if payload.Status != "" {
|
||||
m.Status = &payload.Status
|
||||
}
|
||||
if payload.Indexer != "" {
|
||||
m.Indexer = &payload.Indexer
|
||||
}
|
||||
if payload.Filter != "" {
|
||||
m.Filter = &payload.Filter
|
||||
}
|
||||
if payload.Action != "" || payload.ActionClient != "" {
|
||||
m.Action = &payload.Action
|
||||
|
||||
if payload.ActionClient != "" {
|
||||
m.ActionClient = &payload.ActionClient
|
||||
}
|
||||
}
|
||||
if len(payload.Rejections) > 0 {
|
||||
m.Rejections = payload.Rejections
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
|
@ -2,6 +2,9 @@ package notification
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/autobrr/autobrr/internal/domain"
|
||||
"github.com/autobrr/autobrr/internal/logger"
|
||||
|
@ -117,6 +120,8 @@ 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))
|
||||
}
|
||||
|
@ -147,15 +152,106 @@ func (s *service) Send(event domain.NotificationEvent, payload domain.Notificati
|
|||
func (s *service) Test(ctx context.Context, notification domain.Notification) error {
|
||||
var agent domain.NotificationSender
|
||||
|
||||
// send test events
|
||||
events := []domain.NotificationPayload{
|
||||
{
|
||||
Subject: "Test Notification",
|
||||
Message: "autobrr goes brr!!",
|
||||
Event: domain.NotificationEventTest,
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
{
|
||||
Subject: "New release!",
|
||||
Message: "Best.Show.Ever.S18E21.1080p.AMZN.WEB-DL.DDP2.0.H.264-GROUP",
|
||||
Event: domain.NotificationEventPushApproved,
|
||||
ReleaseName: "Best.Show.Ever.S18E21.1080p.AMZN.WEB-DL.DDP2.0.H.264-GROUP",
|
||||
Filter: "TV",
|
||||
Indexer: "MockIndexer",
|
||||
Status: domain.ReleasePushStatusApproved,
|
||||
Action: "Send to qBittorrent",
|
||||
ActionType: domain.ActionTypeQbittorrent,
|
||||
ActionClient: "qBittorrent",
|
||||
Rejections: nil,
|
||||
Protocol: domain.ReleaseProtocolTorrent,
|
||||
Implementation: domain.ReleaseImplementationIRC,
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
{
|
||||
Subject: "New release!",
|
||||
Message: "Best.Show.Ever.S18E21.1080p.AMZN.WEB-DL.DDP2.0.H.264-GROUP",
|
||||
Event: domain.NotificationEventPushRejected,
|
||||
ReleaseName: "Best.Show.Ever.S18E21.1080p.AMZN.WEB-DL.DDP2.0.H.264-GROUP",
|
||||
Filter: "TV",
|
||||
Indexer: "MockIndexer",
|
||||
Status: domain.ReleasePushStatusRejected,
|
||||
Action: "Send to Sonarr",
|
||||
ActionType: domain.ActionTypeSonarr,
|
||||
ActionClient: "Sonarr",
|
||||
Rejections: []string{"Unknown Series"},
|
||||
Protocol: domain.ReleaseProtocolTorrent,
|
||||
Implementation: domain.ReleaseImplementationIRC,
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
{
|
||||
Subject: "New release!",
|
||||
Message: "Best.Show.Ever.S18E21.1080p.AMZN.WEB-DL.DDP2.0.H.264-GROUP",
|
||||
Event: domain.NotificationEventPushError,
|
||||
ReleaseName: "Best.Show.Ever.S18E21.1080p.AMZN.WEB-DL.DDP2.0.H.264-GROUP",
|
||||
Filter: "TV",
|
||||
Indexer: "MockIndexer",
|
||||
Status: domain.ReleasePushStatusErr,
|
||||
Action: "Send to Sonarr",
|
||||
ActionType: domain.ActionTypeSonarr,
|
||||
ActionClient: "Sonarr",
|
||||
Rejections: []string{"error pushing to client"},
|
||||
Protocol: domain.ReleaseProtocolTorrent,
|
||||
Implementation: domain.ReleaseImplementationIRC,
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
{
|
||||
Subject: "IRC Disconnected unexpectedly",
|
||||
Message: "Network: P2P-Network",
|
||||
Event: domain.NotificationEventIRCDisconnected,
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
{
|
||||
Subject: "IRC Reconnected",
|
||||
Message: "Network: P2P-Network",
|
||||
Event: domain.NotificationEventIRCReconnected,
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
{
|
||||
Subject: "New update available!",
|
||||
Message: "v1.6.0",
|
||||
Event: domain.NotificationEventAppUpdateAvailable,
|
||||
Timestamp: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return agent.Send(domain.NotificationEventTest, domain.NotificationPayload{
|
||||
Subject: "Test Notification",
|
||||
Message: "autobrr goes brr!!",
|
||||
})
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
for _, event := range events {
|
||||
e := event
|
||||
g.Go(func() error {
|
||||
return agent.Send(e.Event, e)
|
||||
})
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
s.log.Error().Err(err).Msgf("Something went wrong sending test notifications to %v", notification.Type)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue