From 7e9137c35cf078b7d67bfe653eed54ffc3e36caa Mon Sep 17 00:00:00 2001 From: barelyprofessional <150058423+barelyprofessional@users.noreply.github.com> Date: Sat, 15 Feb 2025 23:31:26 +0800 Subject: [PATCH] Added a generic interface for retrieving images. Where multiple images exist, it'll retrieve the least seen. If there are sufficient images to work with, it'll randomly pick from a subset of the least seen to make it less predictable what's going to show up. --- KfChatDotNetBot/ApplicationDbContext.cs | 1 + KfChatDotNetBot/Commands/AdminCommands.cs | 180 ----------- KfChatDotNetBot/Commands/ImageCommands.cs | 159 ++++++++++ KfChatDotNetBot/Commands/MemeCommands.cs | 118 +------ .../20250207015155_Images.Designer.cs | 291 ++++++++++++++++++ .../Migrations/20250207015155_Images.cs | 48 +++ .../ApplicationDbContextModelSnapshot.cs | 22 ++ .../Models/DbModels/ImageDbModel.cs | 9 + KfChatDotNetBot/Program.cs | 2 + KfChatDotNetBot/Settings/BuiltIn.cs | 109 +++++++ 10 files changed, 642 insertions(+), 297 deletions(-) create mode 100644 KfChatDotNetBot/Commands/ImageCommands.cs create mode 100644 KfChatDotNetBot/Migrations/20250207015155_Images.Designer.cs create mode 100644 KfChatDotNetBot/Migrations/20250207015155_Images.cs create mode 100644 KfChatDotNetBot/Models/DbModels/ImageDbModel.cs diff --git a/KfChatDotNetBot/ApplicationDbContext.cs b/KfChatDotNetBot/ApplicationDbContext.cs index f74722c..e56f1b0 100644 --- a/KfChatDotNetBot/ApplicationDbContext.cs +++ b/KfChatDotNetBot/ApplicationDbContext.cs @@ -17,4 +17,5 @@ public class ApplicationDbContext : DbContext public DbSet RainbetBets { get; set; } public DbSet TwitchViewCounts { get; set; } public DbSet ChipsggBets { get; set; } + public DbSet Images { get; set; } } \ No newline at end of file diff --git a/KfChatDotNetBot/Commands/AdminCommands.cs b/KfChatDotNetBot/Commands/AdminCommands.cs index 5ea9b9e..8889f91 100644 --- a/KfChatDotNetBot/Commands/AdminCommands.cs +++ b/KfChatDotNetBot/Commands/AdminCommands.cs @@ -37,186 +37,6 @@ public class SetRoleCommand : ICommand } } -public class GmKasinoAddCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^admin gmkasino add (?.+)$") - ]; - - public string? HelpText => "Add an image to the gmkasino image list"; - public UserRight RequiredRight => UserRight.TrueAndHonest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGmKasinoImageRotation)).JsonDeserialize>(); - if (images == null) - { - await botInstance.SendChatMessageAsync("Images list was null", true); - return; - } - var newImage = arguments["image"].Value; - if (images.Contains(newImage)) - { - await botInstance.SendChatMessageAsync("Image is already in the list", true); - return; - } - - images.Add(newImage); - await Helpers.SetValueAsJsonObject(BuiltIn.Keys.BotGmKasinoImageRotation, images); - await botInstance.SendChatMessageAsync("Updated list of images", true); - } -} - -public class GmKasinoRemoveCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^admin gmkasino remove (?.+)$") - ]; - - public string? HelpText => "Remove an image in the gmkasino image list"; - public UserRight RequiredRight => UserRight.TrueAndHonest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGmKasinoImageRotation)).JsonDeserialize>(); - if (images == null) - { - await botInstance.SendChatMessageAsync("Images list was null", true); - return; - } - var targetImage = arguments["image"].Value; - if (!images.Contains(targetImage)) - { - await botInstance.SendChatMessageAsync("Image is not in the list", true); - return; - } - - images.Remove(targetImage); - await Helpers.SetValueAsJsonObject(BuiltIn.Keys.BotGmKasinoImageRotation, images); - await botInstance.SendChatMessageAsync("Updated list of images", true); - } -} - -public class GmKasinoListCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^admin gmkasino list$") - ]; - - public string? HelpText => "Dump out the list of images for gmkasino"; - public UserRight RequiredRight => UserRight.TrueAndHonest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGmKasinoImageRotation)).JsonDeserialize>(); - if (images == null) - { - await botInstance.SendChatMessageAsync("Images list was null", true); - return; - } - - var result = "List of images:"; - var i = 0; - foreach (var image in images) - { - i++; - result += $"[br]{i}: {image}"; - } - - await botInstance.SendChatMessagesAsync(result.FancySplitMessage(partSeparator: "[br]"), true); - } -} - -public class GnKasinoAddCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^admin gnkasino add (?.+)$") - ]; - - public string? HelpText => "Add an image to the gnkasino image list"; - public UserRight RequiredRight => UserRight.TrueAndHonest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGnKasinoImageRotation)).JsonDeserialize>(); - if (images == null) - { - await botInstance.SendChatMessageAsync("Images list was null", true); - return; - } - var newImage = arguments["image"].Value; - if (images.Contains(newImage)) - { - await botInstance.SendChatMessageAsync("Image is already in the list", true); - return; - } - - images.Add(newImage); - await Helpers.SetValueAsJsonObject(BuiltIn.Keys.BotGnKasinoImageRotation, images); - await botInstance.SendChatMessageAsync("Updated list of images", true); - } -} - -public class GnKasinoRemoveCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^admin gnkasino remove (?.+)$") - ]; - - public string? HelpText => "Remove an image in the gnkasino image list"; - public UserRight RequiredRight => UserRight.TrueAndHonest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGnKasinoImageRotation)).JsonDeserialize>(); - if (images == null) - { - await botInstance.SendChatMessageAsync("Images list was null", true); - return; - } - var targetImage = arguments["image"].Value; - if (!images.Contains(targetImage)) - { - await botInstance.SendChatMessageAsync("Image is not in the list", true); - return; - } - - images.Remove(targetImage); - await Helpers.SetValueAsJsonObject(BuiltIn.Keys.BotGnKasinoImageRotation, images); - await botInstance.SendChatMessageAsync("Updated list of images", true); - } -} - -public class GnKasinoListCommand : ICommand -{ - public List Patterns => [ - new Regex(@"^admin gnkasino list$") - ]; - - public string? HelpText => "Dump out the list of images for gnkasino"; - public UserRight RequiredRight => UserRight.TrueAndHonest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGnKasinoImageRotation)).JsonDeserialize>(); - if (images == null) - { - await botInstance.SendChatMessageAsync("Images list was null", true); - return; - } - - var result = "List of images:"; - var i = 0; - foreach (var image in images) - { - i++; - result += $"[br]{i}: {image}"; - } - - await botInstance.SendChatMessagesAsync(result.FancySplitMessage(partSeparator: "[br]"), true); - } -} - public class ToggleLiveStatusAdminCommand : ICommand { public List Patterns => [ diff --git a/KfChatDotNetBot/Commands/ImageCommands.cs b/KfChatDotNetBot/Commands/ImageCommands.cs new file mode 100644 index 0000000..ecfa2dd --- /dev/null +++ b/KfChatDotNetBot/Commands/ImageCommands.cs @@ -0,0 +1,159 @@ +using System.Text.RegularExpressions; +using KfChatDotNetBot.Models.DbModels; +using KfChatDotNetBot.Services; +using KfChatDotNetBot.Settings; +using KfChatDotNetWsClient.Models.Events; +using Microsoft.EntityFrameworkCore; + +namespace KfChatDotNetBot.Commands; + +public class AddImageCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^admin image (?\w+) add (?.+)$"), + new Regex(@"^admin images (?\w+) add (?.+)$") + ]; + public string? HelpText => "Add an image to the image rotation specified"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + await using var db = new ApplicationDbContext(); + var imageKeys = (await Helpers.GetValue(BuiltIn.Keys.BotImageAcceptableKeys)).JsonDeserialize>(); + if (imageKeys == null) throw new InvalidOperationException($"{BuiltIn.Keys.BotImageAcceptableKeys} was null"); + var key = arguments["key"].Value; + var url = arguments["url"].Value; + if (!imageKeys.Contains(key)) + { + await botInstance.SendChatMessageAsync( + $"Key you specified is not supported. Available keys are: {string.Join(' ', imageKeys)}", true); + return; + } + + if (await db.Images.AnyAsync(i => i.Key == key && i.Url == url, ctx)) + { + await botInstance.SendChatMessageAsync("This image already exists in the database with this key", true); + return; + } + + await db.Images.AddAsync(new ImageDbModel { Key = key, Url = url, LastSeen = DateTimeOffset.MinValue }, ctx); + await db.SaveChangesAsync(ctx); + await botInstance.SendChatMessageAsync("Added image to database", true); + } +} + +public class RemoveImageCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^admin image (?\w+) remove (?.+)$"), + new Regex(@"^admin images (?\w+) remove (?.+)$"), + new Regex(@"^admin image (?\w+) delete (?.+)$"), + new Regex(@"^admin images (?\w+) delete (?.+)$") + ]; + public string? HelpText => "Remove an image from the image rotation specified"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + await using var db = new ApplicationDbContext(); + var imageKeys = (await Helpers.GetValue(BuiltIn.Keys.BotImageAcceptableKeys)).JsonDeserialize>(); + if (imageKeys == null) throw new InvalidOperationException($"{BuiltIn.Keys.BotImageAcceptableKeys} was null"); + var key = arguments["key"].Value; + var url = arguments["url"].Value; + if (!imageKeys.Contains(key)) + { + await botInstance.SendChatMessageAsync( + $"Key you specified is not supported. Available keys are: {string.Join(' ', imageKeys)}", true); + return; + } + + var image = await db.Images.FirstOrDefaultAsync(i => i.Key == key && i.Url == url, ctx); + if (image == null) + { + await botInstance.SendChatMessageAsync("This image isn't in the database with this key", true); + return; + } + + db.Images.Remove(image); + await db.SaveChangesAsync(ctx); + await botInstance.SendChatMessageAsync("Removed image from database", true); + } +} + +public class ListImageCommand : ICommand +{ + public List Patterns => [ + new Regex(@"^admin image (?\w+) list$"), + new Regex(@"^admin images (?\w+) list$") + ]; + public string? HelpText => "Remove an image from the image rotation specified"; + public UserRight RequiredRight => UserRight.TrueAndHonest; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + await using var db = new ApplicationDbContext(); + var imageKeys = (await Helpers.GetValue(BuiltIn.Keys.BotImageAcceptableKeys)).JsonDeserialize>(); + if (imageKeys == null) throw new InvalidOperationException($"{BuiltIn.Keys.BotImageAcceptableKeys} was null"); + var key = arguments["key"].Value; + if (!imageKeys.Contains(key)) + { + await botInstance.SendChatMessageAsync( + $"Key you specified is not supported. Available keys are: {string.Join(' ', imageKeys)}", true); + return; + } + + var images = db.Images.Where(i => i.Key == key); + var i = 0; + var result = $"List of images for {key}:"; + foreach (var image in images) + { + i++; + result += $"[br]{i}: {image.Url}"; + } + + await botInstance.SendChatMessagesAsync(result.FancySplitMessage(partSeparator: "[br]"), + bypassSeshDetect: true); + } +} + +[AllowAdditionalMatches] +public class GetRandomImage : ICommand +{ + public List Patterns => [ + new Regex(@"^(?\w+)") + ]; + public string? HelpText => "Get a random image"; + public UserRight RequiredRight => UserRight.Loser; + public TimeSpan Timeout => TimeSpan.FromSeconds(10); + + public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, + CancellationToken ctx) + { + await using var db = new ApplicationDbContext(); + var key = arguments["key"].Value.ToLower(); + var images = db.Images.Where(i => i.Key == key); + if (!await images.AnyAsync(ctx)) return; + var divideBy = (await Helpers.GetValue(BuiltIn.Keys.BotImageRandomSliceDivideBy)).ToType(); + var limit = 1; + var count = await images.CountAsync(ctx); + if (count > divideBy) + { + limit = count / divideBy; + } + + // EF with SQLite can't sort on dates as it's just TEXT + var selection = (await images.ToListAsync(ctx)).OrderBy(i => i.LastSeen).Take(limit).ToList(); + // MaxValue is never returned by Next so you don't need to -1 for indexing + var image = selection[new Random().Next(0, selection.Count)]; + image.LastSeen = DateTimeOffset.UtcNow; + db.Images.Update(image); + await db.SaveChangesAsync(ctx); + await botInstance.SendChatMessageAsync($"[img]{image.Url}[/img]", true); + } +} \ No newline at end of file diff --git a/KfChatDotNetBot/Commands/MemeCommands.cs b/KfChatDotNetBot/Commands/MemeCommands.cs index 8b8dc8d..f96b234 100644 --- a/KfChatDotNetBot/Commands/MemeCommands.cs +++ b/KfChatDotNetBot/Commands/MemeCommands.cs @@ -36,64 +36,6 @@ public class TwistedCommand : ICommand } } -public class HelpMeCommand : ICommand -{ - public List Patterns => [new Regex("^helpme")]; - public string? HelpText => "Somebody please help me"; - public UserRight RequiredRight => UserRight.Guest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - // ReSharper disable once StringLiteralTypo - await botInstance.SendChatMessageAsync("[img]https://i.postimg.cc/fTw6tGWZ/ineedmoneydumbfuck.png[/img]", true); - } -} - -public class SentCommand : ICommand -{ - public List Patterns => [new Regex("^sent$")]; - public string? HelpText => "Sent love"; - public UserRight RequiredRight => UserRight.Guest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - // ReSharper disable once StringLiteralTypo - await botInstance.SendChatMessageAsync("[img]https://i.ibb.co/GHq7hb1/4373-g-N5-HEH2-Hkc.png[/img]", true); - } -} - -public class GmKasinoCommand : ICommand -{ - public List Patterns => [new Regex("^gmkasino")]; - public string? HelpText => "Good Morning Kasino"; - public UserRight RequiredRight => UserRight.Guest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGmKasinoImageRotation)).JsonDeserialize>(); - if (images == null) return; - var random = new Random(); - var image = images[random.Next(images.Count)]; - await botInstance.SendChatMessageAsync($"[img]{image}[/img]", true); - } -} - -public class GnKasinoCommand : ICommand -{ - public List Patterns => [new Regex("^gnkasino")]; - public string? HelpText => "Good Night, Kasino"; - public UserRight RequiredRight => UserRight.Loser; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var images = (await Helpers.GetValue(BuiltIn.Keys.BotGnKasinoImageRotation)).JsonDeserialize>(); - if (images == null) return; - var random = new Random(); - var image = images[random.Next(images.Count)]; - await botInstance.SendChatMessageAsync($"[img]{image}[/img]", true); - } -} - public class CrackedCommand : ICommand { public List Patterns => [ @@ -117,51 +59,6 @@ public class CrackedCommand : ICommand } } -public class WinmanjackCommand : ICommand -{ - public List Patterns => [ - new Regex("^winmanjack") - ]; - public string? HelpText => "winmanjack.jpg"; - public UserRight RequiredRight => UserRight.Loser; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var image = await Helpers.GetValue(BuiltIn.Keys.WinmanjackImgUrl); - await botInstance.SendChatMessageAsync($"[img]{image.Value}[/img]", true); - } -} - -public class PraygeCommand : ICommand -{ - public List Patterns => [ - new Regex("^prayge") - ]; - public string? HelpText => "prayge.jpg"; - public UserRight RequiredRight => UserRight.Loser; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var image = await Helpers.GetValue(BuiltIn.Keys.BotPraygeImgUrl); - await botInstance.SendChatMessageAsync($"[img]{image.Value}[/img]", true); - } -} - -public class CrackpipeCommand : ICommand -{ - public List Patterns => [ - new Regex("^crackpipe") - ]; - public string? HelpText => "crackpipe.gif"; - public UserRight RequiredRight => UserRight.Loser; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - var image = await Helpers.GetValue(BuiltIn.Keys.BotCrackpipeImgUrl); - await botInstance.SendChatMessageAsync($"[img]{image.Value}[/img]", true); - } -} - public class CleanCommand : ICommand { public List Patterns => [ @@ -183,7 +80,7 @@ public class CleanCommand : ICommand } } -public class RehbCommand : ICommand +public class RehabCommand : ICommand { public List Patterns => [ new Regex("^rehab") @@ -320,19 +217,6 @@ public class JailCommand : ICommand } } -public class BassmanJackCommand : ICommand -{ - public List Patterns => [new Regex("^bassmanjack")]; - public string? HelpText => "Bassman image"; - public UserRight RequiredRight => UserRight.Guest; - public TimeSpan Timeout => TimeSpan.FromSeconds(10); - public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) - { - // ReSharper disable once StringLiteralTypo - await botInstance.SendChatMessageAsync("[img]https://i.postimg.cc/SRstzMQt/boss-soy-koi.gif[/img]", true); - } -} - public class LastStreamCommand : ICommand { public List Patterns => [new Regex("^laststream")]; diff --git a/KfChatDotNetBot/Migrations/20250207015155_Images.Designer.cs b/KfChatDotNetBot/Migrations/20250207015155_Images.Designer.cs new file mode 100644 index 0000000..b4b15fd --- /dev/null +++ b/KfChatDotNetBot/Migrations/20250207015155_Images.Designer.cs @@ -0,0 +1,291 @@ +// +using System; +using KfChatDotNetBot; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace KfChatDotNetBot.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250207015155_Images")] + partial class Images + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.1"); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.ChipsggBetDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Amount") + .HasColumnType("REAL"); + + b.Property("BetId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CurrencyPrice") + .HasColumnType("REAL"); + + b.Property("GameTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Multiplier") + .HasColumnType("REAL"); + + b.Property("Updated") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Win") + .HasColumnType("INTEGER"); + + b.Property("Winnings") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("ChipsggBets"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.HowlggBetsDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Bet") + .HasColumnType("INTEGER"); + + b.Property("BetId") + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("Game") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("INTEGER"); + + b.Property("Profit") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("HowlggBets"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.ImageDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.JuicerDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Amount") + .HasColumnType("REAL"); + + b.Property("JuicedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Juicers"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.RainbetBetsDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BetId") + .HasColumnType("INTEGER"); + + b.Property("BetSeenAt") + .HasColumnType("TEXT"); + + b.Property("GameName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Multiplier") + .HasColumnType("REAL"); + + b.Property("Payout") + .HasColumnType("REAL"); + + b.Property("PublicId") + .HasColumnType("TEXT"); + + b.Property("RainbetUserId") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.ToTable("RainbetBets"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.SettingDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CacheDuration") + .HasColumnType("REAL"); + + b.Property("Default") + .HasColumnType("TEXT"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsSecret") + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Regex") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.Property("ValueType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.TwitchViewCountDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ServerTime") + .HasColumnType("REAL"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.Property("Topic") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Viewers") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TwitchViewCounts"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.UserDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Ignored") + .HasColumnType("INTEGER"); + + b.Property("KfId") + .HasColumnType("INTEGER"); + + b.Property("KfUsername") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserRight") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.JuicerDbModel", b => + { + b.HasOne("KfChatDotNetBot.Models.DbModels.UserDbModel", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/KfChatDotNetBot/Migrations/20250207015155_Images.cs b/KfChatDotNetBot/Migrations/20250207015155_Images.cs new file mode 100644 index 0000000..caf02e0 --- /dev/null +++ b/KfChatDotNetBot/Migrations/20250207015155_Images.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace KfChatDotNetBot.Migrations +{ + /// + public partial class Images : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ValueType", + table: "Settings", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "Images", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Key = table.Column(type: "TEXT", nullable: false), + Url = table.Column(type: "TEXT", nullable: false), + LastSeen = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Images", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Images"); + + migrationBuilder.DropColumn( + name: "ValueType", + table: "Settings"); + } + } +} diff --git a/KfChatDotNetBot/Migrations/ApplicationDbContextModelSnapshot.cs b/KfChatDotNetBot/Migrations/ApplicationDbContextModelSnapshot.cs index 59d90eb..56963ec 100644 --- a/KfChatDotNetBot/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/KfChatDotNetBot/Migrations/ApplicationDbContextModelSnapshot.cs @@ -102,6 +102,28 @@ namespace KfChatDotNetBot.Migrations b.ToTable("HowlggBets"); }); + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.ImageDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + modelBuilder.Entity("KfChatDotNetBot.Models.DbModels.JuicerDbModel", b => { b.Property("Id") diff --git a/KfChatDotNetBot/Models/DbModels/ImageDbModel.cs b/KfChatDotNetBot/Models/DbModels/ImageDbModel.cs new file mode 100644 index 0000000..53bd78a --- /dev/null +++ b/KfChatDotNetBot/Models/DbModels/ImageDbModel.cs @@ -0,0 +1,9 @@ +namespace KfChatDotNetBot.Models.DbModels; + +public class ImageDbModel +{ + public int Id { get; set; } + public required string Key { get; set; } + public required string Url { get; set; } + public required DateTimeOffset LastSeen { get; set; } +} \ No newline at end of file diff --git a/KfChatDotNetBot/Program.cs b/KfChatDotNetBot/Program.cs index ad86c2d..21d06b9 100644 --- a/KfChatDotNetBot/Program.cs +++ b/KfChatDotNetBot/Program.cs @@ -17,6 +17,8 @@ namespace KfChatDotNetBot await BuiltIn.SyncSettingsWithDb(); logger.Info("Migrating settings from config.json (if needed)"); await BuiltIn.MigrateJsonSettingsToDb(); + logger.Info("Migrating images over to the new system"); + await BuiltIn.MigrateImages(); logger.Info("Handing over to bot now"); Console.OutputEncoding = Encoding.UTF8; new ChatBot(); diff --git a/KfChatDotNetBot/Settings/BuiltIn.cs b/KfChatDotNetBot/Settings/BuiltIn.cs index 72c369f..7f0fd73 100644 --- a/KfChatDotNetBot/Settings/BuiltIn.cs +++ b/KfChatDotNetBot/Settings/BuiltIn.cs @@ -1,6 +1,7 @@ using System.Text.Json; using KfChatDotNetBot.Models; using KfChatDotNetBot.Models.DbModels; +using Microsoft.EntityFrameworkCore; using NLog; namespace KfChatDotNetBot.Settings; @@ -95,6 +96,89 @@ public static class BuiltIn logger.Info("File renamed"); } + + public static async Task MigrateImages() + { + var logger = LogManager.GetCurrentClassLogger(); + await using var db = new ApplicationDbContext(); + logger.Info("Migrating images to the database table"); + if (await db.Images.AnyAsync()) + { + logger.Info("Not continuing as there's already images in the database"); + return; + } + + var imagesToMigrate = await Helpers.GetMultipleValues([ + Keys.BotGmKasinoImageRotation, Keys.BotGnKasinoImageRotation, Keys.WinmanjackImgUrl, Keys.BotPraygeImgUrl, + Keys.BotCrackpipeImgUrl + ]); + + logger.Info("Migrating gmkasino images"); + foreach (var image in imagesToMigrate[Keys.BotGmKasinoImageRotation].JsonDeserialize>() ?? []) + { + await db.Images.AddAsync(new ImageDbModel {Key = "gmkasino", LastSeen = DateTimeOffset.UtcNow, Url = image}); + } + + logger.Info("Migrating gnkasino images"); + foreach (var image in imagesToMigrate[Keys.BotGnKasinoImageRotation].JsonDeserialize>() ?? []) + { + await db.Images.AddAsync(new ImageDbModel {Key = "gnkasino", LastSeen = DateTimeOffset.UtcNow, Url = image}); + } + + if (imagesToMigrate[Keys.WinmanjackImgUrl].Value != null) + { + logger.Info("Migrating winmanjack"); + await db.Images.AddAsync(new ImageDbModel + { + Key = "winmanjack", LastSeen = DateTimeOffset.UtcNow, + Url = imagesToMigrate[Keys.WinmanjackImgUrl].Value! + }); + } + + if (imagesToMigrate[Keys.BotPraygeImgUrl].Value != null) + { + logger.Info("Migrating prayge"); + await db.Images.AddAsync(new ImageDbModel + { + Key = "prayge", LastSeen = DateTimeOffset.UtcNow, + Url = imagesToMigrate[Keys.BotPraygeImgUrl].Value! + }); + } + + if (imagesToMigrate[Keys.BotCrackpipeImgUrl].Value != null) + { + logger.Info("Migrating crackpipe"); + await db.Images.AddAsync(new ImageDbModel + { + Key = "crackpipe", LastSeen = DateTimeOffset.UtcNow, + Url = imagesToMigrate[Keys.BotCrackpipeImgUrl].Value! + }); + } + + logger.Info("Adding bassmanjack"); + await db.Images.AddAsync(new ImageDbModel + { + Key = "bassmanjack", LastSeen = DateTimeOffset.UtcNow, + Url = "https://i.postimg.cc/SRstzMQt/boss-soy-koi.gif" + }); + + logger.Info("Adding sent"); + await db.Images.AddAsync(new ImageDbModel + { + Key = "sent", LastSeen = DateTimeOffset.UtcNow, + Url = "https://i.ibb.co/GHq7hb1/4373-g-N5-HEH2-Hkc.png" + }); + + logger.Info("Adding helpme"); + await db.Images.AddAsync(new ImageDbModel + { + Key = "helpme", LastSeen = DateTimeOffset.UtcNow, + Url = "https://i.postimg.cc/fTw6tGWZ/ineedmoneydumbfuck.png" + }); + + await db.SaveChangesAsync(); + logger.Info("Image migration complete"); + } public static List BuiltInSettings = [ @@ -715,6 +799,16 @@ public static class BuiltIn ValueType = SettingValueType.Boolean }, new BuiltInSettingsModel + { + Key = Keys.BotImageAcceptableKeys, + Regex = ".+", + Description = "List of valid keys for the image rotation feature", + Default = "[\"gmkasino\", \"gnkasino\", \"winmanjack\", \"prayge\", \"crackpipe\", \"bassmanjack\", \"sent\", \"helpme\"]", + IsSecret = false, + CacheDuration = TimeSpan.FromHours(1), + ValueType = SettingValueType.Array + }, + new BuiltInSettingsModel { Key = Keys.BotToyStoryImage, Regex = ".+", @@ -723,6 +817,19 @@ public static class BuiltIn IsSecret = false, CacheDuration = TimeSpan.FromHours(1), ValueType = SettingValueType.Text + }, + new BuiltInSettingsModel + { + Key = Keys.BotImageRandomSliceDivideBy, + Regex = @"\d+", + Description = "What value to divide the image count by for determining how many images to randomly choose from. " + + "e.g. a value of 10 on 50 images means the 5 least seen images are chosen from randomly. " + + "If the count of images is =< this value, it'll just grab the oldest image. " + + "Fractions will be rounded, so a value of 5 with 7 images will round down and take the oldest image.", + Default = "5", + IsSecret = false, + CacheDuration = TimeSpan.FromHours(1), + ValueType = SettingValueType.Text } ]; @@ -787,6 +894,8 @@ public static class BuiltIn public static string HowlggEnabled = "Howlgg.Enabled"; public static string ChipsggEnabled = "Chipsgg.Enabled"; public static string RainbetEnabled = "Rainbet.Enabled"; + public static string BotImageAcceptableKeys = "Bot.Image.AcceptableKeys"; public static string BotToyStoryImage = "Bot.ToyStoryImage"; + public static string BotImageRandomSliceDivideBy = "Bot.Image.RandomSliceDivideBy"; } } \ No newline at end of file