@@ -309,8 +309,8 @@ namespace Discord.Interactions.Builders | |||||
if (parameters.Count(x => typeof(IModal).IsAssignableFrom(x.ParameterType)) > 1) | if (parameters.Count(x => typeof(IModal).IsAssignableFrom(x.ParameterType)) > 1) | ||||
throw new InvalidOperationException($"A modal command can only have one {nameof(IModal)} parameter."); | throw new InvalidOperationException($"A modal command can only have one {nameof(IModal)} parameter."); | ||||
if (!parameters.All(x => x.ParameterType == typeof(string) || typeof(IModal).IsAssignableFrom(x.ParameterType))) | |||||
throw new InvalidOperationException($"All parameters of a modal command must be either a string or an implementation of {nameof(IModal)}"); | |||||
if (!typeof(IModal).IsAssignableFrom(parameters.Last().ParameterType)) | |||||
throw new InvalidOperationException($"Last parameter of a modal command must be an implementation of {nameof(IModal)}"); | |||||
var attributes = methodInfo.GetCustomAttributes(); | var attributes = methodInfo.GetCustomAttributes(); | ||||
@@ -20,6 +20,11 @@ namespace Discord.Interactions.Builders | |||||
/// </summary> | /// </summary> | ||||
public bool IsModalParameter => Modal is not null; | public bool IsModalParameter => Modal is not null; | ||||
/// <summary> | |||||
/// Gets the <see cref="TypeReader"/> assigned to this parameter, if <see cref="IsModalParameter"/> is <see langword="true"/>. | |||||
/// </summary> | |||||
public TypeReader TypeReader { get; private set; } | |||||
internal ModalCommandParameterBuilder(ICommandBuilder command) : base(command) { } | internal ModalCommandParameterBuilder(ICommandBuilder command) : base(command) { } | ||||
/// <summary> | /// <summary> | ||||
@@ -35,6 +40,8 @@ namespace Discord.Interactions.Builders | |||||
{ | { | ||||
if (typeof(IModal).IsAssignableFrom(type)) | if (typeof(IModal).IsAssignableFrom(type)) | ||||
Modal = ModalUtils.GetOrAdd(type, Command.Module.InteractionService); | Modal = ModalUtils.GetOrAdd(type, Command.Module.InteractionService); | ||||
else | |||||
TypeReader = Command.Module.InteractionService.GetTypeReader(type); | |||||
return base.SetParameterType(type); | return base.SetParameterType(type); | ||||
} | } | ||||
@@ -47,24 +47,40 @@ namespace Discord.Interactions | |||||
try | try | ||||
{ | { | ||||
var args = new List<object>(); | |||||
var args = new object[Parameters.Count]; | |||||
var captureCount = additionalArgs.Length; | |||||
if (additionalArgs is not null) | |||||
args.AddRange(additionalArgs); | |||||
for(var i = 0; i < Parameters.Count; i++) | |||||
{ | |||||
var parameter = Parameters.ElementAt(i); | |||||
var modalResult = await Modal.CreateModalAsync(context, services, Module.CommandService._exitOnMissingModalField).ConfigureAwait(false); | |||||
if(i < captureCount) | |||||
{ | |||||
var readResult = await parameter.TypeReader.ReadAsync(context, additionalArgs[i], services).ConfigureAwait(false); | |||||
if(!modalResult.IsSuccess) | |||||
{ | |||||
await InvokeModuleEvent(context, modalResult).ConfigureAwait(false); | |||||
return modalResult; | |||||
} | |||||
if(!readResult.IsSuccess) | |||||
{ | |||||
await InvokeModuleEvent(context, readResult).ConfigureAwait(false); | |||||
return readResult; | |||||
} | |||||
args[i] = readResult.Value; | |||||
} | |||||
else | |||||
{ | |||||
var modalResult = await Modal.CreateModalAsync(context, services, Module.CommandService._exitOnMissingModalField).ConfigureAwait(false); | |||||
if(modalResult is ParseResult parseResult) | |||||
args.Add(parseResult.Value); | |||||
else | |||||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason."); | |||||
if (!modalResult.IsSuccess) | |||||
{ | |||||
await InvokeModuleEvent(context, modalResult).ConfigureAwait(false); | |||||
return modalResult; | |||||
} | |||||
if (modalResult is ParseResult parseResult) | |||||
args[i] = parseResult.Value; | |||||
else | |||||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason."); | |||||
} | |||||
} | |||||
return await RunAsync(context, args.ToArray(), services); | return await RunAsync(context, args.ToArray(), services); | ||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
@@ -15,7 +15,12 @@ namespace Discord.Interactions | |||||
/// <summary> | /// <summary> | ||||
/// Gets whether this parameter is an <see cref="IModal"/> | /// Gets whether this parameter is an <see cref="IModal"/> | ||||
/// </summary> | /// </summary> | ||||
public bool IsModalParameter => Modal is not null; | |||||
public bool IsModalParameter { get; } | |||||
/// <summary> | |||||
/// Gets the <see cref="TypeReader"/> assigned to this parameter, if <see cref="IsModalParameter"/> is <see langword="true"/>. | |||||
/// </summary> | |||||
public TypeReader TypeReader { get; } | |||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
public new ModalCommandInfo Command => base.Command as ModalCommandInfo; | public new ModalCommandInfo Command => base.Command as ModalCommandInfo; | ||||
@@ -23,6 +28,8 @@ namespace Discord.Interactions | |||||
internal ModalCommandParameterInfo(ModalCommandParameterBuilder builder, ICommandInfo command) : base(builder, command) | internal ModalCommandParameterInfo(ModalCommandParameterBuilder builder, ICommandInfo command) : base(builder, command) | ||||
{ | { | ||||
Modal = builder.Modal; | Modal = builder.Modal; | ||||
IsModalParameter = builder.IsModalParameter; | |||||
TypeReader = builder.TypeReader; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -13,7 +13,7 @@ namespace Discord.Interactions | |||||
if (!ulong.TryParse(option, out var snowflake)) | if (!ulong.TryParse(option, out var snowflake)) | ||||
return TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} isn't a valid snowflake thus cannot be converted into {typeof(T).Name}"); | return TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} isn't a valid snowflake thus cannot be converted into {typeof(T).Name}"); | ||||
var result = GetEntity(snowflake, context); | |||||
var result = await GetEntity(snowflake, context).ConfigureAwait(false); | |||||
if (result is not null) | if (result is not null) | ||||
return TypeConverterResult.FromSuccess(result); | return TypeConverterResult.FromSuccess(result); | ||||
@@ -37,7 +37,7 @@ namespace Discord.Interactions | |||||
} | } | ||||
internal sealed class DefaultRoleReader<T> : DefaultSnowflakeReader<T> | internal sealed class DefaultRoleReader<T> : DefaultSnowflakeReader<T> | ||||
where T : class, IUser | |||||
where T : class, IRole | |||||
{ | { | ||||
protected override Task<T> GetEntity(ulong id, IInteractionContext ctx) => Task.FromResult(ctx.Guild?.GetRole(id) as T); | protected override Task<T> GetEntity(ulong id, IInteractionContext ctx) => Task.FromResult(ctx.Guild?.GetRole(id) as T); | ||||
} | } | ||||