diff --git a/src/EllieBot/Modules/Gambling/Bank/BankCommands.cs b/src/EllieBot/Modules/Gambling/Bank/BankCommands.cs index 91388ef..ee66b6e 100644 --- a/src/EllieBot/Modules/Gambling/Bank/BankCommands.cs +++ b/src/EllieBot/Modules/Gambling/Bank/BankCommands.cs @@ -74,6 +74,27 @@ public partial class Gambling } } + [Cmd] + [OwnerOnly] + public async Task BankBalance([Leftover] IUser user) + { + var bal = await _bank.GetBalanceAsync(user.Id); + + var eb = _sender.CreateEmbed() + .WithOkColor() + .WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal)))); + + try + { + await Response().User(ctx.User).Embed(eb).SendAsync(); + await ctx.OkAsync(); + } + catch + { + await Response().Error(strs.cant_dm).SendAsync(); + } + } + private async Task BankTakeInternalAsync(long amount, ulong userId) { if (await _bank.TakeAsync(userId, amount)) diff --git a/src/EllieBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs b/src/EllieBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs index 5f7db11..19b1dc5 100644 --- a/src/EllieBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs +++ b/src/EllieBot/Modules/Gambling/Waifus/WaifuClaimCommands.cs @@ -3,6 +3,7 @@ using EllieBot.Modules.Gambling.Common; using EllieBot.Modules.Gambling.Common.Waifu; using EllieBot.Modules.Gambling.Services; using EllieBot.Db.Models; +using TwitchLib.Api.Helix.Models.Teams; namespace EllieBot.Modules.Gambling; @@ -21,8 +22,8 @@ public partial class Gambling { var price = _service.GetResetPrice(ctx.User); var embed = _sender.CreateEmbed() - .WithTitle(GetText(strs.waifu_reset_confirm)) - .WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price))))); + .WithTitle(GetText(strs.waifu_reset_confirm)) + .WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price))))); if (!await PromptUserConfirmAsync(embed)) return; @@ -307,24 +308,26 @@ public partial class Gambling fansStr = "-"; var embed = _sender.CreateEmbed() - .WithOkColor() - .WithTitle(GetText(strs.waifu) - + " " - + (wi.FullName ?? name ?? targetId.ToString()) - + " - \"the " - + _service.GetClaimTitle(wi.ClaimCount) - + "\"") - .AddField(GetText(strs.price), N(wi.Price), true) - .AddField(GetText(strs.claimed_by), wi.ClaimerName ?? nobody, true) - .AddField(GetText(strs.likes), wi.AffinityName ?? nobody, true) - .AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true) - .AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true) - .AddField("\u200B", "\u200B", true) - .AddField(GetText(strs.fans(fansList.Count)), fansStr, true) - .AddField($"Waifus ({wi.ClaimCount})", - wi.ClaimCount == 0 ? nobody : claimsStr, - true) - .AddField(GetText(strs.gifts), itemsStr, true); + .WithOkColor() + .WithTitle(GetText(strs.waifu) + + " " + + (wi.FullName ?? name ?? targetId.ToString()) + + " - \"the " + + _service.GetClaimTitle(wi.ClaimCount) + + "\"") + .AddField(GetText(strs.price), N(wi.Price), true) + .AddField(GetText(strs.claimed_by), wi.ClaimerName ?? nobody, true) + .AddField(GetText(strs.likes), wi.AffinityName ?? nobody, true) + .AddField(GetText(strs.changes_of_heart), + $"{wi.AffinityCount} - \"the {affInfo}\"", + true) + .AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true) + .AddField("\u200B", "\u200B", true) + .AddField(GetText(strs.fans(fansList.Count)), fansStr, true) + .AddField($"Waifus ({wi.ClaimCount})", + wi.ClaimCount == 0 ? nobody : claimsStr, + true) + .AddField(GetText(strs.gifts), itemsStr, true); await Response().Embed(embed).SendAsync(); } @@ -348,7 +351,7 @@ public partial class Gambling .Page((items, _) => { var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor(); - + items .ToList() .ForEach(x => embed.AddField( @@ -364,30 +367,27 @@ public partial class Gambling [Cmd] [RequireContext(ContextType.Guild)] [Priority(0)] - public async Task WaifuGift(string itemName, [Leftover] IUser waifu) + public async Task WaifuGift(MultipleWaifuItems items, [Leftover] IUser waifu) { if (waifu.Id == ctx.User.Id) return; - var allItems = _service.GetWaifuItems(); - var item = allItems.FirstOrDefault(x => x.Name.ToLowerInvariant() == itemName.ToLowerInvariant()); - if (item is null) - { - await Response().Error(strs.waifu_gift_not_exist).SendAsync(); - return; - } - - var sucess = await _service.GiftWaifuAsync(ctx.User, waifu, item); + var sucess = await _service.GiftWaifuAsync(ctx.User, waifu, items.Item, items.Count); if (sucess) { await Response() - .Confirm(strs.waifu_gift(Format.Bold(item + " " + item.ItemEmoji), + .Confirm(strs.waifu_gift(Format.Bold($"{GetCountString(items)}{items.Item} {items.Item.ItemEmoji}"), Format.Bold(waifu.ToString()))) .SendAsync(); } else await Response().Error(strs.not_enough(CurrencySign)).SendAsync(); } + + private static string GetCountString(MultipleWaifuItems items) + => items.Count > 1 + ? $"{items.Count}x " + : string.Empty; } } \ No newline at end of file diff --git a/src/EllieBot/Modules/Gambling/Waifus/WaifuService.cs b/src/EllieBot/Modules/Gambling/Waifus/WaifuService.cs index dded8a9..ab5e021 100644 --- a/src/EllieBot/Modules/Gambling/Waifus/WaifuService.cs +++ b/src/EllieBot/Modules/Gambling/Waifus/WaifuService.cs @@ -7,6 +7,7 @@ using EllieBot.Db; using EllieBot.Db.Models; using EllieBot.Modules.Gambling.Common; using EllieBot.Modules.Gambling.Common.Waifu; +using SixLabors.ImageSharp; namespace EllieBot.Modules.Gambling.Services; @@ -89,9 +90,14 @@ public class WaifuService : IEService, IReadyExecutor if (waifu is null) return settings.Waifu.MinPrice; - var divorces = uow.Set().Count(x - => x.Old != null && x.Old.UserId == user.Id && x.UpdateType == WaifuUpdateType.Claimed && x.New == null); - var affs = uow.Set().AsQueryable() + var divorces = uow.Set() + .Count(x + => x.Old != null + && x.Old.UserId == user.Id + && x.UpdateType == WaifuUpdateType.Claimed + && x.New == null); + var affs = uow.Set() + .AsQueryable() .Where(w => w.User.UserId == user.Id && w.UpdateType == WaifuUpdateType.AffinityChanged && w.New != null) @@ -110,12 +116,14 @@ public class WaifuService : IEService, IReadyExecutor if (!await _cs.RemoveAsync(user.Id, price, new("waifu", "reset"))) return false; - var affs = uow.Set().AsQueryable() + var affs = uow.Set() + .AsQueryable() .Where(w => w.User.UserId == user.Id && w.UpdateType == WaifuUpdateType.AffinityChanged && w.New != null); - var divorces = uow.Set().AsQueryable() + var divorces = uow.Set() + .AsQueryable() .Where(x => x.Old != null && x.Old.UserId == user.Id && x.UpdateType == WaifuUpdateType.Claimed @@ -158,20 +166,22 @@ public class WaifuService : IEService, IReadyExecutor result = WaifuClaimResult.NotEnoughFunds; else { - uow.Set().Add(w = new() - { - Waifu = waifu, - Claimer = claimer, - Affinity = null, - Price = amount - }); - uow.Set().Add(new() - { - User = waifu, - Old = null, - New = claimer, - UpdateType = WaifuUpdateType.Claimed - }); + uow.Set() + .Add(w = new() + { + Waifu = waifu, + Claimer = claimer, + Affinity = null, + Price = amount + }); + uow.Set() + .Add(new() + { + User = waifu, + Old = null, + New = claimer, + UpdateType = WaifuUpdateType.Claimed + }); result = WaifuClaimResult.Success; } } @@ -186,13 +196,14 @@ public class WaifuService : IEService, IReadyExecutor w.Price = amount + (amount / 4); result = WaifuClaimResult.Success; - uow.Set().Add(new() - { - User = w.Waifu, - Old = oldClaimer, - New = w.Claimer, - UpdateType = WaifuUpdateType.Claimed - }); + uow.Set() + .Add(new() + { + User = w.Waifu, + Old = oldClaimer, + New = w.Claimer, + UpdateType = WaifuUpdateType.Claimed + }); } } else if (amount >= w.Price * settings.Waifu.Multipliers.NormalClaim) // if no affinity @@ -206,13 +217,14 @@ public class WaifuService : IEService, IReadyExecutor w.Price = amount; result = WaifuClaimResult.Success; - uow.Set().Add(new() - { - User = w.Waifu, - Old = oldClaimer, - New = w.Claimer, - UpdateType = WaifuUpdateType.Claimed - }); + uow.Set() + .Add(new() + { + User = w.Waifu, + Old = oldClaimer, + New = w.Claimer, + UpdateType = WaifuUpdateType.Claimed + }); } } else @@ -241,29 +253,31 @@ public class WaifuService : IEService, IReadyExecutor remaining = await _cache.GetRatelimitAsync(GetAffinityKey(user.Id), 30.Minutes()); - + if (remaining is not null) { } else if (w is null) { var thisUser = uow.GetOrCreateUser(user); - uow.Set().Add(new() - { - Affinity = newAff, - Waifu = thisUser, - Price = 1, - Claimer = null - }); + uow.Set() + .Add(new() + { + Affinity = newAff, + Waifu = thisUser, + Price = 1, + Claimer = null + }); success = true; - uow.Set().Add(new() - { - User = thisUser, - Old = null, - New = newAff, - UpdateType = WaifuUpdateType.AffinityChanged - }); + uow.Set() + .Add(new() + { + User = thisUser, + Old = null, + New = newAff, + UpdateType = WaifuUpdateType.AffinityChanged + }); } else { @@ -272,13 +286,14 @@ public class WaifuService : IEService, IReadyExecutor w.Affinity = newAff; success = true; - uow.Set().Add(new() - { - User = w.Waifu, - Old = oldAff, - New = newAff, - UpdateType = WaifuUpdateType.AffinityChanged - }); + uow.Set() + .Add(new() + { + User = w.Waifu, + Old = oldAff, + New = newAff, + UpdateType = WaifuUpdateType.AffinityChanged + }); } await uow.SaveChangesAsync(); @@ -301,10 +316,10 @@ public class WaifuService : IEService, IReadyExecutor private static TypedKey GetDivorceKey(ulong userId) => new($"waifu:divorce_cd:{userId}"); - + private static TypedKey GetAffinityKey(ulong userId) => new($"waifu:affinity:{userId}"); - + public async Task<(WaifuInfo, DivorceResult, long, TimeSpan?)> DivorceWaifuAsync(IUser user, ulong targetId) { DivorceResult result; @@ -343,13 +358,14 @@ public class WaifuService : IEService, IReadyExecutor var oldClaimer = w.Claimer; w.Claimer = null; - uow.Set().Add(new() - { - User = w.Waifu, - Old = oldClaimer, - New = null, - UpdateType = WaifuUpdateType.Claimed - }); + uow.Set() + .Add(new() + { + User = w.Waifu, + Old = oldClaimer, + New = null, + UpdateType = WaifuUpdateType.Claimed + }); } await uow.SaveChangesAsync(); @@ -358,40 +374,54 @@ public class WaifuService : IEService, IReadyExecutor return (w, result, amount, remaining); } - public async Task GiftWaifuAsync(IUser from, IUser giftedWaifu, WaifuItemModel itemObj) + public async Task GiftWaifuAsync( + IUser from, + IUser giftedWaifu, + WaifuItemModel itemObj, + int count) { - if (!await _cs.RemoveAsync(from, itemObj.Price, new("waifu", "item"))) + ArgumentOutOfRangeException.ThrowIfLessThan(count, 1, nameof(count)); + + if (!await _cs.RemoveAsync(from, itemObj.Price * count, new("waifu", "item"))) return false; + var totalValue = itemObj.Price * count; + await using var uow = _db.GetDbContext(); - var w = uow.Set().ByWaifuUserId(giftedWaifu.Id, set => set.Include(x => x.Items).Include(x => x.Claimer)); + var w = uow.Set() + .ByWaifuUserId(giftedWaifu.Id, + set => set + .Include(x => x.Items) + .Include(x => x.Claimer)); if (w is null) { - uow.Set().Add(w = new() - { - Affinity = null, - Claimer = null, - Price = 1, - Waifu = uow.GetOrCreateUser(giftedWaifu) - }); + uow.Set() + .Add(w = new() + { + Affinity = null, + Claimer = null, + Price = 1, + Waifu = uow.GetOrCreateUser(giftedWaifu) + }); } if (!itemObj.Negative) { - w.Items.Add(new() - { - Name = itemObj.Name.ToLowerInvariant(), - ItemEmoji = itemObj.ItemEmoji - }); + w.Items.AddRange(Enumerable.Range(0, count) + .Select((_) => new WaifuItem() + { + Name = itemObj.Name.ToLowerInvariant(), + ItemEmoji = itemObj.ItemEmoji + })); if (w.Claimer?.UserId == from.Id) - w.Price += (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.GiftEffect); + w.Price += (long)(totalValue * _gss.Data.Waifu.Multipliers.GiftEffect); else - w.Price += itemObj.Price / 2; + w.Price += totalValue / 2; } else { - w.Price -= (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.NegativeGiftEffect); + w.Price -= (long)(totalValue * _gss.Data.Waifu.Multipliers.NegativeGiftEffect); if (w.Price < 1) w.Price = 1; } @@ -492,6 +522,7 @@ public class WaifuService : IEService, IReadyExecutor } private static readonly TypedKey _waifuDecayKey = $"waifu:last_decay"; + public async Task OnReadyAsync() { // only decay waifu values from shard 0 @@ -513,7 +544,7 @@ public class WaifuService : IEService, IReadyExecutor var nowB = now.ToBinary(); var result = await _cache.GetAsync(_waifuDecayKey); - + if (result.TryGetValue(out var val)) { var lastDecay = DateTime.FromBinary(val); @@ -533,7 +564,6 @@ public class WaifuService : IEService, IReadyExecutor { Price = (long)(old.Price * multi) }); - } catch (Exception ex) { @@ -550,33 +580,35 @@ public class WaifuService : IEService, IReadyExecutor { await using var ctx = _db.GetDbContext(); return await ctx.GetTable() - .Where(x => ctx.GetTable() - .Where(wi => wi.ClaimerId == waifuId) - .Select(wi => wi.WaifuId) - .Contains(x.Id)) - .Select(x => $"{x.Username}#{x.Discriminator}") - .ToListAsyncEF(); + .Where(x => ctx.GetTable() + .Where(wi => wi.ClaimerId == waifuId) + .Select(wi => wi.WaifuId) + .Contains(x.Id)) + .Select(x => $"{x.Username}#{x.Discriminator}") + .ToListAsyncEF(); } + public async Task> GetFansNames(int waifuId) { await using var ctx = _db.GetDbContext(); return await ctx.GetTable() - .Where(x => ctx.GetTable() - .Where(wi => wi.AffinityId == waifuId) - .Select(wi => wi.WaifuId) - .Contains(x.Id)) - .Select(x => $"{x.Username}#{x.Discriminator}") - .ToListAsyncEF(); + .Where(x => ctx.GetTable() + .Where(wi => wi.AffinityId == waifuId) + .Select(wi => wi.WaifuId) + .Contains(x.Id)) + .Select(x => $"{x.Username}#{x.Discriminator}") + .ToListAsyncEF(); } public async Task> GetItems(int waifuId) { await using var ctx = _db.GetDbContext(); return await ctx.GetTable() - .Where(x => x.WaifuInfoId == ctx.GetTable() - .Where(x => x.WaifuId == waifuId) - .Select(x => x.Id) - .FirstOrDefault()) - .ToListAsyncEF(); + .Where(x => x.WaifuInfoId + == ctx.GetTable() + .Where(x => x.WaifuId == waifuId) + .Select(x => x.Id) + .FirstOrDefault()) + .ToListAsyncEF(); } } \ No newline at end of file diff --git a/src/EllieBot/Modules/Gambling/Waifus/_common/MultipleWaifuItems.cs b/src/EllieBot/Modules/Gambling/Waifus/_common/MultipleWaifuItems.cs new file mode 100644 index 0000000..63b5742 --- /dev/null +++ b/src/EllieBot/Modules/Gambling/Waifus/_common/MultipleWaifuItems.cs @@ -0,0 +1,6 @@ +#nullable disable +using EllieBot.Modules.Gambling.Common; + +namespace EllieBot.Modules.Gambling; + +public record class MultipleWaifuItems(int Count, WaifuItemModel Item); \ No newline at end of file diff --git a/src/EllieBot/Modules/Gambling/Waifus/_common/MultipleWaifuItemsTypeReader.cs b/src/EllieBot/Modules/Gambling/Waifus/_common/MultipleWaifuItemsTypeReader.cs new file mode 100644 index 0000000..bac72c1 --- /dev/null +++ b/src/EllieBot/Modules/Gambling/Waifus/_common/MultipleWaifuItemsTypeReader.cs @@ -0,0 +1,47 @@ +#nullable disable +using EllieBot.Common.TypeReaders; +using EllieBot.Modules.Gambling.Services; +using System.Text.RegularExpressions; + +namespace EllieBot.Modules.Gambling; + +public partial class MultipleWaifuItemsTypeReader : EllieTypeReader +{ + private readonly WaifuService _service; + + [GeneratedRegex(@"(?:(?\d+)[x*])?(?.+)")] + private static partial Regex ItemRegex(); + + public MultipleWaifuItemsTypeReader(WaifuService service) + { + _service = service; + } + public override ValueTask> ReadAsync(ICommandContext ctx, string input) + { + input = input.ToLowerInvariant(); + var match = ItemRegex().Match(input); + if (!match.Success) + { + return new(Discord.Commands.TypeReaderResult.FromError(CommandError.ParseFailed, "Invalid input.")); + } + + var count = 1; + if (match.Groups["count"].Success) + { + if (!int.TryParse(match.Groups["count"].Value, out count) || count < 1) + { + return new(Discord.Commands.TypeReaderResult.FromError(CommandError.ParseFailed, "Invalid count.")); + } + } + + var itemName = match.Groups["item"].Value?.ToLowerInvariant(); + var allItems = _service.GetWaifuItems(); + var item = allItems.FirstOrDefault(x => x.Name.ToLowerInvariant() == itemName); + if (item is null) + { + return new(Discord.Commands.TypeReaderResult.FromError(CommandError.ParseFailed, "Waifu gift does not exist.")); + } + + return new(Discord.Commands.TypeReaderResult.FromSuccess(new MultipleWaifuItems(count, item))); + } +}