Merge pull request #109 from iopred/map

BREAKING CHANGE -- Mapped Event Handlers
This commit is contained in:
Bruce 2016-02-15 21:43:59 -06:00
commit 28236b429c
8 changed files with 371 additions and 483 deletions

View file

@ -13,7 +13,10 @@
// Package discordgo provides Discord binding for Go // Package discordgo provides Discord binding for Go
package discordgo package discordgo
import "fmt" import (
"fmt"
"reflect"
)
// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/) // VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.11.0-alpha" const VERSION = "0.11.0-alpha"
@ -118,3 +121,81 @@ func New(args ...interface{}) (s *Session, err error) {
return return
} }
func (s *Session) AddHandler(handler interface{}) {
s.Lock()
defer s.Unlock()
handlerType := reflect.TypeOf(handler)
if handlerType.NumIn() != 2 {
panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).")
}
if handlerType.In(0) != reflect.TypeOf(s) {
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
}
if s.handlers == nil {
s.Unlock()
s.initialize()
s.Lock()
}
eventType := handlerType.In(1)
// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
if eventType.Kind() == reflect.Interface {
eventType = nil
}
handlers := s.handlers[eventType]
if handlers == nil {
handlers = []reflect.Value{}
}
handlers = append(handlers, reflect.ValueOf(handler))
s.handlers[eventType] = handlers
}
func (s *Session) handle(event interface{}) {
s.RLock()
defer s.RUnlock()
handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}
if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
for _, handler := range handlers {
handler.Call(handlerParameters)
}
}
if handlers, ok := s.handlers[nil]; ok {
for _, handler := range handlers {
handler.Call(handlerParameters)
}
}
}
// initialize adds all internal handlers and state tracking handlers.
func (s *Session) initialize() {
s.Lock()
s.handlers = map[interface{}][]reflect.Value{}
s.Unlock()
s.AddHandler(s.onEvent)
s.AddHandler(s.onReady)
s.AddHandler(s.onVoiceServerUpdate)
s.AddHandler(s.onVoiceStateUpdate)
s.AddHandler(s.State.onInterface)
}
// onEvent handles events that are unhandled or errored while unmarshalling
func (s *Session) onEvent(se *Session, e *Event) {
printEvent(e)
}
// onReady handles the ready event.
func (s *Session) onReady(se *Session, r *Ready) {
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}

View file

@ -224,3 +224,39 @@ func TestOpenClose(t *testing.T) {
t.Fatalf("TestClose, d.Close failed: %+v", err) t.Fatalf("TestClose, d.Close failed: %+v", err)
} }
} }
func TestHandlers(t *testing.T) {
testHandlerCalled := false
testHandler := func(s *Session, t *testing.T) {
testHandlerCalled = true
}
interfaceHandlerCalled := false
interfaceHandler := func(s *Session, i interface{}) {
interfaceHandlerCalled = true
}
bogusHandlerCalled := false
bogusHandler := func(s *Session, se *Session) {
bogusHandlerCalled = true
}
d := Session{}
d.AddHandler(testHandler)
d.AddHandler(interfaceHandler)
d.AddHandler(bogusHandler)
d.handle(t)
if !testHandlerCalled {
t.Fatalf("testHandler was not called.")
}
if !interfaceHandlerCalled {
t.Fatalf("interfaceHandler was not called.")
}
if bogusHandlerCalled {
t.Fatalf("bogusHandler was called.")
}
}

100
events.go Normal file
View file

@ -0,0 +1,100 @@
package discordgo
// Connect is an empty struct for an event.
type Connect struct{}
// Disconnect is an empty struct for an event.
type Disconnect struct{}
// MessageCreate is a wrapper struct for an event.
type MessageCreate struct {
*Message
}
// MessageUpdate is a wrapper struct for an event.
type MessageUpdate struct {
*Message
}
// MessageDelete is a wrapper struct for an event.
type MessageDelete struct {
*Message
}
// ChannelCreate is a wrapper struct for an event.
type ChannelCreate struct {
*Channel
}
// ChannelUpdate is a wrapper struct for an event.
type ChannelUpdate struct {
*Channel
}
// ChannelDelete is a wrapper struct for an event.
type ChannelDelete struct {
*Channel
}
// GuildCreate is a wrapper struct for an event.
type GuildCreate struct {
*Guild
}
// GuildUpdate is a wrapper struct for an event.
type GuildUpdate struct {
*Guild
}
// GuildDelete is a wrapper struct for an event.
type GuildDelete struct {
*Guild
}
// GuildBanAdd is a wrapper struct for an event.
type GuildBanAdd struct {
*GuildBan
}
// GuildBanRemove is a wrapper struct for an event.
type GuildBanRemove struct {
*GuildBan
}
// GuildMemberAdd is a wrapper struct for an event.
type GuildMemberAdd struct {
*Member
}
// GuildMemberUpdate is a wrapper struct for an event.
type GuildMemberUpdate struct {
*Member
}
// GuildMemberRemove is a wrapper struct for an event.
type GuildMemberRemove struct {
*Member
}
// GuildRoleCreate is a wrapper struct for an event.
type GuildRoleCreate struct {
*GuildRole
}
// GuildRoleUpdate is a wrapper struct for an event.
type GuildRoleUpdate struct {
*GuildRole
}
// VoiceStateUpdate is a wrapper struct for an event.
type VoiceStateUpdate struct {
*VoiceState
}
// UserUpdate is a wrapper struct for an event.
type UserUpdate struct {
*UserUpdate
}
// UserSettingsUpdate is a map for an event.
type UserSettingsUpdate map[string]interface{}

View file

@ -23,9 +23,10 @@ func main() {
// Create a new Discord Session interface and set a handler for the // Create a new Discord Session interface and set a handler for the
// OnMessageCreate event that happens for every new message on any channel // OnMessageCreate event that happens for every new message on any channel
dg := discordgo.Session{ dg := discordgo.Session{}
OnMessageCreate: messageCreate,
} // Register messageCreate as a callback for the messageCreate events.
dg.AddHandler(messageCreate)
// Login to the Discord server and store the authentication token // Login to the Discord server and store the authentication token
err = dg.Login(os.Args[1], os.Args[2]) err = dg.Login(os.Args[1], os.Args[2])
@ -46,9 +47,9 @@ func main() {
return return
} }
// This function will be called (due to above assignment) every time a new // This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated user has access to. // message is created on any channel that the autenticated user has access to.
func messageCreate(s *discordgo.Session, m *discordgo.Message) { func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
// Print message to stdout. // Print message to stdout.
fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content) fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content)

View file

@ -28,8 +28,8 @@ func main() {
return return
} }
// Register messageCreate as a callback for the OnMessageCreate event. // Register messageCreate as a callback for the messageCreate events.
dg.OnMessageCreate = messageCreate dg.AddHandler(messageCreate)
// Open the websocket and begin listening. // Open the websocket and begin listening.
dg.Open() dg.Open()
@ -40,9 +40,9 @@ func main() {
return return
} }
// This function will be called (due to above assignment) every time a new // This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated user has access to. // message is created on any channel that the autenticated user has access to.
func messageCreate(s *discordgo.Session, m *discordgo.Message) { func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
// Print message to stdout. // Print message to stdout.
fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content) fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content)

View file

@ -35,6 +35,7 @@ func (s *State) OnReady(r *Ready) error {
if s == nil { if s == nil {
return ErrNilState return ErrNilState
} }
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
@ -48,6 +49,7 @@ func (s *State) GuildAdd(guild *Guild) error {
if s == nil { if s == nil {
return ErrNilState return ErrNilState
} }
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
@ -73,6 +75,7 @@ func (s *State) GuildRemove(guild *Guild) error {
if s == nil { if s == nil {
return ErrNilState return ErrNilState
} }
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
@ -94,6 +97,7 @@ func (s *State) Guild(guildID string) (*Guild, error) {
if s == nil { if s == nil {
return nil, ErrNilState return nil, ErrNilState
} }
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -294,6 +298,7 @@ func (s *State) PrivateChannel(channelID string) (*Channel, error) {
if s == nil { if s == nil {
return nil, ErrNilState return nil, ErrNilState
} }
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
@ -429,6 +434,7 @@ func (s *State) MessageRemove(message *Message) error {
if s == nil { if s == nil {
return ErrNilState return ErrNilState
} }
c, err := s.Channel(message.ChannelID) c, err := s.Channel(message.ChannelID)
if err != nil { if err != nil {
return err return err
@ -452,6 +458,7 @@ func (s *State) Message(channelID, messageID string) (*Message, error) {
if s == nil { if s == nil {
return nil, ErrNilState return nil, ErrNilState
} }
c, err := s.Channel(channelID) c, err := s.Channel(channelID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -468,3 +475,41 @@ func (s *State) Message(channelID, messageID string) (*Message, error) {
return nil, errors.New("Message not found.") return nil, errors.New("Message not found.")
} }
// onInterface handles all events related to states.
func (s *State) onInterface(se *Session, i interface{}) {
if s == nil || !se.StateEnabled {
return
}
switch t := i.(type) {
case *Ready:
s.OnReady(t)
case *GuildCreate:
s.GuildAdd(t.Guild)
case *GuildUpdate:
s.GuildAdd(t.Guild)
case *GuildDelete:
s.GuildRemove(t.Guild)
case *GuildMemberAdd:
s.MemberAdd(t.Member)
case *GuildMemberUpdate:
s.MemberAdd(t.Member)
case *GuildMemberRemove:
s.MemberRemove(t.Member)
case *GuildEmojisUpdate:
s.EmojisAdd(t.GuildID, t.Emojis)
case *ChannelCreate:
s.ChannelAdd(t.Channel)
case *ChannelUpdate:
s.ChannelAdd(t.Channel)
case *ChannelDelete:
s.ChannelRemove(t.Channel)
case *MessageCreate:
s.MessageAdd(t.Message)
case *MessageUpdate:
s.MessageAdd(t.Message)
case *MessageDelete:
s.MessageRemove(t.Message)
}
}

View file

@ -13,84 +13,66 @@ package discordgo
import ( import (
"encoding/json" "encoding/json"
"reflect"
"sync" "sync"
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
// A Session represents a connection to the Discord REST API. // A Session represents a connection to the Discord API.
// token : The authentication token returned from Discord
// Debug : If set to ture debug logging will be displayed.
type Session struct { type Session struct {
sync.RWMutex sync.RWMutex
// General configurable settings. // General configurable settings.
Token string // Authentication token for this session
Debug bool // Debug for printing JSON request/responses
// Settable Callback functions for Internal Events // Authentication token for this session
// OnConnect is called when the websocket connection opens. Token string
OnConnect func(*Session)
// OnDisconnect is called when the websocket connection closes.
// This is a good handler to add reconnection logic to.
OnDisconnect func(*Session)
// Settable Callback functions for Websocket Events // Debug for printing JSON request/responses
OnEvent func(*Session, *Event) Debug bool
OnReady func(*Session, *Ready)
OnTypingStart func(*Session, *TypingStart)
OnMessageCreate func(*Session, *Message)
OnMessageUpdate func(*Session, *Message)
OnMessageDelete func(*Session, *Message)
OnMessageAck func(*Session, *MessageAck)
OnUserUpdate func(*Session, *User)
OnPresenceUpdate func(*Session, *PresenceUpdate)
OnVoiceServerUpdate func(*Session, *VoiceServerUpdate)
OnVoiceStateUpdate func(*Session, *VoiceState)
OnChannelCreate func(*Session, *Channel)
OnChannelUpdate func(*Session, *Channel)
OnChannelDelete func(*Session, *Channel)
OnGuildCreate func(*Session, *Guild)
OnGuildUpdate func(*Session, *Guild)
OnGuildDelete func(*Session, *Guild)
OnGuildMemberAdd func(*Session, *Member)
OnGuildMemberRemove func(*Session, *Member)
OnGuildMemberDelete func(*Session, *Member)
OnGuildMemberUpdate func(*Session, *Member)
OnGuildRoleCreate func(*Session, *GuildRole)
OnGuildRoleUpdate func(*Session, *GuildRole)
OnGuildRoleDelete func(*Session, *GuildRoleDelete)
OnGuildIntegrationsUpdate func(*Session, *GuildIntegrationsUpdate)
OnGuildBanAdd func(*Session, *GuildBan)
OnGuildBanRemove func(*Session, *GuildBan)
OnGuildEmojisUpdate func(*Session, *GuildEmojisUpdate)
OnUserSettingsUpdate func(*Session, map[string]interface{}) // TODO: Find better way?
// Exposed but should not be modified by User.
SessionID string // from websocket READY packet
DataReady bool // Set to true when Data Websocket is ready
VoiceReady bool // Set to true when Voice Websocket is ready
UDPReady bool // Set to true when UDP Connection is ready
// The websocket connection.
wsConn *websocket.Conn
// Stores all details related to voice connections
Voice *Voice
// Managed state object, updated with events.
State *State
StateEnabled bool
// When nil, the session is not listening.
listening chan interface{}
// Should the session reconnect the websocket on errors. // Should the session reconnect the websocket on errors.
ShouldReconnectOnError bool ShouldReconnectOnError bool
// Should the session request compressed websocket data. // Should the session request compressed websocket data.
Compress bool Compress bool
// Should state tracking be enabled.
// State tracking is the best way for getting the the users
// active guilds and the members of the guilds.
StateEnabled bool
// Exposed but should not be modified by User.
// Whether the Data Websocket is ready
DataReady bool
// Whether the Voice Websocket is ready
VoiceReady bool
// Whether the UDP Connection is ready
UDPReady bool
// Stores all details related to voice connections
Voice *Voice
// Managed state object, updated internally with events when
// StateEnabled is true.
State *State
// This is a mapping of event structs to a reflected value
// for event handlers.
// We store the reflected value instead of the function
// reference as it is more performant, instead of re-reflecting
// the function each event.
handlers map[interface{}][]reflect.Value
// The websocket connection.
wsConn *websocket.Conn
// When nil, the session is not listening.
listening chan interface{}
} }
// A VoiceRegion stores data for a specific voice region server. // A VoiceRegion stores data for a specific voice region server.

469
wsapi.go
View file

@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"reflect"
"runtime" "runtime"
"time" "time"
@ -86,9 +87,7 @@ func (s *Session) Open() (err error) {
s.Unlock() s.Unlock()
if s.OnConnect != nil { s.handle(&Connect{})
s.OnConnect(s)
}
return return
} }
@ -112,9 +111,7 @@ func (s *Session) Close() (err error) {
s.Unlock() s.Unlock()
if s.OnDisconnect != nil { s.handle(&Disconnect{})
s.OnDisconnect(s)
}
return return
} }
@ -247,15 +244,36 @@ func (s *Session) UpdateStatus(idle int, game string) (err error) {
return return
} }
// Not sure how needed this is and where it would be best to call it. // eventToInterface is a mapping of Discord WSAPI events to their
// somewhere. // DiscordGo event container.
var eventToInterface = map[string]interface{}{
func unmarshalEvent(event *Event, i interface{}) (err error) { "CHANNEL_CREATE": ChannelCreate{},
if err = unmarshal(event.RawData, i); err != nil { "CHANNEL_UPDATE": ChannelUpdate{},
fmt.Println("Unable to unmarshal event data.") "CHANNEL_DELETE": ChannelDelete{},
printEvent(event) "GUILD_CREATE": GuildCreate{},
} "GUILD_UPDATE": GuildUpdate{},
return "GUILD_DELETE": GuildDelete{},
"GUILD_BAN_ADD": GuildBanAdd{},
"GUILD_BAN_REMOVE": GuildBanRemove{},
"GUILD_MEMBER_ADD": GuildMemberAdd{},
"GUILD_MEMBER_UPDATE": GuildMemberUpdate{},
"GUILD_MEMBER_REMOVE": GuildMemberRemove{},
"GUILD_ROLE_CREATE": GuildRoleCreate{},
"GUILD_ROLE_UPDATE": GuildRoleUpdate{},
"GUILD_ROLE_DELETE": GuildRoleDelete{},
"GUILD_INTEGRATIONS_UPDATE": GuildIntegrationsUpdate{},
"GUILD_EMOJIS_UPDATE": GuildEmojisUpdate{},
"MESSAGE_ACK": MessageAck{},
"MESSAGE_CREATE": MessageCreate{},
"MESSAGE_UPDATE": MessageUpdate{},
"MESSAGE_DELETE": MessageDelete{},
"PRESENCE_UPDATE": PresenceUpdate{},
"READY": Ready{},
"USER_UPDATE": UserUpdate{},
"USER_SETTINGS_UPDATE": UserSettingsUpdate{},
"TYPING_START": TypingStart{},
"VOICE_SERVER_UPDATE": VoiceServerUpdate{},
"VOICE_STATE_UPDATE": VoiceStateUpdate{},
} }
// Front line handler for all Websocket Events. Determines the // Front line handler for all Websocket Events. Determines the
@ -266,6 +284,14 @@ func unmarshalEvent(event *Event, i interface{}) (err error) {
// Events will be handled by any implemented handler in Session. // Events will be handled by any implemented handler in Session.
// All unhandled events will then be handled by OnEvent. // All unhandled events will then be handled by OnEvent.
func (s *Session) event(messageType int, message []byte) { func (s *Session) event(messageType int, message []byte) {
s.RLock()
if s.handlers == nil {
s.RUnlock()
s.initialize()
} else {
s.RUnlock()
}
var err error var err error
var reader io.Reader var reader io.Reader
reader = bytes.NewBuffer(message) reader = bytes.NewBuffer(message)
@ -296,406 +322,23 @@ func (s *Session) event(messageType int, message []byte) {
printEvent(e) printEvent(e)
} }
switch e.Type { i := eventToInterface[e.Type]
case "READY": if i != nil {
var st *Ready // Create a new instance of the event type.
if err = unmarshalEvent(e, &st); err == nil { i = reflect.New(reflect.TypeOf(i)).Interface()
go s.heartbeat(s.wsConn, s.listening, st.HeartbeatInterval)
if s.StateEnabled {
err := s.State.OnReady(st)
if err != nil {
fmt.Println("error: ", err)
}
} // Attempt to unmarshal our event.
if s.OnReady != nil { // If there is an error we should handle the event itself.
s.OnReady(s, st) if err = unmarshal(e.RawData, i); err != nil {
} fmt.Println("Unable to unmarshal event data.")
i = e
} }
if s.OnReady != nil { } else {
return fmt.Println("Unknown event.")
} i = e
case "VOICE_SERVER_UPDATE":
if s.Voice == nil && s.OnVoiceServerUpdate == nil {
break
}
var st *VoiceServerUpdate
if err = unmarshalEvent(e, &st); err == nil {
if s.Voice != nil {
s.onVoiceServerUpdate(st)
}
if s.OnVoiceServerUpdate != nil {
s.OnVoiceServerUpdate(s, st)
}
}
if s.OnVoiceServerUpdate != nil {
return
}
case "VOICE_STATE_UPDATE":
if s.Voice == nil && s.OnVoiceStateUpdate == nil {
break
}
var st *VoiceState
if err = unmarshalEvent(e, &st); err == nil {
if s.Voice != nil {
s.onVoiceStateUpdate(st)
}
if s.OnVoiceStateUpdate != nil {
s.OnVoiceStateUpdate(s, st)
}
}
if s.OnVoiceStateUpdate != nil {
return
}
case "USER_UPDATE":
if s.OnUserUpdate != nil {
var st *User
if err = unmarshalEvent(e, &st); err == nil {
s.OnUserUpdate(s, st)
}
return
}
case "PRESENCE_UPDATE":
if s.OnPresenceUpdate != nil {
var st *PresenceUpdate
if err = unmarshalEvent(e, &st); err == nil {
s.OnPresenceUpdate(s, st)
}
return
}
case "TYPING_START":
if s.OnTypingStart != nil {
var st *TypingStart
if err = unmarshalEvent(e, &st); err == nil {
s.OnTypingStart(s, st)
}
return
}
/* Never seen this come in but saw it in another Library.
case "MESSAGE_ACK":
if s.OnMessageAck != nil {
}
*/
case "MESSAGE_CREATE":
stateEnabled := s.StateEnabled && s.State.MaxMessageCount > 0
if !stateEnabled && s.OnMessageCreate == nil {
break
}
var st *Message
if err = unmarshalEvent(e, &st); err == nil {
if stateEnabled {
err := s.State.MessageAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnMessageCreate != nil {
s.OnMessageCreate(s, st)
}
}
if s.OnMessageCreate != nil {
return
}
case "MESSAGE_UPDATE":
stateEnabled := s.StateEnabled && s.State.MaxMessageCount > 0
if !stateEnabled && s.OnMessageUpdate == nil {
break
}
var st *Message
if err = unmarshalEvent(e, &st); err == nil {
if stateEnabled {
err := s.State.MessageAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnMessageUpdate != nil {
s.OnMessageUpdate(s, st)
}
}
return
case "MESSAGE_DELETE":
stateEnabled := s.StateEnabled && s.State.MaxMessageCount > 0
if !stateEnabled && s.OnMessageDelete == nil {
break
}
var st *Message
if err = unmarshalEvent(e, &st); err == nil {
if stateEnabled {
err := s.State.MessageRemove(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnMessageDelete != nil {
s.OnMessageDelete(s, st)
}
}
return
case "MESSAGE_ACK":
if s.OnMessageAck != nil {
var st *MessageAck
if err = unmarshalEvent(e, &st); err == nil {
s.OnMessageAck(s, st)
}
return
}
case "CHANNEL_CREATE":
if !s.StateEnabled && s.OnChannelCreate == nil {
break
}
var st *Channel
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.ChannelAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnChannelCreate != nil {
s.OnChannelCreate(s, st)
}
}
if s.OnChannelCreate != nil {
return
}
case "CHANNEL_UPDATE":
if !s.StateEnabled && s.OnChannelUpdate == nil {
break
}
var st *Channel
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.ChannelAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnChannelUpdate != nil {
s.OnChannelUpdate(s, st)
}
}
if s.OnChannelUpdate != nil {
return
}
case "CHANNEL_DELETE":
if !s.StateEnabled && s.OnChannelDelete == nil {
break
}
var st *Channel
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.ChannelRemove(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnChannelDelete != nil {
s.OnChannelDelete(s, st)
}
}
if s.OnChannelDelete != nil {
return
}
case "GUILD_CREATE":
if !s.StateEnabled && s.OnGuildCreate == nil {
break
}
var st *Guild
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.GuildAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnGuildCreate != nil {
s.OnGuildCreate(s, st)
}
}
if s.OnGuildCreate != nil {
return
}
case "GUILD_UPDATE":
if !s.StateEnabled && s.OnGuildUpdate == nil {
break
}
var st *Guild
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.GuildAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnGuildCreate != nil {
s.OnGuildUpdate(s, st)
}
}
if s.OnGuildUpdate != nil {
return
}
case "GUILD_DELETE":
if !s.StateEnabled && s.OnGuildDelete == nil {
break
}
var st *Guild
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.GuildRemove(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnGuildDelete != nil {
s.OnGuildDelete(s, st)
}
}
if s.OnGuildDelete != nil {
return
}
case "GUILD_MEMBER_ADD":
if !s.StateEnabled && s.OnGuildMemberAdd == nil {
break
}
var st *Member
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.MemberAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnGuildMemberAdd != nil {
s.OnGuildMemberAdd(s, st)
}
}
if s.OnGuildMemberAdd != nil {
return
}
case "GUILD_MEMBER_REMOVE":
if !s.StateEnabled && s.OnGuildMemberRemove == nil {
break
}
var st *Member
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.MemberRemove(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnGuildMemberRemove != nil {
s.OnGuildMemberRemove(s, st)
}
}
if s.OnGuildMemberRemove != nil {
return
}
case "GUILD_MEMBER_UPDATE":
if !s.StateEnabled && s.OnGuildMemberUpdate == nil {
break
}
var st *Member
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.MemberAdd(st)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnGuildMemberUpdate != nil {
s.OnGuildMemberUpdate(s, st)
}
}
if s.OnGuildMemberUpdate != nil {
return
}
case "GUILD_ROLE_CREATE":
if s.OnGuildRoleCreate != nil {
var st *GuildRole
if err = unmarshalEvent(e, &st); err == nil {
s.OnGuildRoleCreate(s, st)
}
return
}
case "GUILD_ROLE_UPDATE":
if s.OnGuildRoleUpdate != nil {
var st *GuildRole
if err = unmarshalEvent(e, &st); err == nil {
s.OnGuildRoleUpdate(s, st)
}
return
}
case "GUILD_ROLE_DELETE":
if s.OnGuildRoleDelete != nil {
var st *GuildRoleDelete
if err = unmarshalEvent(e, &st); err == nil {
s.OnGuildRoleDelete(s, st)
}
return
}
case "GUILD_INTEGRATIONS_UPDATE":
if s.OnGuildIntegrationsUpdate != nil {
var st *GuildIntegrationsUpdate
if err = unmarshalEvent(e, &st); err == nil {
s.OnGuildIntegrationsUpdate(s, st)
}
return
}
case "GUILD_BAN_ADD":
if s.OnGuildBanAdd != nil {
var st *GuildBan
if err = unmarshalEvent(e, &st); err == nil {
s.OnGuildBanAdd(s, st)
}
return
}
case "GUILD_BAN_REMOVE":
if s.OnGuildBanRemove != nil {
var st *GuildBan
if err = unmarshalEvent(e, &st); err == nil {
s.OnGuildBanRemove(s, st)
}
return
}
case "GUILD_EMOJIS_UPDATE":
if !s.StateEnabled && s.OnGuildEmojisUpdate == nil {
break
}
var st *GuildEmojisUpdate
if err = unmarshalEvent(e, &st); err == nil {
if s.StateEnabled {
err := s.State.EmojisAdd(st.GuildID, st.Emojis)
if err != nil {
fmt.Println("error :", err)
}
}
if s.OnGuildEmojisUpdate != nil {
s.OnGuildEmojisUpdate(s, st)
}
}
if s.OnGuildEmojisUpdate != nil {
return
}
case "USER_SETTINGS_UPDATE":
if s.OnUserSettingsUpdate != nil {
var st map[string]interface{}
if err = unmarshalEvent(e, &st); err == nil {
s.OnUserSettingsUpdate(s, st)
}
return
}
default:
fmt.Println("Unknown Event.")
printEvent(e)
} }
// if still here, send to generic OnEvent s.handle(i)
if s.OnEvent != nil {
s.OnEvent(s, e)
return
}
return return
} }
@ -765,7 +408,7 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (err error)
// onVoiceStateUpdate handles Voice State Update events on the data // onVoiceStateUpdate handles Voice State Update events on the data
// websocket. This comes immediately after the call to VoiceChannelJoin // websocket. This comes immediately after the call to VoiceChannelJoin
// for the session user. // for the session user.
func (s *Session) onVoiceStateUpdate(st *VoiceState) { func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
// Need to have this happen at login and store it in the Session // Need to have this happen at login and store it in the Session
// TODO : This should be done upon connecting to Discord, or // TODO : This should be done upon connecting to Discord, or
@ -791,7 +434,7 @@ func (s *Session) onVoiceStateUpdate(st *VoiceState) {
// onVoiceServerUpdate handles the Voice Server Update data websocket event. // onVoiceServerUpdate handles the Voice Server Update data websocket event.
// This event tells us the information needed to open a voice websocket // This event tells us the information needed to open a voice websocket
// connection and should happen after the VOICE_STATE event. // connection and should happen after the VOICE_STATE event.
func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) { func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) {
// Store values for later use // Store values for later use
s.Voice.token = st.Token s.Voice.token = st.Token