Thread safety and more events.
This commit is contained in:
parent
fb6ae92555
commit
8ffaa85b0b
4 changed files with 66 additions and 122 deletions
34
discord.go
34
discord.go
|
@ -123,6 +123,9 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) AddHandler(handler interface{}) {
|
func (s *Session) AddHandler(handler interface{}) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
handlerType := reflect.TypeOf(handler)
|
handlerType := reflect.TypeOf(handler)
|
||||||
|
|
||||||
if handlerType.NumIn() != 2 {
|
if handlerType.NumIn() != 2 {
|
||||||
|
@ -133,12 +136,14 @@ func (s *Session) AddHandler(handler interface{}) {
|
||||||
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
|
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
|
||||||
}
|
}
|
||||||
|
|
||||||
eventType := handlerType.In(1)
|
|
||||||
|
|
||||||
if s.Handlers == nil {
|
if s.Handlers == nil {
|
||||||
|
s.Unlock()
|
||||||
s.initialize()
|
s.initialize()
|
||||||
|
s.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eventType := handlerType.In(1)
|
||||||
|
|
||||||
handlers := s.Handlers[eventType]
|
handlers := s.Handlers[eventType]
|
||||||
if handlers == nil {
|
if handlers == nil {
|
||||||
handlers = []interface{}{}
|
handlers = []interface{}{}
|
||||||
|
@ -149,6 +154,9 @@ func (s *Session) AddHandler(handler interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Handle(event interface{}) (handled bool) {
|
func (s *Session) Handle(event interface{}) (handled bool) {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
eventType := reflect.TypeOf(event)
|
eventType := reflect.TypeOf(event)
|
||||||
|
|
||||||
handlers, ok := s.Handlers[eventType]
|
handlers, ok := s.Handlers[eventType]
|
||||||
|
@ -164,17 +172,23 @@ func (s *Session) Handle(event interface{}) (handled bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize adds internal handlers such as onEvent and state tracking
|
// initialize adds all internal handlers and state tracking handlers.
|
||||||
// handlers.
|
|
||||||
func (s *Session) initialize() {
|
func (s *Session) initialize() {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
s.Handlers = map[interface{}][]interface{}{}
|
s.Handlers = map[interface{}][]interface{}{}
|
||||||
s.AddHandler(s.ready)
|
s.AddHandler(s.onReady)
|
||||||
s.AddHandler(s.State.ready)
|
s.AddHandler(s.onVoiceServerUpdate)
|
||||||
s.AddHandler(s.State.messageCreate)
|
s.AddHandler(s.onVoiceStateUpdate)
|
||||||
s.AddHandler(s.State.messageUpdate)
|
|
||||||
s.AddHandler(s.State.messageDelete)
|
s.AddHandler(s.State.onReady)
|
||||||
|
s.AddHandler(s.State.onMessageCreate)
|
||||||
|
s.AddHandler(s.State.onMessageUpdate)
|
||||||
|
s.AddHandler(s.State.onMessageDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) ready(se *Session, r *Ready) {
|
// onReady handles the ready event.
|
||||||
|
func (s *Session) onReady(se *Session, r *Ready) {
|
||||||
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
|
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
|
||||||
}
|
}
|
||||||
|
|
22
state.go
22
state.go
|
@ -469,30 +469,30 @@ func (s *State) Message(channelID, messageID string) (*Message, error) {
|
||||||
return nil, errors.New("Message not found.")
|
return nil, errors.New("Message not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ready is an event handler.
|
// onReady handles the ready event.
|
||||||
func (s *State) ready(se *Session, r *Ready) {
|
func (s *State) onReady(se *Session, r *Ready) {
|
||||||
if se.StateEnabled {
|
if se.StateEnabled {
|
||||||
s.OnReady(r)
|
s.OnReady(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// messageCreate is an event handler.
|
// onMessageCreate handles the messageCreate event.
|
||||||
func (s *State) messageCreate(se *Session, m *MessageCreate) {
|
func (s *State) onMessageCreate(se *Session, m *MessageCreate) {
|
||||||
if se.StateEnabled {
|
if se.StateEnabled {
|
||||||
s.MessageAdd(&m.Message)
|
s.MessageAdd(m.Message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// messageUpdate is an event handler.
|
// onMessageUpdate handles the messageUpdate event.
|
||||||
func (s *State) messageUpdate(se *Session, m *MessageUpdate) {
|
func (s *State) onMessageUpdate(se *Session, m *MessageUpdate) {
|
||||||
if se.StateEnabled {
|
if se.StateEnabled {
|
||||||
s.MessageAdd(&m.Message)
|
s.MessageAdd(m.Message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// messageDelete is an event handler.
|
// onMessageDelete handles the messageDelete event.
|
||||||
func (s *State) messageDelete(se *Session, m *MessageDelete) {
|
func (s *State) onMessageDelete(se *Session, m *MessageDelete) {
|
||||||
if se.StateEnabled {
|
if se.StateEnabled {
|
||||||
s.MessageRemove(&m.Message)
|
s.MessageRemove(m.Message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
structs.go
38
structs.go
|
@ -29,46 +29,8 @@ type Session struct {
|
||||||
Token string // Authentication token for this session
|
Token string // Authentication token for this session
|
||||||
Debug bool // Debug for printing JSON request/responses
|
Debug bool // Debug for printing JSON request/responses
|
||||||
|
|
||||||
// Settable Callback functions for Internal Events
|
|
||||||
// OnConnect is called when the websocket connection opens.
|
|
||||||
OnConnect func(*Session)
|
|
||||||
// OnDisconnect is called when the websocket connection closes.
|
|
||||||
// This is a good handler to add reconnection logic to.
|
|
||||||
OnDisconnect func(*Session)
|
|
||||||
|
|
||||||
Handlers map[interface{}][]interface{}
|
Handlers map[interface{}][]interface{}
|
||||||
|
|
||||||
// Settable Callback functions for Websocket Events
|
|
||||||
OnEvent func(*Session, *Event)
|
|
||||||
OnReady func(*Session, *Ready)
|
|
||||||
OnTypingStart func(*Session, *TypingStart)
|
|
||||||
OnMessageCreate func(*Session, *Message)
|
|
||||||
OnMessageUpdate func(*Session, *Message)
|
|
||||||
OnMessageDelete func(*Session, *Message)
|
|
||||||
OnMessageAck func(*Session, *MessageAck)
|
|
||||||
OnUserUpdate func(*Session, *User)
|
|
||||||
OnPresenceUpdate func(*Session, *PresenceUpdate)
|
|
||||||
OnVoiceServerUpdate func(*Session, *VoiceServerUpdate)
|
|
||||||
OnVoiceStateUpdate func(*Session, *VoiceState)
|
|
||||||
OnChannelCreate func(*Session, *Channel)
|
|
||||||
OnChannelUpdate func(*Session, *Channel)
|
|
||||||
OnChannelDelete func(*Session, *Channel)
|
|
||||||
OnGuildCreate func(*Session, *Guild)
|
|
||||||
OnGuildUpdate func(*Session, *Guild)
|
|
||||||
OnGuildDelete func(*Session, *Guild)
|
|
||||||
OnGuildMemberAdd func(*Session, *Member)
|
|
||||||
OnGuildMemberRemove func(*Session, *Member)
|
|
||||||
OnGuildMemberDelete func(*Session, *Member)
|
|
||||||
OnGuildMemberUpdate func(*Session, *Member)
|
|
||||||
OnGuildRoleCreate func(*Session, *GuildRole)
|
|
||||||
OnGuildRoleUpdate func(*Session, *GuildRole)
|
|
||||||
OnGuildRoleDelete func(*Session, *GuildRoleDelete)
|
|
||||||
OnGuildIntegrationsUpdate func(*Session, *GuildIntegrationsUpdate)
|
|
||||||
OnGuildBanAdd func(*Session, *GuildBan)
|
|
||||||
OnGuildBanRemove func(*Session, *GuildBan)
|
|
||||||
OnGuildEmojisUpdate func(*Session, *GuildEmojisUpdate)
|
|
||||||
OnUserSettingsUpdate func(*Session, map[string]interface{}) // TODO: Find better way?
|
|
||||||
|
|
||||||
// Exposed but should not be modified by User.
|
// Exposed but should not be modified by User.
|
||||||
SessionID string // from websocket READY packet
|
SessionID string // from websocket READY packet
|
||||||
DataReady bool // Set to true when Data Websocket is ready
|
DataReady bool // Set to true when Data Websocket is ready
|
||||||
|
|
94
wsapi.go
94
wsapi.go
|
@ -86,9 +86,7 @@ func (s *Session) Open() (err error) {
|
||||||
|
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
||||||
if s.OnConnect != nil {
|
s.Handle(&Connect{})
|
||||||
s.OnConnect(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -112,9 +110,7 @@ func (s *Session) Close() (err error) {
|
||||||
|
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
||||||
if s.OnDisconnect != nil {
|
s.Handle(&Disconnect{})
|
||||||
s.OnDisconnect(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -255,8 +251,12 @@ func (s *Session) UpdateStatus(idle int, game string) (err error) {
|
||||||
// Events will be handled by any implemented handler in Session.
|
// Events will be handled by any implemented handler in Session.
|
||||||
// All unhandled events will then be handled by OnEvent.
|
// All unhandled events will then be handled by OnEvent.
|
||||||
func (s *Session) event(messageType int, message []byte) {
|
func (s *Session) event(messageType int, message []byte) {
|
||||||
|
s.RLock()
|
||||||
if s.Handlers == nil {
|
if s.Handlers == nil {
|
||||||
|
s.RUnlock()
|
||||||
s.initialize()
|
s.initialize()
|
||||||
|
} else {
|
||||||
|
s.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -291,6 +291,9 @@ func (s *Session) event(messageType int, message []byte) {
|
||||||
|
|
||||||
var i interface{}
|
var i interface{}
|
||||||
|
|
||||||
|
// TODO(iopred): Figure out a clean way to do this with a map, simply
|
||||||
|
// creating a map[string]interface{} will not work, as that will reuse
|
||||||
|
// the same instance for each event.
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case "READY":
|
case "READY":
|
||||||
i = &Ready{}
|
i = &Ready{}
|
||||||
|
@ -304,63 +307,28 @@ func (s *Session) event(messageType int, message []byte) {
|
||||||
i = &PresenceUpdate{}
|
i = &PresenceUpdate{}
|
||||||
case "TYPING_START":
|
case "TYPING_START":
|
||||||
i = &TypingStart{}
|
i = &TypingStart{}
|
||||||
|
case "VOICE_SERVER_UPDATE":
|
||||||
|
i = &VoiceServerUpdate{}
|
||||||
|
case "VOICE_STATE_UPDATE":
|
||||||
|
i = &VoiceStateUpdate{}
|
||||||
|
case "USER_UPDATE":
|
||||||
|
i = &UserUpdate{}
|
||||||
|
case "MESSAGE_ACK":
|
||||||
|
i = &MessageAck{}
|
||||||
|
case "GUILD_ROLE_CREATE":
|
||||||
|
i = &GuildRoleCreate{}
|
||||||
|
case "GUILD_ROLE_UPDATE":
|
||||||
|
i = &GuildRoleUpdate{}
|
||||||
|
case "GUILD_ROLE_DELETE":
|
||||||
|
i = &GuildRoleDelete{}
|
||||||
|
case "GUILD_INTEGRATIONS_UPDATE":
|
||||||
|
i = &GuildIntegrationsUpdate{}
|
||||||
|
case "GUILD_BAN_ADD":
|
||||||
|
i = &GuildBanAdd{}
|
||||||
|
case "GUILD_BAN_REMOVE":
|
||||||
|
i = &GuildBanRemove{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// case "VOICE_SERVER_UPDATE":
|
|
||||||
// if s.Voice == nil && s.OnVoiceServerUpdate == nil {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// var st *VoiceServerUpdate
|
|
||||||
// if err = unmarshalEvent(e, &st); err == nil {
|
|
||||||
// if s.Voice != nil {
|
|
||||||
// s.onVoiceServerUpdate(st)
|
|
||||||
// }
|
|
||||||
// if s.OnVoiceServerUpdate != nil {
|
|
||||||
// s.OnVoiceServerUpdate(s, st)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if s.OnVoiceServerUpdate != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// case "VOICE_STATE_UPDATE":
|
|
||||||
// if s.Voice == nil && s.OnVoiceStateUpdate == nil {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// var st *VoiceState
|
|
||||||
// if err = unmarshalEvent(e, &st); err == nil {
|
|
||||||
// if s.Voice != nil {
|
|
||||||
// s.onVoiceStateUpdate(st)
|
|
||||||
// }
|
|
||||||
// if s.OnVoiceStateUpdate != nil {
|
|
||||||
// s.OnVoiceStateUpdate(s, st)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if s.OnVoiceStateUpdate != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// case "USER_UPDATE":
|
|
||||||
// if s.OnUserUpdate != nil {
|
|
||||||
// var st *User
|
|
||||||
// if err = unmarshalEvent(e, &st); err == nil {
|
|
||||||
// s.OnUserUpdate(s, st)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /* Never seen this come in but saw it in another Library.
|
|
||||||
// case "MESSAGE_ACK":
|
|
||||||
// if s.OnMessageAck != nil {
|
|
||||||
// }
|
|
||||||
// */
|
|
||||||
|
|
||||||
// case "MESSAGE_ACK":
|
|
||||||
// if s.OnMessageAck != nil {
|
|
||||||
// var st *MessageAck
|
|
||||||
// if err = unmarshalEvent(e, &st); err == nil {
|
|
||||||
// s.OnMessageAck(s, st)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// case "CHANNEL_CREATE":
|
// case "CHANNEL_CREATE":
|
||||||
// if !s.StateEnabled && s.OnChannelCreate == nil {
|
// if !s.StateEnabled && s.OnChannelCreate == nil {
|
||||||
// break
|
// break
|
||||||
|
@ -696,7 +664,7 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (err error)
|
||||||
// onVoiceStateUpdate handles Voice State Update events on the data
|
// onVoiceStateUpdate handles Voice State Update events on the data
|
||||||
// websocket. This comes immediately after the call to VoiceChannelJoin
|
// websocket. This comes immediately after the call to VoiceChannelJoin
|
||||||
// for the session user.
|
// for the session user.
|
||||||
func (s *Session) onVoiceStateUpdate(st *VoiceState) {
|
func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
|
||||||
|
|
||||||
// Need to have this happen at login and store it in the Session
|
// Need to have this happen at login and store it in the Session
|
||||||
// TODO : This should be done upon connecting to Discord, or
|
// TODO : This should be done upon connecting to Discord, or
|
||||||
|
@ -722,7 +690,7 @@ func (s *Session) onVoiceStateUpdate(st *VoiceState) {
|
||||||
// onVoiceServerUpdate handles the Voice Server Update data websocket event.
|
// onVoiceServerUpdate handles the Voice Server Update data websocket event.
|
||||||
// This event tells us the information needed to open a voice websocket
|
// This event tells us the information needed to open a voice websocket
|
||||||
// connection and should happen after the VOICE_STATE event.
|
// connection and should happen after the VOICE_STATE event.
|
||||||
func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
|
func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) {
|
||||||
|
|
||||||
// Store values for later use
|
// Store values for later use
|
||||||
s.Voice.token = st.Token
|
s.Voice.token = st.Token
|
||||||
|
|
Loading…
Reference in a new issue