| @@ -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); | ||||
| } | } | ||||