From b033b062a5b76fb235dbe8fe66d134d338adc6b8 Mon Sep 17 00:00:00 2001
From: Toastie <toastie@toastiet0ast.com>
Date: Wed, 19 Mar 2025 18:53:40 +1300
Subject: [PATCH] fixed module list Updated changelog, version upped to 6.0.9
 .cinfo now also has a member list commandlist will be auto-regenerated in
 debug runs

---
 CHANGELOG.md                                  |  12 ++
 .../EllieBot.GrpcApiBase.csproj               |   2 +-
 src/EllieBot/EllieBot.csproj                  |   4 +-
 .../Protection/ProtectionService.cs           |   1 +
 .../Modules/Help/CommandListGenerator.cs      |  62 ++++++
 src/EllieBot/Modules/Help/Help.cs             |  40 +---
 .../Modules/Utility/Info/InfoCommands.cs      |  31 ++-
 .../LiveChannel/LiveChannelCommands.cs        |   4 +-
 .../Utility/Scheduled/ScheduledCommands.cs    |   3 +-
 src/EllieBot/data/commandlist.json            | 202 +++++++++---------
 10 files changed, 210 insertions(+), 151 deletions(-)
 create mode 100644 src/EllieBot/Modules/Help/CommandListGenerator.cs

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82d9e1a..2dd6640 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
 
 *a,c,f,r,o*
 
+## [6.0.9] - 19.03.2025
+ 
+### Changed
+ 
+ - `.cinfo` now also has a member list
+ 
+### Fixed
+ 
+ - `.antispamignore` will now properly persist through restarts
+ - livechannels and scheduled commands will now be inside utility module as they should
+
 ## [6.0.8] - 19.03.2025
  
 ### Added
@@ -14,6 +25,7 @@
    - `.lchd <channel or channelId>` removed a live channel
  
 ### Fixed
+
  - `.antispamignore` fixed
 
 ## [6.0.7] - 19.03.2025
diff --git a/src/EllieBot.GrpcApiBase/EllieBot.GrpcApiBase.csproj b/src/EllieBot.GrpcApiBase/EllieBot.GrpcApiBase.csproj
index 16fcb89..abdc85d 100644
--- a/src/EllieBot.GrpcApiBase/EllieBot.GrpcApiBase.csproj
+++ b/src/EllieBot.GrpcApiBase/EllieBot.GrpcApiBase.csproj
@@ -9,7 +9,7 @@
   <ItemGroup>
     <PackageReference Include="Google.Protobuf" Version="3.29.3" />
     <PackageReference Include="Grpc" Version="2.46.6" />
-    <PackageReference Include="Grpc.Tools" Version="2.69.0" PrivateAssets="All" />
+    <PackageReference Include="Grpc.Tools" Version="2.68.1" PrivateAssets="All" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/EllieBot/EllieBot.csproj b/src/EllieBot/EllieBot.csproj
index 5ba587d..3cf7abb 100644
--- a/src/EllieBot/EllieBot.csproj
+++ b/src/EllieBot/EllieBot.csproj
@@ -4,7 +4,7 @@
         <Nullable>enable</Nullable>
         <ImplicitUsings>true</ImplicitUsings>
         <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
-        <Version>6.0.8</Version>
+        <Version>6.0.9</Version>
 
         <!-- Output/build -->
         <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
@@ -34,7 +34,7 @@
         <PackageReference Include="Google.Protobuf" Version="3.29.3" />
         <PackageReference Include="Grpc" Version="2.46.6" />
         <PackageReference Include="Grpc.Net.Client" Version="2.67.0" />
-        <PackageReference Include="Grpc.Tools" Version="2.69.0" PrivateAssets="All" />
+        <PackageReference Include="Grpc.Tools" Version="2.68.1" PrivateAssets="All" />
         
         <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
 
diff --git a/src/EllieBot/Modules/Administration/Protection/ProtectionService.cs b/src/EllieBot/Modules/Administration/Protection/ProtectionService.cs
index a6716e8..d97442d 100644
--- a/src/EllieBot/Modules/Administration/Protection/ProtectionService.cs
+++ b/src/EllieBot/Modules/Administration/Protection/ProtectionService.cs
@@ -407,6 +407,7 @@ public class ProtectionService : IReadyExecutor, IEService
             if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
                 temp.AntiSpamSettings.IgnoredChannels.Add(obj); // add to local cache
 
+            spam.IgnoredChannels.Add(obj);
             added = true;
         }
         else
diff --git a/src/EllieBot/Modules/Help/CommandListGenerator.cs b/src/EllieBot/Modules/Help/CommandListGenerator.cs
new file mode 100644
index 0000000..212c9b3
--- /dev/null
+++ b/src/EllieBot/Modules/Help/CommandListGenerator.cs
@@ -0,0 +1,62 @@
+#nullable disable
+using System.Globalization;
+using System.Text;
+using Ellie.Common.Marmalade;
+using EllieBot.Common.ModuleBehaviors;
+using Newtonsoft.Json;
+
+namespace EllieBot.Modules.Help;
+
+public sealed partial class CommandListGenerator(
+    CommandService cmds,
+    IMarmaladeLoaderService marmalades,
+    IBotStrings strings
+) : IEService, IReadyExecutor
+{
+    public async Task OnReadyAsync()
+    {
+        await Task.Delay(10_000);
+
+#if DEBUG
+        await GenerateCommandListAsync(".", CultureInfo.InvariantCulture);
+#endif
+    }
+
+    public async Task<Stream> GenerateCommandListAsync(string prefix, CultureInfo culture)
+    {
+        // order commands by top level name
+        // and make a dictionary of <ModuleName, Array<JsonCommandData>>
+        var cmdData = cmds.Commands.GroupBy(x => x.Module.GetTopLevelModule().Name)
+            .OrderBy(x => x.Key)
+            .ToDictionary(x => x.Key,
+                x => x.DistinctBy(c => c.Aliases.First())
+                    .Select(com =>
+                    {
+                        List<string> optHelpStr = null;
+
+                        var opt = CommandsUtilityService.GetEllieOptionType(com.Attributes);
+                        if (opt is not null)
+                            optHelpStr = CommandsUtilityService.GetCommandOptionHelpList(opt);
+
+                        return new CommandJsonObject
+                        {
+                            Aliases = com.Aliases.Select(alias => prefix + alias).ToArray(),
+                            Description = com.RealSummary(strings, marmalades, culture, prefix),
+                            Usage = com.RealRemarksArr(strings, marmalades, culture, prefix),
+                            Submodule = com.Module.Name,
+                            Module = com.Module.GetTopLevelModule().Name,
+                            Options = optHelpStr,
+                            Requirements = CommandsUtilityService.GetCommandRequirements(com)
+                        };
+                    })
+                    .ToList());
+
+        var readableData = JsonConvert.SerializeObject(cmdData, Formatting.Indented);
+        
+        // send the indented file to chat
+        var rDataStream = new MemoryStream(Encoding.ASCII.GetBytes(readableData));
+        await File.WriteAllTextAsync("data/commandlist.json", readableData);
+
+        return rDataStream;
+    }
+}
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Help/Help.cs b/src/EllieBot/Modules/Help/Help.cs
index 095394b..b2503ee 100644
--- a/src/EllieBot/Modules/Help/Help.cs
+++ b/src/EllieBot/Modules/Help/Help.cs
@@ -1,8 +1,6 @@
 #nullable disable
 using EllieBot.Modules.Help.Common;
 using EllieBot.Modules.Help.Services;
-using Newtonsoft.Json;
-using System.Text;
 using Ellie.Common.Marmalade;
 
 namespace EllieBot.Modules.Help;
@@ -22,6 +20,7 @@ public sealed partial class Help : EllieModule<HelpService>
 
     private readonly AsyncLazy<ulong> _lazyClientId;
     private readonly IMarmaladeLoaderService _marmalades;
+    private readonly CommandListGenerator _cmdListGen;
 
     public Help(
         ICommandsUtilityService _cus,
@@ -31,7 +30,8 @@ public sealed partial class Help : EllieModule<HelpService>
         IServiceProvider services,
         DiscordSocketClient client,
         IBotStrings strings,
-        IMarmaladeLoaderService marmalades)
+        IMarmaladeLoaderService marmalades,
+        CommandListGenerator  cmdListGen)
     {
         this._cus = _cus;
         _cmds = cmds;
@@ -41,6 +41,7 @@ public sealed partial class Help : EllieModule<HelpService>
         _client = client;
         _strings = strings;
         _marmalades = marmalades;
+        _cmdListGen = cmdListGen;
 
         _lazyClientId = new(async () => (await _client.GetApplicationInfoAsync()).Id);
     }
@@ -488,38 +489,7 @@ public sealed partial class Help : EllieModule<HelpService>
     {
         _ = ctx.Channel.TriggerTypingAsync();
 
-        // order commands by top level module name
-        // and make a dictionary of <ModuleName, Array<JsonCommandData>>
-        var cmdData = _cmds.Commands.GroupBy(x => x.Module.GetTopLevelModule().Name)
-            .OrderBy(x => x.Key)
-            .ToDictionary(x => x.Key,
-                x => x.DistinctBy(c => c.Aliases.First())
-                    .Select(com =>
-                    {
-                        List<string> optHelpStr = null;
-
-                        var opt = CommandsUtilityService.GetEllieOptionType(com.Attributes);
-                        if (opt is not null)
-                            optHelpStr = CommandsUtilityService.GetCommandOptionHelpList(opt);
-
-                        return new CommandJsonObject
-                        {
-                            Aliases = com.Aliases.Select(alias => prefix + alias).ToArray(),
-                            Description = com.RealSummary(_strings, _marmalades, Culture, prefix),
-                            Usage = com.RealRemarksArr(_strings, _marmalades, Culture, prefix),
-                            Submodule = com.Module.Name,
-                            Module = com.Module.GetTopLevelModule().Name,
-                            Options = optHelpStr,
-                            Requirements = CommandsUtilityService.GetCommandRequirements(com)
-                        };
-                    })
-                    .ToList());
-
-        var readableData = JsonConvert.SerializeObject(cmdData, Formatting.Indented);
-
-        // send the indented file to chat
-        await using var rDataStream = new MemoryStream(Encoding.ASCII.GetBytes(readableData));
-        await File.WriteAllTextAsync("data/commandlist.json", readableData);
+        await using var rDataStream = await _cmdListGen.GenerateCommandListAsync(prefix, Culture);
         await ctx.Channel.SendFileAsync(rDataStream, "cmds.json", GetText(strs.commandlist_regen));
     }
 
diff --git a/src/EllieBot/Modules/Utility/Info/InfoCommands.cs b/src/EllieBot/Modules/Utility/Info/InfoCommands.cs
index 88232a9..0bd8414 100644
--- a/src/EllieBot/Modules/Utility/Info/InfoCommands.cs
+++ b/src/EllieBot/Modules/Utility/Info/InfoCommands.cs
@@ -84,14 +84,29 @@ public partial class Utility
                 return;
             var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22);
             var usercount = (await ch.GetUsersAsync().FlattenAsync()).Count();
-            var embed = CreateEmbed()
-                .WithTitle(ch.Name)
-                .WithDescription(ch.Topic?.SanitizeMentions(true))
-                .AddField(GetText(strs.id), ch.Id.ToString(), true)
-                .AddField(GetText(strs.created_at), $"{createdAt:dd.MM.yyyy HH:mm}", true)
-                .AddField(GetText(strs.users), usercount.ToString(), true)
-                .WithOkColor();
-            await Response().Embed(embed).SendAsync();
+            
+            var users = await ch.GetUsersAsync(CacheMode.CacheOnly).FlattenAsync().Fmap(x => x.ToList());
+
+            await Response()
+                .Paginated()
+                .Items(users)
+                .PageSize(20)
+                .Page((items, _) =>
+                {
+                    var embed = CreateEmbed()
+                        .WithTitle(ch.Name)
+                        .WithDescription(ch.Topic?.SanitizeMentions(true))
+                        .AddField(GetText(strs.id), ch.Id.ToString(), true)
+                        .AddField(GetText(strs.created_at),
+                            TimestampTag.FromDateTime(createdAt, TimestampTagStyles.ShortDate),
+                            true)
+                        .AddField(GetText(strs.users), usercount.ToString(), true)
+                        .AddField(GetText(strs.members), string.Join(" . ", items.Select(x => x.DisplayName)))
+                        .WithOkColor();
+
+                    return embed;
+                })
+                .SendAsync();
         }
 
         [Cmd]
diff --git a/src/EllieBot/Modules/Utility/LiveChannel/LiveChannelCommands.cs b/src/EllieBot/Modules/Utility/LiveChannel/LiveChannelCommands.cs
index 3c3b7b5..b3093f6 100644
--- a/src/EllieBot/Modules/Utility/LiveChannel/LiveChannelCommands.cs
+++ b/src/EllieBot/Modules/Utility/LiveChannel/LiveChannelCommands.cs
@@ -1,4 +1,6 @@
-namespace EllieBot.Modules.Utility.LiveChannel;
+using EllieBot.Modules.Utility.LiveChannel;
+
+namespace EllieBot.Modules.Utility;
 
 public partial class Utility
 {
diff --git a/src/EllieBot/Modules/Utility/Scheduled/ScheduledCommands.cs b/src/EllieBot/Modules/Utility/Scheduled/ScheduledCommands.cs
index 4bb6f92..0972ad5 100644
--- a/src/EllieBot/Modules/Utility/Scheduled/ScheduledCommands.cs
+++ b/src/EllieBot/Modules/Utility/Scheduled/ScheduledCommands.cs
@@ -1,6 +1,7 @@
 using EllieBot.Common.TypeReaders.Models;
+using EllieBot.Modules.Utility.Scheduled;
 
-namespace EllieBot.Modules.Utility.Scheduled;
+namespace EllieBot.Modules.Utility;
 
 public partial class Utility
 {
diff --git a/src/EllieBot/data/commandlist.json b/src/EllieBot/data/commandlist.json
index 01eada3..6eb75ae 100644
--- a/src/EllieBot/data/commandlist.json
+++ b/src/EllieBot/data/commandlist.json
@@ -4528,61 +4528,6 @@
       ]
     }
   ],
-  "LiveChannelCommands": [
-    {
-      "Aliases": [
-        ".livechadd",
-        ".lcha",
-        ".lchadd"
-      ],
-      "Description": "Adds a channel as a live channel with the specified template.\nYou can see a full list of placeholders with `.phs` command.",
-      "Usage": [
-        ".livechadd #general Time: %server.time%",
-        ".livechadd #general -- %server.members% --"
-      ],
-      "Submodule": "LiveChannelCommands",
-      "Module": "LiveChannelCommands",
-      "Options": null,
-      "Requirements": [
-        "ManageChannels Server Permission"
-      ]
-    },
-    {
-      "Aliases": [
-        ".livechlist",
-        ".lchl",
-        ".lchli",
-        ".lchlist"
-      ],
-      "Description": "Lists all live channels in the server.",
-      "Usage": [
-        ".livechlist"
-      ],
-      "Submodule": "LiveChannelCommands",
-      "Module": "LiveChannelCommands",
-      "Options": null,
-      "Requirements": [
-        "ManageChannels Server Permission"
-      ]
-    },
-    {
-      "Aliases": [
-        ".livechremove",
-        ".lchd",
-        ".lchrm"
-      ],
-      "Description": "Removes a live channel.",
-      "Usage": [
-        ".livechremove #general"
-      ],
-      "Submodule": "LiveChannelCommands",
-      "Module": "LiveChannelCommands",
-      "Options": null,
-      "Requirements": [
-        "ManageChannels Server Permission"
-      ]
-    }
-  ],
   "Marmalade": [
     {
       "Aliases": [
@@ -5836,54 +5781,6 @@
       ]
     }
   ],
-  "ScheduledCommands": [
-    {
-      "Aliases": [
-        ".schedulelist",
-        ".schl",
-        ".schli"
-      ],
-      "Description": "Lists your scheduled commands in the current server.",
-      "Usage": [
-        ".schedulelist"
-      ],
-      "Submodule": "ScheduledCommands",
-      "Module": "ScheduledCommands",
-      "Options": null,
-      "Requirements": []
-    },
-    {
-      "Aliases": [
-        ".scheduledelete",
-        ".schd",
-        ".schdel"
-      ],
-      "Description": "Deletes one of your scheduled commands by its ID.",
-      "Usage": [
-        ".scheduledelete 5"
-      ],
-      "Submodule": "ScheduledCommands",
-      "Module": "ScheduledCommands",
-      "Options": null,
-      "Requirements": []
-    },
-    {
-      "Aliases": [
-        ".scheduleadd",
-        ".scha",
-        ".schadd"
-      ],
-      "Description": "Schedules a command to be executed after the specified amount of time.\nYou can schedule up to 5 commands at a time.",
-      "Usage": [
-        ".scheduleadd 1h5m .say Hello after 1 hour and 5 minutes",
-        ".scheduleadd 3h .br all"
-      ],
-      "Submodule": "ScheduledCommands",
-      "Module": "ScheduledCommands",
-      "Options": null,
-      "Requirements": []
-    }
-  ],
   "Searches": [
     {
       "Aliases": [
@@ -7487,6 +7384,59 @@
         "ManageChannels Channel Permission"
       ]
     },
+    {
+      "Aliases": [
+        ".livechadd",
+        ".lcha",
+        ".lchadd"
+      ],
+      "Description": "Adds a channel as a live channel with the specified template.\nYou can see a full list of placeholders with `.phs` command.",
+      "Usage": [
+        ".livechadd #general Time: %server.time%",
+        ".livechadd #general -- %server.members% --"
+      ],
+      "Submodule": "LiveChannelCommands",
+      "Module": "Utility",
+      "Options": null,
+      "Requirements": [
+        "ManageChannels Server Permission"
+      ]
+    },
+    {
+      "Aliases": [
+        ".livechlist",
+        ".lchl",
+        ".lchli",
+        ".lchlist"
+      ],
+      "Description": "Lists all live channels in the server.",
+      "Usage": [
+        ".livechlist"
+      ],
+      "Submodule": "LiveChannelCommands",
+      "Module": "Utility",
+      "Options": null,
+      "Requirements": [
+        "ManageChannels Server Permission"
+      ]
+    },
+    {
+      "Aliases": [
+        ".livechremove",
+        ".lchd",
+        ".lchrm"
+      ],
+      "Description": "Removes a live channel.",
+      "Usage": [
+        ".livechremove #general"
+      ],
+      "Submodule": "LiveChannelCommands",
+      "Module": "Utility",
+      "Options": null,
+      "Requirements": [
+        "ManageChannels Server Permission"
+      ]
+    },
     {
       "Aliases": [
         ".quotelist",
@@ -7837,6 +7787,52 @@
         "ManageMessages Server Permission"
       ]
     },
+    {
+      "Aliases": [
+        ".schedulelist",
+        ".schl",
+        ".schli"
+      ],
+      "Description": "Lists your scheduled commands in the current server.",
+      "Usage": [
+        ".schedulelist"
+      ],
+      "Submodule": "ScheduledCommands",
+      "Module": "Utility",
+      "Options": null,
+      "Requirements": []
+    },
+    {
+      "Aliases": [
+        ".scheduledelete",
+        ".schd",
+        ".schdel"
+      ],
+      "Description": "Deletes one of your scheduled commands by its ID.",
+      "Usage": [
+        ".scheduledelete 5"
+      ],
+      "Submodule": "ScheduledCommands",
+      "Module": "Utility",
+      "Options": null,
+      "Requirements": []
+    },
+    {
+      "Aliases": [
+        ".scheduleadd",
+        ".scha",
+        ".schadd"
+      ],
+      "Description": "Schedules a command to be executed after the specified amount of time.\nYou can schedule up to 5 commands at a time.",
+      "Usage": [
+        ".scheduleadd 1h5m .say Hello after 1 hour and 5 minutes",
+        ".scheduleadd 3h .br all"
+      ],
+      "Submodule": "ScheduledCommands",
+      "Module": "Utility",
+      "Options": null,
+      "Requirements": []
+    },
     {
       "Aliases": [
         ".streamrole"