Skip to content

Commit

Permalink
Add Discord support
Browse files Browse the repository at this point in the history
This commit,
- adds discord bot
- adds discord notifier
- adds required discord configurations into comm_config.yaml
- adds discordgo module into go.mod
- adds discord configs into all yaml files
- adds apk add build-base into dockerfile Build Env
  • Loading branch information
codenio committed Jul 1, 2020
1 parent 5749185 commit a3ff62f
Show file tree
Hide file tree
Showing 11 changed files with 425 additions and 8 deletions.
4 changes: 2 additions & 2 deletions build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ FROM golang:1.12-alpine3.10 AS BUILD-ENV
ARG GOOS_VAL
ARG GOARCH_VAL

# Add git
RUN apk update && apk add git curl
# Add git curl and gcc
RUN apk update && apk add git curl build-base

WORKDIR /app

Expand Down
9 changes: 9 additions & 0 deletions cmd/botkube/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ func main() {
go mb.Start()
}

if conf.Communications.Discord.Enabled {
log.Info("Starting discord bot")
db := bot.NewDiscordBot(conf)
go db.Start()
}

// Prometheus metrics
metricsPort, exists := os.LookupEnv("METRICS_PORT")
if !exists {
Expand Down Expand Up @@ -88,6 +94,9 @@ func listNotifiers(conf *config.Config) []notify.Notifier {
log.Error(fmt.Sprintf("Failed to create Mattermost client. Error: %v", err))
}
}
if conf.Communications.Discord.Enabled {
notifiers = append(notifiers, notify.NewDiscord(conf))
}
if conf.Communications.ElasticSearch.Enabled {
if els, err := notify.NewElasticSearch(conf); err == nil {
notifiers = append(notifiers, els)
Expand Down
19 changes: 14 additions & 5 deletions comm_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@ communications:
enabled: false
channel: 'SLACK_CHANNEL'
token: 'SLACK_API_TOKEN'
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)

# Settings for Mattermost
mattermost:
enabled: false
url: 'MATTERMOST_SERVER_URL' # URL where Mattermost is running. e.g https://example.com:9243
token: 'MATTERMOST_TOKEN' # Personal Access token generated by BotKube user
team: 'MATTERMOST_TEAM' # Mattermost Team to configure with BotKube
url: 'MATTERMOST_SERVER_URL' # URL where Mattermost is running. e.g https://example.com:9243
token: 'MATTERMOST_TOKEN' # Personal Access token generated by BotKube user
team: 'MATTERMOST_TEAM' # Mattermost Team to configure with BotKube
channel: 'MATTERMOST_CHANNEL' # Mattermost Channel for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)

# Settings for Discord
discord:
enabled: false
token: 'DISCORD_TOKEN' # BotKube Bot Token
botid: 'DISCORD_BOT_ID' # BotKube Application Client ID
channel: 'DISCORD_CHANNEL_ID' # Discord Channel id for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)


# Settings for ELS
elasticsearch:
enabled: false
Expand Down
8 changes: 8 additions & 0 deletions deploy-all-in-one-tls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ stringData:
channel: 'MATTERMOST_CHANNEL' # Mattermost Channel for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)
# Settings for Discord
discord:
enabled: false
token: 'DISCORD_TOKEN' # BotKube Bot Token
botid: 'DISCORD_BOT_ID' # BotKube Application Client ID
channel: 'DISCORD_CHANNEL_ID' # Discord Channel id for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)
# Settings for ELS
elasticsearch:
enable: false
Expand Down
8 changes: 8 additions & 0 deletions deploy-all-in-one.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,14 @@ stringData:
team: 'MATTERMOST_TEAM' # Mattermost Team to configure with BotKube
channel: 'MATTERMOST_CHANNEL' # Mattermost Channel for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)
# Settings for Discord
discord:
enabled: false
token: 'DISCORD_TOKEN' # BotKube Bot Token
botid: 'DISCORD_BOT_ID' # BotKube Application Client ID
channel: 'DISCORD_CHANNEL_ID' # Discord Channel id for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)
# Settings for ELS
elasticsearch:
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ require (
github.com/Masterminds/squirrel v1.1.0 // indirect
github.com/aws/aws-sdk-go v1.32.12
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/bwmarrin/discordgo v0.20.3
github.com/docker/docker v1.13.1 // indirect
github.com/dyatlov/go-opengraph v0.0.0-20180429202543-816b6608b3c8 // indirect
github.com/evanphx/json-patch v4.5.0+incompatible // indirect
github.com/fortytw2/leaktest v1.3.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bwmarrin/discordgo v0.20.3 h1:AxjcHGbyBFSC0a3Zx5nDQwbOjU7xai5dXjRnZ0YB7nU=
github.com/bwmarrin/discordgo v0.20.3/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA=
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
Expand All @@ -56,6 +58,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0 h1:w3NnFcKR5241cfmQU5ZZAsf0xcpId6mWOupTvJlUX2U=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
Expand Down Expand Up @@ -155,6 +159,7 @@ github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJ
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
Expand Down Expand Up @@ -323,6 +328,7 @@ go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down
10 changes: 9 additions & 1 deletion helm/botkube/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,15 @@ communications:
team: 'MATTERMOST_TEAM' # Mattermost Team to configure with BotKube
channel: 'MATTERMOST_CHANNEL' # Mattermost Channel for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)


# Settings for Discord
discord:
enabled: false
token: 'DISCORD_TOKEN' # BotKube Bot Token
botid: 'DISCORD_BOT_ID' # BotKube Application Client ID
channel: 'DISCORD_CHANNEL_ID' # Discord Channel id for receiving BotKube alerts
notiftype: short # Change notification type short/long you want to receive. notiftype is optional and Default notification type is short (if not specified)

# Settings for ELS
elasticsearch:
enabled: false
Expand Down
147 changes: 147 additions & 0 deletions pkg/bot/discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) 2020 InfraCloud Technologies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package bot

import (
"strings"

"github.com/bwmarrin/discordgo"
"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/pkg/execute"
"github.com/infracloudio/botkube/pkg/log"
)

// DiscordBot listens for user's message, execute commands and sends back the response
type DiscordBot struct {
Token string
AllowKubectl bool
RestrictAccess bool
ClusterName string
ChannelID string
BotID string
DefaultNamespace string
}

// discordMessage contains message details to execute command and send back the result
type discordMessage struct {
Event *discordgo.MessageCreate
BotID string
Request string
Response string
IsAuthChannel bool
Session *discordgo.Session
}

// NewDiscordBot returns new Bot object
func NewDiscordBot(c *config.Config) Bot {
return &DiscordBot{
Token: c.Communications.Discord.Token,
BotID: c.Communications.Discord.BotID,
AllowKubectl: c.Settings.Kubectl.Enabled,
RestrictAccess: c.Settings.Kubectl.RestrictAccess,
ClusterName: c.Settings.ClusterName,
ChannelID: c.Communications.Discord.Channel,
DefaultNamespace: c.Settings.Kubectl.DefaultNamespace,
}
}

// Start starts the DiscordBot websocket connection and listens for messages
func (b *DiscordBot) Start() {
api, err := discordgo.New("Bot " + b.Token)
if err != nil {
log.Error("error creating Discord session,", err)
return
}

// Register the messageCreate func as a callback for MessageCreate events.
api.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
dm := discordMessage{
Event: m,
BotID: b.BotID,
Session: s,
}

dm.HandleMessage(b)
})

// Open a websocket connection to Discord and begin listening.
go func() {
err := api.Open()
if err != nil {
log.Error("error opening connection,", err)
return
}
}()

log.Info("BotKube connected to Discord!")
}

// HandleMessage handles the incomming messages
func (dm *discordMessage) HandleMessage(b *DiscordBot) {

// Serve only if starts with mention
if !strings.HasPrefix(dm.Event.Content, "<@!"+dm.BotID+"> ") {
return
}

log.Debugf("Discord incoming message: %+v", dm.Event.Content)

// Serve only if current channel is in config
if b.ChannelID == dm.Event.ChannelID {
dm.IsAuthChannel = true
}

// Trim the @BotKube prefix
dm.Request = strings.TrimPrefix(dm.Event.Content, "<@!"+dm.BotID+"> ")
if len(dm.Request) == 0 {
return
}

e := execute.NewDefaultExecutor(dm.Request, b.AllowKubectl, b.RestrictAccess, b.DefaultNamespace, b.ClusterName, b.ChannelID, dm.IsAuthChannel)
dm.Response = e.Execute()
dm.Send()
}

func (dm discordMessage) Send() {
// Upload message as a file if too long
if len(dm.Response) >= 2000 {
params := &discordgo.MessageSend{
Content: dm.Request,
Files: []*discordgo.File{
{
Name: "Response",
Reader: strings.NewReader(dm.Response),
},
},
}

if _, err := dm.Session.ChannelMessageSendComplex(dm.Event.ChannelID, params); err != nil {
log.Error("Error in uploading file:", err)
}
return
} else if len(dm.Response) == 0 {
log.Info("Invalid request. Dumping the response")
return
}

if _, err := dm.Session.ChannelMessageSend(dm.Event.ChannelID, dm.Response); err != nil {
log.Error("Error in sending message:", err)
}
}
10 changes: 10 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ type Communications struct {
Slack Slack
ElasticSearch ElasticSearch
Mattermost Mattermost
Discord Discord
Webhook Webhook
}

Expand Down Expand Up @@ -148,6 +149,15 @@ type Mattermost struct {
NotifType NotifType `yaml:",omitempty"`
}

// Discord configuration for authentication and send notifications
type Discord struct {
Enabled bool
Token string
BotID string
Channel string
NotifType NotifType `yaml:",omitempty"`
}

// Webhook configuration to send notifications
type Webhook struct {
Enabled bool
Expand Down
Loading

0 comments on commit a3ff62f

Please sign in to comment.