public static (int rank, int amount) GetKarmaRank(ulong userId)
        {
            using (var dctx = new SparkyContext())
            {
                var karmaRanks = KarmaEvent.GetForAllUsers(dctx.KarmaEvents.ToList(), dctx.Users.ToList());
                var userRank   = karmaRanks.First(r => r.Item1 == userId);

                return(karmaRanks.IndexOf(userRank) + 1, userRank.Item2);
            }
        }
        public async Task GetKarmaLeaderboardAsync()
        {
            var eb = new EmbedBuilder()
                     .WithCurrentTimestamp()
                     .WithColor(Color.DarkBlue);

            var ranks = KarmaEvent.GetForAllUsers(DbCtx.KarmaEvents.ToList(), DbCtx.Users.ToList()).Take(10);

            var response = await ReplyAsync(embed : BuildLeaderboardEmbed(eb, "Karma Leaderboard", ranks.ToList()).Build());
        }
        private Task HandleReactionRemoved(Cacheable <IUserMessage, ulong> cacheable, ISocketMessageChannel channel, SocketReaction reaction)
        {
            if (!VerifyIsKarmaEmote(reaction))
            {
                return(Task.CompletedTask);
            }

            using (var dctx = new SparkyContext())
            {
                dctx.Remove(KarmaEvent.GetId(reaction.UserId, cacheable.Id));

                return(dctx.SaveChangesAsync());
            }
        }
        public async Task ViewProfileAsync([Summary("@user"), Remainder] SocketGuildUser member = null)
        {
            if (member?.IsBot ?? false)
            {
                return;
            }

            var targetId = member?.Id ?? Context.User.Id;
            var users    = DbCtx.Users.ToList();
            var userData = users.First(u => u.Id == Convert.ToInt64(targetId));

            (int karmaRank, int karmaCount) = KarmaService.GetKarmaRank(targetId);

            int moderatedKarma = 0;
            var giverList      = new List <(ulong, int)>();

            var giverEvents = DbCtx.KarmaEvents.Where(e => e.RecipientId == Convert.ToInt64(targetId)).ToList();
            var sparkyEvent = giverEvents.FirstOrDefault(e => e.Id == KarmaEvent.GetId(Context.Client.CurrentUser.Id, member?.Id ?? Context.User.Id));

            if (sparkyEvent != null)
            {
                giverEvents.Remove(sparkyEvent);
                moderatedKarma = sparkyEvent.Amount;
            }
            var giverGroups = giverEvents
                              .GroupBy(e => e.GiverId)
                              .OrderByDescending(g => g.Sum(e => e.Amount))
                              .Take(5);

            foreach (var giver in giverGroups)
            {
                giverList.Add(((ulong)giver.Key, giver.Sum(e => e.Amount)));
            }

            var messageRank = users.OrderByDescending(u => u.Points).ToList().IndexOf(userData) + 1;

            var eb = new EmbedBuilder()
                     .WithTitle($"Profile of: {(member ?? Context.User as SocketGuildUser).Nickname ?? member?.Username ?? Context.User.Username}")
                     .AddField($"Karma (Rank {karmaRank})", karmaCount, true)
                     .AddField($"Message Count (Rank {messageRank})", userData.Points, true)
                     .AddField("Top 5 Karma Givers", string.Join(", ", giverList.Count == 0 ? new[] { "None" } : giverList.Select(tuple => $"<@{tuple.Item1}> {((tuple.Item2 / (double)karmaCount) * 100):.0}%")))
                     .AddField("Moderated Karma", moderatedKarma.ToString(), true)
                     .WithColor(Color.DarkBlue)
                     .WithCurrentTimestamp()
                     .Build();

            await ReplyAsync(embed : eb);
        }
        private async Task HandleReactionAdded(Cacheable <IUserMessage, ulong> cacheable, ISocketMessageChannel channel, SocketReaction reaction)
        {
            if (!VerifyIsKarmaEmote(reaction) || !VerifyIsKarmaChannel(reaction))
            {
                return;
            }
            var message = await cacheable.GetOrDownloadAsync();

            if (message.Author.IsBot || message.Author.Id == reaction.UserId)
            {
                return;
            }

            using (var dctx = new SparkyContext())
            {
                var relevantEvents = dctx.KarmaEvents
                                     .Where(k => k.RecipientId == Convert.ToInt64(message.Author.Id) || k.GiverId == Convert.ToInt64(reaction.UserId)).ToList();

                // Check if the giver has already given on this message
                var eventOnMessage = relevantEvents.FirstOrDefault(e => e.Id == KarmaEvent.GetId(reaction.UserId, message.Id));
                if (eventOnMessage != null)
                {
                    return;
                }

                // Check if the giver is allowed to give in general
                var lastGiverEvent = relevantEvents.Where(e => e.GiverId == Convert.ToInt64(reaction.UserId)).OrderByDescending(e => e.GivenAt).FirstOrDefault();
                if (DateTimeOffset.UtcNow.Subtract(lastGiverEvent?.GivenAt ?? DateTime.MinValue).TotalMinutes < Configuration.Get <int>("karma_limit_all"))
                {
                    return;
                }

                // Check if the giver has given to recipient witin limit
                var lastGiverToRecipient = relevantEvents.Where(e => e.GiverId == Convert.ToInt64(reaction.UserId) && e.RecipientId == Convert.ToInt64(message.Author.Id))
                                           .OrderByDescending(e => e.GivenAt).FirstOrDefault();
                if (DateTimeOffset.UtcNow.Subtract(lastGiverToRecipient?.GivenAt ?? DateTime.MinValue).TotalMinutes < Configuration.Get <int>("karma_limit_mutual"))
                {
                    return;
                }

                // Write new event to db
                dctx.Add(KarmaEvent.New(reaction.UserId, message, 1));

                await dctx.SaveChangesAsync();
            }
        }
        public async Task LeaderboardAsync()
        {
            var top5Messages = DbCtx.Users.OrderByDescending(u => u.Points).Take(5).ToList();

            var top5Karma = KarmaEvent.GetForAllUsers(DbCtx.KarmaEvents.ToList(), DbCtx.Users.ToList()).Take(5);

            var eb = new EmbedBuilder()
                     .WithColor(Color.DarkBlue)
                     .WithCurrentTimestamp();

            BuildLeaderboardEmbed(eb, "Points Leaderboard", top5Messages, u => u.Points);
            BuildLeaderboardEmbed(eb, "Karma Leaderboard", top5Karma.ToList());

            var response = await ReplyAsync(embed : eb.Build());

            //await WaitAndDeleteAsync(response);
        }