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