diff --git a/.res/notif-teams.png b/.res/notif-teams.png new file mode 100644 index 00000000..a564a117 Binary files /dev/null and b/.res/notif-teams.png differ diff --git a/doc/configuration.md b/doc/configuration.md index 387e78a0..fc0dafc9 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -20,22 +20,19 @@ watch: first_check_notif: false notif: - amqp: - enable: false + amqp: host: localhost port: 5672 username: guest password: guest exchange: queue: queue - gotify: - enable: false + gotify: endpoint: http://gotify.foo.com token: Token123456 priority: 1 timeout: 10 - mail: - enable: false + mail: host: localhost port: 25 ssl: false @@ -44,24 +41,22 @@ notif: password: from: to: - rocketchat: - enable: false + rocketchat: endpoint: http://rocket.foo.com:3000 channel: "#general" user_id: abcdEFGH012345678 token: Token123456 timeout: 10 - slack: - enable: false + slack: webhook_url: https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij + teams: + webhook_url: https://outlook.office.com/webhook/ABCD12EFG/HIJK34LMN/01234567890abcdefghij telegram: - enable: false token: aabbccdd:11223344 chat_ids: - 123456789 - 987654321 webhook: - enable: false endpoint: http://webhook.foo.com/sd54qad89azd5a method: GET headers: @@ -107,6 +102,7 @@ providers: * [mail](notifications.md#mail) * [rocketchat](notifications.md#rocketchat) * [slack](notifications.md#slack) +* [teams](notifications.md#teams) * [telegram](notifications.md#telegram) * [webhook](notifications.md#webhook) diff --git a/doc/notifications.md b/doc/notifications.md index 41191562..9fd625a7 100644 --- a/doc/notifications.md +++ b/doc/notifications.md @@ -91,6 +91,15 @@ You can send notifications to your Slack channel using an [incoming webhook URL] ![](../.res/notif-slack.png) +## Teams + +You can send notifications to your Teams team-channel using an [incoming webhook URL](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/what-are-webhooks-and-connectors): + +* `teams` + * `webhook_url`: Teams [incoming webhook URL](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/what-are-webhooks-and-connectors). **required** + +![](../.res/notif-teams.png) + ## Telegram Notifications can be sent via Telegram using a [Telegram Bot](https://core.telegram.org/bots). diff --git a/internal/config/config.test.yml b/internal/config/config.test.yml index 89e8ecb9..3314d2c9 100644 --- a/internal/config/config.test.yml +++ b/internal/config/config.test.yml @@ -37,6 +37,8 @@ notif: timeout: 10 slack: webhook_url: https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij + teams: + webhook_url: https://outlook.office.com/webhook/ABCD12EFG/HIJK34LMN/01234567890abcdefghij telegram: token: abcdef123456 chat_ids: diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 8299baa1..66dd2c05 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -85,6 +85,9 @@ func TestLoad(t *testing.T) { Slack: &model.NotifSlack{ WebhookURL: "https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij", }, + Teams: &model.NotifTeams{ + WebhookURL: "https://outlook.office.com/webhook/ABCD12EFG/HIJK34LMN/01234567890abcdefghij", + }, Telegram: &model.NotifTelegram{ BotToken: "abcdef123456", ChatIDs: []int64{8547439, 1234567}, diff --git a/internal/model/notif.go b/internal/model/notif.go index 62ecd33c..b95b6072 100644 --- a/internal/model/notif.go +++ b/internal/model/notif.go @@ -19,6 +19,7 @@ type Notif struct { Mail *NotifMail `yaml:"mail,omitempty"` RocketChat *NotifRocketChat `yaml:"rocketchat,omitempty"` Slack *NotifSlack `yaml:"slack,omitempty"` + Teams *NotifTeams `yaml:"teams,omitempty"` Telegram *NotifTelegram `yaml:"telegram,omitempty"` Webhook *NotifWebhook `yaml:"webhook,omitempty"` } @@ -71,6 +72,11 @@ type NotifSlack struct { WebhookURL string `yaml:"webhook_url,omitempty"` } +// NotifTeams holds Teams notification configuration details +type NotifTeams struct { + WebhookURL string `yaml:"webhook_url,omitempty"` +} + // NotifTelegram holds Telegram notification configuration details type NotifTelegram struct { BotToken string `yaml:"token,omitempty"` diff --git a/internal/notif/client.go b/internal/notif/client.go index 57c783ac..c0eb8f6a 100644 --- a/internal/notif/client.go +++ b/internal/notif/client.go @@ -8,6 +8,7 @@ import ( "github.com/crazy-max/diun/internal/notif/notifier" "github.com/crazy-max/diun/internal/notif/rocketchat" "github.com/crazy-max/diun/internal/notif/slack" + "github.com/crazy-max/diun/internal/notif/teams" "github.com/crazy-max/diun/internal/notif/telegram" "github.com/crazy-max/diun/internal/notif/webhook" "github.com/rs/zerolog/log" @@ -49,6 +50,9 @@ func New(config *model.Notif, app model.App, userAgent string) (*Client, error) if config.Slack != nil { c.notifiers = append(c.notifiers, slack.New(config.Slack, app)) } + if config.Teams != nil { + c.notifiers = append(c.notifiers, teams.New(config.Teams, app, userAgent)) + } if config.Telegram != nil { c.notifiers = append(c.notifiers, telegram.New(config.Telegram, app)) } diff --git a/internal/notif/teams/client.go b/internal/notif/teams/client.go new file mode 100644 index 00000000..05557ab3 --- /dev/null +++ b/internal/notif/teams/client.go @@ -0,0 +1,103 @@ +package teams + +import ( + "bytes" + "encoding/json" + "net/http" + "text/template" + "time" + + "github.com/crazy-max/diun/internal/model" + "github.com/crazy-max/diun/internal/notif/notifier" +) + +// Client represents an active webhook notification object +type Client struct { + *notifier.Notifier + cfg *model.NotifTeams + app model.App + userAgent string +} + +// New creates a new webhook notification instance +func New(config *model.NotifTeams, app model.App, userAgent string) notifier.Notifier { + return notifier.Notifier{ + Handler: &Client{ + cfg: config, + app: app, + userAgent: userAgent, + }, + } +} + +// Name returns notifier's name +func (c *Client) Name() string { + return "teams" +} + +// Sections is grouping data together containing title, subtitle and facts and creating a nested json element +type Sections struct { + ActivityTitle string `json:"activityTitle"` + ActivitySubtitle string `json:"activitySubtitle"` + Facts []Fact `json:"facts"` +} + +// Fact is grouping data togheter to create a nested json element containg a name and an associated value +type Fact struct { + Name string `json:"Name"` + Value string `json:"Value"` +} + +// Send creates and sends a webhook notification with an entry +func (c *Client) Send(entry model.NotifEntry) error { + hc := http.Client{ + Timeout: time.Duration(10) * time.Second, + } + + var textBuf bytes.Buffer + textTpl := template.Must(template.New("text").Parse("Docker tag `{{ .Image.Domain }}/{{ .Image.Path }}:{{ .Image.Tag }}` {{ if (eq .Status \"new\") }}newly added{{ else }}updated{{ end }}.")) + if err := textTpl.Execute(&textBuf, entry); err != nil { + return err + } + + themeColor := "68CA00" + if entry.Status == model.ImageStatusUpdate { + themeColor = "0076D7" + } + + var body, err = json.Marshal(struct { + Type string `json:"@type"` + Context string `json:"@context"` + ThemeColor string `json:"themeColor"` + Summary string `json:"summary"` + Sections []Sections `json:"sections"` + }{ + Type: "MessageCard", + Context: "http://schema.org/extensions", + ThemeColor: themeColor, + Summary: textBuf.String(), + Sections: []Sections{{ + ActivityTitle: textBuf.String(), + ActivitySubtitle: "Provider: " + entry.Provider, + Facts: []Fact{ + {"Created", entry.Manifest.Created.Format("Jan 02, 2006 15:04:05 UTC")}, + {"Digest", entry.Manifest.Digest.String()}, + {"Platform", entry.Manifest.Platform}, + }}}, + }) + if err != nil { + return err + } + + req, err := http.NewRequest("POST", c.cfg.WebhookURL, bytes.NewBuffer(body)) + if err != nil { + return err + } + + req.Header.Add("Content-Type", "application/json") + + req.Header.Set("User-Agent", c.userAgent) + + _, err = hc.Do(req) + return err +}