413 lines
9.3 KiB
Go
Raw Normal View History

2016-09-25 22:34:02 +02:00
package service
import (
"fmt"
"log"
2017-07-21 14:04:16 +02:00
"regexp"
2016-09-27 22:05:44 +02:00
"strconv"
"time"
2016-09-25 22:34:02 +02:00
"github.com/nlopes/slack"
2017-07-21 14:04:16 +02:00
"github.com/erroneousboat/slack-term/config"
2016-09-25 22:34:02 +02:00
)
const (
ChannelTypeChannel = "channel"
ChannelTypeGroup = "group"
ChannelTypeIM = "im"
)
2016-09-25 22:34:02 +02:00
type SlackService struct {
2016-09-30 23:52:26 +02:00
Client *slack.Client
RTM *slack.RTM
SlackChannels []interface{}
Channels []Channel
UserCache map[string]string
CurrentUserID string
2016-09-25 22:34:02 +02:00
}
type Channel struct {
ID string
Name string
Topic string
Type string
UserID string
2016-09-25 22:34:02 +02:00
}
2016-10-02 16:07:35 +02:00
// NewSlackService is the constructor for the SlackService and will initialize
// the RTM and a Client
2016-09-25 22:34:02 +02:00
func NewSlackService(token string) *SlackService {
2016-09-29 21:19:09 +02:00
svc := &SlackService{
Client: slack.New(token),
UserCache: make(map[string]string),
}
2016-09-25 22:34:02 +02:00
// Get user associated with token, mainly
// used to identify user when new messages
// arrives
authTest, err := svc.Client.AuthTest()
if err != nil {
log.Fatal("ERROR: not able to authorize client, check your connection and/or slack-token")
}
svc.CurrentUserID = authTest.UserID
2016-09-25 22:34:02 +02:00
// Create RTM
svc.RTM = svc.Client.NewRTM()
2016-09-25 22:34:02 +02:00
go svc.RTM.ManageConnection()
2016-10-02 16:07:35 +02:00
// Creation of user cache this speeds up
// the uncovering of usernames of messages
2016-09-30 11:22:40 +02:00
users, _ := svc.Client.GetUsers()
for _, user := range users {
// only add non-deleted users
if !user.Deleted {
svc.UserCache[user.ID] = user.Name
}
2016-09-30 11:22:40 +02:00
}
2016-09-25 22:34:02 +02:00
return svc
}
2016-10-02 16:07:35 +02:00
// GetChannels will retrieve all available channels, groups, and im channels.
// Because the channels are of different types, we will append them to
// an []interface as well as to a []Channel which will give us easy access
// to the id and name of the Channel.
2016-09-25 22:34:02 +02:00
func (s *SlackService) GetChannels() []Channel {
var chans []Channel
2016-09-30 23:52:26 +02:00
// Channel
2016-09-25 22:34:02 +02:00
slackChans, err := s.Client.GetChannels(true)
if err != nil {
chans = append(chans, Channel{})
}
2016-09-30 23:52:26 +02:00
for _, chn := range slackChans {
s.SlackChannels = append(s.SlackChannels, chn)
chans = append(
chans, Channel{
ID: chn.ID,
Name: chn.Name,
Topic: chn.Topic.Value,
Type: ChannelTypeChannel,
UserID: "",
},
)
2016-09-30 23:52:26 +02:00
}
2016-09-25 22:34:02 +02:00
2016-09-30 23:52:26 +02:00
// Groups
2016-10-01 12:48:15 +02:00
slackGroups, err := s.Client.GetGroups(true)
if err != nil {
chans = append(chans, Channel{})
}
for _, grp := range slackGroups {
s.SlackChannels = append(s.SlackChannels, grp)
chans = append(
chans, Channel{
ID: grp.ID,
Name: grp.Name,
Topic: grp.Topic.Value,
Type: ChannelTypeGroup,
UserID: "",
},
)
2016-10-01 12:48:15 +02:00
}
2016-09-30 23:52:26 +02:00
// IM
slackIM, err := s.Client.GetIMChannels()
if err != nil {
chans = append(chans, Channel{})
}
for _, im := range slackIM {
2016-10-01 12:48:15 +02:00
// Uncover name, when we can't uncover name for
// IM channel this is then probably a deleted
// user, because we won't add deleted users
// to the UserCache, so we skip it
2016-10-01 12:48:15 +02:00
name, ok := s.UserCache[im.User]
if ok {
chans = append(
chans,
Channel{
ID: im.ID,
Name: name,
Topic: "",
Type: ChannelTypeIM,
UserID: im.User,
},
)
2016-10-09 16:23:44 +02:00
s.SlackChannels = append(s.SlackChannels, im)
2016-10-01 12:48:15 +02:00
}
2016-09-25 22:34:02 +02:00
}
2016-09-30 23:52:26 +02:00
s.Channels = chans
2016-09-25 22:34:02 +02:00
return chans
}
// GetUserPresence will get the presence of a specific user
func (s *SlackService) GetUserPresence(userID string) (string, error) {
presence, err := s.Client.GetUserPresence(userID)
if err != nil {
return "", err
}
return presence.Presence, nil
}
2016-10-29 23:59:16 +02:00
// SetChannelReadMark will set the read mark for a channel, group, and im
// channel based on the current time.
func (s *SlackService) SetChannelReadMark(channel interface{}) {
switch channel := channel.(type) {
case slack.Channel:
s.Client.SetChannelReadMark(
channel.ID, fmt.Sprintf("%f",
float64(time.Now().Unix())),
)
case slack.Group:
s.Client.SetGroupReadMark(
channel.ID, fmt.Sprintf("%f",
float64(time.Now().Unix())),
)
case slack.IM:
s.Client.MarkIMChannel(
channel.ID, fmt.Sprintf("%f",
float64(time.Now().Unix())),
)
}
}
// SendMessage will send a message to a particular channel
2016-09-29 19:40:09 +02:00
func (s *SlackService) SendMessage(channel string, message string) {
2016-09-27 22:05:44 +02:00
// https://godoc.org/github.com/nlopes/slack#PostMessageParameters
2016-09-28 22:10:04 +02:00
postParams := slack.PostMessageParameters{
2016-09-29 19:40:09 +02:00
AsUser: true,
2016-09-28 22:10:04 +02:00
}
2016-09-27 22:05:44 +02:00
// https://godoc.org/github.com/nlopes/slack#Client.PostMessage
s.Client.PostMessage(channel, message, postParams)
}
2016-09-25 22:34:02 +02:00
2016-10-29 23:59:16 +02:00
// GetMessages will get messages for a channel, group or im channel delimited
// by a count.
2016-09-30 23:52:26 +02:00
func (s *SlackService) GetMessages(channel interface{}, count int) []string {
// https://api.slack.com/methods/channels.history
historyParams := slack.HistoryParameters{
Count: count,
Inclusive: false,
Unreads: false,
}
// https://godoc.org/github.com/nlopes/slack#History
history := new(slack.History)
var err error
switch chnType := channel.(type) {
case slack.Channel:
history, err = s.Client.GetChannelHistory(chnType.ID, historyParams)
if err != nil {
log.Fatal(err) // FIXME
}
case slack.Group:
history, err = s.Client.GetGroupHistory(chnType.ID, historyParams)
if err != nil {
log.Fatal(err) // FIXME
}
case slack.IM:
history, err = s.Client.GetIMHistory(chnType.ID, historyParams)
if err != nil {
log.Fatal(err) // FIXME
}
}
// Construct the messages
var messages []string
for _, message := range history.Messages {
msg := s.CreateMessage(message)
2016-10-09 14:19:28 +02:00
messages = append(messages, msg...)
2016-09-25 22:34:02 +02:00
}
2016-10-11 09:33:56 +02:00
// Reverse the order of the messages, we want the newest in
// the last place
var messagesReversed []string
for i := len(messages) - 1; i >= 0; i-- {
messagesReversed = append(messagesReversed, messages[i])
}
return messagesReversed
2016-09-25 22:34:02 +02:00
}
2016-09-29 21:19:09 +02:00
// CreateMessage will create a string formatted message that can be rendered
// in the Chat pane.
//
// [23:59] <erroneousboat> Hello world!
//
2016-10-09 14:19:28 +02:00
// This returns an array of string because we will try to uncover attachments
// associated with messages.
func (s *SlackService) CreateMessage(message slack.Message) []string {
var msgs []string
2016-09-29 21:19:09 +02:00
var name string
// Get username from cache
name, ok := s.UserCache[message.User]
// Name not in cache
if !ok {
2016-09-30 11:22:40 +02:00
if message.BotID != "" {
2016-09-29 21:19:09 +02:00
// Name not found, perhaps a bot, use Username
2016-09-30 11:22:40 +02:00
name, ok = s.UserCache[message.BotID]
if !ok {
// Not found in cache, add it
name = message.Username
s.UserCache[message.BotID] = message.Username
}
} else {
// Not a bot, not in cache, get user info
user, err := s.Client.GetUserInfo(message.User)
if err != nil {
name = "unknown"
s.UserCache[message.User] = name
} else {
name = user.Name
s.UserCache[message.User] = user.Name
2016-09-29 21:19:09 +02:00
}
}
}
if name == "" {
name = "unknown"
}
2016-10-09 14:19:28 +02:00
// When there are attachments append them
if len(message.Attachments) > 0 {
msgs = append(msgs, createMessageFromAttachments(message.Attachments)...)
}
2016-09-29 21:19:09 +02:00
// Parse time
floatTime, err := strconv.ParseFloat(message.Timestamp, 64)
if err != nil {
floatTime = 0.0
}
intTime := int64(floatTime)
// Format message
msg := fmt.Sprintf(
"[%s] <%s> %s",
time.Unix(intTime, 0).Format("15:04"),
name,
2017-07-21 14:04:16 +02:00
parseEmoji(message.Text),
2016-09-29 21:19:09 +02:00
)
2016-10-09 14:19:28 +02:00
msgs = append(msgs, msg)
2016-10-11 09:33:56 +02:00
return msgs
2016-09-29 21:19:09 +02:00
}
2016-10-09 14:19:28 +02:00
func (s *SlackService) CreateMessageFromMessageEvent(message *slack.MessageEvent) []string {
2016-09-29 21:19:09 +02:00
2016-10-09 14:19:28 +02:00
var msgs []string
2016-09-29 21:19:09 +02:00
var name string
// Append (edited) when an edited message is received
2016-10-30 15:38:13 +01:00
if message.SubType == "message_changed" {
message = &slack.MessageEvent{Msg: *message.SubMessage}
message.Text = fmt.Sprintf("%s (edited)", message.Text)
}
2016-09-29 21:19:09 +02:00
// Get username from cache
name, ok := s.UserCache[message.User]
// Name not in cache
if !ok {
2016-09-30 11:22:40 +02:00
if message.BotID != "" {
2016-09-29 21:19:09 +02:00
// Name not found, perhaps a bot, use Username
2016-09-30 11:22:40 +02:00
name, ok = s.UserCache[message.BotID]
if !ok {
// Not found in cache, add it
name = message.Username
s.UserCache[message.BotID] = message.Username
}
} else {
// Not a bot, not in cache, get user info
user, err := s.Client.GetUserInfo(message.User)
if err != nil {
name = "unknown"
s.UserCache[message.User] = name
} else {
name = user.Name
s.UserCache[message.User] = user.Name
2016-09-29 21:19:09 +02:00
}
}
}
if name == "" {
name = "unknown"
}
2016-10-09 14:19:28 +02:00
// When there are attachments append them
if len(message.Attachments) > 0 {
msgs = append(msgs, createMessageFromAttachments(message.Attachments)...)
}
2016-09-29 21:19:09 +02:00
// Parse time
floatTime, err := strconv.ParseFloat(message.Timestamp, 64)
if err != nil {
floatTime = 0.0
}
intTime := int64(floatTime)
// Format message
msg := fmt.Sprintf(
"[%s] <%s> %s",
time.Unix(intTime, 0).Format("15:04"),
name,
2017-07-21 14:04:16 +02:00
parseEmoji(message.Text),
2016-09-29 21:19:09 +02:00
)
2016-10-09 14:19:28 +02:00
msgs = append(msgs, msg)
2016-10-11 09:33:56 +02:00
return msgs
2016-10-09 14:19:28 +02:00
}
// createMessageFromAttachments will construct a array of string of the Field
// values of Attachments from a Message.
func createMessageFromAttachments(atts []slack.Attachment) []string {
var msgs []string
for _, att := range atts {
for i := len(att.Fields) - 1; i >= 0; i-- {
msgs = append(msgs,
fmt.Sprintf(
"%s %s",
att.Fields[i].Title,
att.Fields[i].Value,
),
)
}
if att.Text != "" {
msgs = append(msgs, att.Text)
}
if att.Title != "" {
msgs = append(msgs, att.Title)
}
2016-10-09 14:19:28 +02:00
}
2016-10-09 14:19:28 +02:00
return msgs
2016-09-29 21:19:09 +02:00
}
2017-07-21 14:04:16 +02:00
// parseEmoji will try to find emoji placeholders in the message
// string and replace them with the correct unicode equivalent
func parseEmoji(msg string) string {
r := regexp.MustCompile("(:\\w+:)")
return r.ReplaceAllStringFunc(
msg, func(str string) string {
code, ok := config.EmojiCodemap[str]
if !ok {
return str
}
return code
},
)
}