Skip to content

Commit

Permalink
Add discord webhook sender
Browse files Browse the repository at this point in the history
  • Loading branch information
hotpheex committed Aug 5, 2023
1 parent f8dabdc commit 21c273a
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/keel/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (

// notification extensions
"github.com/keel-hq/keel/extension/notification/auditor"
_ "github.com/keel-hq/keel/extension/notification/discord"
_ "github.com/keel-hq/keel/extension/notification/hipchat"
_ "github.com/keel-hq/keel/extension/notification/mail"
_ "github.com/keel-hq/keel/extension/notification/mattermost"
Expand Down
3 changes: 3 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const (
// MS Teams webhook url, see https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook
EnvTeamsWebhookUrl = "TEAMS_WEBHOOK_URL"

// Discord webhook url, see https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks
EnvDiscordWebhookUrl = "DISCORD_WEBHOOK_URL"

// Mail notification settings
EnvMailTo = "MAIL_TO"
EnvMailFrom = "MAIL_FROM"
Expand Down
99 changes: 99 additions & 0 deletions extension/notification/discord/discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package discord

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"time"

"github.com/keel-hq/keel/constants"
"github.com/keel-hq/keel/extension/notification"
"github.com/keel-hq/keel/types"

log "github.com/sirupsen/logrus"
)

const timeout = 5 * time.Second

type sender struct {
endpoint string
client *http.Client
}

// Config represents the configuration of a Discord Webhook Sender.
type Config struct {
Endpoint string
}

func init() {
log.Error("RUNNING")
notification.RegisterSender("discord", &sender{})
}

func (s *sender) Configure(config *notification.Config) (bool, error) {
// Get configuration
var httpConfig Config

if os.Getenv(constants.EnvDiscordWebhookUrl) != "" {
httpConfig.Endpoint = os.Getenv(constants.EnvDiscordWebhookUrl)
} else {
return false, nil
}

// Validate endpoint URL.
if httpConfig.Endpoint == "" {
return false, nil
}
if _, err := url.ParseRequestURI(httpConfig.Endpoint); err != nil {
return false, fmt.Errorf("could not parse endpoint URL: %s", err)
}
s.endpoint = httpConfig.Endpoint

// Setup HTTP client.
s.client = &http.Client{
Transport: http.DefaultTransport,
Timeout: timeout,
}

log.WithFields(log.Fields{
"name": "discord",
"endpoint": s.endpoint,
}).Info("extension.notification.discord: sender configured")

return true, nil
}

// type notificationEnvelope struct {
// types.EventNotification
// }

type DiscordMessage struct {
Content string `json:"content"`
Username string `json:"username"`
}

func (s *sender) Send(event types.EventNotification) error {
discordMessage := DiscordMessage{
Content: fmt.Sprintf("**%s**\n%s", event.Name, event.Message),
Username: "Keel",
}

jsonMessage, err := json.Marshal(discordMessage)
if err != nil {
return fmt.Errorf("could not marshal: %s", err)
}

resp, err := s.client.Post(s.endpoint, "application/json", bytes.NewBuffer(jsonMessage))
if err != nil || resp == nil || (resp.StatusCode != 200 && resp.StatusCode != 204) {
if resp != nil {
return fmt.Errorf("got status %d, expected 200/204", resp.StatusCode)
}
return err
}
defer resp.Body.Close()

return nil
}
59 changes: 59 additions & 0 deletions extension/notification/discord/discord_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package discord

import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/keel-hq/keel/types"
)

func TestDiscordWebhookRequest(t *testing.T) {
currentTime := time.Now()
handler := func(resp http.ResponseWriter, req *http.Request) {
body, err := io.ReadAll(req.Body)
if err != nil {
t.Errorf("failed to parse body: %s", err)
}

bodyStr := string(body)

if !strings.Contains(bodyStr, types.NotificationPreDeploymentUpdate.String()) {
t.Errorf("missing deployment type")
}

if !strings.Contains(bodyStr, "debug") {
t.Errorf("missing level")
}

if !strings.Contains(bodyStr, "update deployment") {
t.Errorf("missing name")
}
if !strings.Contains(bodyStr, "message here") {
t.Errorf("missing message")
}

t.Log(bodyStr)

}

// create test server with handler
ts := httptest.NewServer(http.HandlerFunc(handler))
defer ts.Close()

s := &sender{
endpoint: ts.URL,
client: &http.Client{},
}

s.Send(types.EventNotification{
Name: "update deployment",
Message: "message here",
CreatedAt: currentTime,
Type: types.NotificationPreDeploymentUpdate,
Level: types.LevelDebug,
})
}

0 comments on commit 21c273a

Please sign in to comment.