From 025ad78a605ea1c1f38274629caa569d6750e629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorben=20G=C3=BCnther?= Date: Thu, 9 Mar 2023 17:02:05 +0100 Subject: [PATCH] Add ntfy email support Implements: https://todo.xenrox.net/~xenrox/ntfy-alertmanager/15 --- README.md | 11 ++++++++--- config.go | 30 +++++++++++++++++++++++------- config_test.go | 8 +++++++- main.go | 33 +++++++++++++++++++++++++-------- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d5708c7..bac826a 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ You can specify the configuration file location with the `--config` flag. By def the configuration file will be read from `/etc/ntfy-alertmanager/config`. The format of this file is [scfg]. -ntfy-alertmanager has support for setting ntfy [priority], [tags], [icon] and [action buttons] -(which can be used to create an Alertmanager silence). -Define a decreasing order of labels in the config file and map those labels to tags, priority or an icon. +ntfy-alertmanager has support for setting ntfy [priority], [tags], [icon], [action buttons] +(which can be used to create an Alertmanager silence) and [email notifications]. +Define a decreasing order of labels in the config file and map those labels to tags, priority, an icon or an email address. - For priority and icon the first found value will be chosen. An icon for "resolved" alerts will take precedence. - Tags are added together. @@ -47,6 +47,8 @@ labels { priority 5 tags "rotating_light" icon "https://foo.com/critical.png" + # Forward messages which severity "critical" to the specified email address. + email-address foo@bar.com } severity "info" { @@ -73,6 +75,8 @@ ntfy { # ntfy authentication via access tokens (https://docs.ntfy.sh/publish/#access-tokens) # Either access-token or a user/password combination can be used - not both. access-token foobar + # Forward all messages to the specified email address. + email-address foo@bar.com } alertmanager { @@ -135,6 +139,7 @@ or write to me directly on matrix [@xenrox:xenrox.net]. [tags]: https://ntfy.sh/docs/publish/#tags-emojis [icon]: https://docs.ntfy.sh/publish/#icons [action buttons]: https://docs.ntfy.sh/publish/#action-buttons +[email notifications]: https://docs.ntfy.sh/publish/#e-mail-notifications [issue tracker]: https://todo.xenrox.net/~xenrox/ntfy-alertmanager [mailing list]: https://lists.xenrox.net/~xenrox/public-inbox [@xenrox:xenrox.net]: https://matrix.to/#/@xenrox:xenrox.net diff --git a/config.go b/config.go index 9bb10fb..4488bb0 100644 --- a/config.go +++ b/config.go @@ -38,10 +38,11 @@ type config struct { } type ntfyConfig struct { - Topic string - User string - Password string - AccessToken string + Topic string + User string + Password string + AccessToken string + emailAddress string } type labels struct { @@ -50,9 +51,10 @@ type labels struct { } type labelConfig struct { - Priority string - Tags []string - Icon string + Priority string + Tags []string + Icon string + emailAddress string } type cacheConfig struct { @@ -201,6 +203,13 @@ func readConfig(path string) (*config, error) { } } + d = labelDir.Children.Get("email-address") + if d != nil { + if err := d.ParseParams(&labelConfig.emailAddress); err != nil { + return nil, err + } + } + labels[fmt.Sprintf("%s:%s", labelName, name)] = *labelConfig } } @@ -251,6 +260,13 @@ func readConfig(path string) (*config, error) { return nil, errors.New("ntfy: cannot use both an access-token and a user/password at the same time") } + d = ntfyDir.Children.Get("email-address") + if d != nil { + if err := d.ParseParams(&config.ntfy.emailAddress); err != nil { + return nil, err + } + } + cacheDir := cfg.Get("cache") if cacheDir != nil { diff --git a/config_test.go b/config_test.go index e5a7ad3..8114ab4 100644 --- a/config_test.go +++ b/config_test.go @@ -24,6 +24,7 @@ labels { priority 5 tags "rotating_light" icon "https://foo.com/critical.png" + email-address foo@bar.com } severity "info" { @@ -70,7 +71,12 @@ cache { ntfy: ntfyConfig{Topic: "https://ntfy.sh/alertmanager-alerts", User: "user", Password: "pass"}, labels: labels{Order: []string{"severity", "instance"}, Label: map[string]labelConfig{ - "severity:critical": {Priority: "5", Tags: []string{"rotating_light"}, Icon: "https://foo.com/critical.png"}, + "severity:critical": { + Priority: "5", + Tags: []string{"rotating_light"}, + Icon: "https://foo.com/critical.png", + emailAddress: "foo@bar.com", + }, "severity:info": {Priority: "1"}, "instance:example.com": {Tags: []string{"computer", "example"}}, }, diff --git a/main.go b/main.go index 57f0577..3a577b0 100644 --- a/main.go +++ b/main.go @@ -46,14 +46,15 @@ type alert struct { } type notification struct { - title string - body string - priority string - tags string - icon string - silenceBody string - fingerprint string - status string + title string + body string + priority string + tags string + icon string + emailAddress string + silenceBody string + fingerprint string + status string } func (br *bridge) singleAlertNotifications(p *payload) []*notification { @@ -100,6 +101,8 @@ func (br *bridge) singleAlertNotifications(p *payload) []*notification { n.icon = br.cfg.resolved.Icon } + n.emailAddress = br.cfg.ntfy.emailAddress + for _, labelName := range br.cfg.labels.Order { val, ok := alert.Labels[labelName] if !ok { @@ -119,6 +122,10 @@ func (br *bridge) singleAlertNotifications(p *payload) []*notification { n.icon = labelConfig.Icon } + if n.emailAddress == "" { + n.emailAddress = labelConfig.emailAddress + } + for _, val := range labelConfig.Tags { if !sliceContains(tags, val) { tags = append(tags, val) @@ -197,6 +204,8 @@ func (br *bridge) multiAlertNotification(p *payload) *notification { n.icon = br.cfg.resolved.Icon } + n.emailAddress = br.cfg.ntfy.emailAddress + for _, labelName := range br.cfg.labels.Order { val, ok := p.CommonLabels[labelName] if !ok { @@ -216,6 +225,10 @@ func (br *bridge) multiAlertNotification(p *payload) *notification { n.icon = labelConfig.Icon } + if n.emailAddress == "" { + n.emailAddress = labelConfig.emailAddress + } + for _, val := range labelConfig.Tags { if !sliceContains(tags, val) { tags = append(tags, val) @@ -270,6 +283,10 @@ func (br *bridge) publish(n *notification) error { req.Header.Set("X-Tags", n.tags) } + if n.emailAddress != "" { + req.Header.Set("X-Email", n.emailAddress) + } + if n.silenceBody != "" { url := br.cfg.BaseURL + "/silences"