mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 17:58:26 -04:00
Global usings and file scoped namespaces
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services;
|
||||
using System.Collections.Concurrent;
|
||||
using NadekoBot.Modules.Gambling.Common.AnimalRacing;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class AnimalRaceService : INService
|
||||
{
|
||||
public class AnimalRaceService : INService
|
||||
{
|
||||
public ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
|
||||
}
|
||||
}
|
||||
public ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
|
||||
}
|
@@ -2,10 +2,9 @@
|
||||
using NadekoBot.Services;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class BlackJackService : INService
|
||||
{
|
||||
public class BlackJackService : INService
|
||||
{
|
||||
public ConcurrentDictionary<ulong, Blackjack> Games { get; } = new ConcurrentDictionary<ulong, Blackjack>();
|
||||
}
|
||||
}
|
||||
public ConcurrentDictionary<ulong, Blackjack> Games { get; } = new ConcurrentDictionary<ulong, Blackjack>();
|
||||
}
|
@@ -2,83 +2,77 @@
|
||||
using NadekoBot.Modules.Gambling.Common.Events;
|
||||
using System.Collections.Concurrent;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Net.Http;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class CurrencyEventsService : INService
|
||||
{
|
||||
public class CurrencyEventsService : INService
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly GamblingConfigService _configService;
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, ICurrencyEvent> _events =
|
||||
new ConcurrentDictionary<ulong, ICurrencyEvent>();
|
||||
|
||||
|
||||
public CurrencyEventsService(
|
||||
DiscordSocketClient client,
|
||||
ICurrencyService cs,
|
||||
GamblingConfigService configService)
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly GamblingConfigService _configService;
|
||||
_client = client;
|
||||
_cs = cs;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, ICurrencyEvent> _events =
|
||||
new ConcurrentDictionary<ulong, ICurrencyEvent>();
|
||||
public async Task<bool> TryCreateEventAsync(ulong guildId, ulong channelId, CurrencyEvent.Type type,
|
||||
EventOptions opts, Func<CurrencyEvent.Type, EventOptions, long, IEmbedBuilder> embed)
|
||||
{
|
||||
SocketGuild g = _client.GetGuild(guildId);
|
||||
SocketTextChannel ch = g?.GetChannel(channelId) as SocketTextChannel;
|
||||
if (ch is null)
|
||||
return false;
|
||||
|
||||
ICurrencyEvent ce;
|
||||
|
||||
public CurrencyEventsService(
|
||||
DiscordSocketClient client,
|
||||
ICurrencyService cs,
|
||||
GamblingConfigService configService)
|
||||
if (type == CurrencyEvent.Type.Reaction)
|
||||
{
|
||||
_client = client;
|
||||
_cs = cs;
|
||||
_configService = configService;
|
||||
ce = new ReactionEvent(_client, _cs, g, ch, opts, _configService.Data, embed);
|
||||
}
|
||||
else if (type == CurrencyEvent.Type.GameStatus)
|
||||
{
|
||||
ce = new GameStatusEvent(_client, _cs, g, ch, opts, embed);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> TryCreateEventAsync(ulong guildId, ulong channelId, CurrencyEvent.Type type,
|
||||
EventOptions opts, Func<CurrencyEvent.Type, EventOptions, long, IEmbedBuilder> embed)
|
||||
var added = _events.TryAdd(guildId, ce);
|
||||
if (added)
|
||||
{
|
||||
SocketGuild g = _client.GetGuild(guildId);
|
||||
SocketTextChannel ch = g?.GetChannel(channelId) as SocketTextChannel;
|
||||
if (ch is null)
|
||||
return false;
|
||||
|
||||
ICurrencyEvent ce;
|
||||
|
||||
if (type == CurrencyEvent.Type.Reaction)
|
||||
try
|
||||
{
|
||||
ce = new ReactionEvent(_client, _cs, g, ch, opts, _configService.Data, embed);
|
||||
ce.OnEnded += OnEventEnded;
|
||||
await ce.StartEvent().ConfigureAwait(false);
|
||||
}
|
||||
else if (type == CurrencyEvent.Type.GameStatus)
|
||||
{
|
||||
ce = new GameStatusEvent(_client, _cs, g, ch, opts, embed);
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error starting event");
|
||||
_events.TryRemove(guildId, out ce);
|
||||
return false;
|
||||
}
|
||||
|
||||
var added = _events.TryAdd(guildId, ce);
|
||||
if (added)
|
||||
{
|
||||
try
|
||||
{
|
||||
ce.OnEnded += OnEventEnded;
|
||||
await ce.StartEvent().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error starting event");
|
||||
_events.TryRemove(guildId, out ce);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
private Task OnEventEnded(ulong gid)
|
||||
{
|
||||
_events.TryRemove(gid, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
private Task OnEventEnded(ulong gid)
|
||||
{
|
||||
_events.TryRemove(gid, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
@@ -2,87 +2,83 @@
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Discord;
|
||||
using System;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class CurrencyRaffleService : INService
|
||||
{
|
||||
public class CurrencyRaffleService : INService
|
||||
public enum JoinErrorType
|
||||
{
|
||||
public enum JoinErrorType
|
||||
{
|
||||
NotEnoughCurrency,
|
||||
AlreadyJoinedOrInvalidAmount
|
||||
}
|
||||
private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1);
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
NotEnoughCurrency,
|
||||
AlreadyJoinedOrInvalidAmount
|
||||
}
|
||||
private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1);
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
|
||||
public Dictionary<ulong, CurrencyRaffleGame> Games { get; } = new Dictionary<ulong, CurrencyRaffleGame>();
|
||||
public Dictionary<ulong, CurrencyRaffleGame> Games { get; } = new Dictionary<ulong, CurrencyRaffleGame>();
|
||||
|
||||
public CurrencyRaffleService(DbService db, ICurrencyService cs)
|
||||
{
|
||||
_db = db;
|
||||
_cs = cs;
|
||||
}
|
||||
public CurrencyRaffleService(DbService db, ICurrencyService cs)
|
||||
{
|
||||
_db = db;
|
||||
_cs = cs;
|
||||
}
|
||||
|
||||
public async Task<(CurrencyRaffleGame, JoinErrorType?)> JoinOrCreateGame(ulong channelId, IUser user, long amount, bool mixed, Func<IUser, long, Task> onEnded)
|
||||
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
|
||||
{
|
||||
await _locker.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
var newGame = false;
|
||||
if (!Games.TryGetValue(channelId, out var crg))
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
//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)
|
||||
{
|
||||
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);
|
||||
Games.Remove(channelId);
|
||||
return (null, JoinErrorType.NotEnoughCurrency);
|
||||
}
|
||||
|
||||
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
|
||||
if (!crg.AddUser(user, amount))
|
||||
{
|
||||
_locker.Release();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,85 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Configs;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
{
|
||||
public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
{
|
||||
public override string Name { get; } = "gambling";
|
||||
private const string FilePath = "data/gambling.yml";
|
||||
private static TypedKey<GamblingConfig> changeKey = new TypedKey<GamblingConfig>("config.gambling.updated");
|
||||
public override string Name { get; } = "gambling";
|
||||
private const string FilePath = "data/gambling.yml";
|
||||
private static TypedKey<GamblingConfig> changeKey = new TypedKey<GamblingConfig>("config.gambling.updated");
|
||||
|
||||
|
||||
public GamblingConfigService(IConfigSeria serializer, IPubSub pubSub)
|
||||
: base(FilePath, serializer, pubSub, changeKey)
|
||||
{
|
||||
AddParsedProp("currency.name", gs => gs.Currency.Name, ConfigParsers.String, ConfigPrinters.ToString);
|
||||
AddParsedProp("currency.sign", gs => gs.Currency.Sign, ConfigParsers.String, ConfigPrinters.ToString);
|
||||
public GamblingConfigService(IConfigSeria serializer, IPubSub pubSub)
|
||||
: base(FilePath, serializer, pubSub, changeKey)
|
||||
{
|
||||
AddParsedProp("currency.name", gs => gs.Currency.Name, ConfigParsers.String, ConfigPrinters.ToString);
|
||||
AddParsedProp("currency.sign", gs => gs.Currency.Sign, ConfigParsers.String, ConfigPrinters.ToString);
|
||||
|
||||
AddParsedProp("minbet", gs => gs.MinBet, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("maxbet", gs => gs.MaxBet, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("minbet", gs => gs.MinBet, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("maxbet", gs => gs.MaxBet, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
|
||||
AddParsedProp("gen.min", gs => gs.Generation.MinAmount, int.TryParse, ConfigPrinters.ToString, val => val >= 1);
|
||||
AddParsedProp("gen.max", gs => gs.Generation.MaxAmount, int.TryParse, ConfigPrinters.ToString, val => val >= 1);
|
||||
AddParsedProp("gen.cd", gs => gs.Generation.GenCooldown, int.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("gen.chance", gs => gs.Generation.Chance, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0 && val <= 1);
|
||||
AddParsedProp("gen.has_pw", gs => gs.Generation.HasPassword, bool.TryParse, ConfigPrinters.ToString);
|
||||
AddParsedProp("bf.multi", gs => gs.BetFlip.Multiplier, decimal.TryParse, ConfigPrinters.ToString, val => val >= 1);
|
||||
AddParsedProp("waifu.min_price", gs => gs.Waifu.MinPrice, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.reset", gs => gs.Waifu.Multipliers.WaifuReset, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.crush_claim", gs => gs.Waifu.Multipliers.CrushClaim, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.normal_claim", gs => gs.Waifu.Multipliers.NormalClaim, decimal.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("waifu.multi.divorce_value", gs => gs.Waifu.Multipliers.DivorceNewValue, decimal.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("waifu.multi.all_gifts", gs => gs.Waifu.Multipliers.AllGiftPrices, decimal.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("waifu.multi.gift_effect", gs => gs.Waifu.Multipliers.GiftEffect, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.negative_gift_effect", gs => gs.Waifu.Multipliers.NegativeGiftEffect, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("decay.percent", gs => gs.Decay.Percent, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0 && val <= 1);
|
||||
AddParsedProp("decay.maxdecay", gs => gs.Decay.MaxDecay, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("decay.threshold", gs => gs.Decay.MinThreshold, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("gen.min", gs => gs.Generation.MinAmount, int.TryParse, ConfigPrinters.ToString, val => val >= 1);
|
||||
AddParsedProp("gen.max", gs => gs.Generation.MaxAmount, int.TryParse, ConfigPrinters.ToString, val => val >= 1);
|
||||
AddParsedProp("gen.cd", gs => gs.Generation.GenCooldown, int.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("gen.chance", gs => gs.Generation.Chance, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0 && val <= 1);
|
||||
AddParsedProp("gen.has_pw", gs => gs.Generation.HasPassword, bool.TryParse, ConfigPrinters.ToString);
|
||||
AddParsedProp("bf.multi", gs => gs.BetFlip.Multiplier, decimal.TryParse, ConfigPrinters.ToString, val => val >= 1);
|
||||
AddParsedProp("waifu.min_price", gs => gs.Waifu.MinPrice, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.reset", gs => gs.Waifu.Multipliers.WaifuReset, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.crush_claim", gs => gs.Waifu.Multipliers.CrushClaim, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.normal_claim", gs => gs.Waifu.Multipliers.NormalClaim, decimal.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("waifu.multi.divorce_value", gs => gs.Waifu.Multipliers.DivorceNewValue, decimal.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("waifu.multi.all_gifts", gs => gs.Waifu.Multipliers.AllGiftPrices, decimal.TryParse, ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("waifu.multi.gift_effect", gs => gs.Waifu.Multipliers.GiftEffect, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("waifu.multi.negative_gift_effect", gs => gs.Waifu.Multipliers.NegativeGiftEffect, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("decay.percent", gs => gs.Decay.Percent, decimal.TryParse, ConfigPrinters.ToString, val => val >= 0 && val <= 1);
|
||||
AddParsedProp("decay.maxdecay", gs => gs.Decay.MaxDecay, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("decay.threshold", gs => gs.Decay.MinThreshold, int.TryParse, ConfigPrinters.ToString, val => val >= 0);
|
||||
|
||||
Migrate();
|
||||
Migrate();
|
||||
}
|
||||
|
||||
private readonly IEnumerable<WaifuItemModel> antiGiftSeed = new[]
|
||||
{
|
||||
new WaifuItemModel("🥀", 100, "WiltedRose", true),
|
||||
new WaifuItemModel("✂️", 1000, "Haircut", true),
|
||||
new WaifuItemModel("🧻", 10000, "ToiletPaper", true),
|
||||
};
|
||||
|
||||
public void Migrate()
|
||||
{
|
||||
if (_data.Version < 2)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Waifu.Items = c.Waifu.Items.Concat(antiGiftSeed).ToList();
|
||||
c.Version = 2;
|
||||
});
|
||||
}
|
||||
|
||||
private readonly IEnumerable<WaifuItemModel> antiGiftSeed = new[]
|
||||
if (_data.Version < 3)
|
||||
{
|
||||
new WaifuItemModel("🥀", 100, "WiltedRose", true),
|
||||
new WaifuItemModel("✂️", 1000, "Haircut", true),
|
||||
new WaifuItemModel("🧻", 10000, "ToiletPaper", true),
|
||||
};
|
||||
|
||||
public void Migrate()
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 3;
|
||||
c.VoteReward = 100;
|
||||
});
|
||||
}
|
||||
|
||||
if (_data.Version < 4)
|
||||
{
|
||||
if (_data.Version < 2)
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Waifu.Items = c.Waifu.Items.Concat(antiGiftSeed).ToList();
|
||||
c.Version = 2;
|
||||
});
|
||||
}
|
||||
|
||||
if (_data.Version < 3)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 3;
|
||||
c.VoteReward = 100;
|
||||
});
|
||||
}
|
||||
|
||||
if (_data.Version < 4)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 4;
|
||||
});
|
||||
}
|
||||
c.Version = 4;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,69 +4,63 @@ using NadekoBot.Services;
|
||||
using NadekoBot.Modules.Gambling.Common.Connect4;
|
||||
using NadekoBot.Modules.Gambling.Common.WheelOfFortune;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Gambling.Common.Slot;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class GamblingService : INService
|
||||
{
|
||||
public class GamblingService : INService
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly Bot _bot;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly GamblingConfigService _gss;
|
||||
|
||||
public ConcurrentDictionary<(ulong, ulong), RollDuelGame> Duels { get; } = new ConcurrentDictionary<(ulong, ulong), RollDuelGame>();
|
||||
public ConcurrentDictionary<ulong, Connect4Game> Connect4Games { get; } = new ConcurrentDictionary<ulong, Connect4Game>();
|
||||
|
||||
private readonly Timer _decayTimer;
|
||||
|
||||
public GamblingService(DbService db, Bot bot, ICurrencyService cs,
|
||||
DiscordSocketClient client, IDataCache cache, GamblingConfigService gss)
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly Bot _bot;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly GamblingConfigService _gss;
|
||||
|
||||
public ConcurrentDictionary<(ulong, ulong), RollDuelGame> Duels { get; } = new ConcurrentDictionary<(ulong, ulong), RollDuelGame>();
|
||||
public ConcurrentDictionary<ulong, Connect4Game> Connect4Games { get; } = new ConcurrentDictionary<ulong, Connect4Game>();
|
||||
|
||||
private readonly Timer _decayTimer;
|
||||
|
||||
public GamblingService(DbService db, Bot bot, ICurrencyService cs,
|
||||
DiscordSocketClient client, IDataCache cache, GamblingConfigService gss)
|
||||
{
|
||||
_db = db;
|
||||
_cs = cs;
|
||||
_bot = bot;
|
||||
_client = client;
|
||||
_cache = cache;
|
||||
_gss = gss;
|
||||
_db = db;
|
||||
_cs = cs;
|
||||
_bot = bot;
|
||||
_client = client;
|
||||
_cache = cache;
|
||||
_gss = gss;
|
||||
|
||||
if (_bot.Client.ShardId == 0)
|
||||
if (_bot.Client.ShardId == 0)
|
||||
{
|
||||
_decayTimer = new Timer(_ =>
|
||||
{
|
||||
_decayTimer = new Timer(_ =>
|
||||
{
|
||||
var config = _gss.Data;
|
||||
var maxDecay = config.Decay.MaxDecay;
|
||||
if (config.Decay.Percent <= 0 || config.Decay.Percent > 1 || maxDecay < 0)
|
||||
return;
|
||||
var config = _gss.Data;
|
||||
var maxDecay = config.Decay.MaxDecay;
|
||||
if (config.Decay.Percent <= 0 || config.Decay.Percent > 1 || maxDecay < 0)
|
||||
return;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var lastCurrencyDecay = _cache.GetLastCurrencyDecay();
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var lastCurrencyDecay = _cache.GetLastCurrencyDecay();
|
||||
|
||||
if (DateTime.UtcNow - lastCurrencyDecay < TimeSpan.FromHours(config.Decay.HourInterval))
|
||||
return;
|
||||
if (DateTime.UtcNow - lastCurrencyDecay < TimeSpan.FromHours(config.Decay.HourInterval))
|
||||
return;
|
||||
|
||||
Log.Information($"Decaying users' currency - decay: {config.Decay.Percent * 100}% " +
|
||||
$"| max: {maxDecay} " +
|
||||
$"| threshold: {config.Decay.MinThreshold}");
|
||||
Log.Information($"Decaying users' currency - decay: {config.Decay.Percent * 100}% " +
|
||||
$"| max: {maxDecay} " +
|
||||
$"| threshold: {config.Decay.MinThreshold}");
|
||||
|
||||
if (maxDecay == 0)
|
||||
maxDecay = int.MaxValue;
|
||||
if (maxDecay == 0)
|
||||
maxDecay = int.MaxValue;
|
||||
|
||||
uow.Database.ExecuteSqlInterpolated($@"
|
||||
uow.Database.ExecuteSqlInterpolated($@"
|
||||
UPDATE DiscordUser
|
||||
SET CurrencyAmount=
|
||||
CASE WHEN
|
||||
@@ -78,99 +72,98 @@ SET CurrencyAmount=
|
||||
END
|
||||
WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentUser.Id};");
|
||||
|
||||
_cache.SetLastCurrencyDecay();
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SlotResponse> SlotAsync(ulong userId, long amount)
|
||||
{
|
||||
var takeRes = await _cs.RemoveAsync(userId, "Slot Machine", amount, true);
|
||||
|
||||
if (!takeRes)
|
||||
{
|
||||
return new SlotResponse
|
||||
{
|
||||
Error = GamblingError.NotEnough
|
||||
};
|
||||
}
|
||||
|
||||
var game = new SlotGame();
|
||||
var result = game.Spin();
|
||||
long won = 0;
|
||||
|
||||
if (result.Multiplier > 0)
|
||||
{
|
||||
won = (long)(result.Multiplier * amount);
|
||||
|
||||
await _cs.AddAsync(userId, $"Slot Machine x{result.Multiplier}", won, true);
|
||||
}
|
||||
|
||||
var toReturn = new SlotResponse
|
||||
{
|
||||
Multiplier = result.Multiplier,
|
||||
Won = won,
|
||||
};
|
||||
|
||||
toReturn.Rolls.AddRange(result.Rolls);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
|
||||
public struct EconomyResult
|
||||
{
|
||||
public decimal Cash { get; set; }
|
||||
public decimal Planted { get; set; }
|
||||
public decimal Waifus { get; set; }
|
||||
public decimal OnePercent { get; set; }
|
||||
public long Bot { get; set; }
|
||||
}
|
||||
|
||||
public EconomyResult GetEconomy()
|
||||
{
|
||||
if (_cache.TryGetEconomy(out var data))
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<EconomyResult>(data);
|
||||
_cache.SetLastCurrencyDecay();
|
||||
uow.SaveChanges();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
decimal cash;
|
||||
decimal onePercent;
|
||||
decimal planted;
|
||||
decimal waifus;
|
||||
long bot;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
cash = uow.DiscordUser.GetTotalCurrency();
|
||||
onePercent = uow.DiscordUser.GetTopOnePercentCurrency(_client.CurrentUser.Id);
|
||||
planted = uow.PlantedCurrency.AsQueryable().Sum(x => x.Amount);
|
||||
waifus = uow.WaifuInfo.GetTotalValue();
|
||||
bot = uow.DiscordUser.GetUserCurrency(_client.CurrentUser.Id);
|
||||
}
|
||||
|
||||
var result = new EconomyResult
|
||||
{
|
||||
Cash = cash,
|
||||
Planted = planted,
|
||||
Bot = bot,
|
||||
Waifus = waifus,
|
||||
OnePercent = onePercent,
|
||||
};
|
||||
|
||||
_cache.SetEconomy(JsonConvert.SerializeObject(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<WheelOfFortuneGame.Result> WheelOfFortuneSpinAsync(ulong userId, long bet)
|
||||
{
|
||||
return new WheelOfFortuneGame(userId, bet, _gss.Data, _cs).SpinAsync();
|
||||
}, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SlotResponse> SlotAsync(ulong userId, long amount)
|
||||
{
|
||||
var takeRes = await _cs.RemoveAsync(userId, "Slot Machine", amount, true);
|
||||
|
||||
if (!takeRes)
|
||||
{
|
||||
return new SlotResponse
|
||||
{
|
||||
Error = GamblingError.NotEnough
|
||||
};
|
||||
}
|
||||
|
||||
var game = new SlotGame();
|
||||
var result = game.Spin();
|
||||
long won = 0;
|
||||
|
||||
if (result.Multiplier > 0)
|
||||
{
|
||||
won = (long)(result.Multiplier * amount);
|
||||
|
||||
await _cs.AddAsync(userId, $"Slot Machine x{result.Multiplier}", won, true);
|
||||
}
|
||||
|
||||
var toReturn = new SlotResponse
|
||||
{
|
||||
Multiplier = result.Multiplier,
|
||||
Won = won,
|
||||
};
|
||||
|
||||
toReturn.Rolls.AddRange(result.Rolls);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
|
||||
public struct EconomyResult
|
||||
{
|
||||
public decimal Cash { get; set; }
|
||||
public decimal Planted { get; set; }
|
||||
public decimal Waifus { get; set; }
|
||||
public decimal OnePercent { get; set; }
|
||||
public long Bot { get; set; }
|
||||
}
|
||||
|
||||
public EconomyResult GetEconomy()
|
||||
{
|
||||
if (_cache.TryGetEconomy(out var data))
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<EconomyResult>(data);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
decimal cash;
|
||||
decimal onePercent;
|
||||
decimal planted;
|
||||
decimal waifus;
|
||||
long bot;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
cash = uow.DiscordUser.GetTotalCurrency();
|
||||
onePercent = uow.DiscordUser.GetTopOnePercentCurrency(_client.CurrentUser.Id);
|
||||
planted = uow.PlantedCurrency.AsQueryable().Sum(x => x.Amount);
|
||||
waifus = uow.WaifuInfo.GetTotalValue();
|
||||
bot = uow.DiscordUser.GetUserCurrency(_client.CurrentUser.Id);
|
||||
}
|
||||
|
||||
var result = new EconomyResult
|
||||
{
|
||||
Cash = cash,
|
||||
Planted = planted,
|
||||
Bot = bot,
|
||||
Waifus = waifus,
|
||||
OnePercent = onePercent,
|
||||
};
|
||||
|
||||
_cache.SetEconomy(JsonConvert.SerializeObject(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<WheelOfFortuneGame.Result> WheelOfFortuneSpinAsync(ulong userId, long bet)
|
||||
{
|
||||
return new WheelOfFortuneGame(userId, bet, _gss.Data, _cs).SpinAsync();
|
||||
}
|
||||
}
|
@@ -1,43 +1,42 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public interface IShopService
|
||||
{
|
||||
public interface IShopService
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes the price of a shop item
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="index">Index of the item</param>
|
||||
/// <param name="newPrice">New item price</param>
|
||||
/// <returns>Success status</returns>
|
||||
Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice);
|
||||
/// <summary>
|
||||
/// Changes the price of a shop item
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="index">Index of the item</param>
|
||||
/// <param name="newPrice">New item price</param>
|
||||
/// <returns>Success status</returns>
|
||||
Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the name of a shop item
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="index">Index of the item</param>
|
||||
/// <param name="newName">New item name</param>
|
||||
/// <returns>Success status</returns>
|
||||
Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName);
|
||||
/// <summary>
|
||||
/// Changes the name of a shop item
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="index">Index of the item</param>
|
||||
/// <param name="newName">New item name</param>
|
||||
/// <returns>Success status</returns>
|
||||
Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName);
|
||||
|
||||
/// <summary>
|
||||
/// Swaps indexes of 2 items in the shop
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="index1">First entry's index</param>
|
||||
/// <param name="index2">Second entry's index</param>
|
||||
/// <returns>Whether swap was successful</returns>
|
||||
Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2);
|
||||
/// <summary>
|
||||
/// Swaps indexes of 2 items in the shop
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="index1">First entry's index</param>
|
||||
/// <param name="index2">Second entry's index</param>
|
||||
/// <returns>Whether swap was successful</returns>
|
||||
Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2);
|
||||
|
||||
/// <summary>
|
||||
/// Swaps indexes of 2 items in the shop
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="fromIndex">Current index of the entry to move</param>
|
||||
/// <param name="toIndex">Destination index of the entry</param>
|
||||
/// <returns>Whether swap was successful</returns>
|
||||
Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex);
|
||||
}
|
||||
/// <summary>
|
||||
/// Swaps indexes of 2 items in the shop
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild in which the shop is</param>
|
||||
/// <param name="fromIndex">Current index of the entry to move</param>
|
||||
/// <param name="toIndex">Destination index of the entry</param>
|
||||
/// <returns>Whether swap was successful</returns>
|
||||
Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex);
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Services;
|
||||
@@ -7,102 +6,100 @@ using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class ShopService : IShopService, INService
|
||||
{
|
||||
public class ShopService : IShopService, INService
|
||||
private readonly DbService _db;
|
||||
|
||||
public ShopService(DbService db)
|
||||
{
|
||||
private readonly DbService _db;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public ShopService(DbService db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
private IndexedCollection<ShopEntry> GetEntriesInternal(NadekoContext uow, ulong guildId) =>
|
||||
uow.GuildConfigsForId(
|
||||
guildId,
|
||||
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)
|
||||
)
|
||||
.ShopEntries
|
||||
.ToIndexed();
|
||||
|
||||
private IndexedCollection<ShopEntry> GetEntriesInternal(NadekoContext uow, ulong guildId) =>
|
||||
uow.GuildConfigsForId(
|
||||
guildId,
|
||||
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)
|
||||
)
|
||||
.ShopEntries
|
||||
.ToIndexed();
|
||||
public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice)
|
||||
{
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (newPrice <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(newPrice));
|
||||
|
||||
public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice)
|
||||
{
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (newPrice <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(newPrice));
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
if (index >= entries.Count)
|
||||
return false;
|
||||
|
||||
if (index >= entries.Count)
|
||||
return false;
|
||||
entries[index].Price = newPrice;
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
entries[index].Price = newPrice;
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName)
|
||||
{
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (string.IsNullOrWhiteSpace(newName))
|
||||
throw new ArgumentNullException(nameof(newName));
|
||||
|
||||
public async Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName)
|
||||
{
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (string.IsNullOrWhiteSpace(newName))
|
||||
throw new ArgumentNullException(nameof(newName));
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
if (index >= entries.Count)
|
||||
return false;
|
||||
|
||||
if (index >= entries.Count)
|
||||
return false;
|
||||
entries[index].Name = newName.TrimTo(100);
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
entries[index].Name = newName.TrimTo(100);
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2)
|
||||
{
|
||||
if (index1 < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index1));
|
||||
if (index2 < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index2));
|
||||
|
||||
public async Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2)
|
||||
{
|
||||
if (index1 < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index1));
|
||||
if (index2 < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index2));
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
if (index1 >= entries.Count || index2 >= entries.Count || index1 == index2)
|
||||
return false;
|
||||
|
||||
if (index1 >= entries.Count || index2 >= entries.Count || index1 == index2)
|
||||
return false;
|
||||
entries[index1].Index = index2;
|
||||
entries[index2].Index = index1;
|
||||
|
||||
entries[index1].Index = index2;
|
||||
entries[index2].Index = index1;
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex)
|
||||
{
|
||||
if (fromIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(fromIndex));
|
||||
if (toIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(toIndex));
|
||||
|
||||
public async Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex)
|
||||
{
|
||||
if (fromIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(fromIndex));
|
||||
if (toIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(toIndex));
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var entries = GetEntriesInternal(uow, guildId);
|
||||
if (fromIndex >= entries.Count || toIndex >= entries.Count || fromIndex == toIndex)
|
||||
return false;
|
||||
|
||||
if (fromIndex >= entries.Count || toIndex >= entries.Count || fromIndex == toIndex)
|
||||
return false;
|
||||
var entry = entries[fromIndex];
|
||||
entries.RemoveAt(fromIndex);
|
||||
entries.Insert(toIndex, entry);
|
||||
|
||||
var entry = entries[fromIndex];
|
||||
entries.RemoveAt(fromIndex);
|
||||
entries.Insert(toIndex, entry);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
await uow.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -11,373 +11,369 @@ using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Db;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
using Color = SixLabors.ImageSharp.Color;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class PlantPickService : INService
|
||||
{
|
||||
public class PlantPickService : INService
|
||||
private readonly DbService _db;
|
||||
private readonly IBotStrings _strings;
|
||||
private readonly IImageCache _images;
|
||||
private readonly FontProvider _fonts;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly CommandHandler _cmdHandler;
|
||||
private readonly NadekoRandom _rng;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly GamblingConfigService _gss;
|
||||
|
||||
public readonly ConcurrentHashSet<ulong> _generationChannels = new ConcurrentHashSet<ulong>();
|
||||
//channelId/last generation
|
||||
public ConcurrentDictionary<ulong, DateTime> LastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
private readonly SemaphoreSlim pickLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
public PlantPickService(DbService db, CommandHandler cmd, IBotStrings strings,
|
||||
IDataCache cache, FontProvider fonts, ICurrencyService cs,
|
||||
CommandHandler cmdHandler, DiscordSocketClient client, GamblingConfigService gss)
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly IBotStrings _strings;
|
||||
private readonly IImageCache _images;
|
||||
private readonly FontProvider _fonts;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly CommandHandler _cmdHandler;
|
||||
private readonly NadekoRandom _rng;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly GamblingConfigService _gss;
|
||||
_db = db;
|
||||
_strings = strings;
|
||||
_images = cache.LocalImages;
|
||||
_fonts = fonts;
|
||||
_cs = cs;
|
||||
_cmdHandler = cmdHandler;
|
||||
_rng = new NadekoRandom();
|
||||
_client = client;
|
||||
_gss = gss;
|
||||
|
||||
public readonly ConcurrentHashSet<ulong> _generationChannels = new ConcurrentHashSet<ulong>();
|
||||
//channelId/last generation
|
||||
public ConcurrentDictionary<ulong, DateTime> LastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
private readonly SemaphoreSlim pickLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
public PlantPickService(DbService db, CommandHandler cmd, IBotStrings strings,
|
||||
IDataCache cache, FontProvider fonts, ICurrencyService cs,
|
||||
CommandHandler cmdHandler, DiscordSocketClient client, GamblingConfigService gss)
|
||||
cmd.OnMessageNoTrigger += PotentialFlowerGeneration;
|
||||
using (var uow = db.GetDbContext())
|
||||
{
|
||||
_db = db;
|
||||
_strings = strings;
|
||||
_images = cache.LocalImages;
|
||||
_fonts = fonts;
|
||||
_cs = cs;
|
||||
_cmdHandler = cmdHandler;
|
||||
_rng = new NadekoRandom();
|
||||
_client = client;
|
||||
_gss = gss;
|
||||
|
||||
cmd.OnMessageNoTrigger += PotentialFlowerGeneration;
|
||||
using (var uow = db.GetDbContext())
|
||||
{
|
||||
var guildIds = client.Guilds.Select(x => x.Id).ToList();
|
||||
var configs = uow.Set<GuildConfig>()
|
||||
.AsQueryable()
|
||||
.Include(x => x.GenerateCurrencyChannelIds)
|
||||
.Where(x => guildIds.Contains(x.GuildId))
|
||||
.ToList();
|
||||
var guildIds = client.Guilds.Select(x => x.Id).ToList();
|
||||
var configs = uow.Set<GuildConfig>()
|
||||
.AsQueryable()
|
||||
.Include(x => x.GenerateCurrencyChannelIds)
|
||||
.Where(x => guildIds.Contains(x.GuildId))
|
||||
.ToList();
|
||||
|
||||
_generationChannels = new ConcurrentHashSet<ulong>(configs
|
||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||
}
|
||||
_generationChannels = new ConcurrentHashSet<ulong>(configs
|
||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||
}
|
||||
}
|
||||
|
||||
private string GetText(ulong gid, LocStr str)
|
||||
=> _strings.GetText(str, gid);
|
||||
private string GetText(ulong gid, LocStr str)
|
||||
=> _strings.GetText(str, gid);
|
||||
|
||||
public bool ToggleCurrencyGeneration(ulong gid, ulong cid)
|
||||
public bool ToggleCurrencyGeneration(ulong gid, ulong cid)
|
||||
{
|
||||
bool enabled;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
bool enabled;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var guildConfig = uow.GuildConfigsForId(gid, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
|
||||
var guildConfig = uow.GuildConfigsForId(gid, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
|
||||
|
||||
var toAdd = new GCChannelId() { ChannelId = cid };
|
||||
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
|
||||
{
|
||||
guildConfig.GenerateCurrencyChannelIds.Add(toAdd);
|
||||
_generationChannels.Add(cid);
|
||||
enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var toDelete = guildConfig.GenerateCurrencyChannelIds.FirstOrDefault(x => x.Equals(toAdd));
|
||||
if (toDelete != null)
|
||||
{
|
||||
uow.Remove(toDelete);
|
||||
}
|
||||
_generationChannels.TryRemove(cid);
|
||||
enabled = false;
|
||||
}
|
||||
uow.SaveChanges();
|
||||
var toAdd = new GCChannelId() { ChannelId = cid };
|
||||
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
|
||||
{
|
||||
guildConfig.GenerateCurrencyChannelIds.Add(toAdd);
|
||||
_generationChannels.Add(cid);
|
||||
enabled = true;
|
||||
}
|
||||
return enabled;
|
||||
else
|
||||
{
|
||||
var toDelete = guildConfig.GenerateCurrencyChannelIds.FirstOrDefault(x => x.Equals(toAdd));
|
||||
if (toDelete != null)
|
||||
{
|
||||
uow.Remove(toDelete);
|
||||
}
|
||||
_generationChannels.TryRemove(cid);
|
||||
enabled = false;
|
||||
}
|
||||
uow.SaveChanges();
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public IEnumerable<GuildConfigExtensions.GeneratingChannel> GetAllGeneratingChannels()
|
||||
public IEnumerable<GuildConfigExtensions.GeneratingChannel> GetAllGeneratingChannels()
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var chs = uow.GuildConfigs.GetGeneratingChannels();
|
||||
return chs;
|
||||
}
|
||||
var chs = uow.GuildConfigs.GetGeneratingChannels();
|
||||
return chs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a random currency image stream, with an optional password sticked onto it.
|
||||
/// </summary>
|
||||
/// <param name="pass">Optional password to add to top left corner.</param>
|
||||
/// <returns>Stream of the currency image</returns>
|
||||
public Stream GetRandomCurrencyImage(string pass, out string extension)
|
||||
/// <summary>
|
||||
/// Get a random currency image stream, with an optional password sticked onto it.
|
||||
/// </summary>
|
||||
/// <param name="pass">Optional password to add to top left corner.</param>
|
||||
/// <returns>Stream of the currency image</returns>
|
||||
public Stream GetRandomCurrencyImage(string pass, out string extension)
|
||||
{
|
||||
// get a random currency image bytes
|
||||
var rng = new NadekoRandom();
|
||||
var curImg = _images.Currency[rng.Next(0, _images.Currency.Count)];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pass))
|
||||
{
|
||||
// get a random currency image bytes
|
||||
var rng = new NadekoRandom();
|
||||
var curImg = _images.Currency[rng.Next(0, _images.Currency.Count)];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pass))
|
||||
// determine the extension
|
||||
using (var img = Image.Load(curImg, out var format))
|
||||
{
|
||||
// determine the extension
|
||||
using (var img = Image.Load(curImg, out var format))
|
||||
{
|
||||
extension = format.FileExtensions.FirstOrDefault() ?? "png";
|
||||
}
|
||||
// return the image
|
||||
return curImg.ToStream();
|
||||
extension = format.FileExtensions.FirstOrDefault() ?? "png";
|
||||
}
|
||||
|
||||
// get the image stream and extension
|
||||
var (s, ext) = AddPassword(curImg, pass);
|
||||
// set the out extension parameter to the extension we've got
|
||||
extension = ext;
|
||||
// return the image
|
||||
return s;
|
||||
return curImg.ToStream();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a password to the image.
|
||||
/// </summary>
|
||||
/// <param name="curImg">Image to add password to.</param>
|
||||
/// <param name="pass">Password to add to top left corner.</param>
|
||||
/// <returns>Image with the password in the top left corner.</returns>
|
||||
private (Stream, string) AddPassword(byte[] curImg, string pass)
|
||||
// get the image stream and extension
|
||||
var (s, ext) = AddPassword(curImg, pass);
|
||||
// set the out extension parameter to the extension we've got
|
||||
extension = ext;
|
||||
// return the image
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a password to the image.
|
||||
/// </summary>
|
||||
/// <param name="curImg">Image to add password to.</param>
|
||||
/// <param name="pass">Password to add to top left corner.</param>
|
||||
/// <returns>Image with the password in the top left corner.</returns>
|
||||
private (Stream, string) AddPassword(byte[] curImg, string pass)
|
||||
{
|
||||
// draw lower, it looks better
|
||||
pass = pass.TrimTo(10, true).ToLowerInvariant();
|
||||
using (var img = Image.Load<Rgba32>(curImg, out var format))
|
||||
{
|
||||
// draw lower, it looks better
|
||||
pass = pass.TrimTo(10, true).ToLowerInvariant();
|
||||
using (var img = Image.Load<Rgba32>(curImg, out var format))
|
||||
// choose font size based on the image height, so that it's visible
|
||||
var font = _fonts.NotoSans.CreateFont(img.Height / 12, FontStyle.Bold);
|
||||
img.Mutate(x =>
|
||||
{
|
||||
// choose font size based on the image height, so that it's visible
|
||||
var font = _fonts.NotoSans.CreateFont(img.Height / 12, FontStyle.Bold);
|
||||
img.Mutate(x =>
|
||||
{
|
||||
// measure the size of the text to be drawing
|
||||
var size = TextMeasurer.Measure(pass, new RendererOptions(font, new PointF(0, 0)));
|
||||
// measure the size of the text to be drawing
|
||||
var size = TextMeasurer.Measure(pass, new RendererOptions(font, new PointF(0, 0)));
|
||||
|
||||
// fill the background with black, add 5 pixels on each side to make it look better
|
||||
x.FillPolygon(Color.ParseHex("00000080"),
|
||||
new PointF(0, 0),
|
||||
new PointF(size.Width + 5, 0),
|
||||
new PointF(size.Width + 5, size.Height + 10),
|
||||
new PointF(0, size.Height + 10));
|
||||
// fill the background with black, add 5 pixels on each side to make it look better
|
||||
x.FillPolygon(Color.ParseHex("00000080"),
|
||||
new PointF(0, 0),
|
||||
new PointF(size.Width + 5, 0),
|
||||
new PointF(size.Width + 5, size.Height + 10),
|
||||
new PointF(0, size.Height + 10));
|
||||
|
||||
// draw the password over the background
|
||||
x.DrawText(pass,
|
||||
font,
|
||||
SixLabors.ImageSharp.Color.White,
|
||||
new PointF(0, 0));
|
||||
});
|
||||
// return image as a stream for easy sending
|
||||
return (img.ToStream(format), format.FileExtensions.FirstOrDefault() ?? "png");
|
||||
}
|
||||
}
|
||||
|
||||
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
||||
{
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg is null || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!(imsg.Channel is ITextChannel channel))
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!_generationChannels.Contains(channel.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = _gss.Data;
|
||||
var lastGeneration = LastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
if (DateTime.UtcNow - TimeSpan.FromSeconds(config.Generation.GenCooldown) < lastGeneration) //recently generated in this channel, don't generate again
|
||||
return;
|
||||
|
||||
var num = rng.Next(1, 101) + config.Generation.Chance * 100;
|
||||
if (num > 100 && LastGenerations.TryUpdate(channel.Id, DateTime.UtcNow, lastGeneration))
|
||||
{
|
||||
var dropAmount = config.Generation.MinAmount;
|
||||
var dropAmountMax = config.Generation.MaxAmount;
|
||||
|
||||
if (dropAmountMax > dropAmount)
|
||||
dropAmount = new NadekoRandom().Next(dropAmount, dropAmountMax + 1);
|
||||
|
||||
if (dropAmount > 0)
|
||||
{
|
||||
var prefix = _cmdHandler.GetPrefix(channel.Guild.Id);
|
||||
var toSend = dropAmount == 1
|
||||
? GetText(channel.GuildId, strs.curgen_sn(config.Currency.Sign))
|
||||
+ " " + GetText(channel.GuildId, strs.pick_sn(prefix))
|
||||
: GetText(channel.GuildId, strs.curgen_pl(dropAmount, config.Currency.Sign))
|
||||
+ " " + GetText(channel.GuildId, strs.pick_pl(prefix));
|
||||
|
||||
var pw = config.Generation.HasPassword ? GenerateCurrencyPassword().ToUpperInvariant() : null;
|
||||
|
||||
IUserMessage sent;
|
||||
using (var stream = GetRandomCurrencyImage(pw, out var ext))
|
||||
{
|
||||
sent = await channel.SendFileAsync(stream, $"currency_image.{ext}", toSend).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await AddPlantToDatabase(channel.GuildId,
|
||||
channel.Id,
|
||||
_client.CurrentUser.Id,
|
||||
sent.Id,
|
||||
dropAmount,
|
||||
pw).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
// draw the password over the background
|
||||
x.DrawText(pass,
|
||||
font,
|
||||
SixLabors.ImageSharp.Color.White,
|
||||
new PointF(0, 0));
|
||||
});
|
||||
// return image as a stream for easy sending
|
||||
return (img.ToStream(format), format.FileExtensions.FirstOrDefault() ?? "png");
|
||||
}
|
||||
}
|
||||
|
||||
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
||||
{
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg is null || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a hexadecimal string from 1000 to ffff.
|
||||
/// </summary>
|
||||
/// <returns>A hexadecimal string from 1000 to ffff</returns>
|
||||
private string GenerateCurrencyPassword()
|
||||
{
|
||||
// generate a number from 1000 to ffff
|
||||
var num = _rng.Next(4096, 65536);
|
||||
// convert it to hexadecimal
|
||||
return num.ToString("x4");
|
||||
}
|
||||
if (!(imsg.Channel is ITextChannel channel))
|
||||
return Task.CompletedTask;
|
||||
|
||||
public async Task<long> PickAsync(ulong gid, ITextChannel ch, ulong uid, string pass)
|
||||
if (!_generationChannels.Contains(channel.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await pickLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
long amount;
|
||||
ulong[] ids;
|
||||
using (var uow = _db.GetDbContext())
|
||||
var config = _gss.Data;
|
||||
var lastGeneration = LastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
if (DateTime.UtcNow - TimeSpan.FromSeconds(config.Generation.GenCooldown) < lastGeneration) //recently generated in this channel, don't generate again
|
||||
return;
|
||||
|
||||
var num = rng.Next(1, 101) + config.Generation.Chance * 100;
|
||||
if (num > 100 && LastGenerations.TryUpdate(channel.Id, DateTime.UtcNow, lastGeneration))
|
||||
{
|
||||
// this method will sum all plants with that password,
|
||||
// remove them, and get messageids of the removed plants
|
||||
var dropAmount = config.Generation.MinAmount;
|
||||
var dropAmountMax = config.Generation.MaxAmount;
|
||||
|
||||
pass = pass?.Trim().TrimTo(10, hideDots: true).ToUpperInvariant();
|
||||
// gets all plants in this channel with the same password
|
||||
var entries = uow.PlantedCurrency
|
||||
.AsQueryable()
|
||||
.Where(x => x.ChannelId == ch.Id && pass == x.Password)
|
||||
.ToList();
|
||||
// sum how much currency that is, and get all of the message ids (so that i can delete them)
|
||||
amount = entries.Sum(x => x.Amount);
|
||||
ids = entries.Select(x => x.MessageId).ToArray();
|
||||
// remove them from the database
|
||||
uow.RemoveRange(entries);
|
||||
if (dropAmountMax > dropAmount)
|
||||
dropAmount = new NadekoRandom().Next(dropAmount, dropAmountMax + 1);
|
||||
|
||||
|
||||
if (amount > 0)
|
||||
if (dropAmount > 0)
|
||||
{
|
||||
// give the picked currency to the user
|
||||
await _cs.AddAsync(uid, "Picked currency", amount, gamble: false);
|
||||
var prefix = _cmdHandler.GetPrefix(channel.Guild.Id);
|
||||
var toSend = dropAmount == 1
|
||||
? GetText(channel.GuildId, strs.curgen_sn(config.Currency.Sign))
|
||||
+ " " + GetText(channel.GuildId, strs.pick_sn(prefix))
|
||||
: GetText(channel.GuildId, strs.curgen_pl(dropAmount, config.Currency.Sign))
|
||||
+ " " + GetText(channel.GuildId, strs.pick_pl(prefix));
|
||||
|
||||
var pw = config.Generation.HasPassword ? GenerateCurrencyPassword().ToUpperInvariant() : null;
|
||||
|
||||
IUserMessage sent;
|
||||
using (var stream = GetRandomCurrencyImage(pw, out var ext))
|
||||
{
|
||||
sent = await channel.SendFileAsync(stream, $"currency_image.{ext}", toSend).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await AddPlantToDatabase(channel.GuildId,
|
||||
channel.Id,
|
||||
_client.CurrentUser.Id,
|
||||
sent.Id,
|
||||
dropAmount,
|
||||
pw).ConfigureAwait(false);
|
||||
}
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// delete all of the plant messages which have just been picked
|
||||
var _ = ch.DeleteMessagesAsync(ids);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// return the amount of currency the user picked
|
||||
return amount;
|
||||
}
|
||||
finally
|
||||
{
|
||||
pickLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ulong?> SendPlantMessageAsync(ulong gid, IMessageChannel ch, string user, long amount, string pass)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get the text
|
||||
var prefix = _cmdHandler.GetPrefix(gid);
|
||||
var msgToSend = GetText(gid,
|
||||
strs.planted(
|
||||
Format.Bold(user),
|
||||
amount + _gss.Data.Currency.Sign));
|
||||
|
||||
if (amount > 1)
|
||||
msgToSend += " " + GetText(gid, strs.pick_pl(prefix));
|
||||
else
|
||||
msgToSend += " " + GetText(gid, strs.pick_sn(prefix));
|
||||
|
||||
//get the image
|
||||
using (var stream = GetRandomCurrencyImage(pass, out var ext))
|
||||
{
|
||||
// send it
|
||||
var msg = await ch.SendFileAsync(stream, $"img.{ext}", msgToSend).ConfigureAwait(false);
|
||||
// return sent message's id (in order to be able to delete it when it's picked)
|
||||
return msg.Id;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if sending fails, return null as message id
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<bool> PlantAsync(ulong gid, IMessageChannel ch, ulong uid, string user, long amount, string pass)
|
||||
{
|
||||
// normalize it - no more than 10 chars, uppercase
|
||||
pass = pass?.Trim().TrimTo(10, hideDots: true).ToUpperInvariant();
|
||||
// has to be either null or alphanumeric
|
||||
if (!string.IsNullOrWhiteSpace(pass) && !pass.IsAlphaNumeric())
|
||||
return false;
|
||||
|
||||
// remove currency from the user who's planting
|
||||
if (await _cs.RemoveAsync(uid, "Planted currency", amount, gamble: false))
|
||||
{
|
||||
// try to send the message with the currency image
|
||||
var msgId = await SendPlantMessageAsync(gid, ch, user, amount, pass).ConfigureAwait(false);
|
||||
if (msgId is null)
|
||||
{
|
||||
// if it fails it will return null, if it returns null, refund
|
||||
await _cs.AddAsync(uid, "Planted currency refund", amount, gamble: false);
|
||||
return false;
|
||||
}
|
||||
// if it doesn't fail, put the plant in the database for other people to pick
|
||||
await AddPlantToDatabase(gid, ch.Id, uid, msgId.Value, amount, pass).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
// if user doesn't have enough currency, fail
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task AddPlantToDatabase(ulong gid, ulong cid, ulong uid, ulong mid, long amount, string pass)
|
||||
/// <summary>
|
||||
/// Generate a hexadecimal string from 1000 to ffff.
|
||||
/// </summary>
|
||||
/// <returns>A hexadecimal string from 1000 to ffff</returns>
|
||||
private string GenerateCurrencyPassword()
|
||||
{
|
||||
// generate a number from 1000 to ffff
|
||||
var num = _rng.Next(4096, 65536);
|
||||
// convert it to hexadecimal
|
||||
return num.ToString("x4");
|
||||
}
|
||||
|
||||
public async Task<long> PickAsync(ulong gid, ITextChannel ch, ulong uid, string pass)
|
||||
{
|
||||
await pickLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
long amount;
|
||||
ulong[] ids;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
uow.PlantedCurrency.Add(new PlantedCurrency
|
||||
// this method will sum all plants with that password,
|
||||
// remove them, and get messageids of the removed plants
|
||||
|
||||
pass = pass?.Trim().TrimTo(10, hideDots: true).ToUpperInvariant();
|
||||
// gets all plants in this channel with the same password
|
||||
var entries = uow.PlantedCurrency
|
||||
.AsQueryable()
|
||||
.Where(x => x.ChannelId == ch.Id && pass == x.Password)
|
||||
.ToList();
|
||||
// sum how much currency that is, and get all of the message ids (so that i can delete them)
|
||||
amount = entries.Sum(x => x.Amount);
|
||||
ids = entries.Select(x => x.MessageId).ToArray();
|
||||
// remove them from the database
|
||||
uow.RemoveRange(entries);
|
||||
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
Amount = amount,
|
||||
GuildId = gid,
|
||||
ChannelId = cid,
|
||||
Password = pass,
|
||||
UserId = uid,
|
||||
MessageId = mid,
|
||||
});
|
||||
await uow.SaveChangesAsync();
|
||||
// give the picked currency to the user
|
||||
await _cs.AddAsync(uid, "Picked currency", amount, gamble: false);
|
||||
}
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// delete all of the plant messages which have just been picked
|
||||
var _ = ch.DeleteMessagesAsync(ids);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// return the amount of currency the user picked
|
||||
return amount;
|
||||
}
|
||||
finally
|
||||
{
|
||||
pickLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ulong?> SendPlantMessageAsync(ulong gid, IMessageChannel ch, string user, long amount, string pass)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get the text
|
||||
var prefix = _cmdHandler.GetPrefix(gid);
|
||||
var msgToSend = GetText(gid,
|
||||
strs.planted(
|
||||
Format.Bold(user),
|
||||
amount + _gss.Data.Currency.Sign));
|
||||
|
||||
if (amount > 1)
|
||||
msgToSend += " " + GetText(gid, strs.pick_pl(prefix));
|
||||
else
|
||||
msgToSend += " " + GetText(gid, strs.pick_sn(prefix));
|
||||
|
||||
//get the image
|
||||
using (var stream = GetRandomCurrencyImage(pass, out var ext))
|
||||
{
|
||||
// send it
|
||||
var msg = await ch.SendFileAsync(stream, $"img.{ext}", msgToSend).ConfigureAwait(false);
|
||||
// return sent message's id (in order to be able to delete it when it's picked)
|
||||
return msg.Id;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if sending fails, return null as message id
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> PlantAsync(ulong gid, IMessageChannel ch, ulong uid, string user, long amount, string pass)
|
||||
{
|
||||
// normalize it - no more than 10 chars, uppercase
|
||||
pass = pass?.Trim().TrimTo(10, hideDots: true).ToUpperInvariant();
|
||||
// has to be either null or alphanumeric
|
||||
if (!string.IsNullOrWhiteSpace(pass) && !pass.IsAlphaNumeric())
|
||||
return false;
|
||||
|
||||
// remove currency from the user who's planting
|
||||
if (await _cs.RemoveAsync(uid, "Planted currency", amount, gamble: false))
|
||||
{
|
||||
// try to send the message with the currency image
|
||||
var msgId = await SendPlantMessageAsync(gid, ch, user, amount, pass).ConfigureAwait(false);
|
||||
if (msgId is null)
|
||||
{
|
||||
// if it fails it will return null, if it returns null, refund
|
||||
await _cs.AddAsync(uid, "Planted currency refund", amount, gamble: false);
|
||||
return false;
|
||||
}
|
||||
// if it doesn't fail, put the plant in the database for other people to pick
|
||||
await AddPlantToDatabase(gid, ch.Id, uid, msgId.Value, amount, pass).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
// if user doesn't have enough currency, fail
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task AddPlantToDatabase(ulong gid, ulong cid, ulong uid, ulong mid, long amount, string pass)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
uow.PlantedCurrency.Add(new PlantedCurrency
|
||||
{
|
||||
Amount = amount,
|
||||
GuildId = gid,
|
||||
ChannelId = cid,
|
||||
Password = pass,
|
||||
UserId = uid,
|
||||
MessageId = mid,
|
||||
});
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,121 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Services;
|
||||
using Discord.WebSocket;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class VoteModel
|
||||
{
|
||||
public class VoteModel
|
||||
{
|
||||
[JsonPropertyName("userId")]
|
||||
public ulong UserId { get; set; }
|
||||
}
|
||||
[JsonPropertyName("userId")]
|
||||
public ulong UserId { get; set; }
|
||||
}
|
||||
|
||||
public class VoteRewardService : INService, IReadyExecutor
|
||||
public class VoteRewardService : INService, IReadyExecutor
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ICurrencyService _currencyService;
|
||||
private readonly GamblingConfigService _gamb;
|
||||
private HttpClient _http;
|
||||
|
||||
public VoteRewardService(
|
||||
DiscordSocketClient client,
|
||||
IBotCredentials creds,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ICurrencyService currencyService,
|
||||
GamblingConfigService gamb)
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ICurrencyService _currencyService;
|
||||
private readonly GamblingConfigService _gamb;
|
||||
private HttpClient _http;
|
||||
|
||||
public VoteRewardService(
|
||||
DiscordSocketClient client,
|
||||
IBotCredentials creds,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ICurrencyService currencyService,
|
||||
GamblingConfigService gamb)
|
||||
{
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_currencyService = currencyService;
|
||||
_gamb = gamb;
|
||||
}
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_currencyService = currencyService;
|
||||
_gamb = gamb;
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
if (_client.ShardId != 0)
|
||||
return;
|
||||
|
||||
_http = new HttpClient(new HttpClientHandler()
|
||||
{
|
||||
if (_client.ShardId != 0)
|
||||
return;
|
||||
AllowAutoRedirect = false,
|
||||
ServerCertificateCustomValidationCallback = delegate { return true; }
|
||||
});
|
||||
|
||||
_http = new HttpClient(new HttpClientHandler()
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
ServerCertificateCustomValidationCallback = delegate { return true; }
|
||||
});
|
||||
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(30000);
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(30000);
|
||||
|
||||
var topggKey = _creds.Votes?.TopggKey;
|
||||
var topggServiceUrl = _creds.Votes?.TopggServiceUrl;
|
||||
var topggKey = _creds.Votes?.TopggKey;
|
||||
var topggServiceUrl = _creds.Votes?.TopggServiceUrl;
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(topggKey)
|
||||
&& !string.IsNullOrWhiteSpace(topggServiceUrl))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(topggKey)
|
||||
&& !string.IsNullOrWhiteSpace(topggServiceUrl))
|
||||
_http.DefaultRequestHeaders.Authorization = new(topggKey);
|
||||
var uri = new Uri(new(topggServiceUrl), "topgg/new");
|
||||
var res = await _http.GetStringAsync(uri);
|
||||
var data = JsonSerializer.Deserialize<List<VoteModel>>(res);
|
||||
|
||||
if (data is { Count: > 0 })
|
||||
{
|
||||
_http.DefaultRequestHeaders.Authorization = new(topggKey);
|
||||
var uri = new Uri(new(topggServiceUrl), "topgg/new");
|
||||
var res = await _http.GetStringAsync(uri);
|
||||
var data = JsonSerializer.Deserialize<List<VoteModel>>(res);
|
||||
var ids = data.Select(x => x.UserId).ToList();
|
||||
|
||||
if (data is { Count: > 0 })
|
||||
{
|
||||
var ids = data.Select(x => x.UserId).ToList();
|
||||
await _currencyService.AddBulkAsync(ids,
|
||||
data.Select(_ => "top.gg vote reward"),
|
||||
data.Select(x => _gamb.Data.VoteReward),
|
||||
true);
|
||||
|
||||
await _currencyService.AddBulkAsync(ids,
|
||||
data.Select(_ => "top.gg vote reward"),
|
||||
data.Select(x => _gamb.Data.VoteReward),
|
||||
true);
|
||||
|
||||
Log.Information("Rewarding {Count} top.gg voters", ids.Count());
|
||||
}
|
||||
Log.Information("Rewarding {Count} top.gg voters", ids.Count());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Critical error loading top.gg vote rewards.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Critical error loading top.gg vote rewards.");
|
||||
}
|
||||
|
||||
var discordsKey = _creds.Votes?.DiscordsKey;
|
||||
var discordsServiceUrl = _creds.Votes?.DiscordsServiceUrl;
|
||||
var discordsKey = _creds.Votes?.DiscordsKey;
|
||||
var discordsServiceUrl = _creds.Votes?.DiscordsServiceUrl;
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(discordsKey)
|
||||
&& !string.IsNullOrWhiteSpace(discordsServiceUrl))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(discordsKey)
|
||||
&& !string.IsNullOrWhiteSpace(discordsServiceUrl))
|
||||
_http.DefaultRequestHeaders.Authorization = new(discordsKey);
|
||||
var res = await _http.GetStringAsync(new Uri(new(discordsServiceUrl), "discords/new"));
|
||||
var data = JsonSerializer.Deserialize<List<VoteModel>>(res);
|
||||
|
||||
if (data is { Count: > 0 })
|
||||
{
|
||||
_http.DefaultRequestHeaders.Authorization = new(discordsKey);
|
||||
var res = await _http.GetStringAsync(new Uri(new(discordsServiceUrl), "discords/new"));
|
||||
var data = JsonSerializer.Deserialize<List<VoteModel>>(res);
|
||||
|
||||
if (data is { Count: > 0 })
|
||||
{
|
||||
var ids = data.Select(x => x.UserId).ToList();
|
||||
var ids = data.Select(x => x.UserId).ToList();
|
||||
|
||||
await _currencyService.AddBulkAsync(ids,
|
||||
data.Select(_ => "discords.com vote reward"),
|
||||
data.Select(x => _gamb.Data.VoteReward),
|
||||
true);
|
||||
await _currencyService.AddBulkAsync(ids,
|
||||
data.Select(_ => "discords.com vote reward"),
|
||||
data.Select(x => _gamb.Data.VoteReward),
|
||||
true);
|
||||
|
||||
Log.Information("Rewarding {Count} discords.com voters", ids.Count());
|
||||
}
|
||||
Log.Information("Rewarding {Count} discords.com voters", ids.Count());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Critical error loading discords.com vote rewards.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Critical error loading discords.com vote rewards.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,527 +2,523 @@
|
||||
using NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class WaifuService : INService
|
||||
{
|
||||
public class WaifuService : INService
|
||||
public class FullWaifuInfo
|
||||
{
|
||||
public class FullWaifuInfo
|
||||
{
|
||||
public WaifuInfo Waifu { get; set; }
|
||||
public IEnumerable<string> Claims { get; set; }
|
||||
public int Divorces { get; set; }
|
||||
}
|
||||
public WaifuInfo Waifu { get; set; }
|
||||
public IEnumerable<string> Claims { get; set; }
|
||||
public int Divorces { get; set; }
|
||||
}
|
||||
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly GamblingConfigService _gss;
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly GamblingConfigService _gss;
|
||||
|
||||
public WaifuService(DbService db, ICurrencyService cs, IDataCache cache,
|
||||
GamblingConfigService gss)
|
||||
{
|
||||
_db = db;
|
||||
_cs = cs;
|
||||
_cache = cache;
|
||||
_gss = gss;
|
||||
}
|
||||
public WaifuService(DbService db, ICurrencyService cs, IDataCache cache,
|
||||
GamblingConfigService gss)
|
||||
{
|
||||
_db = db;
|
||||
_cs = cs;
|
||||
_cache = cache;
|
||||
_gss = gss;
|
||||
}
|
||||
|
||||
public async Task<bool> WaifuTransfer(IUser owner, ulong waifuId, IUser newOwner)
|
||||
{
|
||||
if (owner.Id == newOwner.Id || waifuId == newOwner.Id)
|
||||
return false;
|
||||
public async Task<bool> WaifuTransfer(IUser owner, ulong waifuId, IUser newOwner)
|
||||
{
|
||||
if (owner.Id == newOwner.Id || waifuId == newOwner.Id)
|
||||
return false;
|
||||
|
||||
var settings = _gss.Data;
|
||||
var settings = _gss.Data;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(waifuId);
|
||||
var ownerUser = uow.GetOrCreateUser(owner);
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(waifuId);
|
||||
var ownerUser = uow.GetOrCreateUser(owner);
|
||||
|
||||
// owner has to be the owner of the waifu
|
||||
if (waifu is null || waifu.ClaimerId != ownerUser.Id)
|
||||
return false;
|
||||
// owner has to be the owner of the waifu
|
||||
if (waifu is null || waifu.ClaimerId != ownerUser.Id)
|
||||
return false;
|
||||
|
||||
// if waifu likes the person, gotta pay the penalty
|
||||
if (waifu.AffinityId == ownerUser.Id)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(owner.Id,
|
||||
// if waifu likes the person, gotta pay the penalty
|
||||
if (waifu.AffinityId == ownerUser.Id)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(owner.Id,
|
||||
"Waifu Transfer - affinity penalty",
|
||||
(int)(waifu.Price * 0.6),
|
||||
true))
|
||||
{
|
||||
// unable to pay 60% penalty
|
||||
return false;
|
||||
}
|
||||
|
||||
waifu.Price = (int)(waifu.Price * 0.7); // half of 60% = 30% price reduction
|
||||
if (waifu.Price < settings.Waifu.MinPrice)
|
||||
waifu.Price = settings.Waifu.MinPrice;
|
||||
}
|
||||
else // if not, pay 10% fee
|
||||
{
|
||||
if (!await _cs.RemoveAsync(owner.Id, "Waifu Transfer", waifu.Price / 10, gamble: true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
waifu.Price = (int) (waifu.Price * 0.95); // half of 10% = 5% price reduction
|
||||
if (waifu.Price < settings.Waifu.MinPrice)
|
||||
waifu.Price = settings.Waifu.MinPrice;
|
||||
}
|
||||
|
||||
//new claimerId is the id of the new owner
|
||||
var newOwnerUser = uow.GetOrCreateUser(newOwner);
|
||||
waifu.ClaimerId = newOwnerUser.Id;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetResetPrice(IUser user)
|
||||
{
|
||||
var settings = _gss.Data;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
|
||||
if (waifu is null)
|
||||
return settings.Waifu.MinPrice;
|
||||
|
||||
var divorces = uow.WaifuUpdates.Count(x => x.Old != null &&
|
||||
x.Old.UserId == user.Id &&
|
||||
x.UpdateType == WaifuUpdateType.Claimed &&
|
||||
x.New == null);
|
||||
var affs = uow.WaifuUpdates
|
||||
.AsQueryable()
|
||||
.Where(w => w.User.UserId == user.Id && w.UpdateType == WaifuUpdateType.AffinityChanged &&
|
||||
w.New != null)
|
||||
.ToList()
|
||||
.GroupBy(x => x.New)
|
||||
.Count();
|
||||
|
||||
return (int) Math.Ceiling(waifu.Price * 1.25f) +
|
||||
((divorces + affs + 2) * settings.Waifu.Multipliers.WaifuReset);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TryReset(IUser user)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var price = GetResetPrice(user);
|
||||
if (!await _cs.RemoveAsync(user.Id, "Waifu Reset", price, gamble: true))
|
||||
// unable to pay 60% penalty
|
||||
return false;
|
||||
}
|
||||
|
||||
var affs = uow.WaifuUpdates
|
||||
.AsQueryable()
|
||||
.Where(w => w.User.UserId == user.Id
|
||||
&& w.UpdateType == WaifuUpdateType.AffinityChanged
|
||||
&& w.New != null);
|
||||
waifu.Price = (int)(waifu.Price * 0.7); // half of 60% = 30% price reduction
|
||||
if (waifu.Price < settings.Waifu.MinPrice)
|
||||
waifu.Price = settings.Waifu.MinPrice;
|
||||
}
|
||||
else // if not, pay 10% fee
|
||||
{
|
||||
if (!await _cs.RemoveAsync(owner.Id, "Waifu Transfer", waifu.Price / 10, gamble: true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var divorces = uow.WaifuUpdates
|
||||
.AsQueryable()
|
||||
.Where(x => x.Old != null &&
|
||||
x.Old.UserId == user.Id &&
|
||||
x.UpdateType == WaifuUpdateType.Claimed &&
|
||||
x.New == null);
|
||||
|
||||
//reset changes of heart to 0
|
||||
uow.WaifuUpdates.RemoveRange(affs);
|
||||
//reset divorces to 0
|
||||
uow.WaifuUpdates.RemoveRange(divorces);
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
//reset price, remove items
|
||||
//remove owner, remove affinity
|
||||
waifu.Price = 50;
|
||||
waifu.Items.Clear();
|
||||
waifu.ClaimerId = null;
|
||||
waifu.AffinityId = null;
|
||||
|
||||
//wives stay though
|
||||
|
||||
uow.SaveChanges();
|
||||
waifu.Price = (int) (waifu.Price * 0.95); // half of 10% = 5% price reduction
|
||||
if (waifu.Price < settings.Waifu.MinPrice)
|
||||
waifu.Price = settings.Waifu.MinPrice;
|
||||
}
|
||||
|
||||
return true;
|
||||
//new claimerId is the id of the new owner
|
||||
var newOwnerUser = uow.GetOrCreateUser(newOwner);
|
||||
waifu.ClaimerId = newOwnerUser.Id;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<(WaifuInfo, bool, WaifuClaimResult)> ClaimWaifuAsync(IUser user, IUser target, int amount)
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetResetPrice(IUser user)
|
||||
{
|
||||
var settings = _gss.Data;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var settings = _gss.Data;
|
||||
WaifuClaimResult result;
|
||||
WaifuInfo w;
|
||||
bool isAffinity;
|
||||
using (var uow = _db.GetDbContext())
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
|
||||
if (waifu is null)
|
||||
return settings.Waifu.MinPrice;
|
||||
|
||||
var divorces = uow.WaifuUpdates.Count(x => x.Old != null &&
|
||||
x.Old.UserId == user.Id &&
|
||||
x.UpdateType == WaifuUpdateType.Claimed &&
|
||||
x.New == null);
|
||||
var affs = uow.WaifuUpdates
|
||||
.AsQueryable()
|
||||
.Where(w => w.User.UserId == user.Id && w.UpdateType == WaifuUpdateType.AffinityChanged &&
|
||||
w.New != null)
|
||||
.ToList()
|
||||
.GroupBy(x => x.New)
|
||||
.Count();
|
||||
|
||||
return (int) Math.Ceiling(waifu.Price * 1.25f) +
|
||||
((divorces + affs + 2) * settings.Waifu.Multipliers.WaifuReset);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TryReset(IUser user)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var price = GetResetPrice(user);
|
||||
if (!await _cs.RemoveAsync(user.Id, "Waifu Reset", price, gamble: true))
|
||||
return false;
|
||||
|
||||
var affs = uow.WaifuUpdates
|
||||
.AsQueryable()
|
||||
.Where(w => w.User.UserId == user.Id
|
||||
&& w.UpdateType == WaifuUpdateType.AffinityChanged
|
||||
&& w.New != null);
|
||||
|
||||
var divorces = uow.WaifuUpdates
|
||||
.AsQueryable()
|
||||
.Where(x => x.Old != null &&
|
||||
x.Old.UserId == user.Id &&
|
||||
x.UpdateType == WaifuUpdateType.Claimed &&
|
||||
x.New == null);
|
||||
|
||||
//reset changes of heart to 0
|
||||
uow.WaifuUpdates.RemoveRange(affs);
|
||||
//reset divorces to 0
|
||||
uow.WaifuUpdates.RemoveRange(divorces);
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
//reset price, remove items
|
||||
//remove owner, remove affinity
|
||||
waifu.Price = 50;
|
||||
waifu.Items.Clear();
|
||||
waifu.ClaimerId = null;
|
||||
waifu.AffinityId = null;
|
||||
|
||||
//wives stay though
|
||||
|
||||
uow.SaveChanges();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<(WaifuInfo, bool, WaifuClaimResult)> ClaimWaifuAsync(IUser user, IUser target, int amount)
|
||||
{
|
||||
var settings = _gss.Data;
|
||||
WaifuClaimResult result;
|
||||
WaifuInfo w;
|
||||
bool isAffinity;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
w = uow.WaifuInfo.ByWaifuUserId(target.Id);
|
||||
isAffinity = (w?.Affinity?.UserId == user.Id);
|
||||
if (w is null)
|
||||
{
|
||||
w = uow.WaifuInfo.ByWaifuUserId(target.Id);
|
||||
isAffinity = (w?.Affinity?.UserId == user.Id);
|
||||
if (w is null)
|
||||
var claimer = uow.GetOrCreateUser(user);
|
||||
var waifu = uow.GetOrCreateUser(target);
|
||||
if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, gamble: true))
|
||||
{
|
||||
var claimer = uow.GetOrCreateUser(user);
|
||||
var waifu = uow.GetOrCreateUser(target);
|
||||
if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, gamble: true))
|
||||
{
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
uow.WaifuInfo.Add(w = new WaifuInfo()
|
||||
{
|
||||
Waifu = waifu,
|
||||
Claimer = claimer,
|
||||
Affinity = null,
|
||||
Price = amount
|
||||
});
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = waifu,
|
||||
Old = null,
|
||||
New = claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
result = WaifuClaimResult.Success;
|
||||
}
|
||||
}
|
||||
else if (isAffinity && amount > w.Price * settings.Waifu.Multipliers.CrushClaim)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, gamble: true))
|
||||
{
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = uow.GetOrCreateUser(user);
|
||||
w.Price = amount + (amount / 4);
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = w.Claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (amount >= w.Price * settings.Waifu.Multipliers.NormalClaim) // if no affinity
|
||||
{
|
||||
if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, gamble: true))
|
||||
{
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = uow.GetOrCreateUser(user);
|
||||
w.Price = amount;
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = w.Claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
result = WaifuClaimResult.InsufficientAmount;
|
||||
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return (w, isAffinity, result);
|
||||
}
|
||||
|
||||
public async Task<(DiscordUser, bool, TimeSpan?)> ChangeAffinityAsync(IUser user, IGuildUser target)
|
||||
{
|
||||
DiscordUser oldAff = null;
|
||||
var success = false;
|
||||
TimeSpan? remaining = null;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var w = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
var newAff = target is null ? null : uow.GetOrCreateUser(target);
|
||||
if (w?.Affinity?.UserId == target?.Id)
|
||||
{
|
||||
}
|
||||
else if (!_cache.TryAddAffinityCooldown(user.Id, out remaining))
|
||||
{
|
||||
}
|
||||
else if (w is null)
|
||||
{
|
||||
var thisUser = uow.GetOrCreateUser(user);
|
||||
uow.WaifuInfo.Add(new WaifuInfo()
|
||||
uow.WaifuInfo.Add(w = new WaifuInfo()
|
||||
{
|
||||
Affinity = newAff,
|
||||
Waifu = thisUser,
|
||||
Price = 1,
|
||||
Claimer = null
|
||||
Waifu = waifu,
|
||||
Claimer = claimer,
|
||||
Affinity = null,
|
||||
Price = amount
|
||||
});
|
||||
success = true;
|
||||
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = thisUser,
|
||||
User = waifu,
|
||||
Old = null,
|
||||
New = newAff,
|
||||
UpdateType = WaifuUpdateType.AffinityChanged
|
||||
New = claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
result = WaifuClaimResult.Success;
|
||||
}
|
||||
}
|
||||
else if (isAffinity && amount > w.Price * settings.Waifu.Multipliers.CrushClaim)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, gamble: true))
|
||||
{
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (w.Affinity != null)
|
||||
oldAff = w.Affinity;
|
||||
w.Affinity = newAff;
|
||||
success = true;
|
||||
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldAff,
|
||||
New = newAff,
|
||||
UpdateType = WaifuUpdateType.AffinityChanged
|
||||
});
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return (oldAff, success, remaining);
|
||||
}
|
||||
|
||||
public IEnumerable<WaifuLbResult> GetTopWaifusAtPage(int page)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
return uow.WaifuInfo.GetTop(9, page * 9);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetWaifuUserId(ulong ownerId, string name)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.WaifuInfo.GetWaifuUserId(ownerId, name);
|
||||
}
|
||||
|
||||
public async Task<(WaifuInfo, DivorceResult, long, TimeSpan?)> DivorceWaifuAsync(IUser user, ulong targetId)
|
||||
{
|
||||
DivorceResult result;
|
||||
TimeSpan? remaining = null;
|
||||
long amount = 0;
|
||||
WaifuInfo w = null;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
w = uow.WaifuInfo.ByWaifuUserId(targetId);
|
||||
var now = DateTime.UtcNow;
|
||||
if (w?.Claimer is null || w.Claimer.UserId != user.Id)
|
||||
result = DivorceResult.NotYourWife;
|
||||
else if (!_cache.TryAddDivorceCooldown(user.Id, out remaining))
|
||||
{
|
||||
result = DivorceResult.Cooldown;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = w.Price / 2;
|
||||
|
||||
if (w.Affinity?.UserId == user.Id)
|
||||
{
|
||||
await _cs.AddAsync(w.Waifu.UserId, "Waifu Compensation", amount, gamble: true);
|
||||
w.Price = (int) Math.Floor(w.Price * _gss.Data.Waifu.Multipliers.DivorceNewValue);
|
||||
result = DivorceResult.SucessWithPenalty;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _cs.AddAsync(user.Id, "Waifu Refund", amount, gamble: true);
|
||||
|
||||
result = DivorceResult.Success;
|
||||
}
|
||||
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = null;
|
||||
w.Claimer = uow.GetOrCreateUser(user);
|
||||
w.Price = amount + (amount / 4);
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = null,
|
||||
New = w.Claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return (w, result, amount, remaining);
|
||||
}
|
||||
|
||||
public async Task<bool> GiftWaifuAsync(IUser from, IUser giftedWaifu, WaifuItemModel itemObj)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(from, "Bought waifu item", itemObj.Price, gamble: true))
|
||||
else if (amount >= w.Price * settings.Waifu.Multipliers.NormalClaim) // if no affinity
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var w = uow.WaifuInfo.ByWaifuUserId(giftedWaifu.Id,
|
||||
set => set.Include(x => x.Items)
|
||||
.Include(x => x.Claimer));
|
||||
if (w is null)
|
||||
if (!await _cs.RemoveAsync(user.Id, "Claimed Waifu", amount, gamble: true))
|
||||
{
|
||||
uow.WaifuInfo.Add(w = new WaifuInfo()
|
||||
{
|
||||
Affinity = null,
|
||||
Claimer = null,
|
||||
Price = 1,
|
||||
Waifu = uow.GetOrCreateUser(giftedWaifu),
|
||||
});
|
||||
}
|
||||
|
||||
if (!itemObj.Negative)
|
||||
{
|
||||
w.Items.Add(new WaifuItem()
|
||||
{
|
||||
Name = itemObj.Name.ToLowerInvariant(),
|
||||
ItemEmoji = itemObj.ItemEmoji,
|
||||
});
|
||||
|
||||
if (w.Claimer?.UserId == from.Id)
|
||||
{
|
||||
w.Price += (int)(itemObj.Price * _gss.Data.Waifu.Multipliers.GiftEffect);
|
||||
}
|
||||
else
|
||||
{
|
||||
w.Price += itemObj.Price / 2;
|
||||
}
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
w.Price -= (int)(itemObj.Price * _gss.Data.Waifu.Multipliers.NegativeGiftEffect);
|
||||
if (w.Price < 1)
|
||||
w.Price = 1;
|
||||
}
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = uow.GetOrCreateUser(user);
|
||||
w.Price = amount;
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public WaifuInfoStats GetFullWaifuInfoAsync(ulong targetId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var wi = uow.GetWaifuInfo(targetId);
|
||||
if (wi is null)
|
||||
{
|
||||
wi = new WaifuInfoStats
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
AffinityCount = 0,
|
||||
AffinityName = null,
|
||||
ClaimCount = 0,
|
||||
ClaimerName = null,
|
||||
Claims = new List<string>(),
|
||||
Fans = new List<string>(),
|
||||
DivorceCount = 0,
|
||||
FullName = null,
|
||||
Items = new List<WaifuItem>(),
|
||||
Price = 1
|
||||
};
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = w.Claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
|
||||
return wi;
|
||||
}
|
||||
else
|
||||
result = WaifuClaimResult.InsufficientAmount;
|
||||
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
public WaifuInfoStats GetFullWaifuInfoAsync(IGuildUser target)
|
||||
|
||||
return (w, isAffinity, result);
|
||||
}
|
||||
|
||||
public async Task<(DiscordUser, bool, TimeSpan?)> ChangeAffinityAsync(IUser user, IGuildUser target)
|
||||
{
|
||||
DiscordUser oldAff = null;
|
||||
var success = false;
|
||||
TimeSpan? remaining = null;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
var w = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
var newAff = target is null ? null : uow.GetOrCreateUser(target);
|
||||
if (w?.Affinity?.UserId == target?.Id)
|
||||
{
|
||||
var du = uow.GetOrCreateUser(target);
|
||||
|
||||
return GetFullWaifuInfoAsync(target.Id);
|
||||
}
|
||||
}
|
||||
else if (!_cache.TryAddAffinityCooldown(user.Id, out remaining))
|
||||
{
|
||||
}
|
||||
else if (w is null)
|
||||
{
|
||||
var thisUser = uow.GetOrCreateUser(user);
|
||||
uow.WaifuInfo.Add(new WaifuInfo()
|
||||
{
|
||||
Affinity = newAff,
|
||||
Waifu = thisUser,
|
||||
Price = 1,
|
||||
Claimer = null
|
||||
});
|
||||
success = true;
|
||||
|
||||
public string GetClaimTitle(int count)
|
||||
{
|
||||
ClaimTitle title;
|
||||
if (count == 0)
|
||||
title = ClaimTitle.Lonely;
|
||||
else if (count == 1)
|
||||
title = ClaimTitle.Devoted;
|
||||
else if (count < 3)
|
||||
title = ClaimTitle.Rookie;
|
||||
else if (count < 6)
|
||||
title = ClaimTitle.Schemer;
|
||||
else if (count < 10)
|
||||
title = ClaimTitle.Dilettante;
|
||||
else if (count < 17)
|
||||
title = ClaimTitle.Intermediate;
|
||||
else if (count < 25)
|
||||
title = ClaimTitle.Seducer;
|
||||
else if (count < 35)
|
||||
title = ClaimTitle.Expert;
|
||||
else if (count < 50)
|
||||
title = ClaimTitle.Veteran;
|
||||
else if (count < 75)
|
||||
title = ClaimTitle.Incubis;
|
||||
else if (count < 100)
|
||||
title = ClaimTitle.Harem_King;
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = thisUser,
|
||||
Old = null,
|
||||
New = newAff,
|
||||
UpdateType = WaifuUpdateType.AffinityChanged
|
||||
});
|
||||
}
|
||||
else
|
||||
title = ClaimTitle.Harem_God;
|
||||
{
|
||||
if (w.Affinity != null)
|
||||
oldAff = w.Affinity;
|
||||
w.Affinity = newAff;
|
||||
success = true;
|
||||
|
||||
return title.ToString().Replace('_', ' ');
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldAff,
|
||||
New = newAff,
|
||||
UpdateType = WaifuUpdateType.AffinityChanged
|
||||
});
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public string GetAffinityTitle(int count)
|
||||
{
|
||||
AffinityTitle title;
|
||||
if (count < 1)
|
||||
title = AffinityTitle.Pure;
|
||||
else if (count < 2)
|
||||
title = AffinityTitle.Faithful;
|
||||
else if (count < 4)
|
||||
title = AffinityTitle.Playful;
|
||||
else if (count < 8)
|
||||
title = AffinityTitle.Cheater;
|
||||
else if (count < 11)
|
||||
title = AffinityTitle.Tainted;
|
||||
else if (count < 15)
|
||||
title = AffinityTitle.Corrupted;
|
||||
else if (count < 20)
|
||||
title = AffinityTitle.Lewd;
|
||||
else if (count < 25)
|
||||
title = AffinityTitle.Sloot;
|
||||
else if (count < 35)
|
||||
title = AffinityTitle.Depraved;
|
||||
else
|
||||
title = AffinityTitle.Harlot;
|
||||
return (oldAff, success, remaining);
|
||||
}
|
||||
|
||||
return title.ToString().Replace('_', ' ');
|
||||
}
|
||||
|
||||
public IReadOnlyList<WaifuItemModel> GetWaifuItems()
|
||||
public IEnumerable<WaifuLbResult> GetTopWaifusAtPage(int page)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conf = _gss.Data;
|
||||
return conf.Waifu.Items
|
||||
.Select(x => new WaifuItemModel(x.ItemEmoji, (int)(x.Price * conf.Waifu.Multipliers.AllGiftPrices), x.Name, x.Negative))
|
||||
.ToList();
|
||||
return uow.WaifuInfo.GetTop(9, page * 9);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetWaifuUserId(ulong ownerId, string name)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.WaifuInfo.GetWaifuUserId(ownerId, name);
|
||||
}
|
||||
|
||||
public async Task<(WaifuInfo, DivorceResult, long, TimeSpan?)> DivorceWaifuAsync(IUser user, ulong targetId)
|
||||
{
|
||||
DivorceResult result;
|
||||
TimeSpan? remaining = null;
|
||||
long amount = 0;
|
||||
WaifuInfo w = null;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
w = uow.WaifuInfo.ByWaifuUserId(targetId);
|
||||
var now = DateTime.UtcNow;
|
||||
if (w?.Claimer is null || w.Claimer.UserId != user.Id)
|
||||
result = DivorceResult.NotYourWife;
|
||||
else if (!_cache.TryAddDivorceCooldown(user.Id, out remaining))
|
||||
{
|
||||
result = DivorceResult.Cooldown;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = w.Price / 2;
|
||||
|
||||
if (w.Affinity?.UserId == user.Id)
|
||||
{
|
||||
await _cs.AddAsync(w.Waifu.UserId, "Waifu Compensation", amount, gamble: true);
|
||||
w.Price = (int) Math.Floor(w.Price * _gss.Data.Waifu.Multipliers.DivorceNewValue);
|
||||
result = DivorceResult.SucessWithPenalty;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _cs.AddAsync(user.Id, "Waifu Refund", amount, gamble: true);
|
||||
|
||||
result = DivorceResult.Success;
|
||||
}
|
||||
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = null;
|
||||
|
||||
uow.WaifuUpdates.Add(new WaifuUpdate()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = null,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return (w, result, amount, remaining);
|
||||
}
|
||||
|
||||
public async Task<bool> GiftWaifuAsync(IUser from, IUser giftedWaifu, WaifuItemModel itemObj)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(from, "Bought waifu item", itemObj.Price, gamble: true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var w = uow.WaifuInfo.ByWaifuUserId(giftedWaifu.Id,
|
||||
set => set.Include(x => x.Items)
|
||||
.Include(x => x.Claimer));
|
||||
if (w is null)
|
||||
{
|
||||
uow.WaifuInfo.Add(w = new WaifuInfo()
|
||||
{
|
||||
Affinity = null,
|
||||
Claimer = null,
|
||||
Price = 1,
|
||||
Waifu = uow.GetOrCreateUser(giftedWaifu),
|
||||
});
|
||||
}
|
||||
|
||||
if (!itemObj.Negative)
|
||||
{
|
||||
w.Items.Add(new WaifuItem()
|
||||
{
|
||||
Name = itemObj.Name.ToLowerInvariant(),
|
||||
ItemEmoji = itemObj.ItemEmoji,
|
||||
});
|
||||
|
||||
if (w.Claimer?.UserId == from.Id)
|
||||
{
|
||||
w.Price += (int)(itemObj.Price * _gss.Data.Waifu.Multipliers.GiftEffect);
|
||||
}
|
||||
else
|
||||
{
|
||||
w.Price += itemObj.Price / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
w.Price -= (int)(itemObj.Price * _gss.Data.Waifu.Multipliers.NegativeGiftEffect);
|
||||
if (w.Price < 1)
|
||||
w.Price = 1;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public WaifuInfoStats GetFullWaifuInfoAsync(ulong targetId)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var wi = uow.GetWaifuInfo(targetId);
|
||||
if (wi is null)
|
||||
{
|
||||
wi = new WaifuInfoStats
|
||||
{
|
||||
AffinityCount = 0,
|
||||
AffinityName = null,
|
||||
ClaimCount = 0,
|
||||
ClaimerName = null,
|
||||
Claims = new List<string>(),
|
||||
Fans = new List<string>(),
|
||||
DivorceCount = 0,
|
||||
FullName = null,
|
||||
Items = new List<WaifuItem>(),
|
||||
Price = 1
|
||||
};
|
||||
}
|
||||
|
||||
return wi;
|
||||
}
|
||||
}
|
||||
public WaifuInfoStats GetFullWaifuInfoAsync(IGuildUser target)
|
||||
{
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var du = uow.GetOrCreateUser(target);
|
||||
|
||||
return GetFullWaifuInfoAsync(target.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetClaimTitle(int count)
|
||||
{
|
||||
ClaimTitle title;
|
||||
if (count == 0)
|
||||
title = ClaimTitle.Lonely;
|
||||
else if (count == 1)
|
||||
title = ClaimTitle.Devoted;
|
||||
else if (count < 3)
|
||||
title = ClaimTitle.Rookie;
|
||||
else if (count < 6)
|
||||
title = ClaimTitle.Schemer;
|
||||
else if (count < 10)
|
||||
title = ClaimTitle.Dilettante;
|
||||
else if (count < 17)
|
||||
title = ClaimTitle.Intermediate;
|
||||
else if (count < 25)
|
||||
title = ClaimTitle.Seducer;
|
||||
else if (count < 35)
|
||||
title = ClaimTitle.Expert;
|
||||
else if (count < 50)
|
||||
title = ClaimTitle.Veteran;
|
||||
else if (count < 75)
|
||||
title = ClaimTitle.Incubis;
|
||||
else if (count < 100)
|
||||
title = ClaimTitle.Harem_King;
|
||||
else
|
||||
title = ClaimTitle.Harem_God;
|
||||
|
||||
return title.ToString().Replace('_', ' ');
|
||||
}
|
||||
|
||||
public string GetAffinityTitle(int count)
|
||||
{
|
||||
AffinityTitle title;
|
||||
if (count < 1)
|
||||
title = AffinityTitle.Pure;
|
||||
else if (count < 2)
|
||||
title = AffinityTitle.Faithful;
|
||||
else if (count < 4)
|
||||
title = AffinityTitle.Playful;
|
||||
else if (count < 8)
|
||||
title = AffinityTitle.Cheater;
|
||||
else if (count < 11)
|
||||
title = AffinityTitle.Tainted;
|
||||
else if (count < 15)
|
||||
title = AffinityTitle.Corrupted;
|
||||
else if (count < 20)
|
||||
title = AffinityTitle.Lewd;
|
||||
else if (count < 25)
|
||||
title = AffinityTitle.Sloot;
|
||||
else if (count < 35)
|
||||
title = AffinityTitle.Depraved;
|
||||
else
|
||||
title = AffinityTitle.Harlot;
|
||||
|
||||
return title.ToString().Replace('_', ' ');
|
||||
}
|
||||
|
||||
public IReadOnlyList<WaifuItemModel> GetWaifuItems()
|
||||
{
|
||||
var conf = _gss.Data;
|
||||
return conf.Waifu.Items
|
||||
.Select(x => new WaifuItemModel(x.ItemEmoji, (int)(x.Price * conf.Waifu.Multipliers.AllGiftPrices), x.Name, x.Negative))
|
||||
.ToList();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user