/
conn.go
155 lines (130 loc) · 4.09 KB
/
conn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package rtm
import (
"regexp"
"strings"
"github.com/benbariteau/slack"
"github.com/gorilla/websocket"
)
type Conn struct {
conn *websocket.Conn
Token string
messageCounter int
cancel chan struct{}
}
func (c *Conn) Close() error {
close(c.cancel)
return c.conn.Close()
}
type sendMessage struct {
ID int `json:"id"`
Type string `json:"type"`
Channel string `json:"channel"`
Text string `json:"text"`
}
func (c *Conn) SendMessage(text, channel string) error {
c.messageCounter++
msg := sendMessage{
ID: c.messageCounter,
Type: "message",
Channel: channel,
Text: text,
}
return c.conn.WriteJSON(msg)
}
/*
NextEvent blocks until the next Event is sent and then returns it to the caller.
*/
func (c *Conn) NextEvent() Event {
rawEvent := make(map[string]interface{})
c.conn.ReadJSON(&rawEvent)
return toEvent(rawEvent)
}
var escapeRegex = regexp.MustCompile("<(.*?)>")
var escapeTypePostprocessors = map[EscapeType]func(string) string{
UserEscape: func(s string) string { return "@" + s },
ChannelEscape: func(s string) string { return "#" + s },
}
/*
UnescapeMessage takes in the escape string text of a message and returns a new string that appears as it would to a user.
UnescapeMessage does so by parsing escape sequences according to <https://api.slack.com/docs/formatting> and substituting the appropriate user-facing junk (e.g. <@UABC123> would become @firba1, assuming there's a user named firba1 with the user ID UABC123).
*/
func (c Conn) UnescapeMessage(message string) string {
return c.UnescapeMessagePostprocess(message, func(s string, i EscapeType) string { return s })
}
func (c Conn) UnescapeMessagePostprocess(
message string,
postprocessor func(userString string, escapeType EscapeType) string,
) string {
message = escapeRegex.ReplaceAllStringFunc(message, func(match string) string {
unescapedMatch, escapeType := replaceEscapeHelper(c, match)
postprocess := escapeTypePostprocessors[escapeType]
if postprocess != nil {
unescapedMatch = postprocess(unescapedMatch)
}
return postprocessor(unescapedMatch, escapeType)
})
// finally replace all html entity escapes
message = strings.Replace(message, "&", "&", -1)
message = strings.Replace(message, "<", "<", -1)
message = strings.Replace(message, ">", ">", -1)
return message
}
func replaceEscapeHelper(c Conn, match string) (unescape string, escapeType EscapeType) {
// remove < and > from each end
fullEscape := match[1 : len(match)-1]
// check for display string
escapeParts := strings.Split(fullEscape, "|")
// this is a case we don't recognize, just return the original match and treat it as a link (the default)
if len(escapeParts) > 2 || len(escapeParts) <= 0 {
unescape = match
escapeType = LinkEscape
return
}
escape := escapeParts[0]
escapeType = parseEscapeType(escape)
// if we have an alias, just return that
if len(escapeParts) == 2 {
unescape = escapeParts[1]
return
}
// since there's no alias, now it's time for idenitifier lookup
escapeType = parseEscapeType(escape)
switch escapeType {
case UserEscape:
// user link
user, err := slack.NewAPI(c.Token).UsersInfo(escape[1:])
if err != nil {
// if user is zero value, this will just be empty string, which we handle later
unescape = user.Profile.DisplayName
}
}
// if we couldn't unescape properly, just return the original match text, make it a LinkEscape type to prevent post processing
if unescape == "" {
escapeType = LinkEscape
unescape = match
}
return
}
type EscapeType int
const (
LinkEscape EscapeType = iota
UserEscape
ChannelEscape
CommandEscape
)
/*
parseEscapeType is a convience function for getting an easily comparable type from an escape sequence (e.g. "@U123A56BC" for users "#C789D10EF" for channels, etc)
*/
func parseEscapeType(escapeString string) EscapeType {
switch {
case escapeString[0:2] == "@U":
return UserEscape
case escapeString[0:2] == "#C":
return ChannelEscape
case escapeString[0] == '!':
return CommandEscape
default:
// as per the docs, anything we can't recognize like this is a link
return LinkEscape
}
}