forked from pothtonswer/discordmuffin
Handler updates, no backwards incompatible API changes.
AddHandler now returns a func that can remove the handler. The handlers map is now guarded by its own mutex. Moved eventMap to events.go for readability. Improved documentation.
This commit is contained in:
parent
f832d3da4a
commit
79247272ff
5 changed files with 137 additions and 60 deletions
73
discord.go
73
discord.go
|
@ -122,11 +122,11 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHandler allows you to add an event handler that will be fired anytime
|
// validateHandler takes an event handler func, and returns the type of event.
|
||||||
// the given event is triggered.
|
// eg.
|
||||||
func (s *Session) AddHandler(handler interface{}) {
|
// Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
|
||||||
s.initialize()
|
// will return the reflect.Type of *discordgo.MessageCreate
|
||||||
|
func (s *Session) validateHandler(handler interface{}) reflect.Type {
|
||||||
handlerType := reflect.TypeOf(handler)
|
handlerType := reflect.TypeOf(handler)
|
||||||
|
|
||||||
if handlerType.NumIn() != 2 {
|
if handlerType.NumIn() != 2 {
|
||||||
|
@ -137,9 +137,6 @@ func (s *Session) AddHandler(handler interface{}) {
|
||||||
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
|
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
eventType := handlerType.In(1)
|
eventType := handlerType.In(1)
|
||||||
|
|
||||||
// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
|
// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
|
||||||
|
@ -147,18 +144,62 @@ func (s *Session) AddHandler(handler interface{}) {
|
||||||
eventType = nil
|
eventType = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return eventType
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHandler allows you to add an event handler that will be fired anytime
|
||||||
|
// the Discord WSAPI event that matches the interface fires.
|
||||||
|
// eventToInterface in events.go has a list of all the Discord WSAPI events
|
||||||
|
// and their respective interface.
|
||||||
|
// eg:
|
||||||
|
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// or:
|
||||||
|
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
|
||||||
|
// })
|
||||||
|
// The return value of this method is a function, that when called will remove the
|
||||||
|
// event handler.
|
||||||
|
func (s *Session) AddHandler(handler interface{}) func() {
|
||||||
|
s.initialize()
|
||||||
|
|
||||||
|
eventType := s.validateHandler(handler)
|
||||||
|
|
||||||
|
s.handlersMu.Lock()
|
||||||
|
defer s.handlersMu.Unlock()
|
||||||
|
|
||||||
|
h := reflect.ValueOf(handler)
|
||||||
|
|
||||||
handlers := s.handlers[eventType]
|
handlers := s.handlers[eventType]
|
||||||
if handlers == nil {
|
if handlers == nil {
|
||||||
handlers = []reflect.Value{}
|
handlers = []reflect.Value{}
|
||||||
}
|
}
|
||||||
s.handlers[eventType] = append(handlers, reflect.ValueOf(handler))
|
s.handlers[eventType] = append(handlers, h)
|
||||||
|
|
||||||
|
// This must be done as we need a consistent reference to the
|
||||||
|
// reflected value, otherwise a RemoveHandler method would have
|
||||||
|
// been nice.
|
||||||
|
return func() {
|
||||||
|
s.handlersMu.Lock()
|
||||||
|
defer s.handlersMu.Unlock()
|
||||||
|
|
||||||
|
handlers := s.handlers[eventType]
|
||||||
|
for i, v := range handlers {
|
||||||
|
if h == v {
|
||||||
|
s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle calls any handlers that match the event type and any handlers of
|
||||||
|
// interface{}.
|
||||||
func (s *Session) handle(event interface{}) {
|
func (s *Session) handle(event interface{}) {
|
||||||
s.initialize()
|
s.initialize()
|
||||||
|
|
||||||
s.RLock()
|
s.handlersMu.RLock()
|
||||||
defer s.RUnlock()
|
defer s.handlersMu.RUnlock()
|
||||||
|
|
||||||
handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}
|
handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}
|
||||||
|
|
||||||
|
@ -177,16 +218,16 @@ func (s *Session) handle(event interface{}) {
|
||||||
|
|
||||||
// initialize adds all internal handlers and state tracking handlers.
|
// initialize adds all internal handlers and state tracking handlers.
|
||||||
func (s *Session) initialize() {
|
func (s *Session) initialize() {
|
||||||
s.RLock()
|
s.handlersMu.RLock()
|
||||||
if s.handlers != nil {
|
if s.handlers != nil {
|
||||||
s.RUnlock()
|
s.handlersMu.RUnlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.RUnlock()
|
s.handlersMu.RUnlock()
|
||||||
|
|
||||||
s.Lock()
|
s.handlersMu.Lock()
|
||||||
s.handlers = map[interface{}][]reflect.Value{}
|
s.handlers = map[interface{}][]reflect.Value{}
|
||||||
s.Unlock()
|
s.handlersMu.Unlock()
|
||||||
|
|
||||||
s.AddHandler(s.onEvent)
|
s.AddHandler(s.onEvent)
|
||||||
s.AddHandler(s.onReady)
|
s.AddHandler(s.onReady)
|
||||||
|
|
|
@ -225,15 +225,15 @@ func TestOpenClose(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandlers(t *testing.T) {
|
func TestAddHandler(t *testing.T) {
|
||||||
testHandlerCalled := false
|
testHandlerCalled := 0
|
||||||
testHandler := func(s *Session, t *testing.T) {
|
testHandler := func(s *Session, m *MessageCreate) {
|
||||||
testHandlerCalled = true
|
testHandlerCalled++
|
||||||
}
|
}
|
||||||
|
|
||||||
interfaceHandlerCalled := false
|
interfaceHandlerCalled := 0
|
||||||
interfaceHandler := func(s *Session, i interface{}) {
|
interfaceHandler := func(s *Session, i interface{}) {
|
||||||
interfaceHandlerCalled = true
|
interfaceHandlerCalled++
|
||||||
}
|
}
|
||||||
|
|
||||||
bogusHandlerCalled := false
|
bogusHandlerCalled := false
|
||||||
|
@ -243,20 +243,45 @@ func TestHandlers(t *testing.T) {
|
||||||
|
|
||||||
d := Session{}
|
d := Session{}
|
||||||
d.AddHandler(testHandler)
|
d.AddHandler(testHandler)
|
||||||
|
d.AddHandler(testHandler)
|
||||||
|
|
||||||
d.AddHandler(interfaceHandler)
|
d.AddHandler(interfaceHandler)
|
||||||
d.AddHandler(bogusHandler)
|
d.AddHandler(bogusHandler)
|
||||||
|
|
||||||
d.handle(t)
|
d.handle(&MessageCreate{})
|
||||||
|
d.handle(&MessageDelete{})
|
||||||
|
|
||||||
if !testHandlerCalled {
|
// testHandler will be called twice because it was added twice.
|
||||||
t.Fatalf("testHandler was not called.")
|
if testHandlerCalled != 2 {
|
||||||
|
t.Fatalf("testHandler was not called twice.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !interfaceHandlerCalled {
|
// interfaceHandler will be called twice for each event.
|
||||||
t.Fatalf("interfaceHandler was not called.")
|
if interfaceHandlerCalled != 2 {
|
||||||
|
t.Fatalf("interfaceHandler was not called twice.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if bogusHandlerCalled {
|
if bogusHandlerCalled {
|
||||||
t.Fatalf("bogusHandler was called.")
|
t.Fatalf("bogusHandler was called.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoveHandler(t *testing.T) {
|
||||||
|
testHandlerCalled := 0
|
||||||
|
testHandler := func(s *Session, m *MessageCreate) {
|
||||||
|
testHandlerCalled++
|
||||||
|
}
|
||||||
|
|
||||||
|
d := Session{}
|
||||||
|
r := d.AddHandler(testHandler)
|
||||||
|
|
||||||
|
d.handle(&MessageCreate{})
|
||||||
|
|
||||||
|
r()
|
||||||
|
|
||||||
|
d.handle(&MessageCreate{})
|
||||||
|
|
||||||
|
if testHandlerCalled != 1 {
|
||||||
|
t.Fatalf("testHandler was not called once.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
42
events.go
42
events.go
|
@ -1,5 +1,47 @@
|
||||||
package discordgo
|
package discordgo
|
||||||
|
|
||||||
|
// eventToInterface is a mapping of Discord WSAPI events to their
|
||||||
|
// DiscordGo event container.
|
||||||
|
// Each Discord WSAPI event maps to a unique interface.
|
||||||
|
// Use Session.AddHandler with one of these types to handle that
|
||||||
|
// type of event.
|
||||||
|
// eg:
|
||||||
|
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// or:
|
||||||
|
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
|
||||||
|
// })
|
||||||
|
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{},
|
||||||
|
}
|
||||||
|
|
||||||
// Connect is an empty struct for an event.
|
// Connect is an empty struct for an event.
|
||||||
type Connect struct{}
|
type Connect struct{}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,8 @@ type Session struct {
|
||||||
// StateEnabled is true.
|
// StateEnabled is true.
|
||||||
State *State
|
State *State
|
||||||
|
|
||||||
// This is a mapping of event structs to a reflected value
|
handlersMu sync.RWMutex
|
||||||
|
// This is a mapping of event struct to a reflected value
|
||||||
// for event handlers.
|
// for event handlers.
|
||||||
// We store the reflected value instead of the function
|
// We store the reflected value instead of the function
|
||||||
// reference as it is more performant, instead of re-reflecting
|
// reference as it is more performant, instead of re-reflecting
|
||||||
|
|
32
wsapi.go
32
wsapi.go
|
@ -245,38 +245,6 @@ func (s *Session) UpdateStatus(idle int, game string) (err error) {
|
||||||
return
|
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
|
// Front line handler for all Websocket Events. Determines the
|
||||||
// event type and passes the message along to the next handler.
|
// event type and passes the message along to the next handler.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue