/// <summary>
        /// Checks if money were deposited to an address associated with any user who has a deposit address.
        /// When money are deposited user's balance is updated.
        /// </summary>
        private void CheckDeposits(BotDbContext context)
        {
            this.logger.Trace("()");

            List <DiscordUserModel> usersToTrack = context.Users.Where(x => x.DepositAddress != null).ToList();

            this.logger.Trace("Tracking {0} users.", usersToTrack.Count);

            foreach (DiscordUserModel user in usersToTrack)
            {
                decimal receivedByAddress = this.coinService.GetReceivedByAddress(user.DepositAddress, this.settings.MinConfirmationsForDeposit);

                if (receivedByAddress > user.LastCheckedReceivedAmountByAddress)
                {
                    this.logger.Debug("New value for received by address is {0}. Old was {1}. Address is {2}.", receivedByAddress, user.LastCheckedReceivedAmountByAddress, user.DepositAddress);

                    decimal recentlyReceived = receivedByAddress - user.LastCheckedReceivedAmountByAddress;
                    user.LastCheckedReceivedAmountByAddress = receivedByAddress;

                    user.Balance += recentlyReceived;

                    this.logger.Info("User '{0}' deposited {1}!", user, recentlyReceived);

                    context.Update(user);
                    context.SaveChanges();
                }
            }

            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("(-)");
        }
        /// <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("(-)");
        }
        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
            });
        }
示例#5
0
        private async Task GrantPermission(string discordUserId, Permission permission, BotDbContext dbContext, IDMChannel channel)
        {
            var user = await dbContext.Users.FirstOrDefaultAsync(u => u.DiscordUserId == discordUserId);

            user.UserPermissions.Add(new UserPermissions()
            {
                PermissionId = permission.Id, UserId = user.Id, User = user, Permission = permission
            });
            dbContext.Update(user);
            await dbContext.SaveChangesAsync();

            await channel.SendMessageAsync("Granted Permission");
        }
示例#6
0
        public void AnswerToQuiz_FailsIfTimeIsCorrectButExpired()
        {
            this.testContext.CommandsManager.StartQuiz(this.caller, 2, Cryptography.Hash("1"), 2, string.Empty);

            using (BotDbContext dbContext = this.testContext.CreateDbContext())
            {
                QuizModel quiz = dbContext.ActiveQuizes.First();
                quiz.CreationTime = DateTime.Now - TimeSpan.FromMinutes(20);
                dbContext.Update(quiz);
                dbContext.SaveChanges();
            }

            AnswerToQuizResponseModel answer = this.testContext.CommandsManager.AnswerToQuiz(this.caller, "1");

            Assert.False(answer.Success);
        }
示例#7
0
        /// <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("(-)");
        }
示例#8
0
        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();
            }
        }
        /// <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("(-)");
        }
示例#11
0
        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 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("(-)");
        }
        /// <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("(-)");
        }