Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added notification support for discord #157

Merged
merged 24 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f891a4b
added notification support for discord
wryonik Jun 9, 2019
1edf4d8
removed changes from go.sum
wryonik Jun 9, 2019
fedfd99
changed discord icon
wryonik Jun 10, 2019
812cdc0
tried to improve notification structure
wryonik Jun 14, 2019
eed16a0
tried to implement interface for notifier
wryonik Jun 15, 2019
4c1f818
tried to implement interface for notifier. Some errors are still ther…
wryonik Jun 15, 2019
3ee5816
tried to implement interface for notifier. Some errors are still ther…
wryonik Jun 15, 2019
e946b1f
solved some errors. Some remains with sending payload
wryonik Jun 18, 2019
7e19304
enumeration of notification providers
wryonik Jun 19, 2019
7dea82c
completed notification for discord
wryonik Jun 20, 2019
5fae9bf
completed notification for discord
wryonik Jun 20, 2019
e7b4349
removed changes from go.sum
wryonik Jun 20, 2019
4a840a4
Review changes fixed
wryonik Aug 3, 2019
c485521
Merge branch 'master' into added_more_notification_channels
wryonik Aug 3, 2019
2603ae6
Formatting errors fixed
wryonik Aug 3, 2019
4b5eb5e
Changed notification functions such that now both the notification fo…
wryonik Aug 4, 2019
e34c8b3
Minor review changes
wryonik Aug 5, 2019
c7b7ac6
Minor review changes
wryonik Aug 26, 2019
d13953b
Minor changes
wryonik Sep 1, 2019
681cead
Review changes fixed
wryonik Sep 25, 2019
d091dbf
Removed discord test server link from example.config.toml
wryonik Sep 27, 2019
6215179
Some changes fixed
wryonik Oct 9, 2019
ee0bbdb
Minor changes fixes
wryonik Oct 10, 2019
6e5fd76
Documentation update
wryonik Oct 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ type BeastConfig struct {
GitRemote GitRemote `toml:"remote"`
JWTSecret string `toml:"jwt_secret"`
SlackWebHookURL string `toml:"slack_webhook"`

TickerFrequency int `toml:"ticker_frequency"`
DiscordWebHookURL string `toml:"discord_webhook"`
TickerFrequency int `toml:"ticker_frequency"`

RemoteSyncPeriod time.Duration `toml:"-"`
Rsp string `toml:"remote_sync_period"`
Expand Down
4 changes: 2 additions & 2 deletions core/manager/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,11 +523,11 @@ func StartUndeployChallenge(challengeName string, purge bool) error {
if err != nil {
msg := fmt.Sprintf("UNDEPLOY ERROR: %s : %s", challengeName, err)
log.Error(msg)
notify.SendNotificationToSlack(notify.Error, msg)
notify.SendNotification(notify.Error, msg)
} else {
msg := fmt.Sprintf("UNDEPLOY SUCCESSFUL: %s", challengeName)
log.Info(msg)
notify.SendNotificationToSlack(notify.Success, msg)
notify.SendNotification(notify.Success, msg)
}

log.Infof("Notification for the event sent to slack.")
Expand Down
2 changes: 1 addition & 1 deletion core/manager/health_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func ChallengesHealthProber(waitTime int) {
log.WithFields(log.Fields{
"ChallName": chall.Name,
}).Error(msg)
notify.SendNotificationToSlack(notify.Error, msg)
notify.SendNotification(notify.Error, msg)
} else {
log.WithFields(log.Fields{
"ChallName": chall.Name,
Expand Down
4 changes: 2 additions & 2 deletions core/manager/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,10 @@ func StartDeployPipeline(challengeDir string, skipStage bool, skipCommit bool) {

err := bootstrapDeployPipeline(challengeDir, skipStage, skipCommit)
if err != nil {
notify.SendNotificationToSlack(notify.Error, err.Error())
notify.SendNotification(notify.Error, err.Error())
} else {
msg := fmt.Sprintf("DEPLOY SUCCESS : %s : Challenge deployment pipeline successful.", challengeName)
notify.SendNotificationToSlack(notify.Success, msg)
notify.SendNotification(notify.Success, msg)
}

log.Debugf("%s: Notification sent to slack", challengeName)
Expand Down
68 changes: 68 additions & 0 deletions pkg/notify/discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package notify

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)

type DiscordNotificationProvider struct {
Request
}

func (d *DiscordNotificationProvider) SendNotification(nType NotificationType, msg string) error {
if d.Request.WebHookURL == "" {
return fmt.Errorf("Need a WebHookURL to send notification.")
}

d.Request.FillReqParams()

nAttachment := Attachment{
AuthorName: "Beast Notifier",
AuthorLink: "https://backdoor.sdslabs.co",
Footer: "Beast Discord API",
FooterIcon: "https://discordapp.com/assets/e05ead6e6ebc08df9291738d0aa6986d.png",
Timestamp: time.Now().Unix(),
Text: msg,
}

switch nType {
case Success:
nAttachment.Color = SuccessColor
nAttachment.Title = "Beast Deployment Success"
break
case Error:
nAttachment.Color = ErrorColor
nAttachment.Title = "Beast Deployment Error"
break
}

d.Request.PostPayload.Attachments = []Attachment{nAttachment}

if d.Request.PostPayload.Channel == "" || d.Request.PostPayload.Username == "" {
return fmt.Errorf("Username and Channel required to send the notification.")
}

payload, err := json.Marshal(d.PostPayload)
if err != nil {
return fmt.Errorf("Error while converting payload to JSON : %s", err)
}

payloadReader := bytes.NewReader(payload)
req, err := http.NewRequest("POST", d.Request.WebHookURL, payloadReader)
if err != nil {
return fmt.Errorf("Error while connecting to webhook url host : %s", err)
}

req.Header.Set("Content-Type", "application/json")
client := http.Client{}
_, err = client.Do(req)

if err != nil {
return fmt.Errorf("Error while posting payload for notification : %s", err)
}

return nil
}
116 changes: 116 additions & 0 deletions pkg/notify/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package notify

import (
"fmt"

"github.com/sdslabs/beastv4/core/config"
log "github.com/sirupsen/logrus"
)

type Attachment struct {
Fallback string `json:"fallback,omitempty"`
Color NotificationColor `json:"color,omitempty"`
PreText string `json:"pretext,omitempty"`
AuthorName string `json:"author_name,omitempty"`
AuthorLink string `json:"author_link,omitempty"`
AuthorIcon string `json:"author_icon,omitempty"`
Title string `json:"title,omitempty"`
TitleLink string `json:"title_link,omitempty"`
Text string `json:"text,omitempty"`
ImageUrl string `json:"image_url,omitempty"`
Footer string `json:"footer,omitempty"`
FooterIcon string `json:"footer_icon,omitempty"`
Timestamp int64 `json:"ts,omitempty"`
MarkdownIn []string `json:"mrkdwn_in,omitempty"`
CallbackID string `json:"callback_id,omitempty"`
ThumbnailUrl string `json:"thumb_url,omitempty"`
}

type PostPayload struct {
Parse string `json:"parse,omitempty"`
Username string `json:"username,omitempty"`
IconUrl string `json:"icon_url,omitempty"`
IconEmoji string `json:"icon_emoji,omitempty"`
Channel string `json:"channel,omitempty"`
Text string `json:"text,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
Markdown bool `json:"mrkdwn,omitempty"`
}

type Notifier interface {
SendNotification(nType NotificationType, msg string) error
}

type Request struct {
WebHookURL string
PostPayload
wryonik marked this conversation as resolved.
Show resolved Hide resolved
}

type ProviderTypeEnum int

const (
DiscordProvider ProviderTypeEnum = 1 + iota
SlackProvider
)

//In the Discord notification provider it was using the same payload which was used for slack.
//By writing "/slack" in the discord WebHookURL, it execute Slack-Compatible Webhook
func NewNotifier(URL string, ProviderType ProviderTypeEnum) Notifier {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate URL here

switch ProviderType {
case SlackProvider:
return &SlackNotificationProvider{
Request{
WebHookURL: URL,
},
}
case DiscordProvider:
return &DiscordNotificationProvider{
Request{
WebHookURL: URL + "/slack",
wryonik marked this conversation as resolved.
Show resolved Hide resolved
},
}
}
return nil
}

func (req *Request) FillReqParams() error {
req.PostPayload = PostPayload{
Username: "Beast",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put these values in notify/defaults.go and use from there.

IconUrl: "https://i.ibb.co/sjC5dRY/beast-eye-39371.png",
Channel: "#beast",
}
return nil
}

func SendNotification(nType NotificationType, message string) error {
if config.Cfg.SlackWebHookURL != "" {
slackNotifier := NewNotifier(config.Cfg.SlackWebHookURL, SlackProvider)

err := slackNotifier.SendNotification(nType, message)
if err != nil {
log.Errorf("Error while sending notification to slack : %s", err)
fmt.Errorf("NOTIFICATION_SEND_ERROR: %s", err)
}

log.Infof("Notfication sent to slack.")
} else {
log.Warnf("No slack webhook url provided in beast config, cannot send notification.")
fmt.Errorf("No webhook URL in beast config.")
}

if config.Cfg.DiscordWebHookURL != "" {
discordNotifier := NewNotifier(config.Cfg.DiscordWebHookURL, DiscordProvider)

err := discordNotifier.SendNotification(nType, message)
if err != nil {
log.Errorf("Error while sending notification to discord : %s", err)
return fmt.Errorf("NOTIFICATION_SEND_ERROR: %s", err)
}

log.Infof("Notfication sent to discord.")
return nil
} else {
log.Warnf("No discord webhook url provided in beast config, cannot send notification.")
return fmt.Errorf("No webhook URL in beast config.")
}
}
113 changes: 26 additions & 87 deletions pkg/notify/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,95 +6,18 @@ import (
"fmt"
"net/http"
"time"

"github.com/sdslabs/beastv4/core/config"
log "github.com/sirupsen/logrus"
)

type Attachment struct {
Fallback string `json:"fallback,omitempty"`
Color NotificationColor `json:"color,omitempty"`
PreText string `json:"pretext,omitempty"`
AuthorName string `json:"author_name,omitempty"`
AuthorLink string `json:"author_link,omitempty"`
AuthorIcon string `json:"author_icon,omitempty"`
Title string `json:"title,omitempty"`
TitleLink string `json:"title_link,omitempty"`
Text string `json:"text,omitempty"`
ImageUrl string `json:"image_url,omitempty"`
Footer string `json:"footer,omitempty"`
FooterIcon string `json:"footer_icon,omitempty"`
Timestamp int64 `json:"ts,omitempty"`
MarkdownIn []string `json:"mrkdwn_in,omitempty"`
CallbackID string `json:"callback_id,omitempty"`
ThumbnailUrl string `json:"thumb_url,omitempty"`
}

type SlackPostPayload struct {
Parse string `json:"parse,omitempty"`
Username string `json:"username,omitempty"`
IconUrl string `json:"icon_url,omitempty"`
IconEmoji string `json:"icon_emoji,omitempty"`
Channel string `json:"channel,omitempty"`
Text string `json:"text,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
Markdown bool `json:"mrkdwn,omitempty"`
type SlackNotificationProvider struct {
Request
}

type SlackNotifier struct {
WebHookURL string
SlackPostPayload SlackPostPayload
}

func NewSlackNotifier(webhookUrl string) *SlackNotifier {
return &SlackNotifier{
WebHookURL: webhookUrl,
}
}

func (notifier *SlackNotifier) SendNotification() error {
if notifier.WebHookURL == "" {
func (s *SlackNotificationProvider) SendNotification(nType NotificationType, msg string) error {
if s.Request.WebHookURL == "" {
return fmt.Errorf("Need a WebHookURL to send notification.")
}

if notifier.SlackPostPayload.Channel == "" || notifier.SlackPostPayload.Username == "" {
return fmt.Errorf("Username and Channel required to send the notification.")
}

payload, err := json.Marshal(notifier.SlackPostPayload)
if err != nil {
return fmt.Errorf("Error while converting payload to JSON : %s", err)
}

payloadReader := bytes.NewReader(payload)
req, err := http.NewRequest("POST", notifier.WebHookURL, payloadReader)
if err != nil {
return fmt.Errorf("Error while connecting to webhook url host : %s", err)
}

req.Header.Set("Content-Type", "application/json")
client := http.Client{}
_, err = client.Do(req)

if err != nil {
return fmt.Errorf("Error while posting payload for notification : %s", err)
}

return nil
}

func SendNotificationToSlack(nType NotificationType, msg string) error {
if config.Cfg.SlackWebHookURL == "" {
log.Warnf("No slack webhook url provided in beast config, cannot send notification.")
return fmt.Errorf("No webhook URL in beast config.")
}

slackNotifier := NewSlackNotifier(config.Cfg.SlackWebHookURL)
slackNotifier.SlackPostPayload = SlackPostPayload{
Username: "Beast",
IconUrl: "https://i.ibb.co/sjC5dRY/beast-eye-39371.png",
Channel: "#beast",
}
s.Request.FillReqParams()

nAttachment := Attachment{
AuthorName: "Beast Notifier",
Expand All @@ -115,14 +38,30 @@ func SendNotificationToSlack(nType NotificationType, msg string) error {
nAttachment.Title = "Beast Deployment Error"
break
}
s.Request.PostPayload.Attachments = []Attachment{nAttachment}

if s.Request.PostPayload.Channel == "" || s.Request.PostPayload.Username == "" {
return fmt.Errorf("Username and Channel required to send the notification.")
}

slackNotifier.SlackPostPayload.Attachments = []Attachment{nAttachment}
err := slackNotifier.SendNotification()
payload, err := json.Marshal(s.PostPayload)
if err != nil {
log.Errorf("Error while sending notification to slack : %s", err)
return fmt.Errorf("NOTIFICATION_SEND_ERROR: %s", err)
return fmt.Errorf("Error while converting payload to JSON : %s", err)
}

payloadReader := bytes.NewReader(payload)
req, err := http.NewRequest("POST", s.Request.WebHookURL, payloadReader)
if err != nil {
return fmt.Errorf("Error while connecting to webhook url host : %s", err)
}

req.Header.Set("Content-Type", "application/json")
client := http.Client{}
_, err = client.Do(req)

if err != nil {
return fmt.Errorf("Error while posting payload for notification : %s", err)
}

log.Infof("Notfication sent to slack.")
return nil
}