using System.Text.RegularExpressions; using KfChatDotNetBot.Extensions; using KfChatDotNetBot.Models; using KfChatDotNetBot.Models.DbModels; using KfChatDotNetBot.Services; using KfChatDotNetBot.Settings; using KfChatDotNetWsClient.Models.Events; using Microsoft.EntityFrameworkCore; using NLog; namespace KfChatDotNetBot.Commands; public class AddImageCommand : ICommand { public List Patterns => [ new Regex(@"^admin image (?\w+) add (?.+)$"), new Regex(@"^admin images (?\w+) add (?.+)$"), new Regex(@"^admin image (?\w+) add_nigger (?.+)$"), new Regex(@"^admin images (?\w+) add_nigger (?.+)$") ]; public string? HelpText => "Add an image to the image rotation specified"; public UserRight RequiredRight => UserRight.TrueAndHonest; public TimeSpan Timeout => TimeSpan.FromSeconds(10); public RateLimitOptionsModel? RateLimitOptions => null; public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) { await using var db = new ApplicationDbContext(); var imageKeys = (await SettingsProvider.GetValueAsync(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; var niggerMode = message.Message.Contains("add_nigger"); if (!imageKeys.Contains(key)) { await botInstance.SendChatMessageAsync( $"Key you specified is not supported. Available keys are: {string.Join(' ', imageKeys)}", true); return; } if (!niggerMode && 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 RateLimitOptionsModel? RateLimitOptions => null; public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) { await using var db = new ApplicationDbContext(); var imageKeys = (await SettingsProvider.GetValueAsync(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 RateLimitOptionsModel? RateLimitOptions => null; public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) { await using var db = new ApplicationDbContext(); var imageKeys = (await SettingsProvider.GetValueAsync(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++; var ts = DateTimeOffset.UtcNow - image.LastSeen; result += $"[br]{i}: {image.Url} (Last seen {ts.TotalDays:N0}d{ts.Hours:N0}h{ts.Minutes:N0}m{ts.Seconds:N0}s ago)"; } 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.FromMinutes(10); public RateLimitOptionsModel? RateLimitOptions => new() { Window = TimeSpan.FromSeconds(30), MaxInvocations = 7, Flags = RateLimitFlags.UseEntireMessage }; public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments, CancellationToken ctx) { var logger = LogManager.GetCurrentClassLogger(); 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)) { RateLimitService.RemoveMostRecentEntry(user, this); return; } var settings = await SettingsProvider.GetMultipleValuesAsync([ BuiltIn.Keys.BotImageRandomSliceDivideBy, BuiltIn.Keys.BotImagePigCubeSelfDestruct, BuiltIn.Keys.BotImageInvertedCubeUrl, BuiltIn.Keys.BotImagePigCubeSelfDestructMin, BuiltIn.Keys.BotImagePigCubeSelfDestructMax, BuiltIn.Keys.BotImageInvertedPigCubeSelfDestructDelay, BuiltIn.Keys.BotImageChinkSelfDestruct, BuiltIn.Keys.BotImageChinkSelfDestructDelay ]); var divideBy = settings[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); TimeSpan? timeToDeletion = null; if (key == "pigcube" && settings[BuiltIn.Keys.BotImagePigCubeSelfDestruct].ToBoolean()) { timeToDeletion = TimeSpan.FromMilliseconds(image.Url == settings[BuiltIn.Keys.BotImageInvertedCubeUrl].Value ? settings[BuiltIn.Keys.BotImageInvertedPigCubeSelfDestructDelay].ToType() : new Random().Next(settings[BuiltIn.Keys.BotImagePigCubeSelfDestructMin].ToType(), settings[BuiltIn.Keys.BotImagePigCubeSelfDestructMax].ToType())); } else if (key == "chink" && settings[BuiltIn.Keys.BotImageChinkSelfDestruct].ToBoolean()) { timeToDeletion = TimeSpan.FromMilliseconds(settings[BuiltIn.Keys.BotImageChinkSelfDestructDelay].ToType()); } await botInstance.SendChatMessageAsync($"[img]{image.Url}[/img]", true, autoDeleteAfter: timeToDeletion); } }