public async Task <(CurrencyRaffleGame, JoinErrorType?)> JoinOrCreateGame(ulong channelId, IUser user, long amount, bool mixed, Func <IUser, long, Task> onEnded)
        {
            await _locker.WaitAsync().ConfigureAwait(false);

            try
            {
                var newGame = false;
                if (!Games.TryGetValue(channelId, out var crg))
                {
                    newGame = true;
                    crg     = new CurrencyRaffleGame(mixed
                        ? CurrencyRaffleGame.Type.Mixed
                        : CurrencyRaffleGame.Type.Normal);
                    Games.Add(channelId, crg);
                }

                //remove money, and stop the game if this
                // user created it and doesn't have the money
                if (!await _cs.RemoveAsync(user.Id, "Currency Raffle Join", amount).ConfigureAwait(false))
                {
                    if (newGame)
                    {
                        Games.Remove(channelId);
                    }
                    return(null, JoinErrorType.NotEnoughCurrency);
                }

                if (!crg.AddUser(user, amount))
                {
                    await _cs.AddAsync(user.Id, "Curency Raffle Refund", amount).ConfigureAwait(false);

                    return(null, JoinErrorType.AlreadyJoinedOrInvalidAmount);
                }
                if (newGame)
                {
                    var _t = Task.Run(async() =>
                    {
                        await Task.Delay(60000).ConfigureAwait(false);
                        await _locker.WaitAsync().ConfigureAwait(false);
                        try
                        {
                            var winner = crg.GetWinner();
                            var won    = crg.Users.Sum(x => x.Amount);

                            await _cs.AddAsync(winner.DiscordUser.Id, "Currency Raffle Win",
                                               won).ConfigureAwait(false);
                            Games.Remove(channelId, out _);
                            var oe = onEnded(winner.DiscordUser, won);
                        }
                        catch { }
                        finally { _locker.Release(); }
                    });
                }
                return(crg, null);
            }
            finally
            {
                _locker.Release();
            }
        }
        public async Task <(CurrencyRaffleGame, JoinErrorType?)> JoinOrCreateGame(ulong channelId, IUser user, int amount, Func <IUser, int, Task> onEnded)
        {
            await _locker.WaitAsync().ConfigureAwait(false);

            try
            {
                var newGame = false;
                if (!Games.TryGetValue(channelId, out var crg))
                {
                    newGame = true;
                    crg     = new CurrencyRaffleGame(amount);
                }
                using (var uow = _db.UnitOfWork)
                {
                    //remove money, and stop the game if this
                    // user created it and doesn't have the money
                    if (!await _cs.RemoveAsync(user.Id, "Currency Raffle Join", amount, uow).ConfigureAwait(false))
                    {
                        if (newGame)
                        {
                            Games.Remove(channelId);
                        }
                        return(null, JoinErrorType.NotEnoughCurrency);
                    }

                    if (!crg.AddUser(user))
                    {
                        await _cs.AddAsync(user.Id, "Curency Raffle Refund", amount, uow).ConfigureAwait(false);

                        return(null, JoinErrorType.AlreadyJoined);
                    }
                }
                if (newGame)
                {
                    var _t = new Timer(async state =>
                    {
                        await _locker.WaitAsync().ConfigureAwait(false);
                        try
                        {
                            var users = crg.Users.ToArray();
                            var rng   = new NadekoRandom();
                            var usr   = users[rng.Next(0, users.Length)];

                            using (var uow = _db.UnitOfWork)
                            {
                                await _cs.AddAsync(usr.Id, "Currency Raffle Win",
                                                   amount * users.Length, uow);
                            }
                            Games.Remove(channelId, out _);
                            var oe = onEnded(usr, users.Length * amount);
                        }
                        catch { }
                        finally { _locker.Release(); }
                    }, null, 30000, Timeout.Infinite);
                }
                return(crg, null);
            }
            finally
            {
                _locker.Release();
            }
        }