.rafflecur added

This commit is contained in:
Master Kwoth 2017-10-20 17:20:26 +02:00
parent cfee252ce3
commit 204cdbfb2b
9 changed files with 129 additions and 57 deletions

View File

@ -1,34 +1,85 @@
using Discord;
using NadekoBot.Common;
using NadekoBot.Core.Services;
using System;
using NLog;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Core.Modules.Gambling.Common
{
public class CurrencyRaffleGame
{
private readonly HashSet<IUser> _users = new HashSet<IUser>();
public IEnumerable<IUser> Users => _users;
private readonly int _amount;
public CurrencyRaffleGame(int amount)
{
if (amount < 1)
throw new ArgumentOutOfRangeException();
_amount = amount;
public enum Type {
Mixed,
Normal
}
public bool AddUser(IUser usr)
public class User
{
if (!_users.Add(usr))
public IUser DiscordUser { get; set; }
public int Amount { get; set; }
public override int GetHashCode()
{
return DiscordUser.GetHashCode();
}
public override bool Equals(object obj)
{
return obj is User u
? u.DiscordUser == DiscordUser
: false;
}
}
private readonly HashSet<User> _users = new HashSet<User>();
public IEnumerable<User> Users => _users;
public Type GameType { get; }
private readonly Logger _log;
public CurrencyRaffleGame(Type type)
{
GameType = type;
_log = LogManager.GetCurrentClassLogger();
}
public bool AddUser(IUser usr, int amount)
{
// if game type is normal, and someone already joined the game
// (that's the user who created it)
if (GameType == Type.Normal && _users.Count > 0 &&
_users.First().Amount != amount)
return false;
if (!_users.Add(new User
{
DiscordUser = usr,
Amount = amount,
}))
{
return false;
}
return true;
}
public User GetWinner()
{
var rng = new NadekoRandom();
if (GameType == Type.Mixed)
{
var num = rng.Next(0, Users.Sum(x => x.Amount));
var sum = 0;
foreach (var u in Users)
{
sum += u.Amount;
if (sum > num)
return u;
}
_log.Error("Woah. Report this.\nRoll: {0}\nAmounts: {1}", num, string.Join(",", Users.Select(x => x.Amount)));
}
var usrs = _users.ToArray();
return usrs[rng.Next(0, usrs.Length)];
}
}
}

View File

@ -2,9 +2,10 @@
using NadekoBot.Core.Modules.Gambling.Services;
using System.Threading.Tasks;
using Discord;
using System;
using NadekoBot.Core.Services;
using NadekoBot.Extensions;
using System.Linq;
using Discord.Commands;
namespace NadekoBot.Modules.Gambling
{
@ -18,28 +19,42 @@ namespace NadekoBot.Modules.Gambling
{
_bc = bc;
}
public enum Mixed { Mixed }
[NadekoCommand, Usage, Description, Aliases]
public async Task RaffleCur(int amount)
[RequireContext(ContextType.Guild)]
[Priority(0)]
public Task RaffleCur(Mixed _, int amount) =>
RaffleCur(amount, true);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public async Task RaffleCur(int amount, bool mixed = false)
{
if (amount < 1)
return;
async Task OnEnded(IUser arg, int won)
{
await ReplyConfirmLocalized("rafflecur_ended", _bc.BotConfig.CurrencyName, Format.Bold(arg.ToString()), won + _bc.BotConfig.CurrencySign);
await Context.Channel.SendConfirmAsync(GetText("rafflecur_ended", _bc.BotConfig.CurrencyName, Format.Bold(arg.ToString()), won + _bc.BotConfig.CurrencySign));
}
var res = await _service.JoinOrCreateGame(Context.Channel.Id,
Context.User, amount, OnEnded)
Context.User, amount, mixed, OnEnded)
.ConfigureAwait(false);
if (res.Item1 != null)
{
await Context.Channel.SendConfirmAsync(GetText("rafflecur_joined", Context.User.ToString()),
string.Join("\n", res.Item1.Users)).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(GetText("rafflecur", res.Item1.GameType.ToString()),
string.Join("\n", res.Item1.Users.Select(x => $"{x.DiscordUser} ({x.Amount})")),
footer: GetText("rafflecur_joined", Context.User.ToString())).ConfigureAwait(false);
}
else
{
if (res.Item2 == CurrencyRaffleService.JoinErrorType.AlreadyJoined)
if (res.Item2 == CurrencyRaffleService.JoinErrorType.AlreadyJoinedOrInvalidAmount)
await ReplyErrorLocalized("rafflecur_already_joined").ConfigureAwait(false);
else if (res.Item2 == CurrencyRaffleService.JoinErrorType.NotEnoughCurrency)
await ReplyErrorLocalized("not_enough").ConfigureAwait(false);
await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false);
}
}
}

View File

@ -3,7 +3,6 @@ using NadekoBot.Core.Services;
using NadekoBot.Core.Modules.Gambling.Common;
using System.Threading;
using System.Linq;
using NadekoBot.Common;
using System.Collections.Generic;
using Discord;
using System;
@ -12,9 +11,10 @@ namespace NadekoBot.Core.Modules.Gambling.Services
{
public class CurrencyRaffleService : INService
{
public enum JoinErrorType {
public enum JoinErrorType
{
NotEnoughCurrency,
AlreadyJoined
AlreadyJoinedOrInvalidAmount
}
private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1);
private readonly DbService _db;
@ -28,7 +28,7 @@ namespace NadekoBot.Core.Modules.Gambling.Services
_cs = cs;
}
public async Task<(CurrencyRaffleGame, JoinErrorType?)> JoinOrCreateGame(ulong channelId, IUser user, int amount, Func<IUser, int, Task> onEnded)
public async Task<(CurrencyRaffleGame, JoinErrorType?)> JoinOrCreateGame(ulong channelId, IUser user, int amount, bool mixed, Func<IUser, int, Task> onEnded)
{
await _locker.WaitAsync().ConfigureAwait(false);
try
@ -37,7 +37,10 @@ namespace NadekoBot.Core.Modules.Gambling.Services
if (!Games.TryGetValue(channelId, out var crg))
{
newGame = true;
crg = new CurrencyRaffleGame(amount);
crg = new CurrencyRaffleGame(mixed
? CurrencyRaffleGame.Type.Mixed
: CurrencyRaffleGame.Type.Normal);
Games.Add(channelId, crg);
}
using (var uow = _db.UnitOfWork)
{
@ -50,35 +53,38 @@ namespace NadekoBot.Core.Modules.Gambling.Services
return (null, JoinErrorType.NotEnoughCurrency);
}
if (!crg.AddUser(user))
if (!crg.AddUser(user, amount))
{
await _cs.AddAsync(user.Id, "Curency Raffle Refund", amount, uow).ConfigureAwait(false);
return (null, JoinErrorType.AlreadyJoined);
return (null, JoinErrorType.AlreadyJoinedOrInvalidAmount);
}
uow.Complete();
}
if (newGame)
{
var _t = new Timer(async state =>
var _t = Task.Run(async () =>
{
await Task.Delay(30000).ConfigureAwait(false);
await _locker.WaitAsync().ConfigureAwait(false);
try
{
var users = crg.Users.ToArray();
var rng = new NadekoRandom();
var usr = users[rng.Next(0, users.Length)];
var winner = crg.GetWinner();
var won = crg.Users.Sum(x => x.Amount);
using (var uow = _db.UnitOfWork)
{
await _cs.AddAsync(usr.Id, "Currency Raffle Win",
amount * users.Length, uow);
await _cs.AddAsync(winner.DiscordUser.Id, "Currency Raffle Win",
won, uow);
uow.Complete();
}
Games.Remove(channelId, out _);
var oe = onEnded(usr, users.Length * amount);
var oe = onEnded(winner.DiscordUser, won);
}
catch { }
finally { _locker.Release(); }
}, null, 30000, Timeout.Infinite);
});
}
return (crg, null);
}

View File

@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Games
private async Task InternalStartPoll(string arg)
{
if(await _service.StartPoll((ITextChannel)Context.Channel, Context.Message, arg) == false)
if(await _service.StartPoll(Context.Channel.Id, Context.Message, arg) == false)
await ReplyErrorLocalized("poll_already_running").ConfigureAwait(false);
}
@ -54,7 +54,5 @@ namespace NadekoBot.Modules.Games
await poll.StopPoll().ConfigureAwait(false);
}
}
}
}

View File

@ -26,7 +26,7 @@ namespace NadekoBot.Modules.Games.Services
_strings = strings;
}
public async Task<bool?> StartPoll(ITextChannel channel, IUserMessage msg, string arg)
public async Task<bool?> StartPoll(ulong guildId, IUserMessage msg, string arg)
{
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return null;
@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Games.Services
return null;
var poll = new Poll(_client, _strings, msg, data[0], data.Skip(1));
if (ActivePolls.TryAdd(channel.Guild.Id, poll))
if (ActivePolls.TryAdd(guildId, poll))
{
poll.OnEnded += (gid) =>
{

View File

@ -24,13 +24,17 @@ namespace NadekoBot.Modules.Utility
private readonly IStatsService _stats;
private readonly IBotCredentials _creds;
private readonly NadekoBot _bot;
private readonly DbService _db;
public Utility(NadekoBot nadeko, DiscordSocketClient client, IStatsService stats, IBotCredentials creds)
public Utility(NadekoBot nadeko, DiscordSocketClient client,
IStatsService stats, IBotCredentials creds,
DbService db)
{
_client = client;
_stats = stats;
_creds = creds;
_bot = nadeko;
_db = db;
}
[NadekoCommand, Usage, Description, Aliases]

View File

@ -1,8 +1,4 @@
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Core.Services

View File

@ -885,7 +885,8 @@
"customreactions_edit_fail": "Custom reaction with that ID does not exist.",
"searches_streaming": "Streaming",
"searches_followers": "Followers",
"gambling_rafflecur_joined": "User {0} joined the raffle!",
"gambling_rafflecur_already_joined": "You have already joined this raffle.",
"gambling_rafflecur": "{0} Currency Raffle",
"gambling_rafflecur_joined": "User {0} joined the raffle",
"gambling_rafflecur_already_joined": "You have already joined this raffle or the value you used is not valid.",
"gambling_rafflecur_ended": "{0} raffle ended. {1} won {2}!"
}

View File

@ -3040,9 +3040,10 @@
},
"rafflecur": {
"cmd": "rafflecur",
"desc": "Starts a currency raffle with a specified amount. Users who join the raffle will lose the amount of currency specified and add it to the pot. After 30 seconds, random winner will be selected who will receive the whole pot.",
"desc": "Starts or joins a currency raffle with a specified amount. Users who join the raffle will lose the amount of currency specified and add it to the pot. After 30 seconds, random winner will be selected who will receive the whole pot. There is also a `mixed` mode in which the users will be able to join the game with any amount of currency, and have their chances be proportional to the amount they've bet.",
"usage": [
"{0}rafflecur 20"
"{0}rafflecur 20",
"{0}rafflecur mixed 15"
]
}
}