~xenrox/ntfy-alertmanager

025ad78a605ea1c1f38274629caa569d6750e629 — Thorben Günther 1 year, 1 month ago 3bdecb7
Add ntfy email support

Implements: https://todo.xenrox.net/~xenrox/ntfy-alertmanager/15
4 files changed, 63 insertions(+), 19 deletions(-)

M README.md
M config.go
M config_test.go
M main.go
M README.md => README.md +8 -3
@@ 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

M config.go => config.go +23 -7
@@ 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 {

M config_test.go => config_test.go +7 -1
@@ 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"}},
			},

M main.go => main.go +25 -8
@@ 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"