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  }