2019-05-31 00:18:41 +01:00
|
|
|
package slack
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2020-07-27 13:22:00 +01:00
|
|
|
"github.com/micro/go-micro/v3/agent/input"
|
2019-05-31 00:18:41 +01:00
|
|
|
"github.com/nlopes/slack"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Satisfies the input.Conn interface
|
|
|
|
type slackConn struct {
|
|
|
|
auth *slack.AuthTestResponse
|
|
|
|
rtm *slack.RTM
|
|
|
|
exit chan bool
|
|
|
|
|
|
|
|
sync.Mutex
|
|
|
|
names map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *slackConn) run() {
|
|
|
|
// func retrieves user names and maps to IDs
|
|
|
|
setNames := func() {
|
|
|
|
names := make(map[string]string)
|
|
|
|
users, err := s.rtm.Client.GetUsers()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, user := range users {
|
|
|
|
names[user.ID] = user.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Lock()
|
|
|
|
s.names = names
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
setNames()
|
|
|
|
|
|
|
|
t := time.NewTicker(time.Minute)
|
|
|
|
defer t.Stop()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-s.exit:
|
|
|
|
return
|
|
|
|
case <-t.C:
|
|
|
|
setNames()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *slackConn) getName(id string) string {
|
|
|
|
s.Lock()
|
|
|
|
name := s.names[id]
|
|
|
|
s.Unlock()
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *slackConn) Close() error {
|
|
|
|
select {
|
|
|
|
case <-s.exit:
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
close(s.exit)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *slackConn) Recv(event *input.Event) error {
|
|
|
|
if event == nil {
|
|
|
|
return errors.New("event cannot be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-s.exit:
|
|
|
|
return errors.New("connection closed")
|
|
|
|
case e := <-s.rtm.IncomingEvents:
|
|
|
|
switch ev := e.Data.(type) {
|
|
|
|
case *slack.MessageEvent:
|
|
|
|
// only accept type message
|
|
|
|
if ev.Type != "message" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// only accept DMs or messages to me
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(ev.Channel, "D"):
|
|
|
|
case strings.HasPrefix(ev.Text, s.auth.User):
|
|
|
|
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
|
|
|
|
default:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Strip username from text
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(ev.Text, s.auth.User):
|
|
|
|
args := strings.Split(ev.Text, " ")[1:]
|
|
|
|
ev.Text = strings.Join(args, " ")
|
|
|
|
event.To = s.auth.User
|
|
|
|
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
|
|
|
|
args := strings.Split(ev.Text, " ")[1:]
|
|
|
|
ev.Text = strings.Join(args, " ")
|
|
|
|
event.To = s.auth.UserID
|
|
|
|
}
|
|
|
|
|
|
|
|
if event.Meta == nil {
|
|
|
|
event.Meta = make(map[string]interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// fill in the blanks
|
|
|
|
event.From = ev.Channel + ":" + ev.User
|
|
|
|
event.Type = input.TextEvent
|
|
|
|
event.Data = []byte(ev.Text)
|
|
|
|
event.Meta["reply"] = ev
|
|
|
|
return nil
|
|
|
|
case *slack.InvalidAuthEvent:
|
|
|
|
return errors.New("invalid credentials")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *slackConn) Send(event *input.Event) error {
|
|
|
|
var channel, message, name string
|
|
|
|
|
|
|
|
if len(event.To) == 0 {
|
|
|
|
return errors.New("require Event.To")
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := strings.Split(event.To, ":")
|
|
|
|
|
|
|
|
if len(parts) == 2 {
|
|
|
|
channel = parts[0]
|
|
|
|
name = s.getName(parts[1])
|
|
|
|
// try using reply meta
|
|
|
|
} else if ev, ok := event.Meta["reply"]; ok {
|
|
|
|
channel = ev.(*slack.MessageEvent).Channel
|
|
|
|
name = s.getName(ev.(*slack.MessageEvent).User)
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't know where to send the message
|
|
|
|
if len(channel) == 0 {
|
|
|
|
return errors.New("could not determine who message is to")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(name) == 0 || strings.HasPrefix(channel, "D") {
|
|
|
|
message = string(event.Data)
|
|
|
|
} else {
|
|
|
|
message = fmt.Sprintf("@%s: %s", name, string(event.Data))
|
|
|
|
}
|
|
|
|
|
|
|
|
s.rtm.SendMessage(s.rtm.NewOutgoingMessage(message, channel))
|
|
|
|
return nil
|
|
|
|
}
|