From fb6ae92555b6a3a90a926fb2b9b03c067b57c0d1 Mon Sep 17 00:00:00 2001 From: Chris Rhodes Date: Sun, 14 Feb 2016 13:17:20 -0800 Subject: [PATCH] Add basic support for mapped event handlers. --- discord.go | 62 ++++- state.go | 28 ++ structs.go | 2 + wsapi.go | 737 ++++++++++++++++++++++++----------------------------- 4 files changed, 425 insertions(+), 404 deletions(-) diff --git a/discord.go b/discord.go index 68d2a7e..1ad8c7b 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,60 @@ func New(args ...interface{}) (s *Session, err error) { return } + +func (s *Session) AddHandler(handler interface{}) { + 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.") + } + + eventType := handlerType.In(1) + + if s.Handlers == nil { + s.initialize() + } + + handlers := s.Handlers[eventType] + if handlers == nil { + handlers = []interface{}{} + } + + handlers = append(handlers, handler) + s.Handlers[eventType] = handlers +} + +func (s *Session) Handle(event interface{}) (handled bool) { + eventType := reflect.TypeOf(event) + + handlers, ok := s.Handlers[eventType] + if !ok { + return + } + + for _, handler := range handlers { + reflect.ValueOf(handler).Call([]reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}) + handled = true + } + + return +} + +// initialize adds internal handlers such as onEvent and state tracking +// handlers. +func (s *Session) initialize() { + s.Handlers = map[interface{}][]interface{}{} + s.AddHandler(s.ready) + s.AddHandler(s.State.ready) + s.AddHandler(s.State.messageCreate) + s.AddHandler(s.State.messageUpdate) + s.AddHandler(s.State.messageDelete) +} + +func (s *Session) ready(se *Session, r *Ready) { + go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval) +} diff --git a/state.go b/state.go index 0ee8963..189a17e 100644 --- a/state.go +++ b/state.go @@ -468,3 +468,31 @@ func (s *State) Message(channelID, messageID string) (*Message, error) { return nil, errors.New("Message not found.") } + +// ready is an event handler. +func (s *State) ready(se *Session, r *Ready) { + if se.StateEnabled { + s.OnReady(r) + } +} + +// messageCreate is an event handler. +func (s *State) messageCreate(se *Session, m *MessageCreate) { + if se.StateEnabled { + s.MessageAdd(&m.Message) + } +} + +// messageUpdate is an event handler. +func (s *State) messageUpdate(se *Session, m *MessageUpdate) { + if se.StateEnabled { + s.MessageAdd(&m.Message) + } +} + +// messageDelete is an event handler. +func (s *State) messageDelete(se *Session, m *MessageDelete) { + if se.StateEnabled { + s.MessageRemove(&m.Message) + } +} diff --git a/structs.go b/structs.go index 7275ed4..6d5bf55 100644 --- a/structs.go +++ b/structs.go @@ -36,6 +36,8 @@ type Session struct { // This is a good handler to add reconnection logic to. OnDisconnect func(*Session) + Handlers map[interface{}][]interface{} + // Settable Callback functions for Websocket Events OnEvent func(*Session, *Event) OnReady func(*Session, *Ready) diff --git a/wsapi.go b/wsapi.go index f2d055c..ee05872 100644 --- a/wsapi.go +++ b/wsapi.go @@ -247,17 +247,6 @@ 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 -} - // Front line handler for all Websocket Events. Determines the // event type and passes the message along to the next handler. @@ -266,6 +255,10 @@ 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) { + if s.Handlers == nil { + s.initialize() + } + var err error var reader io.Reader reader = bytes.NewBuffer(message) @@ -296,405 +289,343 @@ func (s *Session) event(messageType int, message []byte) { printEvent(e) } + var i interface{} + 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) - } - - } - if s.OnReady != nil { - s.OnReady(s, st) - } - } - 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 { - } - */ + i = &Ready{} 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 - } + i = &MessageCreate{} 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 + i = &MessageUpdate{} 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) + i = &MessageDelete{} + case "PRESENCE_UPDATE": + i = &PresenceUpdate{} + case "TYPING_START": + i = &TypingStart{} } - // if still here, send to generic OnEvent - if s.OnEvent != nil { - s.OnEvent(s, e) - 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 + // } + + // /* Never seen this come in but saw it in another Library. + // case "MESSAGE_ACK": + // if s.OnMessageAck != nil { + // } + // */ + + // 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) + // } + + // Attempt to unmarshal our event. + // If there is an error (eg. we don't know how to handle it) we should handle the event itself. + if err = unmarshal(e.RawData, i); err != nil { + fmt.Println("Unable to unmarshal event data.") + printEvent(e) + i = e + } + + if !s.Handle(i) { + if i != e { + // If there was not a handler for the struct, handle the event, as long as it wasn't the + // event we were trying to handle. + s.Handle(e) + } } return