mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 16:29:12 +00:00
feat(notifications): add LunaSea support (#1284)
* feat(notifications): add lunasea * fix(web): truncate overflow in PasswordFieldWide * refactor(notifications): centralize msg building Left the building logic in discord.go and notifiarr.go as is because of their unique structure. * refactor: moved components and swapped to outline - Refactored the iconComponentMap to use a single iconStyle variable. * upped size from 4 to 5 * rename NotificationBuilder function
This commit is contained in:
parent
da365da17c
commit
a89a1a55d9
13 changed files with 266 additions and 203 deletions
|
@ -80,6 +80,7 @@ const (
|
||||||
NotificationTypeSlack NotificationType = "SLACK"
|
NotificationTypeSlack NotificationType = "SLACK"
|
||||||
NotificationTypeTelegram NotificationType = "TELEGRAM"
|
NotificationTypeTelegram NotificationType = "TELEGRAM"
|
||||||
NotificationTypeGotify NotificationType = "GOTIFY"
|
NotificationTypeGotify NotificationType = "GOTIFY"
|
||||||
|
NotificationTypeLunaSea NotificationType = "LUNASEA"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NotificationEvent string
|
type NotificationEvent string
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"github.com/autobrr/autobrr/internal/domain"
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
"github.com/autobrr/autobrr/pkg/errors"
|
"github.com/autobrr/autobrr/pkg/errors"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,26 +25,28 @@ type gotifyMessage struct {
|
||||||
type gotifySender struct {
|
type gotifySender struct {
|
||||||
log zerolog.Logger
|
log zerolog.Logger
|
||||||
Settings domain.Notification
|
Settings domain.Notification
|
||||||
|
builder NotificationBuilderPlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGotifySender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
func NewGotifySender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
||||||
return &gotifySender{
|
return &gotifySender{
|
||||||
log: log.With().Str("sender", "gotify").Logger(),
|
log: log.With().Str("sender", "gotify").Logger(),
|
||||||
Settings: settings,
|
Settings: settings,
|
||||||
|
builder: NotificationBuilderPlainText{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *gotifySender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
func (s *gotifySender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
||||||
m := gotifyMessage{
|
m := gotifyMessage{
|
||||||
Message: s.buildMessage(payload),
|
Message: s.builder.BuildBody(payload),
|
||||||
Title: s.buildTitle(event),
|
Title: s.builder.BuildTitle(event),
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
data := url.Values{}
|
||||||
data.Set("message", m.Message)
|
data.Set("message", m.Message)
|
||||||
data.Set("title", m.Title)
|
data.Set("title", m.Title)
|
||||||
|
|
||||||
url := fmt.Sprintf("%v/message?token=%v", s.Settings.Host, s.Settings.Token);
|
url := fmt.Sprintf("%v/message?token=%v", s.Settings.Host, s.Settings.Token)
|
||||||
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(data.Encode()))
|
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(data.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Error().Err(err).Msgf("gotify client request error: %v", event)
|
s.log.Error().Err(err).Msgf("gotify client request error: %v", event)
|
||||||
|
@ -116,61 +117,3 @@ func (s *gotifySender) isEnabledEvent(event domain.NotificationEvent) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *gotifySender) buildMessage(payload domain.NotificationPayload) string {
|
|
||||||
msg := ""
|
|
||||||
|
|
||||||
if payload.Subject != "" && payload.Message != "" {
|
|
||||||
msg += fmt.Sprintf("%v\n%v", payload.Subject, payload.Message)
|
|
||||||
}
|
|
||||||
if payload.ReleaseName != "" {
|
|
||||||
msg += fmt.Sprintf("\nNew release: %v", payload.ReleaseName)
|
|
||||||
}
|
|
||||||
if payload.Size > 0 {
|
|
||||||
msg += fmt.Sprintf("\nSize: %v", humanize.Bytes(payload.Size))
|
|
||||||
}
|
|
||||||
if payload.Status != "" {
|
|
||||||
msg += fmt.Sprintf("\nStatus: %v", payload.Status.String())
|
|
||||||
}
|
|
||||||
if payload.Indexer != "" {
|
|
||||||
msg += fmt.Sprintf("\nIndexer: %v", payload.Indexer)
|
|
||||||
}
|
|
||||||
if payload.Filter != "" {
|
|
||||||
msg += fmt.Sprintf("\nFilter: %v", payload.Filter)
|
|
||||||
}
|
|
||||||
if payload.Action != "" {
|
|
||||||
action := fmt.Sprintf("\nAction: %v Type: %v", payload.Action, payload.ActionType)
|
|
||||||
if payload.ActionClient != "" {
|
|
||||||
action += fmt.Sprintf(" Client: %v", payload.ActionClient)
|
|
||||||
}
|
|
||||||
msg += action
|
|
||||||
}
|
|
||||||
if len(payload.Rejections) > 0 {
|
|
||||||
msg += fmt.Sprintf("\nRejections: %v", strings.Join(payload.Rejections, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *gotifySender) buildTitle(event domain.NotificationEvent) string {
|
|
||||||
title := ""
|
|
||||||
|
|
||||||
switch event {
|
|
||||||
case domain.NotificationEventAppUpdateAvailable:
|
|
||||||
title = "Autobrr update available"
|
|
||||||
case domain.NotificationEventPushApproved:
|
|
||||||
title = "Push Approved"
|
|
||||||
case domain.NotificationEventPushRejected:
|
|
||||||
title = "Push Rejected"
|
|
||||||
case domain.NotificationEventPushError:
|
|
||||||
title = "Error"
|
|
||||||
case domain.NotificationEventIRCDisconnected:
|
|
||||||
title = "IRC Disconnected"
|
|
||||||
case domain.NotificationEventIRCReconnected:
|
|
||||||
title = "IRC Reconnected"
|
|
||||||
case domain.NotificationEventTest:
|
|
||||||
title = "Test"
|
|
||||||
}
|
|
||||||
|
|
||||||
return title
|
|
||||||
}
|
|
||||||
|
|
100
internal/notification/lunasea.go
Normal file
100
internal/notification/lunasea.go
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
|
"github.com/autobrr/autobrr/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// unsure if this is the best approach to send an image with the notification
|
||||||
|
const defaultImageURL = "https://raw.githubusercontent.com/autobrr/autobrr/master/.github/images/logo.png"
|
||||||
|
|
||||||
|
type LunaSeaMessage struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Image string `json:"image,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type lunaSeaSender struct {
|
||||||
|
log zerolog.Logger
|
||||||
|
Settings domain.Notification
|
||||||
|
builder NotificationBuilderPlainText
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *lunaSeaSender) rewriteWebhookURL(url string) string {
|
||||||
|
re := regexp.MustCompile(`/(radarr|sonarr|lidarr|tautulli|overseerr)/`)
|
||||||
|
return re.ReplaceAllString(url, "/custom/")
|
||||||
|
} // `custom` is not mentioned in their docs, so I thought this would be a good idea to add to avoid user errors
|
||||||
|
|
||||||
|
func NewLunaSeaSender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
||||||
|
return &lunaSeaSender{
|
||||||
|
log: log.With().Str("sender", "lunasea").Logger(),
|
||||||
|
Settings: settings,
|
||||||
|
builder: NotificationBuilderPlainText{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *lunaSeaSender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
||||||
|
m := LunaSeaMessage{
|
||||||
|
Title: s.builder.BuildTitle(event),
|
||||||
|
Body: s.builder.BuildBody(payload),
|
||||||
|
Image: defaultImageURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error().Err(err).Msg("lunasea client could not marshal data")
|
||||||
|
return errors.Wrap(err, "could not marshal data")
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrittenURL := s.rewriteWebhookURL(s.Settings.Webhook)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, rewrittenURL, bytes.NewBuffer(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error().Err(err).Msg("lunasea client request error")
|
||||||
|
return errors.Wrap(err, "could not create request")
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 30 * time.Second}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error().Err(err).Msg("lunasea client request error")
|
||||||
|
return errors.Wrap(err, "could not make request")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.StatusCode >= 300 {
|
||||||
|
s.log.Error().Msgf("bad status from lunasea: %v", res.StatusCode)
|
||||||
|
return errors.New("bad status: %v", res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Debug().Msg("notification successfully sent to lunasea")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *lunaSeaSender) CanSend(event domain.NotificationEvent) bool {
|
||||||
|
if s.Settings.Enabled && s.Settings.Webhook != "" && s.isEnabledEvent(event) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *lunaSeaSender) isEnabledEvent(event domain.NotificationEvent) bool {
|
||||||
|
for _, e := range s.Settings.Events {
|
||||||
|
if e == string(event) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
56
internal/notification/message_builder.go
Normal file
56
internal/notification/message_builder.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotificationBuilderPlainText struct{}
|
||||||
|
|
||||||
|
// BuildBody constructs the body of the notification message.
|
||||||
|
func (b *NotificationBuilderPlainText) BuildBody(payload domain.NotificationPayload) string {
|
||||||
|
var parts []string
|
||||||
|
|
||||||
|
buildPart := func(condition bool, format string, a ...interface{}) {
|
||||||
|
if condition {
|
||||||
|
parts = append(parts, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildPart(payload.Subject != "" && payload.Message != "", "%v\n%v", payload.Subject, payload.Message)
|
||||||
|
buildPart(payload.ReleaseName != "", "\nNew release: %v", payload.ReleaseName)
|
||||||
|
buildPart(payload.Size > 0, "\nSize: %v", humanize.Bytes(payload.Size))
|
||||||
|
buildPart(payload.Status != "", "\nStatus: %v", payload.Status.String())
|
||||||
|
buildPart(payload.Indexer != "", "\nIndexer: %v", payload.Indexer)
|
||||||
|
buildPart(payload.Filter != "", "\nFilter: %v", payload.Filter)
|
||||||
|
buildPart(payload.Action != "", "\nAction: %v Type: %v", payload.Action, payload.ActionType)
|
||||||
|
buildPart(len(payload.Rejections) > 0, "\nRejections: %v", strings.Join(payload.Rejections, ", "))
|
||||||
|
|
||||||
|
if payload.Action != "" && payload.ActionClient != "" {
|
||||||
|
parts = append(parts, fmt.Sprintf(" Client: %v", payload.ActionClient))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(parts, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildTitle constructs the title of the notification message.
|
||||||
|
func (b *NotificationBuilderPlainText) BuildTitle(event domain.NotificationEvent) string {
|
||||||
|
titles := map[domain.NotificationEvent]string{
|
||||||
|
domain.NotificationEventAppUpdateAvailable: "Autobrr update available",
|
||||||
|
domain.NotificationEventPushApproved: "Push Approved",
|
||||||
|
domain.NotificationEventPushRejected: "Push Rejected",
|
||||||
|
domain.NotificationEventPushError: "Error",
|
||||||
|
domain.NotificationEventIRCDisconnected: "IRC Disconnected",
|
||||||
|
domain.NotificationEventIRCReconnected: "IRC Reconnected",
|
||||||
|
domain.NotificationEventTest: "Test",
|
||||||
|
}
|
||||||
|
|
||||||
|
if title, ok := titles[event]; ok {
|
||||||
|
return title
|
||||||
|
}
|
||||||
|
|
||||||
|
return "New Event"
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ package notification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -16,7 +15,6 @@ import (
|
||||||
"github.com/autobrr/autobrr/internal/domain"
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
"github.com/autobrr/autobrr/pkg/errors"
|
"github.com/autobrr/autobrr/pkg/errors"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,6 +32,7 @@ type pushoverSender struct {
|
||||||
log zerolog.Logger
|
log zerolog.Logger
|
||||||
Settings domain.Notification
|
Settings domain.Notification
|
||||||
baseUrl string
|
baseUrl string
|
||||||
|
builder NotificationBuilderPlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPushoverSender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
func NewPushoverSender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
||||||
|
@ -45,12 +44,16 @@ func NewPushoverSender(log zerolog.Logger, settings domain.Notification) domain.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *pushoverSender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
func (s *pushoverSender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
||||||
|
|
||||||
|
title := s.builder.BuildTitle(event)
|
||||||
|
message := s.builder.BuildBody(payload)
|
||||||
|
|
||||||
m := pushoverMessage{
|
m := pushoverMessage{
|
||||||
Token: s.Settings.APIKey,
|
Token: s.Settings.APIKey,
|
||||||
User: s.Settings.Token,
|
User: s.Settings.Token,
|
||||||
Priority: s.Settings.Priority,
|
Priority: s.Settings.Priority,
|
||||||
Message: s.buildMessage(payload),
|
Message: message,
|
||||||
Title: s.buildTitle(event),
|
Title: title,
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
Html: 1,
|
Html: 1,
|
||||||
}
|
}
|
||||||
|
@ -139,61 +142,3 @@ func (s *pushoverSender) isEnabledEvent(event domain.NotificationEvent) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *pushoverSender) buildMessage(payload domain.NotificationPayload) string {
|
|
||||||
msg := ""
|
|
||||||
|
|
||||||
if payload.Subject != "" && payload.Message != "" {
|
|
||||||
msg += fmt.Sprintf("%v\n<b>%v</b>", payload.Subject, html.EscapeString(payload.Message))
|
|
||||||
}
|
|
||||||
if payload.ReleaseName != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>New release:</b> %v", html.EscapeString(payload.ReleaseName))
|
|
||||||
}
|
|
||||||
if payload.Size > 0 {
|
|
||||||
msg += fmt.Sprintf("\n<b>Size:</b> %v", humanize.Bytes(payload.Size))
|
|
||||||
}
|
|
||||||
if payload.Status != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>Status:</b> %v", payload.Status.String())
|
|
||||||
}
|
|
||||||
if payload.Indexer != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>Indexer:</b> %v", payload.Indexer)
|
|
||||||
}
|
|
||||||
if payload.Filter != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>Filter:</b> %v", html.EscapeString(payload.Filter))
|
|
||||||
}
|
|
||||||
if payload.Action != "" {
|
|
||||||
action := fmt.Sprintf("\n<b>Action:</b> %v <b>Type:</b> %v", html.EscapeString(payload.Action), payload.ActionType)
|
|
||||||
if payload.ActionClient != "" {
|
|
||||||
action += fmt.Sprintf(" <b>Client:</b> %v", html.EscapeString(payload.ActionClient))
|
|
||||||
}
|
|
||||||
msg += action
|
|
||||||
}
|
|
||||||
if len(payload.Rejections) > 0 {
|
|
||||||
msg += fmt.Sprintf("\nRejections: %v", strings.Join(payload.Rejections, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *pushoverSender) buildTitle(event domain.NotificationEvent) string {
|
|
||||||
title := ""
|
|
||||||
|
|
||||||
switch event {
|
|
||||||
case domain.NotificationEventAppUpdateAvailable:
|
|
||||||
title = "Autobrr update available"
|
|
||||||
case domain.NotificationEventPushApproved:
|
|
||||||
title = "Push Approved"
|
|
||||||
case domain.NotificationEventPushRejected:
|
|
||||||
title = "Push Rejected"
|
|
||||||
case domain.NotificationEventPushError:
|
|
||||||
title = "Error"
|
|
||||||
case domain.NotificationEventIRCDisconnected:
|
|
||||||
title = "IRC Disconnected"
|
|
||||||
case domain.NotificationEventIRCReconnected:
|
|
||||||
title = "IRC Reconnected"
|
|
||||||
case domain.NotificationEventTest:
|
|
||||||
title = "Test"
|
|
||||||
}
|
|
||||||
|
|
||||||
return title
|
|
||||||
}
|
|
||||||
|
|
|
@ -132,6 +132,8 @@ func (s *service) registerSenders() {
|
||||||
s.senders = append(s.senders, NewPushoverSender(s.log, n))
|
s.senders = append(s.senders, NewPushoverSender(s.log, n))
|
||||||
case domain.NotificationTypeGotify:
|
case domain.NotificationTypeGotify:
|
||||||
s.senders = append(s.senders, NewGotifySender(s.log, n))
|
s.senders = append(s.senders, NewGotifySender(s.log, n))
|
||||||
|
case domain.NotificationTypeLunaSea:
|
||||||
|
s.senders = append(s.senders, NewLunaSeaSender(s.log, n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,6 +249,8 @@ func (s *service) Test(ctx context.Context, notification domain.Notification) er
|
||||||
agent = NewPushoverSender(s.log, notification)
|
agent = NewPushoverSender(s.log, notification)
|
||||||
case domain.NotificationTypeGotify:
|
case domain.NotificationTypeGotify:
|
||||||
agent = NewGotifySender(s.log, notification)
|
agent = NewGotifySender(s.log, notification)
|
||||||
|
case domain.NotificationTypeLunaSea:
|
||||||
|
agent = NewLunaSeaSender(s.log, notification)
|
||||||
default:
|
default:
|
||||||
s.log.Error().Msgf("unsupported notification type: %v", notification.Type)
|
s.log.Error().Msgf("unsupported notification type: %v", notification.Type)
|
||||||
return errors.New("unsupported notification type")
|
return errors.New("unsupported notification type")
|
||||||
|
|
|
@ -7,17 +7,14 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/autobrr/autobrr/internal/domain"
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
"github.com/autobrr/autobrr/pkg/errors"
|
"github.com/autobrr/autobrr/pkg/errors"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,6 +30,7 @@ type telegramSender struct {
|
||||||
log zerolog.Logger
|
log zerolog.Logger
|
||||||
Settings domain.Notification
|
Settings domain.Notification
|
||||||
ThreadID int
|
ThreadID int
|
||||||
|
builder NotificationBuilderPlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTelegramSender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
func NewTelegramSender(log zerolog.Logger, settings domain.Notification) domain.NotificationSender {
|
||||||
|
@ -52,9 +50,10 @@ func NewTelegramSender(log zerolog.Logger, settings domain.Notification) domain.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *telegramSender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
func (s *telegramSender) Send(event domain.NotificationEvent, payload domain.NotificationPayload) error {
|
||||||
|
message := s.builder.BuildBody(payload)
|
||||||
m := TelegramMessage{
|
m := TelegramMessage{
|
||||||
ChatID: s.Settings.Channel,
|
ChatID: s.Settings.Channel,
|
||||||
Text: s.buildMessage(event, payload),
|
Text: message,
|
||||||
MessageThreadID: s.ThreadID,
|
MessageThreadID: s.ThreadID,
|
||||||
ParseMode: "HTML",
|
ParseMode: "HTML",
|
||||||
//ParseMode: "MarkdownV2",
|
//ParseMode: "MarkdownV2",
|
||||||
|
@ -126,38 +125,3 @@ func (s *telegramSender) isEnabledEvent(event domain.NotificationEvent) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *telegramSender) buildMessage(event domain.NotificationEvent, payload domain.NotificationPayload) string {
|
|
||||||
msg := ""
|
|
||||||
|
|
||||||
if payload.Subject != "" && payload.Message != "" {
|
|
||||||
msg += fmt.Sprintf("%v\n<b>%v</b>", payload.Subject, html.EscapeString(payload.Message))
|
|
||||||
}
|
|
||||||
if payload.ReleaseName != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>New release:</b> %v", html.EscapeString(payload.ReleaseName))
|
|
||||||
}
|
|
||||||
if payload.Size > 0 {
|
|
||||||
msg += fmt.Sprintf("\n<b>File Size:</b> %v", html.EscapeString(humanize.Bytes(payload.Size)))
|
|
||||||
}
|
|
||||||
if payload.Status != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>Status:</b> %v", payload.Status.String())
|
|
||||||
}
|
|
||||||
if payload.Indexer != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>Indexer:</b> %v", payload.Indexer)
|
|
||||||
}
|
|
||||||
if payload.Filter != "" {
|
|
||||||
msg += fmt.Sprintf("\n<b>Filter:</b> %v", html.EscapeString(payload.Filter))
|
|
||||||
}
|
|
||||||
if payload.Action != "" {
|
|
||||||
action := fmt.Sprintf("\n<b>Action:</b> %v <b>Type:</b> %v", html.EscapeString(payload.Action), payload.ActionType)
|
|
||||||
if payload.ActionClient != "" {
|
|
||||||
action += fmt.Sprintf(" <b>Client:</b> %v", html.EscapeString(payload.ActionClient))
|
|
||||||
}
|
|
||||||
msg += action
|
|
||||||
}
|
|
||||||
if len(payload.Rejections) > 0 {
|
|
||||||
msg += fmt.Sprintf("\nRejections: %v", strings.Join(payload.Rejections, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ export const PasswordFieldWide = ({
|
||||||
meta.touched && meta.error
|
meta.touched && meta.error
|
||||||
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
? "border-red-500 focus:ring-red-500 focus:border-red-500"
|
||||||
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
: "border-gray-300 dark:border-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500",
|
||||||
"block w-full shadow-sm sm:text-sm rounded-md border py-2.5 bg-gray-100 dark:bg-gray-850 dark:text-gray-100"
|
"block w-full shadow-sm sm:text-sm rounded-md border py-2.5 bg-gray-100 dark:bg-gray-850 dark:text-gray-100 overflow-hidden pr-8"
|
||||||
)}
|
)}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
|
@ -399,6 +399,10 @@ export const NotificationTypeOptions: OptionBasicTyped<NotificationType>[] = [
|
||||||
{
|
{
|
||||||
label: "Gotify",
|
label: "Gotify",
|
||||||
value: "GOTIFY"
|
value: "GOTIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "LunaSea",
|
||||||
|
value: "LUNASEA"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,36 @@ function FormFieldsNotifiarr() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FormFieldsLunaSea() {
|
||||||
|
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">
|
||||||
|
LunaSea offers notifications across all devices linked to your account (User-Based) or to a single device without an account, using a unique webhook per device (Device-Based).
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
{"Read the "}
|
||||||
|
<ExternalLink
|
||||||
|
href="https://docs.lunasea.app/lunasea/notifications"
|
||||||
|
className="font-medium text-blue-500 underline underline-offset-1 hover:text-blue-400"
|
||||||
|
>
|
||||||
|
LunaSea docs
|
||||||
|
</ExternalLink>
|
||||||
|
{"."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PasswordFieldWide
|
||||||
|
name="webhook"
|
||||||
|
label="Webhook URL"
|
||||||
|
help="LunaSea Webhook URL"
|
||||||
|
placeholder="https://notify.lunasea.app/v1/custom/user/TOKEN"
|
||||||
|
/>
|
||||||
|
</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">
|
||||||
|
@ -172,7 +202,8 @@ const componentMap: componentMapType = {
|
||||||
NOTIFIARR: <FormFieldsNotifiarr />,
|
NOTIFIARR: <FormFieldsNotifiarr />,
|
||||||
TELEGRAM: <FormFieldsTelegram />,
|
TELEGRAM: <FormFieldsTelegram />,
|
||||||
PUSHOVER: <FormFieldsPushover />,
|
PUSHOVER: <FormFieldsPushover />,
|
||||||
GOTIFY: <FormFieldsGotify />
|
GOTIFY: <FormFieldsGotify />,
|
||||||
|
LUNASEA: <FormFieldsLunaSea />
|
||||||
};
|
};
|
||||||
|
|
||||||
interface NotificationAddFormValues {
|
interface NotificationAddFormValues {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import toast from "react-hot-toast";
|
||||||
import { Section } from "./_components";
|
import { Section } from "./_components";
|
||||||
import { PlusIcon } from "@heroicons/react/24/solid";
|
import { PlusIcon } from "@heroicons/react/24/solid";
|
||||||
import { Checkbox } from "@components/Checkbox";
|
import { Checkbox } from "@components/Checkbox";
|
||||||
|
import { DiscordIcon, GotifyIcon, LunaSeaIcon, NotifiarrIcon, PushoverIcon, TelegramIcon } from "./_components";
|
||||||
|
|
||||||
export const notificationKeys = {
|
export const notificationKeys = {
|
||||||
all: ["notifications"] as const,
|
all: ["notifications"] as const,
|
||||||
|
@ -68,44 +69,14 @@ function NotificationSettings() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const iconStyle = "flex items-center px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-400";
|
||||||
const DiscordIcon = () => (
|
|
||||||
<svg viewBox="0 0 71 71" xmlns="http://www.w3.org/2000/svg" className="mr-2 h-4">
|
|
||||||
<path
|
|
||||||
d="M60.104 12.927a58.55 58.55 0 0 0-14.452-4.482.22.22 0 0 0-.232.11 40.783 40.783 0 0 0-1.8 3.696c-5.457-.817-10.886-.817-16.232 0-.484-1.164-1.2-2.586-1.827-3.696a.228.228 0 0 0-.233-.11 58.39 58.39 0 0 0-14.452 4.482.207.207 0 0 0-.095.082C1.577 26.759-.945 40.174.292 53.42a.244.244 0 0 0 .093.166c6.073 4.46 11.956 7.167 17.729 8.962a.23.23 0 0 0 .249-.082 42.08 42.08 0 0 0 3.627-5.9.225.225 0 0 0-.123-.312 38.772 38.772 0 0 1-5.539-2.64.228.228 0 0 1-.022-.377c.372-.28.744-.57 1.1-.862a.22.22 0 0 1 .23-.031c11.62 5.305 24.198 5.305 35.681 0a.219.219 0 0 1 .232.028c.356.293.728.586 1.103.865a.228.228 0 0 1-.02.377 36.384 36.384 0 0 1-5.54 2.637.227.227 0 0 0-.12.316 47.249 47.249 0 0 0 3.623 5.897.225.225 0 0 0 .25.084c5.8-1.795 11.683-4.502 17.756-8.962a.228.228 0 0 0 .093-.163c1.48-15.315-2.48-28.618-10.498-40.412a.18.18 0 0 0-.093-.085zM23.725 45.355c-3.498 0-6.38-3.212-6.38-7.156s2.826-7.156 6.38-7.156c3.582 0 6.437 3.24 6.38 7.156 0 3.944-2.826 7.156-6.38 7.156zm23.592 0c-3.498 0-6.38-3.212-6.38-7.156s2.826-7.156 6.38-7.156c3.582 0 6.437 3.24 6.38 7.156 0 3.944-2.798 7.156-6.38 7.156z"
|
|
||||||
fill="currentColor"></path>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
|
|
||||||
const TelegramIcon = () => (
|
|
||||||
<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" className="mr-2 h-4">
|
|
||||||
<path
|
|
||||||
d="M0 24c0 13.255 10.745 24 24 24s24-10.745 24-24S37.255 0 24 0 0 10.745 0 24zm19.6 11 .408-6.118 11.129-10.043c.488-.433-.107-.645-.755-.252l-13.735 8.665-5.933-1.851c-1.28-.393-1.29-1.273.288-1.906l23.118-8.914c1.056-.48 2.075.254 1.672 1.87l-3.937 18.553c-.275 1.318-1.072 1.633-2.175 1.024l-5.998-4.43L20.8 34.4l-.027.027c-.323.314-.59.573-1.173.573z"
|
|
||||||
clipRule="evenodd" fill="currentColor" fillRule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
|
|
||||||
const PushoverIcon = () => (
|
|
||||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" className="mr-2 h-4">
|
|
||||||
<path d="m495.6 319.4 104-13.7-101.3 228.6c17.8-1.4 35.2-7.4 52.3-18.1 17.1-10.7 32.9-24.2 47.2-40.4 14.4-16.2 26.8-34.2 37.3-54.1 10.5-19.8 18-39.4 22.6-58.5 2.7-11.9 4-23.3 3.8-34.2-.2-10.9-3.1-20.5-8.6-28.7s-13.8-14.8-25-19.8-26.3-7.5-45.5-7.5c-22.4 0-44.4 3.6-66 10.9-21.7 7.3-41.7 17.9-60.2 31.8-18.5 13.9-34.5 31.2-48.2 52-13.7 20.8-23.5 44.4-29.4 70.8-2.3 8.7-3.6 15.6-4.1 20.9-.5 5.3-.6 9.6-.3 13 .2 3.4.7 6.1 1.4 7.9.7 1.8 1.3 3.6 1.7 5.5-23.3 0-40.3-4.7-51-14-10.7-9.3-13.3-25.7-7.9-48.9 5.5-24.2 17.9-47.2 37.3-69.1 19.4-21.9 42.4-41.2 69.1-57.8 26.7-16.6 55.9-29.9 87.6-39.7 31.7-9.8 62.6-14.7 92.7-14.7 26.5 0 48.7 3.8 66.7 11.3 18 7.5 32.1 17.5 42.1 29.8s16.3 26.7 18.8 43.1c2.5 16.4 1.7 33.5-2.4 51.3-5 21.4-14.5 43-28.4 64.7-13.9 21.7-31.4 41.3-52.3 58.8-21 17.6-45 31.8-72.2 42.8-27.1 10.9-56 16.4-86.6 16.4h-3.4l-86.9 195H302l193.6-435.4z"
|
|
||||||
clipRule="evenodd" fill="currentColor" fillRule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
|
|
||||||
const GotifyIcon = () => (
|
|
||||||
<svg viewBox="0 0 140 140" xmlns="http://www.w3.org/2000/svg" className="mr-2 h-4">
|
|
||||||
<path d="m 114.5,21.4 c -11.7,0 -47.3,5.9 -54.3,7.1 -47.3,8.0 -48.4,9.9 -50.1,12.8 -1.2,2.1 -2.4,4.0 2.6,29.4 2.3,11.5 5.8,26.9 8.8,35.8 1.8,5.4 3.6,8.8 6.9,10.1 0.8,0.3 1.7,0.5 2.7,0.6 0.2,0.0 0.3,0.0 0.5,0.0 12.8,0 89.1,-19.5 89.9,-19.7 1.4,-0.4 4.0,-1.5 5.3,-5.1 1.8,-4.7 1.9,-16.7 0.5,-35.7 -2.1,-28.0 -4.1,-31.0 -4.8,-32.0 -2.0,-3.1 -5.6,-3.3 -6.7,-3.3 -0.4,-0.0 -0.9,-0.0 -1.4,-0.0 z m -1.9,6.6 c -9.3,12.0 -18.9,24.0 -25.9,32.4 -2.3,2.8 -4.3,5.1 -6.0,7.0 -1.7,1.9 -2.9,3.2 -3.8,4.0 l -0.3,0.3 -0.4,-0.1 c -1.0,-0.3 -2.5,-0.9 -4.4,-1.7 -2.3,-1.0 -5.2,-2.3 -8.8,-3.9 C 51.6,60.7 34.4,52.2 18.0,43.6 30.3,39.7 95.0,28.7 112.6,27.9 Z m 5.7,5.0 c 2.0,11.8 4.5,42.6 3.1,54.0 -1.8,-1.4 -10.1,-8.0 -19.8,-15.2 -3.0,-2.3 -5.9,-4.3 -8.4,-6.1 l -0.7,-0.5 0.5,-0.6 C 99.5,56.9 108.0,46.2 118.3,32.9 Z M 16.1,51.1 c 3.0,1.5 14.3,7.4 27.4,13.8 5.3,2.6 9.9,4.8 13.9,6.7 l 0.9,0.4 -0.7,0.8 C 50.3,81.2 40.6,92.8 28.8,107.2 24.5,96.7 17.9,65.0 16.1,51.1 Z m 71.5,19.7 0.6,0.4 c 7.8,5.5 18.1,13.2 27.9,21.0 C 104.9,95.1 53.2,107.9 36.0,110.3 46.6,97.4 57.3,84.7 65.1,75.8 l 0.4,-0.4 0.5,0.2 c 5.7,2.5 9.3,3.7 11.1,3.8 0.1,0.0 0.2,0.0 0.3,0.0 0.6,0 1.0,-0.1 1.4,-0.3 0.6,-0.2 2.0,-0.7 8.3,-7.7 z"
|
|
||||||
clipRule="evenodd" fill="currentColor" fillRule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
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={iconStyle}><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>,
|
NOTIFIARR: <span className={iconStyle}><NotifiarrIcon /> 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={iconStyle}><TelegramIcon /> Telegram</span>,
|
||||||
PUSHOVER: <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"><PushoverIcon /> Pushover</span>,
|
PUSHOVER: <span className={iconStyle}><PushoverIcon /> Pushover</span>,
|
||||||
GOTIFY: <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"><GotifyIcon /> Gotify</span>
|
GOTIFY: <span className={iconStyle}><GotifyIcon /> Gotify</span>,
|
||||||
|
LUNASEA: <span className={iconStyle}><LunaSeaIcon /> LunaSea</span>
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ListItemProps {
|
interface ListItemProps {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { classNames } from "@utils";
|
import { classNames } from "@utils";
|
||||||
|
import { SVGProps } from "react";
|
||||||
|
|
||||||
type SectionProps = {
|
type SectionProps = {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -82,3 +83,46 @@ export const RowItem = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const commonSVGProps: SVGProps<SVGSVGElement> = {
|
||||||
|
clipRule: "evenodd", fill: "currentColor", fillRule: "evenodd", xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
className: "mr-2 h-5"
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DiscordIcon = () => (
|
||||||
|
<svg {...commonSVGProps} viewBox="0 0 50 50">
|
||||||
|
<path strokeWidth="1" stroke="currentColor" d="M 18.90625 7 C 18.90625 7 12.539063 7.4375 8.375 10.78125 C 8.355469 10.789063 8.332031 10.800781 8.3125 10.8125 C 7.589844 11.480469 7.046875 12.515625 6.375 14 C 5.703125 15.484375 4.992188 17.394531 4.34375 19.53125 C 3.050781 23.808594 2 29.058594 2 34 C 1.996094 34.175781 2.039063 34.347656 2.125 34.5 C 3.585938 37.066406 6.273438 38.617188 8.78125 39.59375 C 11.289063 40.570313 13.605469 40.960938 14.78125 41 C 15.113281 41.011719 15.429688 40.859375 15.625 40.59375 L 18.0625 37.21875 C 20.027344 37.683594 22.332031 38 25 38 C 27.667969 38 29.972656 37.683594 31.9375 37.21875 L 34.375 40.59375 C 34.570313 40.859375 34.886719 41.011719 35.21875 41 C 36.394531 40.960938 38.710938 40.570313 41.21875 39.59375 C 43.726563 38.617188 46.414063 37.066406 47.875 34.5 C 47.960938 34.347656 48.003906 34.175781 48 34 C 48 29.058594 46.949219 23.808594 45.65625 19.53125 C 45.007813 17.394531 44.296875 15.484375 43.625 14 C 42.953125 12.515625 42.410156 11.480469 41.6875 10.8125 C 41.667969 10.800781 41.644531 10.789063 41.625 10.78125 C 37.460938 7.4375 31.09375 7 31.09375 7 C 31.019531 6.992188 30.949219 6.992188 30.875 7 C 30.527344 7.046875 30.234375 7.273438 30.09375 7.59375 C 30.09375 7.59375 29.753906 8.339844 29.53125 9.40625 C 27.582031 9.09375 25.941406 9 25 9 C 24.058594 9 22.417969 9.09375 20.46875 9.40625 C 20.246094 8.339844 19.90625 7.59375 19.90625 7.59375 C 19.734375 7.203125 19.332031 6.964844 18.90625 7 Z M 18.28125 9.15625 C 18.355469 9.359375 18.40625 9.550781 18.46875 9.78125 C 16.214844 10.304688 13.746094 11.160156 11.4375 12.59375 C 11.074219 12.746094 10.835938 13.097656 10.824219 13.492188 C 10.816406 13.882813 11.039063 14.246094 11.390625 14.417969 C 11.746094 14.585938 12.167969 14.535156 12.46875 14.28125 C 17.101563 11.410156 22.996094 11 25 11 C 27.003906 11 32.898438 11.410156 37.53125 14.28125 C 37.832031 14.535156 38.253906 14.585938 38.609375 14.417969 C 38.960938 14.246094 39.183594 13.882813 39.175781 13.492188 C 39.164063 13.097656 38.925781 12.746094 38.5625 12.59375 C 36.253906 11.160156 33.785156 10.304688 31.53125 9.78125 C 31.59375 9.550781 31.644531 9.359375 31.71875 9.15625 C 32.859375 9.296875 37.292969 9.894531 40.3125 12.28125 C 40.507813 12.460938 41.1875 13.460938 41.8125 14.84375 C 42.4375 16.226563 43.09375 18.027344 43.71875 20.09375 C 44.9375 24.125 45.921875 29.097656 45.96875 33.65625 C 44.832031 35.496094 42.699219 36.863281 40.5 37.71875 C 38.5 38.496094 36.632813 38.84375 35.65625 38.9375 L 33.96875 36.65625 C 34.828125 36.378906 35.601563 36.078125 36.28125 35.78125 C 38.804688 34.671875 40.15625 33.5 40.15625 33.5 C 40.570313 33.128906 40.605469 32.492188 40.234375 32.078125 C 39.863281 31.664063 39.226563 31.628906 38.8125 32 C 38.8125 32 37.765625 32.957031 35.46875 33.96875 C 34.625 34.339844 33.601563 34.707031 32.4375 35.03125 C 32.167969 35 31.898438 35.078125 31.6875 35.25 C 29.824219 35.703125 27.609375 36 25 36 C 22.371094 36 20.152344 35.675781 18.28125 35.21875 C 18.070313 35.078125 17.8125 35.019531 17.5625 35.0625 C 16.394531 34.738281 15.378906 34.339844 14.53125 33.96875 C 12.234375 32.957031 11.1875 32 11.1875 32 C 10.960938 31.789063 10.648438 31.699219 10.34375 31.75 C 9.957031 31.808594 9.636719 32.085938 9.53125 32.464844 C 9.421875 32.839844 9.546875 33.246094 9.84375 33.5 C 9.84375 33.5 11.195313 34.671875 13.71875 35.78125 C 14.398438 36.078125 15.171875 36.378906 16.03125 36.65625 L 14.34375 38.9375 C 13.367188 38.84375 11.5 38.496094 9.5 37.71875 C 7.300781 36.863281 5.167969 35.496094 4.03125 33.65625 C 4.078125 29.097656 5.0625 24.125 6.28125 20.09375 C 6.90625 18.027344 7.5625 16.226563 8.1875 14.84375 C 8.8125 13.460938 9.492188 12.460938 9.6875 12.28125 C 12.707031 9.894531 17.140625 9.296875 18.28125 9.15625 Z M 18.5 21 C 15.949219 21 14 23.316406 14 26 C 14 28.683594 15.949219 31 18.5 31 C 21.050781 31 23 28.683594 23 26 C 23 23.316406 21.050781 21 18.5 21 Z M 31.5 21 C 28.949219 21 27 23.316406 27 26 C 27 28.683594 28.949219 31 31.5 31 C 34.050781 31 36 28.683594 36 26 C 36 23.316406 34.050781 21 31.5 21 Z M 18.5 23 C 19.816406 23 21 24.265625 21 26 C 21 27.734375 19.816406 29 18.5 29 C 17.183594 29 16 27.734375 16 26 C 16 24.265625 17.183594 23 18.5 23 Z M 31.5 23 C 32.816406 23 34 24.265625 34 26 C 34 27.734375 32.816406 29 31.5 29 C 30.183594 29 29 27.734375 29 26 C 29 24.265625 30.183594 23 31.5 23 Z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NotifiarrIcon = () => (
|
||||||
|
<svg {...commonSVGProps} viewBox="0 0 144 144">
|
||||||
|
<path d="m68.4 2.3c-1.7 4.2 0.5 7.6 5.6 8.6 1.4 0.2 2.9 1.3 3.5 2.3 0.5 1 3.4 5.4 6.2 9.8 7.1 10.7 7.5 10.1-7.5 9.6-15.5-0.5-31.6 1.8-47.4 6.9-10 3.3-10.6 3.4-13.6 1.9-5.7-2.7-10.2-0.1-10.2 5.8 0 6.1 5.5 9.3 10.8 6.1 4-2.4 29.6 14.9 40.1 27 13.8 15.9 21.7 42.3 14.2 47.4-6.5 4.4-2.9 14.3 4.8 13.1 2-0.3 4.2-0.6 4.9-0.6 1.9 0 3.3-4.4 2.2-7.2-0.8-2-0.2-3.5 4.4-10.2 8.2-12 14.6-27.9 14.6-36.1 0-5.9-3.5-4-4.4 2.4-2.9 18.7-19.9 45.2-20.3 31.6-0.8-25.8-19.3-50.6-49.9-66.9-9.6-5.1-8.2-7.9 5.7-12.2 17.3-5.2 42.4-7.8 54.9-5.6 7.6 1.3 15.5 31.8 9.3 36-4.2 2.8 0.2 11.4 4.7 9.1 1.2-0.7 2.4-0.9 2.7-0.6 3 2.8 3.8-3.4 0.9-6.8-1.9-2.2-2.4-4.1-2.9-11.2-0.5-7.1-2.5-16-5.4-23-0.3-0.9 0.2-1.4 1.4-1.4 6.6 0 33.3 11.5 33.3 14.3 0 4.6 10.5 6.6 11.6 2.3 1.2-4.4-3.6-8.6-8.7-7.6-2.2 0.4-5.3-0.6-12.8-4.1-5.5-2.6-13.9-5.6-18.6-6.8-8.6-2.2-8.6-2.2-11.5-7.6-1.6-3-4.9-8.2-7.3-11.6-3.2-4.4-4.3-6.9-4-8.5 1-5.2-9.4-10.9-11.3-6.2z"/>
|
||||||
|
<path d="m67.9 5.6c0.6 3.4 1.8 4.4 6.1 5.3 1.4 0.2 2.9 1.3 3.5 2.3 0.5 1 3.4 5.4 6.2 9.8 2.9 4.3 5.3 8.6 5.3 9.4 0 0.8-1.8-1.4-3.9-4.9-5.9-9.7-9-13.2-11.6-13.2-5.8 0-9.4-4.6-7.3-9.2 1.1-2.7 1.1-2.7 1.7 0.5zm-4.7-1.7c0.5-0.1 0.8 1.4 0.8 3.2 0 1.8 0.2 3.8 0.5 4.5 0.4 1-1.2 1.6-5.6 2.4-12.9 2.4-27.1 10.8-35.6 21.3-3.8 4.7-4.9 5.5-6.4 4.7-1-0.5-2.9-0.9-4.3-0.9-3.4 0-3.3-0.2 1.8-7 9.8-13.2 26-23.6 42.1-26.9 3.3-0.7 6.3-1.3 6.7-1.3zm21.3 0.5c20.4 3.4 38.8 15.6 50 33.3 4.2 6.7 4.2 6.7 0.7 7.4-1.5 0.3-3.4 0.8-4.3 1.1-1.2 0.5-2.5-0.8-4.9-4.9-7.1-12.1-24.6-24.3-38.9-27.1-5.9-1.2-5.9-1.2-5.7-5.7 0.1-4.5 0.1-4.5 3.1-4.1zm3 31.7c5.5 0.9 9.6 11.5 10.1 26.4 0.3 9.5 0.3 9.5-0.6 0.4-1.8-18.8-4.9-24.5-13.8-25.3-16.8-1.5-39.2 1.4-55.5 7.2-3.2 1.2-6.1 1.9-6.4 1.6-1.5-1.5 18.4-7.6 31.7-9.7 8.2-1.3 28.1-1.7 34.5-0.6zm10.2 2c6.6 0 33.3 11.5 33.3 14.3 0 2.6 3.6 5.1 6.2 4.5 3.4-0.8 5.7 0.8 3 2.1-5.2 2.7-11.2 0.2-11.2-4.6 0-3.1-9.7-8.1-23.7-12.4-4.6-1.4-8.6-2.9-9-3.2-0.3-0.4 0.3-0.7 1.4-0.7zm-92.5 8.2c-0.6 6.8 4.9 10.4 10.6 7 1.5-0.9 3.4-0.2 10.4 3.8 29.3 16.5 44.4 36.7 46.1 61.7 0.6 7.6 0.6 7.6-0.4 1.4-4.5-28-17.3-44.9-46.2-61.3-6.7-3.8-6.7-3.8-10.3-2.2-8.2 3.7-16-3.6-12.1-11.5 2.1-4.2 2.3-4.1 1.9 1.1zm-0.3 11.3c0.7 0.8 2.2 1.5 3.3 1.7 2 0.3 2.1 1 2.3 12.7 0.5 28.8 19.3 51.9 46.9 57.8 5.2 1.1 5.2 1.1 5.2 5.7 0 5.2 0.9 4.9-9.1 2.6-34-7.9-57.8-43.9-51.9-78.3 0.7-3.8 1.5-4.3 3.3-2.2zm135.6 35.6c-7.5 22.6-35.3 46-54.7 46-5.8 0-2.1-8.3 4.6-10.2 27.2-7.9 45.9-33 44.4-59.6-0.5-7.4-0.5-7.4 2.3-7.4 1.5 0 3.1-0.4 3.4-1 3.6-5.5 3.6 21.3 0 32.2zm-45.5-16.6c0 2.7 3.4 5.9 5.1 4.9 3-1.8 6.2-1.4 4.3 0.6-1.5 1.6-4.6 2.1-8.5 1.2-2.1-0.5-4.1-4.8-3.4-7.1 0.9-2.5 2.5-2.3 2.5 0.4zm-26.3 51.2c0.4 0 0.2 0.5-0.5 1.1-5 4.8 0.5 12.7 8.1 11.8 5.2-0.6 5.2-0.6 2.8 1.3-7.6 6.3-18.5-2.9-13.2-11.1 1.2-1.7 2.4-3.1 2.8-3.1z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TelegramIcon = () => (
|
||||||
|
<svg {...commonSVGProps} viewBox="0 0 50 50">
|
||||||
|
<path d="M 25 2 C 12.309288 2 2 12.309297 2 25 C 2 37.690703 12.309288 48 25 48 C 37.690712 48 48 37.690703 48 25 C 48 12.309297 37.690712 2 25 2 z M 25 4 C 36.609833 4 46 13.390175 46 25 C 46 36.609825 36.609833 46 25 46 C 13.390167 46 4 36.609825 4 25 C 4 13.390175 13.390167 4 25 4 z M 34.087891 14.035156 C 33.403891 14.035156 32.635328 14.193578 31.736328 14.517578 C 30.340328 15.020578 13.920734 21.992156 12.052734 22.785156 C 10.984734 23.239156 8.9960938 24.083656 8.9960938 26.097656 C 8.9960938 27.432656 9.7783594 28.3875 11.318359 28.9375 C 12.146359 29.2325 14.112906 29.828578 15.253906 30.142578 C 15.737906 30.275578 16.25225 30.34375 16.78125 30.34375 C 17.81625 30.34375 18.857828 30.085859 19.673828 29.630859 C 19.666828 29.798859 19.671406 29.968672 19.691406 30.138672 C 19.814406 31.188672 20.461875 32.17625 21.421875 32.78125 C 22.049875 33.17725 27.179312 36.614156 27.945312 37.160156 C 29.021313 37.929156 30.210813 38.335938 31.382812 38.335938 C 33.622813 38.335938 34.374328 36.023109 34.736328 34.912109 C 35.261328 33.299109 37.227219 20.182141 37.449219 17.869141 C 37.600219 16.284141 36.939641 14.978953 35.681641 14.376953 C 35.210641 14.149953 34.672891 14.035156 34.087891 14.035156 z M 34.087891 16.035156 C 34.362891 16.035156 34.608406 16.080641 34.816406 16.181641 C 35.289406 16.408641 35.530031 16.914688 35.457031 17.679688 C 35.215031 20.202687 33.253938 33.008969 32.835938 34.292969 C 32.477938 35.390969 32.100813 36.335938 31.382812 36.335938 C 30.664813 36.335938 29.880422 36.08425 29.107422 35.53125 C 28.334422 34.97925 23.201281 31.536891 22.488281 31.087891 C 21.863281 30.693891 21.201813 29.711719 22.132812 28.761719 C 22.899812 27.979719 28.717844 22.332938 29.214844 21.835938 C 29.584844 21.464938 29.411828 21.017578 29.048828 21.017578 C 28.923828 21.017578 28.774141 21.070266 28.619141 21.197266 C 28.011141 21.694266 19.534781 27.366266 18.800781 27.822266 C 18.314781 28.124266 17.56225 28.341797 16.78125 28.341797 C 16.44825 28.341797 16.111109 28.301891 15.787109 28.212891 C 14.659109 27.901891 12.750187 27.322734 11.992188 27.052734 C 11.263188 26.792734 10.998047 26.543656 10.998047 26.097656 C 10.998047 25.463656 11.892938 25.026 12.835938 24.625 C 13.831938 24.202 31.066062 16.883437 32.414062 16.398438 C 33.038062 16.172438 33.608891 16.035156 34.087891 16.035156 z" strokeWidth="1" stroke="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const PushoverIcon = () => (
|
||||||
|
<svg {...commonSVGProps} viewBox="0 0 600 600">
|
||||||
|
<path d="M 280.949 172.514 L 355.429 162.714 L 282.909 326.374 L 282.909 326.374 C 295.649 325.394 308.142 321.067 320.389 313.394 L 320.389 313.394 L 320.389 313.394 C 332.642 305.714 343.916 296.077 354.209 284.484 L 354.209 284.484 L 354.209 284.484 C 364.496 272.884 373.396 259.981 380.909 245.774 L 380.909 245.774 L 380.909 245.774 C 388.422 231.561 393.812 217.594 397.079 203.874 L 397.079 203.874 L 397.079 203.874 C 399.039 195.381 399.939 187.214 399.779 179.374 L 399.779 179.374 L 399.779 179.374 C 399.612 171.534 397.569 164.674 393.649 158.794 L 393.649 158.794 L 393.649 158.794 C 389.729 152.914 383.766 148.177 375.759 144.584 L 375.759 144.584 L 375.759 144.584 C 367.759 140.991 356.899 139.194 343.179 139.194 L 343.179 139.194 L 343.179 139.194 C 327.172 139.194 311.409 141.807 295.889 147.034 L 295.889 147.034 L 295.889 147.034 C 280.376 152.261 266.002 159.857 252.769 169.824 L 252.769 169.824 L 252.769 169.824 C 239.542 179.784 228.029 192.197 218.229 207.064 L 218.229 207.064 L 218.229 207.064 C 208.429 221.924 201.406 238.827 197.159 257.774 L 197.159 257.774 L 197.159 257.774 C 195.526 263.981 194.546 268.961 194.219 272.714 L 194.219 272.714 L 194.219 272.714 C 193.892 276.474 193.812 279.577 193.979 282.024 L 193.979 282.024 L 193.979 282.024 C 194.139 284.477 194.462 286.357 194.949 287.664 L 194.949 287.664 L 194.949 287.664 C 195.442 288.971 195.852 290.277 196.179 291.584 L 196.179 291.584 L 196.179 291.584 C 179.519 291.584 167.349 288.234 159.669 281.534 L 159.669 281.534 L 159.669 281.534 C 151.996 274.841 150.119 263.164 154.039 246.504 L 154.039 246.504 L 154.039 246.504 C 157.959 229.191 166.862 212.694 180.749 197.014 L 180.749 197.014 L 180.749 197.014 C 194.629 181.334 211.122 167.531 230.229 155.604 L 230.229 155.604 L 230.229 155.604 C 249.342 143.684 270.249 134.214 292.949 127.194 L 292.949 127.194 L 292.949 127.194 C 315.656 120.167 337.789 116.654 359.349 116.654 L 359.349 116.654 L 359.349 116.654 C 378.296 116.654 394.219 119.347 407.119 124.734 L 407.119 124.734 L 407.119 124.734 C 420.026 130.127 430.072 137.234 437.259 146.054 L 437.259 146.054 L 437.259 146.054 C 444.446 154.874 448.936 165.164 450.729 176.924 L 450.729 176.924 L 450.729 176.924 C 452.529 188.684 451.959 200.934 449.019 213.674 L 449.019 213.674 L 449.019 213.674 C 445.426 229.027 438.646 244.464 428.679 259.984 L 428.679 259.984 L 428.679 259.984 C 418.719 275.497 406.226 289.544 391.199 302.124 L 391.199 302.124 L 391.199 302.124 C 376.172 314.697 358.939 324.904 339.499 332.744 L 339.499 332.744 L 339.499 332.744 C 320.066 340.584 299.406 344.504 277.519 344.504 L 277.519 344.504 L 275.069 344.504 L 212.839 484.154 L 142.279 484.154 L 280.949 172.514 Z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const GotifyIcon = () => (
|
||||||
|
<svg {...commonSVGProps} viewBox="0 0 140 140">
|
||||||
|
<path d="m 114.5,21.4 c -11.7,0 -47.3,5.9 -54.3,7.1 -47.3,8.0 -48.4,9.9 -50.1,12.8 -1.2,2.1 -2.4,4.0 2.6,29.4 2.3,11.5 5.8,26.9 8.8,35.8 1.8,5.4 3.6,8.8 6.9,10.1 0.8,0.3 1.7,0.5 2.7,0.6 0.2,0.0 0.3,0.0 0.5,0.0 12.8,0 89.1,-19.5 89.9,-19.7 1.4,-0.4 4.0,-1.5 5.3,-5.1 1.8,-4.7 1.9,-16.7 0.5,-35.7 -2.1,-28.0 -4.1,-31.0 -4.8,-32.0 -2.0,-3.1 -5.6,-3.3 -6.7,-3.3 -0.4,-0.0 -0.9,-0.0 -1.4,-0.0 z m -1.9,6.6 c -9.3,12.0 -18.9,24.0 -25.9,32.4 -2.3,2.8 -4.3,5.1 -6.0,7.0 -1.7,1.9 -2.9,3.2 -3.8,4.0 l -0.3,0.3 -0.4,-0.1 c -1.0,-0.3 -2.5,-0.9 -4.4,-1.7 -2.3,-1.0 -5.2,-2.3 -8.8,-3.9 C 51.6,60.7 34.4,52.2 18.0,43.6 30.3,39.7 95.0,28.7 112.6,27.9 Z m 5.7,5.0 c 2.0,11.8 4.5,42.6 3.1,54.0 -1.8,-1.4 -10.1,-8.0 -19.8,-15.2 -3.0,-2.3 -5.9,-4.3 -8.4,-6.1 l -0.7,-0.5 0.5,-0.6 C 99.5,56.9 108.0,46.2 118.3,32.9 Z M 16.1,51.1 c 3.0,1.5 14.3,7.4 27.4,13.8 5.3,2.6 9.9,4.8 13.9,6.7 l 0.9,0.4 -0.7,0.8 C 50.3,81.2 40.6,92.8 28.8,107.2 24.5,96.7 17.9,65.0 16.1,51.1 Z m 71.5,19.7 0.6,0.4 c 7.8,5.5 18.1,13.2 27.9,21.0 C 104.9,95.1 53.2,107.9 36.0,110.3 46.6,97.4 57.3,84.7 65.1,75.8 l 0.4,-0.4 0.5,0.2 c 5.7,2.5 9.3,3.7 11.1,3.8 0.1,0.0 0.2,0.0 0.3,0.0 0.6,0 1.0,-0.1 1.4,-0.3 0.6,-0.2 2.0,-0.7 8.3,-7.7 z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const LunaSeaIcon = () => (
|
||||||
|
<svg {...commonSVGProps} viewBox="0 0 750 750">
|
||||||
|
<path d="m554.69 180.46c-333.63 0-452.75 389.23-556.05 389.23 185.37 0 237.85-247.18 419.12-247.18l47.24-102.05z"/>
|
||||||
|
<path d="m749.31 375.08c0 107.48-87.14 194.61-194.62 194.61s-194.62-87.13-194.62-194.61 87.13-194.62 194.62-194.62c7.391-2e-3 14.776 0.412 22.12 1.24-78.731 10.172-136.59 78.893-133.2 158.2 3.393 79.313 66.907 142.84 146.22 146.25 79.311 3.411 148.05-54.43 158.24-133.16 0.826 7.331 1.24 14.703 1.24 22.08z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
2
web/src/types/Notification.d.ts
vendored
2
web/src/types/Notification.d.ts
vendored
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type NotificationType = "DISCORD" | "NOTIFIARR" | "TELEGRAM" | "PUSHOVER" | "GOTIFY";
|
type NotificationType = "DISCORD" | "NOTIFIARR" | "TELEGRAM" | "PUSHOVER" | "GOTIFY" | "LUNASEA";
|
||||||
type NotificationEvent =
|
type NotificationEvent =
|
||||||
"PUSH_APPROVED"
|
"PUSH_APPROVED"
|
||||||
| "PUSH_REJECTED"
|
| "PUSH_REJECTED"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue