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
This commit is contained in:
Kwoth
2024-05-20 00:37:29 +00:00
parent e2066f433f
commit 4e570475df
5 changed files with 124 additions and 61 deletions

View File

@@ -4,7 +4,6 @@ using NadekoBot.Modules.Help.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text; using System.Text;
using Nadeko.Common.Medusa; using Nadeko.Common.Medusa;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
namespace NadekoBot.Modules.Help; namespace NadekoBot.Modules.Help;
@@ -89,7 +88,7 @@ public sealed partial class Help : NadekoModule<HelpService>
var menu = new SelectMenuBuilder() var menu = new SelectMenuBuilder()
.WithPlaceholder("Select a module to see its commands") .WithPlaceholder("Select a module to see its commands")
.WithCustomId("modules"); .WithCustomId("cmds:modules_select");
foreach (var m in topLevelModules) foreach (var m in topLevelModules)
menu.AddOption(m.Name, m.Name, GetModuleEmoji(m.Name)); menu.AddOption(m.Name, m.Name, GetModuleEmoji(m.Name));
@@ -102,7 +101,7 @@ public sealed partial class Help : NadekoModule<HelpService>
var val = smc.Data.Values.FirstOrDefault(); var val = smc.Data.Values.FirstOrDefault();
if (val is null) if (val is null)
return; return;
await Commands(val); await Commands(val);
}); });
@@ -242,10 +241,28 @@ public sealed partial class Help : NadekoModule<HelpService>
// order by name // order by name
var allowed = new List<CommandInfo>(); var allowed = new List<CommandInfo>();
foreach (var cmd in _cmds.Commands var mdls = _cmds.Commands
.Where(c => c.Module.GetTopLevelModule() .Where(c => c.Module.GetTopLevelModule()
.Name .Name
.StartsWith(module, StringComparison.InvariantCultureIgnoreCase))) .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, var result = await _perms.CheckPermsAsync(ctx.Guild,
ctx.Channel, ctx.Channel,
@@ -257,6 +274,7 @@ public sealed partial class Help : NadekoModule<HelpService>
allowed.Add(cmd); allowed.Add(cmd);
} }
var cmds = allowed.OrderBy(c => c.Aliases[0]) var cmds = allowed.OrderBy(c => c.Aliases[0])
.DistinctBy(x => x.Aliases[0]) .DistinctBy(x => x.Aliases[0])
.ToList(); .ToList();
@@ -296,55 +314,97 @@ public sealed partial class Help : NadekoModule<HelpService>
return; return;
} }
var cnt = 0; var sb = new SelectMenuBuilder()
var groups = cmdsWithGroup.GroupBy(_ => cnt++ / 48).ToArray(); .WithCustomId("cmds:submodule_select")
.WithPlaceholder("Select a submodule to see detailed commands");
var groups = cmdsWithGroup.ToArray();
var embed = _sender.CreateEmbed().WithOkColor(); var embed = _sender.CreateEmbed().WithOkColor();
foreach (var g in groups) foreach (var g in groups)
{ {
var last = g.Count(); sb.AddOption(g.Key, g.Key);
for (var i = 0; i < last; i++) var transformed = g
{ .Select(x =>
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)
//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]}";
{ }
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))); 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) private async Task Group(ModuleInfo group)
{ {
var eb = _sender.CreateEmbed() var menu = new SelectMenuBuilder()
.WithTitle(GetText(strs.cmd_group_commands(group.Name))) .WithCustomId("cmds:group_select")
.WithOkColor(); .WithPlaceholder("Select a command to see its details");
foreach (var cmd in group.Commands.DistinctBy(x => x.Aliases[0])) foreach (var cmd in group.Commands.DistinctBy(x => x.Aliases[0]))
{ {
string cmdName; menu.AddOption(prefix + cmd.Aliases[0], cmd.Aliases[0]);
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));
} }
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] [Cmd]
@@ -364,12 +424,9 @@ public sealed partial class Help : NadekoModule<HelpService>
var group = _cmds.Modules var group = _cmds.Modules
.SelectMany(x => x.Submodules) .SelectMany(x => x.Submodules)
.FirstOrDefault(x => string.Equals(x.Name?.Replace("Commands", string.Empty), .FirstOrDefault(x => string.Equals(x.Group,
fail, fail,
StringComparison.InvariantCultureIgnoreCase) StringComparison.InvariantCultureIgnoreCase));
|| string.Equals(x.Group,
fail,
StringComparison.InvariantCultureIgnoreCase));
if (group is not null) if (group is not null)
{ {

View File

@@ -6,10 +6,10 @@ public sealed class NadekoButtonInteraction : NadekoInteraction
DiscordSocketClient client, DiscordSocketClient client,
ulong authorId, ulong authorId,
ButtonBuilder button, ButtonBuilder button,
Func<SocketMessageComponent, Task> onClick, Func<SocketMessageComponent, Task> onAction,
bool onlyAuthor, bool onlyAuthor,
bool singleUse = true) bool singleUse = true)
: base(client, authorId, button.CustomId, onClick, onlyAuthor, singleUse) : base(client, authorId, button.CustomId, onAction, onlyAuthor, singleUse)
{ {
Button = button; Button = button;
} }

View File

@@ -6,10 +6,10 @@ public sealed class NadekoSelectInteraction : NadekoInteraction
DiscordSocketClient client, DiscordSocketClient client,
ulong authorId, ulong authorId,
SelectMenuBuilder menu, SelectMenuBuilder menu,
Func<SocketMessageComponent, Task> onClick, Func<SocketMessageComponent, Task> onAction,
bool onlyAuthor, bool onlyAuthor,
bool singleUse = true) bool singleUse = true)
: base(client, authorId, menu.CustomId, onClick, onlyAuthor, singleUse) : base(client, authorId, menu.CustomId, onAction, onlyAuthor, singleUse)
{ {
Menu = menu; Menu = menu;
} }

View File

@@ -3,7 +3,7 @@
public abstract class NadekoInteraction public abstract class NadekoInteraction
{ {
private readonly ulong _authorId; private readonly ulong _authorId;
private readonly Func<SocketMessageComponent, Task> _onClick; private readonly Func<SocketMessageComponent, Task> _onAction;
private readonly bool _onlyAuthor; private readonly bool _onlyAuthor;
public DiscordSocketClient Client { get; } public DiscordSocketClient Client { get; }
@@ -17,13 +17,13 @@ public abstract class NadekoInteraction
DiscordSocketClient client, DiscordSocketClient client,
ulong authorId, ulong authorId,
string customId, string customId,
Func<SocketMessageComponent, Task> onClick, Func<SocketMessageComponent, Task> onAction,
bool onlyAuthor, bool onlyAuthor,
bool singleUse = true) bool singleUse = true)
{ {
_authorId = authorId; _authorId = authorId;
_customId = customId; _customId = customId;
_onClick = onClick; _onAction = onAction;
_onlyAuthor = onlyAuthor; _onlyAuthor = onlyAuthor;
_singleUse = singleUse; _singleUse = singleUse;
_interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously); _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -61,12 +61,19 @@ public abstract class NadekoInteraction
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
_interactionCompletedSource.TrySetResult(true); try
await ExecuteOnActionAsync(smc);
if (!smc.HasResponded)
{ {
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 abstract void AddTo(ComponentBuilder cb);
public Task ExecuteOnActionAsync(SocketMessageComponent smc) public Task ExecuteOnActionAsync(SocketMessageComponent smc)
=> _onClick(smc); => _onAction(smc);
} }

View File

@@ -44,9 +44,8 @@ public partial class ResponseBuilder
var leftButton = new ButtonBuilder() var leftButton = new ButtonBuilder()
.WithStyle(ButtonStyle.Primary) .WithStyle(ButtonStyle.Primary)
.WithCustomId(BUTTON_LEFT) .WithCustomId(BUTTON_LEFT)
.WithDisabled(lastPage == 0)
.WithEmote(InteractionHelpers.ArrowLeft) .WithEmote(InteractionHelpers.ArrowLeft)
.WithDisabled(currentPage <= 0); .WithDisabled(lastPage == 0 || currentPage <= 0);
var leftBtnInter = new NadekoButtonInteraction(_client, var leftBtnInter = new NadekoButtonInteraction(_client,
model.User?.Id ?? 0, model.User?.Id ?? 0,
@@ -78,9 +77,8 @@ public partial class ResponseBuilder
var rightButton = new ButtonBuilder() var rightButton = new ButtonBuilder()
.WithStyle(ButtonStyle.Primary) .WithStyle(ButtonStyle.Primary)
.WithCustomId(BUTTON_RIGHT) .WithCustomId(BUTTON_RIGHT)
.WithDisabled(lastPage == 0)
.WithEmote(InteractionHelpers.ArrowRight) .WithEmote(InteractionHelpers.ArrowRight)
.WithDisabled(lastPage == 0 || currentPage > lastPage); .WithDisabled(lastPage == 0 || currentPage >= lastPage);
var rightBtnInter = new NadekoButtonInteraction(_client, var rightBtnInter = new NadekoButtonInteraction(_client,
model.User?.Id ?? 0, model.User?.Id ?? 0,
@@ -141,6 +139,7 @@ public partial class ResponseBuilder
.SendMessageAsync(model.Text, .SendMessageAsync(model.Text,
embed: embed.Build(), embed: embed.Build(),
components: cb.Build(), components: cb.Build(),
allowedMentions: model.SanitizeMentions,
messageReference: model.MessageReference); messageReference: model.MessageReference);
if (lastPage == 0 && _paginationBuilder.InteractionFunc is null) if (lastPage == 0 && _paginationBuilder.InteractionFunc is null)