public Medium.Response CreatePost(Repository repo, Cycle prevCycle, Cycle currentCycle) { StringBuilder post = new StringBuilder(); post.AppendLine($"<h1>Tezos Blockchain cycle {prevCycle.index} stats</h1>"); post.AppendLine("<p><i>This post was generated fully automatically by the <a href='https://tzsnt.fr/'>TezosNotifierBot</a>.</i></p>"); post.AppendLine("<p><img src='https://tzsnt.fr/img/tezos-notifier-horizontal.png' /></p>"); post.AppendLine("<h1>General cycle stats</h1>"); post.AppendLine($"<p>Cycle {prevCycle.index} is completed in {prevCycle.Length.Days} days, {prevCycle.Length.Hours} hours, {prevCycle.Length.Minutes} minutes!</p>"); post.AppendLine($"<p>Next {currentCycle.index} cycle will end on {currentCycle.endTime.ToString("MMMMM d a\\t HH:mm", CultureInfo.GetCultureInfo("en"))} UTC.</p>"); post.AppendLine("<h1>Transaction stats</h1>"); post.AppendLine($"<p>In the {prevCycle.index} cycle were made {_tzKtClient.GetTransactionsCount(prevCycle.firstLevel, prevCycle.lastLevel).ToString("###,###,###,###")} transactions.</p>"); Tezos.MarketData md = FillRates(post, prevCycle, currentCycle); post.AppendLine("<h1>Whale transactions</h1>"); var whaleTransactions = _tzKtClient.GetTransactions($"level.ge={prevCycle.firstLevel}&level.le={prevCycle.lastLevel}&status=applied&amount.ge=500000000000"); if (whaleTransactions.Count == 0) { post.AppendLine("<p>Not a single whale transaction was made.</p>"); } else if (whaleTransactions.Count == 0) { post.AppendLine("<p>There was 1 whale transaction with amount > 500 000 XTZ:</p>"); } else { post.AppendLine($"<p>There were {whaleTransactions.Count} whale transactions with amount > 500 000 XTZ:</p>"); } post.AppendLine("<ul>"); foreach (var wt in whaleTransactions) { var sender = repo.GetUserTezosAddress(0, wt.Sender.address); var target = repo.GetUserTezosAddress(0, wt.Target.address); post.AppendLine($"<li><a target='_blank' href='https://tzkt.io/{wt.Hash}?utm_source=tezosnotifierbot'>transaction</a> of {(wt.Amount / 1000000M).TezToString()} ({(wt.Amount / 1000000M).TezToUsd(md)} USD) from <a href='https://tzkt.io/{wt.Sender.address}?utm_source=tezosnotifierbot'>{sender.DisplayName()}</a> to <a href='https://tzkt.io/{wt.Target.address}?utm_source=tezosnotifierbot'>{target.DisplayName()}</a></li>"); } post.AppendLine("</ul>"); post.AppendLine($"<p>So more then {whaleTransactions.Sum(o => o.Amount / 1000000M).TezToString()} was transferred by whales!</p>"); FillGovernance(post, prevCycle, currentCycle); post.AppendLine("<p><hr /></p>"); FillLinks(post); var result = Publish($"Tezos Blockchain cycle {prevCycle.index} stats", post.ToString()); return(result); }
async Task Run(CancellationToken stoppingToken) { using var scope = _provider.CreateScope(); var provider = scope.ServiceProvider; using var db = scope.ServiceProvider.GetRequiredService <TezosDataContext>(); var bot = scope.ServiceProvider.GetRequiredService <TezosBot>(); var block = db.LastBlock.Single(); if (block.Level > lastBlock) { var tzKt = provider.GetService <ITzKtClient>(); var tzKtBlock = tzKt.GetBlock(block.Level); var repo = provider.GetRequiredService <Repository>(); var wtlist = repo.GetWhaleTransactions(); var allUsers = repo.GetUsers(); if (DateTime.Now.Subtract(mdReceived).TotalMinutes > 5) { try { md = _provider.GetRequiredService <TezosNotifyBot.CryptoCompare.IMarketDataProvider>().GetMarketData(); } catch { } mdReceived = DateTime.Now; } foreach (var address in wtlist.GroupBy(o => o.FromAddress).Where(o => o.Sum(o1 => o1.Amount) >= 250000)) { var minLevel = address.Min(a => a.Level); var maxLevel = address.Max(a => a.Level); if (maxLevel < block.Level) { continue; } var timeStamp = address.Min(a => a.Timestamp); var from_start = tzKt.GetBalance(address.Key, minLevel - 1); var from_end = tzKt.GetBalance(address.Key, block.Level); var amount = (from_start - from_end); foreach (var u in allUsers.Where(o => !o.Inactive && o.WhaleThreshold > 0 && o.WhaleThreshold <= amount && o.SmartWhaleAlerts)) { var ua_from = repo.GetUserTezosAddress(u.Id, address.Key); var listFiltered = address.Where(o => !o.Notifications.Any(n => n.UserId == u.Id) && o.Amount < u.WhaleThreshold); if (listFiltered.Count() <= 1 || listFiltered.Sum(o => o.Amount) < u.WhaleThreshold) { continue; } string result = resMgr.Get(Res.WhaleOutflow, new ContextObject { u = u, Amount = from_start - from_end, md = md, ua = ua_from, Period = (int)Math.Ceiling(tzKtBlock.Timestamp.Subtract(timeStamp).TotalDays) }); string tags = ""; foreach (var op in listFiltered.OrderByDescending(o => o.Amount).Take(10).OrderBy(o => o.Level)) { var ua_to = repo.GetUserTezosAddress(u.Id, op.ToAddress); result += "\n" + resMgr.Get(Res.WhaleOutflowItem, new ContextObject { u = u, Amount = op.Amount, md = md, ua = ua_to, Block = op.Level, OpHash = op.OpHash }); repo.AddWhaleTransactionNotify(op.Id, u.Id); tags += ua_to.HashTag(); } if (u.Type == 0) { result += "\n" + resMgr.Get(Res.TurnOff, u) + ": /outflow_off"; } if (!u.HideHashTags) { result += "\n\n#whale" + ua_from.HashTag() + tags; } bot.SendTextMessage(u.Id, result, ReplyKeyboards.MainMenu(resMgr, u)); } } var minDate = tzKtBlock.Timestamp.AddDays(-_config.WhaleSeriesLength); repo.CleanWhaleTransactions(minDate); lastBlock = block.Level; } await Task.Delay(1000, stoppingToken); }