diff --git a/discord.go b/discord.go index 68d2a7e..18f7ffa 100644 --- a/discord.go +++ b/discord.go @@ -13,7 +13,10 @@ // Package discordgo provides Discord binding for Go package discordgo -import "fmt" +import ( + "fmt" + "reflect" +) // VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/) const VERSION = "0.11.0-alpha" @@ -118,3 +121,81 @@ func New(args ...interface{}) (s *Session, err error) { 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) +} diff --git a/discord_test.go b/discord_test.go index 687f820..7442651 100644 --- a/discord_test.go +++ b/discord_test.go @@ -224,3 +224,39 @@ func TestOpenClose(t *testing.T) { 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.") + } +} diff --git a/events.go b/events.go new file mode 100644 index 0000000..d81ecef --- /dev/null +++ b/events.go @@ -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{} diff --git a/examples/api_basic/api_basic.go b/examples/api_basic/api_basic.go index aea4e56..733cece 100644 --- a/examples/api_basic/api_basic.go +++ b/examples/api_basic/api_basic.go @@ -23,9 +23,10 @@ func main() { // Create a new Discord Session interface and set a handler for the // OnMessageCreate event that happens for every new message on any channel - dg := discordgo.Session{ - OnMessageCreate: messageCreate, - } + dg := discordgo.Session{} + + // Register messageCreate as a callback for the messageCreate events. + dg.AddHandler(messageCreate) // Login to the Discord server and store the authentication token err = dg.Login(os.Args[1], os.Args[2]) @@ -46,9 +47,9 @@ func main() { 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. -func messageCreate(s *discordgo.Session, m *discordgo.Message) { +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { // Print message to stdout. fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content) diff --git a/examples/new_basic/new_basic.go b/examples/new_basic/new_basic.go index 43492ec..6d524ac 100644 --- a/examples/new_basic/new_basic.go +++ b/examples/new_basic/new_basic.go @@ -28,8 +28,8 @@ func main() { return } - // Register messageCreate as a callback for the OnMessageCreate event. - dg.OnMessageCreate = messageCreate + // Register messageCreate as a callback for the messageCreate events. + dg.AddHandler(messageCreate) // Open the websocket and begin listening. dg.Open() @@ -40,9 +40,9 @@ func main() { 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. -func messageCreate(s *discordgo.Session, m *discordgo.Message) { +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { // Print message to stdout. fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content) diff --git a/state.go b/state.go index 0ee8963..8074853 100644 --- a/state.go +++ b/state.go @@ -35,6 +35,7 @@ func (s *State) OnReady(r *Ready) error { if s == nil { return ErrNilState } + s.Lock() defer s.Unlock() @@ -48,6 +49,7 @@ func (s *State) GuildAdd(guild *Guild) error { if s == nil { return ErrNilState } + s.Lock() defer s.Unlock() @@ -73,6 +75,7 @@ func (s *State) GuildRemove(guild *Guild) error { if s == nil { return ErrNilState } + s.Lock() defer s.Unlock() @@ -94,6 +97,7 @@ func (s *State) Guild(guildID string) (*Guild, error) { if s == nil { return nil, ErrNilState } + s.RLock() defer s.RUnlock() @@ -294,6 +298,7 @@ func (s *State) PrivateChannel(channelID string) (*Channel, error) { if s == nil { return nil, ErrNilState } + s.RLock() defer s.RUnlock() @@ -429,6 +434,7 @@ func (s *State) MessageRemove(message *Message) error { if s == nil { return ErrNilState } + c, err := s.Channel(message.ChannelID) if err != nil { return err @@ -452,6 +458,7 @@ func (s *State) Message(channelID, messageID string) (*Message, error) { if s == nil { return nil, ErrNilState } + c, err := s.Channel(channelID) if err != nil { return nil, err @@ -468,3 +475,41 @@ func (s *State) Message(channelID, messageID string) (*Message, error) { 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) + } +} diff --git a/structs.go b/structs.go index 7275ed4..0d10c1c 100644 --- a/structs.go +++ b/structs.go @@ -13,84 +13,66 @@ package discordgo import ( "encoding/json" + "reflect" "sync" "time" "github.com/gorilla/websocket" ) -// A Session represents a connection to the Discord REST API. -// token : The authentication token returned from Discord -// Debug : If set to ture debug logging will be displayed. +// A Session represents a connection to the Discord API. type Session struct { sync.RWMutex // General configurable settings. - Token string // Authentication token for this session - Debug bool // Debug for printing JSON request/responses - // Settable Callback functions for Internal Events - // OnConnect is called when the websocket connection opens. - OnConnect func(*Session) - // OnDisconnect is called when the websocket connection closes. - // This is a good handler to add reconnection logic to. - OnDisconnect func(*Session) + // Authentication token for this session + Token string - // Settable Callback functions for Websocket Events - OnEvent func(*Session, *Event) - 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{} + // Debug for printing JSON request/responses + Debug bool // Should the session reconnect the websocket on errors. ShouldReconnectOnError bool // Should the session request compressed websocket data. 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. diff --git a/wsapi.go b/wsapi.go index f2d055c..b75c7e0 100644 --- a/wsapi.go +++ b/wsapi.go @@ -18,6 +18,7 @@ import ( "fmt" "io" "net/http" + "reflect" "runtime" "time" @@ -86,9 +87,7 @@ func (s *Session) Open() (err error) { s.Unlock() - if s.OnConnect != nil { - s.OnConnect(s) - } + s.handle(&Connect{}) return } @@ -112,9 +111,7 @@ func (s *Session) Close() (err error) { s.Unlock() - if s.OnDisconnect != nil { - s.OnDisconnect(s) - } + s.handle(&Disconnect{}) return } @@ -247,15 +244,36 @@ func (s *Session) UpdateStatus(idle int, game string) (err error) { return } -// Not sure how needed this is and where it would be best to call it. -// somewhere. - -func unmarshalEvent(event *Event, i interface{}) (err error) { - if err = unmarshal(event.RawData, i); err != nil { - fmt.Println("Unable to unmarshal event data.") - printEvent(event) - } - return +// eventToInterface is a mapping of Discord WSAPI events to their +// DiscordGo event container. +var eventToInterface = map[string]interface{}{ + "CHANNEL_CREATE": ChannelCreate{}, + "CHANNEL_UPDATE": ChannelUpdate{}, + "CHANNEL_DELETE": ChannelDelete{}, + "GUILD_CREATE": GuildCreate{}, + "GUILD_UPDATE": GuildUpdate{}, + "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 @@ -266,6 +284,14 @@ func unmarshalEvent(event *Event, i interface{}) (err error) { // Events will be handled by any implemented handler in Session. // All unhandled events will then be handled by OnEvent. 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 reader io.Reader reader = bytes.NewBuffer(message) @@ -296,406 +322,23 @@ func (s *Session) event(messageType int, message []byte) { printEvent(e) } - switch e.Type { - case "READY": - var st *Ready - if err = unmarshalEvent(e, &st); err == nil { - go s.heartbeat(s.wsConn, s.listening, st.HeartbeatInterval) - if s.StateEnabled { - err := s.State.OnReady(st) - if err != nil { - fmt.Println("error: ", err) - } + i := eventToInterface[e.Type] + if i != nil { + // Create a new instance of the event type. + i = reflect.New(reflect.TypeOf(i)).Interface() - } - if s.OnReady != nil { - s.OnReady(s, st) - } + // Attempt to unmarshal our event. + // If there is an error we should handle the event itself. + if err = unmarshal(e.RawData, i); err != nil { + fmt.Println("Unable to unmarshal event data.") + i = e } - if s.OnReady != nil { - return - } - 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) + } else { + fmt.Println("Unknown event.") + i = e } - // if still here, send to generic OnEvent - if s.OnEvent != nil { - s.OnEvent(s, e) - return - } + s.handle(i) 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 // websocket. This comes immediately after the call to VoiceChannelJoin // 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 // 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. // This event tells us the information needed to open a voice websocket // 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 s.Voice.token = st.Token