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  }