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{}) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
handlerType := reflect.TypeOf(handler)
|
||||
|
||||
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.")
|
||||
}
|
||||
|
||||
eventType := handlerType.In(1)
|
||||
|
||||
if s.Handlers == nil {
|
||||
s.Unlock()
|
||||
s.initialize()
|
||||
s.Lock()
|
||||
}
|
||||
|
||||
eventType := handlerType.In(1)
|
||||
|
||||
handlers := s.Handlers[eventType]
|
||||
if handlers == nil {
|
||||
handlers = []interface{}{}
|
||||
|
@ -149,6 +154,9 @@ func (s *Session) AddHandler(handler interface{}) {
|
|||
}
|
||||
|
||||
func (s *Session) Handle(event interface{}) (handled bool) {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
eventType := reflect.TypeOf(event)
|
||||
|
||||
handlers, ok := s.Handlers[eventType]
|
||||
|
@ -164,17 +172,23 @@ func (s *Session) Handle(event interface{}) (handled bool) {
|
|||
return
|
||||
}
|
||||
|
||||
// initialize adds internal handlers such as onEvent and state tracking
|
||||
// handlers.
|
||||
// initialize adds all internal handlers and state tracking handlers.
|
||||
func (s *Session) initialize() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.Handlers = map[interface{}][]interface{}{}
|
||||
s.AddHandler(s.ready)
|
||||
s.AddHandler(s.State.ready)
|
||||
s.AddHandler(s.State.messageCreate)
|
||||
s.AddHandler(s.State.messageUpdate)
|
||||
s.AddHandler(s.State.messageDelete)
|
||||
s.AddHandler(s.onReady)
|
||||
s.AddHandler(s.onVoiceServerUpdate)
|
||||
s.AddHandler(s.onVoiceStateUpdate)
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
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.")
|
||||
}
|
||||
|
||||
// ready is an event handler.
|
||||
func (s *State) ready(se *Session, r *Ready) {
|
||||
// onReady handles the ready event.
|
||||
func (s *State) onReady(se *Session, r *Ready) {
|
||||
if se.StateEnabled {
|
||||
s.OnReady(r)
|
||||
}
|
||||
}
|
||||
|
||||
// messageCreate is an event handler.
|
||||
func (s *State) messageCreate(se *Session, m *MessageCreate) {
|
||||
// onMessageCreate handles the messageCreate event.
|
||||
func (s *State) onMessageCreate(se *Session, m *MessageCreate) {
|
||||
if se.StateEnabled {
|
||||
s.MessageAdd(&m.Message)
|
||||
s.MessageAdd(m.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// messageUpdate is an event handler.
|
||||
func (s *State) messageUpdate(se *Session, m *MessageUpdate) {
|
||||
// onMessageUpdate handles the messageUpdate event.
|
||||
func (s *State) onMessageUpdate(se *Session, m *MessageUpdate) {
|
||||
if se.StateEnabled {
|
||||
s.MessageAdd(&m.Message)
|
||||
s.MessageAdd(m.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// messageDelete is an event handler.
|
||||
func (s *State) messageDelete(se *Session, m *MessageDelete) {
|
||||
// onMessageDelete handles the messageDelete event.
|
||||
func (s *State) onMessageDelete(se *Session, m *MessageDelete) {
|
||||
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
|
||||
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{}
|
||||
|
||||
// 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.
|
||||
SessionID string // from websocket READY packet
|
||||
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()
|
||||
|
||||
if s.OnConnect != nil {
|
||||
s.OnConnect(s)
|
||||
}
|
||||
s.Handle(&Connect{})
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -112,9 +110,7 @@ func (s *Session) Close() (err error) {
|
|||
|
||||
s.Unlock()
|
||||
|
||||
if s.OnDisconnect != nil {
|
||||
s.OnDisconnect(s)
|
||||
}
|
||||
s.Handle(&Disconnect{})
|
||||
|
||||
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.
|
||||
// All unhandled events will then be handled by OnEvent.
|
||||
func (s *Session) event(messageType int, message []byte) {
|
||||
s.RLock()
|
||||
if s.Handlers == nil {
|
||||
s.RUnlock()
|
||||
s.initialize()
|
||||
} else {
|
||||
s.RUnlock()
|
||||
}
|
||||
|
||||
var err error
|
||||
|
@ -291,6 +291,9 @@ func (s *Session) event(messageType int, message []byte) {
|
|||
|
||||
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 {
|
||||
case "READY":
|
||||
i = &Ready{}
|
||||
|
@ -304,63 +307,28 @@ func (s *Session) event(messageType int, message []byte) {
|
|||
i = &PresenceUpdate{}
|
||||
case "TYPING_START":
|
||||
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":
|
||||
// if !s.StateEnabled && s.OnChannelCreate == nil {
|
||||
// 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
|
||||
// websocket. This comes immediately after the call to VoiceChannelJoin
|
||||
// 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
|
||||
// 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.
|
||||
// This event tells us the information needed to open a voice websocket
|
||||
// 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
|
||||
s.Voice.token = st.Token
|
||||
|
|
Loading…
Reference in a new issue