internal async Task HandleOnReactionAdded( Cacheable <IUserMessage, ulong> cachedMessage, ISocketMessageChannel messageChannel, SocketReaction reaction) { // TODO: Combine checks for reaction add/remove handler into one method if (!cachedMessage.HasValue || reaction.UserId == this.Client.CurrentUser.Id) { // Ignore the bot's own additions. return; } if (!(reaction.Channel is IGuildChannel guildChannel)) { // Only listen to guild reacts. this.Logger.Verbose("Reaction addition ignored because it wasn't on a guild channel"); return; } if (!(reaction.User.IsSpecified && reaction.User.Value is IGuildUser guildUser)) { this.Logger.Verbose("Reaction addition ignored because it wasn't by a guild user"); return; } IUserMessage message = cachedMessage.Value; Player player = null; bool playerAdded = false; string errorMessage = null; bool attempt = this .GetTournamentsManager(guildChannel.Guild) .TryReadWriteActionOnCurrentTournament(currentTournament => { player = this.GetPlayerFromReactionEventOrNull( currentTournament, guildUser, message.Id, reaction.Emote.Name, out errorMessage); if (player == null) { // TODO: we may want to remove the reaction if it's on our team-join messages. this.Logger.Verbose( "Reaction addition ignored because it wasn't by a player. Message: {errorMessage}", errorMessage); return; } // Because player equality/hashing is only based on the ID, we can check if the player is in the set with // the new instance. playerAdded = currentTournament.TryAddPlayer(player); }); if (!(attempt && playerAdded) && errorMessage != null) { // TODO: We should remove the reaction they gave instead of this one (this may also require the manage // emojis permission?). This would also require a map from userIds/Players to emojis in the tournament // state. The Reactions collection doesn't have information on who added it, and iterating through each // emoji to see if the user was there would be slow. Task deleteReactionTask = message.RemoveReactionAsync( reaction.Emote, guildUser, RequestOptionsSettings.Default); Task sendMessageTask = guildUser.SendMessageAsync(errorMessage); await Task.WhenAll(deleteReactionTask, sendMessageTask); this.Logger.Verbose("Reaction removed. Message: {errorMessage}", errorMessage); return; } else if (player != null) { this.Logger.Debug("Player {id} joined team {name}", player.Id, player.Team.Name); await guildUser.SendMessageAsync(BotStrings.YouHaveJoinedTeam(player.Team.Name)); } }