Skip to content

Commit

Permalink
Improve websocket types a bit.
Browse files Browse the repository at this point in the history
This makes it possible to select on the event type and act accordingly.
  • Loading branch information
nlopes committed Feb 3, 2015
1 parent 648871c commit 676c3c4
Show file tree
Hide file tree
Showing 13 changed files with 372 additions and 47 deletions.
51 changes: 51 additions & 0 deletions examples/websocket/websocket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"fmt"
"time"

"github.com/nlopes/slack"
)

func main() {
chSender := make(chan slack.OutgoingMessage)
chReceiver := make(chan slack.SlackEvent)

api := slack.New("YOUR TOKEN HERE")
api.SetDebug(true)
wsAPI, err := api.StartRTM("", "http://example.com")
if err != nil {
fmt.Errorf("%s\n", err)
}
go wsAPI.HandleIncomingEvents(chReceiver)
go wsAPI.Keepalive(20 * time.Second)
go func(wsAPI *slack.SlackWS, chSender chan slack.OutgoingMessage) {
for {
select {
case msg := <-chSender:
wsAPI.SendMessage(&msg)
}
}
}(wsAPI, chSender)
for {
select {
case msg := <-chReceiver:
fmt.Print("Event Received: ")
switch msg.Data.(type) {
case slack.HelloEvent:
// Ignore hello
case *slack.MessageEvent:
a := msg.Data.(*slack.MessageEvent)
fmt.Printf("%v\n", a)
case *slack.PresenceChangeEvent:
a := msg.Data.(*slack.PresenceChangeEvent)
fmt.Printf("%v\n", a)
case slack.LatencyReport:
a := msg.Data.(slack.LatencyReport)
fmt.Printf("Current latency: %v\n", a.Value)
default:
fmt.Printf("Unknown: %v\n", msg.Data)
}
}
}
}
1 change: 1 addition & 0 deletions files.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Comment struct {
Timestamp JSONTime `json:"timestamp"`
UserId string `json:"user"`
Comment string `json:"comment"`
Created JSONTime `json:"created,omitempty"`
}

// File contains all the information for a file
Expand Down
9 changes: 9 additions & 0 deletions messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Msg struct {
Timestamp string `json:"ts,omitempty"`
Text string `json:"text,omitempty"`
Team string `json:"team,omitempty"`
File File `json:"file,omitempty"`
// Type may come if it's part of a message list
// e.g.: channel.history
Type string `json:"type,omitempty"`
Expand All @@ -32,6 +33,8 @@ type Msg struct {
Hidden bool `json:"bool,omitempty"`
DeletedTimestamp string `json:"deleted_ts,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
ReplyTo int `json:"reply_to,omitempty"`
Upload bool `json:"upload,omitempty"`
}

// Presence XXX: not used yet
Expand All @@ -51,6 +54,12 @@ type Ping struct {
Type string `json:"type"`
}

// Pong contains information about a Pong Event
type Pong struct {
Type string `json:"type"`
ReplyTo int `json:"reply_to"`
}

// AckMessage is used for messages received in reply to other messages
type AckMessage struct {
ReplyTo int `json:"reply_to"`
Expand Down
11 changes: 0 additions & 11 deletions slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@ import (
*/
var SLACK_API string = "https://slack.com/api/"

type UserTyping struct {
Type string `json:"type"`
UserId string `json:"user"`
ChannelId string `json:"channel"`
}

type SlackEvent struct {
Type int
Data interface{}
}

type SlackResponse struct {
Ok bool `json:"ok"`
Error string `json:"error"`
Expand Down
9 changes: 5 additions & 4 deletions stars.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ type StarsParameters struct {
Page int
}

// XXX: Verify this. The whole thing is complicated. I don't like the way they mixed things
// TODO: Verify this. The whole thing is complicated. I don't like the way they mixed things
// It also appears to be a bug in parsing the message
type StarredItem struct {
Type string `json:"type"`
ChannelId string `json:"channel"`
Message `json:"message"`
File `json:"file"`
Comment `json:"comment"`
Message `json:"message,omitempty"`
File `json:"file,omitempty"`
Comment `json:"comment,omitempty"`
}

type starsResponseFull struct {
Expand Down
150 changes: 118 additions & 32 deletions websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,89 @@ import (
"log"
"net"
"net/url"
"strconv"
"sync"
"time"

"golang.org/x/net/websocket"
)

const (
EV_MESSAGE = iota
EV_USER_TYPING
)
type MessageEvent Message

var eventMapping map[string]interface{} = map[string]interface{}{
"message": &MessageEvent{},
"presence_change": &PresenceChangeEvent{},
"user_typing": &UserTypingEvent{},

"channel_marked": &ChannelMarkedEvent{},
"channel_created": &ChannelCreatedEvent{},
"channel_joined": &ChannelJoinedEvent{},
"channel_left": &ChannelLeftEvent{},
"channel_deleted": &ChannelDeletedEvent{},
"channel_rename": &ChannelRenameEvent{},
"channel_archive": &ChannelArchiveEvent{},
"channel_unarchive": &ChannelUnarchiveEvent{},
"channel_history_changed": &ChannelHistoryChangedEvent{},

"im_created": &IMCreatedEvent{},
"im_open": &IMOpenEvent{},
"im_close": &IMCloseEvent{},
"im_marked": &IMMarkedEvent{},
"im_history_changed": &IMHistoryChangedEvent{},

"group_marked": &GroupMarkedEvent{},
"group_open": &GroupOpenEvent{},
"group_joined": &GroupJoinedEvent{},
"group_left": &GroupLeftEvent{},
"group_close": &GroupCloseEvent{},
"group_rename": &GroupRenameEvent{},
"group_archive": &GroupArchiveEvent{},
"group_unarchive": &GroupUnarchiveEvent{},
"group_history_changed": &GroupHistoryChangedEvent{},

// XXX: Not implemented below here
"file_created": &FileCreatedEvent{},
"file_shared": &FileSharedEvent{},
"file_unshared": &FileUnsharedEvent{},
"file_public": &FilePublicEvent{},
"file_private": &FilePrivateEvent{},
"file_change": &FileChangeEvent{},
"file_deleted": &FileDeletedEvent{},
"file_comment_added": &FileCommentAddedEvent{},
"file_comment_edited": &FileCommentEditedEvent{},
"file_comment_deleted": &FileCommentDeletedEvent{},

"team_join": &TeamJoinEvent{},
"team_rename": &TeamRenameEvent{},
"team_pref_change": &TeamPrefChangeEvent{},
"team_domain_change": &TeamDomainChangeEvent{},
"team_migration_started": &TeamMigrationStartedEvent{},

"manual_presence_change": &ManualPresenceChangeEvent{},

"pref_change": &PrefChangeEvent{},
"user_change": &UserChangeEvent{},

"star_added": &StarAddedEvent{},
"star_removed": &StarRemovedEvent{},

"emoji_changed": &EmojiChangedEvent{},

"commands_changed": &CommandsChangedEvent{},

"email_domain_changed": &EmailDomainChangedEvent{},

"bot_added": &BotAddedEvent{},
"bot_changed": &BotChangedEvent{},

"accounts_changed": &AccountsChangedEvent{},
}

type SlackWS struct {
conn *websocket.Conn
messageId int
mutex sync.Mutex
pings map[int]time.Time
Slack
}

Expand All @@ -35,6 +103,28 @@ type SlackWSError struct {
Msg string
}

type SlackEvent struct {
Type uint64
Data interface{}
}

type JSONTimeString string

// String converts the unix timestamp into a string
func (t JSONTimeString) String() string {
if t == "" {
return ""
}
floatN, err := strconv.ParseFloat(string(t), 64)
if err != nil {
log.Panicln(err)
return ""
}
timeStr := int64(floatN)
tm := time.Unix(int64(timeStr), 0)
return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2"))
}

func (s SlackWSError) Error() string {
return s.Msg
}
Expand Down Expand Up @@ -76,6 +166,7 @@ func (api *Slack) StartRTM(protocol, origin string) (*SlackWS, error) {
if err != nil {
return nil, err
}
wsApi.pings = make(map[int]time.Time)
return wsApi, nil
}

Expand All @@ -87,6 +178,8 @@ func (api *SlackWS) Ping() error {
if err := websocket.JSON.Send(api.conn, msg); err != nil {
return err
}
// TODO: What happens if we already have this id?
api.pings[api.messageId] = time.Now()
return nil
}

Expand Down Expand Up @@ -140,19 +233,19 @@ func (api *SlackWS) HandleIncomingEvents(ch chan SlackEvent) {
if api.debug {
log.Println(string(event[:]))
}
handleEvent(ch, event)
api.handleEvent(ch, event)
}
time.Sleep(time.Millisecond * 500)
}
}

func handleEvent(ch chan SlackEvent, event json.RawMessage) {
func (api *SlackWS) handleEvent(ch chan SlackEvent, event json.RawMessage) {
em := Event{}
err := json.Unmarshal(event, &em)
if err != nil {
log.Fatal(err)
}

log.Println(em.Type)
switch em.Type {
case "":
// try ok
Expand All @@ -162,44 +255,37 @@ func handleEvent(ch chan SlackEvent, event json.RawMessage) {
}

if ack.Ok {
// TODO: Send the ack back (is this useful?)
//ch <- SlackEvent{Type: EventAck, Data: ack}
log.Printf("Received an ok for: %d", ack.ReplyTo)
return
}

// TODO: errors end up in this bucket. They shouldn't.
log.Printf("Got error(?): %s", event)
case "hello":
return
ch <- SlackEvent{Data: HelloEvent{}}
case "pong":
// XXX: Eventually check to which ping this matched with
// Allows us to have stats about latency and what not
return
case "presence_change":
//log.Printf("`%s is %s`\n", info.GetUserById(event.PUserId).Name, event.Presence)
case "message":
handleMessage(ch, event)
case "channel_marked":
log.Printf("XXX: To implement %s", em)
case "user_typing":
handleUserTyping(ch, event)
pong := Pong{}
if err = json.Unmarshal(event, &pong); err != nil {
log.Fatal(err)
}
api.mutex.Lock()
latency := time.Since(api.pings[pong.ReplyTo])
api.mutex.Unlock()
ch <- SlackEvent{Data: LatencyReport{Value: latency}}
default:
log.Println("XXX: " + string(event))
callEvent(em.Type, ch, event)
}
}

func handleUserTyping(ch chan SlackEvent, event json.RawMessage) {
msg := UserTyping{}
if err := json.Unmarshal(event, &msg); err != nil {
log.Fatal(err)
func callEvent(eventType string, ch chan SlackEvent, event json.RawMessage) {
msg := eventMapping[eventType]
if msg == nil {
log.Printf("XXX: Not implemented yet: %s -> %v", eventType, event)
}
ch <- SlackEvent{Type: EV_USER_TYPING, Data: msg}
}

func handleMessage(ch chan SlackEvent, event json.RawMessage) {
msg := Message{}
err := json.Unmarshal(event, &msg)
if err != nil {
if err := json.Unmarshal(event, &msg); err != nil {
log.Fatal(err)
}
ch <- SlackEvent{Type: EV_MESSAGE, Data: msg}
ch <- SlackEvent{Data: msg}
}
Loading

0 comments on commit 676c3c4

Please sign in to comment.