This repository has been archived on 2019-11-22. You can view files and clone it, but cannot push or open issues or pull requests.
aocbot/slack.go

241 lines
5.1 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"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
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)
}
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
}
func (s *Slack) getChannelList() ([]Channel, error) {
url := fmt.Sprintf("https://slack.com/api/channels.list?token=%s", s.apiToken)
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
}
fmt.Println(string(body))
var respObj responseChannelList
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
}
for i := range respObj.Channels {
respObj.Channels[i].LastRead = convertSlackTimestamp(respObj.Channels[i].LastReadRaw)
}
return respObj.Channels, nil
}
func (s *Slack) getChannelInfo(cid string) (*Channel, error) {
url := fmt.Sprintf("https://slack.com/api/channels.info?token=%s&channel=%s", s.apiToken, cid)
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 responseChannelLookup
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
}
respObj.Channel.LastRead = convertSlackTimestamp(respObj.Channel.LastReadRaw)
return respObj.Channel, nil
}
// 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"`
}
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
}