From 57a5993064d60248c3fad985d88ca37b76940d1a Mon Sep 17 00:00:00 2001
From: Toastie <toastie@toastiet0ast.com>
Date: Sun, 30 Mar 2025 15:06:56 +1300
Subject: [PATCH] color typereader fix delmsgoncmd guildconfig init

---
 .../Controllers/WebhookController.cs          |   2 +-
 src/EllieBot/Db/EllieContext.cs               | 267 +++++++++---------
 src/EllieBot/Db/Models/GuildConfig.cs         |  32 +--
 .../Modules/Administration/Administration.cs  |   2 +-
 .../Administration/AdministrationService.cs   |  90 +++---
 .../Modules/Games/NCanvas/NCanvasCommands.cs  |  24 +-
 .../Modules/Games/Quests/QuestService.cs      |  18 +-
 .../_common/TypeReaders/Rgba32TypeReader.cs   |  22 ++
 8 files changed, 244 insertions(+), 213 deletions(-)

diff --git a/src/EllieBot.VotesApi/Controllers/WebhookController.cs b/src/EllieBot.VotesApi/Controllers/WebhookController.cs
index 24e506d..b240439 100644
--- a/src/EllieBot.VotesApi/Controllers/WebhookController.cs
+++ b/src/EllieBot.VotesApi/Controllers/WebhookController.cs
@@ -14,7 +14,7 @@ namespace EllieBot.VotesApi.Controllers
         [Authorize(Policy = Policies.DiscordsAuth)]
         public async Task<IActionResult> DiscordsWebhook([FromBody] DiscordsVoteWebhookModel data)
         {
-            if (data.Type != "vote")
+            if ((data.Type?.Contains("vote") ?? false) == false)
                 return Ok();
             
             logger.LogInformation("User {UserId} has voted for Bot {BotId} on {Platform}",
diff --git a/src/EllieBot/Db/EllieContext.cs b/src/EllieBot/Db/EllieContext.cs
index 4289e50..e51e409 100644
--- a/src/EllieBot/Db/EllieContext.cs
+++ b/src/EllieBot/Db/EllieContext.cs
@@ -80,7 +80,7 @@ public abstract class EllieContext : DbContext
     {
         // load all entities from current assembly
         modelBuilder.ApplyConfigurationsFromAssembly(typeof(EllieContext).Assembly);
-        
+
         #region Notify
 
         modelBuilder.Entity<Notify>(e =>
@@ -113,8 +113,8 @@ public abstract class EllieContext : DbContext
         #region GuildColors
 
         modelBuilder.Entity<GuildColors>()
-                    .HasIndex(x => x.GuildId)
-                    .IsUnique(true);
+            .HasIndex(x => x.GuildId)
+            .IsUnique(true);
 
         #endregion
 
@@ -123,7 +123,7 @@ public abstract class EllieContext : DbContext
         modelBuilder.Entity<ButtonRole>(br =>
         {
             br.HasIndex(x => x.GuildId)
-              .IsUnique(false);
+                .IsUnique(false);
 
             br.HasAlternateKey(x => new
             {
@@ -145,27 +145,27 @@ public abstract class EllieContext : DbContext
             });
 
             sg.HasMany(x => x.Roles)
-              .WithOne()
-              .OnDelete(DeleteBehavior.Cascade);
+                .WithOne()
+                .OnDelete(DeleteBehavior.Cascade);
         });
 
         modelBuilder.Entity<Sar>()
-                    .HasAlternateKey(x => new
-                    {
-                        x.GuildId,
-                        x.RoleId
-                    });
+            .HasAlternateKey(x => new
+            {
+                x.GuildId,
+                x.RoleId
+            });
 
         modelBuilder.Entity<SarAutoDelete>()
-                    .HasIndex(x => x.GuildId)
-                    .IsUnique();
+            .HasIndex(x => x.GuildId)
+            .IsUnique();
 
         #endregion
 
         #region Rakeback
 
         modelBuilder.Entity<Rakeback>()
-                    .HasKey(x => x.UserId);
+            .HasKey(x => x.UserId);
 
         #endregion
 
@@ -174,14 +174,14 @@ public abstract class EllieContext : DbContext
         modelBuilder.Entity<UserBetStats>(ubs =>
         {
             ubs.HasIndex(x => new
-               {
-                   x.UserId,
-                   x.Game
-               })
-               .IsUnique();
+                {
+                    x.UserId,
+                    x.Game
+                })
+                .IsUnique();
 
             ubs.HasIndex(x => x.MaxWin)
-               .IsUnique(false);
+                .IsUnique(false);
         });
 
         #endregion
@@ -189,22 +189,22 @@ public abstract class EllieContext : DbContext
         #region Flag Translate
 
         modelBuilder.Entity<FlagTranslateChannel>()
-                    .HasIndex(x => new
-                    {
-                        x.GuildId,
-                        x.ChannelId
-                    })
-                    .IsUnique();
+            .HasIndex(x => new
+            {
+                x.GuildId,
+                x.ChannelId
+            })
+            .IsUnique();
 
         #endregion
 
         #region NCanvas
 
         modelBuilder.Entity<NCPixel>()
-                    .HasAlternateKey(x => x.Position);
+            .HasAlternateKey(x => x.Position);
 
         modelBuilder.Entity<NCPixel>()
-                    .HasIndex(x => x.OwnerId);
+            .HasIndex(x => x.OwnerId);
 
         #endregion
 
@@ -221,10 +221,11 @@ public abstract class EllieContext : DbContext
         var configEntity = modelBuilder.Entity<GuildConfig>();
 
         configEntity.HasIndex(c => c.GuildId)
-                    .IsUnique();
+            .IsUnique();
 
         configEntity.Property(x => x.VerboseErrors)
-                    .HasDefaultValue(true);
+            .HasDefaultValue(true);
+
         // end shop
 
         modelBuilder.Entity<PlantedCurrency>().HasIndex(x => x.MessageId).IsUnique();
@@ -270,19 +271,19 @@ public abstract class EllieContext : DbContext
         modelBuilder.Entity<DiscordUser>(du =>
         {
             du.Property(x => x.IsClubAdmin)
-              .HasDefaultValue(false);
+                .HasDefaultValue(false);
 
             du.Property(x => x.TotalXp)
-              .HasDefaultValue(0);
+                .HasDefaultValue(0);
 
             du.Property(x => x.CurrencyAmount)
-              .HasDefaultValue(0);
+                .HasDefaultValue(0);
 
             du.HasAlternateKey(w => w.UserId);
             du.HasOne(x => x.Club)
-              .WithMany(x => x.Members)
-              .IsRequired(false)
-              .OnDelete(DeleteBehavior.NoAction);
+                .WithMany(x => x.Members)
+                .IsRequired(false)
+                .OnDelete(DeleteBehavior.NoAction);
 
             du.HasIndex(x => x.TotalXp);
             du.HasIndex(x => x.CurrencyAmount);
@@ -308,11 +309,11 @@ public abstract class EllieContext : DbContext
 
         var xps = modelBuilder.Entity<UserXpStats>();
         xps.HasIndex(x => new
-           {
-               x.UserId,
-               x.GuildId
-           })
-           .IsUnique();
+            {
+                x.UserId,
+                x.GuildId
+            })
+            .IsUnique();
 
         xps.HasIndex(x => x.UserId);
         xps.HasIndex(x => x.GuildId);
@@ -324,49 +325,49 @@ public abstract class EllieContext : DbContext
 
         var ci = modelBuilder.Entity<ClubInfo>();
         ci.HasOne(x => x.Owner)
-          .WithOne()
-          .HasForeignKey<ClubInfo>(x => x.OwnerId)
-          .OnDelete(DeleteBehavior.SetNull);
+            .WithOne()
+            .HasForeignKey<ClubInfo>(x => x.OwnerId)
+            .OnDelete(DeleteBehavior.SetNull);
 
         ci.HasIndex(x => new
-          {
-              x.Name
-          })
-          .IsUnique();
+            {
+                x.Name
+            })
+            .IsUnique();
 
         #endregion
 
         #region ClubManytoMany
 
         modelBuilder.Entity<ClubApplicants>()
-                    .HasKey(t => new
-                    {
-                        t.ClubId,
-                        t.UserId
-                    });
+            .HasKey(t => new
+            {
+                t.ClubId,
+                t.UserId
+            });
 
         modelBuilder.Entity<ClubApplicants>()
-                    .HasOne(pt => pt.User)
-                    .WithMany();
+            .HasOne(pt => pt.User)
+            .WithMany();
 
         modelBuilder.Entity<ClubApplicants>()
-                    .HasOne(pt => pt.Club)
-                    .WithMany(x => x.Applicants);
+            .HasOne(pt => pt.Club)
+            .WithMany(x => x.Applicants);
 
         modelBuilder.Entity<ClubBans>()
-                    .HasKey(t => new
-                    {
-                        t.ClubId,
-                        t.UserId
-                    });
+            .HasKey(t => new
+            {
+                t.ClubId,
+                t.UserId
+            });
 
         modelBuilder.Entity<ClubBans>()
-                    .HasOne(pt => pt.User)
-                    .WithMany();
+            .HasOne(pt => pt.User)
+            .WithMany();
 
         modelBuilder.Entity<ClubBans>()
-                    .HasOne(pt => pt.Club)
-                    .WithMany(x => x.Bans);
+            .HasOne(pt => pt.Club)
+            .WithMany(x => x.Bans);
 
         #endregion
 
@@ -375,16 +376,16 @@ public abstract class EllieContext : DbContext
         modelBuilder.Entity<CurrencyTransaction>(e =>
         {
             e.HasIndex(x => x.UserId)
-             .IsUnique(false);
+                .IsUnique(false);
 
             e.Property(x => x.OtherId)
-             .HasDefaultValueSql(CurrencyTransactionOtherIdDefaultValue);
+                .HasDefaultValueSql(CurrencyTransactionOtherIdDefaultValue);
 
             e.Property(x => x.Type)
-             .IsRequired();
+                .IsRequired();
 
             e.Property(x => x.Extra)
-             .IsRequired();
+                .IsRequired();
         });
 
         #endregion
@@ -399,21 +400,21 @@ public abstract class EllieContext : DbContext
 
         modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
         modelBuilder.Entity<BanTemplate>()
-                    .Property(x => x.PruneDays)
-                    .HasDefaultValue(null)
-                    .IsRequired(false);
+            .Property(x => x.PruneDays)
+            .HasDefaultValue(null)
+            .IsRequired(false);
 
         #endregion
 
         #region Perm Override
 
         modelBuilder.Entity<DiscordPermOverride>()
-                    .HasIndex(x => new
-                    {
-                        x.GuildId,
-                        x.Command
-                    })
-                    .IsUnique();
+            .HasIndex(x => new
+            {
+                x.GuildId,
+                x.Command
+            })
+            .IsUnique();
 
         #endregion
 
@@ -430,14 +431,14 @@ public abstract class EllieContext : DbContext
         modelBuilder.Entity<ReactionRoleV2>(rr2 =>
         {
             rr2.HasIndex(x => x.GuildId)
-               .IsUnique(false);
+                .IsUnique(false);
 
             rr2.HasIndex(x => new
-               {
-                   x.MessageId,
-                   x.Emote
-               })
-               .IsUnique();
+                {
+                    x.MessageId,
+                    x.Emote
+                })
+                .IsUnique();
         });
 
         #endregion
@@ -447,18 +448,18 @@ public abstract class EllieContext : DbContext
         modelBuilder.Entity<LogSetting>(ls => ls.HasIndex(x => x.GuildId).IsUnique());
 
         modelBuilder.Entity<LogSetting>(ls => ls
-                                              .HasMany(x => x.LogIgnores)
-                                              .WithOne(x => x.LogSetting)
-                                              .OnDelete(DeleteBehavior.Cascade));
+            .HasMany(x => x.LogIgnores)
+            .WithOne(x => x.LogSetting)
+            .OnDelete(DeleteBehavior.Cascade));
 
         modelBuilder.Entity<IgnoredLogItem>(ili => ili
-                                                   .HasIndex(x => new
-                                                   {
-                                                       x.LogSettingId,
-                                                       x.LogItemId,
-                                                       x.ItemType
-                                                   })
-                                                   .IsUnique());
+            .HasIndex(x => new
+            {
+                x.LogSettingId,
+                x.LogItemId,
+                x.ItemType
+            })
+            .IsUnique());
 
         #endregion
 
@@ -511,12 +512,12 @@ public abstract class EllieContext : DbContext
             {
                 // user can own only one of each item
                 x.HasIndex(model => new
-                 {
-                     model.UserId,
-                     model.ItemType,
-                     model.ItemKey
-                 })
-                 .IsUnique();
+                    {
+                        model.UserId,
+                        model.ItemType,
+                        model.ItemKey
+                    })
+                    .IsUnique();
             });
 
         #endregion
@@ -524,27 +525,27 @@ public abstract class EllieContext : DbContext
         #region AutoPublish
 
         modelBuilder.Entity<AutoPublishChannel>(apc => apc
-                                                       .HasIndex(x => x.GuildId)
-                                                       .IsUnique());
+            .HasIndex(x => x.GuildId)
+            .IsUnique());
 
         #endregion
 
         #region GamblingStats
 
         modelBuilder.Entity<GamblingStats>(gs => gs
-                                                 .HasIndex(x => x.Feature)
-                                                 .IsUnique());
+            .HasIndex(x => x.Feature)
+            .IsUnique());
 
         #endregion
 
         #region Sticky Roles
 
         modelBuilder.Entity<StickyRole>(sr => sr.HasIndex(x => new
-                                                {
-                                                    x.GuildId,
-                                                    x.UserId
-                                                })
-                                                .IsUnique());
+            {
+                x.GuildId,
+                x.UserId
+            })
+            .IsUnique());
 
         #endregion
 
@@ -552,35 +553,35 @@ public abstract class EllieContext : DbContext
         #region Giveaway
 
         modelBuilder.Entity<GiveawayModel>()
-                    .HasMany(x => x.Participants)
-                    .WithOne()
-                    .HasForeignKey(x => x.GiveawayId)
-                    .OnDelete(DeleteBehavior.Cascade);
+            .HasMany(x => x.Participants)
+            .WithOne()
+            .HasForeignKey(x => x.GiveawayId)
+            .OnDelete(DeleteBehavior.Cascade);
 
         modelBuilder.Entity<GiveawayUser>(gu => gu
-                                                .HasIndex(x => new
-                                                {
-                                                    x.GiveawayId,
-                                                    x.UserId
-                                                })
-                                                .IsUnique());
+            .HasIndex(x => new
+            {
+                x.GiveawayId,
+                x.UserId
+            })
+            .IsUnique());
 
         #endregion
 
         #region Todo
 
         modelBuilder.Entity<TodoModel>()
-                    .HasKey(x => x.Id);
+            .HasKey(x => x.Id);
 
         modelBuilder.Entity<TodoModel>()
-                    .HasIndex(x => x.UserId)
-                    .IsUnique(false);
+            .HasIndex(x => x.UserId)
+            .IsUnique(false);
 
         modelBuilder.Entity<ArchivedTodoListModel>()
-                    .HasMany(x => x.Items)
-                    .WithOne()
-                    .HasForeignKey(x => x.ArchiveId)
-                    .OnDelete(DeleteBehavior.Cascade);
+            .HasMany(x => x.Items)
+            .WithOne()
+            .HasForeignKey(x => x.ArchiveId)
+            .OnDelete(DeleteBehavior.Cascade);
 
         #endregion
 
@@ -588,11 +589,11 @@ public abstract class EllieContext : DbContext
 
         modelBuilder
             .Entity<GreetSettings>(gs => gs.HasIndex(x => new
-                                           {
-                                               x.GuildId,
-                                               x.GreetType
-                                           })
-                                           .IsUnique());
+                {
+                    x.GuildId,
+                    x.GreetType
+                })
+                .IsUnique());
 
         modelBuilder.Entity<GreetSettings>(gs =>
         {
@@ -619,6 +620,6 @@ public abstract class EllieContext : DbContext
 #endif
 
         optionsBuilder.ConfigureWarnings(x => x.Log(RelationalEventId.PendingModelChangesWarning)
-                                               .Ignore());
+            .Ignore());
     }
 }
\ No newline at end of file
diff --git a/src/EllieBot/Db/Models/GuildConfig.cs b/src/EllieBot/Db/Models/GuildConfig.cs
index 7d1f152..0cce1dc 100644
--- a/src/EllieBot/Db/Models/GuildConfig.cs
+++ b/src/EllieBot/Db/Models/GuildConfig.cs
@@ -1,4 +1,3 @@
-#nullable disable
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore.Metadata.Builders;
 using System.ComponentModel.DataAnnotations;
@@ -31,39 +30,38 @@ public sealed class GuildFilterConfigEntityConfiguration : IEntityTypeConfigurat
 public class GuildConfig : DbEntity
 {
     public ulong GuildId { get; set; }
-    public string Prefix { get; set; }
+    public string? Prefix { get; set; } = null;
 
-    public bool DeleteMessageOnCommand { get; set; }
-
-    public string AutoAssignRoleIds { get; set; }
+    public bool DeleteMessageOnCommand { get; set; } = false;
 
+    public string? AutoAssignRoleIds { get; set; } = null;
     public bool VerbosePermissions { get; set; } = true;
-    public string PermissionRole { get; set; }
+    public string? PermissionRole { get; set; } = null;
 
     //filtering
-    public string MuteRoleName { get; set; }
+    public string? MuteRoleName { get; set; } = null;
 
     // chatterbot
-    public bool CleverbotEnabled { get; set; }
+    public bool CleverbotEnabled { get; set; } = false;
 
     // aliases
-    public bool WarningsInitialized { get; set; }
+    public bool WarningsInitialized { get; set; } = false;
 
-    public ulong? GameVoiceChannel { get; set; }
+    public ulong? GameVoiceChannel { get; set; } = null;
     public bool VerboseErrors { get; set; } = true;
 
 
-    public bool NotifyStreamOffline { get; set; }
-    public bool DeleteStreamOnlineMessage { get; set; }
-    public int WarnExpireHours { get; set; }
+    public bool NotifyStreamOffline { get; set; } = true;
+    public bool DeleteStreamOnlineMessage { get; set; } = false;
+    public int WarnExpireHours { get; set; } = 0;
     public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
 
     public bool DisableGlobalExpressions { get; set; } = false;
 
-    public bool StickyRoles { get; set; }
-    
-    public string TimeZoneId { get; set; }
-    public string Locale { get; set; }
+    public bool StickyRoles { get; set; } = false;
+
+    public string? TimeZoneId { get; set; } = null;
+    public string? Locale { get; set; } = null;
 
     public List<Permissionv2> Permissions { get; set; } = [];
 }
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Administration/Administration.cs b/src/EllieBot/Modules/Administration/Administration.cs
index 00c3935..621e273 100644
--- a/src/EllieBot/Modules/Administration/Administration.cs
+++ b/src/EllieBot/Modules/Administration/Administration.cs
@@ -124,7 +124,7 @@ public partial class Administration : EllieModule<AdministrationService>
     [Priority(1)]
     public async Task Delmsgoncmd(Server _ = Server.Server)
     {
-        var enabled = await _service.ToggleDeleteMessageOnCommand(ctx.Guild.Id);
+        var enabled = await _service.ToggleDelMsgOnCmd(ctx.Guild.Id);
         if (enabled)
         {
             await Response().Confirm(strs.delmsg_on).SendAsync();
diff --git a/src/EllieBot/Modules/Administration/AdministrationService.cs b/src/EllieBot/Modules/Administration/AdministrationService.cs
index d87cacd..4dfeee1 100644
--- a/src/EllieBot/Modules/Administration/AdministrationService.cs
+++ b/src/EllieBot/Modules/Administration/AdministrationService.cs
@@ -1,7 +1,5 @@
 #nullable disable
-using LinqToDB;
 using LinqToDB.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore;
 using EllieBot.Common.ModuleBehaviors;
 using EllieBot.Db.Models;
 using EllieBot.Modules.Administration._common.results;
@@ -10,8 +8,8 @@ namespace EllieBot.Modules.Administration;
 
 public class AdministrationService : IEService, IReadyExecutor
 {
-    private ConcurrentHashSet<ulong> deleteMessagesOnCommand;
-    private ConcurrentDictionary<ulong, bool> delMsgOnCmdChannels;
+    private ConcurrentHashSet<ulong> _deleteMessagesOnCommand;
+    private ConcurrentDictionary<ulong, bool> _delMsgOnCmdChannels;
 
     private readonly DbService _db;
     private readonly IReplacementService _repSvc;
@@ -39,31 +37,32 @@ public class AdministrationService : IEService, IReadyExecutor
     public async Task OnReadyAsync()
     {
         await using var uow = _db.GetDbContext();
-        deleteMessagesOnCommand = new(await uow.GetTable<GuildConfig>()
-                                         .Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && x.DeleteMessageOnCommand)
-                                         .Select(x => x.GuildId)
-                                         .ToListAsyncLinqToDB());
+        _deleteMessagesOnCommand = new(await uow.GetTable<GuildConfig>()
+            .Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) &&
+                        x.DeleteMessageOnCommand)
+            .Select(x => x.GuildId)
+            .ToListAsyncLinqToDB());
 
-        delMsgOnCmdChannels = (await uow.GetTable<DelMsgOnCmdChannel>()
-                                                   .Where(x => deleteMessagesOnCommand.Contains(x.GuildId))
-                                                   .ToDictionaryAsyncLinqToDB(x => x.ChannelId, x => x.State))
-                                                   .ToConcurrent();
+        _delMsgOnCmdChannels = (await uow.GetTable<DelMsgOnCmdChannel>()
+                .Where(x => _deleteMessagesOnCommand.Contains(x.GuildId))
+                .ToDictionaryAsyncLinqToDB(x => x.ChannelId, x => x.State))
+            .ToConcurrent();
 
         _cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
     }
 
     public async Task<(bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels)> GetDelMsgOnCmdData(ulong guildId)
     {
-        using var uow = _db.GetDbContext();
+        await using var uow = _db.GetDbContext();
 
         var conf = await uow.GetTable<GuildConfig>()
-        .Where(x => x.GuildId == guildId)
-        .Select(x => x.DeleteMessageOnCommand)
-        .FirstOrDefaultAsyncLinqToDB();
+            .Where(x => x.GuildId == guildId)
+            .Select(x => x.DeleteMessageOnCommand)
+            .FirstOrDefaultAsyncLinqToDB();
 
         var channels = await uow.GetTable<DelMsgOnCmdChannel>()
-        .Where(x => x.GuildId == guildId)
-        .ToListAsyncLinqToDB();
+            .Where(x => x.GuildId == guildId)
+            .ToListAsyncLinqToDB();
 
         return (conf, channels);
     }
@@ -76,50 +75,50 @@ public class AdministrationService : IEService, IReadyExecutor
         _ = Task.Run(async () =>
         {
             //wat ?!
-            if (delMsgOnCmdChannels.TryGetValue(channel.Id, out var state))
+            if (_delMsgOnCmdChannels.TryGetValue(channel.Id, out var state))
             {
                 if (state && cmd.Name != "prune" && cmd.Name != "pick")
                 {
                     _logService.AddDeleteIgnore(msg.Id);
                     try
-                    { await msg.DeleteAsync(); }
-                    catch { }
+                    {
+                        await msg.DeleteAsync();
+                    }
+                    catch
+                    {
+                    }
                 }
                 //if state is false, that means do not do it
             }
-            else if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick")
+            else if (_deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick")
             {
                 _logService.AddDeleteIgnore(msg.Id);
                 try
-                { await msg.DeleteAsync(); }
-                catch { }
+                {
+                    await msg.DeleteAsync();
+                }
+                catch
+                {
+                }
             }
         });
         return Task.CompletedTask;
     }
 
-    public async Task<bool> ToggleDeleteMessageOnCommand(ulong guildId)
+    public async Task<bool> ToggleDelMsgOnCmd(ulong guildId)
     {
-        using var uow = _db.GetDbContext();
+        await using var uow = _db.GetDbContext();
 
-        var conf = await uow.GetTable<GuildConfig>()
-            .Where(x => x.GuildId == guildId)
-            .UpdateWithOutputAsync(x => new()
-            {
-                DeleteMessageOnCommand = !x.DeleteMessageOnCommand
-            }, (old, newVal) => newVal);
+        var gc = uow.GuildConfigsForId(guildId);
+        gc.DeleteMessageOnCommand = !gc.DeleteMessageOnCommand;
 
-        if (conf.Length == 0)
-            return false;
-
-        var val = conf[0].DeleteMessageOnCommand;
-
-        if (val)
-            deleteMessagesOnCommand.Add(guildId);
+        if (gc.DeleteMessageOnCommand)
+            _deleteMessagesOnCommand.Add(guildId);
         else
-            deleteMessagesOnCommand.TryRemove(guildId);
+            _deleteMessagesOnCommand.TryRemove(guildId);
 
-        return val;
+        await uow.SaveChangesAsync();
+        return gc.DeleteMessageOnCommand;
     }
 
     public async Task SetDelMsgOnCmdState(ulong guildId, ulong chId, Administration.State newState)
@@ -151,7 +150,7 @@ public class AdministrationService : IEService, IReadyExecutor
                 }
 
                 old.State = newState == Administration.State.Enable;
-                delMsgOnCmdChannels[chId] = newState == Administration.State.Enable;
+                _delMsgOnCmdChannels[chId] = newState == Administration.State.Enable;
             }
 
             await uow.SaveChangesAsync();
@@ -162,11 +161,11 @@ public class AdministrationService : IEService, IReadyExecutor
         }
         else if (newState == Administration.State.Enable)
         {
-            delMsgOnCmdChannels[chId] = true;
+            _delMsgOnCmdChannels[chId] = true;
         }
         else
         {
-            delMsgOnCmdChannels.TryRemove(chId, out _);
+            _delMsgOnCmdChannels.TryRemove(chId, out _);
         }
     }
 
@@ -249,5 +248,6 @@ public class AdministrationService : IEService, IReadyExecutor
         return SetServerIconResult.Success;
     }
 
-    private bool IsValidUri(string img) => !string.IsNullOrWhiteSpace(img) && Uri.IsWellFormedUriString(img, UriKind.Absolute);
+    private bool IsValidUri(string img)
+        => !string.IsNullOrWhiteSpace(img) && Uri.IsWellFormedUriString(img, UriKind.Absolute);
 }
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Games/NCanvas/NCanvasCommands.cs b/src/EllieBot/Modules/Games/NCanvas/NCanvasCommands.cs
index ee35fba..bcce32d 100644
--- a/src/EllieBot/Modules/Games/NCanvas/NCanvasCommands.cs
+++ b/src/EllieBot/Modules/Games/NCanvas/NCanvasCommands.cs
@@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Advanced;
 using SixLabors.ImageSharp.Drawing.Processing;
 using SixLabors.ImageSharp.PixelFormats;
 using SixLabors.ImageSharp.Processing;
+using Color = SixLabors.ImageSharp.Color;
 using Image = SixLabors.ImageSharp.Image;
 
 namespace EllieBot.Modules.Games;
@@ -81,7 +82,15 @@ public partial class Games
 
             using var img = await GetZoomImage(position);
             await using var stream = await img.ToStreamAsync();
-            await ctx.Channel.SendFileAsync(stream, $"zoom_{position}.png");
+            var eb = CreateEmbed()
+                .WithOkColor()
+                .WithImageUrl($"attachment://zoom_{position}.png")
+                .WithFooter($"`.ncs code color` to set. (.ncs abc green)" );
+            
+            await Response()
+                .Embed(eb)
+                .File(stream, $"zoom_{position}.png")
+                .SendAsync();
         }
 
         private async Task<Image<Rgba32>> GetZoomImage(kwum position)
@@ -135,7 +144,7 @@ public partial class Games
         }
 
         [Cmd]
-        public async Task NcSetPixel(kwum position, string colorHex, [Leftover] string text = "")
+        public async Task NcSetPixel(kwum position, Rgba32 color, [Leftover] string text = "")
         {
             if (position < 0 || position >= _service.GetWidth() * _service.GetHeight())
             {
@@ -143,15 +152,6 @@ public partial class Games
                 return;
             }
 
-            if (colorHex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
-                colorHex = colorHex[2..];
-
-            if (!Rgba32.TryParseHex(colorHex, out var clr))
-            {
-                await Response().Error(strs.invalid_color).SendAsync();
-                return;
-            }
-
             var pixel = await _service.GetPixel(position);
             if (pixel is null)
             {
@@ -171,7 +171,7 @@ public partial class Games
                 return;
             }
 
-            var result = await _service.SetPixel(position, clr.PackedValue, text, ctx.User.Id, pixel.Price);
+            var result = await _service.SetPixel(position, color.PackedValue, text, ctx.User.Id, pixel.Price);
 
             if (result == SetPixelResult.NotEnoughMoney)
             {
diff --git a/src/EllieBot/Modules/Games/Quests/QuestService.cs b/src/EllieBot/Modules/Games/Quests/QuestService.cs
index cdc30ee..e184087 100644
--- a/src/EllieBot/Modules/Games/Quests/QuestService.cs
+++ b/src/EllieBot/Modules/Games/Quests/QuestService.cs
@@ -46,34 +46,44 @@ public sealed class QuestService(
 
         _ = Task.Run(async () =>
         {
-            // Log.Information("Action reported by {UserId}: {EventType} {Metadata}",
-               // userId,
-               // eventType,
-               // metadata.ToJson());
+            Log.Information("Action reported by {UserId}: {EventType} {Metadata}",
+                userId,
+                eventType,
+                metadata.ToJson());
             metadata ??= new();
             var now = DateTime.UtcNow;
 
+            Log.Information("done?");
             var alreadyDone = await botCache.GetAsync(UserCompletedDailiesKey(userId));
             if (alreadyDone.IsT0)
                 return;
+            
+            Log.Information("not done");
 
             var userQuests = await GetUserQuestsAsync(userId, now);
 
+            Log.Information("got quests");
             foreach (var (q, uq) in userQuests)
             {
                 // deleted quest
                 if (q is null)
                     continue;
+                
+                Log.Information("not deleted {QuestEventType} - {EventType}", q.EventType, eventType);
 
                 // user already completed or incorrect event
                 if (uq.IsCompleted || q.EventType != eventType)
                     continue;
+                
+                Log.Information("Gonna update progress");
 
                 var newProgress = q.TryUpdateProgress(metadata, uq.Progress);
 
                 // user already did that part of the quest
                 if (newProgress == uq.Progress)
                     continue;
+                
+                Log.Information("new progress");
 
                 var isCompleted = newProgress >= q.RequiredAmount;
 
diff --git a/src/EllieBot/_common/TypeReaders/Rgba32TypeReader.cs b/src/EllieBot/_common/TypeReaders/Rgba32TypeReader.cs
index d1f3711..ab1f9e8 100644
--- a/src/EllieBot/_common/TypeReaders/Rgba32TypeReader.cs
+++ b/src/EllieBot/_common/TypeReaders/Rgba32TypeReader.cs
@@ -8,11 +8,33 @@ public sealed class Rgba32TypeReader : EllieTypeReader<Rgba32>
 {
     public override ValueTask<TypeReaderResult<Rgba32>> ReadAsync(ICommandContext context, string input)
     {
+        if (!Color.TryParse(input, out var color))
+        {
+            Log.Information("Fail");
+            return ValueTask.FromResult(
+                TypeReaderResult.FromError<Rgba32>(CommandError.ParseFailed, "Parameter is not a valid color hex."));
+        }
+        Log.Information(color.ToHex());
+
+        return ValueTask.FromResult(TypeReaderResult.FromSuccess((Rgba32)color));
+
         if (Rgba32.TryParseHex(input, out var clr))
         {
             return ValueTask.FromResult(TypeReaderResult.FromSuccess(clr));
         }
 
+        if (!Enum.TryParse<Color>(input, true, out var clrName))
+            return ValueTask.FromResult(
+                TypeReaderResult.FromError<Rgba32>(CommandError.ParseFailed,
+                    "Parameter is not a valid color hex."));
+
+        Log.Information(clrName.ToString());
+
+        if (Rgba32.TryParseHex(clrName.ToHex(), out clr))
+        {
+            return ValueTask.FromResult(TypeReaderResult.FromSuccess(clr));
+        }
+
         return ValueTask.FromResult(
             TypeReaderResult.FromError<Rgba32>(CommandError.ParseFailed, "Parameter is not a valid color hex."));
     }