mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 16:59:12 +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
|
@ -28,7 +28,7 @@ func NewNotificationRepo(log logger.Logger, db *DB) domain.NotificationRepo {
|
||||||
func (r *NotificationRepo) Find(ctx context.Context, params domain.NotificationQueryParams) ([]domain.Notification, int, error) {
|
func (r *NotificationRepo) Find(ctx context.Context, params domain.NotificationQueryParams) ([]domain.Notification, int, error) {
|
||||||
|
|
||||||
queryBuilder := r.db.squirrel.
|
queryBuilder := r.db.squirrel.
|
||||||
Select("id", "name", "type", "enabled", "events", "webhook", "token", "channel", "created_at", "updated_at", "COUNT(*) OVER() AS total_count").
|
Select("id", "name", "type", "enabled", "events", "webhook", "token", "api_key", "channel", "created_at", "updated_at", "COUNT(*) OVER() AS total_count").
|
||||||
From("notification").
|
From("notification").
|
||||||
OrderBy("name")
|
OrderBy("name")
|
||||||
|
|
||||||
|
@ -49,15 +49,15 @@ func (r *NotificationRepo) Find(ctx context.Context, params domain.NotificationQ
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var n domain.Notification
|
var n domain.Notification
|
||||||
|
|
||||||
var webhook, token, channel sql.NullString
|
var webhook, token, apiKey, channel sql.NullString
|
||||||
//var token, apiKey, webhook, title, icon, host, username, password, channel, targets, devices sql.NullString
|
//var token, apiKey, webhook, title, icon, host, username, password, channel, targets, devices sql.NullString
|
||||||
//if err := rows.Scan(&n.ID, &n.Name, &n.Type, &n.Enabled, pq.Array(&n.Events), &token, &apiKey, &webhook, &title, &icon, &host, &username, &password, &channel, &targets, &devices, &n.CreatedAt, &n.UpdatedAt); err != nil {
|
//if err := rows.Scan(&n.ID, &n.Name, &n.Type, &n.Enabled, pq.Array(&n.Events), &token, &apiKey, &webhook, &title, &icon, &host, &username, &password, &channel, &targets, &devices, &n.CreatedAt, &n.UpdatedAt); err != nil {
|
||||||
//var token, apiKey, webhook, title, icon, host, username, password, channel, targets, devices sql.NullString
|
//var token, apiKey, webhook, title, icon, host, username, password, channel, targets, devices sql.NullString
|
||||||
if err := rows.Scan(&n.ID, &n.Name, &n.Type, &n.Enabled, pq.Array(&n.Events), &webhook, &token, &channel, &n.CreatedAt, &n.UpdatedAt, &totalCount); err != nil {
|
if err := rows.Scan(&n.ID, &n.Name, &n.Type, &n.Enabled, pq.Array(&n.Events), &webhook, &token, &apiKey, &channel, &n.CreatedAt, &n.UpdatedAt, &totalCount); err != nil {
|
||||||
return nil, 0, errors.Wrap(err, "error scanning row")
|
return nil, 0, errors.Wrap(err, "error scanning row")
|
||||||
}
|
}
|
||||||
|
|
||||||
//n.APIKey = apiKey.String
|
n.APIKey = apiKey.String
|
||||||
n.Webhook = webhook.String
|
n.Webhook = webhook.String
|
||||||
n.Token = token.String
|
n.Token = token.String
|
||||||
n.Channel = channel.String
|
n.Channel = channel.String
|
||||||
|
@ -130,6 +130,16 @@ func (r *NotificationRepo) FindByID(ctx context.Context, id int) (*domain.Notifi
|
||||||
"enabled",
|
"enabled",
|
||||||
"events",
|
"events",
|
||||||
"token",
|
"token",
|
||||||
|
"api_key",
|
||||||
|
"webhook",
|
||||||
|
"title",
|
||||||
|
"icon",
|
||||||
|
"host",
|
||||||
|
"username",
|
||||||
|
"password",
|
||||||
|
"channel",
|
||||||
|
"targets",
|
||||||
|
"devices",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
).
|
).
|
||||||
|
@ -172,6 +182,7 @@ func (r *NotificationRepo) FindByID(ctx context.Context, id int) (*domain.Notifi
|
||||||
func (r *NotificationRepo) Store(ctx context.Context, notification domain.Notification) (*domain.Notification, error) {
|
func (r *NotificationRepo) Store(ctx context.Context, notification domain.Notification) (*domain.Notification, error) {
|
||||||
webhook := toNullString(notification.Webhook)
|
webhook := toNullString(notification.Webhook)
|
||||||
token := toNullString(notification.Token)
|
token := toNullString(notification.Token)
|
||||||
|
apiKey := toNullString(notification.APIKey)
|
||||||
channel := toNullString(notification.Channel)
|
channel := toNullString(notification.Channel)
|
||||||
|
|
||||||
queryBuilder := r.db.squirrel.
|
queryBuilder := r.db.squirrel.
|
||||||
|
@ -183,6 +194,7 @@ func (r *NotificationRepo) Store(ctx context.Context, notification domain.Notifi
|
||||||
"events",
|
"events",
|
||||||
"webhook",
|
"webhook",
|
||||||
"token",
|
"token",
|
||||||
|
"api_key",
|
||||||
"channel",
|
"channel",
|
||||||
).
|
).
|
||||||
Values(
|
Values(
|
||||||
|
@ -192,6 +204,7 @@ func (r *NotificationRepo) Store(ctx context.Context, notification domain.Notifi
|
||||||
pq.Array(notification.Events),
|
pq.Array(notification.Events),
|
||||||
webhook,
|
webhook,
|
||||||
token,
|
token,
|
||||||
|
apiKey,
|
||||||
channel,
|
channel,
|
||||||
).
|
).
|
||||||
Suffix("RETURNING id").RunWith(r.db.handler)
|
Suffix("RETURNING id").RunWith(r.db.handler)
|
||||||
|
@ -213,6 +226,7 @@ func (r *NotificationRepo) Store(ctx context.Context, notification domain.Notifi
|
||||||
func (r *NotificationRepo) Update(ctx context.Context, notification domain.Notification) (*domain.Notification, error) {
|
func (r *NotificationRepo) Update(ctx context.Context, notification domain.Notification) (*domain.Notification, error) {
|
||||||
webhook := toNullString(notification.Webhook)
|
webhook := toNullString(notification.Webhook)
|
||||||
token := toNullString(notification.Token)
|
token := toNullString(notification.Token)
|
||||||
|
apiKey := toNullString(notification.APIKey)
|
||||||
channel := toNullString(notification.Channel)
|
channel := toNullString(notification.Channel)
|
||||||
|
|
||||||
queryBuilder := r.db.squirrel.
|
queryBuilder := r.db.squirrel.
|
||||||
|
@ -223,6 +237,7 @@ func (r *NotificationRepo) Update(ctx context.Context, notification domain.Notif
|
||||||
Set("events", pq.Array(notification.Events)).
|
Set("events", pq.Array(notification.Events)).
|
||||||
Set("webhook", webhook).
|
Set("webhook", webhook).
|
||||||
Set("token", token).
|
Set("token", token).
|
||||||
|
Set("api_key", apiKey).
|
||||||
Set("channel", channel).
|
Set("channel", channel).
|
||||||
Set("updated_at", sq.Expr("CURRENT_TIMESTAMP")).
|
Set("updated_at", sq.Expr("CURRENT_TIMESTAMP")).
|
||||||
Where("id = ?", notification.ID)
|
Where("id = ?", notification.ID)
|
||||||
|
|
|
@ -64,6 +64,7 @@ type NotificationType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NotificationTypeDiscord NotificationType = "DISCORD"
|
NotificationTypeDiscord NotificationType = "DISCORD"
|
||||||
|
NotificationTypeNotifiarr NotificationType = "NOTIFIARR"
|
||||||
NotificationTypeIFTTT NotificationType = "IFTTT"
|
NotificationTypeIFTTT NotificationType = "IFTTT"
|
||||||
NotificationTypeJoin NotificationType = "JOIN"
|
NotificationTypeJoin NotificationType = "JOIN"
|
||||||
NotificationTypeMattermost NotificationType = "MATTERMOST"
|
NotificationTypeMattermost NotificationType = "MATTERMOST"
|
||||||
|
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/autobrr/autobrr/internal/domain"
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
"github.com/autobrr/autobrr/internal/logger"
|
"github.com/autobrr/autobrr/internal/logger"
|
||||||
|
@ -117,6 +120,8 @@ func (s *service) registerSenders() {
|
||||||
switch n.Type {
|
switch n.Type {
|
||||||
case domain.NotificationTypeDiscord:
|
case domain.NotificationTypeDiscord:
|
||||||
s.senders = append(s.senders, NewDiscordSender(s.log, n))
|
s.senders = append(s.senders, NewDiscordSender(s.log, n))
|
||||||
|
case domain.NotificationTypeNotifiarr:
|
||||||
|
s.senders = append(s.senders, NewNotifiarrSender(s.log, n))
|
||||||
case domain.NotificationTypeTelegram:
|
case domain.NotificationTypeTelegram:
|
||||||
s.senders = append(s.senders, NewTelegramSender(s.log, n))
|
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 {
|
func (s *service) Test(ctx context.Context, notification domain.Notification) error {
|
||||||
var agent domain.NotificationSender
|
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 {
|
switch notification.Type {
|
||||||
case domain.NotificationTypeDiscord:
|
case domain.NotificationTypeDiscord:
|
||||||
agent = NewDiscordSender(s.log, notification)
|
agent = NewDiscordSender(s.log, notification)
|
||||||
|
case domain.NotificationTypeNotifiarr:
|
||||||
|
agent = NewNotifiarrSender(s.log, notification)
|
||||||
case domain.NotificationTypeTelegram:
|
case domain.NotificationTypeTelegram:
|
||||||
agent = NewTelegramSender(s.log, notification)
|
agent = NewTelegramSender(s.log, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
return agent.Send(domain.NotificationEventTest, domain.NotificationPayload{
|
g, ctx := errgroup.WithContext(ctx)
|
||||||
Subject: "Test Notification",
|
|
||||||
Message: "autobrr goes brr!!",
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,6 +263,11 @@ export interface OptionBasic {
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OptionBasicTyped<T> {
|
||||||
|
label: string;
|
||||||
|
value: T;
|
||||||
|
}
|
||||||
|
|
||||||
export const PushStatusOptions: OptionBasic[] = [
|
export const PushStatusOptions: OptionBasic[] = [
|
||||||
{
|
{
|
||||||
label: "Rejected",
|
label: "Rejected",
|
||||||
|
@ -278,11 +283,15 @@ export const PushStatusOptions: OptionBasic[] = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const NotificationTypeOptions: OptionBasic[] = [
|
export const NotificationTypeOptions: OptionBasicTyped<NotificationType>[] = [
|
||||||
{
|
{
|
||||||
label: "Discord",
|
label: "Discord",
|
||||||
value: "DISCORD"
|
value: "DISCORD"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Notifiarr",
|
||||||
|
value: "NOTIFIARR"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Telegram",
|
label: "Telegram",
|
||||||
value: "TELEGRAM"
|
value: "TELEGRAM"
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { Field, Form, Formik, FormikErrors, FormikValues } from "formik";
|
|
||||||
import type { FieldProps } from "formik";
|
import type { FieldProps } from "formik";
|
||||||
|
import { Field, Form, Formik, FormikErrors, FormikValues } from "formik";
|
||||||
import { XIcon } from "@heroicons/react/solid";
|
import { XIcon } from "@heroicons/react/solid";
|
||||||
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
|
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
|
||||||
import {
|
import { PasswordFieldWide, SwitchGroupWide, TextFieldWide } from "../../components/inputs";
|
||||||
PasswordFieldWide,
|
|
||||||
SwitchGroupWide,
|
|
||||||
TextFieldWide
|
|
||||||
} from "../../components/inputs";
|
|
||||||
import DEBUG from "../../components/debug";
|
import DEBUG from "../../components/debug";
|
||||||
import { EventOptions, NotificationTypeOptions, SelectOption } from "../../domain/constants";
|
import { EventOptions, NotificationTypeOptions, SelectOption } from "../../domain/constants";
|
||||||
import { useMutation } from "react-query";
|
import { useMutation } from "react-query";
|
||||||
|
@ -80,6 +76,25 @@ function FormFieldsDiscord() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FormFieldsNotifiarr() {
|
||||||
|
return (
|
||||||
|
<div className="border-t border-gray-200 dark:border-gray-700 py-4">
|
||||||
|
<div className="px-4 space-y-1">
|
||||||
|
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">Settings</Dialog.Title>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Enable the autobrr integration and optionally create a new API Key.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PasswordFieldWide
|
||||||
|
name="api_key"
|
||||||
|
label="API Key"
|
||||||
|
help="Notifiarr API Key"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function FormFieldsTelegram() {
|
function FormFieldsTelegram() {
|
||||||
return (
|
return (
|
||||||
<div className="border-t border-gray-200 dark:border-gray-700 py-4">
|
<div className="border-t border-gray-200 dark:border-gray-700 py-4">
|
||||||
|
@ -106,6 +121,7 @@ function FormFieldsTelegram() {
|
||||||
|
|
||||||
const componentMap: componentMapType = {
|
const componentMap: componentMapType = {
|
||||||
DISCORD: <FormFieldsDiscord />,
|
DISCORD: <FormFieldsDiscord />,
|
||||||
|
NOTIFIARR: <FormFieldsNotifiarr />,
|
||||||
TELEGRAM: <FormFieldsTelegram />
|
TELEGRAM: <FormFieldsTelegram />
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -428,6 +444,7 @@ export function NotificationUpdateForm({ isOpen, toggle, notification }: UpdateP
|
||||||
name: notification.name,
|
name: notification.name,
|
||||||
webhook: notification.webhook,
|
webhook: notification.webhook,
|
||||||
token: notification.token,
|
token: notification.token,
|
||||||
|
api_key: notification.api_key,
|
||||||
channel: notification.channel,
|
channel: notification.channel,
|
||||||
events: notification.events || []
|
events: notification.events || []
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,6 +80,7 @@ const TelegramIcon = () => (
|
||||||
|
|
||||||
const iconComponentMap: componentMapType = {
|
const iconComponentMap: componentMapType = {
|
||||||
DISCORD: <span className="flex items-center px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-400"><DiscordIcon /> Discord</span>,
|
DISCORD: <span className="flex items-center px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-400"><DiscordIcon /> Discord</span>,
|
||||||
|
NOTIFIARR: <span className="flex items-center px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-400"><DiscordIcon /> Notifiarr</span>,
|
||||||
TELEGRAM: <span className="flex items-center px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-400"><TelegramIcon /> Telegram</span>
|
TELEGRAM: <span className="flex items-center px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-400"><TelegramIcon /> Telegram</span>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
3
web/src/types/Notification.d.ts
vendored
3
web/src/types/Notification.d.ts
vendored
|
@ -1,4 +1,4 @@
|
||||||
type NotificationType = "DISCORD" | "TELEGRAM";
|
type NotificationType = "DISCORD" | "NOTIFIARR" | "TELEGRAM";
|
||||||
type NotificationEvent = "PUSH_APPROVED" | "PUSH_REJECTED" | "PUSH_ERROR" | "IRC_DISCONNECTED" | "IRC_RECONNECTED" | "APP_UPDATE_AVAILABLE";
|
type NotificationEvent = "PUSH_APPROVED" | "PUSH_REJECTED" | "PUSH_ERROR" | "IRC_DISCONNECTED" | "IRC_RECONNECTED" | "APP_UPDATE_AVAILABLE";
|
||||||
|
|
||||||
interface Notification {
|
interface Notification {
|
||||||
|
@ -9,5 +9,6 @@ interface Notification {
|
||||||
events: NotificationEvent[];
|
events: NotificationEvent[];
|
||||||
webhook?: string;
|
webhook?: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
|
api_key?: string;
|
||||||
channel?: string;
|
channel?: string;
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue