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

Add Discord support #268

Merged
merged 1 commit into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ FROM golang:1.13-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
6 changes: 6 additions & 0 deletions cmd/botkube/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ func startController() error {
go tb.Start()
}

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

// Start upgrade notifier
if conf.Settings.UpgradeNotifier {
log.Info("Starting upgrade notifier")
Expand Down
33 changes: 21 additions & 12 deletions comm_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ 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
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)
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)

# Settings for MS Teams
teams:
Expand All @@ -24,15 +24,24 @@ communications:
notiftype: short
port: 3978

# 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
awsSigning:
enabled: false # enable awsSigning using IAM for Elastisearch hosted on AWS, if true make sure AWS environment variables are set. Refer https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
awsRegion: 'us-east-1' # AWS region where Elasticsearch is deployed
roleArn: '' # AWS IAM Role arn to assume for credentials, use this only if you dont want to use the EC2 instance role or not running on AWS instance
server: 'ELASTICSEARCH_ADDRESS' # e.g https://example.com:9243
username: 'ELASTICSEARCH_USERNAME' # Basic Auth
enabled: false # enable awsSigning using IAM for Elastisearch hosted on AWS, if true make sure AWS environment variables are set. Refer https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
awsRegion: 'us-east-1' # AWS region where Elasticsearch is deployed
roleArn: '' # AWS IAM Role arn to assume for credentials, use this only if you dont want to use the EC2 instance role or not running on AWS instance
server: 'ELASTICSEARCH_ADDRESS' # e.g https://example.com:9243
username: 'ELASTICSEARCH_USERNAME' # Basic Auth
password: 'ELASTICSEARCH_PASSWORD'
# ELS index settings
index:
Expand All @@ -44,4 +53,4 @@ communications:
# Settings for Webhook
webhook:
enabled: false
url: 'WEBHOOK_URL' # e.g https://example.com:80
url: 'WEBHOOK_URL' # e.g https://example.com:80
8 changes: 8 additions & 0 deletions deploy-all-in-one-tls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,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:
enabled: 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 @@ -256,6 +256,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 @@ -46,6 +46,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 @@ -65,6 +67,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 @@ -165,6 +169,7 @@ github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1a
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
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 @@ -340,6 +345,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
9 changes: 9 additions & 0 deletions helm/botkube/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,15 @@ communications:
notiftype: short
port: 3978


# 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
156 changes: 156 additions & 0 deletions pkg/bot/discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// 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 incoming messages
func (dm *discordMessage) HandleMessage(b *DiscordBot) {

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

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

// Trim the @BotKube prefix
if strings.HasPrefix(dm.Event.Content, "<@!"+dm.BotID+"> ") {
dm.Request = strings.TrimPrefix(dm.Event.Content, "<@!"+dm.BotID+"> ")
} else if strings.HasPrefix(dm.Event.Content, "<@"+dm.BotID+"> ") {
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, config.DiscordBot, b.ChannelID, dm.IsAuthChannel)

dm.Response = e.Execute()
dm.Send()
}

func (dm discordMessage) Send() {
log.Debugf("Discord incoming Request: %s", dm.Request)
log.Debugf("Discord Response: %s", dm.Response)

// 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)
}
}
12 changes: 12 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ const (
MattermostBot BotPlatform = "mattermost"
// TeamsBot bot platform
TeamsBot BotPlatform = "teams"
// DiscordBot bot Platform
DiscordBot BotPlatform = "discord"
)

// EventType to watch
Expand Down Expand Up @@ -132,6 +134,7 @@ type Namespaces struct {
type CommunicationsConfig struct {
Slack Slack
Mattermost Mattermost
Discord Discord
Webhook Webhook
Teams Teams
ElasticSearch ElasticSearch
Expand Down Expand Up @@ -191,6 +194,15 @@ type Teams 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