1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2024-12-01 03:03:48 +00:00
arikawa/state/state_events.go

433 lines
11 KiB
Go
Raw Normal View History

2020-01-18 21:04:12 +00:00
package state
import (
"github.com/pkg/errors"
2020-10-28 22:39:59 +00:00
"github.com/diamondburned/arikawa/v2/discord"
"github.com/diamondburned/arikawa/v2/gateway"
"github.com/diamondburned/arikawa/v2/state/store"
2020-01-18 21:04:12 +00:00
)
func (s *State) hookSession() {
s.Session.AddHandler(func(event interface{}) {
// Call the pre-handler before the state handler.
if s.PreHandler != nil {
s.PreHandler.Call(event)
}
// Run the state handler.
s.onEvent(event)
2020-06-19 07:59:44 +00:00
switch event := event.(type) {
case *gateway.ReadyEvent:
2020-06-19 07:59:44 +00:00
s.Handler.Call(event)
s.handleReady(event)
case *gateway.GuildCreateEvent:
2020-06-19 07:59:44 +00:00
s.Handler.Call(event)
s.handleGuildCreate(event)
case *gateway.GuildDeleteEvent:
2020-06-19 07:59:44 +00:00
s.Handler.Call(event)
s.handleGuildDelete(event)
// https://github.com/discord/discord-api-docs/commit/01665c4
case *gateway.MessageCreateEvent:
2020-06-19 07:59:44 +00:00
if event.Member != nil {
event.Member.User = event.Author
}
2020-06-19 07:59:44 +00:00
s.Handler.Call(event)
case *gateway.MessageUpdateEvent:
2020-06-19 07:59:44 +00:00
if event.Member != nil {
event.Member.User = event.Author
}
2020-06-19 07:59:44 +00:00
s.Handler.Call(event)
default:
s.Handler.Call(event)
}
2020-01-18 21:04:12 +00:00
})
}
func (s *State) onEvent(iface interface{}) {
switch ev := iface.(type) {
case *gateway.ReadyEvent:
2020-12-20 02:46:21 +00:00
// Acquire the ready mutex, but since we're only writing the value and
// not anything in it, we should be fine.
s.readyMu.Lock()
s.ready = *ev
2020-12-20 02:46:21 +00:00
s.readyMu.Unlock()
// Reset the store before proceeding.
if err := s.Cabinet.Reset(); err != nil {
2020-12-20 02:46:21 +00:00
s.stateErr(err, "failed to reset state in Ready")
}
2020-01-18 21:04:12 +00:00
// Handle guilds
for i := range ev.Guilds {
s.batchLog(storeGuildCreate(s.Cabinet, &ev.Guilds[i]))
2020-01-18 21:04:12 +00:00
}
2020-12-20 02:46:21 +00:00
// Handle guild presences
for _, p := range ev.Presences {
if err := s.Cabinet.PresenceSet(p.GuildID, p); err != nil {
s.stateErr(err, "failed to set presence in Ready")
}
}
2020-01-18 21:04:12 +00:00
// Handle private channels
for _, ch := range ev.PrivateChannels {
if err := s.Cabinet.ChannelSet(ch); err != nil {
2020-12-20 02:46:21 +00:00
s.stateErr(err, "failed to set channel in Ready")
2020-01-18 21:04:12 +00:00
}
}
// Handle user
if err := s.Cabinet.MyselfSet(ev.User); err != nil {
2020-12-20 02:46:21 +00:00
s.stateErr(err, "failed to set self in Ready")
2020-01-18 21:04:12 +00:00
}
case *gateway.ReadySupplementalEvent:
2020-12-19 06:51:26 +00:00
// Handle guilds
for _, guild := range ev.Guilds {
// Handle guild voice states
for _, v := range guild.VoiceStates {
if err := s.Cabinet.VoiceStateSet(guild.ID, v); err != nil {
s.stateErr(err, "failed to set guild voice state in Ready Supplemental")
}
}
}
for _, friend := range ev.MergedPresences.Friends {
sPresence := gateway.ConvertSupplementalPresence(friend)
if err := s.Cabinet.PresenceSet(0, sPresence); err != nil {
s.stateErr(err, "failed to set friend presence in Ready Supplemental")
}
}
// Discord uses weird indexing, so we'll need the Guilds slice.
ready := s.Ready()
for i := 0; i < len(ready.Guilds) && i < len(ev.MergedMembers); i++ {
guild := ready.Guilds[i]
for _, member := range ev.MergedMembers[i] {
sMember := gateway.ConvertSupplementalMember(member)
if err := s.Cabinet.MemberSet(guild.ID, sMember); err != nil {
s.stateErr(err, "failed to set friend presence in Ready Supplemental")
}
}
for _, member := range ev.MergedPresences.Guilds[i] {
sPresence := gateway.ConvertSupplementalPresence(member)
if err := s.Cabinet.PresenceSet(guild.ID, sPresence); err != nil {
s.stateErr(err, "failed to set member presence in Ready Supplemental")
}
}
}
case *gateway.GuildCreateEvent:
s.batchLog(storeGuildCreate(s.Cabinet, ev))
2020-01-18 21:04:12 +00:00
case *gateway.GuildUpdateEvent:
if err := s.Cabinet.GuildSet(ev.Guild); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update guild in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.GuildDeleteEvent:
if err := s.Cabinet.GuildRemove(ev.ID); err != nil && !ev.Unavailable {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to delete guild in state")
2020-01-18 21:04:12 +00:00
}
case *gateway.GuildMemberAddEvent:
if err := s.Cabinet.MemberSet(ev.GuildID, ev.Member); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to add a member in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.GuildMemberUpdateEvent:
m, err := s.Cabinet.Member(ev.GuildID, ev.User.ID)
2020-01-18 21:04:12 +00:00
if err != nil {
// We can't do much here.
m = &discord.Member{}
}
// Update available fields from ev into m
ev.Update(m)
if err := s.Cabinet.MemberSet(ev.GuildID, *m); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update a member in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.GuildMemberRemoveEvent:
if err := s.Cabinet.MemberRemove(ev.GuildID, ev.User.ID); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to remove a member in state")
2020-01-18 21:04:12 +00:00
}
case *gateway.GuildMembersChunkEvent:
for _, m := range ev.Members {
if err := s.Cabinet.MemberSet(ev.GuildID, m); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to add a member from chunk in state")
2020-01-18 21:04:12 +00:00
}
}
for _, p := range ev.Presences {
if err := s.Cabinet.PresenceSet(ev.GuildID, p); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to add a presence from chunk in state")
2020-01-18 21:04:12 +00:00
}
}
case *gateway.GuildRoleCreateEvent:
if err := s.Cabinet.RoleSet(ev.GuildID, ev.Role); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to add a role in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.GuildRoleUpdateEvent:
if err := s.Cabinet.RoleSet(ev.GuildID, ev.Role); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update a role in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.GuildRoleDeleteEvent:
if err := s.Cabinet.RoleRemove(ev.GuildID, ev.RoleID); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to remove a role in state")
2020-01-18 21:04:12 +00:00
}
case *gateway.GuildEmojisUpdateEvent:
if err := s.Cabinet.EmojiSet(ev.GuildID, ev.Emojis); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update emojis in state")
2020-01-18 21:04:12 +00:00
}
case *gateway.ChannelCreateEvent:
if err := s.Cabinet.ChannelSet(ev.Channel); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to create a channel in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.ChannelUpdateEvent:
if err := s.Cabinet.ChannelSet(ev.Channel); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update a channel in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.ChannelDeleteEvent:
if err := s.Cabinet.ChannelRemove(ev.Channel); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to remove a channel in state")
2020-01-18 21:04:12 +00:00
}
2020-04-12 23:14:27 +00:00
case *gateway.ChannelPinsUpdateEvent:
// not tracked.
2020-01-18 21:04:12 +00:00
case *gateway.MessageCreateEvent:
if err := s.Cabinet.MessageSet(ev.Message); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to add a message in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.MessageUpdateEvent:
if err := s.Cabinet.MessageSet(ev.Message); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update a message in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.MessageDeleteEvent:
if err := s.Cabinet.MessageRemove(ev.ChannelID, ev.ID); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to delete a message in state")
2020-01-18 21:04:12 +00:00
}
2020-01-18 21:04:12 +00:00
case *gateway.MessageDeleteBulkEvent:
for _, id := range ev.IDs {
if err := s.Cabinet.MessageRemove(ev.ChannelID, id); err != nil {
s.stateErr(err, "failed to delete bulk messages in state")
2020-01-18 21:04:12 +00:00
}
}
2020-04-12 23:14:27 +00:00
case *gateway.MessageReactionAddEvent:
s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool {
if i := findReaction(m.Reactions, ev.Emoji); i > -1 {
m.Reactions[i].Count++
} else {
var me bool
if u, _ := s.Cabinet.Me(); u != nil {
me = ev.UserID == u.ID
2020-04-12 23:14:27 +00:00
}
m.Reactions = append(m.Reactions, discord.Reaction{
Count: 1,
Me: me,
2020-04-12 23:14:27 +00:00
Emoji: ev.Emoji,
})
}
return true
})
2020-04-12 23:14:27 +00:00
case *gateway.MessageReactionRemoveEvent:
s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool {
var i = findReaction(m.Reactions, ev.Emoji)
if i < 0 {
return false
}
r := &m.Reactions[i]
r.Count--
switch {
case r.Count < 1: // If the count is 0:
// Remove the reaction.
m.Reactions = append(m.Reactions[:i], m.Reactions[i+1:]...)
case r.Me: // If reaction removal is the user's
u, err := s.Cabinet.Me()
2020-04-12 23:14:27 +00:00
if err == nil && ev.UserID == u.ID {
r.Me = false
}
}
return true
})
2020-04-12 23:14:27 +00:00
case *gateway.MessageReactionRemoveAllEvent:
s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool {
m.Reactions = nil
return true
})
case *gateway.MessageReactionRemoveEmojiEvent:
2020-04-12 23:14:27 +00:00
s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool {
var i = findReaction(m.Reactions, ev.Emoji)
if i < 0 {
return false
}
m.Reactions = append(m.Reactions[:i], m.Reactions[i+1:]...)
return true
})
2020-01-18 21:04:12 +00:00
case *gateway.PresenceUpdateEvent:
if err := s.Cabinet.PresenceSet(ev.GuildID, ev.Presence); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update presence in state")
2020-01-18 21:04:12 +00:00
}
case *gateway.PresencesReplaceEvent:
for _, p := range *ev {
if err := s.Cabinet.PresenceSet(p.GuildID, p.Presence); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update presence in state")
}
}
2020-05-11 23:57:40 +00:00
case *gateway.SessionsReplaceEvent:
// TODO
2020-05-11 23:57:40 +00:00
case *gateway.UserGuildSettingsUpdateEvent:
// TODO
case *gateway.UserSettingsUpdateEvent:
s.readyMu.Lock()
s.ready.UserSettings = &ev.UserSettings
s.readyMu.Unlock()
case *gateway.UserNoteUpdateEvent:
// TODO
case *gateway.UserUpdateEvent:
if err := s.Cabinet.MyselfSet(ev.User); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update myself from USER_UPDATE")
}
2020-04-19 02:22:49 +00:00
case *gateway.VoiceStateUpdateEvent:
2020-05-12 03:06:28 +00:00
vs := &ev.VoiceState
2020-04-19 02:22:49 +00:00
if vs.ChannelID == 0 {
if err := s.Cabinet.VoiceStateRemove(vs.GuildID, vs.UserID); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to remove voice state from state")
2020-04-19 02:22:49 +00:00
}
} else {
if err := s.Cabinet.VoiceStateSet(vs.GuildID, *vs); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to update voice state in state")
2020-04-19 02:22:49 +00:00
}
}
2020-01-18 21:04:12 +00:00
}
}
func (s *State) stateErr(err error, wrap string) {
2020-02-16 05:29:25 +00:00
s.StateLog(errors.Wrap(err, wrap))
2020-01-18 21:04:12 +00:00
}
func (s *State) batchLog(errors []error) {
for _, err := range errors {
s.StateLog(err)
}
}
// Helper functions
func (s *State) editMessage(ch discord.ChannelID, msg discord.MessageID, fn func(m *discord.Message) bool) {
m, err := s.Cabinet.Message(ch, msg)
2020-04-12 23:14:27 +00:00
if err != nil {
return
}
if !fn(m) {
return
}
if err := s.Cabinet.MessageSet(*m); err != nil {
2020-05-16 21:14:49 +00:00
s.stateErr(err, "failed to save message in reaction add")
2020-04-12 23:14:27 +00:00
}
}
func findReaction(rs []discord.Reaction, emoji discord.Emoji) int {
for i := range rs {
if rs[i].Emoji.ID == emoji.ID && rs[i].Emoji.Name == emoji.Name {
return i
}
}
return -1
}
func storeGuildCreate(cab store.Cabinet, guild *gateway.GuildCreateEvent) []error {
if guild.Unavailable {
return nil
}
stack, errs := newErrorStack()
if err := cab.GuildSet(guild.Guild); err != nil {
errs(err, "failed to set guild in Ready")
}
// Handle guild emojis
if guild.Emojis != nil {
if err := cab.EmojiSet(guild.ID, guild.Emojis); err != nil {
errs(err, "failed to set guild emojis")
}
}
// Handle guild member
for _, m := range guild.Members {
if err := cab.MemberSet(guild.ID, m); err != nil {
errs(err, "failed to set guild member in Ready")
}
}
// Handle guild channels
for _, ch := range guild.Channels {
// I HATE Discord.
ch.GuildID = guild.ID
if err := cab.ChannelSet(ch); err != nil {
errs(err, "failed to set guild channel in Ready")
}
}
// Handle guild presences
for _, p := range guild.Presences {
if err := cab.PresenceSet(guild.ID, p); err != nil {
errs(err, "failed to set guild presence in Ready")
}
}
// Handle guild voice states
for _, v := range guild.VoiceStates {
if err := cab.VoiceStateSet(guild.ID, v); err != nil {
errs(err, "failed to set guild voice state in Ready")
}
}
return *stack
}
func newErrorStack() (*[]error, func(error, string)) {
var errs = new([]error)
return errs, func(err error, wrap string) {
*errs = append(*errs, errors.Wrap(err, wrap))
}
}