async Task HandleReactionAdded(ReactionAddedEventArgs e) { var options = _options.CurrentValue; if (_claimEmojiFilter.IsClaimEmoji(e.Emoji) && _pendingClaims.TryRemove(e.Message.Id, out var claim)) { var(logPlace, channel, message, character, stopwatch) = claim; await Task.Delay(TimeSpan.FromSeconds(options.DelaySeconds)); var replySubs = new { character = character.DisplayName.Split(' ', 2)[0].ToLowerInvariant(), character_full = character.DisplayName.ToLowerInvariant(), Character = character.DisplayName.Split(' ', 2)[0], Character_full = character.DisplayName, anime = character.DisplayAnime.ToLowerInvariant(), Anime = character.DisplayAnime }; await _replySender.SendAsync(channel, ReplyEvent.BeforeClaim, replySubs); IUserMessage response; try { response = await _commandHandler.ReactAsync(message, e.Emoji); } catch (Exception ex) { _logger.LogWarning(ex, $"Could not claim character '{character}' in {logPlace}."); return; } if (_outputParser.TryParseClaimSucceeded(response.Content, out var claimer, out _) && claimer.Equals(e.Client.CurrentUser.Name, StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning($"Claimed character '{character}' in {logPlace} in {stopwatch.Elapsed.TotalMilliseconds}ms."); await _replySender.SendAsync(channel, ReplyEvent.ClaimSucceeded, replySubs); return; } if (_outputParser.TryParseClaimFailed(response.Content, out var resetTime)) { _states.GetOrAdd(message.ChannelId, new ClaimState()).CooldownResetTime = DateTime.Now + resetTime; _logger.LogWarning($"Could not claim character '{character}' in {logPlace} due to cooldown. Cooldown finishes in {resetTime}."); await _replySender.SendAsync(channel, ReplyEvent.ClaimFailed, replySubs); return; } _logger.LogWarning($"Probably claimed character '{character}' in {logPlace}, but result could not be determined. Channel is probably busy."); } else if (_claimEmojiFilter.IsKakeraEmoji(e.Emoji, out var kakera) && _pendingKakeraClaims.TryRemove(e.Message.Id, out claim)) { var(logPlace, channel, message, character, stopwatch) = claim; // check cooldown here (to allow skipping cooldown check for purple kakera) var state = _states.GetOrAdd(channel.Id, new ClaimState()); var now = DateTime.Now; if (!options.KakeraIgnoreCooldown && now < state.KakeraResetTime && kakera != KakeraType.Purple) { return; } if (!options.KakeraTargets.Contains(kakera)) { _logger.LogInformation($"Ignoring {kakera} kakera on character '{character}' in {logPlace} because it is not targeted."); return; } await Task.Delay(TimeSpan.FromSeconds(options.KakeraDelaySeconds)); var replySubs = new { kakera = kakera.ToString().ToLowerInvariant(), Kakera = kakera.ToString() }; await _replySender.SendAsync(channel, ReplyEvent.BeforeKakera, replySubs); IUserMessage response; try { response = await _commandHandler.ReactAsync(message, e.Emoji); } catch (Exception ex) { _logger.LogWarning(ex, $"Could not claim {kakera} kakera on character '{character}' in {logPlace}."); return; } if (_outputParser.TryParseKakeraSucceeded(response.Content, out var claimer, out _) && claimer.Equals(e.Client.CurrentUser.Name, StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning($"Claimed {kakera} kakera on character '{character}' in {logPlace} in {stopwatch.Elapsed.TotalMilliseconds}ms."); await _replySender.SendAsync(channel, ReplyEvent.KakeraSucceeded, replySubs); return; } if (_outputParser.TryParseKakeraFailed(response.Content, out var resetTime)) { state.KakeraResetTime = now + resetTime; _logger.LogWarning($"Could not claim {kakera} kakera on character '{character}' in {logPlace} due to cooldown. Kakera is reset in {resetTime}."); await _replySender.SendAsync(channel, ReplyEvent.KakeraFailed, replySubs); return; } _logger.LogWarning($"Probably claimed {kakera} kakera on character '{character}' in {logPlace}, but result could not be determined. Channel is probably busy."); } }
#pragma warning disable 1998 async Task HandleMessageReceived(MessageReceivedEventArgs e) #pragma warning restore 1998 { // purge old pending claims (avoids memory leak) foreach (var id in _pendingClaims.Keys) { if (_pendingClaims.TryGetValue(id, out var claim) && claim.CreatedTime.AddMinutes(1) < DateTime.Now) { _pendingClaims.TryRemove(id, out _); } } foreach (var id in _pendingKakeraClaims.Keys) { if (_pendingKakeraClaims.TryGetValue(id, out var claim) && claim.CreatedTime.AddMinutes(1) < DateTime.Now) { _pendingKakeraClaims.TryRemove(id, out _); } } var options = _options.CurrentValue; // enabled, message author is mudae, channel is configured, embed exists if (!options.Enabled || !(e.Message is IUserMessage message && _userFilter.IsMudae(message.Author)) || _channelList.CurrentValue.Items.All(x => x.Id != message.ChannelId) || message.Embeds.Count == 0) { return; } var stopwatch = Stopwatch.StartNew(); var channel = (IMessageChannel)e.Client.GetChannel(message.ChannelId); var guild = channel is IGuildChannel gc?e.Client.GetGuild(gc.GuildId) : null; var logPlace = $"channel '{channel.Name}' ({channel.Id}, server '{guild?.Name}')"; var embed = message.Embeds[0]; var description = embed.Description.Split('\n', StringSplitOptions.RemoveEmptyEntries); var character = new CharacterInfo(embed.Author.Name, description[0]); // ignore $im messages if (!options.BypassImCheck && description.Any(l => l.StartsWith("claim rank:", StringComparison.OrdinalIgnoreCase) || l.StartsWith("like rank:", StringComparison.OrdinalIgnoreCase))) { return; } _logger.LogDebug($"Detected character '{character}' in {logPlace}."); _pendingKakeraClaims[message.Id] = new PendingClaim(logPlace, channel, message, character, stopwatch); // ignore already claimed if (embed.Footer?.Text.StartsWith("belongs", StringComparison.OrdinalIgnoreCase) == true) { return; } var wishedBy = message.Content.StartsWith("wished by", StringComparison.OrdinalIgnoreCase) ? message.GetUserIds().ToArray() : null; // must be wished or included in a user wishlist if (!_characterFilter.IsWished(character, wishedBy)) { _logger.LogInformation($"Ignoring character '{character}' in {logPlace} because they are not wished."); return; } // check cooldown var state = _states.GetOrAdd(channel.Id, new ClaimState()); var now = DateTime.Now; if (!options.IgnoreCooldown && now < state.CooldownResetTime) { _logger.LogWarning($"Ignoring character '{character}' in {logPlace} because of cooldown. Cooldown finishes in {state.CooldownResetTime - now}."); return; } _logger.LogWarning($"Attempting to claim character '{character}' in {logPlace}..."); _pendingClaims[message.Id] = new PendingClaim(logPlace, channel, message, character, stopwatch); /* Claim from message event instead of reaction event */ if (_pendingClaims.TryRemove(e.Message.Id, out var claim1)) { var(logPlace1, channel1, message1, character1, stopwatch1) = claim1; await Task.Delay(TimeSpan.FromSeconds(options.DelaySeconds)); var replySubs = new { character = character1.DisplayName.Split(' ', 2)[0].ToLowerInvariant(), character_full = character1.DisplayName.ToLowerInvariant(), Character = character1.DisplayName.Split(' ', 2)[0], Character_full = character1.DisplayName, anime = character1.DisplayAnime.ToLowerInvariant(), Anime = character1.DisplayAnime }; await _replySender.SendAsync(channel1, ReplyEvent.BeforeClaim, replySubs); IUserMessage response; try { IEmoji emoji = new LocalEmoji("\uD83D\uDC96"); response = await _commandHandler.ReactAsync(message1, emoji); } catch (Exception ex) { _logger.LogWarning(ex, $"Could not claim character '{character1}' in {logPlace1}."); return; } bool tryParseClaim = _outputParser.TryParseClaimSucceeded(response.Content, out var claimer, out _); _logger.LogDebug(response.Content); bool claimerEquals = claimer.Equals(e.Client.CurrentUser.Name, StringComparison.OrdinalIgnoreCase); _logger.LogDebug($"TryParseClaim: '{tryParseClaim}'. ClaimerEquals: '{claimerEquals}', claimer:{claimer} and user {e.Client.CurrentUser.Name}"); if (tryParseClaim && claimerEquals) { _logger.LogWarning($"Claimed character '{character1}' in {logPlace1} in {stopwatch1.Elapsed.TotalMilliseconds}ms."); if (options.NotifyOnCharacter) { _notification.SendToast($"Claimed character '{character1}' in {logPlace1}."); } await _replySender.SendAsync(channel1, ReplyEvent.ClaimSucceeded, replySubs); return; } if (_outputParser.TryParseClaimFailed(response.Content, out var resetTime)) { _states.GetOrAdd(message1.ChannelId, new ClaimState()).CooldownResetTime = DateTime.Now + resetTime; _logger.LogWarning($"Could not claim character '{character1}' in {logPlace1} due to cooldown. Cooldown finishes in {resetTime}."); await _replySender.SendAsync(channel1, ReplyEvent.ClaimFailed, replySubs); return; } _logger.LogWarning($"Probably claimed character '{character1}' in {logPlace1}, but result could not be determined. Channel is probably busy."); if (options.NotifyOnCharacter) { _notification.SendToast($"Probably claimed character '{character1}' in {logPlace1}, but result could not be determined."); } } }