feat: add notifications (#216)

* feat: initial notifications support

* chore: update deps
This commit is contained in:
Ludvig Lundgren 2022-04-04 19:13:09 +02:00 committed by GitHub
parent 3185832708
commit 431742fd94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1632 additions and 36 deletions

View file

@ -0,0 +1,104 @@
package notification
import (
"bytes"
"crypto/tls"
"encoding/json"
"net/http"
"time"
"github.com/autobrr/autobrr/internal/domain"
"github.com/rs/zerolog/log"
)
type DiscordMessage struct {
Content interface{} `json:"content"`
Embeds []DiscordEmbeds `json:"embeds"`
Username string `json:"username"`
}
type DiscordEmbeds struct {
Title string `json:"title"`
Description string `json:"description"`
Color int `json:"color"`
Fields []DiscordEmbedsFields `json:"fields"`
Timestamp time.Time `json:"timestamp"`
}
type DiscordEmbedsFields struct {
Name string `json:"name"`
Value string `json:"value"`
Inline bool `json:"inline,omitempty"`
}
func discordNotification(event domain.EventsReleasePushed, webhookURL string) {
t := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := http.Client{Transport: t, Timeout: 15 * time.Second}
m := DiscordMessage{
Content: nil,
Embeds: []DiscordEmbeds{
{
Title: event.ReleaseName,
Description: "New release!",
Color: 5814783,
Fields: []DiscordEmbedsFields{
{
Name: "Status",
Value: event.Status.String(),
Inline: true,
},
{
Name: "Indexer",
Value: event.Indexer,
Inline: true,
},
{
Name: "Filter",
Value: event.Filter,
Inline: true,
},
{
Name: "Action",
Value: event.Action,
Inline: false,
},
},
Timestamp: time.Now(),
},
},
Username: "brr",
}
jsonData, err := json.Marshal(m)
if err != nil {
log.Error().Err(err).Msgf("discord client could not marshal data: %v", m)
return
}
req, err := http.NewRequest(http.MethodPost, webhookURL, bytes.NewBuffer(jsonData))
if err != nil {
//log.Error().Err(err).Msgf("webhook client request error: %v", action.WebhookHost)
return
}
req.Header.Set("Content-Type", "application/json")
//req.Header.Set("User-Agent", "autobrr")
res, err := client.Do(req)
if err != nil {
//log.Error().Err(err).Msgf("webhook client request error: %v", action.WebhookHost)
return
}
defer res.Body.Close()
log.Debug().Msg("notification successfully sent to discord")
return
}

View file

@ -0,0 +1,145 @@
package notification
import (
"context"
"fmt"
"github.com/autobrr/autobrr/internal/domain"
"github.com/containrrr/shoutrrr"
t "github.com/containrrr/shoutrrr/pkg/types"
)
type Service interface {
Find(ctx context.Context, params domain.NotificationQueryParams) ([]domain.Notification, int, error)
FindByID(ctx context.Context, id int) (*domain.Notification, error)
Store(ctx context.Context, n domain.Notification) (*domain.Notification, error)
Update(ctx context.Context, n domain.Notification) (*domain.Notification, error)
Delete(ctx context.Context, id int) error
Send(event domain.NotificationEvent, msg string) error
SendEvent(event domain.EventsReleasePushed) error
}
type service struct {
repo domain.NotificationRepo
}
func NewService(repo domain.NotificationRepo) Service {
return &service{
repo: repo,
}
}
func (s *service) Find(ctx context.Context, params domain.NotificationQueryParams) ([]domain.Notification, int, error) {
return s.repo.Find(ctx, params)
}
func (s *service) FindByID(ctx context.Context, id int) (*domain.Notification, error) {
return s.repo.FindByID(ctx, id)
}
func (s *service) Store(ctx context.Context, n domain.Notification) (*domain.Notification, error) {
return s.repo.Store(ctx, n)
}
func (s *service) Update(ctx context.Context, n domain.Notification) (*domain.Notification, error) {
return s.repo.Update(ctx, n)
}
func (s *service) Delete(ctx context.Context, id int) error {
return s.repo.Delete(ctx, id)
}
// Send notifications
func (s *service) Send(event domain.NotificationEvent, msg string) error {
// find notifications for type X
notifications, err := s.repo.List(context.Background())
if err != nil {
return err
}
var urls []string
for _, n := range notifications {
if !n.Enabled {
continue
}
switch n.Type {
case domain.NotificationTypeDiscord:
urls = append(urls, fmt.Sprintf("discord://%v@%v", n.Token, n.Webhook))
default:
return nil
}
}
if len(urls) == 0 {
return nil
}
sender, err := shoutrrr.CreateSender(urls...)
if err != nil {
return err
}
p := t.Params{"title": "TEST"}
items := []t.MessageItem{
{
Text: "text hello",
Fields: []t.Field{
{
Key: "eventt",
Value: "push?",
},
},
},
}
//items = append(items, t.MessageItem{
// Text: "text hello",
// Fields: []t.Field{
// {
// Key: "eventt",
// Value: "push?",
// },
// },
//})
sender.SendItems(items, p)
return nil
}
func (s *service) SendEvent(event domain.EventsReleasePushed) error {
notifications, err := s.repo.List(context.Background())
if err != nil {
return err
}
return s.send(notifications, event)
}
func (s *service) send(notifications []domain.Notification, event domain.EventsReleasePushed) error {
// find notifications for type X
for _, n := range notifications {
if !n.Enabled {
continue
}
if n.Events == nil {
continue
}
for _, evt := range n.Events {
if evt == string(event.Status) {
switch n.Type {
case domain.NotificationTypeDiscord:
go discordNotification(event, n.Webhook)
default:
return nil
}
}
}
}
return nil
}