From a726851a29ea6c2a370df4d512f7890ba275ed20 Mon Sep 17 00:00:00 2001 From: Marcelo Castagna Date: Tue, 26 May 2020 11:40:29 -0300 Subject: [PATCH] Teams Notification (#72) Co-authored-by: Okan Celik --- .res/notif-teams.png | Bin 0 -> 14519 bytes doc/configuration.md | 20 +++---- doc/notifications.md | 9 +++ internal/config/config.test.yml | 2 + internal/config/config_test.go | 3 + internal/model/notif.go | 6 ++ internal/notif/client.go | 4 ++ internal/notif/teams/client.go | 103 ++++++++++++++++++++++++++++++++ 8 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 .res/notif-teams.png create mode 100644 internal/notif/teams/client.go diff --git a/.res/notif-teams.png b/.res/notif-teams.png new file mode 100644 index 0000000000000000000000000000000000000000..a564a117d85b33c6b00458911cc14f9cd14f6bee GIT binary patch literal 14519 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 literal 0 HcmV?d00001 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 +}