public static Task OnMessageCreated(DiscordClient _, MessageCreateEventArgs args)
        {
            if (args.Channel.IsPrivate)
            {
                return(Task.CompletedTask);
            }

            if (!MessageQueue.TryGetValue(args.Channel.Id, out var queue))
            {
                lock (MessageQueue)
                {
                    if (!MessageQueue.TryGetValue(args.Channel.Id, out queue))
                    {
                        MessageQueue[args.Channel.Id] = queue = new FixedLengthBuffer <ulong, DiscordMessage>(KeyGen);
                    }
                }
            }
            lock (queue.syncObj)
                queue.Add(args.Message);

            while (queue.Count > Config.ChannelMessageHistorySize)
            {
                lock (queue.syncObj)
                    queue.TrimExcess();
            }
            return(Task.CompletedTask);
        }
        internal static Task <List <DiscordMessage> > GetMessagesBeforeCachedAsync(this DiscordChannel ch, ulong msgId, int count = 100)
        {
            if (!MessageQueue.TryGetValue(ch.Id, out var queue))
            {
                lock (MessageQueue)
                    if (!MessageQueue.TryGetValue(ch.Id, out queue))
                    {
                        MessageQueue[ch.Id] = queue = new FixedLengthBuffer <ulong, DiscordMessage>(KeyGen);
                    }
            }
            List <DiscordMessage> result;

            lock (queue.syncObj)
                result = queue.Reverse().SkipWhile(m => m.Id >= msgId).Take(count).ToList();
            var cacheCount = result.Count;
            var fetchCount = Math.Max(count - cacheCount, 0);

            if (fetchCount > 0)
            {
                IReadOnlyList <DiscordMessage> fetchedList;
                if (result.Any())
                {
                    fetchedList = ch.GetMessagesBeforeAsync(result[0].Id, fetchCount).ConfigureAwait(false).GetAwaiter().GetResult();
                    if (queue.Count < Config.ChannelMessageHistorySize)
                    {
                        lock (queue.syncObj)
                        {
                            // items in queue might've changed since the previous check at the beginning of this method
                            var freshCopy = queue.Reverse().ToList();
                            queue.Clear();
                            queue.AddRange(freshCopy.Concat(fetchedList).Distinct().Reverse());
                        }
                    }
                }
                else
                {
                    fetchedList = ch.GetMessagesBeforeAsync(msgId, fetchCount).ConfigureAwait(false).GetAwaiter().GetResult();
                }
                result.AddRange(fetchedList);
            }
            return(Task.FromResult(result));
        }