Skip to content

Commit 7085fa4

Browse files
committed
Support Slack links in HTML/Markdown text
1 parent 701e294 commit 7085fa4

File tree

1 file changed

+87
-13
lines changed

1 file changed

+87
-13
lines changed

webhook.go

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package webhook
22

33
import (
4+
"bytes"
45
"errors"
6+
"regexp"
57

68
"github.com/requilence/integram"
79
)
810

911
var m = integram.HTMLRichText{}
12+
var markdownRichText = integram.MarkdownRichText{}
1013

1114
type Config struct{
1215
integram.BotConfig
@@ -17,16 +20,17 @@ type webhook struct {
1720
Mrkdwn bool
1821
Channel string
1922
Attachments []struct {
20-
Pretext string `json:"pretext"`
21-
Fallback string `json:"fallback"`
22-
AuthorName string `json:"author_name"`
23-
AuthorLink string `json:"author_link"`
24-
Title string `json:"title"`
25-
TitleLink string `json:"title_link"`
26-
Text string `json:"text"`
27-
ImageURL string `json:"image_url"`
28-
ThumbURL string `json:"thumb_url"`
29-
Ts int `json:"ts"`
23+
Pretext string `json:"pretext"`
24+
Fallback string `json:"fallback"`
25+
AuthorName string `json:"author_name"`
26+
AuthorLink string `json:"author_link"`
27+
Title string `json:"title"`
28+
TitleLink string `json:"title_link"`
29+
Text string `json:"text"`
30+
MrkdwnIn []string `json:"mrkdwn_in"`
31+
ImageURL string `json:"image_url"`
32+
ThumbURL string `json:"thumb_url"`
33+
Ts int `json:"ts"`
3034
} `json:"attachments"`
3135
}
3236

@@ -60,6 +64,62 @@ func update(c *integram.Context) error {
6064
return nil
6165
}
6266

67+
func convertLinks(text string, regex *regexp.Regexp, encodeEntities func(string) string, linkFormat func(string, string) string) string {
68+
if encodeEntities == nil {
69+
encodeEntities = func(text string) string {
70+
return text
71+
}
72+
}
73+
submatches := regex.FindAllStringSubmatchIndex(text, -1)
74+
if submatches == nil {
75+
return encodeEntities(text)
76+
}
77+
78+
convertedBuffer := new(bytes.Buffer)
79+
convertedBuffer.Grow(len(text))
80+
currentPosition := 0
81+
for _, submatch := range submatches {
82+
if submatch[0] > 0 {
83+
convertedBuffer.WriteString(encodeEntities(text[currentPosition:submatch[0]]))
84+
}
85+
if submatch[2] < 0 {
86+
// Code block, copy as-is
87+
convertedBuffer.WriteString(encodeEntities(text[submatch[0]:submatch[1]]))
88+
} else {
89+
// URL link, convert
90+
url := text[submatch[2]:submatch[3]]
91+
displayText := url
92+
if submatch[4] > 0 && submatch[4] != submatch[5] {
93+
displayText = text[submatch[4] + 1:submatch[5]]
94+
}
95+
convertedBuffer.WriteString(linkFormat(displayText, url))
96+
}
97+
currentPosition = submatch[1]
98+
}
99+
if currentPosition < len(text) {
100+
convertedBuffer.WriteString(encodeEntities(text[currentPosition:]))
101+
}
102+
return convertedBuffer.String()
103+
}
104+
105+
func convertLinksToMarkdown(text string) string {
106+
// Escape URL links if outside code blocks.
107+
// Message format is documented at https://api.slack.com/docs/message-formatting)
108+
// References to a Slack channel (@), user (#) or variable (!) are kept as-is
109+
linkOrCodeBlockRegexp := regexp.MustCompile("```.+```|`[^`\n]+`|<([^@#! \n][^|> \n]*)(|[^>\n]+)?>")
110+
return convertLinks(text, linkOrCodeBlockRegexp, nil, markdownRichText.URL);
111+
}
112+
113+
func convertLinksToHtml(text string) string {
114+
linkOrCodeBlockRegexp := regexp.MustCompile("<code>.*</code>|<pre>.*</pre>|<([^@#! \n][^|> \n]*)(|[^>\n]+)?>")
115+
return convertLinks(text, linkOrCodeBlockRegexp, nil, m.URL);
116+
}
117+
118+
func convertPlainWithLinksToHTML(text string) string {
119+
linkRegexp := regexp.MustCompile("<([^@#! \n][^|> \n]*)(|[^>\n]+)?>")
120+
return convertLinks(text, linkRegexp, m.EncodeEntities, m.URL);
121+
}
122+
63123
func webhookHandler(c *integram.Context, wc *integram.WebhookContext) (err error) {
64124

65125
wh := webhook{Mrkdwn: true}
@@ -81,6 +141,7 @@ func webhookHandler(c *integram.Context, wc *integram.WebhookContext) (err error
81141
}
82142

83143
haveAttachmentWithText:=false
144+
haveMrkdwnAttachment:=false
84145
for i, attachment := range wh.Attachments {
85146
if i > 0 {
86147
text += "\n"
@@ -99,19 +160,32 @@ func webhookHandler(c *integram.Context, wc *integram.WebhookContext) (err error
99160
}
100161

101162
haveAttachmentWithText = true
163+
for _, field := range attachment.MrkdwnIn {
164+
if field == "pretext" {
165+
haveMrkdwnAttachment = true
166+
}
167+
}
102168

103169
text += attachment.Pretext
104170
}
105171

106172
if haveAttachmentWithText {
107-
return c.NewMessage().SetText(text).EnableAntiFlood().EnableHTML().Send()
173+
m := c.NewMessage().EnableAntiFlood()
174+
if haveMrkdwnAttachment {
175+
m.SetText(convertLinksToMarkdown(text)).EnableMarkdown()
176+
} else {
177+
m.SetText(convertLinksToHtml(text)).EnableHTML()
178+
}
179+
return m.Send()
108180
}
109181
}
110182

111183
if wh.Text != "" {
112-
m := c.NewMessage().SetText(wh.Text + " " + wh.Channel).EnableAntiFlood()
184+
m := c.NewMessage().EnableAntiFlood()
113185
if wh.Mrkdwn {
114-
m.EnableMarkdown()
186+
m.SetText(convertLinksToMarkdown(wh.Text + " " + wh.Channel)).EnableMarkdown()
187+
} else {
188+
m.SetText(convertPlainWithLinksToHTML(wh.Text + " " + wh.Channel)).EnableHTML()
115189
}
116190
return m.Send()
117191
}

0 commit comments

Comments
 (0)