public async Task ProcessQueue(CancellationToken cancellationToken) { while (true) { cancellationToken.ThrowIfCancellationRequested(); if (DateTime.UtcNow >= DateTime.UtcNow.Date.AddHours(7) && dateTime != DateTime.UtcNow.Date) { Log.Write("Refreshing queue...", LogType.Queue); List <User> usersCopy = users.ShallowCopy(); users.Shuffle(); DateTime tmpDate = DateTime.UtcNow; foreach (User user in usersCopy) { if (usersToSkip.Contains(user.Login)) { Log.Write($"Skipping user {user.Login}", LogType.Queue); usersToSkip.Remove(user.Login); continue; } tmpDate = tmpDate.AddMinutes(rnd.Next(15, 41)).AddSeconds(rnd.Next(0, 60)); usersQueue.Enqueue(new QueueEntry() { User = user, ProcessTime = tmpDate }); Log.Write($"Queued user {user.Login} at {tmpDate}", LogType.Queue); await communication.SendMessageAsync(user.UserId, "Your InstaShit session " + $"will be started at {tmpDate.AddHours(2).ToString("HH:mm:ss")} Polish time (with max. 1 minute delay). Please don't attempt " + "to start Insta.Ling session at this time, even from other InstaShit apps. " + "You'll be notified when your session finishes."); } dateTime = DateTime.UtcNow.Date; } while (usersQueue.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); QueueEntry entry = usersQueue.Peek(); if (DateTime.UtcNow >= entry.ProcessTime) { usersQueue.Dequeue(); if (!users.Any(u => u.Login == entry.User.Login)) { continue; } if (usersToSkip.Contains(entry.User.Login)) { Log.Write($"Skipping user {entry.User.Login}", LogType.Queue); usersToSkip.Remove(entry.User.Login); continue; } InstaShit instaShit; try { instaShit = new InstaShit(Path.Combine(assemblyLocation, entry.User.Login)); } catch (Exception ex) { Log.Write("Can't create InstaShit object: " + ex, LogType.Queue); await communication.SendMessageAsync(entry.User.UserId, "An internal error occured. " + "It has been reported to the administrator."); continue; } Log.Write($"Starting InstaShit for user {entry.User.Login}", LogType.Queue); await communication.SendMessageAsync(entry.User.UserId, "Starting session."); cancellationToken.ThrowIfCancellationRequested(); if (!await instaShit.Process()) { await communication.SendMessageAsync(entry.User.UserId, "An error occured " + "while processing your session. Please try to solve session using " + "InstaShit.CLI or InstaShit.Android. Please also check if Insta.Ling website " + "is down. If you think it's InstaShit's fault, create an issue on GitHub: " + "https://github.com/konrad11901/InstaShit.Bot if it applies only to InstaShit.Bot or " + "https://github.com/konrad11901/InstaShitCore if it affects all InstaShit apps."); await communication.SendMessageAsync(entry.User.UserId, $"Detailed information: {instaShit.ErrorMessage}"); Log.Write("Can't solve session, moving to next person", LogType.Queue); } else { Log.Write($" Session finished", LogType.Queue); await communication.SendMessageAsync(entry.User.UserId, "Session successfully " + "finished."); var childResults = await instaShit.GetResultsAsync(); StringBuilder messageToSend = new StringBuilder(); if (childResults.PreviousMark != "NONE") { messageToSend.AppendLine($"Mark from previous week: {childResults.PreviousMark}"); } messageToSend.AppendLine($"Days of work in this week: {childResults.DaysOfWork}"); messageToSend.AppendLine($"From extracurricular words: +{childResults.ExtraParentWords}"); messageToSend.AppendLine($"Teacher's words: {childResults.TeacherWords}"); messageToSend.AppendLine($"Extracurricular words in current edition: {childResults.ParentWords}"); messageToSend.AppendLine($"Mark as of today at least: {childResults.CurrrentMark}"); messageToSend.Append($"Days until the end of this week: {childResults.WeekRemainingDays}"); await communication.SendMessageAsync(entry.User.UserId, messageToSend.ToString()); } cancellationToken.ThrowIfCancellationRequested(); } if (usersQueue.Count > 0) { TimeSpan span = usersQueue.Peek().ProcessTime - DateTime.UtcNow; if (span.TotalMilliseconds > 0) { Log.Write("Waiting " + (int)span.TotalMilliseconds + " milliseconds for next person...", LogType.Queue); await Task.Delay((int)span.TotalMilliseconds, cancellationToken); } } } TimeSpan span2; if (DateTime.UtcNow.Hour < 7) { span2 = DateTime.UtcNow.Date.AddHours(7) - DateTime.UtcNow; } else { span2 = DateTime.UtcNow.Date.AddDays(1).AddHours(7) - DateTime.UtcNow; } Log.Write("Waiting " + (int)span2.TotalMilliseconds + " milliseconds for next queue refresh...", LogType.Queue); await Task.Delay((int)span2.TotalMilliseconds, cancellationToken); } }
private async void BotOnMessageReceived(object sender, MessageEventArgs args) { Telegram.Bot.Types.Message message = args.Message; if (message.Type == MessageType.Document && userStep.ContainsKey(message.From.Id) && userStep[message.From.Id] == 1) { Telegram.Bot.Types.File file = await Bot.GetFileAsync(message.Document.FileId); try { string content; using (var client = new WebClient()) content = await client.DownloadStringTaskAsync($"https://api.telegram.org/file/bot{token}/{file.FilePath}"); InstaShitCore.Settings settings = JsonConvert.DeserializeObject <InstaShitCore.Settings>(content); if (Directory.Exists(Path.Combine(assemblyLocation, settings.Login))) { await SendMessageAsync(message.From.Id, "This user is already registered. " + "If you have recently unregisted from this bot, you have to wait up to 24 hours " + "before registering again."); return; } if (settings.MinimumSleepTime < 3000 || settings.MinimumSleepTime > 10000) { await SendMessageAsync(message.Chat.Id, "MinimumSleepTime must be at least " + "3000 but no more than 10000."); return; } if (settings.MaximumSleepTime < 5000 || settings.MaximumSleepTime > 15000) { await SendMessageAsync(message.Chat.Id, "MaximumSleepTime must be at least " + "5000 but no more than 15000."); return; } if (settings.MaximumSleepTime - settings.MinimumSleepTime < 2000) { await SendMessageAsync(message.Chat.Id, "The difference between " + "MaximumSleepTime and MinimumSleepTime must be at least 2000 and " + "MaximumSleepTime must be greater than MinimumSleepTime."); return; } InstaShit instaShit = new InstaShit(settings); if (!await instaShit.TryLoginAsync()) { await SendMessageAsync(message.Chat.Id, "Incorrect login or password."); return; } Directory.CreateDirectory(Path.Combine(assemblyLocation, settings.Login)); File.WriteAllText(Path.Combine(assemblyLocation, settings.Login, "settings.json"), JsonConvert.SerializeObject(settings, Formatting.Indented)); userStep.Remove(message.From.Id); User user = new User() { Login = settings.Login, UserId = message.From.Id }; users.Add(user); await SendMessageAsync(message.Chat.Id, "User successfully added!\n" + "You'll be added to queue at the next queue refresh (9:00 Polish time every day)."); } catch { await SendMessageAsync(message.Chat.Id, "An error occured while trying to get settings. " + "Make sure you're sending the right file."); } } if (message.Type == MessageType.Text) { Log.Write($"Received message from user {message.From.Id}: {message.Text}", LogType.Communication); switch (message.Text.ToLower()) { case "/start": await SendMessageAsync(message.Chat.Id, "Hi! I'm InstaShit, a bot " + "for Insta.Ling which automatically solves daily sessions.\nTo get started, " + " type /configure. For more information, type /help."); break; case "/configure": if (users.Any(u => u.UserId == message.From.Id)) { await SendMessageAsync(message.Chat.Id, "Already configured."); return; } if (!userStep.ContainsKey(message.From.Id)) { if (useWhitelist) { userStep.Add(message.From.Id, 4); await SendMessageAsync(message.Chat.Id, "This instance of InstaShit.Bot has whitelisting " + "enabled. Please enter the unique key received from the server administrator. " + "If you don't have it, contact server's owner or use another instance of the " + "bot which is publically available.\nTo abort, type /cancel."); } else { userStep.Add(message.From.Id, 1); await SendMessageAsync(message.Chat.Id, "Please attach the InstaShit settings file.\n" + "You can use InstaShit.CLI to generate it. You can also " + "share settings directly from InstaShit.Android " + "(since version 0.5) - just go to \"Edit settings\", \"Advanced mode\" " + "and touch \"Share\" button. Ability to create new settings directly " + "from bot coming soon!\nType /cancel to abort this action."); } } break; case "/dictionary": var user = users.FirstOrDefault(u => u.UserId == message.From.Id); if (user == null) { await SendMessageAsync(message.Chat.Id, "No configuration found."); return; } if (File.Exists(Path.Combine(assemblyLocation, user.Login, "wordsDictionary.json"))) { await SendFileAsync(message.Chat.Id, Path.Combine(assemblyLocation, user.Login, "wordsDictionary.json")); } else { await SendMessageAsync(message.Chat.Id, "Dictionary file doesn't exist."); } break; case "/cancel": if (userStep.ContainsKey(message.From.Id)) { userStep.Remove(message.From.Id); await SendMessageAsync(message.Chat.Id, "Cancelled!"); } break; case "/remove": if (!users.Any(u => u.UserId == message.From.Id)) { await SendMessageAsync(message.Chat.Id, "No configuration found."); return; } if (!userStep.ContainsKey(message.From.Id)) { userStep.Add(message.From.Id, 2); await SendMessageAsync(message.Chat.Id, "You can stop automatic InstaShit session " + "solving if you wish. Please note that this action won't cancel ongoing InstaShit session, " + "if there's any.\nIf you want to continue, type /remove again. " + "To cancel, type /cancel."); } else if (userStep[message.From.Id] == 2) { userStep.Remove(message.From.Id); User userToRemove = users.Find(u => u.UserId == message.From.Id); users.Remove(userToRemove); try { Directory.Delete(Path.Combine(assemblyLocation, userToRemove.Login), true); await SendMessageAsync(message.Chat.Id, "Successfully removed."); } catch { await Task.Delay(3000); try { Directory.Delete(Path.Combine(assemblyLocation, userToRemove.Login), true); await SendMessageAsync(message.Chat.Id, "Successfully removed."); } catch (Exception ex) { Log.Write("ERROR: " + ex, LogType.Communication); await SendMessageAsync(message.Chat.Id, "An error occured while trying to remove " + "your files. The administrator has been notifies about this issue."); } } } break; case "/skip": if (!users.Any(u => u.UserId == message.From.Id)) { await SendMessageAsync(message.Chat.Id, "No configuration found."); return; } if (usersToSkip.Contains(users.Find(u => u.UserId == message.From.Id).Login)) { await SendMessageAsync(message.Chat.Id, "Already on the skip list."); return; } if (!userStep.ContainsKey(message.From.Id)) { userStep.Add(message.From.Id, 3); await SendMessageAsync(message.Chat.Id, "You can skip the next InstaShit session if you wish. " + " Please note that this action won't cancel ongoing InstaShit session, if there's any." + "\nIf you want to continue, type /skip again. To cancel, type /cancel."); } else if (userStep[message.From.Id] == 3) { userStep.Remove(message.From.Id); usersToSkip.Add(users.Find(u => u.UserId == message.From.Id).Login); await SendMessageAsync(message.Chat.Id, "Successfully added to skip list."); } break; default: if (userStep.ContainsKey(message.From.Id) && userStep[message.From.Id] == 4) { if (whitelist.Any(t => t.Item1 == message.Text)) { whitelist.Remove(whitelist.Find(t => t.Item1 == message.Text)); userStep[message.From.Id] = 1; await SendMessageAsync(message.Chat.Id, "Success!"); await SendMessageAsync(message.Chat.Id, "Please attach the InstaShit settings file.\n" + "You can use InstaShit.CLI to generate it. You can also share settings directly " + "from InstaShit.Android (since version 0.5) - just go to \"Edit settings\", " + "\"Advanced mode\" and touch \"Share\" button. Ability to create new settings " + "using the bot coming soon!\nType /cancel to abort this action (you'll need " + "a new unique key if you try to configure the bot again in the future)."); } else { await SendMessageAsync(message.Chat.Id, "Incorrect key."); } break; } await SendMessageAsync(message.Chat.Id, "Usage:\n" + "/configure - Configures InstaShit bot\n" + "/skip - Skips next InstaShit session\n" + "/remove - Unregisters from the bot\n" + "/cancel - Cancels any ongoing process (configure/remove/skip)\n" + "/dictionary - Returns the wordsDictionary.json file"); break; } } }