github.com/diamondburned/arikawa@v1.3.14/state/state_events.go (about) 1 package state 2 3 import ( 4 "github.com/pkg/errors" 5 6 "github.com/diamondburned/arikawa/discord" 7 "github.com/diamondburned/arikawa/gateway" 8 ) 9 10 func (s *State) hookSession() { 11 s.Session.AddHandler(func(event interface{}) { 12 // Call the pre-handler before the state handler. 13 if s.PreHandler != nil { 14 s.PreHandler.Call(event) 15 } 16 17 // Run the state handler. 18 s.onEvent(event) 19 20 switch event := event.(type) { 21 case *gateway.ReadyEvent: 22 s.Handler.Call(event) 23 s.handleReady(event) 24 case *gateway.GuildCreateEvent: 25 s.Handler.Call(event) 26 s.handleGuildCreate(event) 27 case *gateway.GuildDeleteEvent: 28 s.Handler.Call(event) 29 s.handleGuildDelete(event) 30 31 // https://github.com/discord/discord-api-docs/commit/01665c4 32 case *gateway.MessageCreateEvent: 33 if event.Member != nil { 34 event.Member.User = event.Author 35 } 36 s.Handler.Call(event) 37 38 case *gateway.MessageUpdateEvent: 39 if event.Member != nil { 40 event.Member.User = event.Author 41 } 42 s.Handler.Call(event) 43 44 default: 45 s.Handler.Call(event) 46 } 47 }) 48 } 49 50 func (s *State) onEvent(iface interface{}) { 51 switch ev := iface.(type) { 52 case *gateway.ReadyEvent: 53 // Reset the store before proceeding. 54 if resetter, ok := s.Store.(StoreResetter); ok { 55 if err := resetter.Reset(); err != nil { 56 s.stateErr(err, "Failed to reset state on READY") 57 } 58 } 59 60 // Set Ready to the state 61 s.Ready = *ev 62 63 // Handle presences 64 for _, p := range ev.Presences { 65 if err := s.Store.PresenceSet(0, p); err != nil { 66 s.stateErr(err, "failed to set global presence") 67 } 68 } 69 70 // Handle guilds 71 for i := range ev.Guilds { 72 s.batchLog(storeGuildCreate(s.Store, &ev.Guilds[i])) 73 } 74 75 // Handle private channels 76 for _, ch := range ev.PrivateChannels { 77 if err := s.Store.ChannelSet(ch); err != nil { 78 s.stateErr(err, "failed to set channel in state") 79 } 80 } 81 82 // Handle user 83 if err := s.Store.MyselfSet(ev.User); err != nil { 84 s.stateErr(err, "failed to set self in state") 85 } 86 87 case *gateway.GuildCreateEvent: 88 s.batchLog(storeGuildCreate(s.Store, ev)) 89 90 case *gateway.GuildUpdateEvent: 91 if err := s.Store.GuildSet(ev.Guild); err != nil { 92 s.stateErr(err, "failed to update guild in state") 93 } 94 95 case *gateway.GuildDeleteEvent: 96 if err := s.Store.GuildRemove(ev.ID); err != nil && !ev.Unavailable { 97 s.stateErr(err, "failed to delete guild in state") 98 } 99 100 case *gateway.GuildMemberAddEvent: 101 if err := s.Store.MemberSet(ev.GuildID, ev.Member); err != nil { 102 s.stateErr(err, "failed to add a member in state") 103 } 104 105 case *gateway.GuildMemberUpdateEvent: 106 m, err := s.Store.Member(ev.GuildID, ev.User.ID) 107 if err != nil { 108 // We can't do much here. 109 m = &discord.Member{} 110 } 111 112 // Update available fields from ev into m 113 ev.Update(m) 114 115 if err := s.Store.MemberSet(ev.GuildID, *m); err != nil { 116 s.stateErr(err, "failed to update a member in state") 117 } 118 119 case *gateway.GuildMemberRemoveEvent: 120 if err := s.Store.MemberRemove(ev.GuildID, ev.User.ID); err != nil { 121 s.stateErr(err, "failed to remove a member in state") 122 } 123 124 case *gateway.GuildMembersChunkEvent: 125 for _, m := range ev.Members { 126 if err := s.Store.MemberSet(ev.GuildID, m); err != nil { 127 s.stateErr(err, "failed to add a member from chunk in state") 128 } 129 } 130 131 for _, p := range ev.Presences { 132 if err := s.Store.PresenceSet(ev.GuildID, p); err != nil { 133 s.stateErr(err, "failed to add a presence from chunk in state") 134 } 135 } 136 137 case *gateway.GuildRoleCreateEvent: 138 if err := s.Store.RoleSet(ev.GuildID, ev.Role); err != nil { 139 s.stateErr(err, "failed to add a role in state") 140 } 141 142 case *gateway.GuildRoleUpdateEvent: 143 if err := s.Store.RoleSet(ev.GuildID, ev.Role); err != nil { 144 s.stateErr(err, "failed to update a role in state") 145 } 146 147 case *gateway.GuildRoleDeleteEvent: 148 if err := s.Store.RoleRemove(ev.GuildID, ev.RoleID); err != nil { 149 s.stateErr(err, "failed to remove a role in state") 150 } 151 152 case *gateway.GuildEmojisUpdateEvent: 153 if err := s.Store.EmojiSet(ev.GuildID, ev.Emojis); err != nil { 154 s.stateErr(err, "failed to update emojis in state") 155 } 156 157 case *gateway.ChannelCreateEvent: 158 if err := s.Store.ChannelSet(ev.Channel); err != nil { 159 s.stateErr(err, "failed to create a channel in state") 160 } 161 162 case *gateway.ChannelUpdateEvent: 163 if err := s.Store.ChannelSet(ev.Channel); err != nil { 164 s.stateErr(err, "failed to update a channel in state") 165 } 166 167 case *gateway.ChannelDeleteEvent: 168 if err := s.Store.ChannelRemove(ev.Channel); err != nil { 169 s.stateErr(err, "failed to remove a channel in state") 170 } 171 172 case *gateway.ChannelPinsUpdateEvent: 173 // not tracked. 174 175 case *gateway.MessageCreateEvent: 176 if err := s.Store.MessageSet(ev.Message); err != nil { 177 s.stateErr(err, "failed to add a message in state") 178 } 179 180 case *gateway.MessageUpdateEvent: 181 if err := s.Store.MessageSet(ev.Message); err != nil { 182 s.stateErr(err, "failed to update a message in state") 183 } 184 185 case *gateway.MessageDeleteEvent: 186 if err := s.Store.MessageRemove(ev.ChannelID, ev.ID); err != nil { 187 s.stateErr(err, "failed to delete a message in state") 188 } 189 190 case *gateway.MessageDeleteBulkEvent: 191 for _, id := range ev.IDs { 192 if err := s.Store.MessageRemove(ev.ChannelID, id); err != nil { 193 s.stateErr(err, "failed to delete bulk messages in state") 194 } 195 } 196 197 case *gateway.MessageReactionAddEvent: 198 s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool { 199 if i := findReaction(m.Reactions, ev.Emoji); i > -1 { 200 m.Reactions[i].Count++ 201 } else { 202 var me bool 203 if u, _ := s.Store.Me(); u != nil { 204 me = ev.UserID == u.ID 205 } 206 m.Reactions = append(m.Reactions, discord.Reaction{ 207 Count: 1, 208 Me: me, 209 Emoji: ev.Emoji, 210 }) 211 } 212 return true 213 }) 214 215 case *gateway.MessageReactionRemoveEvent: 216 s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool { 217 var i = findReaction(m.Reactions, ev.Emoji) 218 if i < 0 { 219 return false 220 } 221 222 r := &m.Reactions[i] 223 r.Count-- 224 225 switch { 226 case r.Count < 1: // If the count is 0: 227 // Remove the reaction. 228 m.Reactions = append(m.Reactions[:i], m.Reactions[i+1:]...) 229 230 case r.Me: // If reaction removal is the user's 231 u, err := s.Store.Me() 232 if err == nil && ev.UserID == u.ID { 233 r.Me = false 234 } 235 } 236 237 return true 238 }) 239 240 case *gateway.MessageReactionRemoveAllEvent: 241 s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool { 242 m.Reactions = nil 243 return true 244 }) 245 246 case *gateway.MessageReactionRemoveEmoji: 247 s.editMessage(ev.ChannelID, ev.MessageID, func(m *discord.Message) bool { 248 var i = findReaction(m.Reactions, ev.Emoji) 249 if i < 0 { 250 return false 251 } 252 m.Reactions = append(m.Reactions[:i], m.Reactions[i+1:]...) 253 return true 254 }) 255 256 case *gateway.PresenceUpdateEvent: 257 if err := s.Store.PresenceSet(ev.GuildID, ev.Presence); err != nil { 258 s.stateErr(err, "failed to update presence in state") 259 } 260 261 case *gateway.PresencesReplaceEvent: 262 for _, p := range *ev { 263 if err := s.Store.PresenceSet(p.GuildID, p); err != nil { 264 s.stateErr(err, "failed to update presence in state") 265 } 266 } 267 268 case *gateway.SessionsReplaceEvent: 269 270 case *gateway.UserGuildSettingsUpdateEvent: 271 for i, ugs := range s.Ready.UserGuildSettings { 272 if ugs.GuildID == ev.GuildID { 273 s.Ready.UserGuildSettings[i] = ev.UserGuildSettings 274 } 275 } 276 277 case *gateway.UserSettingsUpdateEvent: 278 s.Ready.Settings = &ev.UserSettings 279 280 case *gateway.UserNoteUpdateEvent: 281 s.Ready.Notes[ev.ID] = ev.Note 282 283 case *gateway.UserUpdateEvent: 284 if err := s.Store.MyselfSet(ev.User); err != nil { 285 s.stateErr(err, "failed to update myself from USER_UPDATE") 286 } 287 288 case *gateway.VoiceStateUpdateEvent: 289 vs := &ev.VoiceState 290 if vs.ChannelID == 0 { 291 if err := s.Store.VoiceStateRemove(vs.GuildID, vs.UserID); err != nil { 292 s.stateErr(err, "failed to remove voice state from state") 293 } 294 } else { 295 if err := s.Store.VoiceStateSet(vs.GuildID, *vs); err != nil { 296 s.stateErr(err, "failed to update voice state in state") 297 } 298 } 299 } 300 } 301 302 func (s *State) stateErr(err error, wrap string) { 303 s.StateLog(errors.Wrap(err, wrap)) 304 } 305 func (s *State) batchLog(errors []error) { 306 for _, err := range errors { 307 s.StateLog(err) 308 } 309 } 310 311 // Helper functions 312 313 func (s *State) editMessage(ch discord.ChannelID, msg discord.MessageID, fn func(m *discord.Message) bool) { 314 m, err := s.Store.Message(ch, msg) 315 if err != nil { 316 return 317 } 318 if !fn(m) { 319 return 320 } 321 if err := s.Store.MessageSet(*m); err != nil { 322 s.stateErr(err, "failed to save message in reaction add") 323 } 324 } 325 326 func findReaction(rs []discord.Reaction, emoji discord.Emoji) int { 327 for i := range rs { 328 if rs[i].Emoji.ID == emoji.ID && rs[i].Emoji.Name == emoji.Name { 329 return i 330 } 331 } 332 return -1 333 } 334 335 func storeGuildCreate(store Store, guild *gateway.GuildCreateEvent) []error { 336 if guild.Unavailable { 337 return nil 338 } 339 340 stack, errs := newErrorStack() 341 342 if err := store.GuildSet(guild.Guild); err != nil { 343 errs(err, "failed to set guild in Ready") 344 } 345 346 // Handle guild emojis 347 if guild.Emojis != nil { 348 if err := store.EmojiSet(guild.ID, guild.Emojis); err != nil { 349 errs(err, "failed to set guild emojis") 350 } 351 } 352 353 // Handle guild member 354 for _, m := range guild.Members { 355 if err := store.MemberSet(guild.ID, m); err != nil { 356 errs(err, "failed to set guild member in Ready") 357 } 358 } 359 360 // Handle guild channels 361 for _, ch := range guild.Channels { 362 // I HATE Discord. 363 ch.GuildID = guild.ID 364 365 if err := store.ChannelSet(ch); err != nil { 366 errs(err, "failed to set guild channel in Ready") 367 } 368 } 369 370 // Handle guild presences 371 for _, p := range guild.Presences { 372 if err := store.PresenceSet(guild.ID, p); err != nil { 373 errs(err, "failed to set guild presence in Ready") 374 } 375 } 376 377 // Handle guild voice states 378 for _, v := range guild.VoiceStates { 379 if err := store.VoiceStateSet(guild.ID, v); err != nil { 380 errs(err, "failed to set guild voice state in Ready") 381 } 382 } 383 384 return *stack 385 } 386 387 func newErrorStack() (*[]error, func(error, string)) { 388 var errs = new([]error) 389 return errs, func(err error, wrap string) { 390 *errs = append(*errs, errors.Wrap(err, wrap)) 391 } 392 }