using System; using System.Linq; using System.Threading.Tasks; using Telegram.Bot; using Telegram.Bot.Args; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using System.Net.Http; using System.Threading; using EFDatabase; using Microsoft.EntityFrameworkCore; using Quartz; using Quartz.Impl; namespace EthermineBotTelegramCore { partial class Program { static ITelegramBotClient botClient; static botdb Botdb; private static async Task Main(string[] args) { botClient = new TelegramBotClient("1785154817:AAGhXD9yQVn9HPdWTcmGJUBeZ8nA50SzHbY"); var me = botClient.GetMeAsync().Result; { string message = $"Hello, World! I am user {me.Id} and my name is {me.FirstName}."; Console.WriteLine(message); await Logger.LogInfo(message); } //enter credentials for DB if (args.Length >= 2) { AppSettings.user = args[0]; AppSettings.password = args[1]; } else { Console.Out.Write("User:"); AppSettings.user = Console.ReadLine(); Console.Out.Write("Password:"); AppSettings.password = Console.ReadLine(); } //init DB Botdb = new botdb(); if (Botdb.Database.CanConnect()) { // Grab the Scheduler instance from the Factory StdSchedulerFactory factory = new StdSchedulerFactory(); IScheduler scheduler = await factory.GetScheduler(); // and start it off await scheduler.Start(); // define the job and tie it to our HelloJob class IJobDetail job = JobBuilder.Create() .WithIdentity("job1", "group1") .UsingJobData("connectionString", "server=" + AppSettings.dbIp + ";port=3306;database=botdb;uid=" + AppSettings.user + "; pwd=" + AppSettings.password) .Build(); // Trigger the job to run now, and then repeat it ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(240) .RepeatForever()) .Build(); // Tell quartz to schedule the job using our trigger await scheduler.ScheduleJob(job, trigger); { string message = "Scheduler started!"; Console.WriteLine(message); await Logger.LogInfo(message); } Thread.Sleep(5000); //start receive messages botClient.OnMessage += BotOnMessage; botClient.StartReceiving(); Console.WriteLine("Press any key to exit"); Console.ReadKey(); Logger.Stop(); await scheduler.Shutdown(); botClient.StopReceiving(); } else { string message = "Don't connected! Check auth data and restart!"; Console.WriteLine(message); await Logger.LogError(message); Logger.Stop(); } } private static async void BotOnMessage(object sender, MessageEventArgs e) { if (e.Message.Text != null) { Console.WriteLine($"Received a text message in chat {e.Message.Chat.Id}."); char[] delimeters = { ' ', '@' }; var message = e.Message; if (message == null || message.Type != MessageType.Text) return; string command = message.Text.Split(delimeters).First(); switch (command) { // add user into database case "/start": await Logger.LogInfo( $"Received a text message {command} in chat {e.Message.Chat.Id}."); AddUser(e.Message.Chat); break; // add user into database case "/stop": await Logger.LogInfo( $"Received a text message {command} in chat {e.Message.Chat.Id}."); DeleteUser(e.Message.Chat); break; // connect wallet to user case "/setwallet": await Logger.LogInfo( $"Received a text message {command} in chat {e.Message.Chat.Id}."); SetWallet(e); break; // get actual data from ethermine case "/actual": await Logger.LogInfo( $"Received a text message {command} in chat {e.Message.Chat.Id}."); if (message.Text.Split(' ').Length > 1) GetActualData(e); else GetActualDataFromDatabase(e.Message.Chat); break; // get actual data from ethermine case "/rate": await Logger.LogInfo( $"Received a text message {message.Text.Split(delimeters).First()} in chat {e.Message.Chat.Id}."); GetActualRate(e); break; // send help case "/help": await Logger.LogInfo( $"Received a text message {message.Text.Split(delimeters).First()} in chat {e.Message.Chat.Id}."); await SendHelp(e.Message.Chat); break; // send last payout case "/lastpayout": await Logger.LogInfo( $"Received a text message {message.Text.Split(delimeters).First()} in chat {e.Message.Chat.Id}."); await GetLastPayout(e.Message.Chat); break; default: if (e.Message.Chat.Id > 0) await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "Incorrect message"); break; } } } //send reauest to pool api and gets back json than parse it and send message with data from it private static async void GetActualData(MessageEventArgs e) { try { var url = AppSettings.poolApiUrl + "/miner/" + e.Message.Text.Split(' ')[1] + "/currentStats"; var currnentStats = JsonDownloader._download_serialized_json_data(url); await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "Updated: " + DateTimeOffset.FromUnixTimeSeconds(currnentStats.data.time).LocalDateTime .ToString("f") + "\n" + "Reported Hashrate: " + Math.Round(currnentStats.data.reportedHashrate / 1000000D, 3) + " MH/s\n" + "Current Hashrate: " + Math.Round(currnentStats.data.currentHashrate / 1000000D, 3) + " MH/s\n" + "Valid Shares: " + currnentStats.data.validShares + "\n" + "Stale Shares: " + currnentStats.data.staleShares + "\n" + "Workers: " + currnentStats.data.activeWorkers + "\n" + "Unpaid Balance: " + Math.Round(currnentStats.data.unpaid / Math.Pow(10, 18), 5) + AppSettings.currency + "\n"); } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /actual command. See log file to get full info."); await Logger.LogError("An exception occurred while executing /actual command."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "An exception occurred while executing this command."); } } //get last data from tables miners and workers from DB and send message to user private static async void GetActualDataFromDatabase(Chat e) { try { var wallet = Botdb.users .Where(u => u.chat_id == e.Id) .Select(u => u.wallet) .FirstOrDefault(); if (wallet != null) { var lastMinerRecord = Botdb.miners .Where(w => w.wallet == wallet) .OrderByDescending(w => w.time) .FirstOrDefault(); if (lastMinerRecord != null) { //var lastWorkerRecord = new workers(); long lastTime = Botdb.workers .Where(w => w.wallet == wallet) .Max(w => w.time); var lastWorkerRecord = Botdb.workers .Where(w => w.wallet == wallet && w.time == lastTime) .OrderByDescending(w => w.time) .ToList(); if (lastWorkerRecord.First() != null) { await botClient.SendTextMessageAsync( chatId: e, text: $"Miner {lastMinerRecord.wallet} stats:\n" + "Updated: " + DateTimeOffset.FromUnixTimeSeconds(lastMinerRecord.time) .LocalDateTime .ToString("f") + "\n" + "Reported Hashrate: " + Math.Round(lastMinerRecord.reported_hashrate / 1000000D, 3) + " MH/s\n" + "Current Hashrate: " + Math.Round(lastMinerRecord.current_hashrate / 1000000D, 3) + " MH/s\n" + "Valid Shares: " + lastMinerRecord.valid_shares + "\n" + "Stale Shares: " + lastMinerRecord.stale_shares + "\n" + "Workers: " + lastMinerRecord.workers + "\n" + "Unpaid Balance: " + Math.Round(lastMinerRecord.unpaid / Math.Pow(10, 18), 5) + AppSettings.currency + "\n"); for (int i = 0; i < lastWorkerRecord.Count; i++) { Thread.Sleep(1000); if (i!=0 && i%20 == 0) //TODO SOMETIHNG BETTER THAN USUAL TIMER Thread.Sleep(30000); try { await botClient.SendTextMessageAsync( chatId: e, text: $"Worker {lastWorkerRecord[i].worker} stats:\n" + "Updated: " + DateTimeOffset.FromUnixTimeSeconds(lastWorkerRecord[i].time) .LocalDateTime .ToString("f") + "\n" + "Reported Hashrate: " + Math.Round(lastWorkerRecord[i].reported_hashrate / 1000000D, 3) + " MH/s\n" + "Current Hashrate: " + Math.Round(lastWorkerRecord[i].current_hashrate / 1000000D, 3) + " MH/s\n" + "Valid Shares: " + lastWorkerRecord[i].valid_shares + "\n" + "Stale Shares: " + lastWorkerRecord[i].stale_shares + "\n" + "Unpaid Balance: " + Math.Round(lastWorkerRecord[i].worker_unpaid / Math.Pow(10, 18), 5) + AppSettings.currency + "\n"); } catch (HttpRequestException exception) { await Console.Out.WriteLineAsync("An exception occurred on sending messages while executing /actual command."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); } } } else { await botClient.SendTextMessageAsync( chatId: e, text: "No workers data for now!"); } } else { await botClient.SendTextMessageAsync( chatId: e, text: "No miner data for now!"); } } else { await botClient.SendTextMessageAsync( chatId: e, text: "Set wallet at first! Use /setwallet"); } } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /actual command. See log file to get full info."); await Logger.LogError("An exception occurred while executing /actual command."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e, text: "An exception occurred while executing this command."); } } private static async void GetActualRate(MessageEventArgs e) { try { var url = AppSettings.poolApiUrl + "/networkStats"; var networkStats = JsonDownloader._download_serialized_json_data(url); await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "ETH: " + Math.Round(networkStats.data.usd, 2) + "\n" + "BTC: " + Math.Round(networkStats.data.usd / networkStats.data.btc, 2) ); } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /rate command. See log file to get full info."); await Logger.LogError("An exception occurred while executing /rate command."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "An exception occurred while executing this command."); } } private static async void AddUser(Chat e) { try { if (Botdb.users.Where(users => users.chat_id == e.Id).FirstOrDefault() == null) { var newuser = new users(); newuser.chat_id = e.Id; Botdb.users.Add(newuser); Botdb.SaveChanges(); await botClient.SendTextMessageAsync( chatId: e, text: "Added new user to database. Now you can connect your miner wallet using command /setwallet
"); } else { await botClient.SendTextMessageAsync( chatId: e, text: "Already registered"); } } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /start command. See log file to get full info."); await Logger.LogError("An exception occurred while executing /start command."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e, text: "An exception occurred while executing this command."); } } private static async void SetWallet(MessageEventArgs e) { try { if (Botdb.users.Where(u => u.chat_id == e.Message.Chat.Id).FirstOrDefault() != null) { if (e.Message.Text.Split(' ')[1].Length > 2) { if (e.Message.Text.Split(' ')[1].Contains("0x")) { Botdb.users.Where(u => u.chat_id == e.Message.Chat.Id).FirstOrDefault().wallet = e.Message.Text.Split(' ')[1]; } else { Botdb.users.Where(u => u.chat_id == e.Message.Chat.Id).FirstOrDefault().wallet = "0x" + e.Message.Text.Split(' ')[1]; } await Botdb.SaveChangesAsync(); await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: $"Wallet {"0x" + e.Message.Text.Split(' ')[1]} set!"); } else { await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "Too short!"); } //Botdb.SaveChanges(); } else { await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "You not registered! Type /start first!"); } } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /setwallet command. See log file to get full info."); await Logger.LogError("An exception occurred while executing /setwallet command."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e.Message.Chat, text: "An exception occurred while executing this command."); } } static async void DeleteUser(Chat e) { try { if (Botdb.users.Where(users => users.chat_id == e.Id).FirstOrDefault() != null) { var deletableWallet = Botdb.users.Where(u => u.chat_id == e.Id).First().wallet; if (Botdb.users.Where(u => u.wallet == deletableWallet).Count() > 1) { Botdb.users.RemoveRange(Botdb.users.Where(u=> u.chat_id == e.Id)); } else { Botdb.workers.RemoveRange(Botdb.workers.Where(w=> w.wallet == deletableWallet)); Botdb.miners.RemoveRange(Botdb.miners.Where(m=> m.wallet == deletableWallet)); Botdb.payouts.RemoveRange(Botdb.payouts.Where(p=> p.wallet == deletableWallet)); Botdb.users.RemoveRange(Botdb.users.Where(u=> u.chat_id == e.Id)); } await Botdb.SaveChangesAsync(); await botClient.SendTextMessageAsync( chatId: e, text: "Done!"); } else { await botClient.SendTextMessageAsync( chatId: e, text: "Already deleted"); } } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /delete command. See log file to get full info."); await Logger.LogError("An exception occurred while executing /delete command."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e, text: "An exception occurred while executing this command."); } } //gets data from pool API in json, calculates unpaid for each worker and adds it to DB //public static async Task UpdateData() static async Task SendHelp(Chat e) { try { await botClient.SendTextMessageAsync( chatId: e, text: $"This is bot tracks your miner stats and calculates unpaid per worker.\n" + $"How to start:\n" + $"1) \"/start\" - it will add you to database\n" + $"2) \"/setwallet \" - it will set wallet for tracking, bot will start to add info about your miner and workers to database\n" + $"3) \"/actual\" - it will send you up to date data about your worker\n" + $"4) \"/lastpayout\" - it will send you last payout data with calculated worker unpaid for that period if avaliable\n" + $"5) \"/stop\" - it will clear all data about you from database\n" + $"Additional commands:\n" + $"\"/rate\" - get actual ETH and BTC rate from ethermine\n" + $"\"/actual \" - get up to date data about any wallet without unpaid per worker\n"); } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /help command. See log file to get full info."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e, text: "An exception occurred while executing this command."); } } static async Task GetLastPayout(Chat e) { try { var wallet = Botdb.users .Where(u => u.chat_id == e.Id) .Select(u => u.wallet) .FirstOrDefault(); if (wallet != null) { //TODO rewrite this long payoutTime; try { payoutTime = Botdb.payouts .Where(p => p.wallet == wallet) .OrderByDescending(p => p.time) .Select(p => p.time) .MaxAsync().Result; } catch (Exception exception) { string message = $"No payouts data for {wallet}! Time is set to 0!"; await Console.Out.WriteLineAsync(message); await Logger.LogWarn(message); //await Logger.LogError(exception.Message); //await Logger.LogError(exception.Source); //await Logger.LogError(exception.StackTrace); payoutTime = 0; } var dbPayoutRecords = Botdb.payouts .Where(p => p.wallet == wallet && p.time == payoutTime) .OrderByDescending(p => p.time); if (dbPayoutRecords.FirstOrDefault() != null) { string message = "Payout date: " + DateTimeOffset.FromUnixTimeSeconds(dbPayoutRecords.First().time).LocalDateTime .ToString("f") + "\n" + "Amount: " + Math.Round(dbPayoutRecords.First().amount / 1000000000000000000D, 5) + AppSettings.currency + "\n"; foreach (var payoutRecord in dbPayoutRecords) { message += $"Worker {payoutRecord.worker} paid: {Math.Round(payoutRecord.worker_amount / 1000000000000000000D, 5)} {AppSettings.currency}\n"; } message += "Data source: Bot database \n"; await botClient.SendTextMessageAsync( chatId: e, text: message); } else { var url = AppSettings.poolApiUrl + "/miner/" + wallet + "/payouts"; var payouts = JsonDownloader._download_serialized_json_data(url); await botClient.SendTextMessageAsync( chatId: e, text: "Payout date: " + DateTimeOffset.FromUnixTimeSeconds(payouts.data[0].paidOn).LocalDateTime.ToString("f") + "\n" + "Amount: " + Math.Round(payouts.data[0].amount / 1000000000000000000D, 5) + AppSettings.currency + "\n" + "Data source: Ethermine API \n"); } } else { await botClient.SendTextMessageAsync( chatId: e, text: "Set wallet at first! Use /setwallet"); } } catch (Exception exception) { await Console.Out.WriteLineAsync("An exception occurred while executing /lastpayout command. See log file to get full info."); await Logger.LogError(exception.Message); await Logger.LogError(exception.Source); await Logger.LogError(exception.StackTrace); await botClient.SendTextMessageAsync( chatId: e, text: "An exception occurred while executing this command."); } } } }