/// <summary>Transfers <paramref name="amount"/> of money from <paramref name="sender"/> to <paramref name="userBeingTipped"/>.</summary> /// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> public void TipUser(IUser sender, IUser userBeingTipped, decimal amount) { this.logger.Trace("({0}:{1},{2}:'{3}',{4}:{5})", nameof(sender), sender.Id, nameof(userBeingTipped), userBeingTipped.Id, nameof(amount), amount); this.AssertAmountPositive(amount); this.AssertUsersNotEqual(sender, userBeingTipped); using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUserSender = this.GetOrCreateUser(context, sender); this.AssertBalanceIsSufficient(discordUserSender, amount); DiscordUserModel discordUserReceiver = this.GetOrCreateUser(context, userBeingTipped); discordUserSender.Balance -= amount; discordUserReceiver.Balance += amount; context.Update(discordUserReceiver); this.AddTipToHistory(context, amount, discordUserReceiver.DiscordUserId, discordUserSender.DiscordUserId); context.SaveChanges(); this.logger.Debug("User '{0}' tipped {1} to '{2}'", discordUserSender, discordUserReceiver, amount); } this.logger.Trace("(-)"); }
/// <summary>Checks quizes and removes those that are expired.</summary> private void CheckQuizesExpired() { this.logger.Trace("()"); using (BotDbContext context = this.contextFactory.CreateContext()) { foreach (QuizModel quiz in context.ActiveQuizes.ToList()) { if (DateTime.Now > (quiz.CreationTime + TimeSpan.FromMinutes(quiz.DurationMinutes))) { // Quiz expired. Return money to creator and remove the quiz. this.logger.Info("Quiz {0} expired.", quiz.Id); DiscordUserModel quizCreator = context.Users.Single(x => x.DiscordUserId == quiz.CreatorDiscordUserId); quizCreator.Balance += quiz.Reward; context.Update(quizCreator); context.ActiveQuizes.Remove(quiz); context.SaveChanges(); } } } this.logger.Trace("(-)"); }
public AnswerToQuizResponseModel AnswerToQuiz(IUser user, string answer) { this.logger.Trace("({0}:'{1}')", nameof(answer), answer); if (answer.Length > 1024) { // We don't want to hash big strings. this.logger.Trace("(-)[ANSWER_TOO_LONG]"); return(new AnswerToQuizResponseModel() { Success = false }); } string answerHash = Cryptography.Hash(answer); using (BotDbContext context = this.contextFactory.CreateContext()) { foreach (QuizModel quiz in context.ActiveQuizes.ToList()) { if (DateTime.Now > (quiz.CreationTime + TimeSpan.FromMinutes(quiz.DurationMinutes))) { // Quiz expired but just not deleted yet. continue; } if (quiz.AnswerHash == answerHash) { DiscordUserModel winner = this.GetOrCreateUser(context, user); winner.Balance += quiz.Reward; context.Update(winner); context.ActiveQuizes.Remove(quiz); context.SaveChanges(); this.logger.Debug("User {0} solved quiz with hash {1} and received a reward of {2}.", winner, quiz.AnswerHash, quiz.Reward); var response = new AnswerToQuizResponseModel() { Success = true, Reward = quiz.Reward, QuizCreatorDiscordUserId = quiz.CreatorDiscordUserId, QuizQuestion = quiz.Question }; this.logger.Trace("(-)"); return(response); } } } this.logger.Trace("(-)[QUIZ_NOT_FOUND]"); return(new AnswerToQuizResponseModel() { Success = false }); }
private void AssertBalanceIsSufficient(DiscordUserModel user, decimal balanceRequired) { this.logger.Trace("({0}:'{1}',{2}:{3})", nameof(user), user, nameof(balanceRequired), balanceRequired); if (user.Balance < balanceRequired) { this.logger.Trace("(-)[INVALID_AMOUNT_VALUE]"); throw new CommandExecutionException("Insufficient funds."); } this.logger.Trace("(-)"); }
public decimal GetUserBalance(IUser user) { this.logger.Trace("({0}:{1})", nameof(user), user.Id); using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUser = this.GetOrCreateUser(context, user); decimal balance = discordUser.Balance; this.logger.Trace("(-):{0}", balance); return(balance); } }
private DiscordUserModel GetOrCreateUser(BotDbContext context, IUser user) { this.logger.Trace("({0}:{1})", nameof(user), user.Id); DiscordUserModel discordUser = context.Users.SingleOrDefault(x => x.DiscordUserId == user.Id); if (discordUser == null) { discordUser = this.CreateUser(context, user); } this.logger.Trace("(-):'{0}'", discordUser); return(discordUser); }
/// <summary>Withdraws given amount of money to specified address.</summary> /// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> public void Withdraw(IUser user, decimal amount, string address) { this.logger.Trace("({0}:{1},{2}:{3},{4}:'{5}')", nameof(user), user.Id, nameof(amount), amount, nameof(address), address); this.AssertAmountPositive(amount); if (amount < this.settings.MinWithdrawAmount) { this.logger.Trace("(-)[MIN_WITHDRAW_AMOUNT]"); throw new CommandExecutionException($"Minimal withdraw amount is {this.settings.MinWithdrawAmount} {this.settings.Ticker}."); } using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUser = this.GetOrCreateUser(context, user); // Don't allow withdrawals to deposit address. if (discordUser.DepositAddress != null && discordUser.DepositAddress == address) { this.logger.Trace("(-)[WITHDRAWAL_TO_DEPOSIT_ADDR]"); throw new CommandExecutionException("You can't withdraw to your own deposit address!"); } decimal amountToSend = amount - this.settings.NetworkFee; this.logger.Trace("(The amount after fee: {0})", amountToSend); this.AssertBalanceIsSufficient(discordUser, amountToSend); try { this.nodeIntegration.Withdraw(amountToSend, address); this.logger.Debug("User '{0}' withdrew {1} to address '{2}'. After fee subtracted '{3}'", discordUser, amount, address, amountToSend); } catch (InvalidAddressException) { this.logger.Trace("(-)[INVALID_ADDRESS]"); throw new CommandExecutionException("Address specified is invalid."); } discordUser.Balance -= amount; context.Update(discordUser); context.SaveChanges(); } this.logger.Trace("(-)"); }
public QuizTests() { this.testContext = new TestContext(); this.caller = this.testContext.SetupUser(1, "caller"); // That will create a user in db. this.testContext.CommandsManager.GetUserBalance(this.caller); using (BotDbContext dbContext = this.testContext.CreateDbContext()) { DiscordUserModel user = dbContext.Users.First(); user.Balance = 10; dbContext.Update(user); dbContext.SaveChanges(); } }
private DiscordUserModel CreateUser(BotDbContext context, IUser user) { this.logger.Trace("({0}:{1})", nameof(user), user.Id); this.logger.Debug("Creating a new user with id {0} and username '{1}'.", user.Id, user.Username); var discordUser = new DiscordUserModel() { Balance = 0, DiscordUserId = user.Id, Username = user.Username }; context.Users.Add(discordUser); context.SaveChanges(); this.logger.Trace("(-):'{0}'", discordUser); return(discordUser); }
public async Task AddItem(CommandContext ctx, string itemName, string itemDescription) { var user = new DiscordUserModel { UserId = ctx.Member.Id, DisplayName = ctx.Member.DisplayName }; var loggingEvent = new LoggingEventModel { Date = DateTime.UtcNow, DiscordDisplayName = user.DisplayName, DiscordUserId = user.UserId, LoggingType = LoggingTypeEnum.Command, Message = "AddItem" }; await _loggingService.AddLoggingMessage(loggingEvent); await _itemService.AddItemAsync(itemName, itemDescription); await ctx.Channel.SendMessageAsync("Item added").ConfigureAwait(false); }
public void ReturnsZeroIfUserNotFound() { IUser user = this.testContext.SetupUser(1, "user"); decimal balance = this.testContext.CommandsManager.GetUserBalance(user); Assert.Equal(0, balance); // Make sure new user was created. using (BotDbContext dbContext = this.testContext.CreateDbContext()) { Assert.Equal(1, dbContext.Users.Count()); DiscordUserModel discordUser = dbContext.Users.First(); Assert.Equal(user.Id, discordUser.DiscordUserId); Assert.Equal(user.Username, discordUser.Username); } }
public IActionResult SaveUserToken([FromBody] DiscordUserModel model) { if (model == null) { return(new BadRequestResult()); } try { using (var db = new LiteDatabase($"filename=auth.db; journal=false;")) { var col = db.GetCollection <DiscordUserModel>("DiscordUsers"); col.EnsureIndex(x => x.Id); col.Insert(model); } return(Ok()); } catch (Exception ex) { Log.Logger.Error($"Exception occured: {ex.GetType()}: {ex.Message}"); return(StatusCode((int)System.Net.HttpStatusCode.InternalServerError, ex.Message)); } }
/// <summary>Gets deposit address for a user.</summary> /// <exception cref="OutOfDepositAddressesException">Thrown when bot ran out of unused deposit addresses.</exception> public string GetDepositAddress(IUser user) { this.logger.Trace("({0}:{1})", nameof(user), user.Id); using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUser = this.GetOrCreateUser(context, user); string depositAddress = discordUser.DepositAddress; // Assign deposit address if it wasn't assigned it. if (depositAddress == null) { this.logger.Trace("Assigning deposit address for '{0}'.", discordUser); AddressModel unusedAddress = context.UnusedAddresses.FirstOrDefault(); if (unusedAddress == null) { var message = "Bot ran out of deposit addresses!"; this.logger.Fatal(message); this.fatalNotifier.NotifySupport(message); this.logger.Trace("(-)[NO_ADDRESSES]"); throw new OutOfDepositAddressesException(); } context.UnusedAddresses.Remove(unusedAddress); depositAddress = unusedAddress.Address; discordUser.DepositAddress = depositAddress; context.Update(discordUser); context.SaveChanges(); } this.logger.Trace("(-):'{0}'", depositAddress); return(depositAddress); } }
/// <summary>Withdraws given amount of money to specified address.</summary> /// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> public void Withdraw(IUser user, decimal amount, string address) { this.logger.Trace("({0}:{1},{2}:{3},{4}:'{5}')", nameof(user), user.Id, nameof(amount), amount, nameof(address), address); this.AssertAmountPositive(amount); using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUser = this.GetOrCreateUser(context, user); this.AssertBalanceIsSufficient(discordUser, amount); if (amount < this.settings.MinWithdrawAmount) { this.logger.Trace("(-)[MIN_WITHDRAW_AMOUNT]"); throw new CommandExecutionException($"Minimal withdraw amount is {this.settings.MinWithdrawAmount} {this.settings.Ticker}."); } try { this.nodeIntegration.Withdraw(amount, address); this.logger.Debug("User '{0}' withdrew {1} to address '{2}'.", discordUser, amount, address); } catch (InvalidAddressException) { this.logger.Trace("(-)[INVALID_ADDRESS]"); throw new CommandExecutionException("Address specified is invalid."); } discordUser.Balance -= amount; context.Update(discordUser); context.SaveChanges(); } this.logger.Trace("(-)"); }
public RandomlyTipUsersTests() { this.testContext = new TestContext(); this.caller = this.testContext.SetupUser(1, "caller"); // That will create a user in db. this.testContext.CommandsManager.GetUserBalance(this.caller); using (BotDbContext dbContext = this.testContext.CreateDbContext()) { DiscordUserModel user = dbContext.Users.First(); user.Balance = 10; dbContext.Update(user); dbContext.SaveChanges(); } this.onlineUsers = new List <IUser>(); for (var i = 0; i < 10; i++) { IUser user = this.testContext.SetupUser((ulong)(i + 2), i.ToString()); this.onlineUsers.Add(user); } }
/// <summary>Transfers <paramref name="amount"/> of money from <paramref name="sender"/> to <paramref name="userBeingTipped"/>.</summary> /// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> public void TipUser(IUser sender, IUser userBeingTipped, decimal amount) { this.logger.Trace("({0}:{1},{2}:'{3}',{4}:{5})", nameof(sender), sender.Id, nameof(userBeingTipped), userBeingTipped.Id, nameof(amount), amount); this.AssertAmountPositive(amount); this.AssertUsersNotEqual(sender, userBeingTipped); if (amount < this.settings.MinTipAmount) { this.logger.Trace("(-)[TIP TOO SMALL]'"); throw new CommandExecutionException($"Minimal tip is {this.settings.MinTipAmount}."); } using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUserSender = this.GetOrCreateUser(context, sender); this.AssertBalanceIsSufficient(discordUserSender, amount); DiscordUserModel discordUserReceiver = this.GetOrCreateUser(context, userBeingTipped); discordUserSender.Balance -= amount; discordUserReceiver.Balance += amount; context.Update(discordUserReceiver); this.AddTipToHistory(context, amount, discordUserReceiver.DiscordUserId, discordUserReceiver.Username, discordUserSender.DiscordUserId, discordUserSender.Username); context.SaveChanges(); this.logger.Debug("User '{0}' tipped {1} to '{2}'", discordUserSender, discordUserReceiver, amount); } this.logger.Trace("(-)"); }
/// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> /// <returns>List of users that were tipped.</returns> public List <DiscordUserModel> RandomlyTipUsers(IUser caller, List <IUser> onlineUsers, decimal totalAmount, decimal tipAmount) { this.logger.Trace("({0}:{1},{2}.{3}:{4},{5}:{6})", nameof(caller), caller.Id, nameof(onlineUsers), nameof(onlineUsers.Count), onlineUsers.Count, nameof(totalAmount), totalAmount); this.AssertAmountPositive(totalAmount); if (tipAmount < this.settings.MinMakeItRainTipAmount) { this.logger.Trace("(-)[TIP_SIZE_TOO_SMALL]'"); throw new CommandExecutionException($"Tip amount can't be less than {this.settings.MinMakeItRainTipAmount}."); } if (totalAmount < tipAmount) { this.logger.Trace("(-)[AMOUNT_TOO_SMALL]'"); throw new CommandExecutionException("Total amount for tipping can't be less than specified tip amount."); } if (onlineUsers.Count == 0) { this.logger.Trace("(-)[NO_USERS_ONLINE]'"); throw new CommandExecutionException("There are no users online!"); } var tipsCount = (int)(totalAmount / tipAmount); if (tipsCount > onlineUsers.Count) { this.logger.Trace("Coins to tip was set to amount of users."); tipsCount = onlineUsers.Count; } decimal amountToSpend = tipAmount * tipsCount; using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUserCreator = this.GetOrCreateUser(context, caller); this.AssertBalanceIsSufficient(discordUserCreator, amountToSpend); var chosenDiscordUsers = new List <DiscordUserModel>(tipsCount); for (var i = 0; i < tipsCount; i++) { int userIndex = this.random.Next(onlineUsers.Count); IUser chosenUser = onlineUsers[userIndex]; onlineUsers.Remove(chosenUser); DiscordUserModel chosenDiscordUser = this.GetOrCreateUser(context, chosenUser); chosenDiscordUsers.Add(chosenDiscordUser); } discordUserCreator.Balance -= amountToSpend; context.Update(discordUserCreator); foreach (DiscordUserModel discordUser in chosenDiscordUsers) { discordUser.Balance += tipAmount; context.Update(discordUser); this.AddTipToHistory(context, tipAmount, discordUser.DiscordUserId, discordUser.Username, discordUserCreator.DiscordUserId, discordUserCreator.Username); this.logger.Debug("User '{0}' was randomly tipped.", discordUser); } this.logger.Debug("User '{0}' tipped {1} users {2} coins each.", discordUserCreator, chosenDiscordUsers.Count, tipAmount); context.SaveChanges(); this.logger.Trace("(-)"); return(chosenDiscordUsers); } }
/// <summary>Withdraws given amount of money to specified address.</summary> /// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> public void Withdraw(IUser user, decimal amount, string address) { this.logger.Trace("({0}:{1},{2}:{3},{4}:'{5}')", nameof(user), user.Id, nameof(amount), amount, nameof(address), address); this.AssertAmountPositive(amount); if (amount < this.settings.MinWithdrawAmount) { this.logger.Trace("(-)[MIN_WITHDRAW_AMOUNT]"); throw new CommandExecutionException($"Minimal withdraw amount is {this.settings.MinWithdrawAmount} {this.settings.Ticker}."); } using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUser = this.GetOrCreateUser(context, user); // Don't allow withdrawals to deposit address. if (discordUser.DepositAddress != null && discordUser.DepositAddress == address) { this.logger.Trace("(-)[WITHDRAWAL_TO_DEPOSIT_ADDR]"); throw new CommandExecutionException("You can't withdraw to your own deposit address!"); } decimal amountToSend = amount - this.settings.NetworkFee; this.logger.Trace("(The amount after fee: {0})", amountToSend); this.AssertBalanceIsSufficient(discordUser, amountToSend); try { var withdrawResult = this.nodeIntegration.Withdraw(amountToSend, address); discordUser.Balance -= amount; context.Update(discordUser); context.SaveChanges(); var withdrawHistoryItem = new WithdrawHistory { DiscordUserId = discordUser.DiscordUserId, Amount = amountToSend, ToAddress = address, TransactionId = withdrawResult.TransactionId, WithdrawTime = DateTime.UtcNow }; context.Add(withdrawHistoryItem); context.SaveChanges(); this.logger.Debug("User '{0}' withdrew {1} to address '{2}'. After fee subtracted '{3}'", discordUser, amount, address, amountToSend); } catch (InvalidAddressException) { this.logger.Trace("(-)[INVALID_ADDRESS]"); throw new CommandExecutionException("Address specified is invalid."); } catch (Exception ex) { this.logger.Trace("(-)[FAILED_WITHDRAW]"); this.logger.Debug($"(WITHDRAW_ERROR)[{ex.Message}]"); throw new CommandExecutionException($"Failed to withdraw. Contact {this.settings.Discord.SupportUsername}"); } } this.logger.Trace("(-)"); }
/// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> /// <returns>List of users that were tipped.</returns> public List <DiscordUserModel> RandomlyTipUsers(IUser caller, List <IUser> onlineUsers, decimal amount) { this.logger.Trace("({0}:{1},{2}.{3}:{4},{5}:{6})", nameof(caller), caller.Id, nameof(onlineUsers), nameof(onlineUsers.Count), onlineUsers.Count, nameof(amount), amount); this.AssertAmountPositive(amount); var coinsToTip = (int)amount; if (coinsToTip < 1) { this.logger.Trace("(-)[AMOUNT_TOO_SMALL]'"); throw new CommandExecutionException("Amount can't be less 1."); } if (onlineUsers.Count == 0) { this.logger.Trace("(-)[NO_USERS_ONLINE]'"); throw new CommandExecutionException("There are no users online!"); } if (coinsToTip > onlineUsers.Count) { this.logger.Trace("Coins to tip was set to amount of users."); coinsToTip = onlineUsers.Count; } using (BotDbContext context = this.contextFactory.CreateContext()) { DiscordUserModel discordUserCreator = this.GetOrCreateUser(context, caller); this.AssertBalanceIsSufficient(discordUserCreator, coinsToTip); var chosenDiscordUsers = new List <DiscordUserModel>(coinsToTip); for (int i = 0; i < coinsToTip; i++) { int userIndex = this.random.Next(onlineUsers.Count); IUser chosenUser = onlineUsers[userIndex]; onlineUsers.Remove(chosenUser); DiscordUserModel chosenDiscordUser = this.GetOrCreateUser(context, chosenUser); chosenDiscordUsers.Add(chosenDiscordUser); } discordUserCreator.Balance -= coinsToTip; context.Update(discordUserCreator); foreach (DiscordUserModel discordUser in chosenDiscordUsers) { discordUser.Balance += 1; context.Update(discordUser); this.AddTipToHistory(context, 1, discordUser.DiscordUserId, discordUserCreator.DiscordUserId); this.logger.Debug("User '{0}' was randomly tipped.", discordUser); } this.logger.Debug("User '{0}' tipped {1} users one coin each.", discordUserCreator, chosenDiscordUsers.Count); context.SaveChanges(); this.logger.Trace("(-)"); return(chosenDiscordUsers); } }
/// <exception cref="CommandExecutionException">Thrown when user supplied invalid input data.</exception> public void StartQuiz(IUser user, decimal amount, string answerSHA256, int durationMinutes, string question) { this.logger.Trace("({0}:{1},{2}:{3},{4}:'{5}',{6}:{7},{8}:'{9}')", nameof(user), user.Id, nameof(amount), amount, nameof(answerSHA256), answerSHA256, nameof(durationMinutes), durationMinutes, nameof(question), question); this.AssertAmountPositive(amount); answerSHA256 = answerSHA256.ToLower(); if (answerSHA256.Length != 64) { this.logger.Trace("(-)[INCORRECT_HASH]'"); throw new CommandExecutionException("SHA256 hash should contain 64 characters!"); } if (durationMinutes < 1) { this.logger.Trace("(-)[INCORRECT_DURATION]'"); throw new CommandExecutionException("Duration in minutes can't be less than 1!"); } var maxQuestionLenght = 1024; if (question.Length > maxQuestionLenght) { this.logger.Trace("(-)[QUESTION_TOO_LONG]'"); throw new CommandExecutionException($"Questions longer than {maxQuestionLenght} characters are not allowed!"); } using (BotDbContext context = this.contextFactory.CreateContext()) { if (context.ActiveQuizes.Any(x => x.AnswerHash == answerSHA256)) { this.logger.Trace("(-)[HASH_ALREADY_EXISTS]"); throw new CommandExecutionException("There is already a quiz with that answer hash!"); } DiscordUserModel discordUser = this.GetOrCreateUser(context, user); this.AssertBalanceIsSufficient(discordUser, amount); if (amount < this.settings.MinQuizAmount) { this.logger.Trace("(-)[AMOUNT_TOO_LOW]"); throw new CommandExecutionException($"Minimal quiz reward is {this.settings.MinQuizAmount} {this.settings.Ticker}!"); } var quiz = new QuizModel() { AnswerHash = answerSHA256, CreationTime = DateTime.Now, CreatorDiscordUserId = discordUser.DiscordUserId, DurationMinutes = durationMinutes, Question = question, Reward = amount }; discordUser.Balance -= amount; context.Update(discordUser); context.ActiveQuizes.Add(quiz); context.SaveChanges(); this.logger.Debug("Quiz with reward {0} and answer hash '{1}' was created by user '{2}'.", amount, answerSHA256, discordUser); } this.logger.Trace("(-)"); }