Browse Source

Merge branch 'develop'

Chris Rhodes 8 years ago
parent
commit
002b6b91ca
21 changed files with 2905 additions and 568 deletions
  1. 1 1
      .travis.yml
  2. 4 138
      discord.go
  3. 4 4
      discord_test.go
  4. 18 0
      endpoints.go
  5. 238 0
      event.go
  6. 977 0
      eventhandlers.go
  7. 159 80
      events.go
  8. 5 1
      examples/airhorn/main.go
  9. 75 25
      message.go
  10. 7 6
      oauth2.go
  11. 157 0
      ratelimit.go
  12. 112 0
      ratelimit_test.go
  13. 584 119
      restapi.go
  14. 47 0
      restapi_test.go
  15. 86 38
      state.go
  16. 161 113
      structs.go
  17. 123 0
      tools/cmd/eventhandlers/main.go
  18. 58 0
      types.go
  19. 24 0
      types_test.go
  20. 2 2
      voice.go
  21. 63 41
      wsapi.go

+ 1 - 1
.travis.yml

@@ -1,7 +1,7 @@
 language: go
 go:
     - 1.6
-    - tip
+    - 1.7
 install:
     - go get github.com/bwmarrin/discordgo
     - go get -v .

+ 4 - 138
discord.go

@@ -13,13 +13,10 @@
 // Package discordgo provides Discord binding for Go
 package discordgo
 
-import (
-	"fmt"
-	"reflect"
-)
+import "fmt"
 
 // VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
-const VERSION = "0.13.0"
+const VERSION = "0.14.0-dev"
 
 // New creates a new Discord session and will automate some startup
 // tasks if given enough information to do so.  Currently you can pass zero
@@ -39,11 +36,13 @@ func New(args ...interface{}) (s *Session, err error) {
 	// Create an empty Session interface.
 	s = &Session{
 		State:                  NewState(),
+		ratelimiter:            NewRatelimiter(),
 		StateEnabled:           true,
 		Compress:               true,
 		ShouldReconnectOnError: true,
 		ShardID:                0,
 		ShardCount:             1,
+		MaxRestRetries:         3,
 	}
 
 	// If no arguments are passed return the empty Session interface.
@@ -124,136 +123,3 @@ func New(args ...interface{}) (s *Session, err error) {
 
 	return
 }
-
-// validateHandler takes an event handler func, and returns the type of event.
-// eg.
-//     Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
-//     will return the reflect.Type of *discordgo.MessageCreate
-func (s *Session) validateHandler(handler interface{}) reflect.Type {
-
-	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)
-
-	// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
-	if eventType.Kind() == reflect.Interface {
-		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)
-
-	s.handlers[eventType] = append(s.handlers[eventType], 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{}) {
-
-	s.handlersMu.RLock()
-	defer s.handlersMu.RUnlock()
-
-	if s.handlers == nil {
-		return
-	}
-
-	handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}
-
-	if handlers, ok := s.handlers[nil]; ok {
-		for _, handler := range handlers {
-			go handler.Call(handlerParameters)
-		}
-	}
-
-	if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
-		for _, handler := range handlers {
-			go handler.Call(handlerParameters)
-		}
-	}
-}
-
-// initialize adds all internal handlers and state tracking handlers.
-func (s *Session) initialize() {
-
-	s.log(LogInformational, "called")
-
-	s.handlersMu.Lock()
-	if s.handlers != nil {
-		s.handlersMu.Unlock()
-		return
-	}
-
-	s.handlers = map[interface{}][]reflect.Value{}
-	s.handlersMu.Unlock()
-
-	s.AddHandler(s.onReady)
-	s.AddHandler(s.onResumed)
-	s.AddHandler(s.onVoiceServerUpdate)
-	s.AddHandler(s.onVoiceStateUpdate)
-	s.AddHandler(s.State.onInterface)
-}
-
-// onReady handles the ready event.
-func (s *Session) onReady(se *Session, r *Ready) {
-
-	// Store the SessionID within the Session struct.
-	s.sessionID = r.SessionID
-
-	// Start the heartbeat to keep the connection alive.
-	go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
-}
-
-// onResumed handles the resumed event.
-func (s *Session) onResumed(se *Session, r *Resumed) {
-
-	// Start the heartbeat to keep the connection alive.
-	go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
-}

+ 4 - 4
discord_test.go

@@ -223,8 +223,8 @@ func TestAddHandler(t *testing.T) {
 	d.AddHandler(interfaceHandler)
 	d.AddHandler(bogusHandler)
 
-	d.handle(&MessageCreate{})
-	d.handle(&MessageDelete{})
+	d.handleEvent(messageCreateEventType, &MessageCreate{})
+	d.handleEvent(messageDeleteEventType, &MessageDelete{})
 
 	<-time.After(500 * time.Millisecond)
 
@@ -253,11 +253,11 @@ func TestRemoveHandler(t *testing.T) {
 	d := Session{}
 	r := d.AddHandler(testHandler)
 
-	d.handle(&MessageCreate{})
+	d.handleEvent(messageCreateEventType, &MessageCreate{})
 
 	r()
 
-	d.handle(&MessageCreate{})
+	d.handleEvent(messageCreateEventType, &MessageCreate{})
 
 	<-time.After(500 * time.Millisecond)
 

+ 18 - 0
endpoints.go

@@ -24,6 +24,7 @@ var (
 	EndpointChannels = EndpointAPI + "channels/"
 	EndpointUsers    = EndpointAPI + "users/"
 	EndpointGateway  = EndpointAPI + "gateway"
+	EndpointWebhooks = EndpointAPI + "webhooks/"
 
 	EndpointAuth           = EndpointAPI + "auth/"
 	EndpointLogin          = EndpointAuth + "login"
@@ -61,6 +62,7 @@ var (
 	EndpointGuildChannels        = func(gID string) string { return EndpointGuilds + gID + "/channels" }
 	EndpointGuildMembers         = func(gID string) string { return EndpointGuilds + gID + "/members" }
 	EndpointGuildMember          = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
+	EndpointGuildMemberRole      = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID }
 	EndpointGuildBans            = func(gID string) string { return EndpointGuilds + gID + "/bans" }
 	EndpointGuildBan             = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
 	EndpointGuildIntegrations    = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
@@ -73,6 +75,7 @@ var (
 	EndpointGuildPrune           = func(gID string) string { return EndpointGuilds + gID + "/prune" }
 	EndpointGuildIcon            = func(gID, hash string) string { return EndpointGuilds + gID + "/icons/" + hash + ".jpg" }
 	EndpointGuildSplash          = func(gID, hash string) string { return EndpointGuilds + gID + "/splashes/" + hash + ".jpg" }
+	EndpointGuildWebhooks        = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }
 
 	EndpointChannel                   = func(cID string) string { return EndpointChannels + cID }
 	EndpointChannelPermissions        = func(cID string) string { return EndpointChannels + cID + "/permissions" }
@@ -86,6 +89,21 @@ var (
 	EndpointChannelMessagesPins       = func(cID string) string { return EndpointChannel(cID) + "/pins" }
 	EndpointChannelMessagePin         = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
 
+	EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
+	EndpointWebhook         = func(wID string) string { return EndpointWebhooks + wID }
+	EndpointWebhookToken    = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
+
+	EndpointMessageReactions = func(cID, mID, eID string) string {
+		return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
+	}
+	EndpointMessageReaction = func(cID, mID, eID, uID string) string {
+		return EndpointMessageReactions(cID, mID, eID) + "/" + uID
+	}
+
+	EndpointRelationships       = func() string { return EndpointUsers + "@me" + "/relationships" }
+	EndpointRelationship        = func(uID string) string { return EndpointRelationships() + "/" + uID }
+	EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
+
 	EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
 
 	EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }

+ 238 - 0
event.go

@@ -0,0 +1,238 @@
+package discordgo
+
+import "fmt"
+
+// EventHandler is an interface for Discord events.
+type EventHandler interface {
+	// Type returns the type of event this handler belongs to.
+	Type() string
+
+	// Handle is called whenever an event of Type() happens.
+	// It is the recievers responsibility to type assert that the interface
+	// is the expected struct.
+	Handle(*Session, interface{})
+}
+
+// EventInterfaceProvider is an interface for providing empty interfaces for
+// Discord events.
+type EventInterfaceProvider interface {
+	// Type is the type of event this handler belongs to.
+	Type() string
+
+	// New returns a new instance of the struct this event handler handles.
+	// This is called once per event.
+	// The struct is provided to all handlers of the same Type().
+	New() interface{}
+}
+
+// interfaceEventType is the event handler type for interface{} events.
+const interfaceEventType = "__INTERFACE__"
+
+// interfaceEventHandler is an event handler for interface{} events.
+type interfaceEventHandler func(*Session, interface{})
+
+// Type returns the event type for interface{} events.
+func (eh interfaceEventHandler) Type() string {
+	return interfaceEventType
+}
+
+// Handle is the handler for an interface{} event.
+func (eh interfaceEventHandler) Handle(s *Session, i interface{}) {
+	eh(s, i)
+}
+
+var registeredInterfaceProviders = map[string]EventInterfaceProvider{}
+
+// registerInterfaceProvider registers a provider so that DiscordGo can
+// access it's New() method.
+func registerInterfaceProvider(eh EventInterfaceProvider) error {
+	if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
+		return fmt.Errorf("event %s already registered", eh.Type())
+	}
+	registeredInterfaceProviders[eh.Type()] = eh
+	return nil
+}
+
+// eventHandlerInstance is a wrapper around an event handler, as functions
+// cannot be compared directly.
+type eventHandlerInstance struct {
+	eventHandler EventHandler
+}
+
+// addEventHandler adds an event handler that will be fired anytime
+// the Discord WSAPI matching eventHandler.Type() fires.
+func (s *Session) addEventHandler(eventHandler EventHandler) func() {
+	s.handlersMu.Lock()
+	defer s.handlersMu.Unlock()
+
+	if s.handlers == nil {
+		s.handlers = map[string][]*eventHandlerInstance{}
+	}
+
+	ehi := &eventHandlerInstance{eventHandler}
+	s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi)
+
+	return func() {
+		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
+	}
+}
+
+// addEventHandler adds an event handler that will be fired the next time
+// the Discord WSAPI matching eventHandler.Type() fires.
+func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
+	s.handlersMu.Lock()
+	defer s.handlersMu.Unlock()
+
+	if s.onceHandlers == nil {
+		s.onceHandlers = map[string][]*eventHandlerInstance{}
+	}
+
+	ehi := &eventHandlerInstance{eventHandler}
+	s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi)
+
+	return func() {
+		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
+	}
+}
+
+// AddHandler allows you to add an event handler that will be fired anytime
+// the Discord WSAPI event that matches the function fires.
+// events.go contains all the Discord WSAPI events that can be fired.
+// 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() {
+	eh := handlerForInterface(handler)
+
+	if eh == nil {
+		s.log(LogError, "Invalid handler type, handler will never be called")
+		return func() {}
+	}
+
+	return s.addEventHandler(eh)
+}
+
+// AddHandlerOnce allows you to add an event handler that will be fired the next time
+// the Discord WSAPI event that matches the function fires.
+// See AddHandler for more details.
+func (s *Session) AddHandlerOnce(handler interface{}) func() {
+	eh := handlerForInterface(handler)
+
+	if eh == nil {
+		s.log(LogError, "Invalid handler type, handler will never be called")
+		return func() {}
+	}
+
+	return s.addEventHandlerOnce(eh)
+}
+
+// removeEventHandler instance removes an event handler instance.
+func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
+	s.handlersMu.Lock()
+	defer s.handlersMu.Unlock()
+
+	handlers := s.handlers[t]
+	for i := range handlers {
+		if handlers[i] == ehi {
+			s.handlers[t] = append(handlers[:i], handlers[i+1:]...)
+		}
+	}
+
+	onceHandlers := s.onceHandlers[t]
+	for i := range onceHandlers {
+		if onceHandlers[i] == ehi {
+			s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
+		}
+	}
+}
+
+// Handles calling permanent and once handlers for an event type.
+func (s *Session) handle(t string, i interface{}) {
+	for _, eh := range s.handlers[t] {
+		go eh.eventHandler.Handle(s, i)
+	}
+
+	if len(s.onceHandlers[t]) > 0 {
+		for _, eh := range s.onceHandlers[t] {
+			go eh.eventHandler.Handle(s, i)
+		}
+		s.onceHandlers[t] = nil
+	}
+}
+
+// Handles an event type by calling internal methods, firing handlers and firing the
+// interface{} event.
+func (s *Session) handleEvent(t string, i interface{}) {
+	s.handlersMu.RLock()
+	defer s.handlersMu.RUnlock()
+
+	// All events are dispatched internally first.
+	s.onInterface(i)
+
+	// Then they are dispatched to anyone handling interface{} events.
+	s.handle(interfaceEventType, i)
+
+	// Finally they are dispatched to any typed handlers.
+	s.handle(t, i)
+}
+
+// setGuildIds will set the GuildID on all the members of a guild.
+// This is done as event data does not have it set.
+func setGuildIds(g *Guild) {
+	for _, c := range g.Channels {
+		c.GuildID = g.ID
+	}
+
+	for _, m := range g.Members {
+		m.GuildID = g.ID
+	}
+
+	for _, vs := range g.VoiceStates {
+		vs.GuildID = g.ID
+	}
+}
+
+// onInterface handles all internal events and routes them to the appropriate internal handler.
+func (s *Session) onInterface(i interface{}) {
+	switch t := i.(type) {
+	case *Ready:
+		for _, g := range t.Guilds {
+			setGuildIds(g)
+		}
+		s.onReady(t)
+	case *GuildCreate:
+		setGuildIds(t.Guild)
+	case *GuildUpdate:
+		setGuildIds(t.Guild)
+	case *Resumed:
+		s.onResumed(t)
+	case *VoiceServerUpdate:
+		go s.onVoiceServerUpdate(t)
+	case *VoiceStateUpdate:
+		go s.onVoiceStateUpdate(t)
+	}
+	s.State.onInterface(s, i)
+}
+
+// onReady handles the ready event.
+func (s *Session) onReady(r *Ready) {
+
+	// Store the SessionID within the Session struct.
+	s.sessionID = r.SessionID
+
+	// Start the heartbeat to keep the connection alive.
+	go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
+}
+
+// onResumed handles the resumed event.
+func (s *Session) onResumed(r *Resumed) {
+
+	// Start the heartbeat to keep the connection alive.
+	go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
+}

+ 977 - 0
eventhandlers.go

@@ -0,0 +1,977 @@
+// Code generated by \"eventhandlers\"; DO NOT EDIT
+// See events.go
+
+package discordgo
+
+// Following are all the event types.
+// Event type values are used to match the events returned by Discord.
+// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
+const (
+	channelCreateEventType           = "CHANNEL_CREATE"
+	channelDeleteEventType           = "CHANNEL_DELETE"
+	channelPinsUpdateEventType       = "CHANNEL_PINS_UPDATE"
+	channelUpdateEventType           = "CHANNEL_UPDATE"
+	connectEventType                 = "__CONNECT__"
+	disconnectEventType              = "__DISCONNECT__"
+	eventEventType                   = "__EVENT__"
+	guildBanAddEventType             = "GUILD_BAN_ADD"
+	guildBanRemoveEventType          = "GUILD_BAN_REMOVE"
+	guildCreateEventType             = "GUILD_CREATE"
+	guildDeleteEventType             = "GUILD_DELETE"
+	guildEmojisUpdateEventType       = "GUILD_EMOJIS_UPDATE"
+	guildIntegrationsUpdateEventType = "GUILD_INTEGRATIONS_UPDATE"
+	guildMemberAddEventType          = "GUILD_MEMBER_ADD"
+	guildMemberRemoveEventType       = "GUILD_MEMBER_REMOVE"
+	guildMemberUpdateEventType       = "GUILD_MEMBER_UPDATE"
+	guildMembersChunkEventType       = "GUILD_MEMBERS_CHUNK"
+	guildRoleCreateEventType         = "GUILD_ROLE_CREATE"
+	guildRoleDeleteEventType         = "GUILD_ROLE_DELETE"
+	guildRoleUpdateEventType         = "GUILD_ROLE_UPDATE"
+	guildUpdateEventType             = "GUILD_UPDATE"
+	messageAckEventType              = "MESSAGE_ACK"
+	messageCreateEventType           = "MESSAGE_CREATE"
+	messageDeleteEventType           = "MESSAGE_DELETE"
+	messageReactionAddEventType      = "MESSAGE_REACTION_ADD"
+	messageReactionRemoveEventType   = "MESSAGE_REACTION_REMOVE"
+	messageUpdateEventType           = "MESSAGE_UPDATE"
+	presenceUpdateEventType          = "PRESENCE_UPDATE"
+	presencesReplaceEventType        = "PRESENCES_REPLACE"
+	rateLimitEventType               = "__RATE_LIMIT__"
+	readyEventType                   = "READY"
+	relationshipAddEventType         = "RELATIONSHIP_ADD"
+	relationshipRemoveEventType      = "RELATIONSHIP_REMOVE"
+	resumedEventType                 = "RESUMED"
+	typingStartEventType             = "TYPING_START"
+	userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE"
+	userSettingsUpdateEventType      = "USER_SETTINGS_UPDATE"
+	userUpdateEventType              = "USER_UPDATE"
+	voiceServerUpdateEventType       = "VOICE_SERVER_UPDATE"
+	voiceStateUpdateEventType        = "VOICE_STATE_UPDATE"
+)
+
+// channelCreateEventHandler is an event handler for ChannelCreate events.
+type channelCreateEventHandler func(*Session, *ChannelCreate)
+
+// Type returns the event type for ChannelCreate events.
+func (eh channelCreateEventHandler) Type() string {
+	return channelCreateEventType
+}
+
+// New returns a new instance of ChannelCreate.
+func (eh channelCreateEventHandler) New() interface{} {
+	return &ChannelCreate{}
+}
+
+// Handle is the handler for ChannelCreate events.
+func (eh channelCreateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*ChannelCreate); ok {
+		eh(s, t)
+	}
+}
+
+// channelDeleteEventHandler is an event handler for ChannelDelete events.
+type channelDeleteEventHandler func(*Session, *ChannelDelete)
+
+// Type returns the event type for ChannelDelete events.
+func (eh channelDeleteEventHandler) Type() string {
+	return channelDeleteEventType
+}
+
+// New returns a new instance of ChannelDelete.
+func (eh channelDeleteEventHandler) New() interface{} {
+	return &ChannelDelete{}
+}
+
+// Handle is the handler for ChannelDelete events.
+func (eh channelDeleteEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*ChannelDelete); ok {
+		eh(s, t)
+	}
+}
+
+// channelPinsUpdateEventHandler is an event handler for ChannelPinsUpdate events.
+type channelPinsUpdateEventHandler func(*Session, *ChannelPinsUpdate)
+
+// Type returns the event type for ChannelPinsUpdate events.
+func (eh channelPinsUpdateEventHandler) Type() string {
+	return channelPinsUpdateEventType
+}
+
+// New returns a new instance of ChannelPinsUpdate.
+func (eh channelPinsUpdateEventHandler) New() interface{} {
+	return &ChannelPinsUpdate{}
+}
+
+// Handle is the handler for ChannelPinsUpdate events.
+func (eh channelPinsUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*ChannelPinsUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// channelUpdateEventHandler is an event handler for ChannelUpdate events.
+type channelUpdateEventHandler func(*Session, *ChannelUpdate)
+
+// Type returns the event type for ChannelUpdate events.
+func (eh channelUpdateEventHandler) Type() string {
+	return channelUpdateEventType
+}
+
+// New returns a new instance of ChannelUpdate.
+func (eh channelUpdateEventHandler) New() interface{} {
+	return &ChannelUpdate{}
+}
+
+// Handle is the handler for ChannelUpdate events.
+func (eh channelUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*ChannelUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// connectEventHandler is an event handler for Connect events.
+type connectEventHandler func(*Session, *Connect)
+
+// Type returns the event type for Connect events.
+func (eh connectEventHandler) Type() string {
+	return connectEventType
+}
+
+// New returns a new instance of Connect.
+func (eh connectEventHandler) New() interface{} {
+	return &Connect{}
+}
+
+// Handle is the handler for Connect events.
+func (eh connectEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*Connect); ok {
+		eh(s, t)
+	}
+}
+
+// disconnectEventHandler is an event handler for Disconnect events.
+type disconnectEventHandler func(*Session, *Disconnect)
+
+// Type returns the event type for Disconnect events.
+func (eh disconnectEventHandler) Type() string {
+	return disconnectEventType
+}
+
+// New returns a new instance of Disconnect.
+func (eh disconnectEventHandler) New() interface{} {
+	return &Disconnect{}
+}
+
+// Handle is the handler for Disconnect events.
+func (eh disconnectEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*Disconnect); ok {
+		eh(s, t)
+	}
+}
+
+// eventEventHandler is an event handler for Event events.
+type eventEventHandler func(*Session, *Event)
+
+// Type returns the event type for Event events.
+func (eh eventEventHandler) Type() string {
+	return eventEventType
+}
+
+// New returns a new instance of Event.
+func (eh eventEventHandler) New() interface{} {
+	return &Event{}
+}
+
+// Handle is the handler for Event events.
+func (eh eventEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*Event); ok {
+		eh(s, t)
+	}
+}
+
+// guildBanAddEventHandler is an event handler for GuildBanAdd events.
+type guildBanAddEventHandler func(*Session, *GuildBanAdd)
+
+// Type returns the event type for GuildBanAdd events.
+func (eh guildBanAddEventHandler) Type() string {
+	return guildBanAddEventType
+}
+
+// New returns a new instance of GuildBanAdd.
+func (eh guildBanAddEventHandler) New() interface{} {
+	return &GuildBanAdd{}
+}
+
+// Handle is the handler for GuildBanAdd events.
+func (eh guildBanAddEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildBanAdd); ok {
+		eh(s, t)
+	}
+}
+
+// guildBanRemoveEventHandler is an event handler for GuildBanRemove events.
+type guildBanRemoveEventHandler func(*Session, *GuildBanRemove)
+
+// Type returns the event type for GuildBanRemove events.
+func (eh guildBanRemoveEventHandler) Type() string {
+	return guildBanRemoveEventType
+}
+
+// New returns a new instance of GuildBanRemove.
+func (eh guildBanRemoveEventHandler) New() interface{} {
+	return &GuildBanRemove{}
+}
+
+// Handle is the handler for GuildBanRemove events.
+func (eh guildBanRemoveEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildBanRemove); ok {
+		eh(s, t)
+	}
+}
+
+// guildCreateEventHandler is an event handler for GuildCreate events.
+type guildCreateEventHandler func(*Session, *GuildCreate)
+
+// Type returns the event type for GuildCreate events.
+func (eh guildCreateEventHandler) Type() string {
+	return guildCreateEventType
+}
+
+// New returns a new instance of GuildCreate.
+func (eh guildCreateEventHandler) New() interface{} {
+	return &GuildCreate{}
+}
+
+// Handle is the handler for GuildCreate events.
+func (eh guildCreateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildCreate); ok {
+		eh(s, t)
+	}
+}
+
+// guildDeleteEventHandler is an event handler for GuildDelete events.
+type guildDeleteEventHandler func(*Session, *GuildDelete)
+
+// Type returns the event type for GuildDelete events.
+func (eh guildDeleteEventHandler) Type() string {
+	return guildDeleteEventType
+}
+
+// New returns a new instance of GuildDelete.
+func (eh guildDeleteEventHandler) New() interface{} {
+	return &GuildDelete{}
+}
+
+// Handle is the handler for GuildDelete events.
+func (eh guildDeleteEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildDelete); ok {
+		eh(s, t)
+	}
+}
+
+// guildEmojisUpdateEventHandler is an event handler for GuildEmojisUpdate events.
+type guildEmojisUpdateEventHandler func(*Session, *GuildEmojisUpdate)
+
+// Type returns the event type for GuildEmojisUpdate events.
+func (eh guildEmojisUpdateEventHandler) Type() string {
+	return guildEmojisUpdateEventType
+}
+
+// New returns a new instance of GuildEmojisUpdate.
+func (eh guildEmojisUpdateEventHandler) New() interface{} {
+	return &GuildEmojisUpdate{}
+}
+
+// Handle is the handler for GuildEmojisUpdate events.
+func (eh guildEmojisUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildEmojisUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// guildIntegrationsUpdateEventHandler is an event handler for GuildIntegrationsUpdate events.
+type guildIntegrationsUpdateEventHandler func(*Session, *GuildIntegrationsUpdate)
+
+// Type returns the event type for GuildIntegrationsUpdate events.
+func (eh guildIntegrationsUpdateEventHandler) Type() string {
+	return guildIntegrationsUpdateEventType
+}
+
+// New returns a new instance of GuildIntegrationsUpdate.
+func (eh guildIntegrationsUpdateEventHandler) New() interface{} {
+	return &GuildIntegrationsUpdate{}
+}
+
+// Handle is the handler for GuildIntegrationsUpdate events.
+func (eh guildIntegrationsUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildIntegrationsUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// guildMemberAddEventHandler is an event handler for GuildMemberAdd events.
+type guildMemberAddEventHandler func(*Session, *GuildMemberAdd)
+
+// Type returns the event type for GuildMemberAdd events.
+func (eh guildMemberAddEventHandler) Type() string {
+	return guildMemberAddEventType
+}
+
+// New returns a new instance of GuildMemberAdd.
+func (eh guildMemberAddEventHandler) New() interface{} {
+	return &GuildMemberAdd{}
+}
+
+// Handle is the handler for GuildMemberAdd events.
+func (eh guildMemberAddEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildMemberAdd); ok {
+		eh(s, t)
+	}
+}
+
+// guildMemberRemoveEventHandler is an event handler for GuildMemberRemove events.
+type guildMemberRemoveEventHandler func(*Session, *GuildMemberRemove)
+
+// Type returns the event type for GuildMemberRemove events.
+func (eh guildMemberRemoveEventHandler) Type() string {
+	return guildMemberRemoveEventType
+}
+
+// New returns a new instance of GuildMemberRemove.
+func (eh guildMemberRemoveEventHandler) New() interface{} {
+	return &GuildMemberRemove{}
+}
+
+// Handle is the handler for GuildMemberRemove events.
+func (eh guildMemberRemoveEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildMemberRemove); ok {
+		eh(s, t)
+	}
+}
+
+// guildMemberUpdateEventHandler is an event handler for GuildMemberUpdate events.
+type guildMemberUpdateEventHandler func(*Session, *GuildMemberUpdate)
+
+// Type returns the event type for GuildMemberUpdate events.
+func (eh guildMemberUpdateEventHandler) Type() string {
+	return guildMemberUpdateEventType
+}
+
+// New returns a new instance of GuildMemberUpdate.
+func (eh guildMemberUpdateEventHandler) New() interface{} {
+	return &GuildMemberUpdate{}
+}
+
+// Handle is the handler for GuildMemberUpdate events.
+func (eh guildMemberUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildMemberUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// guildMembersChunkEventHandler is an event handler for GuildMembersChunk events.
+type guildMembersChunkEventHandler func(*Session, *GuildMembersChunk)
+
+// Type returns the event type for GuildMembersChunk events.
+func (eh guildMembersChunkEventHandler) Type() string {
+	return guildMembersChunkEventType
+}
+
+// New returns a new instance of GuildMembersChunk.
+func (eh guildMembersChunkEventHandler) New() interface{} {
+	return &GuildMembersChunk{}
+}
+
+// Handle is the handler for GuildMembersChunk events.
+func (eh guildMembersChunkEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildMembersChunk); ok {
+		eh(s, t)
+	}
+}
+
+// guildRoleCreateEventHandler is an event handler for GuildRoleCreate events.
+type guildRoleCreateEventHandler func(*Session, *GuildRoleCreate)
+
+// Type returns the event type for GuildRoleCreate events.
+func (eh guildRoleCreateEventHandler) Type() string {
+	return guildRoleCreateEventType
+}
+
+// New returns a new instance of GuildRoleCreate.
+func (eh guildRoleCreateEventHandler) New() interface{} {
+	return &GuildRoleCreate{}
+}
+
+// Handle is the handler for GuildRoleCreate events.
+func (eh guildRoleCreateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildRoleCreate); ok {
+		eh(s, t)
+	}
+}
+
+// guildRoleDeleteEventHandler is an event handler for GuildRoleDelete events.
+type guildRoleDeleteEventHandler func(*Session, *GuildRoleDelete)
+
+// Type returns the event type for GuildRoleDelete events.
+func (eh guildRoleDeleteEventHandler) Type() string {
+	return guildRoleDeleteEventType
+}
+
+// New returns a new instance of GuildRoleDelete.
+func (eh guildRoleDeleteEventHandler) New() interface{} {
+	return &GuildRoleDelete{}
+}
+
+// Handle is the handler for GuildRoleDelete events.
+func (eh guildRoleDeleteEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildRoleDelete); ok {
+		eh(s, t)
+	}
+}
+
+// guildRoleUpdateEventHandler is an event handler for GuildRoleUpdate events.
+type guildRoleUpdateEventHandler func(*Session, *GuildRoleUpdate)
+
+// Type returns the event type for GuildRoleUpdate events.
+func (eh guildRoleUpdateEventHandler) Type() string {
+	return guildRoleUpdateEventType
+}
+
+// New returns a new instance of GuildRoleUpdate.
+func (eh guildRoleUpdateEventHandler) New() interface{} {
+	return &GuildRoleUpdate{}
+}
+
+// Handle is the handler for GuildRoleUpdate events.
+func (eh guildRoleUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildRoleUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// guildUpdateEventHandler is an event handler for GuildUpdate events.
+type guildUpdateEventHandler func(*Session, *GuildUpdate)
+
+// Type returns the event type for GuildUpdate events.
+func (eh guildUpdateEventHandler) Type() string {
+	return guildUpdateEventType
+}
+
+// New returns a new instance of GuildUpdate.
+func (eh guildUpdateEventHandler) New() interface{} {
+	return &GuildUpdate{}
+}
+
+// Handle is the handler for GuildUpdate events.
+func (eh guildUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*GuildUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// messageAckEventHandler is an event handler for MessageAck events.
+type messageAckEventHandler func(*Session, *MessageAck)
+
+// Type returns the event type for MessageAck events.
+func (eh messageAckEventHandler) Type() string {
+	return messageAckEventType
+}
+
+// New returns a new instance of MessageAck.
+func (eh messageAckEventHandler) New() interface{} {
+	return &MessageAck{}
+}
+
+// Handle is the handler for MessageAck events.
+func (eh messageAckEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*MessageAck); ok {
+		eh(s, t)
+	}
+}
+
+// messageCreateEventHandler is an event handler for MessageCreate events.
+type messageCreateEventHandler func(*Session, *MessageCreate)
+
+// Type returns the event type for MessageCreate events.
+func (eh messageCreateEventHandler) Type() string {
+	return messageCreateEventType
+}
+
+// New returns a new instance of MessageCreate.
+func (eh messageCreateEventHandler) New() interface{} {
+	return &MessageCreate{}
+}
+
+// Handle is the handler for MessageCreate events.
+func (eh messageCreateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*MessageCreate); ok {
+		eh(s, t)
+	}
+}
+
+// messageDeleteEventHandler is an event handler for MessageDelete events.
+type messageDeleteEventHandler func(*Session, *MessageDelete)
+
+// Type returns the event type for MessageDelete events.
+func (eh messageDeleteEventHandler) Type() string {
+	return messageDeleteEventType
+}
+
+// New returns a new instance of MessageDelete.
+func (eh messageDeleteEventHandler) New() interface{} {
+	return &MessageDelete{}
+}
+
+// Handle is the handler for MessageDelete events.
+func (eh messageDeleteEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*MessageDelete); ok {
+		eh(s, t)
+	}
+}
+
+// messageReactionAddEventHandler is an event handler for MessageReactionAdd events.
+type messageReactionAddEventHandler func(*Session, *MessageReactionAdd)
+
+// Type returns the event type for MessageReactionAdd events.
+func (eh messageReactionAddEventHandler) Type() string {
+	return messageReactionAddEventType
+}
+
+// New returns a new instance of MessageReactionAdd.
+func (eh messageReactionAddEventHandler) New() interface{} {
+	return &MessageReactionAdd{}
+}
+
+// Handle is the handler for MessageReactionAdd events.
+func (eh messageReactionAddEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*MessageReactionAdd); ok {
+		eh(s, t)
+	}
+}
+
+// messageReactionRemoveEventHandler is an event handler for MessageReactionRemove events.
+type messageReactionRemoveEventHandler func(*Session, *MessageReactionRemove)
+
+// Type returns the event type for MessageReactionRemove events.
+func (eh messageReactionRemoveEventHandler) Type() string {
+	return messageReactionRemoveEventType
+}
+
+// New returns a new instance of MessageReactionRemove.
+func (eh messageReactionRemoveEventHandler) New() interface{} {
+	return &MessageReactionRemove{}
+}
+
+// Handle is the handler for MessageReactionRemove events.
+func (eh messageReactionRemoveEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*MessageReactionRemove); ok {
+		eh(s, t)
+	}
+}
+
+// messageUpdateEventHandler is an event handler for MessageUpdate events.
+type messageUpdateEventHandler func(*Session, *MessageUpdate)
+
+// Type returns the event type for MessageUpdate events.
+func (eh messageUpdateEventHandler) Type() string {
+	return messageUpdateEventType
+}
+
+// New returns a new instance of MessageUpdate.
+func (eh messageUpdateEventHandler) New() interface{} {
+	return &MessageUpdate{}
+}
+
+// Handle is the handler for MessageUpdate events.
+func (eh messageUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*MessageUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// presenceUpdateEventHandler is an event handler for PresenceUpdate events.
+type presenceUpdateEventHandler func(*Session, *PresenceUpdate)
+
+// Type returns the event type for PresenceUpdate events.
+func (eh presenceUpdateEventHandler) Type() string {
+	return presenceUpdateEventType
+}
+
+// New returns a new instance of PresenceUpdate.
+func (eh presenceUpdateEventHandler) New() interface{} {
+	return &PresenceUpdate{}
+}
+
+// Handle is the handler for PresenceUpdate events.
+func (eh presenceUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*PresenceUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// presencesReplaceEventHandler is an event handler for PresencesReplace events.
+type presencesReplaceEventHandler func(*Session, *PresencesReplace)
+
+// Type returns the event type for PresencesReplace events.
+func (eh presencesReplaceEventHandler) Type() string {
+	return presencesReplaceEventType
+}
+
+// New returns a new instance of PresencesReplace.
+func (eh presencesReplaceEventHandler) New() interface{} {
+	return &PresencesReplace{}
+}
+
+// Handle is the handler for PresencesReplace events.
+func (eh presencesReplaceEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*PresencesReplace); ok {
+		eh(s, t)
+	}
+}
+
+// rateLimitEventHandler is an event handler for RateLimit events.
+type rateLimitEventHandler func(*Session, *RateLimit)
+
+// Type returns the event type for RateLimit events.
+func (eh rateLimitEventHandler) Type() string {
+	return rateLimitEventType
+}
+
+// New returns a new instance of RateLimit.
+func (eh rateLimitEventHandler) New() interface{} {
+	return &RateLimit{}
+}
+
+// Handle is the handler for RateLimit events.
+func (eh rateLimitEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*RateLimit); ok {
+		eh(s, t)
+	}
+}
+
+// readyEventHandler is an event handler for Ready events.
+type readyEventHandler func(*Session, *Ready)
+
+// Type returns the event type for Ready events.
+func (eh readyEventHandler) Type() string {
+	return readyEventType
+}
+
+// New returns a new instance of Ready.
+func (eh readyEventHandler) New() interface{} {
+	return &Ready{}
+}
+
+// Handle is the handler for Ready events.
+func (eh readyEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*Ready); ok {
+		eh(s, t)
+	}
+}
+
+// relationshipAddEventHandler is an event handler for RelationshipAdd events.
+type relationshipAddEventHandler func(*Session, *RelationshipAdd)
+
+// Type returns the event type for RelationshipAdd events.
+func (eh relationshipAddEventHandler) Type() string {
+	return relationshipAddEventType
+}
+
+// New returns a new instance of RelationshipAdd.
+func (eh relationshipAddEventHandler) New() interface{} {
+	return &RelationshipAdd{}
+}
+
+// Handle is the handler for RelationshipAdd events.
+func (eh relationshipAddEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*RelationshipAdd); ok {
+		eh(s, t)
+	}
+}
+
+// relationshipRemoveEventHandler is an event handler for RelationshipRemove events.
+type relationshipRemoveEventHandler func(*Session, *RelationshipRemove)
+
+// Type returns the event type for RelationshipRemove events.
+func (eh relationshipRemoveEventHandler) Type() string {
+	return relationshipRemoveEventType
+}
+
+// New returns a new instance of RelationshipRemove.
+func (eh relationshipRemoveEventHandler) New() interface{} {
+	return &RelationshipRemove{}
+}
+
+// Handle is the handler for RelationshipRemove events.
+func (eh relationshipRemoveEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*RelationshipRemove); ok {
+		eh(s, t)
+	}
+}
+
+// resumedEventHandler is an event handler for Resumed events.
+type resumedEventHandler func(*Session, *Resumed)
+
+// Type returns the event type for Resumed events.
+func (eh resumedEventHandler) Type() string {
+	return resumedEventType
+}
+
+// New returns a new instance of Resumed.
+func (eh resumedEventHandler) New() interface{} {
+	return &Resumed{}
+}
+
+// Handle is the handler for Resumed events.
+func (eh resumedEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*Resumed); ok {
+		eh(s, t)
+	}
+}
+
+// typingStartEventHandler is an event handler for TypingStart events.
+type typingStartEventHandler func(*Session, *TypingStart)
+
+// Type returns the event type for TypingStart events.
+func (eh typingStartEventHandler) Type() string {
+	return typingStartEventType
+}
+
+// New returns a new instance of TypingStart.
+func (eh typingStartEventHandler) New() interface{} {
+	return &TypingStart{}
+}
+
+// Handle is the handler for TypingStart events.
+func (eh typingStartEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*TypingStart); ok {
+		eh(s, t)
+	}
+}
+
+// userGuildSettingsUpdateEventHandler is an event handler for UserGuildSettingsUpdate events.
+type userGuildSettingsUpdateEventHandler func(*Session, *UserGuildSettingsUpdate)
+
+// Type returns the event type for UserGuildSettingsUpdate events.
+func (eh userGuildSettingsUpdateEventHandler) Type() string {
+	return userGuildSettingsUpdateEventType
+}
+
+// New returns a new instance of UserGuildSettingsUpdate.
+func (eh userGuildSettingsUpdateEventHandler) New() interface{} {
+	return &UserGuildSettingsUpdate{}
+}
+
+// Handle is the handler for UserGuildSettingsUpdate events.
+func (eh userGuildSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*UserGuildSettingsUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// userSettingsUpdateEventHandler is an event handler for UserSettingsUpdate events.
+type userSettingsUpdateEventHandler func(*Session, *UserSettingsUpdate)
+
+// Type returns the event type for UserSettingsUpdate events.
+func (eh userSettingsUpdateEventHandler) Type() string {
+	return userSettingsUpdateEventType
+}
+
+// New returns a new instance of UserSettingsUpdate.
+func (eh userSettingsUpdateEventHandler) New() interface{} {
+	return &UserSettingsUpdate{}
+}
+
+// Handle is the handler for UserSettingsUpdate events.
+func (eh userSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*UserSettingsUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// userUpdateEventHandler is an event handler for UserUpdate events.
+type userUpdateEventHandler func(*Session, *UserUpdate)
+
+// Type returns the event type for UserUpdate events.
+func (eh userUpdateEventHandler) Type() string {
+	return userUpdateEventType
+}
+
+// New returns a new instance of UserUpdate.
+func (eh userUpdateEventHandler) New() interface{} {
+	return &UserUpdate{}
+}
+
+// Handle is the handler for UserUpdate events.
+func (eh userUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*UserUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// voiceServerUpdateEventHandler is an event handler for VoiceServerUpdate events.
+type voiceServerUpdateEventHandler func(*Session, *VoiceServerUpdate)
+
+// Type returns the event type for VoiceServerUpdate events.
+func (eh voiceServerUpdateEventHandler) Type() string {
+	return voiceServerUpdateEventType
+}
+
+// New returns a new instance of VoiceServerUpdate.
+func (eh voiceServerUpdateEventHandler) New() interface{} {
+	return &VoiceServerUpdate{}
+}
+
+// Handle is the handler for VoiceServerUpdate events.
+func (eh voiceServerUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*VoiceServerUpdate); ok {
+		eh(s, t)
+	}
+}
+
+// voiceStateUpdateEventHandler is an event handler for VoiceStateUpdate events.
+type voiceStateUpdateEventHandler func(*Session, *VoiceStateUpdate)
+
+// Type returns the event type for VoiceStateUpdate events.
+func (eh voiceStateUpdateEventHandler) Type() string {
+	return voiceStateUpdateEventType
+}
+
+// New returns a new instance of VoiceStateUpdate.
+func (eh voiceStateUpdateEventHandler) New() interface{} {
+	return &VoiceStateUpdate{}
+}
+
+// Handle is the handler for VoiceStateUpdate events.
+func (eh voiceStateUpdateEventHandler) Handle(s *Session, i interface{}) {
+	if t, ok := i.(*VoiceStateUpdate); ok {
+		eh(s, t)
+	}
+}
+
+func handlerForInterface(handler interface{}) EventHandler {
+	switch v := handler.(type) {
+	case func(*Session, interface{}):
+		return interfaceEventHandler(v)
+	case func(*Session, *ChannelCreate):
+		return channelCreateEventHandler(v)
+	case func(*Session, *ChannelDelete):
+		return channelDeleteEventHandler(v)
+	case func(*Session, *ChannelPinsUpdate):
+		return channelPinsUpdateEventHandler(v)
+	case func(*Session, *ChannelUpdate):
+		return channelUpdateEventHandler(v)
+	case func(*Session, *Connect):
+		return connectEventHandler(v)
+	case func(*Session, *Disconnect):
+		return disconnectEventHandler(v)
+	case func(*Session, *Event):
+		return eventEventHandler(v)
+	case func(*Session, *GuildBanAdd):
+		return guildBanAddEventHandler(v)
+	case func(*Session, *GuildBanRemove):
+		return guildBanRemoveEventHandler(v)
+	case func(*Session, *GuildCreate):
+		return guildCreateEventHandler(v)
+	case func(*Session, *GuildDelete):
+		return guildDeleteEventHandler(v)
+	case func(*Session, *GuildEmojisUpdate):
+		return guildEmojisUpdateEventHandler(v)
+	case func(*Session, *GuildIntegrationsUpdate):
+		return guildIntegrationsUpdateEventHandler(v)
+	case func(*Session, *GuildMemberAdd):
+		return guildMemberAddEventHandler(v)
+	case func(*Session, *GuildMemberRemove):
+		return guildMemberRemoveEventHandler(v)
+	case func(*Session, *GuildMemberUpdate):
+		return guildMemberUpdateEventHandler(v)
+	case func(*Session, *GuildMembersChunk):
+		return guildMembersChunkEventHandler(v)
+	case func(*Session, *GuildRoleCreate):
+		return guildRoleCreateEventHandler(v)
+	case func(*Session, *GuildRoleDelete):
+		return guildRoleDeleteEventHandler(v)
+	case func(*Session, *GuildRoleUpdate):
+		return guildRoleUpdateEventHandler(v)
+	case func(*Session, *GuildUpdate):
+		return guildUpdateEventHandler(v)
+	case func(*Session, *MessageAck):
+		return messageAckEventHandler(v)
+	case func(*Session, *MessageCreate):
+		return messageCreateEventHandler(v)
+	case func(*Session, *MessageDelete):
+		return messageDeleteEventHandler(v)
+	case func(*Session, *MessageReactionAdd):
+		return messageReactionAddEventHandler(v)
+	case func(*Session, *MessageReactionRemove):
+		return messageReactionRemoveEventHandler(v)
+	case func(*Session, *MessageUpdate):
+		return messageUpdateEventHandler(v)
+	case func(*Session, *PresenceUpdate):
+		return presenceUpdateEventHandler(v)
+	case func(*Session, *PresencesReplace):
+		return presencesReplaceEventHandler(v)
+	case func(*Session, *RateLimit):
+		return rateLimitEventHandler(v)
+	case func(*Session, *Ready):
+		return readyEventHandler(v)
+	case func(*Session, *RelationshipAdd):
+		return relationshipAddEventHandler(v)
+	case func(*Session, *RelationshipRemove):
+		return relationshipRemoveEventHandler(v)
+	case func(*Session, *Resumed):
+		return resumedEventHandler(v)
+	case func(*Session, *TypingStart):
+		return typingStartEventHandler(v)
+	case func(*Session, *UserGuildSettingsUpdate):
+		return userGuildSettingsUpdateEventHandler(v)
+	case func(*Session, *UserSettingsUpdate):
+		return userSettingsUpdateEventHandler(v)
+	case func(*Session, *UserUpdate):
+		return userUpdateEventHandler(v)
+	case func(*Session, *VoiceServerUpdate):
+		return voiceServerUpdateEventHandler(v)
+	case func(*Session, *VoiceStateUpdate):
+		return voiceStateUpdateEventHandler(v)
+	}
+
+	return nil
+}
+func init() {
+	registerInterfaceProvider(channelCreateEventHandler(nil))
+	registerInterfaceProvider(channelDeleteEventHandler(nil))
+	registerInterfaceProvider(channelPinsUpdateEventHandler(nil))
+	registerInterfaceProvider(channelUpdateEventHandler(nil))
+	registerInterfaceProvider(guildBanAddEventHandler(nil))
+	registerInterfaceProvider(guildBanRemoveEventHandler(nil))
+	registerInterfaceProvider(guildCreateEventHandler(nil))
+	registerInterfaceProvider(guildDeleteEventHandler(nil))
+	registerInterfaceProvider(guildEmojisUpdateEventHandler(nil))
+	registerInterfaceProvider(guildIntegrationsUpdateEventHandler(nil))
+	registerInterfaceProvider(guildMemberAddEventHandler(nil))
+	registerInterfaceProvider(guildMemberRemoveEventHandler(nil))
+	registerInterfaceProvider(guildMemberUpdateEventHandler(nil))
+	registerInterfaceProvider(guildMembersChunkEventHandler(nil))
+	registerInterfaceProvider(guildRoleCreateEventHandler(nil))
+	registerInterfaceProvider(guildRoleDeleteEventHandler(nil))
+	registerInterfaceProvider(guildRoleUpdateEventHandler(nil))
+	registerInterfaceProvider(guildUpdateEventHandler(nil))
+	registerInterfaceProvider(messageAckEventHandler(nil))
+	registerInterfaceProvider(messageCreateEventHandler(nil))
+	registerInterfaceProvider(messageDeleteEventHandler(nil))
+	registerInterfaceProvider(messageReactionAddEventHandler(nil))
+	registerInterfaceProvider(messageReactionRemoveEventHandler(nil))
+	registerInterfaceProvider(messageUpdateEventHandler(nil))
+	registerInterfaceProvider(presenceUpdateEventHandler(nil))
+	registerInterfaceProvider(presencesReplaceEventHandler(nil))
+	registerInterfaceProvider(readyEventHandler(nil))
+	registerInterfaceProvider(relationshipAddEventHandler(nil))
+	registerInterfaceProvider(relationshipRemoveEventHandler(nil))
+	registerInterfaceProvider(resumedEventHandler(nil))
+	registerInterfaceProvider(typingStartEventHandler(nil))
+	registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil))
+	registerInterfaceProvider(userSettingsUpdateEventHandler(nil))
+	registerInterfaceProvider(userUpdateEventHandler(nil))
+	registerInterfaceProvider(voiceServerUpdateEventHandler(nil))
+	registerInterfaceProvider(voiceStateUpdateEventHandler(nil))
+}

+ 159 - 80
events.go

@@ -1,159 +1,238 @@
 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{},
-	"PRESENCES_REPLACE":          PresencesReplace{},
-	"READY":                      Ready{},
-	"USER_UPDATE":                UserUpdate{},
-	"USER_SETTINGS_UPDATE":       UserSettingsUpdate{},
-	"USER_GUILD_SETTINGS_UPDATE": UserGuildSettingsUpdate{},
-	"TYPING_START":               TypingStart{},
-	"VOICE_SERVER_UPDATE":        VoiceServerUpdate{},
-	"VOICE_STATE_UPDATE":         VoiceStateUpdate{},
-	"RESUMED":                    Resumed{},
-}
-
-// Connect is an empty struct for an event.
+import (
+	"encoding/json"
+	"time"
+)
+
+// This file contains all the possible structs that can be
+// handled by AddHandler/EventHandler.
+// DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE.
+//go:generate go run tools/cmd/eventhandlers/main.go
+
+// Connect is the data for a Connect event.
+// This is a sythetic event and is not dispatched by Discord.
 type Connect struct{}
 
-// Disconnect is an empty struct for an event.
+// Disconnect is the data for a Disconnect event.
+// This is a sythetic event and is not dispatched by Discord.
 type Disconnect struct{}
 
-// RateLimit is a struct for the RateLimited event
+// RateLimit is the data for a RateLimit event.
+// This is a sythetic event and is not dispatched by Discord.
 type RateLimit struct {
 	*TooManyRequests
 	URL string
 }
 
-// MessageCreate is a wrapper struct for an event.
-type MessageCreate struct {
-	*Message
+// Event provides a basic initial struct for all websocket events.
+type Event struct {
+	Operation int             `json:"op"`
+	Sequence  int             `json:"s"`
+	Type      string          `json:"t"`
+	RawData   json.RawMessage `json:"d"`
+	// Struct contains one of the other types in this file.
+	Struct interface{} `json:"-"`
 }
 
-// MessageUpdate is a wrapper struct for an event.
-type MessageUpdate struct {
-	*Message
-}
+// A Ready stores all data for the websocket READY event.
+type Ready struct {
+	Version           int           `json:"v"`
+	SessionID         string        `json:"session_id"`
+	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
+	User              *User         `json:"user"`
+	ReadState         []*ReadState  `json:"read_state"`
+	PrivateChannels   []*Channel    `json:"private_channels"`
+	Guilds            []*Guild      `json:"guilds"`
 
-// MessageDelete is a wrapper struct for an event.
-type MessageDelete struct {
-	*Message
+	// Undocumented fields
+	Settings          *Settings            `json:"user_settings"`
+	UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
+	Relationships     []*Relationship      `json:"relationships"`
+	Presences         []*Presence          `json:"presences"`
 }
 
-// ChannelCreate is a wrapper struct for an event.
+// ChannelCreate is the data for a ChannelCreate event.
 type ChannelCreate struct {
 	*Channel
 }
 
-// ChannelUpdate is a wrapper struct for an event.
+// ChannelUpdate is the data for a ChannelUpdate event.
 type ChannelUpdate struct {
 	*Channel
 }
 
-// ChannelDelete is a wrapper struct for an event.
+// ChannelDelete is the data for a ChannelDelete event.
 type ChannelDelete struct {
 	*Channel
 }
 
-// GuildCreate is a wrapper struct for an event.
+// ChannelPinsUpdate stores data for a ChannelPinsUpdate event.
+type ChannelPinsUpdate struct {
+	LastPinTimestamp string `json:"last_pin_timestamp"`
+	ChannelID        string `json:"channel_id"`
+}
+
+// GuildCreate is the data for a GuildCreate event.
 type GuildCreate struct {
 	*Guild
 }
 
-// GuildUpdate is a wrapper struct for an event.
+// GuildUpdate is the data for a GuildUpdate event.
 type GuildUpdate struct {
 	*Guild
 }
 
-// GuildDelete is a wrapper struct for an event.
+// GuildDelete is the data for a GuildDelete event.
 type GuildDelete struct {
 	*Guild
 }
 
-// GuildBanAdd is a wrapper struct for an event.
+// GuildBanAdd is the data for a GuildBanAdd event.
 type GuildBanAdd struct {
-	*GuildBan
+	User    *User  `json:"user"`
+	GuildID string `json:"guild_id"`
 }
 
-// GuildBanRemove is a wrapper struct for an event.
+// GuildBanRemove is the data for a GuildBanRemove event.
 type GuildBanRemove struct {
-	*GuildBan
+	User    *User  `json:"user"`
+	GuildID string `json:"guild_id"`
 }
 
-// GuildMemberAdd is a wrapper struct for an event.
+// GuildMemberAdd is the data for a GuildMemberAdd event.
 type GuildMemberAdd struct {
 	*Member
 }
 
-// GuildMemberUpdate is a wrapper struct for an event.
+// GuildMemberUpdate is the data for a GuildMemberUpdate event.
 type GuildMemberUpdate struct {
 	*Member
 }
 
-// GuildMemberRemove is a wrapper struct for an event.
+// GuildMemberRemove is the data for a GuildMemberRemove event.
 type GuildMemberRemove struct {
 	*Member
 }
 
-// GuildRoleCreate is a wrapper struct for an event.
+// GuildRoleCreate is the data for a GuildRoleCreate event.
 type GuildRoleCreate struct {
 	*GuildRole
 }
 
-// GuildRoleUpdate is a wrapper struct for an event.
+// GuildRoleUpdate is the data for a GuildRoleUpdate event.
 type GuildRoleUpdate struct {
 	*GuildRole
 }
 
-// PresencesReplace is an array of Presences for an event.
+// A GuildRoleDelete is the data for a GuildRoleDelete event.
+type GuildRoleDelete struct {
+	RoleID  string `json:"role_id"`
+	GuildID string `json:"guild_id"`
+}
+
+// A GuildEmojisUpdate is the data for a guild emoji update event.
+type GuildEmojisUpdate struct {
+	GuildID string   `json:"guild_id"`
+	Emojis  []*Emoji `json:"emojis"`
+}
+
+// A GuildMembersChunk is the data for a GuildMembersChunk event.
+type GuildMembersChunk struct {
+	GuildID string    `json:"guild_id"`
+	Members []*Member `json:"members"`
+}
+
+// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
+type GuildIntegrationsUpdate struct {
+	GuildID string `json:"guild_id"`
+}
+
+// MessageAck is the data for a MessageAck event.
+type MessageAck struct {
+	MessageID string `json:"message_id"`
+	ChannelID string `json:"channel_id"`
+}
+
+// MessageCreate is the data for a MessageCreate event.
+type MessageCreate struct {
+	*Message
+}
+
+// MessageUpdate is the data for a MessageUpdate event.
+type MessageUpdate struct {
+	*Message
+}
+
+// MessageDelete is the data for a MessageDelete event.
+type MessageDelete struct {
+	*Message
+}
+
+// MessageReactionAdd is the data for a MessageReactionAdd event.
+type MessageReactionAdd struct {
+	*MessageReaction
+}
+
+// MessageReactionRemove is the data for a MessageReactionRemove event.
+type MessageReactionRemove struct {
+	*MessageReaction
+}
+
+// PresencesReplace is the data for a PresencesReplace event.
 type PresencesReplace []*Presence
 
-// VoiceStateUpdate is a wrapper struct for an event.
-type VoiceStateUpdate struct {
-	*VoiceState
+// PresenceUpdate is the data for a PresenceUpdate event.
+type PresenceUpdate struct {
+	Presence
+	GuildID string   `json:"guild_id"`
+	Roles   []string `json:"roles"`
+}
+
+// Resumed is the data for a Resumed event.
+type Resumed struct {
+	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
+	Trace             []string      `json:"_trace"`
 }
 
-// UserUpdate is a wrapper struct for an event.
+// RelationshipAdd is the data for a RelationshipAdd event.
+type RelationshipAdd struct {
+	*Relationship
+}
+
+// RelationshipRemove is the data for a RelationshipRemove event.
+type RelationshipRemove struct {
+	*Relationship
+}
+
+// TypingStart is the data for a TypingStart event.
+type TypingStart struct {
+	UserID    string `json:"user_id"`
+	ChannelID string `json:"channel_id"`
+	Timestamp int    `json:"timestamp"`
+}
+
+// UserUpdate is the data for a UserUpdate event.
 type UserUpdate struct {
 	*User
 }
 
-// UserSettingsUpdate is a map for an event.
+// UserSettingsUpdate is the data for a UserSettingsUpdate event.
 type UserSettingsUpdate map[string]interface{}
 
-// UserGuildSettingsUpdate is a map for an event.
+// UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
 type UserGuildSettingsUpdate struct {
 	*UserGuildSettings
 }
+
+// VoiceServerUpdate is the data for a VoiceServerUpdate event.
+type VoiceServerUpdate struct {
+	Token    string `json:"token"`
+	GuildID  string `json:"guild_id"`
+	Endpoint string `json:"endpoint"`
+}
+
+// VoiceStateUpdate is the data for a VoiceStateUpdate event.
+type VoiceStateUpdate struct {
+	*VoiceState
+}

+ 5 - 1
examples/airhorn/main.go

@@ -102,7 +102,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
 // This function will be called (due to AddHandler above) every time a new
 // guild is joined.
 func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
-	if event.Guild.Unavailable != nil {
+	if event.Guild.Unavailable {
 		return
 	}
 
@@ -131,6 +131,10 @@ func loadSound() error {
 
 		// If this is the end of the file, just return.
 		if err == io.EOF || err == io.ErrUnexpectedEOF {
+			file.Close()
+			if err != nil {
+				return err
+			}
 			return nil
 		}
 

+ 75 - 25
message.go

@@ -19,8 +19,8 @@ type Message struct {
 	ID              string               `json:"id"`
 	ChannelID       string               `json:"channel_id"`
 	Content         string               `json:"content"`
-	Timestamp       string               `json:"timestamp"`
-	EditedTimestamp string               `json:"edited_timestamp"`
+	Timestamp       Timestamp            `json:"timestamp"`
+	EditedTimestamp Timestamp            `json:"edited_timestamp"`
 	MentionRoles    []string             `json:"mention_roles"`
 	Tts             bool                 `json:"tts"`
 	MentionEveryone bool                 `json:"mention_everyone"`
@@ -28,6 +28,7 @@ type Message struct {
 	Attachments     []*MessageAttachment `json:"attachments"`
 	Embeds          []*MessageEmbed      `json:"embeds"`
 	Mentions        []*User              `json:"mentions"`
+	Reactions       []*MessageReactions  `json:"reactions"`
 }
 
 // A MessageAttachment stores data for message attachments.
@@ -41,31 +42,80 @@ type MessageAttachment struct {
 	Size     int    `json:"size"`
 }
 
+// MessageEmbedFooter is a part of a MessageEmbed struct.
+type MessageEmbedFooter struct {
+	Text         string `json:"text,omitempty"`
+	IconURL      string `json:"icon_url,omitempty"`
+	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
+}
+
+// MessageEmbedImage is a part of a MessageEmbed struct.
+type MessageEmbedImage struct {
+	URL      string `json:"url,omitempty"`
+	ProxyURL string `json:"proxy_url,omitempty"`
+	Width    int    `json:"width,omitempty"`
+	Height   int    `json:"height,omitempty"`
+}
+
+// MessageEmbedThumbnail is a part of a MessageEmbed struct.
+type MessageEmbedThumbnail struct {
+	URL      string `json:"url,omitempty"`
+	ProxyURL string `json:"proxy_url,omitempty"`
+	Width    int    `json:"width,omitempty"`
+	Height   int    `json:"height,omitempty"`
+}
+
+// MessageEmbedVideo is a part of a MessageEmbed struct.
+type MessageEmbedVideo struct {
+	URL      string `json:"url,omitempty"`
+	ProxyURL string `json:"proxy_url,omitempty"`
+	Width    int    `json:"width,omitempty"`
+	Height   int    `json:"height,omitempty"`
+}
+
+// MessageEmbedProvider is a part of a MessageEmbed struct.
+type MessageEmbedProvider struct {
+	URL  string `json:"url,omitempty"`
+	Name string `json:"name,omitempty"`
+}
+
+// MessageEmbedAuthor is a part of a MessageEmbed struct.
+type MessageEmbedAuthor struct {
+	URL          string `json:"url,omitempty"`
+	Name         string `json:"name,omitempty"`
+	IconURL      string `json:"icon_url,omitempty"`
+	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
+}
+
+// MessageEmbedField is a part of a MessageEmbed struct.
+type MessageEmbedField struct {
+	Name   string `json:"name,omitempty"`
+	Value  string `json:"value,omitempty"`
+	Inline bool   `json:"inline,omitempty"`
+}
+
 // An MessageEmbed stores data for message embeds.
 type MessageEmbed struct {
-	URL         string `json:"url"`
-	Type        string `json:"type"`
-	Title       string `json:"title"`
-	Description string `json:"description"`
-	Thumbnail   *struct {
-		URL      string `json:"url"`
-		ProxyURL string `json:"proxy_url"`
-		Width    int    `json:"width"`
-		Height   int    `json:"height"`
-	} `json:"thumbnail"`
-	Provider *struct {
-		URL  string `json:"url"`
-		Name string `json:"name"`
-	} `json:"provider"`
-	Author *struct {
-		URL  string `json:"url"`
-		Name string `json:"name"`
-	} `json:"author"`
-	Video *struct {
-		URL    string `json:"url"`
-		Width  int    `json:"width"`
-		Height int    `json:"height"`
-	} `json:"video"`
+	URL         string                 `json:"url,omitempty"`
+	Type        string                 `json:"type,omitempty"`
+	Title       string                 `json:"title,omitempty"`
+	Description string                 `json:"description,omitempty"`
+	Timestamp   string                 `json:"timestamp,omitempty"`
+	Color       int                    `json:"color,omitempty"`
+	Footer      *MessageEmbedFooter    `json:"footer,omitempty"`
+	Image       *MessageEmbedImage     `json:"image,omitempty"`
+	Thumbnail   *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
+	Video       *MessageEmbedVideo     `json:"video,omitempty"`
+	Provider    *MessageEmbedProvider  `json:"provider,omitempty"`
+	Author      *MessageEmbedAuthor    `json:"author,omitempty"`
+	Fields      []*MessageEmbedField   `json:"fields,omitempty"`
+}
+
+// MessageReactions holds a reactions object for a message.
+type MessageReactions struct {
+	Count int    `json:"count"`
+	Me    bool   `json:"me"`
+	Emoji *Emoji `json:"emoji"`
 }
 
 // ContentWithMentionsReplaced will replace all @<id> mentions with the

+ 7 - 6
oauth2.go

@@ -21,13 +21,14 @@ type Application struct {
 	Icon         string    `json:"icon,omitempty"`
 	Secret       string    `json:"secret,omitempty"`
 	RedirectURIs *[]string `json:"redirect_uris,omitempty"`
+	Owner        *User     `json:"owner"`
 }
 
 // Application returns an Application structure of a specific Application
 //   appID : The ID of an Application
 func (s *Session) Application(appID string) (st *Application, err error) {
 
-	body, err := s.Request("GET", EndpointApplication(appID), nil)
+	body, err := s.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication(""))
 	if err != nil {
 		return
 	}
@@ -39,7 +40,7 @@ func (s *Session) Application(appID string) (st *Application, err error) {
 // Applications returns all applications for the authenticated user
 func (s *Session) Applications() (st []*Application, err error) {
 
-	body, err := s.Request("GET", EndpointApplications, nil)
+	body, err := s.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications)
 	if err != nil {
 		return
 	}
@@ -59,7 +60,7 @@ func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error
 		RedirectURIs *[]string `json:"redirect_uris,omitempty"`
 	}{ap.Name, ap.Description, ap.RedirectURIs}
 
-	body, err := s.Request("POST", EndpointApplications, data)
+	body, err := s.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications)
 	if err != nil {
 		return
 	}
@@ -78,7 +79,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat
 		RedirectURIs *[]string `json:"redirect_uris,omitempty"`
 	}{ap.Name, ap.Description, ap.RedirectURIs}
 
-	body, err := s.Request("PUT", EndpointApplication(appID), data)
+	body, err := s.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication(""))
 	if err != nil {
 		return
 	}
@@ -91,7 +92,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat
 //   appID : The ID of an Application
 func (s *Session) ApplicationDelete(appID string) (err error) {
 
-	_, err = s.Request("DELETE", EndpointApplication(appID), nil)
+	_, err = s.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication(""))
 	if err != nil {
 		return
 	}
@@ -110,7 +111,7 @@ func (s *Session) ApplicationDelete(appID string) (err error) {
 // NOTE: func name may change, if I can think up something better.
 func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) {
 
-	body, err := s.Request("POST", EndpointApplicationsBot(appID), nil)
+	body, err := s.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot(""))
 	if err != nil {
 		return
 	}

+ 157 - 0
ratelimit.go

@@ -0,0 +1,157 @@
+package discordgo
+
+import (
+	"net/http"
+	"strconv"
+	"sync"
+	"time"
+)
+
+// RateLimiter holds all ratelimit buckets
+type RateLimiter struct {
+	sync.Mutex
+	global          *Bucket
+	buckets         map[string]*Bucket
+	globalRateLimit time.Duration
+}
+
+// NewRatelimiter returns a new RateLimiter
+func NewRatelimiter() *RateLimiter {
+
+	return &RateLimiter{
+		buckets: make(map[string]*Bucket),
+		global:  &Bucket{Key: "global"},
+	}
+}
+
+// getBucket retrieves or creates a bucket
+func (r *RateLimiter) getBucket(key string) *Bucket {
+	r.Lock()
+	defer r.Unlock()
+
+	if bucket, ok := r.buckets[key]; ok {
+		return bucket
+	}
+
+	b := &Bucket{
+		remaining: 1,
+		Key:       key,
+		global:    r.global,
+	}
+
+	r.buckets[key] = b
+	return b
+}
+
+// LockBucket Locks until a request can be made
+func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
+
+	b := r.getBucket(bucketID)
+
+	b.Lock()
+
+	// If we ran out of calls and the reset time is still ahead of us
+	// then we need to take it easy and relax a little
+	if b.remaining < 1 && b.reset.After(time.Now()) {
+		time.Sleep(b.reset.Sub(time.Now()))
+
+	}
+
+	// Check for global ratelimits
+	r.global.Lock()
+	r.global.Unlock()
+
+	b.remaining--
+	return b
+}
+
+// Bucket represents a ratelimit bucket, each bucket gets ratelimited individually (-global ratelimits)
+type Bucket struct {
+	sync.Mutex
+	Key       string
+	remaining int
+	limit     int
+	reset     time.Time
+	global    *Bucket
+}
+
+// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
+// and locks up the whole thing in case if there's a global ratelimit.
+func (b *Bucket) Release(headers http.Header) error {
+
+	defer b.Unlock()
+	if headers == nil {
+		return nil
+	}
+
+	remaining := headers.Get("X-RateLimit-Remaining")
+	reset := headers.Get("X-RateLimit-Reset")
+	global := headers.Get("X-RateLimit-Global")
+	retryAfter := headers.Get("Retry-After")
+
+	// If it's global just keep the main ratelimit mutex locked
+	if global != "" {
+		parsedAfter, err := strconv.Atoi(retryAfter)
+		if err != nil {
+			return err
+		}
+
+		// Lock it in a new goroutine so that this isn't a blocking call
+		go func() {
+			// Make sure if several requests were waiting we don't sleep for n * retry-after
+			// where n is the amount of requests that were going on
+			sleepTo := time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)
+
+			b.global.Lock()
+
+			sleepDuration := sleepTo.Sub(time.Now())
+			if sleepDuration > 0 {
+				time.Sleep(sleepDuration)
+			}
+
+			b.global.Unlock()
+		}()
+
+		return nil
+	}
+
+	// Update reset time if either retry after or reset headers are present
+	// Prefer retryafter because it's more accurate with time sync and whatnot
+	if retryAfter != "" {
+		parsedAfter, err := strconv.ParseInt(retryAfter, 10, 64)
+		if err != nil {
+			return err
+		}
+		b.reset = time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)
+
+	} else if reset != "" {
+		// Calculate the reset time by using the date header returned from discord
+		discordTime, err := http.ParseTime(headers.Get("Date"))
+		if err != nil {
+			return err
+		}
+
+		unix, err := strconv.ParseInt(reset, 10, 64)
+		if err != nil {
+			return err
+		}
+
+		// Calculate the time until reset and add it to the current local time
+		// some extra time is added because without it i still encountered 429's.
+		// The added amount is the lowest amount that gave no 429's
+		// in 1k requests
+		delta := time.Unix(unix, 0).Sub(discordTime) + time.Millisecond*250
+		b.reset = time.Now().Add(delta)
+	}
+
+	// Udpate remaining if header is present
+	if remaining != "" {
+		parsedRemaining, err := strconv.ParseInt(remaining, 10, 32)
+		if err != nil {
+			return err
+		}
+		b.remaining = int(parsedRemaining)
+	}
+
+	return nil
+}

+ 112 - 0
ratelimit_test.go

@@ -0,0 +1,112 @@
+package discordgo
+
+import (
+	"net/http"
+	"strconv"
+	"testing"
+	"time"
+)
+
+// This test takes ~2 seconds to run
+func TestRatelimitReset(t *testing.T) {
+	rl := NewRatelimiter()
+
+	sendReq := func(endpoint string) {
+		bucket := rl.LockBucket(endpoint)
+
+		headers := http.Header(make(map[string][]string))
+
+		headers.Set("X-RateLimit-Remaining", "0")
+		// Reset for approx 2 seconds from now
+		headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Add(time.Second*2).Unix(), 10))
+		headers.Set("Date", time.Now().Format(time.RFC850))
+
+		err := bucket.Release(headers)
+		if err != nil {
+			t.Errorf("Release returned error: %v", err)
+		}
+	}
+
+	sent := time.Now()
+	sendReq("/guilds/99/channels")
+	sendReq("/guilds/55/channels")
+	sendReq("/guilds/66/channels")
+
+	sendReq("/guilds/99/channels")
+	sendReq("/guilds/55/channels")
+	sendReq("/guilds/66/channels")
+
+	// We hit the same endpoint 2 times, so we should only be ratelimited 2 second
+	// And always less than 4 seconds (unless you're on a stoneage computer or using swap or something...)
+	if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*4 {
+		t.Log("OK", time.Since(sent))
+	} else {
+		t.Error("Did not ratelimit correctly, got:", time.Since(sent))
+	}
+}
+
+// This test takes ~1 seconds to run
+func TestRatelimitGlobal(t *testing.T) {
+	rl := NewRatelimiter()
+
+	sendReq := func(endpoint string) {
+		bucket := rl.LockBucket(endpoint)
+
+		headers := http.Header(make(map[string][]string))
+
+		headers.Set("X-RateLimit-Global", "1")
+		// Reset for approx 1 seconds from now
+		headers.Set("Retry-After", "1000")
+
+		err := bucket.Release(headers)
+		if err != nil {
+			t.Errorf("Release returned error: %v", err)
+		}
+	}
+
+	sent := time.Now()
+
+	// This should trigger a global ratelimit
+	sendReq("/guilds/99/channels")
+	time.Sleep(time.Millisecond * 100)
+
+	// This shouldn't go through in less than 1 second
+	sendReq("/guilds/55/channels")
+
+	if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*2 {
+		t.Log("OK", time.Since(sent))
+	} else {
+		t.Error("Did not ratelimit correctly, got:", time.Since(sent))
+	}
+}
+
+func BenchmarkRatelimitSingleEndpoint(b *testing.B) {
+	rl := NewRatelimiter()
+	for i := 0; i < b.N; i++ {
+		sendBenchReq("/guilds/99/channels", rl)
+	}
+}
+
+func BenchmarkRatelimitParallelMultiEndpoints(b *testing.B) {
+	rl := NewRatelimiter()
+	b.RunParallel(func(pb *testing.PB) {
+		i := 0
+		for pb.Next() {
+			sendBenchReq("/guilds/"+strconv.Itoa(i)+"/channels", rl)
+			i++
+		}
+	})
+}
+
+// Does not actually send requests, but locks the bucket and releases it with made-up headers
+func sendBenchReq(endpoint string, rl *RateLimiter) {
+	bucket := rl.LockBucket(endpoint)
+
+	headers := http.Header(make(map[string][]string))
+
+	headers.Set("X-RateLimit-Remaining", "10")
+	headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Unix(), 10))
+	headers.Set("Date", time.Now().Format(time.RFC850))
+
+	bucket.Release(headers)
+}

File diff suppressed because it is too large
+ 584 - 119
restapi.go


+ 47 - 0
restapi_test.go

@@ -131,6 +131,17 @@ func TestUserSettings(t *testing.T) {
 	}
 }
 
+func TestUserUpdateStatus(t *testing.T) {
+	if dg == nil {
+		t.Skip("Cannot TestUserSettings, dg not set.")
+	}
+
+	_, err := dg.UserUpdateStatus(StatusDoNotDisturb)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+}
+
 // TestLogout tests the Logout() function. This should not return an error.
 func TestLogout(t *testing.T) {
 
@@ -227,3 +238,39 @@ func TestChannelMessageSend2(t *testing.T) {
 		t.Errorf("ChannelMessageSend returned error: %+v", err)
 	}
 }
+
+// TestGuildPruneCount tests GuildPruneCount() function. This should not return an error.
+func TestGuildPruneCount(t *testing.T) {
+
+	if envGuild == "" {
+		t.Skip("Skipping, DG_GUILD not set.")
+	}
+
+	if dg == nil {
+		t.Skip("Skipping, dg not set.")
+	}
+
+	_, err := dg.GuildPruneCount(envGuild, 1)
+	if err != nil {
+		t.Errorf("GuildPruneCount returned error: %+v", err)
+	}
+}
+
+/*
+// TestGuildPrune tests GuildPrune() function. This should not return an error.
+func TestGuildPrune(t *testing.T) {
+
+	if envGuild == "" {
+		t.Skip("Skipping, DG_GUILD not set.")
+	}
+
+	if dg == nil {
+		t.Skip("Skipping, dg not set.")
+	}
+
+	_, err := dg.GuildPrune(envGuild, 1)
+	if err != nil {
+		t.Errorf("GuildPrune returned error: %+v", err)
+	}
+}
+*/

+ 86 - 38
state.go

@@ -55,33 +55,6 @@ func NewState() *State {
 	}
 }
 
-// OnReady takes a Ready event and updates all internal state.
-func (s *State) OnReady(r *Ready) error {
-	if s == nil {
-		return ErrNilState
-	}
-
-	s.Lock()
-	defer s.Unlock()
-
-	s.Ready = *r
-
-	for _, g := range s.Guilds {
-		s.guildMap[g.ID] = g
-
-		for _, c := range g.Channels {
-			c.GuildID = g.ID
-			s.channelMap[c.ID] = c
-		}
-	}
-
-	for _, c := range s.PrivateChannels {
-		s.channelMap[c.ID] = c
-	}
-
-	return nil
-}
-
 // GuildAdd adds a guild to the current world state, or
 // updates it if it already exists.
 func (s *State) GuildAdd(guild *Guild) error {
@@ -94,20 +67,30 @@ func (s *State) GuildAdd(guild *Guild) error {
 
 	// Update the channels to point to the right guild, adding them to the channelMap as we go
 	for _, c := range guild.Channels {
-		c.GuildID = guild.ID
 		s.channelMap[c.ID] = c
 	}
 
-	// If the guild exists, replace it.
 	if g, ok := s.guildMap[guild.ID]; ok {
-		// If this guild already exists with data, don't stomp on props.
-		if g.Unavailable != nil && !*g.Unavailable {
+		// We are about to replace `g` in the state with `guild`, but first we need to
+		// make sure we preserve any fields that the `guild` doesn't contain from `g`.
+		if guild.Roles == nil {
+			guild.Roles = g.Roles
+		}
+		if guild.Emojis == nil {
+			guild.Emojis = g.Emojis
+		}
+		if guild.Members == nil {
 			guild.Members = g.Members
+		}
+		if guild.Presences == nil {
 			guild.Presences = g.Presences
+		}
+		if guild.Channels == nil {
 			guild.Channels = g.Channels
+		}
+		if guild.VoiceStates == nil {
 			guild.VoiceStates = g.VoiceStates
 		}
-
 		*g = *guild
 		return nil
 	}
@@ -325,8 +308,12 @@ func (s *State) ChannelAdd(channel *Channel) error {
 
 	// If the channel exists, replace it
 	if c, ok := s.channelMap[channel.ID]; ok {
-		channel.Messages = c.Messages
-		channel.PermissionOverwrites = c.PermissionOverwrites
+		if channel.Messages == nil {
+			channel.Messages = c.Messages
+		}
+		if channel.PermissionOverwrites == nil {
+			channel.PermissionOverwrites = c.PermissionOverwrites
+		}
 
 		*c = *channel
 		return nil
@@ -511,6 +498,12 @@ func (s *State) MessageAdd(message *Message) error {
 			if message.Attachments != nil {
 				m.Attachments = message.Attachments
 			}
+			if message.Timestamp != "" {
+				m.Timestamp = message.Timestamp
+			}
+			if message.Author != nil {
+				m.Author = message.Author
+			}
 
 			return nil
 		}
@@ -602,18 +595,63 @@ func (s *State) Message(channelID, messageID string) (*Message, error) {
 	return nil, errors.New("Message not found.")
 }
 
+// OnReady takes a Ready event and updates all internal state.
+func (s *State) onReady(se *Session, r *Ready) (err error) {
+	if s == nil {
+		return ErrNilState
+	}
+
+	s.Lock()
+	defer s.Unlock()
+
+	// We must track at least the current user for Voice, even
+	// if state is disabled, store the bare essentials.
+	if !se.StateEnabled {
+		ready := Ready{
+			Version:           r.Version,
+			SessionID:         r.SessionID,
+			HeartbeatInterval: r.HeartbeatInterval,
+			User:              r.User,
+		}
+
+		s.Ready = ready
+
+		return nil
+	}
+
+	s.Ready = *r
+
+	for _, g := range s.Guilds {
+		s.guildMap[g.ID] = g
+
+		for _, c := range g.Channels {
+			s.channelMap[c.ID] = c
+		}
+	}
+
+	for _, c := range s.PrivateChannels {
+		s.channelMap[c.ID] = c
+	}
+
+	return nil
+}
+
 // onInterface handles all events related to states.
 func (s *State) onInterface(se *Session, i interface{}) (err error) {
 	if s == nil {
 		return ErrNilState
 	}
+
+	r, ok := i.(*Ready)
+	if ok {
+		return s.onReady(se, r)
+	}
+
 	if !se.StateEnabled {
 		return nil
 	}
 
 	switch t := i.(type) {
-	case *Ready:
-		err = s.OnReady(t)
 	case *GuildCreate:
 		err = s.GuildAdd(t.Guild)
 	case *GuildUpdate:
@@ -685,6 +723,9 @@ func (s *State) onInterface(se *Session, i interface{}) (err error) {
 // userID    : The ID of the user to calculate permissions for.
 // channelID : The ID of the channel to calculate permission for.
 func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) {
+	if s == nil {
+		return 0, ErrNilState
+	}
 
 	channel, err := s.Channel(channelID)
 	if err != nil {
@@ -706,6 +747,13 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
 		return
 	}
 
+	for _, role := range guild.Roles {
+		if role.ID == guild.ID {
+			apermissions |= role.Permissions
+			break
+		}
+	}
+
 	for _, role := range guild.Roles {
 		for _, roleID := range member.Roles {
 			if role.ID == roleID {
@@ -715,7 +763,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
 		}
 	}
 
-	if apermissions&PermissionManageRoles > 0 {
+	if apermissions&PermissionAdministrator > 0 {
 		apermissions |= PermissionAll
 	}
 
@@ -738,7 +786,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
 		}
 	}
 
-	if apermissions&PermissionManageRoles > 0 {
+	if apermissions&PermissionAdministrator > 0 {
 		apermissions |= PermissionAllChannel
 	}
 

+ 161 - 113
structs.go

@@ -13,7 +13,7 @@ package discordgo
 
 import (
 	"encoding/json"
-	"reflect"
+	"strconv"
 	"sync"
 	"time"
 
@@ -53,6 +53,9 @@ type Session struct {
 	// Whether the Data Websocket is ready
 	DataReady bool // NOTE: Maye be deprecated soon
 
+	// Max number of REST API retries
+	MaxRestRetries int
+
 	// Status stores the currect status of the websocket connection
 	// this is being tested, may stay, may go away.
 	status int32
@@ -70,13 +73,10 @@ type Session struct {
 	// StateEnabled is true.
 	State *State
 
-	handlersMu sync.RWMutex
-	// This is a mapping of event struct 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
+	// Event handlers
+	handlersMu   sync.RWMutex
+	handlers     map[string][]*eventHandlerInstance
+	onceHandlers map[string][]*eventHandlerInstance
 
 	// The websocket connection.
 	wsConn *websocket.Conn
@@ -85,9 +85,7 @@ type Session struct {
 	listening chan interface{}
 
 	// used to deal with rate limits
-	// may switch to slices later
-	// TODO: performance test map vs slices
-	rateLimit rateLimitMutex
+	ratelimiter *RateLimiter
 
 	// sequence tracks the current gateway api websocket sequence number
 	sequence int
@@ -108,12 +106,6 @@ type rateLimitMutex struct {
 	// bucket map[string]*sync.Mutex // TODO :)
 }
 
-// A Resumed struct holds the data received in a RESUMED event
-type Resumed struct {
-	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
-	Trace             []string      `json:"_trace"`
-}
-
 // A VoiceRegion stores data for a specific voice region server.
 type VoiceRegion struct {
 	ID       string `json:"id"`
@@ -137,17 +129,17 @@ type ICEServer struct {
 
 // A Invite stores all data related to a specific Discord Guild or Channel invite.
 type Invite struct {
-	Guild     *Guild   `json:"guild"`
-	Channel   *Channel `json:"channel"`
-	Inviter   *User    `json:"inviter"`
-	Code      string   `json:"code"`
-	CreatedAt string   `json:"created_at"` // TODO make timestamp
-	MaxAge    int      `json:"max_age"`
-	Uses      int      `json:"uses"`
-	MaxUses   int      `json:"max_uses"`
-	XkcdPass  string   `json:"xkcdpass"`
-	Revoked   bool     `json:"revoked"`
-	Temporary bool     `json:"temporary"`
+	Guild     *Guild    `json:"guild"`
+	Channel   *Channel  `json:"channel"`
+	Inviter   *User     `json:"inviter"`
+	Code      string    `json:"code"`
+	CreatedAt Timestamp `json:"created_at"`
+	MaxAge    int       `json:"max_age"`
+	Uses      int       `json:"uses"`
+	MaxUses   int       `json:"max_uses"`
+	XkcdPass  string    `json:"xkcdpass"`
+	Revoked   bool      `json:"revoked"`
+	Temporary bool      `json:"temporary"`
 }
 
 // A Channel holds all data related to an individual Discord channel.
@@ -183,6 +175,17 @@ type Emoji struct {
 	RequireColons bool     `json:"require_colons"`
 }
 
+// APIName returns an correctly formatted API name for use in the MessageReactions endpoints.
+func (e *Emoji) APIName() string {
+	if e.ID != "" && e.Name != "" {
+		return e.Name + ":" + e.ID
+	}
+	if e.Name != "" {
+		return e.Name
+	}
+	return e.ID
+}
+
 // VerificationLevel type defination
 type VerificationLevel int
 
@@ -204,9 +207,10 @@ type Guild struct {
 	AfkChannelID                string            `json:"afk_channel_id"`
 	EmbedChannelID              string            `json:"embed_channel_id"`
 	OwnerID                     string            `json:"owner_id"`
-	JoinedAt                    string            `json:"joined_at"` // make this a timestamp
+	JoinedAt                    Timestamp         `json:"joined_at"`
 	Splash                      string            `json:"splash"`
 	AfkTimeout                  int               `json:"afk_timeout"`
+	MemberCount                 int               `json:"member_count"`
 	VerificationLevel           VerificationLevel `json:"verification_level"`
 	EmbedEnabled                bool              `json:"embed_enabled"`
 	Large                       bool              `json:"large"` // ??
@@ -217,7 +221,16 @@ type Guild struct {
 	Presences                   []*Presence       `json:"presences"`
 	Channels                    []*Channel        `json:"channels"`
 	VoiceStates                 []*VoiceState     `json:"voice_states"`
-	Unavailable                 *bool             `json:"unavailable"`
+	Unavailable                 bool              `json:"unavailable"`
+}
+
+// A UserGuild holds a brief version of a Guild
+type UserGuild struct {
+	ID          string `json:"id"`
+	Name        string `json:"name"`
+	Icon        string `json:"icon"`
+	Owner       bool   `json:"owner"`
+	Permissions int    `json:"permissions"`
 }
 
 // A GuildParams stores all the data needed to update discord guild settings
@@ -232,6 +245,7 @@ type Role struct {
 	ID          string `json:"id"`
 	Name        string `json:"name"`
 	Managed     bool   `json:"managed"`
+	Mentionable bool   `json:"mentionable"`
 	Hoist       bool   `json:"hoist"`
 	Color       int    `json:"color"`
 	Position    int    `json:"position"`
@@ -253,9 +267,11 @@ type VoiceState struct {
 
 // A Presence stores the online, offline, or idle and game status of Guild members.
 type Presence struct {
-	User   *User  `json:"user"`
-	Status string `json:"status"`
-	Game   *Game  `json:"game"`
+	User   *User    `json:"user"`
+	Status Status   `json:"status"`
+	Game   *Game    `json:"game"`
+	Nick   string   `json:"nick"`
+	Roles  []string `json:"roles"`
 }
 
 // A Game struct holds the name of the "playing .." game for a user
@@ -265,6 +281,38 @@ type Game struct {
 	URL  string `json:"url"`
 }
 
+// UnmarshalJSON unmarshals json to Game struct
+func (g *Game) UnmarshalJSON(bytes []byte) error {
+	temp := &struct {
+		Name string          `json:"name"`
+		Type json.RawMessage `json:"type"`
+		URL  string          `json:"url"`
+	}{}
+	err := json.Unmarshal(bytes, temp)
+	if err != nil {
+		return err
+	}
+	g.Name = temp.Name
+	g.URL = temp.URL
+
+	if temp.Type != nil {
+		err = json.Unmarshal(temp.Type, &g.Type)
+		if err == nil {
+			return nil
+		}
+
+		s := ""
+		err = json.Unmarshal(temp.Type, &s)
+		if err == nil {
+			g.Type, err = strconv.Atoi(s)
+		}
+
+		return err
+	}
+
+	return nil
+}
+
 // A Member stores user information for Guild members.
 type Member struct {
 	GuildID  string   `json:"guild_id"`
@@ -291,20 +339,34 @@ type User struct {
 
 // A Settings stores data for a specific users Discord client settings.
 type Settings struct {
-	RenderEmbeds            bool               `json:"render_embeds"`
-	InlineEmbedMedia        bool               `json:"inline_embed_media"`
-	InlineAttachmentMedia   bool               `json:"inline_attachment_media"`
-	EnableTtsCommand        bool               `json:"enable_tts_command"`
-	MessageDisplayCompact   bool               `json:"message_display_compact"`
-	ShowCurrentGame         bool               `json:"show_current_game"`
-	AllowEmailFriendRequest bool               `json:"allow_email_friend_request"`
-	ConvertEmoticons        bool               `json:"convert_emoticons"`
-	Locale                  string             `json:"locale"`
-	Theme                   string             `json:"theme"`
-	GuildPositions          []string           `json:"guild_positions"`
-	RestrictedGuilds        []string           `json:"restricted_guilds"`
-	FriendSourceFlags       *FriendSourceFlags `json:"friend_source_flags"`
-}
+	RenderEmbeds           bool               `json:"render_embeds"`
+	InlineEmbedMedia       bool               `json:"inline_embed_media"`
+	InlineAttachmentMedia  bool               `json:"inline_attachment_media"`
+	EnableTtsCommand       bool               `json:"enable_tts_command"`
+	MessageDisplayCompact  bool               `json:"message_display_compact"`
+	ShowCurrentGame        bool               `json:"show_current_game"`
+	ConvertEmoticons       bool               `json:"convert_emoticons"`
+	Locale                 string             `json:"locale"`
+	Theme                  string             `json:"theme"`
+	GuildPositions         []string           `json:"guild_positions"`
+	RestrictedGuilds       []string           `json:"restricted_guilds"`
+	FriendSourceFlags      *FriendSourceFlags `json:"friend_source_flags"`
+	Status                 Status             `json:"status"`
+	DetectPlatformAccounts bool               `json:"detect_platform_accounts"`
+	DeveloperMode          bool               `json:"developer_mode"`
+}
+
+// Status type defination
+type Status string
+
+// Constants for Status with the different current available status
+const (
+	StatusOnline       Status = "online"
+	StatusIdle         Status = "idle"
+	StatusDoNotDisturb Status = "dnd"
+	StatusInvisible    Status = "invisible"
+	StatusOffline      Status = "offline"
+)
 
 // FriendSourceFlags stores ... TODO :)
 type FriendSourceFlags struct {
@@ -313,32 +375,6 @@ type FriendSourceFlags struct {
 	MutualFriends bool `json:"mutual_friends"`
 }
 
-// An Event provides a basic initial struct for all websocket event.
-type Event struct {
-	Operation int             `json:"op"`
-	Sequence  int             `json:"s"`
-	Type      string          `json:"t"`
-	RawData   json.RawMessage `json:"d"`
-	Struct    interface{}     `json:"-"`
-}
-
-// A Ready stores all data for the websocket READY event.
-type Ready struct {
-	Version           int           `json:"v"`
-	SessionID         string        `json:"session_id"`
-	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
-	User              *User         `json:"user"`
-	ReadState         []*ReadState  `json:"read_state"`
-	PrivateChannels   []*Channel    `json:"private_channels"`
-	Guilds            []*Guild      `json:"guilds"`
-
-	// Undocumented fields
-	Settings          *Settings            `json:"user_settings"`
-	UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
-	Relationships     []*Relationship      `json:"relationships"`
-	Presences         []*Presence          `json:"presences"`
-}
-
 // A Relationship between the logged in user and Relationship.User
 type Relationship struct {
 	User *User  `json:"user"`
@@ -361,54 +397,21 @@ type ReadState struct {
 	ID            string `json:"id"`
 }
 
-// A TypingStart stores data for the typing start websocket event.
-type TypingStart struct {
-	UserID    string `json:"user_id"`
-	ChannelID string `json:"channel_id"`
-	Timestamp int    `json:"timestamp"`
+// An Ack is used to ack messages
+type Ack struct {
+	Token string `json:"token"`
 }
 
-// A PresenceUpdate stores data for the presence update websocket event.
-type PresenceUpdate struct {
-	Presence
-	GuildID string   `json:"guild_id"`
-	Roles   []string `json:"roles"`
-}
-
-// A MessageAck stores data for the message ack websocket event.
-type MessageAck struct {
-	MessageID string `json:"message_id"`
-	ChannelID string `json:"channel_id"`
-}
-
-// A GuildIntegrationsUpdate stores data for the guild integrations update
-// websocket event.
-type GuildIntegrationsUpdate struct {
-	GuildID string `json:"guild_id"`
-}
-
-// A GuildRole stores data for guild role websocket events.
+// A GuildRole stores data for guild roles.
 type GuildRole struct {
 	Role    *Role  `json:"role"`
 	GuildID string `json:"guild_id"`
 }
 
-// A GuildRoleDelete stores data for the guild role delete websocket event.
-type GuildRoleDelete struct {
-	RoleID  string `json:"role_id"`
-	GuildID string `json:"guild_id"`
-}
-
 // A GuildBan stores data for a guild ban.
 type GuildBan struct {
-	User    *User  `json:"user"`
-	GuildID string `json:"guild_id"`
-}
-
-// A GuildEmojisUpdate stores data for a guild emoji update event.
-type GuildEmojisUpdate struct {
-	GuildID string   `json:"guild_id"`
-	Emojis  []*Emoji `json:"emojis"`
+	Reason string `json:"reason"`
+	User   *User  `json:"user"`
 }
 
 // A GuildIntegration stores data for a guild integration.
@@ -464,6 +467,41 @@ type UserGuildSettingsEdit struct {
 	ChannelOverrides     map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"`
 }
 
+// An APIErrorMessage is an api error message returned from discord
+type APIErrorMessage struct {
+	Code    int    `json:"code"`
+	Message string `json:"message"`
+}
+
+// Webhook stores the data for a webhook.
+type Webhook struct {
+	ID        string `json:"id"`
+	GuildID   string `json:"guild_id"`
+	ChannelID string `json:"channel_id"`
+	User      *User  `json:"user"`
+	Name      string `json:"name"`
+	Avatar    string `json:"avatar"`
+	Token     string `json:"token"`
+}
+
+// WebhookParams is a struct for webhook params, used in the WebhookExecute command.
+type WebhookParams struct {
+	Content   string          `json:"content,omitempty"`
+	Username  string          `json:"username,omitempty"`
+	AvatarURL string          `json:"avatar_url,omitempty"`
+	TTS       bool            `json:"tts,omitempty"`
+	File      string          `json:"file,omitempty"`
+	Embeds    []*MessageEmbed `json:"embeds,omitempty"`
+}
+
+// MessageReaction stores the data for a message reaction.
+type MessageReaction struct {
+	UserID    string `json:"user_id"`
+	MessageID string `json:"message_id"`
+	Emoji     Emoji  `json:"emoji"`
+	ChannelID string `json:"channel_id"`
+}
+
 // Constants for the different bit offsets of text channel permissions
 const (
 	PermissionReadMessages = 1 << (iota + 10)
@@ -474,6 +512,7 @@ const (
 	PermissionAttachFiles
 	PermissionReadMessageHistory
 	PermissionMentionEveryone
+	PermissionUseExternalEmojis
 )
 
 // Constants for the different bit offsets of voice permissions
@@ -486,12 +525,21 @@ const (
 	PermissionVoiceUseVAD
 )
 
+// Constants for general management.
+const (
+	PermissionChangeNickname = 1 << (iota + 26)
+	PermissionManageNicknames
+	PermissionManageRoles
+	PermissionManageWebhooks
+	PermissionManageEmojis
+)
+
 // Constants for the different bit offsets of general permissions
 const (
 	PermissionCreateInstantInvite = 1 << iota
 	PermissionKickMembers
 	PermissionBanMembers
-	PermissionManageRoles
+	PermissionAdministrator
 	PermissionManageChannels
 	PermissionManageServer
 

+ 123 - 0
tools/cmd/eventhandlers/main.go

@@ -0,0 +1,123 @@
+package main
+
+import (
+	"bytes"
+	"go/format"
+	"go/parser"
+	"go/token"
+	"io/ioutil"
+	"log"
+	"path/filepath"
+	"regexp"
+	"sort"
+	"strings"
+	"text/template"
+)
+
+var eventHandlerTmpl = template.Must(template.New("eventHandler").Funcs(template.FuncMap{
+	"constName":      constName,
+	"isDiscordEvent": isDiscordEvent,
+	"privateName":    privateName,
+}).Parse(`// Code generated by \"eventhandlers\"; DO NOT EDIT
+// See events.go
+
+package discordgo
+
+// Following are all the event types.
+// Event type values are used to match the events returned by Discord.
+// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
+const ({{range .}}
+  {{privateName .}}EventType = "{{constName .}}"{{end}}
+)
+{{range .}}
+// {{privateName .}}EventHandler is an event handler for {{.}} events.
+type {{privateName .}}EventHandler func(*Session, *{{.}})
+
+// Type returns the event type for {{.}} events.
+func (eh {{privateName .}}EventHandler) Type() string {
+  return {{privateName .}}EventType
+}
+
+// New returns a new instance of {{.}}.
+func (eh {{privateName .}}EventHandler) New() interface{} {
+  return &{{.}}{}
+}
+
+// Handle is the handler for {{.}} events.
+func (eh {{privateName .}}EventHandler) Handle(s *Session, i interface{}) {
+  if t, ok := i.(*{{.}}); ok {
+    eh(s, t)
+  }
+}
+{{end}}
+func handlerForInterface(handler interface{}) EventHandler {
+  switch v := handler.(type) {
+  case func(*Session, interface{}):
+    return interfaceEventHandler(v){{range .}}
+  case func(*Session, *{{.}}):
+    return {{privateName .}}EventHandler(v){{end}}
+  }
+
+  return nil
+}
+func init() { {{range .}}{{if isDiscordEvent .}}
+  registerInterfaceProvider({{privateName .}}EventHandler(nil)){{end}}{{end}}
+}
+`))
+
+func main() {
+	var buf bytes.Buffer
+	dir := filepath.Dir(".")
+
+	fs := token.NewFileSet()
+	parsedFile, err := parser.ParseFile(fs, "events.go", nil, 0)
+	if err != nil {
+		log.Fatalf("warning: internal error: could not parse events.go: %s", err)
+		return
+	}
+
+	names := []string{}
+	for object := range parsedFile.Scope.Objects {
+		names = append(names, object)
+	}
+	sort.Strings(names)
+	eventHandlerTmpl.Execute(&buf, names)
+
+	src, err := format.Source(buf.Bytes())
+	if err != nil {
+		log.Println("warning: internal error: invalid Go generated:", err)
+		src = buf.Bytes()
+	}
+
+	err = ioutil.WriteFile(filepath.Join(dir, strings.ToLower("eventhandlers.go")), src, 0644)
+	if err != nil {
+		log.Fatal(buf, "writing output: %s", err)
+	}
+}
+
+var constRegexp = regexp.MustCompile("([a-z])([A-Z])")
+
+func constCase(name string) string {
+	return strings.ToUpper(constRegexp.ReplaceAllString(name, "${1}_${2}"))
+}
+
+func isDiscordEvent(name string) bool {
+	switch {
+	case name == "Connect", name == "Disconnect", name == "Event", name == "RateLimit", name == "Interface":
+		return false
+	default:
+		return true
+	}
+}
+
+func constName(name string) string {
+	if !isDiscordEvent(name) {
+		return "__" + constCase(name) + "__"
+	}
+
+	return constCase(name)
+}
+
+func privateName(name string) string {
+	return strings.ToLower(string(name[0])) + name[1:]
+}

+ 58 - 0
types.go

@@ -0,0 +1,58 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains custom types, currently only a timestamp wrapper.
+
+package discordgo
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+)
+
+// Timestamp stores a timestamp, as sent by the Discord API.
+type Timestamp string
+
+// Parse parses a timestamp string into a time.Time object.
+// The only time this can fail is if Discord changes their timestamp format.
+func (t Timestamp) Parse() (time.Time, error) {
+	return time.Parse(time.RFC3339, string(t))
+}
+
+// RESTError stores error information about a request with a bad response code.
+// Message is not always present, there are cases where api calls can fail
+// without returning a json message.
+type RESTError struct {
+	Request      *http.Request
+	Response     *http.Response
+	ResponseBody []byte
+
+	Message *APIErrorMessage // Message may be nil.
+}
+
+func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError {
+	restErr := &RESTError{
+		Request:      req,
+		Response:     resp,
+		ResponseBody: body,
+	}
+
+	// Attempt to decode the error and assume no message was provided if it fails
+	var msg *APIErrorMessage
+	err := json.Unmarshal(body, &msg)
+	if err == nil {
+		restErr.Message = msg
+	}
+
+	return restErr
+}
+
+func (r RESTError) Error() string {
+	return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
+}

+ 24 - 0
types_test.go

@@ -0,0 +1,24 @@
+package discordgo
+
+import (
+	"testing"
+	"time"
+)
+
+func TestTimestampParse(t *testing.T) {
+	ts, err := Timestamp("2016-03-24T23:15:59.605000+00:00").Parse()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if ts.Year() != 2016 || ts.Month() != time.March || ts.Day() != 24 {
+		t.Error("Incorrect date")
+	}
+	if ts.Hour() != 23 || ts.Minute() != 15 || ts.Second() != 59 {
+		t.Error("Incorrect time")
+	}
+
+	_, offset := ts.Zone()
+	if offset != 0 {
+		t.Error("Incorrect timezone")
+	}
+}

+ 2 - 2
voice.go

@@ -441,7 +441,7 @@ func (v *VoiceConnection) onEvent(message []byte) {
 		}
 
 	default:
-		v.log(LogError, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
+		v.log(LogDebug, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
 	}
 
 	return
@@ -570,7 +570,7 @@ func (v *VoiceConnection) udpOpen() (err error) {
 		return fmt.Errorf("received udp packet too small")
 	}
 
-	// Loop over position 4 though 20 to grab the IP address
+	// Loop over position 4 through 20 to grab the IP address
 	// Should never be beyond position 20.
 	var ip string
 	for i := 4; i < 20; i++ {

+ 63 - 41
wsapi.go

@@ -17,9 +17,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"log"
 	"net/http"
-	"reflect"
 	"runtime"
 	"time"
 
@@ -47,6 +45,17 @@ func (s *Session) Open() (err error) {
 		}
 	}()
 
+	// A basic state is a hard requirement for Voice.
+	if s.State == nil {
+		state := NewState()
+		state.TrackChannels = false
+		state.TrackEmojis = false
+		state.TrackMembers = false
+		state.TrackRoles = false
+		state.TrackVoice = false
+		s.State = state
+	}
+
 	if s.wsConn != nil {
 		err = errors.New("Web socket already opened.")
 		return
@@ -111,9 +120,8 @@ func (s *Session) Open() (err error) {
 
 	s.Unlock()
 
-	s.initialize()
 	s.log(LogInformational, "emit connect event")
-	s.handle(&Connect{})
+	s.handleEvent(connectEventType, &Connect{})
 
 	s.log(LogInformational, "exiting")
 	return
@@ -269,6 +277,44 @@ func (s *Session) UpdateStatus(idle int, game string) (err error) {
 	return s.UpdateStreamingStatus(idle, game, "")
 }
 
+type requestGuildMembersData struct {
+	GuildID string `json:"guild_id"`
+	Query   string `json:"query"`
+	Limit   int    `json:"limit"`
+}
+
+type requestGuildMembersOp struct {
+	Op   int                     `json:"op"`
+	Data requestGuildMembersData `json:"d"`
+}
+
+// RequestGuildMembers requests guild members from the gateway
+// The gateway responds with GuildMembersChunk events
+// guildID  : The ID of the guild to request members of
+// query    : String that username starts with, leave empty to return all members
+// limit    : Max number of items to return, or 0 to request all members matched
+func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err error) {
+	s.log(LogInformational, "called")
+
+	s.RLock()
+	defer s.RUnlock()
+	if s.wsConn == nil {
+		return errors.New("no websocket connection exists")
+	}
+
+	data := requestGuildMembersData{
+		GuildID: guildID,
+		Query:   query,
+		Limit:   limit,
+	}
+
+	s.wsMutex.Lock()
+	err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data})
+	s.wsMutex.Unlock()
+
+	return
+}
+
 // onEvent is the "event handler" for all messages received on the
 // Discord Gateway API websocket connection.
 //
@@ -361,16 +407,12 @@ func (s *Session) onEvent(messageType int, message []byte) {
 	// Store the message sequence
 	s.sequence = e.Sequence
 
-	// Map event to registered event handlers and pass it along
-	// to any registered functions
-	i := eventToInterface[e.Type]
-	if i != nil {
-
-		// Create a new instance of the event type.
-		i = reflect.New(reflect.TypeOf(i)).Interface()
+	// Map event to registered event handlers and pass it along to any registered handlers.
+	if eh, ok := registeredInterfaceProviders[e.Type]; ok {
+		e.Struct = eh.New()
 
 		// Attempt to unmarshal our event.
-		if err = json.Unmarshal(e.RawData, i); err != nil {
+		if err = json.Unmarshal(e.RawData, e.Struct); err != nil {
 			s.log(LogError, "error unmarshalling %s event, %s", e.Type, err)
 		}
 
@@ -381,30 +423,19 @@ func (s *Session) onEvent(messageType int, message []byte) {
 		// it's better to pass along what we received than nothing at all.
 		// TODO: Think about that decision :)
 		// Either way, READY events must fire, even with errors.
-		go s.handle(i)
-
+		s.handleEvent(e.Type, e.Struct)
 	} else {
 		s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData))
 	}
 
-	// Emit event to the OnEvent handler
-	e.Struct = i
-	go s.handle(e)
+	// For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
+	s.handleEvent(eventEventType, e)
 }
 
 // ------------------------------------------------------------------------------------------------
 // Code related to voice connections that initiate over the data websocket
 // ------------------------------------------------------------------------------------------------
 
-// A VoiceServerUpdate stores the data received during the Voice Server Update
-// data websocket event. This data is used during the initial Voice Channel
-// join handshaking.
-type VoiceServerUpdate struct {
-	Token    string `json:"token"`
-	GuildID  string `json:"guild_id"`
-	Endpoint string `json:"endpoint"`
-}
-
 type voiceChannelJoinData struct {
 	GuildID   *string `json:"guild_id"`
 	ChannelID *string `json:"channel_id"`
@@ -461,7 +492,7 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
 }
 
 // onVoiceStateUpdate handles Voice State Update events on the data websocket.
-func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
+func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {
 
 	// If we don't have a connection for the channel, don't bother
 	if st.ChannelID == "" {
@@ -474,22 +505,13 @@ func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
 		return
 	}
 
-	// Need to have this happen at login and store it in the Session
-	// TODO : This should be done upon connecting to Discord, or
-	// be moved to a small helper function
-	self, err := s.User("@me") // TODO: move to Login/New
-	if err != nil {
-		log.Println(err)
-		return
-	}
-
-	// We only care about events that are about us
-	if st.UserID != self.ID {
+	// We only care about events that are about us.
+	if s.State.User.ID != st.UserID {
 		return
 	}
 
 	// Store the SessionID for later use.
-	voice.UserID = self.ID // TODO: Review
+	voice.UserID = st.UserID
 	voice.sessionID = st.SessionID
 }
 
@@ -498,7 +520,7 @@ func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
 // This is also fired if the Guild's voice region changes while connected
 // to a voice channel.  In that case, need to re-establish connection to
 // the new region endpoint.
-func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) {
+func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
 
 	s.log(LogInformational, "called")
 
@@ -673,7 +695,7 @@ func (s *Session) Close() (err error) {
 	s.Unlock()
 
 	s.log(LogInformational, "emit disconnect event")
-	s.handle(&Disconnect{})
+	s.handleEvent(disconnectEventType, &Disconnect{})
 
 	return
 }