public async Task WatchlistTimerTick() { // adjust timer & start it again var _watchlistTimerInterval = Convert.ToInt32(TimeSpan.FromMinutes(10).TotalMilliseconds) + _rng.Next(-60000, 60000); Logger.Log(LogLevel.Debug, $"Watchlist timer ticked. Next tick at {DateTime.Now.AddMilliseconds(_watchlistTimerInterval):hh:mm:ss tt}"); _watchlistTimer.Change(_watchlistTimerInterval, Timeout.Infinite); if (_apiHeartbeatService.ApiStatus != CustomApiStatus.OK) { Logger.Log(LogLevel.Info, "Heartbeat service reports API is down, skipping watchlist check."); return; } // Don't do anything if the watchlist is muted if (WatchlistMuted) { Logger.Log(LogLevel.Debug, "Watchlist is muted, skipping."); return; } // grab list of items to check var watchlist = await _databaseMarketWatchlist.GetWatchlist(); if (watchlist.Count == 0) { return; } // grab market analyses for items on watchlist List <MarketItemAnalysisModel> WatchlistDifferentials = new List <MarketItemAnalysisModel>(); var itemTasks = Task.Run(() => Parallel.ForEach(watchlist, parallelOptions, watchlistEntry => { var apiResponse = _marketService.CreateMarketAnalysis(watchlistEntry.ItemName, watchlistEntry.ItemID, worldsToSearch).Result; var analysis = apiResponse[2]; // overall analysis if (watchlistEntry.HQOnly) { analysis = apiResponse[0]; // overwrite with hq analysis if needed } WatchlistDifferentials.Add(analysis); })); Task.WaitAll(itemTasks); // build embed & format data to send to my server's watchlist channel // should we implement a null check here? // var dm = await _discord.GetUser(ulong.Parse(_config["discordBotOwnerId"])).GetOrCreateDMChannelAsync(); var channel = _discord.GetChannel(848651281069113354) as ITextChannel; var embed = new EmbedBuilder(); foreach (var entry in WatchlistDifferentials) { if (entry.DifferentialLowest > DifferentialCutoff) { var sb = new StringBuilder(); sb.AppendLine($"lowest diff: **{entry.DifferentialLowest}%** (avg diff: {entry.Differential}%) - avg sold: {entry.AvgSalePrice:N0} - avg mrkt: {entry.AvgMarketPrice:N0}"); foreach (var lowestPrices in entry.LowestPrices.Take(3)) { sb.Append($"• {lowestPrices.Price:N0} on {lowestPrices.Server} "); } embed.AddField(new EmbedFieldBuilder() { Name = entry.Name, Value = sb.ToString() }); } } if (embed.Fields.Any()) { await channel.SendMessageAsync(null, false, embed.Build()); } else { Logger.Log(LogLevel.Debug, $"No items on watchlist met differential cutoff of {DifferentialCutoff}."); } }