Command execution code rework and TypeConverters auto-scope fix (#2306)
* command execution rework and sync service scopes for typeconverters * replace ValueTask with Task * fix implementation bugs Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>
This commit is contained in:
@@ -23,7 +23,7 @@ namespace Discord.Interactions
|
||||
public string CommandName { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IReadOnlyCollection<CommandParameterInfo> Parameters { get; }
|
||||
public override IReadOnlyList<CommandParameterInfo> Parameters { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool SupportsWildCards => false;
|
||||
@@ -41,9 +41,12 @@ namespace Discord.Interactions
|
||||
if (context.Interaction is not IAutocompleteInteraction)
|
||||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Autocomplete Interaction");
|
||||
|
||||
return await RunAsync(context, Array.Empty<object>(), services).ConfigureAwait(false);
|
||||
return await base.ExecuteAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services)
|
||||
=> Task.FromResult(ParseResult.FromSuccess(Array.Empty<object>()) as IResult);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Task InvokeModuleEvent(IInteractionContext context, IResult result) =>
|
||||
CommandService._autocompleteCommandExecutedEvent.InvokeAsync(this, context, result);
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Discord.Interactions
|
||||
public IReadOnlyCollection<PreconditionAttribute> Preconditions { get; }
|
||||
|
||||
/// <inheritdoc cref="ICommandInfo.Parameters"/>
|
||||
public abstract IReadOnlyCollection<TParameter> Parameters { get; }
|
||||
public abstract IReadOnlyList<TParameter> Parameters { get; }
|
||||
|
||||
internal CommandInfo(Builders.ICommandBuilder builder, ModuleInfo module, InteractionService commandService)
|
||||
{
|
||||
@@ -85,71 +85,16 @@ namespace Discord.Interactions
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services);
|
||||
protected abstract Task InvokeModuleEvent(IInteractionContext context, IResult result);
|
||||
protected abstract string GetLogString(IInteractionContext context);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<PreconditionResult> CheckPreconditionsAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
async Task<PreconditionResult> CheckGroups(ILookup<string, PreconditionAttribute> preconditions, string type)
|
||||
{
|
||||
foreach (IGrouping<string, PreconditionAttribute> preconditionGroup in preconditions)
|
||||
{
|
||||
if (preconditionGroup.Key == null)
|
||||
{
|
||||
foreach (PreconditionAttribute precondition in preconditionGroup)
|
||||
{
|
||||
var result = await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false);
|
||||
if (!result.IsSuccess)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var results = new List<PreconditionResult>();
|
||||
foreach (PreconditionAttribute precondition in preconditionGroup)
|
||||
results.Add(await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false));
|
||||
|
||||
if (!results.Any(p => p.IsSuccess))
|
||||
return PreconditionGroupResult.FromError($"{type} precondition group {preconditionGroup.Key} failed.", results);
|
||||
}
|
||||
}
|
||||
return PreconditionGroupResult.FromSuccess();
|
||||
}
|
||||
|
||||
var moduleResult = await CheckGroups(Module.GroupedPreconditions, "Module").ConfigureAwait(false);
|
||||
if (!moduleResult.IsSuccess)
|
||||
return moduleResult;
|
||||
|
||||
var commandResult = await CheckGroups(_groupedPreconditions, "Command").ConfigureAwait(false);
|
||||
return !commandResult.IsSuccess ? commandResult : PreconditionResult.FromSuccess();
|
||||
}
|
||||
|
||||
protected async Task<IResult> RunAsync(IInteractionContext context, object[] args, IServiceProvider services)
|
||||
public virtual async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
switch (RunMode)
|
||||
{
|
||||
case RunMode.Sync:
|
||||
{
|
||||
if (CommandService._autoServiceScopes)
|
||||
{
|
||||
using var scope = services?.CreateScope();
|
||||
return await ExecuteInternalAsync(context, args, scope?.ServiceProvider ?? EmptyServiceProvider.Instance).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return await ExecuteInternalAsync(context, args, services).ConfigureAwait(false);
|
||||
}
|
||||
return await ExecuteInternalAsync(context, services).ConfigureAwait(false);
|
||||
case RunMode.Async:
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (CommandService._autoServiceScopes)
|
||||
{
|
||||
using var scope = services?.CreateScope();
|
||||
await ExecuteInternalAsync(context, args, scope?.ServiceProvider ?? EmptyServiceProvider.Instance).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await ExecuteInternalAsync(context, args, services).ConfigureAwait(false);
|
||||
await ExecuteInternalAsync(context, services).ConfigureAwait(false);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@@ -159,16 +104,33 @@ namespace Discord.Interactions
|
||||
return ExecuteResult.FromSuccess();
|
||||
}
|
||||
|
||||
private async Task<IResult> ExecuteInternalAsync(IInteractionContext context, object[] args, IServiceProvider services)
|
||||
protected abstract Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services);
|
||||
|
||||
private async Task<IResult> ExecuteInternalAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
await CommandService._cmdLogger.DebugAsync($"Executing {GetLogString(context)}").ConfigureAwait(false);
|
||||
|
||||
using var scope = services?.CreateScope();
|
||||
|
||||
if (CommandService._autoServiceScopes)
|
||||
services = scope?.ServiceProvider ?? EmptyServiceProvider.Instance;
|
||||
|
||||
try
|
||||
{
|
||||
var preconditionResult = await CheckPreconditionsAsync(context, services).ConfigureAwait(false);
|
||||
if (!preconditionResult.IsSuccess)
|
||||
return await InvokeEventAndReturn(context, preconditionResult).ConfigureAwait(false);
|
||||
|
||||
var argsResult = await ParseArgumentsAsync(context, services).ConfigureAwait(false);
|
||||
|
||||
if (!argsResult.IsSuccess)
|
||||
return await InvokeEventAndReturn(context, argsResult).ConfigureAwait(false);
|
||||
|
||||
if(argsResult is not ParseResult parseResult)
|
||||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Complex command parsing failed for an unknown reason.");
|
||||
|
||||
var args = parseResult.Args;
|
||||
|
||||
var index = 0;
|
||||
foreach (var parameter in Parameters)
|
||||
{
|
||||
@@ -221,7 +183,47 @@ namespace Discord.Interactions
|
||||
}
|
||||
}
|
||||
|
||||
protected async ValueTask<IResult> InvokeEventAndReturn(IInteractionContext context, IResult result)
|
||||
protected abstract Task InvokeModuleEvent(IInteractionContext context, IResult result);
|
||||
protected abstract string GetLogString(IInteractionContext context);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<PreconditionResult> CheckPreconditionsAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
async Task<PreconditionResult> CheckGroups(ILookup<string, PreconditionAttribute> preconditions, string type)
|
||||
{
|
||||
foreach (IGrouping<string, PreconditionAttribute> preconditionGroup in preconditions)
|
||||
{
|
||||
if (preconditionGroup.Key == null)
|
||||
{
|
||||
foreach (PreconditionAttribute precondition in preconditionGroup)
|
||||
{
|
||||
var result = await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false);
|
||||
if (!result.IsSuccess)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var results = new List<PreconditionResult>();
|
||||
foreach (PreconditionAttribute precondition in preconditionGroup)
|
||||
results.Add(await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false));
|
||||
|
||||
if (!results.Any(p => p.IsSuccess))
|
||||
return PreconditionGroupResult.FromError($"{type} precondition group {preconditionGroup.Key} failed.", results);
|
||||
}
|
||||
}
|
||||
return PreconditionGroupResult.FromSuccess();
|
||||
}
|
||||
|
||||
var moduleResult = await CheckGroups(Module.GroupedPreconditions, "Module").ConfigureAwait(false);
|
||||
if (!moduleResult.IsSuccess)
|
||||
return moduleResult;
|
||||
|
||||
var commandResult = await CheckGroups(_groupedPreconditions, "Command").ConfigureAwait(false);
|
||||
return !commandResult.IsSuccess ? commandResult : PreconditionResult.FromSuccess();
|
||||
}
|
||||
|
||||
protected async Task<T> InvokeEventAndReturn<T>(IInteractionContext context, T result) where T : IResult
|
||||
{
|
||||
await InvokeModuleEvent(context, result).ConfigureAwait(false);
|
||||
return result;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Discord.Interactions
|
||||
public class ComponentCommandInfo : CommandInfo<ComponentCommandParameterInfo>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override IReadOnlyCollection<ComponentCommandParameterInfo> Parameters { get; }
|
||||
public override IReadOnlyList<ComponentCommandParameterInfo> Parameters { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool SupportsWildCards => true;
|
||||
@@ -25,48 +25,32 @@ namespace Discord.Interactions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services)
|
||||
=> await ExecuteAsync(context, services, null).ConfigureAwait(false);
|
||||
|
||||
/// <summary>
|
||||
/// Execute this command using dependency injection.
|
||||
/// </summary>
|
||||
/// <param name="context">Context that will be injected to the <see cref="InteractionModuleBase{T}"/>.</param>
|
||||
/// <param name="services">Services that will be used while initializing the <see cref="InteractionModuleBase{T}"/>.</param>
|
||||
/// <param name="additionalArgs">Provide additional string parameters to the method along with the auto generated parameters.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous command execution process.
|
||||
/// </returns>
|
||||
public async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services, params string[] additionalArgs)
|
||||
{
|
||||
if (context.Interaction is not IComponentInteraction componentInteraction)
|
||||
if (context.Interaction is not IComponentInteraction)
|
||||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Message Component Interaction");
|
||||
|
||||
return await ExecuteAsync(context, Parameters, additionalArgs, componentInteraction.Data, services);
|
||||
return await base.ExecuteAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IResult> ExecuteAsync(IInteractionContext context, IEnumerable<CommandParameterInfo> paramList, IEnumerable<string> wildcardCaptures, IComponentInteractionData data,
|
||||
IServiceProvider services)
|
||||
protected override async Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
var paramCount = paramList.Count();
|
||||
var captureCount = wildcardCaptures?.Count() ?? 0;
|
||||
|
||||
if (context.Interaction is not IComponentInteraction messageComponent)
|
||||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Component Command Interaction");
|
||||
var captures = (context as IRouteMatchContainer)?.SegmentMatches?.ToList();
|
||||
var captureCount = captures?.Count() ?? 0;
|
||||
|
||||
try
|
||||
{
|
||||
var args = new object[paramCount];
|
||||
var data = (context.Interaction as IComponentInteraction).Data;
|
||||
var args = new object[Parameters.Count];
|
||||
|
||||
for (var i = 0; i < paramCount; i++)
|
||||
for(var i = 0; i < Parameters.Count; i++)
|
||||
{
|
||||
var parameter = Parameters.ElementAt(i);
|
||||
var parameter = Parameters[i];
|
||||
var isCapture = i < captureCount;
|
||||
|
||||
if (isCapture ^ parameter.IsRouteSegmentParameter)
|
||||
return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.BadArgs, "Argument type and parameter type didn't match (Wild Card capture/Component value)")).ConfigureAwait(false);
|
||||
|
||||
var readResult = isCapture ? await parameter.TypeReader.ReadAsync(context, wildcardCaptures.ElementAt(i), services).ConfigureAwait(false) :
|
||||
var readResult = isCapture ? await parameter.TypeReader.ReadAsync(context, captures[i].Value, services).ConfigureAwait(false) :
|
||||
await parameter.TypeConverter.ReadAsync(context, data, services).ConfigureAwait(false);
|
||||
|
||||
if (!readResult.IsSuccess)
|
||||
@@ -75,7 +59,7 @@ namespace Discord.Interactions
|
||||
args[i] = readResult.Value;
|
||||
}
|
||||
|
||||
return await RunAsync(context, args, services).ConfigureAwait(false);
|
||||
return ParseResult.FromSuccess(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Discord.Interactions
|
||||
public GuildPermission? DefaultMemberPermissions { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IReadOnlyCollection<CommandParameterInfo> Parameters { get; }
|
||||
public override IReadOnlyList<CommandParameterInfo> Parameters { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool SupportsWildCards => false;
|
||||
|
||||
@@ -14,18 +14,23 @@ namespace Discord.Interactions
|
||||
/// <inheritdoc/>
|
||||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
if (context.Interaction is not IMessageCommandInteraction messageCommand)
|
||||
if (context.Interaction is not IMessageCommandInteraction)
|
||||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Message Command Interation");
|
||||
|
||||
return await base.ExecuteAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
try
|
||||
{
|
||||
object[] args = new object[1] { messageCommand.Data.Message };
|
||||
object[] args = new object[1] { (context.Interaction as IMessageCommandInteraction).Data.Message };
|
||||
|
||||
return await RunAsync(context, args, services).ConfigureAwait(false);
|
||||
return Task.FromResult(ParseResult.FromSuccess(args) as IResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ExecuteResult.FromError(ex);
|
||||
return Task.FromResult(ParseResult.FromError(ex) as IResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,20 @@ namespace Discord.Interactions
|
||||
if (context.Interaction is not IUserCommandInteraction userCommand)
|
||||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Message Command Interation");
|
||||
|
||||
return await base.ExecuteAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
try
|
||||
{
|
||||
object[] args = new object[1] { userCommand.Data.User };
|
||||
object[] args = new object[1] { (context.Interaction as IUserCommandInteraction).Data.User };
|
||||
|
||||
return await RunAsync(context, args, services).ConfigureAwait(false);
|
||||
return Task.FromResult(ParseResult.FromSuccess(args) as IResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ExecuteResult.FromError(ex);
|
||||
return Task.FromResult(ParseResult.FromError(ex) as IResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
namespace Discord.Interactions
|
||||
@@ -20,7 +19,7 @@ namespace Discord.Interactions
|
||||
public override bool SupportsWildCards => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IReadOnlyCollection<ModalCommandParameterInfo> Parameters { get; }
|
||||
public override IReadOnlyList<ModalCommandParameterInfo> Parameters { get; }
|
||||
|
||||
internal ModalCommandInfo(Builders.ModalCommandBuilder builder, ModuleInfo module, InteractionService commandService) : base(builder, module, commandService)
|
||||
{
|
||||
@@ -30,34 +29,29 @@ namespace Discord.Interactions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services)
|
||||
=> await ExecuteAsync(context, services, null).ConfigureAwait(false);
|
||||
|
||||
/// <summary>
|
||||
/// Execute this command using dependency injection.
|
||||
/// </summary>
|
||||
/// <param name="context">Context that will be injected to the <see cref="InteractionModuleBase{T}"/>.</param>
|
||||
/// <param name="services">Services that will be used while initializing the <see cref="InteractionModuleBase{T}"/>.</param>
|
||||
/// <param name="additionalArgs">Provide additional string parameters to the method along with the auto generated parameters.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous command execution process.
|
||||
/// </returns>
|
||||
public async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services, params string[] additionalArgs)
|
||||
{
|
||||
if (context.Interaction is not IModalInteraction modalInteraction)
|
||||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Modal Interaction.");
|
||||
|
||||
return await base.ExecuteAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override async Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
var captures = (context as IRouteMatchContainer)?.SegmentMatches?.ToList();
|
||||
var captureCount = captures?.Count() ?? 0;
|
||||
|
||||
try
|
||||
{
|
||||
var args = new object[Parameters.Count];
|
||||
var captureCount = additionalArgs?.Length ?? 0;
|
||||
|
||||
for(var i = 0; i < Parameters.Count; i++)
|
||||
for (var i = 0; i < Parameters.Count; i++)
|
||||
{
|
||||
var parameter = Parameters.ElementAt(i);
|
||||
|
||||
if(i < captureCount)
|
||||
if (i < captureCount)
|
||||
{
|
||||
var readResult = await parameter.TypeReader.ReadAsync(context, additionalArgs[i], services).ConfigureAwait(false);
|
||||
var readResult = await parameter.TypeReader.ReadAsync(context, captures[i].Value, services).ConfigureAwait(false);
|
||||
if (!readResult.IsSuccess)
|
||||
return await InvokeEventAndReturn(context, readResult).ConfigureAwait(false);
|
||||
|
||||
@@ -69,13 +63,14 @@ namespace Discord.Interactions
|
||||
if (!modalResult.IsSuccess)
|
||||
return await InvokeEventAndReturn(context, modalResult).ConfigureAwait(false);
|
||||
|
||||
if (modalResult is not ParseResult parseResult)
|
||||
if (modalResult is not TypeConverterResult converterResult)
|
||||
return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason."));
|
||||
|
||||
args[i] = parseResult.Value;
|
||||
args[i] = converterResult.Value;
|
||||
}
|
||||
}
|
||||
return await RunAsync(context, args, services);
|
||||
|
||||
return ParseResult.FromSuccess(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Discord.Interactions
|
||||
public GuildPermission? DefaultMemberPermissions { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IReadOnlyCollection<SlashCommandParameterInfo> Parameters { get; }
|
||||
public override IReadOnlyList<SlashCommandParameterInfo> Parameters { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool SupportsWildCards => false;
|
||||
@@ -41,9 +41,9 @@ namespace Discord.Interactions
|
||||
/// <summary>
|
||||
/// Gets the flattened collection of command parameters and complex parameter fields.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<SlashCommandParameterInfo> FlattenedParameters { get; }
|
||||
public IReadOnlyList<SlashCommandParameterInfo> FlattenedParameters { get; }
|
||||
|
||||
internal SlashCommandInfo (Builders.SlashCommandBuilder builder, ModuleInfo module, InteractionService commandService) : base(builder, module, commandService)
|
||||
internal SlashCommandInfo(Builders.SlashCommandBuilder builder, ModuleInfo module, InteractionService commandService) : base(builder, module, commandService)
|
||||
{
|
||||
Description = builder.Description;
|
||||
DefaultPermission = builder.DefaultPermission;
|
||||
@@ -60,49 +60,45 @@ namespace Discord.Interactions
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<IResult> ExecuteAsync (IInteractionContext context, IServiceProvider services)
|
||||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
if(context.Interaction is not ISlashCommandInteraction slashCommand)
|
||||
if (context.Interaction is not ISlashCommandInteraction)
|
||||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Slash Command Interaction");
|
||||
|
||||
var options = slashCommand.Data.Options;
|
||||
|
||||
while (options != null && options.Any(x => x.Type == ApplicationCommandOptionType.SubCommand || x.Type == ApplicationCommandOptionType.SubCommandGroup))
|
||||
options = options.ElementAt(0)?.Options;
|
||||
|
||||
return await ExecuteAsync(context, Parameters, options?.ToList(), services);
|
||||
return await base.ExecuteAsync(context, services);
|
||||
}
|
||||
|
||||
private async Task<IResult> ExecuteAsync (IInteractionContext context, IEnumerable<SlashCommandParameterInfo> paramList,
|
||||
List<IApplicationCommandInteractionDataOption> argList, IServiceProvider services)
|
||||
protected override async Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services)
|
||||
{
|
||||
try
|
||||
List<IApplicationCommandInteractionDataOption> GetOptions()
|
||||
{
|
||||
var slashCommandParameterInfos = paramList.ToList();
|
||||
var args = new object[slashCommandParameterInfos.Count];
|
||||
var options = (context.Interaction as ISlashCommandInteraction).Data.Options;
|
||||
|
||||
for (var i = 0; i < slashCommandParameterInfos.Count; i++)
|
||||
{
|
||||
var parameter = slashCommandParameterInfos[i];
|
||||
var result = await ParseArgument(parameter, context, argList, services).ConfigureAwait(false);
|
||||
while (options != null && options.Any(x => x.Type == ApplicationCommandOptionType.SubCommand || x.Type == ApplicationCommandOptionType.SubCommandGroup))
|
||||
options = options.ElementAt(0)?.Options;
|
||||
|
||||
if (!result.IsSuccess)
|
||||
return await InvokeEventAndReturn(context, result).ConfigureAwait(false);
|
||||
|
||||
if (result is not ParseResult parseResult)
|
||||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason.");
|
||||
|
||||
args[i] = parseResult.Value;
|
||||
}
|
||||
return await RunAsync(context, args, services).ConfigureAwait(false);
|
||||
return options.ToList();
|
||||
}
|
||||
catch(Exception ex)
|
||||
|
||||
var options = GetOptions();
|
||||
var args = new object[Parameters.Count];
|
||||
for(var i = 0; i < Parameters.Count; i++)
|
||||
{
|
||||
return await InvokeEventAndReturn(context, ExecuteResult.FromError(ex)).ConfigureAwait(false);
|
||||
var parameter = Parameters[i];
|
||||
var result = await ParseArgumentAsync(parameter, context, options, services).ConfigureAwait(false);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
return await InvokeEventAndReturn(context, ParseResult.FromError(result)).ConfigureAwait(false);
|
||||
|
||||
if (result is not TypeConverterResult converterResult)
|
||||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Complex command parsing failed for an unknown reason.");
|
||||
|
||||
args[i] = converterResult.Value;
|
||||
}
|
||||
return ParseResult.FromSuccess(args);
|
||||
}
|
||||
|
||||
private async Task<IResult> ParseArgument(SlashCommandParameterInfo parameterInfo, IInteractionContext context, List<IApplicationCommandInteractionDataOption> argList,
|
||||
private async ValueTask<IResult> ParseArgumentAsync(SlashCommandParameterInfo parameterInfo, IInteractionContext context, List<IApplicationCommandInteractionDataOption> argList,
|
||||
IServiceProvider services)
|
||||
{
|
||||
if (parameterInfo.IsComplexParameter)
|
||||
@@ -111,32 +107,29 @@ namespace Discord.Interactions
|
||||
|
||||
for (var i = 0; i < ctorArgs.Length; i++)
|
||||
{
|
||||
var result = await ParseArgument(parameterInfo.ComplexParameterFields.ElementAt(i), context, argList, services).ConfigureAwait(false);
|
||||
var result = await ParseArgumentAsync(parameterInfo.ComplexParameterFields.ElementAt(i), context, argList, services).ConfigureAwait(false);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
return result;
|
||||
|
||||
if (result is not ParseResult parseResult)
|
||||
if (result is not TypeConverterResult converterResult)
|
||||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Complex command parsing failed for an unknown reason.");
|
||||
|
||||
ctorArgs[i] = parseResult.Value;
|
||||
ctorArgs[i] = converterResult.Value;
|
||||
}
|
||||
|
||||
return ParseResult.FromSuccess(parameterInfo._complexParameterInitializer(ctorArgs));
|
||||
return TypeConverterResult.FromSuccess(parameterInfo._complexParameterInitializer(ctorArgs));
|
||||
}
|
||||
|
||||
var arg = argList?.Find(x => string.Equals(x.Name, parameterInfo.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (arg == default)
|
||||
return parameterInfo.IsRequired ? ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command was invoked with too few parameters") :
|
||||
ParseResult.FromSuccess(parameterInfo.DefaultValue);
|
||||
TypeConverterResult.FromSuccess(parameterInfo.DefaultValue);
|
||||
|
||||
var typeConverter = parameterInfo.TypeConverter;
|
||||
var readResult = await typeConverter.ReadAsync(context, arg, services).ConfigureAwait(false);
|
||||
if (!readResult.IsSuccess)
|
||||
return readResult;
|
||||
|
||||
return ParseResult.FromSuccess(readResult.Value);
|
||||
return readResult;
|
||||
}
|
||||
|
||||
protected override Task InvokeModuleEvent (IInteractionContext context, IResult result)
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Discord.Interactions
|
||||
public async Task<IResult> CreateModalAsync(IInteractionContext context, IServiceProvider services = null, bool throwOnMissingField = false)
|
||||
{
|
||||
if (context.Interaction is not IModalInteraction modalInteraction)
|
||||
return ParseResult.FromError(InteractionCommandError.Unsuccessful, "Provided context doesn't belong to a Modal Interaction.");
|
||||
return TypeConverterResult.FromError(InteractionCommandError.Unsuccessful, "Provided context doesn't belong to a Modal Interaction.");
|
||||
|
||||
services ??= EmptyServiceProvider.Instance;
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace Discord.Interactions
|
||||
if (!throwOnMissingField)
|
||||
args[i] = input.DefaultValue;
|
||||
else
|
||||
return ParseResult.FromError(InteractionCommandError.BadArgs, $"Modal interaction is missing the required field: {input.CustomId}");
|
||||
return TypeConverterResult.FromError(InteractionCommandError.BadArgs, $"Modal interaction is missing the required field: {input.CustomId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -133,7 +133,7 @@ namespace Discord.Interactions
|
||||
}
|
||||
}
|
||||
|
||||
return ParseResult.FromSuccess(_initializer(args));
|
||||
return TypeConverterResult.FromSuccess(_initializer(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,7 +822,7 @@ namespace Discord.Interactions
|
||||
|
||||
SetMatchesIfApplicable(context, result);
|
||||
|
||||
return await result.Command.ExecuteAsync(context, services, result.RegexCaptureGroups).ConfigureAwait(false);
|
||||
return await result.Command.ExecuteAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<IResult> ExecuteAutocompleteAsync (IInteractionContext context, IAutocompleteInteraction interaction, IServiceProvider services )
|
||||
@@ -869,7 +869,7 @@ namespace Discord.Interactions
|
||||
|
||||
SetMatchesIfApplicable(context, result);
|
||||
|
||||
return await result.Command.ExecuteAsync(context, services, result.RegexCaptureGroups).ConfigureAwait(false);
|
||||
return await result.Command.ExecuteAsync(context, services).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static void SetMatchesIfApplicable<T>(IInteractionContext context, SearchResult<T> searchResult)
|
||||
|
||||
@@ -2,9 +2,9 @@ using System;
|
||||
|
||||
namespace Discord.Interactions
|
||||
{
|
||||
internal struct ParseResult : IResult
|
||||
public struct ParseResult : IResult
|
||||
{
|
||||
public object Value { get; }
|
||||
public object[] Args { get; }
|
||||
|
||||
public InteractionCommandError? Error { get; }
|
||||
|
||||
@@ -12,15 +12,15 @@ namespace Discord.Interactions
|
||||
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
private ParseResult(object value, InteractionCommandError? error, string reason)
|
||||
private ParseResult(object[] args, InteractionCommandError? error, string reason)
|
||||
{
|
||||
Value = value;
|
||||
Args = args;
|
||||
Error = error;
|
||||
ErrorReason = reason;
|
||||
}
|
||||
|
||||
public static ParseResult FromSuccess(object value) =>
|
||||
new ParseResult(value, null, null);
|
||||
public static ParseResult FromSuccess(object[] args) =>
|
||||
new ParseResult(args, null, null);
|
||||
|
||||
public static ParseResult FromError(Exception exception) =>
|
||||
new ParseResult(null, InteractionCommandError.Exception, exception.Message);
|
||||
|
||||
Reference in New Issue
Block a user