diff --git a/src/EllieBot/Modules/Gambling/Gambling.cs b/src/EllieBot/Modules/Gambling/Gambling.cs index 5cb0b2e..6ce371b 100644 --- a/src/EllieBot/Modules/Gambling/Gambling.cs +++ b/src/EllieBot/Modules/Gambling/Gambling.cs @@ -14,6 +14,13 @@ using System.Text; using EllieBot.Modules.Gambling.Rps; using EllieBot.Common.TypeReaders; using EllieBot.Modules.Patronage; +using SixLabors.Fonts; +using SixLabors.Fonts.Unicode; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Color = SixLabors.ImageSharp.Color; namespace EllieBot.Modules.Gambling; @@ -211,6 +218,7 @@ public partial class Gambling : GamblingModule customId: "timely:" + _rng.Next(123456, 999999)), async (smc) => { + await smc.DeferAsync(); await ClaimTimely(); }); @@ -226,13 +234,65 @@ public partial class Gambling : GamblingModule return; } - if (Config.Timely.HasButton) + if (Config.Timely.ProtType == TimelyProt.Button) { var interaction = CreateTimelyInteraction(); var msg = await Response().Pending(strs.timely_button).Interaction(interaction).SendAsync(); await msg.DeleteAsync(); return; } + else if (Config.Timely.ProtType == TimelyProt.Captcha) + { + var password = _service.GeneratePassword(); + + var img = new Image(70, 35); + + var font = _fonts.NotoSans.CreateFont(30); + var outlinePen = new SolidPen(Color.Black, 1f); + var strikeoutRun = new RichTextRun + { + Start = 0, + End = password.GetGraphemeCount(), + Font = font, + StrikeoutPen = new SolidPen(Color.White, 3), + TextDecorations = TextDecorations.Strikeout + }; + // draw password on the image + img.Mutate(x => + { + x.DrawText(new RichTextOptions(font) + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + FallbackFontFamilies = _fonts.FallBackFonts, + Origin = new(35, 17), + TextRuns = [strikeoutRun] + }, + password, + Brushes.Solid(Color.White), + outlinePen); + }); + using var stream = await img.ToStreamAsync(); + var captcha = await Response() + // .Embed(_sender.CreateEmbed() + // .WithOkColor() + // .WithImageUrl("attachment://timely.png")) + .File(stream, "timely.png") + .SendAsync(); + try + { + var userInput = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id); + if (userInput?.ToLowerInvariant() != password?.ToLowerInvariant()) + { + return; + } + } + finally + { + _ = captcha.DeleteAsync(); + } + + } await ClaimTimely(); } diff --git a/src/EllieBot/Modules/Gambling/GamblingConfig.cs b/src/EllieBot/Modules/Gambling/GamblingConfig.cs index 7aa81b1..f8cd7fa 100644 --- a/src/EllieBot/Modules/Gambling/GamblingConfig.cs +++ b/src/EllieBot/Modules/Gambling/GamblingConfig.cs @@ -11,7 +11,7 @@ namespace EllieBot.Modules.Gambling.Common; public sealed partial class GamblingConfig : ICloneable { [Comment("""DO NOT CHANGE""")] - public int Version { get; set; } = 10; + public int Version { get; set; } = 11; [Comment("""Currency settings""")] public CurrencyConfig Currency { get; set; } @@ -119,9 +119,17 @@ public partial class TimelyConfig public int Cooldown { get; set; } = 24; [Comment(""" - Whether the users are required to type a password when they do timely. + How will timely be protected? + None, Button (users have to click the button) or Captcha (users have to type the captcha from an image) """)] - public bool HasButton { get; set; } = true; + public TimelyProt ProtType { get; set; } = TimelyProt.Button; +} + +public enum TimelyProt +{ + None, + Button, + Captcha } [Cloneable] diff --git a/src/EllieBot/Modules/Gambling/GamblingConfigService.cs b/src/EllieBot/Modules/Gambling/GamblingConfigService.cs index 9a82fdd..f3b9b5e 100644 --- a/src/EllieBot/Modules/Gambling/GamblingConfigService.cs +++ b/src/EllieBot/Modules/Gambling/GamblingConfigService.cs @@ -144,9 +144,9 @@ public sealed class GamblingConfigService : ConfigServiceBase ConfigPrinters.ToString, val => val >= 0); - AddParsedProp("timely.btn", - gs => gs.Timely.HasButton, - bool.TryParse, + AddParsedProp("timely.prot", + gs => gs.Timely.ProtType, + ConfigParsers.InsensitiveEnum, ConfigPrinters.ToString); Migrate(); @@ -189,11 +189,11 @@ public sealed class GamblingConfigService : ConfigServiceBase }); } - if (data.Version < 10) + if (data.Version < 11) { ModifyConfig(c => { - c.Version = 10; + c.Version = 11; }); } } diff --git a/src/EllieBot/Services/GrpcApi/GreetByeSvc.cs b/src/EllieBot/Services/GrpcApi/GreetByeSvc.cs index 0c34266..1c40645 100644 --- a/src/EllieBot/Services/GrpcApi/GreetByeSvc.cs +++ b/src/EllieBot/Services/GrpcApi/GreetByeSvc.cs @@ -17,10 +17,13 @@ public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, IGrpcSvc, IEService public ServerServiceDefinition Bind() => GrpcGreet.BindService(this); - private static GrpcGreetSettings ToConf(GreetSettings? conf) + private static GrpcGreetSettings ToConf(GreetSettings? conf, GreetType type) { if (conf is null) - return new GrpcGreetSettings(); + return new GrpcGreetSettings() + { + Type = (GrpcGreetType)type + }; return new GrpcGreetSettings() { @@ -35,9 +38,10 @@ public sealed class GreetByeSvc : GrpcGreet.GrpcGreetBase, IGrpcSvc, IEService { var guildId = request.GuildId; - var conf = await _gs.GetGreetSettingsAsync(guildId, (GreetType)request.Type); + var type = (GreetType)request.Type; + var conf = await _gs.GetGreetSettingsAsync(guildId, type); - return ToConf(conf); + return ToConf(conf, type); } public override async Task UpdateGreet(UpdateGreetRequest request, ServerCallContext context) diff --git a/src/EllieBot/data/gambling.yml b/src/EllieBot/data/gambling.yml index d1a81c8..f3d71b5 100644 --- a/src/EllieBot/data/gambling.yml +++ b/src/EllieBot/data/gambling.yml @@ -1,5 +1,5 @@ # DO NOT CHANGE -version: 10 +version: 11 # Currency settings currency: # What is the emoji/character which represents the currency @@ -56,8 +56,9 @@ timely: # How often (in hours) can users claim currency with .timely command # setting to 0 or less will disable this feature cooldown: 12 - # Whether the users are required to type a password when they do timely. - hasButton: true + # How will timely be protected? + # None, Button (users have to click the button) or Captcha (users have to type the captcha from an image) + protType: Button # How much will each user's owned currency decay over time. decay: # Percentage of user's current currency which will be deducted every 24h.