State now handles MsgCreate's missing Member.User field, some bug fixes
This addresses discord/discord-api-docs#1440. State documentation has been added, which documents the store and handlers as well. Bug fixes include: - PreHandler being called after the state handler; it is now called before as documented. - Minor behavior changes regarding Guild Create events. Refer to State's documentation.
This commit is contained in:
parent
1373e42fe1
commit
88dd0f8995
|
@ -4,23 +4,14 @@ import (
|
||||||
"github.com/diamondburned/arikawa/gateway"
|
"github.com/diamondburned/arikawa/gateway"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *State) handleEvent(ev interface{}) {
|
|
||||||
if s.PreHandler != nil {
|
|
||||||
s.PreHandler.Call(ev)
|
|
||||||
}
|
|
||||||
s.Handler.Call(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) handleReady(ev *gateway.ReadyEvent) {
|
func (s *State) handleReady(ev *gateway.ReadyEvent) {
|
||||||
s.handleEvent(ev)
|
|
||||||
|
|
||||||
for _, g := range ev.Guilds {
|
for _, g := range ev.Guilds {
|
||||||
// store this so we know when we need to dispatch a belated
|
// store this so we know when we need to dispatch a belated
|
||||||
// GuildReadyEvent
|
// GuildReadyEvent
|
||||||
if g.Unavailable {
|
if g.Unavailable {
|
||||||
s.unreadyGuilds.Add(g.ID)
|
s.unreadyGuilds.Add(g.ID)
|
||||||
} else {
|
} else {
|
||||||
s.handleEvent(&GuildReadyEvent{
|
s.Handler.Call(&GuildReadyEvent{
|
||||||
GuildCreateEvent: &g,
|
GuildCreateEvent: &g,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -28,47 +19,39 @@ func (s *State) handleReady(ev *gateway.ReadyEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) handleGuildCreate(ev *gateway.GuildCreateEvent) {
|
func (s *State) handleGuildCreate(ev *gateway.GuildCreateEvent) {
|
||||||
// before we dispatch the specific events, we can already call the handlers
|
|
||||||
// that subscribed to the generic version
|
|
||||||
s.handleEvent(ev)
|
|
||||||
|
|
||||||
// this guild was unavailable, but has come back online
|
// this guild was unavailable, but has come back online
|
||||||
if s.unavailableGuilds.Delete(ev.ID) {
|
if s.unavailableGuilds.Delete(ev.ID) {
|
||||||
s.handleEvent(&GuildAvailableEvent{
|
s.Handler.Call(&GuildAvailableEvent{
|
||||||
GuildCreateEvent: ev,
|
GuildCreateEvent: ev,
|
||||||
})
|
})
|
||||||
|
|
||||||
// the guild was already unavailable when connecting to the gateway
|
// the guild was already unavailable when connecting to the gateway
|
||||||
// we can dispatch a belated GuildReadyEvent
|
// we can dispatch a belated GuildReadyEvent
|
||||||
} else if s.unreadyGuilds.Delete(ev.ID) {
|
} else if s.unreadyGuilds.Delete(ev.ID) {
|
||||||
s.handleEvent(&GuildReadyEvent{
|
s.Handler.Call(&GuildReadyEvent{
|
||||||
GuildCreateEvent: ev,
|
GuildCreateEvent: ev,
|
||||||
})
|
})
|
||||||
} else { // we don't know this guild, hence we just joined it
|
} else { // we don't know this guild, hence we just joined it
|
||||||
s.handleEvent(&GuildJoinEvent{
|
s.Handler.Call(&GuildJoinEvent{
|
||||||
GuildCreateEvent: ev,
|
GuildCreateEvent: ev,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) handleGuildDelete(ev *gateway.GuildDeleteEvent) {
|
func (s *State) handleGuildDelete(ev *gateway.GuildDeleteEvent) {
|
||||||
// before we dispatch the specific events, we can already call the handlers
|
|
||||||
// that subscribed to the generic version
|
|
||||||
s.handleEvent(ev)
|
|
||||||
|
|
||||||
// store this so we can later dispatch a GuildAvailableEvent, once the
|
// store this so we can later dispatch a GuildAvailableEvent, once the
|
||||||
// guild becomes available again.
|
// guild becomes available again.
|
||||||
if ev.Unavailable {
|
if ev.Unavailable {
|
||||||
s.unavailableGuilds.Add(ev.ID)
|
s.unavailableGuilds.Add(ev.ID)
|
||||||
|
|
||||||
s.handleEvent(&GuildUnavailableEvent{
|
s.Handler.Call(&GuildUnavailableEvent{
|
||||||
GuildDeleteEvent: ev,
|
GuildDeleteEvent: ev,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// it might have been unavailable before we left
|
// it might have been unavailable before we left
|
||||||
s.unavailableGuilds.Delete(ev.ID)
|
s.unavailableGuilds.Delete(ev.ID)
|
||||||
|
|
||||||
s.handleEvent(&GuildLeaveEvent{
|
s.Handler.Call(&GuildLeaveEvent{
|
||||||
GuildDeleteEvent: ev,
|
GuildDeleteEvent: ev,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,42 @@ var (
|
||||||
MaxFetchGuilds uint = 100
|
MaxFetchGuilds uint = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// State is the cache to store events coming from Discord as well as data from
|
||||||
|
// API calls.
|
||||||
|
//
|
||||||
|
// Store
|
||||||
|
//
|
||||||
|
// The state basically provides abstractions on top of the API and the state
|
||||||
|
// storage (Store). The state storage is effectively a set of interfaces which
|
||||||
|
// allow arbitrary backends to be implemented.
|
||||||
|
//
|
||||||
|
// The default storage backend is a typical in-memory structure consisting of
|
||||||
|
// maps and slices. Custom backend implementations could embed this storage
|
||||||
|
// backend as an in-memory fallback. A good example of this would be embedding
|
||||||
|
// the default store for messages only, while handling everything else in Redis.
|
||||||
|
//
|
||||||
|
// The package also provides a no-op store (NoopStore) that implementations
|
||||||
|
// could embed. This no-op store will always return an error, which makes the
|
||||||
|
// state fetch information from the API. The setters are all no-ops, so the
|
||||||
|
// fetched data won't be updated.
|
||||||
|
//
|
||||||
|
// Handler
|
||||||
|
//
|
||||||
|
// The state uses its own handler over session's to make all handlers run after
|
||||||
|
// the state updates itself. A PreHandler is exposed in any case the user needs
|
||||||
|
// the handlers to run before the state updates itself. Refer to that field's
|
||||||
|
// documentation.
|
||||||
|
//
|
||||||
|
// The state also provides extra events and overrides to make up for Discord's
|
||||||
|
// inconsistencies in data. The following are known instances of such.
|
||||||
|
//
|
||||||
|
// The Guild Create event is split up to make the state's Guild Available, Guild
|
||||||
|
// Ready and Guild Join events. Refer to these events' documentations for more
|
||||||
|
// information.
|
||||||
|
//
|
||||||
|
// The Message Create and Message Update events with the Member field provided
|
||||||
|
// will have the User field copied from Author. This is because the User field
|
||||||
|
// will be empty, while the Member structure expects it to be there.
|
||||||
type State struct {
|
type State struct {
|
||||||
*session.Session
|
*session.Session
|
||||||
Store
|
Store
|
||||||
|
@ -41,7 +77,7 @@ type State struct {
|
||||||
|
|
||||||
// Command handler with inherited methods. Ran after PreHandler. You should
|
// Command handler with inherited methods. Ran after PreHandler. You should
|
||||||
// most of the time use this instead of Session's, to avoid race conditions
|
// most of the time use this instead of Session's, to avoid race conditions
|
||||||
// with the State
|
// with the State.
|
||||||
*handler.Handler
|
*handler.Handler
|
||||||
|
|
||||||
unhooker func()
|
unhooker func()
|
||||||
|
|
|
@ -7,23 +7,42 @@ import (
|
||||||
"github.com/diamondburned/arikawa/gateway"
|
"github.com/diamondburned/arikawa/gateway"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *State) hookSession() error {
|
func (s *State) hookSession() {
|
||||||
s.unhooker = s.Session.AddHandler(func(iface interface{}) {
|
s.unhooker = s.Session.AddHandler(func(event interface{}) {
|
||||||
s.onEvent(iface)
|
// Call the pre-handler before the state handler.
|
||||||
|
if s.PreHandler != nil {
|
||||||
|
s.PreHandler.Call(event)
|
||||||
|
}
|
||||||
|
|
||||||
switch e := iface.(type) {
|
// Run the state handler.
|
||||||
|
s.onEvent(event)
|
||||||
|
|
||||||
|
// Always call the event on function exit. Extra events constructed by
|
||||||
|
// handlers will be called before the main event, but that's fine.
|
||||||
|
defer s.Handler.Call(event)
|
||||||
|
|
||||||
|
switch e := event.(type) {
|
||||||
case *gateway.ReadyEvent:
|
case *gateway.ReadyEvent:
|
||||||
s.handleReady(e)
|
s.handleReady(e)
|
||||||
|
return
|
||||||
case *gateway.GuildCreateEvent:
|
case *gateway.GuildCreateEvent:
|
||||||
s.handleGuildCreate(e)
|
s.handleGuildCreate(e)
|
||||||
|
return
|
||||||
case *gateway.GuildDeleteEvent:
|
case *gateway.GuildDeleteEvent:
|
||||||
s.handleGuildDelete(e)
|
s.handleGuildDelete(e)
|
||||||
default:
|
return
|
||||||
s.handleEvent(iface)
|
|
||||||
|
// https://github.com/discord/discord-api-docs/commit/01665c4
|
||||||
|
case *gateway.MessageCreateEvent:
|
||||||
|
if e.Member != nil {
|
||||||
|
e.Member.User = e.Author
|
||||||
|
}
|
||||||
|
case *gateway.MessageUpdateEvent:
|
||||||
|
if e.Member != nil {
|
||||||
|
e.Member.User = e.Author
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) onEvent(iface interface{}) {
|
func (s *State) onEvent(iface interface{}) {
|
||||||
|
|
Loading…
Reference in New Issue