From 75e0b57b85a46c8120263b8904c5954852fbf0f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= <nils@piksel.se>
Date: Sat, 3 Jul 2021 21:43:41 +0200
Subject: [PATCH 1/6] feat(slack): add bot API support

---
 pkg/services/slack/slack.go        |  80 +++++++++---
 pkg/services/slack/slack_config.go |  39 +++---
 pkg/services/slack/slack_errors.go |  24 +---
 pkg/services/slack/slack_json.go   |  49 +++++--
 pkg/services/slack/slack_test.go   | 200 ++++++++++++++++++-----------
 pkg/services/slack/slack_token.go  | 135 +++++++++++++------
 6 files changed, 347 insertions(+), 180 deletions(-)

diff --git a/pkg/services/slack/slack.go b/pkg/services/slack/slack.go
index 45e5aed6..24358402 100644
--- a/pkg/services/slack/slack.go
+++ b/pkg/services/slack/slack.go
@@ -2,11 +2,13 @@ package slack
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"github.com/containrrr/shoutrrr/pkg/format"
+	"github.com/containrrr/shoutrrr/pkg/util/jsonclient"
+	"io/ioutil"
 	"net/http"
 	"net/url"
-	"strings"
 
 	"github.com/containrrr/shoutrrr/pkg/services/standard"
 	"github.com/containrrr/shoutrrr/pkg/types"
@@ -20,7 +22,7 @@ type Service struct {
 }
 
 const (
-	apiURL = "https://hooks.slack.com/services"
+	apiPostMessage = "https://slack.com/api/chat.postMessage"
 )
 
 // Send a notification message to Slack
@@ -31,11 +33,20 @@ func (service *Service) Send(message string, params *types.Params) error {
 		return err
 	}
 
-	if err := ValidateToken(config.Token); err != nil {
-		return err
+	payload := CreateJSONPayload(config, message)
+
+	var err error
+	if config.Token.IsAPIToken() {
+		err = service.sendApi(config, payload)
+	} else {
+		err = service.sendWebhook(config, payload)
+	}
+
+	if err != nil {
+		return fmt.Errorf("failed to send slack notification: %v", err)
 	}
 
-	return service.doSend(config, message)
+	return nil
 }
 
 // Initialize loads ServiceConfig from configURL and sets logger for this Service
@@ -43,33 +54,60 @@ func (service *Service) Initialize(configURL *url.URL, logger types.StdLogger) e
 	service.Logger.SetLogger(logger)
 	service.config = &Config{}
 	service.pkr = format.NewPropKeyResolver(service.config)
-	return service.config.setURL(&service.pkr, configURL)
+	if err := service.config.setURL(&service.pkr, configURL); err != nil {
+		return err
+	}
+	return nil
 }
 
-func (service *Service) doSend(config *Config, message string) error {
-	postURL := service.getURL(config)
-	payload, err := CreateJSONPayload(config, message)
-
-	var res *http.Response
-	if err == nil {
-		res, err = http.Post(postURL, "application/json", bytes.NewBuffer(payload))
+func (service *Service) sendApi(config *Config, payload interface{}) error {
+	response := APIResponse{}
+	jsonClient := jsonclient.Client{
+		HTTPClient:          http.DefaultClient,
+		AuthorizationHeader: config.Token.Authorization(),
 	}
 
-	if res == nil && err == nil {
-		err = fmt.Errorf("unknown error")
+	if err := jsonClient.Post(apiPostMessage, payload, &response); err != nil {
+		return err
 	}
 
-	if err == nil && res.StatusCode != http.StatusOK {
-		err = fmt.Errorf("response status code %s", res.Status)
+	if !response.Ok {
+		if response.Error != "" {
+			return fmt.Errorf("api response: %v", response.Error)
+		}
+		return fmt.Errorf("unknown error")
 	}
 
-	if err != nil {
-		return fmt.Errorf("failed to send slack notification: %v", err)
+	if response.Warning != "" {
+		service.Logger.Logf("Slack API warning: %q", response.Warning)
 	}
 
 	return nil
 }
 
-func (service *Service) getURL(config *Config) string {
-	return fmt.Sprintf("%s/%s", apiURL, strings.Join(config.Token, "/"))
+func (service *Service) sendWebhook(config *Config, payload interface{}) error {
+	payloadBytes, err := json.Marshal(payload)
+	var res *http.Response
+	res, err = http.Post(config.Token.WebhookURL(), jsonclient.ContentType, bytes.NewBuffer(payloadBytes))
+
+	if err != nil {
+		return fmt.Errorf("failed to invoke webhook: %v", err)
+	}
+	defer res.Body.Close()
+	resBytes, _ := ioutil.ReadAll(res.Body)
+	response := string(resBytes)
+
+	switch response {
+	case "":
+		if res.StatusCode != http.StatusOK {
+			return fmt.Errorf("webhook status: %v", res.Status)
+		}
+		// Treat status 200 as no error regardless of actual content
+		fallthrough
+	case "ok":
+		return nil
+	default:
+		return fmt.Errorf("webhook response: %v", response)
+	}
+
 }
diff --git a/pkg/services/slack/slack_config.go b/pkg/services/slack/slack_config.go
index 844e7a8b..54d26cc2 100644
--- a/pkg/services/slack/slack_config.go
+++ b/pkg/services/slack/slack_config.go
@@ -1,21 +1,21 @@
 package slack
 
 import (
-	"fmt"
 	"github.com/containrrr/shoutrrr/pkg/format"
 	"github.com/containrrr/shoutrrr/pkg/services/standard"
 	"github.com/containrrr/shoutrrr/pkg/types"
 	"net/url"
-	"strings"
 )
 
 // Config for the slack service
 type Config struct {
 	standard.EnumlessConfig
-	BotName string   `default:"" optional:"" url:"user" desc:"Bot name (uses default if empty)"`
-	Token   []string `desc:"Webhook token parts" url:"host,path1,path2"`
-	Color   string   `key:"color" optional:"" desc:"Message left-hand border color"`
-	Title   string   `key:"title" optional:"" desc:"Prepended text above the message"`
+	BotName string `optional:"uses bot default" key:"botname,username" desc:"Bot name"`
+	Icon    string `key:"icon,icon_emoji,icon_url" default:"" optional:"" desc:"Use emoji or URL as icon (based on presence of http(s):// prefix)"`
+	Token   Token  `desc:"API Bot token" url:"user,pass"`
+	Color   string `key:"color" optional:"default border color" desc:"Message left-hand border color"`
+	Title   string `key:"title" optional:"omitted" desc:"Prepended text above the message"`
+	Channel string `url:"host" desc:"Channel to send messages to in Cxxxxxxxxxx format"`
 }
 
 // GetURL returns a URL representation of it's current field values
@@ -32,9 +32,8 @@ func (config *Config) SetURL(url *url.URL) error {
 
 func (config *Config) getURL(resolver types.ConfigQueryResolver) *url.URL {
 	return &url.URL{
-		User:       url.User(config.BotName),
-		Host:       config.Token[0],
-		Path:       fmt.Sprintf("/%s/%s", config.Token[1], config.Token[2]),
+		User:       config.Token.UserInfo(),
+		Host:       config.Channel,
 		Scheme:     Scheme,
 		ForceQuery: false,
 		RawQuery:   format.BuildQuery(resolver),
@@ -43,21 +42,21 @@ func (config *Config) getURL(resolver types.ConfigQueryResolver) *url.URL {
 
 func (config *Config) setURL(resolver types.ConfigQueryResolver, serviceURL *url.URL) error {
 
-	botName := serviceURL.User.Username()
+	var token string
+	var err error
 
-	host := serviceURL.Hostname()
+	if len(serviceURL.Path) > 1 {
+		// Reading legacy config URL format
+		token = serviceURL.Hostname() + serviceURL.Path
 
-	token := strings.Split(serviceURL.Path, "/")
-	token[0] = host
-
-	if len(token) < 2 {
-		token = []string{"", "", ""}
+		config.Channel = "webhook"
+		config.BotName = serviceURL.User.Username()
+	} else {
+		token = serviceURL.User.String()
+		config.Channel = serviceURL.Hostname()
 	}
 
-	config.BotName = botName
-	config.Token = token
-
-	if err := ValidateToken(config.Token); err != nil {
+	if err = config.Token.SetFromProp(token); err != nil {
 		return err
 	}
 
diff --git a/pkg/services/slack/slack_errors.go b/pkg/services/slack/slack_errors.go
index c033947e..6adbc49a 100644
--- a/pkg/services/slack/slack_errors.go
+++ b/pkg/services/slack/slack_errors.go
@@ -1,21 +1,11 @@
 package slack
 
-// ErrorMessage for error events within the slack service
-type ErrorMessage string
+import "errors"
 
-const (
-	// TokenAMissing from the service URL
-	TokenAMissing ErrorMessage = "first part of the API token is missing"
-	// TokenBMissing from the service URL
-	TokenBMissing ErrorMessage = "second part of the API token is missing"
-	// TokenCMissing from the service URL
-	TokenCMissing ErrorMessage = "third part of the API token is missing."
-	// TokenAMalformed inthe service URL
-	TokenAMalformed ErrorMessage = "first part of the API token is malformed"
-	// TokenBMalformed inthe service URL
-	TokenBMalformed ErrorMessage = "second part of the API token is malformed"
-	// TokenCMalformed inthe service URL
-	TokenCMalformed ErrorMessage = "third part of the API token is malformed"
-	// NotEnoughArguments provided in the service URL
-	NotEnoughArguments ErrorMessage = "the apiURL does not include enough arguments"
+var (
+	// ErrorInvalidToken is returned whenever the specified token does not match any known formats
+	ErrorInvalidToken = errors.New("invalid slack token format")
+
+	// ErrorMismatchedTokenSeparators is returned if the token uses different separators between parts (of the recognized `/-,`)
+	ErrorMismatchedTokenSeparators = errors.New("invalid webhook token format")
 )
diff --git a/pkg/services/slack/slack_json.go b/pkg/services/slack/slack_json.go
index 2e3502c4..903e5801 100644
--- a/pkg/services/slack/slack_json.go
+++ b/pkg/services/slack/slack_json.go
@@ -1,16 +1,19 @@
 package slack
 
 import (
-	"encoding/json"
+	"regexp"
 	"strings"
 )
 
-// JSON used within the Slack service
-type JSON struct {
+// messagePayload used within the Slack service
+type messagePayload struct {
 	Text        string       `json:"text"`
 	BotName     string       `json:"username,omitempty"`
 	Blocks      []block      `json:"blocks,omitempty"`
 	Attachments []attachment `json:"attachments,omitempty"`
+	Channel     string       `json:"channel,omitempty"`
+	IconEmoji   string       `json:"icon_emoji,omitempty"`
+	IconURL     string       `json:"icon_url,omitempty"`
 }
 
 type block struct {
@@ -39,8 +42,19 @@ type legacyField struct {
 	Short bool   `json:"short,omitempty"`
 }
 
-// CreateJSONPayload compatible with the slack webhook api
-func CreateJSONPayload(config *Config, message string) ([]byte, error) {
+type APIResponse struct {
+	Ok       bool   `json:"ok"`
+	Error    string `json:"error"`
+	Warning  string `json:"warning"`
+	MetaData struct {
+		Warnings []string `json:"warnings"`
+	} `json:"response_metadata"`
+}
+
+var iconUrlPattern = regexp.MustCompile(`https?://`)
+
+// CreateJSONPayload compatible with the slack post message API
+func CreateJSONPayload(config *Config, message string) interface{} {
 
 	var atts []attachment
 	for _, line := range strings.Split(message, "\n") {
@@ -50,10 +64,23 @@ func CreateJSONPayload(config *Config, message string) ([]byte, error) {
 		})
 	}
 
-	return json.Marshal(
-		JSON{
-			Text:        config.Title,
-			BotName:     config.BotName,
-			Attachments: atts,
-		})
+	payload := messagePayload{
+		Text:        config.Title,
+		BotName:     config.BotName,
+		Attachments: atts,
+	}
+
+	if config.Icon != "" {
+		if iconUrlPattern.MatchString(config.Icon) {
+			payload.IconURL = config.Icon
+		} else {
+			payload.IconEmoji = config.Icon
+		}
+	}
+
+	if config.Channel != "webhook" {
+		payload.Channel = config.Channel
+	}
+
+	return payload
 }
diff --git a/pkg/services/slack/slack_test.go b/pkg/services/slack/slack_test.go
index e855fab2..c47190f4 100644
--- a/pkg/services/slack/slack_test.go
+++ b/pkg/services/slack/slack_test.go
@@ -13,9 +13,11 @@ import (
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	gomegaformat "github.com/onsi/gomega/format"
 )
 
 func TestSlack(t *testing.T) {
+	gomegaformat.CharactersAroundMismatchToInclude = 20
 	RegisterFailHandler(Fail)
 	RunSpecs(t, "Shoutrrr Slack Suite")
 }
@@ -49,76 +51,57 @@ var _ = Describe("the slack service", func() {
 		})
 	})
 
+	// xoxb:123456789012-1234567890123-4mt0t4l1YL3g1T5L4cK70k3N
+
 	When("given a token with a malformed part", func() {
 		It("should return an error if part A is not 9 letters", func() {
-			slackURL, err := url.Parse("slack://lol@12345678/123456789/123456789123456789123456")
-			Expect(err).NotTo(HaveOccurred())
-			expectErrorMessageGivenURL(
-				TokenAMalformed,
-				slackURL,
-			)
+			expectErrorMessageGivenURL(ErrorInvalidToken, "slack://lol@12345678/123456789/123456789123456789123456")
 		})
 		It("should return an error if part B is not 9 letters", func() {
-			slackURL, err := url.Parse("slack://lol@123456789/12345678/123456789123456789123456")
-			Expect(err).NotTo(HaveOccurred())
-			expectErrorMessageGivenURL(
-				TokenBMalformed,
-				slackURL,
-			)
+			expectErrorMessageGivenURL(ErrorInvalidToken, "slack://lol@123456789/12345678/123456789123456789123456")
 		})
 		It("should return an error if part C is not 24 letters", func() {
-			slackURL, err := url.Parse("slack://123456789/123456789/12345678912345678912345")
-			Expect(err).NotTo(HaveOccurred())
-			expectErrorMessageGivenURL(
-				TokenCMalformed,
-				slackURL,
-			)
+			expectErrorMessageGivenURL(ErrorInvalidToken, "slack://123456789/123456789/12345678912345678912345")
 		})
 	})
 	When("given a token missing a part", func() {
 		It("should return an error if the missing part is A", func() {
-			slackURL, err := url.Parse("slack://lol@/123456789/123456789123456789123456")
-			Expect(err).NotTo(HaveOccurred())
-			expectErrorMessageGivenURL(
-				TokenAMissing,
-				slackURL,
-			)
+			expectErrorMessageGivenURL(ErrorInvalidToken, "slack://lol@/123456789/123456789123456789123456")
 		})
 		It("should return an error if the missing part is B", func() {
-			slackURL, err := url.Parse("slack://lol@123456789//123456789")
-			Expect(err).NotTo(HaveOccurred())
-			expectErrorMessageGivenURL(
-				TokenBMissing,
-				slackURL,
-			)
-
+			expectErrorMessageGivenURL(ErrorInvalidToken, "slack://lol@123456789//123456789")
 		})
 		It("should return an error if the missing part is C", func() {
-			slackURL, err := url.Parse("slack://lol@123456789/123456789/")
-			Expect(err).NotTo(HaveOccurred())
-			expectErrorMessageGivenURL(
-				TokenCMissing,
-				slackURL,
-			)
+			expectErrorMessageGivenURL(ErrorInvalidToken, "slack://lol@123456789/123456789/")
 		})
 	})
 	Describe("the slack config", func() {
 		When("parsing the configuration URL", func() {
-			It("should be identical after de-/serialization", func() {
-				testURL := "slack://testbot@AAAAAAAAA/BBBBBBBBB/123456789123456789123456?color=3f00fe&title=Test+title"
+			When("given a config using the legacy format", func() {
+				It("should be converted to the new format after de-/serialization", func() {
+					oldURL := "slack://testbot@AAAAAAAAA/BBBBBBBBB/123456789123456789123456?color=3f00fe&title=Test+title"
+					newURL := "slack://hook:AAAAAAAAA-BBBBBBBBB-123456789123456789123456@webhook?botname=testbot&color=3f00fe&title=Test+title"
 
-				url, err := url.Parse(testURL)
-				Expect(err).NotTo(HaveOccurred(), "parsing")
+					config := &Config{}
+					err := config.SetURL(urlMust(oldURL))
+					Expect(err).NotTo(HaveOccurred(), "verifying")
 
-				config := &Config{}
-				err = config.SetURL(url)
-				Expect(err).NotTo(HaveOccurred(), "verifying")
-
-				outputURL := config.GetURL()
-				Expect(outputURL.String()).To(Equal(testURL))
+					Expect(config.GetURL().String()).To(Equal(newURL))
 
+				})
 			})
 		})
+		It("should be identical after de-/serialization", func() {
+			testURL := "slack://hook:AAAAAAAAA-BBBBBBBBB-123456789123456789123456@webhook?botname=testbot&color=3f00fe&title=Test+title"
+
+			config := &Config{}
+			err := config.SetURL(urlMust(testURL))
+			Expect(err).NotTo(HaveOccurred(), "verifying")
+
+			outputURL := config.GetURL()
+			Expect(outputURL.String()).To(Equal(testURL))
+
+		})
 		When("generating a config object", func() {
 			It("should use the default botname if the argument list contains three strings", func() {
 				slackURL, _ := url.Parse("slack://AAAAAAAAA/BBBBBBBBB/123456789123456789123456")
@@ -141,43 +124,112 @@ var _ = Describe("the slack service", func() {
 				Expect(configError).To(HaveOccurred())
 			})
 		})
+		When("getting credentials from token", func() {
+			It("should return a valid webhook URL for the given token", func() {
+				token := tokenMust("AAAAAAAAA/BBBBBBBBB/123456789123456789123456")
+				expected := "https://hooks.slack.com/services/AAAAAAAAA/BBBBBBBBB/123456789123456789123456"
+				Expect(token.WebhookURL()).To(Equal(expected))
+			})
+			It("should return a valid authorization header value for the given token", func() {
+				token := tokenMust("xoxb:AAAAAAAAA-BBBBBBBBB-123456789123456789123456")
+				expected := "Bearer xoxb-AAAAAAAAA-BBBBBBBBB-123456789123456789123456"
+				Expect(token.Authorization()).To(Equal(expected))
+			})
+		})
 	})
 
 	Describe("sending the payload", func() {
-		var err error
-		BeforeEach(func() {
-			httpmock.Activate()
-		})
-		AfterEach(func() {
-			httpmock.DeactivateAndReset()
-		})
-		It("should not report an error if the server accepts the payload", func() {
-			serviceURL, _ := url.Parse("slack://testbot@AAAAAAAAA/BBBBBBBBB/123456789123456789123456")
-			err = service.Initialize(serviceURL, logger)
-			Expect(err).NotTo(HaveOccurred())
+		When("sending via webhook URL", func() {
+			var err error
+			BeforeEach(func() {
+				httpmock.Activate()
+			})
+			AfterEach(func() {
+				httpmock.DeactivateAndReset()
+			})
 
-			targetURL := "https://hooks.slack.com/services/AAAAAAAAA/BBBBBBBBB/123456789123456789123456"
-			httpmock.RegisterResponder("POST", targetURL, httpmock.NewStringResponder(200, ""))
+			It("should not report an error if the server accepts the payload", func() {
+				serviceURL, _ := url.Parse("slack://testbot@AAAAAAAAA/BBBBBBBBB/123456789123456789123456")
+				err = service.Initialize(serviceURL, logger)
+				Expect(err).NotTo(HaveOccurred())
 
-			err = service.Send("Message", nil)
-			Expect(err).NotTo(HaveOccurred())
+				targetURL := "https://hooks.slack.com/services/AAAAAAAAA/BBBBBBBBB/123456789123456789123456"
+				httpmock.RegisterResponder("POST", targetURL, httpmock.NewStringResponder(200, ""))
+
+				err = service.Send("Message", nil)
+				Expect(err).NotTo(HaveOccurred())
+			})
+			It("should not panic if an error occurs when sending the payload", func() {
+				serviceURL, _ := url.Parse("slack://testbot@AAAAAAAAA/BBBBBBBBB/123456789123456789123456")
+				err = service.Initialize(serviceURL, logger)
+				Expect(err).NotTo(HaveOccurred())
+
+				targetURL := "https://hooks.slack.com/services/AAAAAAAAA/BBBBBBBBB/123456789123456789123456"
+				httpmock.RegisterResponder("POST", targetURL, httpmock.NewErrorResponder(errors.New("dummy error")))
+
+				err = service.Send("Message", nil)
+				Expect(err).To(HaveOccurred())
+			})
 		})
-		It("should not panic if an error occurs when sending the payload", func() {
-			serviceURL, _ := url.Parse("slack://testbot@AAAAAAAAA/BBBBBBBBB/123456789123456789123456")
-			err = service.Initialize(serviceURL, logger)
-			Expect(err).NotTo(HaveOccurred())
+		When("sending via bot API", func() {
+			var err error
+			BeforeEach(func() {
+				httpmock.Activate()
+			})
+			AfterEach(func() {
+				httpmock.DeactivateAndReset()
+			})
 
-			targetURL := "https://hooks.slack.com/services/AAAAAAAAA/BBBBBBBBB/123456789123456789123456"
-			httpmock.RegisterResponder("POST", targetURL, httpmock.NewErrorResponder(errors.New("dummy error")))
+			It("should not report an error if the server accepts the payload", func() {
+				serviceURL := urlMust("slack://xoxb:123456789012-1234567890123-4mt0t4l1YL3g1T5L4cK70k3N@C0123456789")
+				err = service.Initialize(serviceURL, logger)
+				Expect(err).NotTo(HaveOccurred())
 
-			err = service.Send("Message", nil)
-			Expect(err).To(HaveOccurred())
+				targetURL := "https://slack.com/api/chat.postMessage"
+				httpmock.RegisterResponder("POST", targetURL, jsonRespondMust(200, APIResponse{
+					Ok: true,
+				}))
+
+				err = service.Send("Message", nil)
+				Expect(err).NotTo(HaveOccurred())
+			})
+			It("should not panic if an error occurs when sending the payload", func() {
+				serviceURL := urlMust("slack://xoxb:123456789012-1234567890123-4mt0t4l1YL3g1T5L4cK70k3N@C0123456789")
+				err = service.Initialize(serviceURL, logger)
+				Expect(err).NotTo(HaveOccurred())
+
+				targetURL := "https://slack.com/api/chat.postMessage"
+				httpmock.RegisterResponder("POST", targetURL, jsonRespondMust(200, APIResponse{
+					Error: "someone turned off the internet",
+				}))
+
+				err = service.Send("Message", nil)
+				Expect(err).To(HaveOccurred())
+			})
 		})
 	})
 })
 
-func expectErrorMessageGivenURL(msg ErrorMessage, slackURL *url.URL) {
-	err := service.Initialize(slackURL, util.TestLogger())
-	Expect(err).To(HaveOccurred())
-	Expect(err.Error()).To(Equal(string(msg)))
+func jsonRespondMust(code int, response APIResponse) httpmock.Responder {
+	responder, err := httpmock.NewJsonResponder(code, response)
+	ExpectWithOffset(1, err).NotTo(HaveOccurred(), "invalid test response struct")
+	return responder
+}
+
+func urlMust(rawURL string) *url.URL {
+	parsed, err := url.Parse(rawURL)
+	ExpectWithOffset(1, err).NotTo(HaveOccurred(), "invalid test URL string")
+	return parsed
+}
+
+func tokenMust(rawToken string) *Token {
+	token, err := ParseToken(rawToken)
+	ExpectWithOffset(1, err).NotTo(HaveOccurred())
+	return token
+}
+
+func expectErrorMessageGivenURL(expected error, rawURL string) {
+	err := service.Initialize(urlMust(rawURL), util.TestLogger())
+	ExpectWithOffset(1, err).To(HaveOccurred())
+	ExpectWithOffset(1, err).To(Equal(expected))
 }
diff --git a/pkg/services/slack/slack_token.go b/pkg/services/slack/slack_token.go
index e6f4aff2..643d58f1 100644
--- a/pkg/services/slack/slack_token.go
+++ b/pkg/services/slack/slack_token.go
@@ -1,60 +1,121 @@
 package slack
 
 import (
-	"errors"
+	"fmt"
+	"github.com/containrrr/shoutrrr/pkg/types"
+	"net/url"
 	"regexp"
 	"strings"
 )
 
-// Token is a three part string split into A, B and C
-type Token []string
+var _ types.ConfigProp = &Token{}
 
-// ValidateToken checks that the token is in the expected format
-func ValidateToken(token Token) error {
-	if err := tokenPartsAreNotEmpty(token); err != nil {
-		return err
-	} else if err := tokenPartsAreValidFormat(token); err != nil {
-		return err
-	}
-	return nil
+const (
+	hookTokenIdentifier = "hook"
+	userTokenIdentifier = "xoxp"
+	botTokenIdentifier  = "xoxb"
+)
+
+// Token is a Slack API token or a Slack webhook token
+type Token struct {
+	raw string
 }
 
-func tokenPartsAreNotEmpty(token Token) error {
-	if token[0] == "" {
-		return errors.New(string(TokenAMissing))
-	} else if token[1] == "" {
-		return errors.New(string(TokenBMissing))
-	} else if token[2] == "" {
-		return errors.New(string(TokenCMissing))
+func (token *Token) SetFromProp(propValue string) error {
+	if len(propValue) < 3 {
+		return ErrorInvalidToken
+	}
+
+	match := tokenPattern.FindStringSubmatch(propValue)
+	if match == nil || len(match) != tokenMatchCount {
+		return ErrorInvalidToken
+	}
+
+	typeIdentifier := match[tokenMatchType]
+	if typeIdentifier == "" {
+		typeIdentifier = hookTokenIdentifier
+	}
+
+	token.raw = fmt.Sprintf("%s:%s-%s-%s",
+		typeIdentifier, match[tokenMatchPart1], match[tokenMatchPart2], match[tokenMatchPart3])
+
+	if match[tokenMatchSep1] != match[tokenMatchSep2] {
+		return ErrorMismatchedTokenSeparators
 	}
+
 	return nil
 }
 
-func tokenPartsAreValidFormat(token Token) error {
-	if !matchesPattern("[A-Z0-9]{9}", token[0]) {
-		return errors.New(string(TokenAMalformed))
-	} else if !matchesPattern("[A-Z0-9]{9}", token[1]) {
-		return errors.New(string(TokenBMalformed))
-	} else if !matchesPattern("[A-Za-z0-9]{24}", token[2]) {
-		return errors.New(string(TokenCMalformed))
+func (token *Token) GetPropValue() (string, error) {
+	if token == nil {
+		return "", nil
 	}
-	return nil
+
+	return token.raw, nil
 }
 
-func matchesPattern(pattern string, part string) bool {
-	matched, err := regexp.Match(pattern, []byte(part))
-	if matched != true || err != nil {
-		return false
+// TypeIdentifier returns the type identifier of the token
+func (token Token) TypeIdentifier() string {
+	return token.raw[:4]
+}
+
+// ParseToken parses and normalizes a token string
+func ParseToken(str string) (*Token, error) {
+	token := &Token{}
+	if err := token.SetFromProp(str); err != nil {
+		return nil, err
 	}
-	return true
+	return token, nil
+}
+
+const (
+	tokenMatchFull = iota
+	tokenMatchType
+	tokenMatchPart1
+	tokenMatchSep1
+	tokenMatchPart2
+	tokenMatchSep2
+	tokenMatchPart3
+	tokenMatchCount
+)
+
+var tokenPattern = regexp.MustCompile(`(?:(?P<type>xox.|hook)[-:]|:?)(?P<p1>[A-Z0-9]{9,})(?P<s1>[-/,])(?P<p2>[A-Z0-9]{9,})(?P<s2>[-/,])(?P<p3>[A-Za-z0-9]{24,})`)
+
+// String returns the token in normalized format with dashes (-) as separator
+func (token *Token) String() string {
+	return token.raw
 }
 
-func (t Token) String() string {
-	return strings.Join(t, "-")
+func (token *Token) UserInfo() *url.Userinfo {
+	return url.UserPassword(token.raw[:4], token.raw[5:])
+}
+
+func (token *Token) IsAPIToken() bool {
+	return token.TypeIdentifier() != hookTokenIdentifier
+}
+
+const webhookBase = "https://hooks.slack.com/services/"
+
+func (token Token) WebhookURL() string {
+	sb := strings.Builder{}
+	sb.WriteString(webhookBase)
+	sb.Grow(len(token.raw) - 5)
+	for i := 5; i < len(token.raw); i++ {
+		c := token.raw[i]
+		if c == '-' {
+			c = '/'
+		}
+		sb.WriteByte(c)
+	}
+	return sb.String()
 }
 
-// ParseToken creates a Token from a sting representation
-func ParseToken(s string) Token {
-	token := strings.Split(s, "-")
-	return token
+func (token *Token) Authorization() string {
+	sb := strings.Builder{}
+	sb.WriteString("Bearer ")
+	sb.Grow(len(token.raw))
+	sb.WriteString(token.raw[:4])
+	sb.WriteRune('-')
+	sb.WriteString(token.raw[5:])
+	return sb.String()
 }

From a6547febfae1d15e049540d76073ca801947da61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= <nils@piksel.se>
Date: Sat, 3 Jul 2021 22:57:49 +0200
Subject: [PATCH 2/6] test: fix common generator tests

---
 go.mod                                 |  1 +
 go.sum                                 | 34 ----------
 pkg/util/generator/generator_common.go |  1 +
 pkg/util/generator/generator_test.go   | 93 ++++++++++++++------------
 4 files changed, 53 insertions(+), 76 deletions(-)

diff --git a/go.mod b/go.mod
index a85a4782..71e60926 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
 	github.com/google/uuid v1.1.5 // indirect
 	github.com/jarcoal/httpmock v1.0.4
 	github.com/klauspost/compress v1.11.7 // indirect
+	github.com/mattn/go-colorable v0.1.8
 	github.com/mitchellh/mapstructure v1.2.2 // indirect
 	github.com/nxadm/tail v1.4.6 // indirect
 	github.com/onsi/ginkgo v1.14.2
diff --git a/go.sum b/go.sum
index e1969ea2..72f3b576 100644
--- a/go.sum
+++ b/go.sum
@@ -35,7 +35,6 @@ github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
 github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -69,9 +68,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
@@ -85,7 +82,6 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
@@ -93,7 +89,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
 github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -107,7 +102,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -121,12 +115,10 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
 github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -137,7 +129,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -151,14 +142,12 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
 github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
 github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
 github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -168,13 +157,11 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nxadm/tail v1.4.6 h1:11TGpSHY7Esh/i/qnq02Jo5oVrI1Gue8Slbq0ujPZFQ=
 github.com/nxadm/tail v1.4.6/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
 github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
@@ -183,7 +170,6 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
 github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
@@ -204,7 +190,6 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
@@ -213,33 +198,27 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
 github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
 github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -261,7 +240,6 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -274,7 +252,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
@@ -293,9 +270,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -312,7 +287,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
 golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -325,7 +299,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -344,14 +317,11 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
-gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
 gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
 gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -360,11 +330,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -374,7 +341,6 @@ gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81
 gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
-nhooyr.io/websocket v1.6.5 h1:8TzpkldRfefda5JST+CnOH135bzVPz5uzfn/AF+gVKg=
 nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=
 nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
 nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
diff --git a/pkg/util/generator/generator_common.go b/pkg/util/generator/generator_common.go
index 9bc84434..38035a50 100644
--- a/pkg/util/generator/generator_common.go
+++ b/pkg/util/generator/generator_common.go
@@ -124,6 +124,7 @@ func (ud *UserDialog) QueryString(prompt string, validator func(string) error, k
 
 		if err := validator(answer); err != nil {
 			ud.Writeln("%v", err)
+			ud.Writeln("")
 			continue
 		}
 		return answer
diff --git a/pkg/util/generator/generator_test.go b/pkg/util/generator/generator_test.go
index 645fb665..1388ec7a 100644
--- a/pkg/util/generator/generator_test.go
+++ b/pkg/util/generator/generator_test.go
@@ -3,6 +3,7 @@ package generator_test
 import (
 	"fmt"
 	"github.com/containrrr/shoutrrr/pkg/util/generator"
+	"github.com/mattn/go-colorable"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 	"github.com/onsi/gomega/gbytes"
@@ -37,51 +38,51 @@ func dumpBuffers() {
 }
 
 var _ = Describe("GeneratorCommon", func() {
-	Describe("attach to the data stream", func() {
-
-		BeforeEach(func() {
-			userOut = gbytes.NewBuffer()
-			userIn = gbytes.NewBuffer()
-			client = generator.NewUserDialog(userOut, userIn, map[string]string{"propKey": "propVal"})
-		})
+	BeforeEach(func() {
+		userOut = gbytes.NewBuffer()
+		userIn = gbytes.NewBuffer()
+		userInMono := colorable.NewNonColorable(userIn)
+		client = generator.NewUserDialog(userOut, userInMono, map[string]string{"propKey": "propVal"})
+	})
 
-		It("reprompt upon invalid answers", func() {
-			defer dumpBuffers()
-			answer := make(chan string)
-			go func() {
-				answer <- client.QueryString("name:", generator.Required, "")
-			}()
+	It("reprompt upon invalid answers", func() {
+		defer dumpBuffers()
+		answer := make(chan string)
+		go func() {
+			answer <- client.QueryString("name:", generator.Required, "")
+		}()
 
-			mockTyped("")
-			mockTyped("Normal Human Name")
+		mockTyped("")
+		mockTyped("Normal Human Name")
 
-			Eventually(userIn).Should(gbytes.Say(`name: `))
+		Eventually(userIn).Should(gbytes.Say(`name: `))
 
-			Eventually(userIn).Should(gbytes.Say(`field is required`))
-			Eventually(userIn).Should(gbytes.Say(`name: `))
-			Eventually(answer).Should(Receive(Equal("Normal Human Name")))
-		})
+		Eventually(userIn).Should(gbytes.Say(`field is required`))
+		Eventually(userIn).Should(gbytes.Say(`name: `))
+		Eventually(answer).Should(Receive(Equal("Normal Human Name")))
+	})
 
-		It("should accept any input when validator is nil", func() {
-			defer dumpBuffers()
-			answer := make(chan string)
-			go func() {
-				answer <- client.QueryString("name:", nil, "")
-			}()
-			mockTyped("")
-			Eventually(answer).Should(Receive(BeEmpty()))
-		})
+	It("should accept any input when validator is nil", func() {
+		defer dumpBuffers()
+		answer := make(chan string)
+		go func() {
+			answer <- client.QueryString("name:", nil, "")
+		}()
+		mockTyped("")
+		Eventually(answer).Should(Receive(BeEmpty()))
+	})
 
-		It("should use predefined prop value if key is present", func() {
-			defer dumpBuffers()
-			answer := make(chan string)
-			go func() {
-				answer <- client.QueryString("name:", generator.Required, "propKey")
-			}()
-			Eventually(answer).Should(Receive(Equal("propVal")))
-		})
+	It("should use predefined prop value if key is present", func() {
+		defer dumpBuffers()
+		answer := make(chan string)
+		go func() {
+			answer <- client.QueryString("name:", generator.Required, "propKey")
+		}()
+		Eventually(answer).Should(Receive(Equal("propVal")))
+	})
 
-		It("Query", func() {
+	Describe("Query", func() {
+		It("should prompt until a valid answer is provided", func() {
 			defer dumpBuffers()
 			answer := make(chan []string)
 			query := "pick foo or bar:"
@@ -97,8 +98,10 @@ var _ = Describe("GeneratorCommon", func() {
 			Eventually(userIn).Should(gbytes.Say(query))
 			Eventually(answer).Should(Receive(ContainElement("foo")))
 		})
+	})
 
-		It("QueryAll", func() {
+	Describe("QueryAll", func() {
+		It("should prompt until a valid answer is provided", func() {
 			defer dumpBuffers()
 			answer := make(chan [][]string)
 			query := "pick foo or bar:"
@@ -114,8 +117,10 @@ var _ = Describe("GeneratorCommon", func() {
 			Expect(matches).To(ContainElement([]string{"foobar", "bar"}))
 			Expect(matches).To(ContainElement([]string{"foobaz", "baz"}))
 		})
+	})
 
-		It("QueryStringPattern", func() {
+	Describe("QueryStringPattern", func() {
+		It("should prompt until a valid answer is provided", func() {
 			defer dumpBuffers()
 			answer := make(chan string)
 			query := "type of bar:"
@@ -131,8 +136,10 @@ var _ = Describe("GeneratorCommon", func() {
 			Eventually(userIn).Should(gbytes.Say(query))
 			Eventually(answer).Should(Receive(Equal("foobar")))
 		})
+	})
 
-		It("QueryInt", func() {
+	Describe("QueryInt", func() {
+		It("should prompt until a valid answer is provided", func() {
 			defer dumpBuffers()
 			answer := make(chan int64)
 			query := "number:"
@@ -148,8 +155,10 @@ var _ = Describe("GeneratorCommon", func() {
 			Eventually(userIn).Should(gbytes.Say(query))
 			Eventually(answer).Should(Receive(Equal(int64(32))))
 		})
+	})
 
-		It("QueryBool", func() {
+	Describe("QueryBool", func() {
+		It("should prompt until a valid answer is provided", func() {
 			defer dumpBuffers()
 			answer := make(chan bool)
 			query := "cool?"

From 002b5159cb50ca5390016cac268951f6bee624d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= <nils@piksel.se>
Date: Sat, 3 Jul 2021 23:52:19 +0200
Subject: [PATCH 3/6] test(slack): add icon and invalid prop tests

---
 pkg/services/slack/slack_json.go | 27 +++++++++++++++++----------
 pkg/services/slack/slack_test.go | 27 +++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/pkg/services/slack/slack_json.go b/pkg/services/slack/slack_json.go
index 903e5801..4d152068 100644
--- a/pkg/services/slack/slack_json.go
+++ b/pkg/services/slack/slack_json.go
@@ -5,8 +5,8 @@ import (
 	"strings"
 )
 
-// messagePayload used within the Slack service
-type messagePayload struct {
+// MessagePayload used within the Slack service
+type MessagePayload struct {
 	Text        string       `json:"text"`
 	BotName     string       `json:"username,omitempty"`
 	Blocks      []block      `json:"blocks,omitempty"`
@@ -16,6 +16,19 @@ type messagePayload struct {
 	IconURL     string       `json:"icon_url,omitempty"`
 }
 
+func (p *MessagePayload) SetIcon(icon string) {
+	p.IconURL = ""
+	p.IconEmoji = ""
+
+	if icon != "" {
+		if iconUrlPattern.MatchString(icon) {
+			p.IconURL = icon
+		} else {
+			p.IconEmoji = icon
+		}
+	}
+}
+
 type block struct {
 	Type string    `json:"type"`
 	Text blockText `json:"text"`
@@ -64,19 +77,13 @@ func CreateJSONPayload(config *Config, message string) interface{} {
 		})
 	}
 
-	payload := messagePayload{
+	payload := MessagePayload{
 		Text:        config.Title,
 		BotName:     config.BotName,
 		Attachments: atts,
 	}
 
-	if config.Icon != "" {
-		if iconUrlPattern.MatchString(config.Icon) {
-			payload.IconURL = config.Icon
-		} else {
-			payload.IconEmoji = config.Icon
-		}
-	}
+	payload.SetIcon(config.Icon)
 
 	if config.Channel != "webhook" {
 		payload.Channel = config.Channel
diff --git a/pkg/services/slack/slack_test.go b/pkg/services/slack/slack_test.go
index c47190f4..aee8a158 100644
--- a/pkg/services/slack/slack_test.go
+++ b/pkg/services/slack/slack_test.go
@@ -91,6 +91,11 @@ var _ = Describe("the slack service", func() {
 				})
 			})
 		})
+		When("the URL contains an invalid property", func() {
+			testURL := urlMust("slack://hook:AAAAAAAAA-BBBBBBBBB-123456789123456789123456@webhook?bass=dirty")
+			err := (&Config{}).SetURL(testURL)
+			Expect(err).To(HaveOccurred())
+		})
 		It("should be identical after de-/serialization", func() {
 			testURL := "slack://hook:AAAAAAAAA-BBBBBBBBB-123456789123456789123456@webhook?botname=testbot&color=3f00fe&title=Test+title"
 
@@ -138,6 +143,28 @@ var _ = Describe("the slack service", func() {
 		})
 	})
 
+	Describe("creating the payload", func() {
+		Describe("the icon fields", func() {
+			payload := MessagePayload{}
+			It("should set IconURL when the configured icon looks like an URL", func() {
+				payload.SetIcon("https://example.com/logo.png")
+				Expect(payload.IconURL).To(Equal("https://example.com/logo.png"))
+				Expect(payload.IconEmoji).To(BeEmpty())
+			})
+			It("should set IconEmoji when the configured icon does not look like an URL", func() {
+				payload.SetIcon("tanabata_tree")
+				Expect(payload.IconEmoji).To(Equal("tanabata_tree"))
+				Expect(payload.IconURL).To(BeEmpty())
+			})
+			It("should clear both fields when icon is empty", func() {
+				payload.SetIcon("")
+				Expect(payload.IconEmoji).To(BeEmpty())
+				Expect(payload.IconURL).To(BeEmpty())
+			})
+		})
+
+	})
+
 	Describe("sending the payload", func() {
 		When("sending via webhook URL", func() {
 			var err error

From 362e4135793166640f0766f9f0fd91ce4f25b141 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= <nils@piksel.se>
Date: Sun, 18 Jul 2021 15:38:40 +0200
Subject: [PATCH 4/6] use generic JSON client header API

---
 pkg/services/slack/slack.go | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/pkg/services/slack/slack.go b/pkg/services/slack/slack.go
index 24358402..84b69460 100644
--- a/pkg/services/slack/slack.go
+++ b/pkg/services/slack/slack.go
@@ -62,10 +62,8 @@ func (service *Service) Initialize(configURL *url.URL, logger types.StdLogger) e
 
 func (service *Service) sendApi(config *Config, payload interface{}) error {
 	response := APIResponse{}
-	jsonClient := jsonclient.Client{
-		HTTPClient:          http.DefaultClient,
-		AuthorizationHeader: config.Token.Authorization(),
-	}
+	jsonClient := jsonclient.NewClient()
+	jsonClient.Headers().Set("Authorization", config.Token.Authorization())
 
 	if err := jsonClient.Post(apiPostMessage, payload, &response); err != nil {
 		return err

From 7e4c553601d2a14141bedf538ac6159a9a349c16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= <nils@piksel.se>
Date: Tue, 27 Jul 2021 11:19:57 +0200
Subject: [PATCH 5/6] lint fixes

---
 pkg/services/slack/slack.go       | 11 +++++------
 pkg/services/slack/slack_json.go  |  8 +++++---
 pkg/services/slack/slack_token.go |  8 ++++++++
 3 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/pkg/services/slack/slack.go b/pkg/services/slack/slack.go
index 84b69460..926cac3c 100644
--- a/pkg/services/slack/slack.go
+++ b/pkg/services/slack/slack.go
@@ -37,7 +37,7 @@ func (service *Service) Send(message string, params *types.Params) error {
 
 	var err error
 	if config.Token.IsAPIToken() {
-		err = service.sendApi(config, payload)
+		err = service.sendAPI(config, payload)
 	} else {
 		err = service.sendWebhook(config, payload)
 	}
@@ -54,13 +54,12 @@ func (service *Service) Initialize(configURL *url.URL, logger types.StdLogger) e
 	service.Logger.SetLogger(logger)
 	service.config = &Config{}
 	service.pkr = format.NewPropKeyResolver(service.config)
-	if err := service.config.setURL(&service.pkr, configURL); err != nil {
-		return err
-	}
-	return nil
+
+	return service.config.setURL(&service.pkr, configURL)
+
 }
 
-func (service *Service) sendApi(config *Config, payload interface{}) error {
+func (service *Service) sendAPI(config *Config, payload interface{}) error {
 	response := APIResponse{}
 	jsonClient := jsonclient.NewClient()
 	jsonClient.Headers().Set("Authorization", config.Token.Authorization())
diff --git a/pkg/services/slack/slack_json.go b/pkg/services/slack/slack_json.go
index e6c4a79b..103a4280 100644
--- a/pkg/services/slack/slack_json.go
+++ b/pkg/services/slack/slack_json.go
@@ -17,12 +17,15 @@ type MessagePayload struct {
 	IconURL     string       `json:"icon_url,omitempty"`
 }
 
+var iconURLPattern = regexp.MustCompile(`https?://`)
+
+// SetIcon sets the appropriate icon field in the payload based on whether the input is a URL or not
 func (p *MessagePayload) SetIcon(icon string) {
 	p.IconURL = ""
 	p.IconEmoji = ""
 
 	if icon != "" {
-		if iconUrlPattern.MatchString(icon) {
+		if iconURLPattern.MatchString(icon) {
 			p.IconURL = icon
 		} else {
 			p.IconEmoji = icon
@@ -56,6 +59,7 @@ type legacyField struct {
 	Short bool   `json:"short,omitempty"`
 }
 
+// APIResponse is the default generic response message sent from the API
 type APIResponse struct {
 	Ok       bool   `json:"ok"`
 	Error    string `json:"error"`
@@ -65,8 +69,6 @@ type APIResponse struct {
 	} `json:"response_metadata"`
 }
 
-var iconUrlPattern = regexp.MustCompile(`https?://`)
-
 // CreateJSONPayload compatible with the slack post message API
 func CreateJSONPayload(config *Config, message string) interface{} {
 
diff --git a/pkg/services/slack/slack_token.go b/pkg/services/slack/slack_token.go
index 643d58f1..2a506727 100644
--- a/pkg/services/slack/slack_token.go
+++ b/pkg/services/slack/slack_token.go
@@ -21,6 +21,8 @@ type Token struct {
 	raw string
 }
 
+// SetFromProp updates it's state according to the passed string
+// (implementation of the types.ConfigProp interface)
 func (token *Token) SetFromProp(propValue string) error {
 	if len(propValue) < 3 {
 		return ErrorInvalidToken
@@ -46,6 +48,8 @@ func (token *Token) SetFromProp(propValue string) error {
 	return nil
 }
 
+// GetPropValue returns a deserializable string representation of the token
+// (implementation of the types.ConfigProp interface)
 func (token *Token) GetPropValue() (string, error) {
 	if token == nil {
 		return "", nil
@@ -86,16 +90,19 @@ func (token *Token) String() string {
 	return token.raw
 }
 
+// UserInfo returns a url.Userinfo struct populated from the token
 func (token *Token) UserInfo() *url.Userinfo {
 	return url.UserPassword(token.raw[:4], token.raw[5:])
 }
 
+// IsAPIToken returns whether the identifier is set to anything else but the webhook identifier (`hook`)
 func (token *Token) IsAPIToken() bool {
 	return token.TypeIdentifier() != hookTokenIdentifier
 }
 
 const webhookBase = "https://hooks.slack.com/services/"
 
+// WebhookURL returns the corresponding Webhook URL for the Token
 func (token Token) WebhookURL() string {
 	sb := strings.Builder{}
 	sb.WriteString(webhookBase)
@@ -110,6 +117,7 @@ func (token Token) WebhookURL() string {
 	return sb.String()
 }
 
+// Authorization returns the corresponding `Authorization` HTTP header value for the Token
 func (token *Token) Authorization() string {
 	sb := strings.Builder{}
 	sb.WriteString("Bearer ")

From cc5bb51540c723f46191e9179422d63e4d8f6cda Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= <nils@piksel.se>
Date: Tue, 27 Jul 2021 14:41:00 +0200
Subject: [PATCH 6/6] update docs

---
 .../slack/app-api-channel-details-id.png      | Bin 0 -> 5935 bytes
 .../guides/slack/app-api-copy-oauth-token.png | Bin 0 -> 22308 bytes
 docs/guides/slack/app-api-oauth-menu.png      | Bin 0 -> 16321 bytes
 docs/guides/slack/app-api-select-channel.png  | Bin 0 -> 19207 bytes
 docs/guides/slack/index.md                    |  47 ++++++++++++++++++
 docs/index.md                                 |  16 +++---
 docs/services/slack.md                        |  31 ++++++------
 docs/stylesheets/extra.css                    |  30 +++++++++++
 mkdocs.yml                                    |   3 ++
 9 files changed, 105 insertions(+), 22 deletions(-)
 create mode 100644 docs/guides/slack/app-api-channel-details-id.png
 create mode 100644 docs/guides/slack/app-api-copy-oauth-token.png
 create mode 100644 docs/guides/slack/app-api-oauth-menu.png
 create mode 100644 docs/guides/slack/app-api-select-channel.png
 create mode 100644 docs/guides/slack/index.md
 create mode 100644 docs/stylesheets/extra.css

diff --git a/docs/guides/slack/app-api-channel-details-id.png b/docs/guides/slack/app-api-channel-details-id.png
new file mode 100644
index 0000000000000000000000000000000000000000..075b879f797f72bc29fbcb23b96e3a1405264c75
GIT binary patch
literal 5935
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
JSKhS=|34k5W*Pth

literal 0
HcmV?d00001

diff --git a/docs/guides/slack/app-api-copy-oauth-token.png b/docs/guides/slack/app-api-copy-oauth-token.png
new file mode 100644
index 0000000000000000000000000000000000000000..135d01104f2ff30fd88c7f11a850a92d12871f25
GIT binary patch
literal 22308
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
YfJD*tydKAo?q>nq)-}?p(0&yDKh!W)3IG5A

literal 0
HcmV?d00001

diff --git a/docs/guides/slack/app-api-oauth-menu.png b/docs/guides/slack/app-api-oauth-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..9df1f9bfa69ef0283e8b0cc212bd306f8f5a88b1
GIT binary patch
literal 16321
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

literal 0
HcmV?d00001

diff --git a/docs/guides/slack/app-api-select-channel.png b/docs/guides/slack/app-api-select-channel.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2f3c4257f7007ba09a439e2869da6c613018b6e
GIT binary patch
literal 19207
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
B+tdI6

literal 0
HcmV?d00001

diff --git a/docs/guides/slack/index.md b/docs/guides/slack/index.md
new file mode 100644
index 00000000..bba50231
--- /dev/null
+++ b/docs/guides/slack/index.md
@@ -0,0 +1,47 @@
+# Slack Guides
+
+Guides for setting up the [Slack](../../services/slack.md) service
+
+## Getting a token
+
+To enable all features, either the Legacy Webhook- (deprecated and might stop working) or the bot API tokens needs to
+be used. Only use the non-legacy Webhook if you don't need to customize the bot name or icon.
+
+### Bot API (preferred)
+
+1. Create a new App for your bot using the [Basic app setup guide](https://api.slack.com/authentication/basics)
+2. Install the App into your workspace ([slack docs](https://api.slack.com/authentication/basics#installing)).
+3. From [Apps](https://api.slack.com/apps), select your new App and go to **Oauth & Permissions**
+   <figure><img alt="Slack app management menu screenshot" src="app-api-oauth-menu.png" height="248" /></figure>
+4. Copy the Bot User OAuth Token
+   <figure><img alt="Copy OAuth token screenshot" src="app-api-copy-oauth-token.png" height="209" /></figure>
+   
+!!! example
+    Given the API token
+    <pre><code><b>xoxb</b>-<b>123456789012</b>-<b>1234567890123</b>-<b>4mt0t4l1YL3g1T5L4cK70k3N</b></code></pre>
+    and the channel ID `C001CH4NN3L` (obtained by using the [guide below](#getting_the_channel_id)), the Shoutrrr URL
+    should look like this:
+    <pre><code>slack://<b>xoxb</b>:<b>123456789012</b>-<b>1234567890123</b>-<b>4mt0t4l1YL3g1T5L4cK70k3N</b>@<b>C001CH4NN3L</b></code></pre>
+
+### Webhook tokens
+
+Get a Webhook URL using the legacy [WebHooks Integration](https://slack.com/apps/new/A0F7XDUAZ-incoming-webhooks), 
+or by using the [Getting started with Incoming Webhooks](https://api.slack.com/messaging/webhooks#getting_started) guide and
+replace the initial `https://hooks.slack.com/services/` part of the webhook URL with `slack://hook:` to get your Shoutrrr URL.
+
+!!! info "Slack Webhook URL"
+    <code>https://hooks.slack.com/services/<b>T00000000</b>/<b>B00000000</b>/<b>XXXXXXXXXXXXXXXXXXXXXXXX</b></code>
+
+!!! info "Shoutrrr URL"
+    <code>slack://hook:<b>T00000000</b>-<b>B00000000</b>-<b>XXXXXXXXXXXXXXXXXXXXXXXX</b>@webhook</code>
+
+## Getting the Channel ID
+
+!!! note ""
+    Only needed for API token. Use `webhook` as the channel for webhook tokens.
+
+1. In the channel you wish to post to, open **Channel Details** by clicking on the channel title.
+   <figure><img alt="Opening channel details screenshot" src="app-api-select-channel.png" height="270" /></figure>
+
+2. Copy the Channel ID from the bottom of the popup and append it to your Shoutrrr URL
+   <figure><img alt="Copy channel ID screenshot" src="app-api-channel-details-id.png" height="99" /></figure>
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
index 7c5741a6..2a472e53 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -9,27 +9,27 @@ Notification library for gophers and their furry friends.<br />
 Heavily inspired by <a href="https://github.com/caronc/apprise">caronc/apprise</a>.
 </p>
 
-<p align="center">
+<p align="center" class="badges">
     <a target="_blank" rel="noopener noreferrer" href="https://github.com/containrrr/shoutrrr/workflows/Main%20Workflow/badge.svg">
-        <img src="https://github.com/containrrr/shoutrrr/workflows/Main%20Workflow/badge.svg" alt="github actions workflow status" style="max-width:100%;">
+        <img src="https://github.com/containrrr/shoutrrr/workflows/Main%20Workflow/badge.svg" alt="github actions workflow status">
     </a>
     <a href="https://codecov.io/gh/containrrr/shoutrrr" rel="nofollow">
-        <img alt="codecov" src="https://codecov.io/gh/containrrr/shoutrrr/branch/master/graph/badge.svg" style="max-width:100%;">
+        <img alt="codecov" src="https://codecov.io/gh/containrrr/shoutrrr/branch/master/graph/badge.svg">
     </a>
     <a href="https://www.codacy.com/gh/containrrr/shoutrrr/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=containrrr/shoutrrr&amp;utm_campaign=Badge_Grade" rel="nofollow">
-        <img alt="Codacy Badge" src="https://app.codacy.com/project/badge/Grade/47eed72de79448e2a6e297d770355544" style="max-width:100%;">
+        <img alt="Codacy Badge" src="https://app.codacy.com/project/badge/Grade/47eed72de79448e2a6e297d770355544">
     </a>
     <a href="https://goreportcard.com/badge/github.com/containrrr/shoutrrr" rel="nofollow">
-        <img alt="report card" src="https://goreportcard.com/badge/github.com/containrrr/shoutrrr" style="max-width:100%;">
+        <img alt="report card" src="https://goreportcard.com/badge/github.com/containrrr/shoutrrr">
     </a>
     <a href="https://pkg.go.dev/github.com/containrrr/shoutrrr" rel="nofollow">
-        <img alt="go.dev reference" src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&amp;logoColor=white&amp;style=flat-square" style="max-width:100%;">
+        <img alt="go.dev reference" src="https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&amp;logoColor=white&amp;style=flat-square">
     </a>
     <a href="https://github.com/containrrr/shoutrrr">
-        <img alt="github code size in bytes" src="https://img.shields.io/github/languages/code-size/containrrr/shoutrrr.svg?style=flat-square" style="max-width:100%;">
+        <img alt="github code size in bytes" src="https://img.shields.io/github/languages/code-size/containrrr/shoutrrr.svg?style=flat-square">
     </a>
     <a href="https://github.com/containrrr/shoutrrr/blob/master/LICENSE">
-        <img alt="license" src="https://img.shields.io/github/license/containrrr/shoutrrr.svg?style=flat-square" style="max-width:100%;">
+        <img alt="license" src="https://img.shields.io/github/license/containrrr/shoutrrr.svg?style=flat-square">
     </a>
 </p>
 
diff --git a/docs/services/slack.md b/docs/services/slack.md
index c011f34c..9cb7e230 100644
--- a/docs/services/slack.md
+++ b/docs/services/slack.md
@@ -1,22 +1,21 @@
 # Slack
 
-The slack notification service uses [Slack Webhook](https://api.slack.com/messaging/webhooks)s to send messages.  
-Follow the [Getting started with Incoming Webhooks](https://api.slack.com/messaging/webhooks#getting_started) guide and
-replace the initial `https://hooks.slack.com/services/` part of the webhook URL with `slack://` to get your Shoutrrr URL.
+!!! attention "New URL format"
+    The URL format for Slack has been changed to allow for API- as well as webhook tokens.  
+    Using the old format (`slack://xxxx/yyyy/zzzz`) will still work as before and will automatically be upgraded to
+    the new format when used.
 
-*Slack Webhook URL:*
+The Slack notification service uses either [Slack Webhooks](https://api.slack.com/messaging/webhooks) or the 
+[Bot API](https://api.slack.com/methods/chat.postMessage) to send messages.  
 
-!!! info ""
-    https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
+See the [guides](../guides/slack/index.md) for information on how to get your *token* and *channel*.
 
-Shoutrrr URL:
 
-!!! info ""
-    slack://T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
+## URL Format
 
+!!! note ""
+    Note that the token uses a prefix to determine the type, usually either `hook` (for webhooks) or `xoxb` (for bot API).
 
-## URL Format
-    
 --8<-- "docs/services/slack/config.md"
 
 !!! info "Color format"
@@ -26,8 +25,12 @@ Shoutrrr URL:
 
 ## Examples
 
-!!! example
-    All fields set:
+!!! example "Bot API"
+    ```uri
+    slack://xoxb:123456789012-1234567890123-4mt0t4l1YL3g1T5L4cK70k3N@C001CH4NN3L?color=good&title=Great+News&icon=man-scientist&botname=Shoutrrrbot
+    ```
+    
+!!! example "Webhook"
     ```uri
-    slack://ShoutrrrBot@T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX?color=good&title=Great+News
+    slack://hook:WNA3PBYV6-F20DUQND3RQ-Webc4MAvoacrpPakR8phF0zi@webhook?color=good&title=Great+News&icon=man-scientist&botname=Shoutrrrbot
     ```
\ No newline at end of file
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
new file mode 100644
index 00000000..83c0653f
--- /dev/null
+++ b/docs/stylesheets/extra.css
@@ -0,0 +1,30 @@
+
+.md-typeset li img {
+    display: inline-block;
+}
+
+.md-typeset figure {
+    background: var(--md-code-bg-color);
+    display: block;
+    width: 100%;
+}
+
+.md-typeset figure img {
+    box-shadow: 2px 2px 4px #00000080;
+    padding: 3px;
+    background: var(--md-code-bg-color);
+}
+
+
+.md-typeset li img:last-child {
+    margin: 10px 0;
+}
+
+.badges img {
+    height: 20px;
+    max-width: 100%;
+    display: inline-block;
+    padding: 0;
+    background: transparent;
+    border-radius: 3px;
+}
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
index 78e6021b..c81d9f19 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -15,6 +15,7 @@ extra:
   generator: false
 extra_css:
   - stylesheets/theme.css
+  - stylesheets/extra.css
 markdown_extensions:
   - toc:
       permalink: True
@@ -49,6 +50,8 @@ nav:
       - Telegram: 'services/telegram.md'
       - Zulip Chat: 'services/zulip.md'
       - Generic Webhook: 'services/generic.md'
+  - Guides:
+      - Slack: 'guides/slack/index.md'
   - Advanced usage:
       - Proxy: 'proxy.md'