Skip to content

Commit aaadc5d

Browse files
authored
Merge pull request #787 from blueberryapple/feat/adds-ntfy
feat(notifications): Adds ntfy.sh notification option
2 parents 2bd5d54 + 1da9ec5 commit aaadc5d

File tree

11 files changed

+244
-0
lines changed

11 files changed

+244
-0
lines changed

docs/config/index.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ You can override this using the [`--config` flag or `CONFIG` env var with `serve
5656
to:
5757
- webmaster@example.com
5858
- me@example.com
59+
ntfy:
60+
endpoint: https://ntfy.sh
61+
topic: diun-acce65a0-b777-46f9-9a11-58c67d1579c4
62+
priority: 3
63+
timeout: 5s
5964
rocketchat:
6065
endpoint: http://rocket.foo.com:3000
6166
channel: "#general"
@@ -131,6 +136,11 @@ All configuration from file can be transposed into environment variables. As an
131136
token: Token123456
132137
priority: 1
133138
timeout: 10s
139+
ntfy:
140+
endpoint: https://ntfy.sh
141+
topic: diun-acce65a0-b777-46f9-9a11-58c67d1579c4
142+
priority: 3
143+
timeout: 5s
134144
telegram:
135145
token: aabbccdd:11223344
136146
chatIDs:
@@ -179,6 +189,11 @@ Can be transposed to:
179189
DIUN_NOTIF_GOTIFY_PRIORITY=1
180190
DIUN_NOTIF_GOTIFY_TIMEOUT=10s
181191

192+
DIUN_NOTIF_NTFY_ENDPOINT=https://ntfy.sh
193+
DIUN_NOTIF_NTFY_TOPIC=diun-acce65a0-b777-46f9-9a11-58c67d1579c4
194+
DIUN_NOTIF_NTFY_TAGS=whale
195+
DIUN_NOTIF_NTFY_TIMEOUT=10s
196+
182197
DIUN_NOTIF_TELEGRAM_TOKEN=aabbccdd:11223344
183198
DIUN_NOTIF_TELEGRAM_CHATIDS=123456789,987654321
184199

@@ -213,6 +228,7 @@ Can be transposed to:
213228
* [mail](../notif/mail.md)
214229
* [matrix](../notif/matrix.md)
215230
* [mqtt](../notif/mqtt.md)
231+
* [ntfy](../notif/ntfy.md)
216232
* [pushover](../notif/pushover.md)
217233
* [rocketchat](../notif/rocketchat.md)
218234
* [script](../notif/script.md)

docs/config/notif.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* [`mail`](../notif/mail.md)
77
* [`matrix`](../notif/matrix.md)
88
* [`mqtt`](../notif/mqtt.md)
9+
* [`ntfy`](../notif/ntfy.md)
910
* [`pushover`](../notif/pushover.md)
1011
* [`rocketchat`](../notif/rocketchat.md)
1112
* [`script`](../notif/script.md)

docs/notif/ntfy.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Ntfy notifications
2+
3+
Notifications can be sent using a [ntfy](https://ntfy.sh/) instance.
4+
5+
## Configuration
6+
7+
!!! example "File"
8+
```yaml
9+
notif:
10+
ntfy:
11+
endpoint: https://ntfy.sh
12+
topic: diun-acce65a0-b777-46f9-9a11-58c67d1579c4
13+
priority: 3
14+
tags:
15+
- whale
16+
timeout: 10s
17+
templateTitle: "{{ .Entry.Image }} released"
18+
templateBody: |
19+
Docker tag {{ .Entry.Image }} which you subscribed to through {{ .Entry.Provider }} provider has been released.
20+
```
21+
22+
| Name | Default | Description |
23+
| ------------------- | ----------------------------------- | -------------------------------------------------------------------------- |
24+
| `endpoint`[^1] | `https://ntfy.sh` | Ntfy base URL |
25+
| `topic` | | Ntfy topic |
26+
| `priority` | 3 | The priority of the message |
27+
| `tags` | `["package"]` | Emoji to go in your notiication |
28+
| `timeout` | `10s` | Timeout specifies a time limit for the request to be made |
29+
| `templateTitle`[^1] | See [below](#default-templatetitle) | [Notification template](../faq.md#notification-template) for message title |
30+
| `templateBody`[^1] | See [below](#default-templatebody) | [Notification template](../faq.md#notification-template) for message body |
31+
32+
!!! abstract "Environment variables"
33+
* `DIUN_NOTIF_NTFY_ENDPOINT`
34+
* `DIUN_NOTIF_NTFY_TOPIC`
35+
* `DIUN_NOTIF_NTFY_PRIORITY`
36+
* `DIUN_NOTIF_NTFY_TAGS`
37+
* `DIUN_NOTIF_NTFY_TIMEOUT`
38+
* `DIUN_NOTIF_NTFY_TEMPLATETITLE`
39+
* `DIUN_NOTIF_NTFY_TEMPLATEBODY`
40+
41+
### Default `templateTitle`
42+
43+
```
44+
[[ config.extra.template.notif.defaultTitle ]]
45+
```
46+
47+
### Default `templateBody`
48+
49+
```
50+
[[ config.extra.template.notif.defaultBody ]]
51+
```
52+
53+
[^1]: Value required

internal/config/config_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ for <code>{{ .Entry.Manifest.Platform }}</code> platform.
127127
Topic: "docker/diun",
128128
QoS: 0,
129129
},
130+
Ntfy: &model.NotifNtfy{
131+
Endpoint: "https://ntfy.sh",
132+
Topic: "diun-acce65a0-b777-46f9-9a11-58c67d1579c4",
133+
Priority: 3,
134+
Tags: []string{"package"},
135+
Timeout: utl.NewDuration(10 * time.Second),
136+
TemplateTitle: model.NotifDefaultTemplateTitle,
137+
TemplateBody: model.NotifDefaultTemplateBody,
138+
},
130139
Pushover: &model.NotifPushover{
131140
Token: "uQiRzpo4DXghDmr9QzzfQu27cmVRsG",
132141
Recipient: "gznej3rKEVAvPUxu9vvNnqpmZpokzF",

internal/config/fixtures/config.test.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ notif:
6363
client: "diun"
6464
topic: "docker/diun"
6565
qos: 0
66+
ntfy:
67+
endpoint: https://ntfy.sh
68+
topic: diun-acce65a0-b777-46f9-9a11-58c67d1579c4
69+
priority: 3
70+
tags:
71+
- package
72+
timeout: 10s
6673
pushover:
6774
token: uQiRzpo4DXghDmr9QzzfQu27cmVRsG
6875
recipient: gznej3rKEVAvPUxu9vvNnqpmZpokzF

internal/config/fixtures/config.validate.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ notif:
6161
client: "diun"
6262
topic: "docker/diun"
6363
qos: 0
64+
ntfy:
65+
endpoint: https://ntfy.sh
66+
topic: diun-acce65a0-b777-46f9-9a11-58c67d1579c4
67+
priority: 3
68+
tags:
69+
- package
70+
timeout: 10s
6471
pushover:
6572
token: uQiRzpo4DXghDmr9QzzfQu27cmVRsG
6673
recipient: gznej3rKEVAvPUxu9vvNnqpmZpokzF

internal/model/notif.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Notif struct {
3838
Mail *NotifMail `yaml:"mail,omitempty" json:"mail,omitempty"`
3939
Matrix *NotifMatrix `yaml:"matrix,omitempty" json:"matrix,omitempty"`
4040
Mqtt *NotifMqtt `yaml:"mqtt,omitempty" json:"mqtt,omitempty"`
41+
Ntfy *NotifNtfy `yaml:"ntfy,omitempty" json:"ntfy,omitempty"`
4142
Pushover *NotifPushover `yaml:"pushover,omitempty" json:"pushover,omitempty"`
4243
RocketChat *NotifRocketChat `yaml:"rocketchat,omitempty" json:"rocketchat,omitempty"`
4344
Script *NotifScript `yaml:"script,omitempty" json:"script,omitempty"`

internal/model/notif_ntfy.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package model
2+
3+
import (
4+
"time"
5+
6+
"github.com/crazy-max/diun/v4/pkg/utl"
7+
)
8+
9+
// NotifNtfy holds ntfy notification configuration details
10+
type NotifNtfy struct {
11+
Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty" validate:"required"`
12+
Topic string `yaml:"topic,omitempty" json:"topic,omitempty" validate:"required"`
13+
Priority int `yaml:"priority,omitempty" json:"priority,omitempty" validate:"omitempty,min=0"`
14+
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty" validate:"required"`
15+
Timeout *time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty" validate:"required"`
16+
TemplateTitle string `yaml:"templateTitle,omitempty" json:"templateTitle,omitempty" validate:"required"`
17+
TemplateBody string `yaml:"templateBody,omitempty" json:"templateBody,omitempty" validate:"required"`
18+
}
19+
20+
// GetDefaults gets the default values
21+
func (s *NotifNtfy) GetDefaults() *NotifNtfy {
22+
n := &NotifNtfy{}
23+
n.SetDefaults()
24+
return n
25+
}
26+
27+
// SetDefaults sets the default values
28+
func (s *NotifNtfy) SetDefaults() {
29+
s.Endpoint = "https://ntfy.sh"
30+
s.Priority = 3
31+
s.Tags = []string{"package"}
32+
s.Timeout = utl.NewDuration(10 * time.Second)
33+
s.TemplateTitle = NotifDefaultTemplateTitle
34+
s.TemplateBody = NotifDefaultTemplateBody
35+
}

internal/notif/client.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/crazy-max/diun/v4/internal/notif/matrix"
1212
"github.com/crazy-max/diun/v4/internal/notif/mqtt"
1313
"github.com/crazy-max/diun/v4/internal/notif/notifier"
14+
"github.com/crazy-max/diun/v4/internal/notif/ntfy"
1415
"github.com/crazy-max/diun/v4/internal/notif/pushover"
1516
"github.com/crazy-max/diun/v4/internal/notif/rocketchat"
1617
"github.com/crazy-max/diun/v4/internal/notif/script"
@@ -61,6 +62,9 @@ func New(config *model.Notif, meta model.Meta) (*Client, error) {
6162
if config.Mqtt != nil {
6263
c.notifiers = append(c.notifiers, mqtt.New(config.Mqtt, meta))
6364
}
65+
if config.Ntfy != nil {
66+
c.notifiers = append(c.notifiers, ntfy.New(config.Ntfy, meta))
67+
}
6468
if config.Pushover != nil {
6569
c.notifiers = append(c.notifiers, pushover.New(config.Pushover, meta))
6670
}

internal/notif/ntfy/client.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package ntfy
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"net/url"
9+
10+
"github.com/crazy-max/diun/v4/internal/model"
11+
"github.com/crazy-max/diun/v4/internal/msg"
12+
"github.com/crazy-max/diun/v4/internal/notif/notifier"
13+
)
14+
15+
// Client represents an active ntfy notification object
16+
type Client struct {
17+
*notifier.Notifier
18+
cfg *model.NotifNtfy
19+
meta model.Meta
20+
}
21+
22+
// New creates a new ntfy notification instance
23+
func New(config *model.NotifNtfy, meta model.Meta) notifier.Notifier {
24+
return notifier.Notifier{
25+
Handler: &Client{
26+
cfg: config,
27+
meta: meta,
28+
},
29+
}
30+
}
31+
32+
// Name returns notifier's name
33+
func (c *Client) Name() string {
34+
return "ntfy"
35+
}
36+
37+
// Send creates and sends a ntfy notification with an entry
38+
func (c *Client) Send(entry model.NotifEntry) error {
39+
hc := http.Client{
40+
Timeout: *c.cfg.Timeout,
41+
}
42+
43+
message, err := msg.New(msg.Options{
44+
Meta: c.meta,
45+
Entry: entry,
46+
TemplateTitle: c.cfg.TemplateTitle,
47+
TemplateBody: c.cfg.TemplateBody,
48+
})
49+
if err != nil {
50+
return err
51+
}
52+
53+
title, body, err := message.RenderMarkdown()
54+
if err != nil {
55+
return err
56+
}
57+
58+
dataBuf := new(bytes.Buffer)
59+
if err := json.NewEncoder(dataBuf).Encode(struct {
60+
Topic string `json:"topic"`
61+
Message string `json:"message"`
62+
Title string `json:"title"`
63+
Priority int `json:"priority"`
64+
Tags []string `json:"tags"`
65+
}{
66+
Topic: c.cfg.Topic,
67+
Message: string(body),
68+
Title: string(title),
69+
Priority: c.cfg.Priority,
70+
Tags: c.cfg.Tags,
71+
}); err != nil {
72+
return err
73+
}
74+
75+
u, err := url.Parse(c.cfg.Endpoint)
76+
if err != nil {
77+
return err
78+
}
79+
80+
q := u.Query()
81+
u.RawQuery = q.Encode()
82+
83+
req, err := http.NewRequest("POST", u.String(), dataBuf)
84+
if err != nil {
85+
return err
86+
}
87+
88+
req.Header.Set("Content-Type", "application/json")
89+
req.Header.Set("User-Agent", c.meta.UserAgent)
90+
91+
resp, err := hc.Do(req)
92+
if err != nil {
93+
return err
94+
}
95+
96+
if resp.StatusCode != http.StatusOK {
97+
var errBody struct {
98+
Error string `json:"error"`
99+
ErrorCode int `json:"errorCode"`
100+
ErrorDescription string `json:"errorDescription"`
101+
}
102+
err := json.NewDecoder(resp.Body).Decode(&errBody)
103+
if err != nil {
104+
return err
105+
}
106+
return fmt.Errorf("%d %s: %s", errBody.ErrorCode, errBody.Error, errBody.ErrorDescription)
107+
}
108+
109+
return nil
110+
}

0 commit comments

Comments
 (0)