From 4e570475df3f259c528d1014f3f29eb5e131cb8d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 20 May 2024 00:37:29 +0000 Subject: [PATCH] add: Added dropdown menu for .cmds help and group help (part of cmds). Group help will no longer be on .h fix: paginated response replies will no longer ping the author --- src/NadekoBot/Modules/Help/Help.cs | 145 ++++++++++++------ .../Models/NadekoButtonInteraction.cs | 4 +- .../Models/NadekoSelectInteraction.cs | 4 +- .../_common/Interaction/NadekoInteraction.cs | 25 +-- .../ResponseBuilder.PaginationSender.cs | 7 +- 5 files changed, 124 insertions(+), 61 deletions(-) diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index f70fbf0a8..fc29aef87 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -4,7 +4,6 @@ using NadekoBot.Modules.Help.Services; using Newtonsoft.Json; using System.Text; using Nadeko.Common.Medusa; -using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping; namespace NadekoBot.Modules.Help; @@ -89,7 +88,7 @@ public sealed partial class Help : NadekoModule var menu = new SelectMenuBuilder() .WithPlaceholder("Select a module to see its commands") - .WithCustomId("modules"); + .WithCustomId("cmds:modules_select"); foreach (var m in topLevelModules) menu.AddOption(m.Name, m.Name, GetModuleEmoji(m.Name)); @@ -102,7 +101,7 @@ public sealed partial class Help : NadekoModule var val = smc.Data.Values.FirstOrDefault(); if (val is null) return; - + await Commands(val); }); @@ -242,10 +241,28 @@ public sealed partial class Help : NadekoModule // order by name var allowed = new List(); - foreach (var cmd in _cmds.Commands - .Where(c => c.Module.GetTopLevelModule() - .Name - .StartsWith(module, StringComparison.InvariantCultureIgnoreCase))) + var mdls = _cmds.Commands + .Where(c => c.Module.GetTopLevelModule() + .Name + .StartsWith(module, StringComparison.InvariantCultureIgnoreCase)) + .ToArray(); + + if (mdls.Length == 0) + { + var group = _cmds.Modules + .Where(x => x.Parent is not null) + .FirstOrDefault(x => string.Equals(x.Name.Replace("Commands", ""), + module, + StringComparison.InvariantCultureIgnoreCase)); + + if (group is not null) + { + await Group(group); + return; + } + } + + foreach (var cmd in mdls) { var result = await _perms.CheckPermsAsync(ctx.Guild, ctx.Channel, @@ -257,6 +274,7 @@ public sealed partial class Help : NadekoModule allowed.Add(cmd); } + var cmds = allowed.OrderBy(c => c.Aliases[0]) .DistinctBy(x => x.Aliases[0]) .ToList(); @@ -296,55 +314,97 @@ public sealed partial class Help : NadekoModule return; } - var cnt = 0; - var groups = cmdsWithGroup.GroupBy(_ => cnt++ / 48).ToArray(); + var sb = new SelectMenuBuilder() + .WithCustomId("cmds:submodule_select") + .WithPlaceholder("Select a submodule to see detailed commands"); + + var groups = cmdsWithGroup.ToArray(); var embed = _sender.CreateEmbed().WithOkColor(); foreach (var g in groups) { - var last = g.Count(); - for (var i = 0; i < last; i++) - { - var transformed = g.ElementAt(i) - .Select(x => - { - //if cross is specified, and the command doesn't satisfy the requirements, cross it out - if (opts.View == CommandsOptions.ViewType.Cross) - { - return $"{(succ.Contains(x) ? "✅" : "❌")} {prefix + x.Aliases[0]}"; - } + sb.AddOption(g.Key, g.Key); + var transformed = g + .Select(x => + { + //if cross is specified, and the command doesn't satisfy the requirements, cross it out + if (opts.View == CommandsOptions.ViewType.Cross) + { + return $"{(succ.Contains(x) ? "✅" : "❌")} {prefix + x.Aliases[0]}"; + } - if (x.Aliases.Count == 1) - return prefix + x.Aliases[0]; - return prefix + x.Aliases[0] + " | " + prefix + x.Aliases[1]; - }); + if (x.Aliases.Count == 1) + return prefix + x.Aliases[0]; - embed.AddField(g.ElementAt(i).Key, "" + string.Join("\n", transformed) + "", true); - } + return prefix + x.Aliases[0] + " | " + prefix + x.Aliases[1]; + }); + + embed.AddField(g.Key, "" + string.Join("\n", transformed) + "", true); } embed.WithFooter(GetText(strs.commands_instr(prefix))); - await Response().Embed(embed).SendAsync(); + + + var inter = _inter.Create(ctx.User.Id, + sb, + async (smc) => + { + var groupName = smc.Data.Values.FirstOrDefault(); + var mdl = _cmds.Modules.FirstOrDefault(x + => string.Equals(x.Name.Replace("Commands", ""), groupName, StringComparison.InvariantCultureIgnoreCase)); + await smc.DeferAsync(); + await Group(mdl); + } + ); + + await Response().Embed(embed).Interaction(inter).SendAsync(); } private async Task Group(ModuleInfo group) { - var eb = _sender.CreateEmbed() - .WithTitle(GetText(strs.cmd_group_commands(group.Name))) - .WithOkColor(); + var menu = new SelectMenuBuilder() + .WithCustomId("cmds:group_select") + .WithPlaceholder("Select a command to see its details"); foreach (var cmd in group.Commands.DistinctBy(x => x.Aliases[0])) { - string cmdName; - if (cmd.Aliases.Count > 1) - cmdName = Format.Code(prefix + cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]); - else - cmdName = Format.Code(prefix + cmd.Aliases.First()); - - eb.AddField(cmdName, cmd.RealSummary(_strings, _medusae, Culture, prefix)); + menu.AddOption(prefix + cmd.Aliases[0], cmd.Aliases[0]); } - await Response().Embed(eb).SendAsync(); + var inter = _inter.Create(ctx.User.Id, + menu, + async (smc) => + { + await smc.DeferAsync(); + + await H(smc.Data.Values.FirstOrDefault()); + }); + + await Response() + .Paginated() + .Items(group.Commands.DistinctBy(x => x.Aliases[0]).ToArray()) + .PageSize(25) + .Interaction(inter) + .Page((items, _) => + { + var eb = _sender.CreateEmbed() + .WithTitle(GetText(strs.cmd_group_commands(group.Name))) + .WithOkColor(); + + foreach (var cmd in items) + { + string cmdName; + if (cmd.Aliases.Count > 1) + cmdName = Format.Code(prefix + cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]); + else + cmdName = Format.Code(prefix + cmd.Aliases.First()); + + eb.AddField(cmdName, cmd.RealSummary(_strings, _medusae, Culture, prefix)); + } + + return eb; + }) + .SendAsync(); } [Cmd] @@ -364,12 +424,9 @@ public sealed partial class Help : NadekoModule var group = _cmds.Modules .SelectMany(x => x.Submodules) - .FirstOrDefault(x => string.Equals(x.Name?.Replace("Commands", string.Empty), - fail, - StringComparison.InvariantCultureIgnoreCase) - || string.Equals(x.Group, - fail, - StringComparison.InvariantCultureIgnoreCase)); + .FirstOrDefault(x => string.Equals(x.Group, + fail, + StringComparison.InvariantCultureIgnoreCase)); if (group is not null) { diff --git a/src/NadekoBot/_common/Interaction/Models/NadekoButtonInteraction.cs b/src/NadekoBot/_common/Interaction/Models/NadekoButtonInteraction.cs index 7ceb158c7..f1fd2cd61 100644 --- a/src/NadekoBot/_common/Interaction/Models/NadekoButtonInteraction.cs +++ b/src/NadekoBot/_common/Interaction/Models/NadekoButtonInteraction.cs @@ -6,10 +6,10 @@ public sealed class NadekoButtonInteraction : NadekoInteraction DiscordSocketClient client, ulong authorId, ButtonBuilder button, - Func onClick, + Func onAction, bool onlyAuthor, bool singleUse = true) - : base(client, authorId, button.CustomId, onClick, onlyAuthor, singleUse) + : base(client, authorId, button.CustomId, onAction, onlyAuthor, singleUse) { Button = button; } diff --git a/src/NadekoBot/_common/Interaction/Models/NadekoSelectInteraction.cs b/src/NadekoBot/_common/Interaction/Models/NadekoSelectInteraction.cs index 4eef84c21..3bdef588a 100644 --- a/src/NadekoBot/_common/Interaction/Models/NadekoSelectInteraction.cs +++ b/src/NadekoBot/_common/Interaction/Models/NadekoSelectInteraction.cs @@ -6,10 +6,10 @@ public sealed class NadekoSelectInteraction : NadekoInteraction DiscordSocketClient client, ulong authorId, SelectMenuBuilder menu, - Func onClick, + Func onAction, bool onlyAuthor, bool singleUse = true) - : base(client, authorId, menu.CustomId, onClick, onlyAuthor, singleUse) + : base(client, authorId, menu.CustomId, onAction, onlyAuthor, singleUse) { Menu = menu; } diff --git a/src/NadekoBot/_common/Interaction/NadekoInteraction.cs b/src/NadekoBot/_common/Interaction/NadekoInteraction.cs index 8f783ea0e..29936c0b3 100644 --- a/src/NadekoBot/_common/Interaction/NadekoInteraction.cs +++ b/src/NadekoBot/_common/Interaction/NadekoInteraction.cs @@ -3,7 +3,7 @@ public abstract class NadekoInteraction { private readonly ulong _authorId; - private readonly Func _onClick; + private readonly Func _onAction; private readonly bool _onlyAuthor; public DiscordSocketClient Client { get; } @@ -17,13 +17,13 @@ public abstract class NadekoInteraction DiscordSocketClient client, ulong authorId, string customId, - Func onClick, + Func onAction, bool onlyAuthor, bool singleUse = true) { _authorId = authorId; _customId = customId; - _onClick = onClick; + _onAction = onAction; _onlyAuthor = onlyAuthor; _singleUse = singleUse; _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -61,12 +61,19 @@ public abstract class NadekoInteraction _ = Task.Run(async () => { - _interactionCompletedSource.TrySetResult(true); - await ExecuteOnActionAsync(smc); - - if (!smc.HasResponded) + try { - await smc.DeferAsync(); + _interactionCompletedSource.TrySetResult(true); + await ExecuteOnActionAsync(smc); + + if (!smc.HasResponded) + { + await smc.DeferAsync(); + } + } + catch (Exception ex) + { + Log.Warning(ex, "An exception occured while handling an interaction: {Message}", ex.Message); } }); @@ -77,5 +84,5 @@ public abstract class NadekoInteraction public abstract void AddTo(ComponentBuilder cb); public Task ExecuteOnActionAsync(SocketMessageComponent smc) - => _onClick(smc); + => _onAction(smc); } \ No newline at end of file diff --git a/src/NadekoBot/_common/Sender/ResponseBuilder.PaginationSender.cs b/src/NadekoBot/_common/Sender/ResponseBuilder.PaginationSender.cs index de0a7d67a..ea43c15ad 100644 --- a/src/NadekoBot/_common/Sender/ResponseBuilder.PaginationSender.cs +++ b/src/NadekoBot/_common/Sender/ResponseBuilder.PaginationSender.cs @@ -44,9 +44,8 @@ public partial class ResponseBuilder var leftButton = new ButtonBuilder() .WithStyle(ButtonStyle.Primary) .WithCustomId(BUTTON_LEFT) - .WithDisabled(lastPage == 0) .WithEmote(InteractionHelpers.ArrowLeft) - .WithDisabled(currentPage <= 0); + .WithDisabled(lastPage == 0 || currentPage <= 0); var leftBtnInter = new NadekoButtonInteraction(_client, model.User?.Id ?? 0, @@ -78,9 +77,8 @@ public partial class ResponseBuilder var rightButton = new ButtonBuilder() .WithStyle(ButtonStyle.Primary) .WithCustomId(BUTTON_RIGHT) - .WithDisabled(lastPage == 0) .WithEmote(InteractionHelpers.ArrowRight) - .WithDisabled(lastPage == 0 || currentPage > lastPage); + .WithDisabled(lastPage == 0 || currentPage >= lastPage); var rightBtnInter = new NadekoButtonInteraction(_client, model.User?.Id ?? 0, @@ -141,6 +139,7 @@ public partial class ResponseBuilder .SendMessageAsync(model.Text, embed: embed.Build(), components: cb.Build(), + allowedMentions: model.SanitizeMentions, messageReference: model.MessageReference); if (lastPage == 0 && _paginationBuilder.InteractionFunc is null)