private async Task collectChat(string board, string oneOfTheServer, DateTimeOffset startTime, DateTimeOffset endTime, CancellationToken cancellationToken)
        {
            if (!this.pastThreadListerCache.TryGetValue(board, out var threadLister))
            {
                threadLister = new Nichan.PastThreadLister(board, oneOfTheServer, this.backTime);
                await threadLister.Initialize(cancellationToken);

                this.pastThreadListerCache.Add(board, threadLister);
            }

            IEnumerable <Nichan.Thread> threads = await threadLister.GetBetween(startTime, endTime, cancellationToken).ConfigureAwait(false);

            IEnumerable <string> currentThreadUrls;

            lock (this.threadList)
                currentThreadUrls = this.threadList.Select(x => x.Uri.ToString()).ToArray();
            IEnumerable <Nichan.Thread> newThreads = threads.Where(x => !currentThreadUrls.Contains(x.Uri.ToString()));

            foreach (var newThread in newThreads)
            {
                var(server, board_, threadId) = getServerBoardThreadFromThreadUrl(newThread.Uri.ToString());
                Nichan.Thread thread = await getThread(server, board_, threadId, cancellationToken);

                thread.Uri = newThread.Uri; // キャッシュにヒットするようにthreadListerの返したUriで記憶する

                lock (this.threadList)
                    this.threadList.Add(thread);
            }
        }
        public async Task <IEnumerable <string> > Get(ChannelInfo channel, DateTimeOffset time, CancellationToken cancellationToken)
        {
            MatchingThread matchingThread = this.threadResolver.Resolve(channel, true);

            if (matchingThread == null)
            {
                return(Enumerable.Empty <string>());
            }

            string[] keywords = matchingThread.ThreadTitleKeywords?.Select(
                x => x.ToLower().Normalize(NormalizationForm.FormKD)
                ).ToArray() ?? Array.Empty <string>();
            (string server, string board) = GetServerAndBoardFromBoardUrl(matchingThread.BoardUri.ToString());

            if (!this.pastThreadListerCache.TryGetValue(board, out var threadLister))
            {
                threadLister = new Nichan.PastThreadLister(board, server, this.backTime);
                await threadLister.Initialize(cancellationToken).ConfigureAwait(false);

                this.pastThreadListerCache.Add(board, threadLister);
            }

            DateTimeOffset startTime            = time;
            IEnumerable <Nichan.Thread> threads = await threadLister.GetBetween(
                startTime, startTime + this.getTimeSpan, cancellationToken
                ).ConfigureAwait(false);

            return(threads.Where(x => keywords.All(
                                     keyword => x.Title.ToLower().Normalize(NormalizationForm.FormKD).Contains(keyword)
                                     )).Select(x => x.Uri.ToString()));
        }