.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 Discord;
using NadekoBot.Common; using NadekoBot.Common;
using NadekoBot.Core.Services; using NLog;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Core.Modules.Gambling.Common namespace NadekoBot.Core.Modules.Gambling.Common
{ {
public class CurrencyRaffleGame public class CurrencyRaffleGame
{ {
private readonly HashSet<IUser> _users = new HashSet<IUser>(); public enum Type {
public IEnumerable<IUser> Users => _users; Mixed,
private readonly int _amount; Normal
public CurrencyRaffleGame(int amount)
{
if (amount < 1)
throw new ArgumentOutOfRangeException();
_amount = amount;
} }
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; return false;
if (!_users.Add(new User
{
DiscordUser = usr,
Amount = amount,
}))
{
return false;
}
return true; 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 NadekoBot.Core.Modules.Gambling.Services;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord; using Discord;
using System;
using NadekoBot.Core.Services; using NadekoBot.Core.Services;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Linq;
using Discord.Commands;
namespace NadekoBot.Modules.Gambling namespace NadekoBot.Modules.Gambling
{ {
@ -18,28 +19,42 @@ namespace NadekoBot.Modules.Gambling
{ {
_bc = bc; _bc = bc;
} }
public enum Mixed { Mixed }
[NadekoCommand, Usage, Description, Aliases] [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) 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, var res = await _service.JoinOrCreateGame(Context.Channel.Id,
Context.User, amount, OnEnded) Context.User, amount, mixed, OnEnded)
.ConfigureAwait(false); .ConfigureAwait(false);
if (res.Item1 != null) if (res.Item1 != null)
{ {
await Context.Channel.SendConfirmAsync(GetText("rafflecur_joined", Context.User.ToString()), await Context.Channel.SendConfirmAsync(GetText("rafflecur", res.Item1.GameType.ToString()),
string.Join("\n", res.Item1.Users)).ConfigureAwait(false); string.Join("\n", res.Item1.Users.Select(x => $"{x.DiscordUser} ({x.Amount})")),
footer: GetText("rafflecur_joined", Context.User.ToString())).ConfigureAwait(false);
} }
else else
{ {
if (res.Item2 == CurrencyRaffleService.JoinErrorType.AlreadyJoined) if (res.Item2 == CurrencyRaffleService.JoinErrorType.AlreadyJoinedOrInvalidAmount)
await ReplyErrorLocalized("rafflecur_already_joined").ConfigureAwait(false); await ReplyErrorLocalized("rafflecur_already_joined").ConfigureAwait(false);
else if (res.Item2 == CurrencyRaffleService.JoinErrorType.NotEnoughCurrency) 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 NadekoBot.Core.Modules.Gambling.Common;
using System.Threading; using System.Threading;
using System.Linq; using System.Linq;
using NadekoBot.Common;
using System.Collections.Generic; using System.Collections.Generic;
using Discord; using Discord;
using System; using System;
@ -12,9 +11,10 @@ namespace NadekoBot.Core.Modules.Gambling.Services
{ {
public class CurrencyRaffleService : INService public class CurrencyRaffleService : INService
{ {
public enum JoinErrorType { public enum JoinErrorType
{
NotEnoughCurrency, NotEnoughCurrency,
AlreadyJoined AlreadyJoinedOrInvalidAmount
} }
private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1);
private readonly DbService _db; private readonly DbService _db;
@ -28,7 +28,7 @@ namespace NadekoBot.Core.Modules.Gambling.Services
_cs = cs; _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); await _locker.WaitAsync().ConfigureAwait(false);
try try
@ -37,7 +37,10 @@ namespace NadekoBot.Core.Modules.Gambling.Services
if (!Games.TryGetValue(channelId, out var crg)) if (!Games.TryGetValue(channelId, out var crg))
{ {
newGame = true; 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) using (var uow = _db.UnitOfWork)
{ {
@ -50,35 +53,38 @@ namespace NadekoBot.Core.Modules.Gambling.Services
return (null, JoinErrorType.NotEnoughCurrency); 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); await _cs.AddAsync(user.Id, "Curency Raffle Refund", amount, uow).ConfigureAwait(false);
return (null, JoinErrorType.AlreadyJoined); return (null, JoinErrorType.AlreadyJoinedOrInvalidAmount);
} }
uow.Complete();
} }
if (newGame) if (newGame)
{ {
var _t = new Timer(async state => var _t = Task.Run(async () =>
{ {
await Task.Delay(30000).ConfigureAwait(false);
await _locker.WaitAsync().ConfigureAwait(false); await _locker.WaitAsync().ConfigureAwait(false);
try try
{ {
var users = crg.Users.ToArray(); var winner = crg.GetWinner();
var rng = new NadekoRandom(); var won = crg.Users.Sum(x => x.Amount);
var usr = users[rng.Next(0, users.Length)];
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
await _cs.AddAsync(usr.Id, "Currency Raffle Win", await _cs.AddAsync(winner.DiscordUser.Id, "Currency Raffle Win",
amount * users.Length, uow); won, uow);
uow.Complete();
} }
Games.Remove(channelId, out _); Games.Remove(channelId, out _);
var oe = onEnded(usr, users.Length * amount); var oe = onEnded(winner.DiscordUser, won);
} }
catch { } catch { }
finally { _locker.Release(); } finally { _locker.Release(); }
});
}, null, 30000, Timeout.Infinite);
} }
return (crg, null); return (crg, null);
} }

View File

@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Games
private async Task InternalStartPoll(string arg) 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); await ReplyErrorLocalized("poll_already_running").ConfigureAwait(false);
} }
@ -54,7 +54,5 @@ namespace NadekoBot.Modules.Games
await poll.StopPoll().ConfigureAwait(false); await poll.StopPoll().ConfigureAwait(false);
} }
} }
} }
} }

View File

@ -26,7 +26,7 @@ namespace NadekoBot.Modules.Games.Services
_strings = strings; _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(";")) if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return null; return null;
@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Games.Services
return null; return null;
var poll = new Poll(_client, _strings, msg, data[0], data.Skip(1)); 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) => poll.OnEnded += (gid) =>
{ {

View File

@ -24,13 +24,17 @@ namespace NadekoBot.Modules.Utility
private readonly IStatsService _stats; private readonly IStatsService _stats;
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;
private readonly NadekoBot _bot; 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; _client = client;
_stats = stats; _stats = stats;
_creds = creds; _creds = creds;
_bot = nadeko; _bot = nadeko;
_db = db;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

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

View File

@ -885,7 +885,8 @@
"customreactions_edit_fail": "Custom reaction with that ID does not exist.", "customreactions_edit_fail": "Custom reaction with that ID does not exist.",
"searches_streaming": "Streaming", "searches_streaming": "Streaming",
"searches_followers": "Followers", "searches_followers": "Followers",
"gambling_rafflecur_joined": "User {0} joined the raffle!", "gambling_rafflecur": "{0} Currency Raffle",
"gambling_rafflecur_already_joined": "You have already joined this 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}!" "gambling_rafflecur_ended": "{0} raffle ended. {1} won {2}!"
} }

View File

@ -3040,9 +3040,10 @@
}, },
"rafflecur": { "rafflecur": {
"cmd": "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": [ "usage": [
"{0}rafflecur 20" "{0}rafflecur 20",
"{0}rafflecur mixed 15"
] ]
} }
} }