mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
Abstract away cache. 2 implementations: redis and memory
This commit is contained in:
@@ -19,8 +19,8 @@ public partial class Gambling
|
||||
private static readonly char[] _fateRolls = { '-', ' ', '+' };
|
||||
private readonly IImageCache _images;
|
||||
|
||||
public DiceRollCommands(IDataCache data)
|
||||
=> _images = data.LocalImages;
|
||||
public DiceRollCommands(ImageCache images)
|
||||
=> _images = images;
|
||||
|
||||
[Cmd]
|
||||
public async partial Task Roll()
|
||||
@@ -31,10 +31,10 @@ public partial class Gambling
|
||||
var num1 = gen / 10;
|
||||
var num2 = gen % 10;
|
||||
|
||||
using var img1 = GetDice(num1);
|
||||
using var img2 = GetDice(num2);
|
||||
using var img1 = await GetDiceAsync(num1);
|
||||
using var img2 = await GetDiceAsync(num2);
|
||||
using var img = new[] { img1, img2 }.Merge(out var format);
|
||||
await using var ms = img.ToStream(format);
|
||||
await using var ms = await img.ToStreamAsync(format);
|
||||
await ctx.Channel.SendFileAsync(ms,
|
||||
$"dice.{format.FileExtensions.First()}",
|
||||
Format.Bold(ctx.User.ToString()) + " " + GetText(strs.dice_rolled(Format.Code(gen.ToString()))));
|
||||
@@ -96,7 +96,7 @@ public partial class Gambling
|
||||
else
|
||||
toInsert = dice.Count;
|
||||
|
||||
dice.Insert(toInsert, GetDice(randomNumber));
|
||||
dice.Insert(toInsert, await GetDiceAsync(randomNumber));
|
||||
values.Insert(toInsert, randomNumber);
|
||||
}
|
||||
|
||||
@@ -195,20 +195,19 @@ public partial class Gambling
|
||||
await ReplyConfirmLocalizedAsync(strs.dice_rolled(Format.Bold(rolled.ToString())));
|
||||
}
|
||||
|
||||
private Image<Rgba32> GetDice(int num)
|
||||
private async Task<Image<Rgba32>> GetDiceAsync(int num)
|
||||
{
|
||||
if (num is < 0 or > 10)
|
||||
throw new ArgumentOutOfRangeException(nameof(num));
|
||||
|
||||
if (num == 10)
|
||||
{
|
||||
var images = _images.Dice;
|
||||
using var imgOne = Image.Load(images[1]);
|
||||
using var imgZero = Image.Load(images[0]);
|
||||
using var imgOne = Image.Load(await _images.GetDiceAsync(1));
|
||||
using var imgZero = Image.Load(await _images.GetDiceAsync(0));
|
||||
return new[] { imgOne, imgZero }.Merge();
|
||||
}
|
||||
|
||||
return Image.Load(_images.Dice[num]);
|
||||
return Image.Load(await _images.GetDiceAsync(num));
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,8 +14,8 @@ public partial class Gambling
|
||||
private static readonly ConcurrentDictionary<IGuild, Deck> _allDecks = new();
|
||||
private readonly IImageCache _images;
|
||||
|
||||
public DrawCommands(IDataCache data)
|
||||
=> _images = data.LocalImages;
|
||||
public DrawCommands(IImageCache images)
|
||||
=> _images = images;
|
||||
|
||||
private async Task<(Stream ImageStream, string ToSend)> InternalDraw(int num, ulong? guildId = null)
|
||||
{
|
||||
@@ -43,7 +43,8 @@ public partial class Gambling
|
||||
|
||||
var currentCard = cards.Draw();
|
||||
cardObjects.Add(currentCard);
|
||||
images.Add(Image.Load(_images.GetCard(currentCard.ToString().ToLowerInvariant().Replace(' ', '_'))));
|
||||
var cardName = currentCard.ToString().ToLowerInvariant().Replace(' ', '_');
|
||||
images.Add(Image.Load(await File.ReadAllBytesAsync($"data/images/cards/{cardName}.png")));
|
||||
}
|
||||
|
||||
using var img = images.Merge();
|
||||
|
@@ -25,11 +25,17 @@ public partial class Gambling
|
||||
private static readonly NadekoRandom _rng = new();
|
||||
private readonly IImageCache _images;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly ImagesConfig _ic;
|
||||
|
||||
public FlipCoinCommands(IDataCache data, ICurrencyService cs, GamblingConfigService gss)
|
||||
public FlipCoinCommands(
|
||||
IImageCache images,
|
||||
ImagesConfig ic,
|
||||
ICurrencyService cs,
|
||||
GamblingConfigService gss)
|
||||
: base(gss)
|
||||
{
|
||||
_images = data.LocalImages;
|
||||
_ic = ic;
|
||||
_images = images;
|
||||
_cs = cs;
|
||||
}
|
||||
|
||||
@@ -47,8 +53,8 @@ public partial class Gambling
|
||||
var imgs = new Image<Rgba32>[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var headsArr = _images.Heads[_rng.Next(0, _images.Heads.Count)];
|
||||
var tailsArr = _images.Tails[_rng.Next(0, _images.Tails.Count)];
|
||||
var headsArr = await _images.GetHeadsImageAsync();
|
||||
var tailsArr = await _images.GetTailsImageAsync();
|
||||
if (_rng.Next(0, 10) < 5)
|
||||
{
|
||||
imgs[i] = Image.Load(headsArr);
|
||||
@@ -94,7 +100,7 @@ public partial class Gambling
|
||||
|
||||
BetFlipGuess result;
|
||||
Uri imageToSend;
|
||||
var coins = _images.ImageUrls.Coins;
|
||||
var coins = _ic.Data.Coins;
|
||||
if (_rng.Next(0, 1000) <= 499)
|
||||
{
|
||||
imageToSend = coins.Heads[_rng.Next(0, coins.Heads.Length)];
|
||||
|
@@ -38,7 +38,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly NumberFormatInfo _enUsCulture;
|
||||
private readonly DownloadTracker _tracker;
|
||||
@@ -51,7 +50,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public Gambling(
|
||||
DbService db,
|
||||
ICurrencyService currency,
|
||||
IDataCache cache,
|
||||
DiscordSocketClient client,
|
||||
DownloadTracker tracker,
|
||||
GamblingConfigService configService,
|
||||
@@ -61,7 +59,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
{
|
||||
_db = db;
|
||||
_cs = currency;
|
||||
_cache = cache;
|
||||
_client = client;
|
||||
_bank = bank;
|
||||
_ps = ps;
|
||||
@@ -124,7 +121,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cache.AddTimelyClaim(ctx.User.Id, period) is { } rem)
|
||||
if (await _service.ClaimTimelyAsync(ctx.User.Id, period) is { } rem)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var relativeTag = TimestampTag.FromDateTime(now.Add(rem), TimestampTagStyles.Relative);
|
||||
@@ -145,7 +142,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
[OwnerOnly]
|
||||
public async partial Task TimelyReset()
|
||||
{
|
||||
_cache.RemoveAllTimelyClaims();
|
||||
await _service.RemoveAllTimelyClaimsAsync();
|
||||
await ReplyConfirmLocalizedAsync(strs.timely_reset);
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,13 @@
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Migrations;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Modules.Gambling.Common.Connect4;
|
||||
using NadekoBot.Modules.Gambling.Common.Slot;
|
||||
using NadekoBot.Modules.Gambling.Common.WheelOfFortune;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
@@ -22,7 +19,7 @@ public class GamblingService : INService, IReadyExecutor
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly Bot _bot;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly IBotCache _cache;
|
||||
private readonly GamblingConfigService _gss;
|
||||
|
||||
public GamblingService(
|
||||
@@ -30,7 +27,7 @@ public class GamblingService : INService, IReadyExecutor
|
||||
Bot bot,
|
||||
ICurrencyService cs,
|
||||
DiscordSocketClient client,
|
||||
IDataCache cache,
|
||||
IBotCache cache,
|
||||
GamblingConfigService gss)
|
||||
{
|
||||
_db = db;
|
||||
@@ -73,6 +70,7 @@ public class GamblingService : INService, IReadyExecutor
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly TypedKey<long> _curDecayKey = new("currency:last_decay");
|
||||
private async Task CurrencyDecayLoopAsync()
|
||||
{
|
||||
if (_bot.Client.ShardId != 0)
|
||||
@@ -88,11 +86,16 @@ public class GamblingService : INService, IReadyExecutor
|
||||
if (config.Decay.Percent is <= 0 or > 1 || maxDecay < 0)
|
||||
continue;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
var lastCurrencyDecay = _cache.GetLastCurrencyDecay();
|
||||
var result = await _cache.GetAsync(_curDecayKey);
|
||||
|
||||
if (DateTime.UtcNow - lastCurrencyDecay < TimeSpan.FromHours(config.Decay.HourInterval))
|
||||
if (result.TryPickT0(out var bin, out _)
|
||||
&& (now - DateTime.FromBinary(bin) < TimeSpan.FromHours(config.Decay.HourInterval)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.Information(@"Decaying users' currency - decay: {ConfigDecayPercent}%
|
||||
| max: {MaxDecay}
|
||||
@@ -115,8 +118,9 @@ public class GamblingService : INService, IReadyExecutor
|
||||
: old.CurrencyAmount - maxDecay
|
||||
});
|
||||
|
||||
_cache.SetLastCurrencyDecay();
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
await _cache.AddAsync(_curDecayKey, now.ToBinary());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -161,60 +165,100 @@ public class GamblingService : INService, IReadyExecutor
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
private static readonly TypedKey<EconomyResult> _ecoKey = new("nadeko:economy");
|
||||
|
||||
public async Task<EconomyResult> GetEconomyAsync()
|
||||
{
|
||||
if (_cache.TryGetEconomy(out var data))
|
||||
{
|
||||
try
|
||||
var data = await _cache.GetOrAddAsync(_ecoKey,
|
||||
async () =>
|
||||
{
|
||||
return JsonConvert.DeserializeObject<EconomyResult>(data);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
await using var uow = _db.GetDbContext();
|
||||
var cash = uow.DiscordUser.GetTotalCurrency();
|
||||
var onePercent = uow.DiscordUser.GetTopOnePercentCurrency(_client.CurrentUser.Id);
|
||||
decimal planted = uow.PlantedCurrency.AsQueryable().Sum(x => x.Amount);
|
||||
var waifus = uow.WaifuInfo.GetTotalValue();
|
||||
var bot = uow.DiscordUser.GetUserCurrency(_client.CurrentUser.Id);
|
||||
decimal bank = await uow.GetTable<BankUser>()
|
||||
.SumAsyncLinqToDB(x => x.Balance);
|
||||
|
||||
decimal cash;
|
||||
decimal onePercent;
|
||||
decimal planted;
|
||||
decimal waifus;
|
||||
decimal bank;
|
||||
long bot;
|
||||
var result = new EconomyResult
|
||||
{
|
||||
Cash = cash,
|
||||
Planted = planted,
|
||||
Bot = bot,
|
||||
Waifus = waifus,
|
||||
OnePercent = onePercent,
|
||||
Bank = bank
|
||||
};
|
||||
|
||||
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);
|
||||
bank = await uow.GetTable<BankUser>()
|
||||
.SumAsyncLinqToDB(x => x.Balance);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
TimeSpan.FromMinutes(3));
|
||||
|
||||
var result = new EconomyResult
|
||||
{
|
||||
Cash = cash,
|
||||
Planted = planted,
|
||||
Bot = bot,
|
||||
Waifus = waifus,
|
||||
OnePercent = onePercent,
|
||||
Bank = bank
|
||||
};
|
||||
|
||||
_cache.SetEconomy(JsonConvert.SerializeObject(result));
|
||||
return result;
|
||||
return data;
|
||||
}
|
||||
|
||||
public Task<WheelOfFortuneGame.Result> WheelOfFortuneSpinAsync(ulong userId, long bet)
|
||||
=> new WheelOfFortuneGame(userId, bet, _gss.Data, _cs).SpinAsync();
|
||||
|
||||
|
||||
public struct EconomyResult
|
||||
private static readonly SemaphoreSlim _timelyLock = new (1, 1);
|
||||
|
||||
private static TypedKey<Dictionary<ulong, long>> _timelyKey
|
||||
= new("timely:claims");
|
||||
public async Task<TimeSpan?> ClaimTimelyAsync(ulong userId, int period)
|
||||
{
|
||||
public decimal Cash { get; set; }
|
||||
public decimal Planted { get; set; }
|
||||
public decimal Waifus { get; set; }
|
||||
public decimal OnePercent { get; set; }
|
||||
public decimal Bank { get; set; }
|
||||
public long Bot { get; set; }
|
||||
if (period == 0)
|
||||
return null;
|
||||
|
||||
await _timelyLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
// get the dictionary from the cache or get a new one
|
||||
var dict = (await _cache.GetOrAddAsync(_timelyKey,
|
||||
() => Task.FromResult(new Dictionary<ulong, long>())))!;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var nowB = now.ToBinary();
|
||||
|
||||
// try to get users last claim
|
||||
if (!dict.TryGetValue(userId, out var lastB))
|
||||
lastB = dict[userId] = now.ToBinary();
|
||||
|
||||
var diff = now - DateTime.FromBinary(lastB);
|
||||
|
||||
// if its now, or too long ago => success
|
||||
if (lastB == nowB || diff > period.Hours())
|
||||
{
|
||||
// update the cache
|
||||
dict[userId] = nowB;
|
||||
await _cache.AddAsync(_timelyKey, dict);
|
||||
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise return the remaining time
|
||||
return period.Hours() - diff;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _timelyLock.WaitAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoveAllTimelyClaimsAsync()
|
||||
=> await _cache.RemoveAsync(_timelyKey);
|
||||
|
||||
|
||||
public readonly struct EconomyResult
|
||||
{
|
||||
public decimal Cash { get; init; }
|
||||
public decimal Planted { get; init; }
|
||||
public decimal Waifus { get; init; }
|
||||
public decimal OnePercent { get; init; }
|
||||
public decimal Bank { get; init; }
|
||||
public long Bot { get; init; }
|
||||
}
|
||||
}
|
@@ -34,7 +34,7 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
DbService db,
|
||||
CommandHandler cmd,
|
||||
IBotStrings strings,
|
||||
IDataCache cache,
|
||||
IImageCache images,
|
||||
FontProvider fonts,
|
||||
ICurrencyService cs,
|
||||
CommandHandler cmdHandler,
|
||||
@@ -43,7 +43,7 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
{
|
||||
_db = db;
|
||||
_strings = strings;
|
||||
_images = cache.LocalImages;
|
||||
_images = images;
|
||||
_fonts = fonts;
|
||||
_cs = cs;
|
||||
_cmdHandler = cmdHandler;
|
||||
@@ -110,30 +110,21 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
/// <param name="pass">Optional password to add to top left corner.</param>
|
||||
/// <param name="extension">Extension of the file, defaults to png</param>
|
||||
/// <returns>Stream of the currency image</returns>
|
||||
public Stream GetRandomCurrencyImage(string pass, out string extension)
|
||||
public async Task<(Stream, string)> GetRandomCurrencyImageAsync(string pass)
|
||||
{
|
||||
// get a random currency image bytes
|
||||
var rng = new NadekoRandom();
|
||||
var curImg = _images.Currency[rng.Next(0, _images.Currency.Count)];
|
||||
var curImg = await _images.GetCurrencyImageAsync();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pass))
|
||||
{
|
||||
// determine the extension
|
||||
using (_ = Image.Load(curImg, out var format))
|
||||
{
|
||||
extension = format.FileExtensions.FirstOrDefault() ?? "png";
|
||||
}
|
||||
using var load = _ = Image.Load(curImg, out var format);
|
||||
|
||||
// return the image
|
||||
return curImg.ToStream();
|
||||
return (curImg.ToStream(), 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 AddPassword(curImg, pass);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -214,10 +205,10 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
var pw = config.Generation.HasPassword ? GenerateCurrencyPassword().ToUpperInvariant() : null;
|
||||
|
||||
IUserMessage sent;
|
||||
await using (var stream = GetRandomCurrencyImage(pw, out var ext))
|
||||
{
|
||||
var (stream, ext) = await GetRandomCurrencyImageAsync(pw);
|
||||
|
||||
await using (stream)
|
||||
sent = await channel.SendFileAsync(stream, $"currency_image.{ext}", toSend);
|
||||
}
|
||||
|
||||
await AddPlantToDatabase(channel.GuildId,
|
||||
channel.Id,
|
||||
@@ -278,7 +269,7 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
if (amount > 0)
|
||||
// give the picked currency to the user
|
||||
await _cs.AddAsync(uid, amount, new("currency", "collect"));
|
||||
uow.SaveChanges();
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
try
|
||||
@@ -316,11 +307,14 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
msgToSend += " " + GetText(gid, strs.pick_sn(prefix));
|
||||
|
||||
//get the image
|
||||
await using var stream = GetRandomCurrencyImage(pass, out var ext);
|
||||
var (stream, ext) = await GetRandomCurrencyImageAsync(pass);
|
||||
// send it
|
||||
var msg = await ch.SendFileAsync(stream, $"img.{ext}", msgToSend);
|
||||
// return sent message's id (in order to be able to delete it when it's picked)
|
||||
return msg.Id;
|
||||
await using (stream)
|
||||
{
|
||||
var msg = await ch.SendFileAsync(stream, $"img.{ext}", msgToSend);
|
||||
// return sent message's id (in order to be able to delete it when it's picked)
|
||||
return msg.Id;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@@ -32,13 +32,13 @@ public partial class Gambling
|
||||
private readonly DbService _db;
|
||||
|
||||
public SlotCommands(
|
||||
IDataCache data,
|
||||
ImageCache images,
|
||||
FontProvider fonts,
|
||||
DbService db,
|
||||
GamblingConfigService gamb)
|
||||
: base(gamb)
|
||||
{
|
||||
_images = data.LocalImages;
|
||||
_images = images;
|
||||
_fonts = fonts;
|
||||
_db = db;
|
||||
}
|
||||
@@ -130,7 +130,8 @@ public partial class Gambling
|
||||
?? 0;
|
||||
}
|
||||
|
||||
using (var bgImage = Image.Load<Rgba32>(_images.SlotBackground, out _))
|
||||
var slotBg = await _images.GetSlotBgAsync();
|
||||
using (var bgImage = Image.Load<Rgba32>(slotBg, out _))
|
||||
{
|
||||
var numbers = new int[3];
|
||||
result.Rolls.CopyTo(numbers, 0);
|
||||
@@ -184,7 +185,7 @@ public partial class Gambling
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
using var img = Image.Load(_images.SlotEmojis[numbers[i]]);
|
||||
using var img = Image.Load(await _images.GetSlotEmojiAsync(numbers[i]));
|
||||
bgImage.Mutate(x => x.DrawImage(img, new Point(148 + (105 * i), 217), 1f));
|
||||
}
|
||||
|
||||
@@ -201,7 +202,7 @@ public partial class Gambling
|
||||
msg = GetText(strs.slot_jackpot(30));
|
||||
}
|
||||
|
||||
await using (var imgStream = bgImage.ToStream())
|
||||
await using (var imgStream = await bgImage.ToStreamAsync())
|
||||
{
|
||||
await ctx.Channel.SendFileAsync(imgStream,
|
||||
"result.png",
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db;
|
||||
@@ -14,7 +15,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly IDataCache _cache;
|
||||
private readonly IBotCache _cache;
|
||||
private readonly GamblingConfigService _gss;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly DiscordSocketClient _client;
|
||||
@@ -22,7 +23,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
public WaifuService(
|
||||
DbService db,
|
||||
ICurrencyService cs,
|
||||
IDataCache cache,
|
||||
IBotCache cache,
|
||||
GamblingConfigService gss,
|
||||
IBotCredentials creds,
|
||||
DiscordSocketClient client)
|
||||
@@ -236,8 +237,13 @@ public class WaifuService : INService, IReadyExecutor
|
||||
var newAff = target is null ? null : uow.GetOrCreateUser(target);
|
||||
if (w?.Affinity?.UserId == target?.Id)
|
||||
{
|
||||
return (null, false, null);
|
||||
}
|
||||
else if (!_cache.TryAddAffinityCooldown(user.Id, out remaining))
|
||||
|
||||
remaining = await _cache.GetRatelimitAsync(GetAffinityKey(user.Id),
|
||||
30.Minutes());
|
||||
|
||||
if (remaining is not null)
|
||||
{
|
||||
}
|
||||
else if (w is null)
|
||||
@@ -294,6 +300,12 @@ public class WaifuService : INService, IReadyExecutor
|
||||
return uow.WaifuInfo.GetWaifuUserId(ownerId, name);
|
||||
}
|
||||
|
||||
private static TypedKey<long> GetDivorceKey(ulong userId)
|
||||
=> new($"waifu:divorce_cd:{userId}");
|
||||
|
||||
private static TypedKey<long> GetAffinityKey(ulong userId)
|
||||
=> new($"waifu:affinity:{userId}");
|
||||
|
||||
public async Task<(WaifuInfo, DivorceResult, long, TimeSpan?)> DivorceWaifuAsync(IUser user, ulong targetId)
|
||||
{
|
||||
DivorceResult result;
|
||||
@@ -305,10 +317,15 @@ public class WaifuService : INService, IReadyExecutor
|
||||
w = uow.WaifuInfo.ByWaifuUserId(targetId);
|
||||
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
|
||||
{
|
||||
remaining = await _cache.GetRatelimitAsync(GetDivorceKey(user.Id), 6.Hours());
|
||||
if (remaining is TimeSpan rem)
|
||||
{
|
||||
result = DivorceResult.Cooldown;
|
||||
return (w, result, amount, rem);
|
||||
}
|
||||
|
||||
amount = w.Price / 2;
|
||||
|
||||
if (w.Affinity?.UserId == user.Id)
|
||||
@@ -486,13 +503,13 @@ public class WaifuService : INService, IReadyExecutor
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static readonly TypedKey<long> _waifuDecayKey = $"waifu:last_decay";
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
// only decay waifu values from shard 0
|
||||
if (_client.ShardId != 0)
|
||||
return;
|
||||
|
||||
var redisKey = $"{_creds.RedisKey()}_last_waifu_decay";
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
@@ -504,28 +521,31 @@ public class WaifuService : INService, IReadyExecutor
|
||||
if (multi is < 0f or > 1f || decayInterval < 0)
|
||||
continue;
|
||||
|
||||
var val = await _cache.Redis.GetDatabase().StringGetAsync(redisKey);
|
||||
if (val != default)
|
||||
var now = DateTime.UtcNow;
|
||||
var nowB = now.ToBinary();
|
||||
|
||||
var result = await _cache.GetAsync(_waifuDecayKey);
|
||||
|
||||
if (result.TryGetValue(out var val))
|
||||
{
|
||||
var lastDecay = DateTime.FromBinary((long)val);
|
||||
var lastDecay = DateTime.FromBinary(val);
|
||||
var toWait = decayInterval.Hours() - (DateTime.UtcNow - lastDecay);
|
||||
|
||||
if (toWait > 0.Hours())
|
||||
continue;
|
||||
}
|
||||
|
||||
await _cache.Redis.GetDatabase().StringSetAsync(redisKey, DateTime.UtcNow.ToBinary());
|
||||
await _cache.AddAsync(_waifuDecayKey, nowB);
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
|
||||
await uow.WaifuInfo
|
||||
await uow.GetTable<WaifuInfo>()
|
||||
.Where(x => x.Price > minPrice && x.ClaimerId == null)
|
||||
.UpdateAsync(old => new()
|
||||
{
|
||||
Price = (long)(old.Price * multi)
|
||||
});
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
Reference in New Issue
Block a user