Merge branch 'develop'

This commit is contained in:
Chris Rhodes 2016-12-10 06:42:35 -08:00
commit 002b6b91ca
21 changed files with 2907 additions and 570 deletions

View file

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

View file

@ -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)
}

View file

@ -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)

View file

@ -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
event.go Normal file
View file

@ -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
eventhandlers.go Normal file
View file

@ -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))
}

243
events.go
View file

@ -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{},
}
import (
"encoding/json"
"time"
)
// Connect is an empty struct for an event.
// 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"`
// Undocumented fields
Settings *Settings `json:"user_settings"`
UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
Relationships []*Relationship `json:"relationships"`
Presences []*Presence `json:"presences"`
}
// MessageDelete is a wrapper struct for an event.
type MessageDelete struct {
*Message
}
// 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.
type PresencesReplace []*Presence
// VoiceStateUpdate is a wrapper struct for an event.
type VoiceStateUpdate struct {
*VoiceState
// A GuildRoleDelete is the data for a GuildRoleDelete event.
type GuildRoleDelete struct {
RoleID string `json:"role_id"`
GuildID string `json:"guild_id"`
}
// UserUpdate is a wrapper struct for an event.
// 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
// 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"`
}
// 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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
ratelimit.go Normal file
View file

@ -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
ratelimit_test.go Normal file
View file

@ -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 Load diff

View file

@ -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)
}
}
*/

120
state.go
View file

@ -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 {
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
}

View file

@ -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
// Event handlers
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
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"`
@ -141,7 +133,7 @@ type Invite struct {
Channel *Channel `json:"channel"`
Inviter *User `json:"inviter"`
Code string `json:"code"`
CreatedAt string `json:"created_at"` // TODO make timestamp
CreatedAt Timestamp `json:"created_at"`
MaxAge int `json:"max_age"`
Uses int `json:"uses"`
MaxUses int `json:"max_uses"`
@ -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"`
@ -254,8 +268,10 @@ 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"`
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"`
@ -297,15 +345,29 @@ type Settings struct {
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"`
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 {
All bool `json:"all"`
@ -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 {
Reason string `json:"reason"`
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"`
}
// 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

View file

@ -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
types.go Normal file
View file

@ -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
types_test.go Normal file
View file

@ -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")
}
}

View file

@ -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++ {

104
wsapi.go
View file

@ -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
}