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

Render plain text for messages sent in attachments and remove the header #773

Merged
merged 21 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
50bfe80
Slack bot now uploads a long msg as a plaintext file with a related d…
ezodude Sep 27, 2022
bad1598
Added interactive message to plaintext tests.
ezodude Sep 27, 2022
0063ebb
Long SocketSlack interactive messages now sent as message and file up…
ezodude Sep 27, 2022
0fbca32
Clean up.
ezodude Sep 27, 2022
d24cb59
Discore and MatterMost long messages now uploaded with a message and …
ezodude Sep 27, 2022
861ae64
MatterMost message response field no longer used.
ezodude Sep 27, 2022
bf561f3
MS Teams long messages now uploaded as a plaintext file.
ezodude Sep 27, 2022
f88fc01
Added E2E test to verify large output is received as plaintext file w…
ezodude Sep 28, 2022
8907fef
Fixed unit tests.
ezodude Sep 28, 2022
2fcbd98
For MS Teams we may need to force markdown regardless of outgoing mes…
ezodude Sep 28, 2022
ffe6e18
Merge branch 'main' into render-plain
huseyinbabal Sep 28, 2022
a6eda16
Merge branch 'main' into render-plain
ezodude Sep 29, 2022
fa751ad
Remove description messages and attach cmd description to actual uplo…
ezodude Sep 29, 2022
ef7d01c
Factor out max message size to consts.
ezodude Sep 29, 2022
819f545
Merge branch 'main' into render-plain
ezodude Sep 29, 2022
3c0cfb7
Fix incorrect spacing in helm services template.
ezodude Sep 30, 2022
acddc90
Generalised Markdown formatting to be used for plaintext formatting.
ezodude Sep 30, 2022
ee3c269
Linter fixes.
ezodude Sep 30, 2022
7c9419d
Merge branch 'main' into render-plain
ezodude Sep 30, 2022
5b16bbc
Update test golden text to match the latest from main.
ezodude Sep 30, 2022
f7691d3
Remove dead code.
ezodude Sep 30, 2022
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
25 changes: 16 additions & 9 deletions pkg/bot/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,32 +247,39 @@ func (b *Discord) handleMessage(dm discordMessage) error {
User: fmt.Sprintf("<@%s>", dm.Event.Author.ID),
})

out := e.Execute()
resp := interactive.MessageToMarkdown(b.mdFormatter, out)
err := b.send(dm.Event, req, resp)
response := e.Execute()
//resp := interactive.MessageToMarkdown(b.mdFormatter, response)
//err := b.send(dm.Event, req, resp)
ezodude marked this conversation as resolved.
Show resolved Hide resolved
err := b.send(dm.Event, req, response)
if err != nil {
return fmt.Errorf("while sending message: %w", err)
}

return nil
}

func (b *Discord) send(event *discordgo.MessageCreate, req, resp string) error {
func (b *Discord) send(event *discordgo.MessageCreate, req string, resp interactive.Message) error {
b.log.Debugf("Discord incoming Request: %s", req)
b.log.Debugf("Discord Response: %s", resp)

if len(resp) == 0 {
markdown := interactive.MessageToMarkdown(b.mdFormatter, resp)

if len(markdown) == 0 {
return fmt.Errorf("while reading Slack response: empty response for request %q", req)
}

// Upload message as a file if too long
if len(resp) >= 2000 {
if len(markdown) >= 2000 {
if _, err := b.api.ChannelMessageSend(event.ChannelID, resp.Description); err != nil {
mszostok marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("while sending attachment message: %w", err)
}

params := &discordgo.MessageSend{
Content: req,
Files: []*discordgo.File{
{
Name: "Response",
Reader: strings.NewReader(resp),
Name: "Response.txt",
Reader: strings.NewReader(interactive.MessageToPlaintext(resp, interactive.NewlineFormatter)),
},
},
}
Expand All @@ -282,7 +289,7 @@ func (b *Discord) send(event *discordgo.MessageCreate, req, resp string) error {
return nil
}

if _, err := b.api.ChannelMessageSend(event.ChannelID, resp); err != nil {
if _, err := b.api.ChannelMessageSend(event.ChannelID, markdown); err != nil {
return fmt.Errorf("while sending message: %w", err)
}
return nil
Expand Down
15 changes: 15 additions & 0 deletions pkg/bot/interactive/formatters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package interactive

import (
"fmt"
)

// NewlineFormatter adds new line formatting.
func NewlineFormatter(msg string) string {
return fmt.Sprintf("%s\n", msg)
}

// MdHeaderFormatter adds Markdown header formatting.
func MdHeaderFormatter(msg string) string {
return fmt.Sprintf("**%s**", msg)
}
24 changes: 7 additions & 17 deletions pkg/bot/interactive/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,30 @@ import (
formatx "github.com/kubeshop/botkube/pkg/format"
)

// DefaultMDLineFormatter represents a Markdown new line formatting.
func DefaultMDLineFormatter(msg string) string {
return fmt.Sprintf("%s\n", msg)
}

// DefaultMDHeaderFormatter represents a Markdown header formatting.
func DefaultMDHeaderFormatter(msg string) string {
return fmt.Sprintf("**%s**", msg)
}

// MDFormatter represents the capability of Markdown Formatter
type MDFormatter struct {
lineFormatter func(msg string) string
headerFormatter func(msg string) string
newlineFormatter func(msg string) string
headerFormatter func(msg string) string
}

// NewMDFormatter is for initializing custom Markdown formatter
func NewMDFormatter(lineFormatter, headerFormatter func(msg string) string) MDFormatter {
func NewMDFormatter(newlineFormatter, headerFormatter func(msg string) string) MDFormatter {
return MDFormatter{
lineFormatter: lineFormatter,
headerFormatter: headerFormatter,
newlineFormatter: newlineFormatter,
headerFormatter: headerFormatter,
}
}

// DefaultMDFormatter is for initializing built-in Markdown formatter
func DefaultMDFormatter() MDFormatter {
return NewMDFormatter(DefaultMDLineFormatter, DefaultMDHeaderFormatter)
return NewMDFormatter(NewlineFormatter, MdHeaderFormatter)
}

// MessageToMarkdown returns interactive message as a plaintext with Markdown syntax.
func MessageToMarkdown(mdFormatter MDFormatter, msg Message) string {
var out strings.Builder
addLine := func(in string) {
out.WriteString(mdFormatter.lineFormatter(in))
out.WriteString(mdFormatter.newlineFormatter(in))
}

if msg.Header != "" {
Expand Down
6 changes: 3 additions & 3 deletions pkg/bot/interactive/markdown_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ func TestInteractiveMessageToMarkdownMultiSelect(t *testing.T) {
// go test -run=TestInteractiveMessageToMarkdown ./pkg/bot/interactive/... -test.update-golden
func TestInteractiveMessageToMarkdown(t *testing.T) {
formatterForCustomNewLines := MDFormatter{
lineFormatter: func(msg string) string {
newlineFormatter: func(msg string) string {
return fmt.Sprintf("%s<br>", msg)
},
headerFormatter: DefaultMDHeaderFormatter,
headerFormatter: MdHeaderFormatter,
}

formatterForCustomHeaders := MDFormatter{
lineFormatter: DefaultMDLineFormatter,
newlineFormatter: NewlineFormatter,
headerFormatter: func(msg string) string {
return fmt.Sprintf("*%s*", msg)
},
Expand Down
80 changes: 80 additions & 0 deletions pkg/bot/interactive/plaintext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package interactive

import (
"fmt"
"strings"
)

// MessageToPlaintext returns interactive message as a plaintext.
func MessageToPlaintext(msg Message, newlineFormatter func(in string) string) string {
mszostok marked this conversation as resolved.
Show resolved Hide resolved
var out strings.Builder
addLine := func(in string) {
out.WriteString(newlineFormatter(in))
}

if msg.Header != "" {
addLine(msg.Header)
}

if msg.Body.Plaintext != "" {
addLine(msg.Body.Plaintext)
}

if msg.Body.CodeBlock != "" {
addLine(msg.Body.CodeBlock)
}

for _, section := range msg.Sections {
addLine("") // padding between sections

if section.Header != "" {
addLine(section.Header)
}

if section.Description != "" {
addLine(section.Description)
}

if section.Body.Plaintext != "" {
addLine(section.Body.Plaintext)
}

if section.Body.CodeBlock != "" {
// not using the adaptive code block is on purpose, we always want to have
// a multiline code block to improve readability
addLine(section.Body.CodeBlock)
}

if section.MultiSelect.AreOptionsDefined() {
ms := section.MultiSelect

if ms.Description.Plaintext != "" {
addLine(ms.Description.Plaintext)
}

if ms.Description.CodeBlock != "" {
addLine(ms.Description.CodeBlock)
}

addLine("") // new line
addLine("Available options:")

for _, opt := range ms.Options {
addLine(fmt.Sprintf(" - %s", opt.Value))
}
}

for _, btn := range section.Buttons {
if btn.URL != "" {
addLine(fmt.Sprintf("%s: %s", btn.Name, btn.URL))
continue
}
if btn.Command != "" {
addLine(fmt.Sprintf(" - %s", btn.Command))
continue
}
}
}

return out.String()
}
64 changes: 64 additions & 0 deletions pkg/bot/interactive/plaintext_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package interactive

import (
"fmt"
"testing"

"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)

// go test -run=TestInteractiveMessageToMarkdownMultiSelect ./pkg/bot/interactive/... -test.update-golden
func TestInteractiveMessageToPlaintextMultiSelect(t *testing.T) {
// given
message := Message{
Base: Base{
Header: "Adjust notifications",
Description: "Adjust notifications description",
},

Sections: []Section{
{
MultiSelect: MultiSelect{
Name: "Adjust notifications",
Description: Body{
Plaintext: "Select notification sources",
},
Command: "@BotKube edit SourceBindings",
Options: []OptionItem{
{
Name: "K8s all events",
Value: "k8s-all-events",
},
{
Name: "K8s recommendations",
Value: "k8s-recommendations",
},
},
},
},
},
}

// when
out := MessageToPlaintext(message, NewlineFormatter)

// then
golden.Assert(t, out, fmt.Sprintf("%s.golden.txt", t.Name()))
}

// go test -run=TestInteractiveMessageToMarkdown ./pkg/bot/interactive/... -test.update-golden
func TestInteractiveMessageToPlaintext(t *testing.T) {
customNewlineFormatter := func(msg string) string {
return fmt.Sprintf("%s\r\n", msg)
}

// given
help := Help("platform", "testing", "@BotKube")

// when
out := MessageToPlaintext(help, customNewlineFormatter)

// then
assert.Assert(t, golden.String(out, fmt.Sprintf("%s.golden.txt", t.Name())))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

Using multiple instances
If you are running multiple BotKube instances in the same channel to interact with testing, make sure to specify the cluster name when typing commands.
--cluster-name="testing"


Manage incoming notifications
@BotKube notifier [start|stop|status]

- @BotKube notifier start
- @BotKube notifier stop
- @BotKube notifier status

Notification types
By default, BotKube will notify only about cluster errors and recommendations.
- @BotKube edit SourceBindings

Ping your cluster
Check the status of connected Kubernetes cluster(s).
- @BotKube ping

Run kubectl commands (if enabled)
You can run kubectl commands directly from Platform!
- @BotKube get services
- @BotKube get pods
- @BotKube get deployments

To list all supported kubectl commands
- @BotKube commands list

Filters (advanced)
You can extend BotKube functionality by writing additional filters that can check resource specs, validate some checks and add messages to the Event struct. Learn more at https://botkube.io/filters

Angry? Amazed?
Give feedback: https://feedback.botkube.io

Read our docs: https://botkube.io/docs
Join our Slack: https://join.botkube.io
Follow us on Twitter: https://twitter.com/botkube_io
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Adjust notifications

Select notification sources

Available options:
- k8s-all-events
- k8s-recommendations
Loading