2016-12-06 04:20:14 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2016-12-06 23:31:22 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2016-12-06 04:20:14 +00:00
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"golang.org/x/net/websocket"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Slack manages the slack connection and implements API calls
|
|
|
|
type Slack struct {
|
|
|
|
apiToken string
|
|
|
|
id string
|
|
|
|
socket *websocket.Conn
|
|
|
|
alive bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateSlack creates the slack object, opening a websocket
|
|
|
|
func CreateSlack(token string) (*Slack, error) {
|
|
|
|
s := Slack{apiToken: token}
|
|
|
|
url := fmt.Sprintf("https://slack.com/api/rtm.start?token=%s", token)
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
resp.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var respObj responseRtmStart
|
|
|
|
err = json.Unmarshal(body, &respObj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !respObj.Ok {
|
|
|
|
err = fmt.Errorf("Slack error: %s", respObj.Error)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
wsurl := respObj.URL
|
|
|
|
s.id = respObj.Self.ID
|
|
|
|
|
|
|
|
ws, err := websocket.Dial(wsurl, "", "https://api.slack.com/")
|
|
|
|
s.socket = ws
|
|
|
|
s.keepAlive()
|
|
|
|
|
|
|
|
return &s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Slack) keepAlive() {
|
|
|
|
s.alive = true
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
if s.alive {
|
|
|
|
s.ping()
|
|
|
|
}
|
|
|
|
time.Sleep(time.Second * 30)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
var counter uint64
|
|
|
|
|
|
|
|
func (s *Slack) ping() error {
|
|
|
|
return s.postMessage(Message{Type: "ping"})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Slack) postMessage(m Message) error {
|
|
|
|
m.ID = atomic.AddUint64(&counter, 1)
|
|
|
|
return websocket.JSON.Send(s.socket, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Slack) getMessage() (Message, error) {
|
|
|
|
var m Message
|
|
|
|
err := websocket.JSON.Receive(s.socket, &m)
|
|
|
|
|
|
|
|
m.Time = convertSlackTimestamp(m.Ts)
|
|
|
|
|
|
|
|
return m, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Slack) sendMessageToUser(uid string, msg string) error {
|
|
|
|
// TODO: Actually send a direct message to uid
|
2016-12-06 23:31:22 +00:00
|
|
|
m := new(Message)
|
|
|
|
m.ID = atomic.AddUint64(&counter, 1)
|
|
|
|
m.Type = "message"
|
|
|
|
m.Channel = ""
|
|
|
|
m.User = uid
|
|
|
|
m.Name = ""
|
|
|
|
m.Text = msg
|
|
|
|
|
|
|
|
return websocket.JSON.Send(s.socket, m)
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Slack) getUserInfo(uid string) (*User, error) {
|
|
|
|
url := fmt.Sprintf("https://slack.com/api/users.info?token=%s&user=%s", s.apiToken, uid)
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
resp.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var respObj responseUserLookup
|
|
|
|
err = json.Unmarshal(body, &respObj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !respObj.Ok {
|
|
|
|
err = fmt.Errorf("Slack error: %s", respObj.Error)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return respObj.User, nil
|
|
|
|
}
|
|
|
|
|
2016-12-06 23:31:22 +00:00
|
|
|
func (s *Slack) getChannelList() ([]Channel, error) {
|
|
|
|
url := fmt.Sprintf("https://slack.com/api/channels.list?token=%s", s.apiToken)
|
2016-12-06 04:20:14 +00:00
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
resp.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-12-06 23:31:22 +00:00
|
|
|
fmt.Println(string(body))
|
|
|
|
|
|
|
|
var respObj responseChannelList
|
2016-12-06 04:20:14 +00:00
|
|
|
err = json.Unmarshal(body, &respObj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !respObj.Ok {
|
|
|
|
err = fmt.Errorf("Slack error: %s", respObj.Error)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-12-06 23:31:22 +00:00
|
|
|
for i := range respObj.Channels {
|
|
|
|
respObj.Channels[i].LastRead = convertSlackTimestamp(respObj.Channels[i].LastReadRaw)
|
|
|
|
}
|
2016-12-06 04:20:14 +00:00
|
|
|
|
2016-12-06 23:31:22 +00:00
|
|
|
return respObj.Channels, nil
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
|
|
|
|
2016-12-06 23:31:22 +00:00
|
|
|
func (s *Slack) getChannelInfo(cid string) (*Channel, error) {
|
|
|
|
url := fmt.Sprintf("https://slack.com/api/channels.info?token=%s&channel=%s", s.apiToken, cid)
|
2016-12-06 04:20:14 +00:00
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
2016-12-06 23:31:22 +00:00
|
|
|
return nil, err
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
err = fmt.Errorf("API request failed with code %d", resp.StatusCode)
|
2016-12-06 23:31:22 +00:00
|
|
|
return nil, err
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
resp.Body.Close()
|
|
|
|
if err != nil {
|
2016-12-06 23:31:22 +00:00
|
|
|
return nil, err
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
|
|
|
var respObj responseChannelLookup
|
|
|
|
err = json.Unmarshal(body, &respObj)
|
|
|
|
if err != nil {
|
2016-12-06 23:31:22 +00:00
|
|
|
return nil, err
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !respObj.Ok {
|
|
|
|
err = fmt.Errorf("Slack error: %s", respObj.Error)
|
2016-12-06 23:31:22 +00:00
|
|
|
return nil, err
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
2016-12-06 23:31:22 +00:00
|
|
|
|
|
|
|
respObj.Channel.LastRead = convertSlackTimestamp(respObj.Channel.LastReadRaw)
|
|
|
|
|
|
|
|
return respObj.Channel, nil
|
2016-12-06 04:20:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// These structures represent the response of the Slack API events
|
|
|
|
// Only some fields are included. The rest are ignored by json.Unmarshal.
|
|
|
|
type responseRtmStart struct {
|
|
|
|
Ok bool `json:"ok"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
URL string `json:"url"`
|
|
|
|
Self responseSelf `json:"self"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type responseSelf struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type responseUserLookup struct {
|
|
|
|
Ok bool `json:"ok"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
User *User `json:"user"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type responseChannelLookup struct {
|
|
|
|
Ok bool `json:"ok"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
Channel *Channel `json:"channel"`
|
|
|
|
}
|
2016-12-06 23:31:22 +00:00
|
|
|
|
|
|
|
type responseChannelList struct {
|
|
|
|
Ok bool `json:"ok"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
Channels []Channel `json:"channels"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func convertSlackTimestamp(ts string) time.Time {
|
|
|
|
var ret time.Time
|
|
|
|
txtArr := strings.Split(ts, ".")
|
|
|
|
if t, err := strconv.Atoi(txtArr[0]); err == nil {
|
|
|
|
rawts := int64(t)
|
|
|
|
ret = time.Unix(0, rawts*1000000000)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|