Merge pull request #84 from velour/slack_parsing

slack: import velour/chat parsing
This commit is contained in:
Chris Sexton 2017-10-25 12:15:25 -04:00 committed by GitHub
commit c399db225a
2 changed files with 105 additions and 9 deletions

96
slack/fix_text.go Normal file
View File

@ -0,0 +1,96 @@
package slack
import (
"unicode/utf8"
)
// fixText strips all of the Slack-specific annotations from message text,
// replacing it with the equivalent display form.
// Currently it:
// • Replaces user mentions like <@U124356> with @ followed by the user's nick.
// This uses the lookupUser function, which must map U1243456 to the nick.
// • Replaces user mentions like <U123456|nick> with the user's nick.
// • Strips < and > surrounding links.
//
// This was directly bogarted from velour/chat with emoji conversion removed.
func fixText(findUser func(id string) (string, bool), text string) string {
var output []rune
for len(text) > 0 {
r, i := utf8.DecodeRuneInString(text)
text = text[i:]
switch {
case r == '<':
var tag []rune
for {
r, i := utf8.DecodeRuneInString(text)
text = text[i:]
switch {
case r == '>':
if t, ok := fixTag(findUser, tag); ok {
output = append(output, t...)
break
}
fallthrough
case len(text) == 0:
output = append(output, '<')
output = append(output, tag...)
output = append(output, r)
default:
tag = append(tag, r)
continue
}
break
}
default:
output = append(output, r)
}
}
return string(output)
}
func fixTag(findUser func(string) (string, bool), tag []rune) ([]rune, bool) {
switch {
case hasPrefix(tag, "@U"):
if i := indexRune(tag, '|'); i >= 0 {
return tag[i+1:], true
}
if findUser != nil {
if u, ok := findUser(string(tag[1:])); ok {
return []rune(u), true
}
}
return tag, true
case hasPrefix(tag, "#C"):
if i := indexRune(tag, '|'); i >= 0 {
return append([]rune{'#'}, tag[i+1:]...), true
}
case hasPrefix(tag, "http"):
if i := indexRune(tag, '|'); i >= 0 {
tag = tag[:i]
}
return tag, true
}
return nil, false
}
func hasPrefix(text []rune, prefix string) bool {
for _, r := range prefix {
if len(text) == 0 || text[0] != r {
return false
}
text = text[1:]
}
return true
}
func indexRune(text []rune, find rune) int {
for i, r := range text {
if r == find {
return i
}
}
return -1
}

View File

@ -306,14 +306,13 @@ var urlDetector = regexp.MustCompile(`<(.+)://([^|^>]+).*>`)
func (s *Slack) buildMessage(m slackMessage) msg.Message {
text := html.UnescapeString(m.Text)
// remove <> from URLs, URLs may also be <url|description>
text = urlDetector.ReplaceAllString(text, "${1}://${2}")
text = fixText(s.getUser, text)
isCmd, text := bot.IsCmd(s.config, text)
isAction := m.SubType == "me_message"
u := s.getUser(m.User)
u, _ := s.getUser(m.User)
if m.Username != "" {
u = m.Username
}
@ -460,9 +459,9 @@ func (s *Slack) connect() {
}
// Get username for Slack user ID
func (s *Slack) getUser(id string) string {
func (s *Slack) getUser(id string) (string, bool) {
if name, ok := s.users[id]; ok {
return name
return name, true
}
log.Printf("User %s not already found, requesting info", id)
@ -472,17 +471,17 @@ func (s *Slack) getUser(id string) string {
if err != nil || resp.StatusCode != 200 {
log.Printf("Error posting user info request: %d %s",
resp.StatusCode, err)
return "UNKNOWN"
return "UNKNOWN", false
}
defer resp.Body.Close()
var userInfo slackUserInfoResp
err = json.NewDecoder(resp.Body).Decode(&userInfo)
if err != nil {
log.Println("Error decoding response: ", err)
return "UNKNOWN"
return "UNKNOWN", false
}
s.users[id] = userInfo.User.Name
return s.users[id]
return s.users[id], true
}
// Who gets usernames out of a channel
@ -513,7 +512,8 @@ func (s *Slack) Who(id string) []string {
handles := []string{}
for _, member := range chanInfo.Channel.Members {
handles = append(handles, s.getUser(member))
u, _ := s.getUser(member)
handles = append(handles, u)
}
log.Printf("Returning %d handles", len(handles))
return handles