| @@ -43,7 +43,7 @@ namespace Discord.Interactions.Builders | |||||
| /// <returns> | /// <returns> | ||||
| /// The builder instance. | /// The builder instance. | ||||
| /// </returns> | /// </returns> | ||||
| public ComponentCommandParameterBuilder SetParameterType(Type type, IServiceProvider services = null) | |||||
| public ComponentCommandParameterBuilder SetParameterType(Type type, IServiceProvider services) | |||||
| { | { | ||||
| base.SetParameterType(type); | base.SetParameterType(type); | ||||
| @@ -41,14 +41,7 @@ namespace Discord.Interactions | |||||
| if (context.Interaction is not IAutocompleteInteraction) | if (context.Interaction is not IAutocompleteInteraction) | ||||
| return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Autocomplete Interaction"); | return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Autocomplete Interaction"); | ||||
| try | |||||
| { | |||||
| return await RunAsync(context, Array.Empty<object>(), services).ConfigureAwait(false); | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| return ExecuteResult.FromError(ex); | |||||
| } | |||||
| return await RunAsync(context, Array.Empty<object>(), services).ConfigureAwait(false); | |||||
| } | } | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| @@ -31,6 +31,8 @@ namespace Discord.Interactions | |||||
| private readonly ExecuteCallback _action; | private readonly ExecuteCallback _action; | ||||
| private readonly ILookup<string, PreconditionAttribute> _groupedPreconditions; | private readonly ILookup<string, PreconditionAttribute> _groupedPreconditions; | ||||
| internal IReadOnlyDictionary<string, TParameter> _parameterDictionary; | |||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| public ModuleInfo Module { get; } | public ModuleInfo Module { get; } | ||||
| @@ -120,10 +122,7 @@ namespace Discord.Interactions | |||||
| return moduleResult; | return moduleResult; | ||||
| var commandResult = await CheckGroups(_groupedPreconditions, "Command").ConfigureAwait(false); | var commandResult = await CheckGroups(_groupedPreconditions, "Command").ConfigureAwait(false); | ||||
| if (!commandResult.IsSuccess) | |||||
| return commandResult; | |||||
| return PreconditionResult.FromSuccess(); | |||||
| return !commandResult.IsSuccess ? commandResult : PreconditionResult.FromSuccess(); | |||||
| } | } | ||||
| protected async Task<IResult> RunAsync(IInteractionContext context, object[] args, IServiceProvider services) | protected async Task<IResult> RunAsync(IInteractionContext context, object[] args, IServiceProvider services) | ||||
| @@ -137,8 +136,8 @@ namespace Discord.Interactions | |||||
| using var scope = services?.CreateScope(); | using var scope = services?.CreateScope(); | ||||
| return await ExecuteInternalAsync(context, args, scope?.ServiceProvider ?? EmptyServiceProvider.Instance).ConfigureAwait(false); | return await ExecuteInternalAsync(context, args, scope?.ServiceProvider ?? EmptyServiceProvider.Instance).ConfigureAwait(false); | ||||
| } | } | ||||
| else | |||||
| return await ExecuteInternalAsync(context, args, services).ConfigureAwait(false); | |||||
| return await ExecuteInternalAsync(context, args, services).ConfigureAwait(false); | |||||
| } | } | ||||
| case RunMode.Async: | case RunMode.Async: | ||||
| _ = Task.Run(async () => | _ = Task.Run(async () => | ||||
| @@ -167,20 +166,14 @@ namespace Discord.Interactions | |||||
| { | { | ||||
| var preconditionResult = await CheckPreconditionsAsync(context, services).ConfigureAwait(false); | var preconditionResult = await CheckPreconditionsAsync(context, services).ConfigureAwait(false); | ||||
| if (!preconditionResult.IsSuccess) | if (!preconditionResult.IsSuccess) | ||||
| { | |||||
| await InvokeModuleEvent(context, preconditionResult).ConfigureAwait(false); | |||||
| return preconditionResult; | |||||
| } | |||||
| return await InvokeEventAndReturn(context, preconditionResult).ConfigureAwait(false); | |||||
| var index = 0; | var index = 0; | ||||
| foreach (var parameter in Parameters) | foreach (var parameter in Parameters) | ||||
| { | { | ||||
| var result = await parameter.CheckPreconditionsAsync(context, args[index++], services).ConfigureAwait(false); | var result = await parameter.CheckPreconditionsAsync(context, args[index++], services).ConfigureAwait(false); | ||||
| if (!result.IsSuccess) | if (!result.IsSuccess) | ||||
| { | |||||
| await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
| return result; | |||||
| } | |||||
| return await InvokeEventAndReturn(context, result).ConfigureAwait(false); | |||||
| } | } | ||||
| var task = _action(context, args, services, this); | var task = _action(context, args, services, this); | ||||
| @@ -189,20 +182,16 @@ namespace Discord.Interactions | |||||
| { | { | ||||
| var result = await resultTask.ConfigureAwait(false); | var result = await resultTask.ConfigureAwait(false); | ||||
| await InvokeModuleEvent(context, result).ConfigureAwait(false); | await InvokeModuleEvent(context, result).ConfigureAwait(false); | ||||
| if (result is RuntimeResult || result is ExecuteResult) | |||||
| if (result is RuntimeResult or ExecuteResult) | |||||
| return result; | return result; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| await task.ConfigureAwait(false); | await task.ConfigureAwait(false); | ||||
| var result = ExecuteResult.FromSuccess(); | |||||
| await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
| return result; | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromSuccess()).ConfigureAwait(false); | |||||
| } | } | ||||
| var failResult = ExecuteResult.FromError(InteractionCommandError.Unsuccessful, "Command execution failed for an unknown reason"); | |||||
| await InvokeModuleEvent(context, failResult).ConfigureAwait(false); | |||||
| return failResult; | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.Unsuccessful, "Command execution failed for an unknown reason")).ConfigureAwait(false); | |||||
| } | } | ||||
| catch (Exception ex) | catch (Exception ex) | ||||
| { | { | ||||
| @@ -231,6 +220,12 @@ namespace Discord.Interactions | |||||
| } | } | ||||
| } | } | ||||
| protected async ValueTask<IResult> InvokeEventAndReturn(IInteractionContext context, IResult result) | |||||
| { | |||||
| await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
| return result; | |||||
| } | |||||
| private static bool CheckTopLevel(ModuleInfo parent) | private static bool CheckTopLevel(ModuleInfo parent) | ||||
| { | { | ||||
| var currentParent = parent; | var currentParent = parent; | ||||
| @@ -48,6 +48,11 @@ namespace Discord.Interactions | |||||
| public async Task<IResult> ExecuteAsync(IInteractionContext context, IEnumerable<CommandParameterInfo> paramList, IEnumerable<string> wildcardCaptures, IComponentInteractionData data, | public async Task<IResult> ExecuteAsync(IInteractionContext context, IEnumerable<CommandParameterInfo> paramList, IEnumerable<string> wildcardCaptures, IComponentInteractionData data, | ||||
| IServiceProvider services) | IServiceProvider services) | ||||
| { | { | ||||
| var paramCount = paramList.Count(); | |||||
| var captureCount = wildcardCaptures?.Count() ?? 0; | |||||
| if (paramCount < captureCount + 1) | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command was invoked with too many parameters")).ConfigureAwait(false); | |||||
| if (context.Interaction is not IComponentInteraction messageComponent) | if (context.Interaction is not IComponentInteraction messageComponent) | ||||
| return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Component Command Interaction"); | return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Component Command Interaction"); | ||||
| @@ -58,27 +63,16 @@ namespace Discord.Interactions | |||||
| for (var i = 0; i < paramCount; i++) | for (var i = 0; i < paramCount; i++) | ||||
| { | { | ||||
| var parameter = Parameters.ElementAt(i); | var parameter = Parameters.ElementAt(i); | ||||
| bool isCapture = i < captureCount; | |||||
| var isCapture = i < captureCount; | |||||
| if (isCapture ^ parameter.IsRouteSegmentParameter) | if (isCapture ^ parameter.IsRouteSegmentParameter) | ||||
| { | |||||
| var result = ExecuteResult.FromError(InteractionCommandError.BadArgs, $"Argument type and parameter type didn't match (Wild Card capture/Component value)"); | |||||
| await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
| return result; | |||||
| } | |||||
| TypeConverterResult readResult; | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.BadArgs, "Argument type and parameter type didn't match (Wild Card capture/Component value)")).ConfigureAwait(false); | |||||
| if (isCapture) | |||||
| readResult = await parameter.TypeReader.ReadAsync(context, wildcardCaptures.ElementAt(i), services).ConfigureAwait(false); | |||||
| else | |||||
| readResult = await parameter.TypeConverter.ReadAsync(context, data, services).ConfigureAwait(false); | |||||
| var readResult = isCapture ? await parameter.TypeReader.ReadAsync(context, wildcardCaptures.ElementAt(i), services).ConfigureAwait(false) : | |||||
| await parameter.TypeConverter.ReadAsync(context, data, services).ConfigureAwait(false); | |||||
| if (!readResult.IsSuccess) | if (!readResult.IsSuccess) | ||||
| { | |||||
| await InvokeModuleEvent(context, readResult).ConfigureAwait(false); | |||||
| return readResult; | |||||
| } | |||||
| return await InvokeEventAndReturn(context, readResult).ConfigureAwait(false); | |||||
| args[i] = readResult.Value; | args[i] = readResult.Value; | ||||
| } | } | ||||
| @@ -87,9 +81,7 @@ namespace Discord.Interactions | |||||
| } | } | ||||
| catch (Exception ex) | catch (Exception ex) | ||||
| { | { | ||||
| var result = ExecuteResult.FromError(ex); | |||||
| await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
| return result; | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromError(ex)).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics.Tracing; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| namespace Discord.Interactions | namespace Discord.Interactions | ||||
| @@ -57,37 +58,28 @@ namespace Discord.Interactions | |||||
| 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, additionalArgs[i], services).ConfigureAwait(false); | ||||
| if (!readResult.IsSuccess) | |||||
| return await InvokeEventAndReturn(context, readResult).ConfigureAwait(false); | |||||
| if(!readResult.IsSuccess) | |||||
| { | |||||
| await InvokeModuleEvent(context, readResult).ConfigureAwait(false); | |||||
| return readResult; | |||||
| } | |||||
| args[i] = readResult.Value; | args[i] = readResult.Value; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| var modalResult = await Modal.CreateModalAsync(context, services, Module.CommandService._exitOnMissingModalField).ConfigureAwait(false); | var modalResult = await Modal.CreateModalAsync(context, services, Module.CommandService._exitOnMissingModalField).ConfigureAwait(false); | ||||
| if (!modalResult.IsSuccess) | if (!modalResult.IsSuccess) | ||||
| { | |||||
| await InvokeModuleEvent(context, modalResult).ConfigureAwait(false); | |||||
| return modalResult; | |||||
| } | |||||
| return await InvokeEventAndReturn(context, modalResult).ConfigureAwait(false); | |||||
| if (modalResult is not ParseResult parseResult) | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason.")); | |||||
| if (modalResult is ParseResult parseResult) | |||||
| args[i] = parseResult.Value; | |||||
| else | |||||
| return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason."); | |||||
| args[i] = parseResult.Value; | |||||
| } | } | ||||
| } | } | ||||
| return await RunAsync(context, args.ToArray(), services); | |||||
| return await RunAsync(context, args, services); | |||||
| } | } | ||||
| catch (Exception ex) | catch (Exception ex) | ||||
| { | { | ||||
| var result = ExecuteResult.FromError(ex); | |||||
| await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||||
| return result; | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromError(ex)).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -56,48 +56,67 @@ namespace Discord.Interactions | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| if (paramList?.Count() < argList?.Count()) | |||||
| return ExecuteResult.FromError(InteractionCommandError.BadArgs ,"Command was invoked with too many parameters"); | |||||
| var slashCommandParameterInfos = paramList.ToList(); | |||||
| var args = new object[slashCommandParameterInfos.Count]; | |||||
| var args = new object[paramList.Count()]; | |||||
| for (var i = 0; i < paramList.Count(); i++) | |||||
| for (var i = 0; i < slashCommandParameterInfos.Count; i++) | |||||
| { | { | ||||
| var parameter = paramList.ElementAt(i); | |||||
| var arg = argList?.Find(x => string.Equals(x.Name, parameter.Name, StringComparison.OrdinalIgnoreCase)); | |||||
| if (arg == default) | |||||
| { | |||||
| if (parameter.IsRequired) | |||||
| return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command was invoked with too few parameters"); | |||||
| else | |||||
| args[i] = parameter.DefaultValue; | |||||
| } | |||||
| else | |||||
| { | |||||
| var typeConverter = parameter.TypeConverter; | |||||
| var readResult = await typeConverter.ReadAsync(context, arg, services).ConfigureAwait(false); | |||||
| if (!readResult.IsSuccess) | |||||
| { | |||||
| await InvokeModuleEvent(context, readResult).ConfigureAwait(false); | |||||
| return readResult; | |||||
| } | |||||
| args[i] = readResult.Value; | |||||
| } | |||||
| } | |||||
| var parameter = slashCommandParameterInfos[i]; | |||||
| var result = await ParseArgument(parameter, context, argList, services).ConfigureAwait(false); | |||||
| 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 await RunAsync(context, args, services).ConfigureAwait(false); | ||||
| } | } | ||||
| catch (Exception ex) | catch (Exception ex) | ||||
| { | { | ||||
| return ExecuteResult.FromError(ex); | |||||
| return await InvokeEventAndReturn(context, ExecuteResult.FromError(ex)).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| private async Task<IResult> ParseArgument(SlashCommandParameterInfo parameterInfo, IInteractionContext context, List<IApplicationCommandInteractionDataOption> argList, | |||||
| IServiceProvider services) | |||||
| { | |||||
| if (parameterInfo.IsComplexParameter) | |||||
| { | |||||
| var ctorArgs = new object[parameterInfo.ComplexParameterFields.Count]; | |||||
| for (var i = 0; i < ctorArgs.Length; i++) | |||||
| { | |||||
| var result = await ParseArgument(parameterInfo.ComplexParameterFields.ElementAt(i), context, argList, services).ConfigureAwait(false); | |||||
| if (!result.IsSuccess) | |||||
| return result; | |||||
| if (result is not ParseResult parseResult) | |||||
| return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Complex command parsing failed for an unknown reason."); | |||||
| ctorArgs[i] = parseResult.Value; | |||||
| } | |||||
| return ParseResult.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); | |||||
| var typeConverter = parameterInfo.TypeConverter; | |||||
| var readResult = await typeConverter.ReadAsync(context, arg, services).ConfigureAwait(false); | |||||
| if (!readResult.IsSuccess) | |||||
| return readResult; | |||||
| return ParseResult.FromSuccess(readResult.Value); | |||||
| } | |||||
| protected override Task InvokeModuleEvent (IInteractionContext context, IResult result) | protected override Task InvokeModuleEvent (IInteractionContext context, IResult result) | ||||
| => CommandService._slashCommandExecutedEvent.InvokeAsync(this, context, result); | => CommandService._slashCommandExecutedEvent.InvokeAsync(this, context, result); | ||||
| @@ -193,7 +193,7 @@ namespace Discord.Interactions | |||||
| [typeof(IMentionable)] = typeof(DefaultMentionableConverter<>), | [typeof(IMentionable)] = typeof(DefaultMentionableConverter<>), | ||||
| [typeof(IConvertible)] = typeof(DefaultValueConverter<>), | [typeof(IConvertible)] = typeof(DefaultValueConverter<>), | ||||
| [typeof(Enum)] = typeof(EnumConverter<>), | [typeof(Enum)] = typeof(EnumConverter<>), | ||||
| [typeof(Nullable<>)] = typeof(NullableConverter<>), | |||||
| [typeof(Nullable<>)] = typeof(NullableConverter<>) | |||||
| }); | }); | ||||
| _compTypeConverterMap = new TypeMap<ComponentTypeConverter, IComponentInteractionData>(this, new ConcurrentDictionary<Type, ComponentTypeConverter>(), | _compTypeConverterMap = new TypeMap<ComponentTypeConverter, IComponentInteractionData>(this, new ConcurrentDictionary<Type, ComponentTypeConverter>(), | ||||
| @@ -209,7 +209,7 @@ namespace Discord.Interactions | |||||
| [typeof(IChannel)] = typeof(DefaultChannelReader<>), | [typeof(IChannel)] = typeof(DefaultChannelReader<>), | ||||
| [typeof(IRole)] = typeof(DefaultRoleReader<>), | [typeof(IRole)] = typeof(DefaultRoleReader<>), | ||||
| [typeof(IUser)] = typeof(DefaultUserReader<>), | [typeof(IUser)] = typeof(DefaultUserReader<>), | ||||
| [typeof(IMessage)] = typeof(DefaultUserReader<>), | |||||
| [typeof(IMessage)] = typeof(DefaultMessageReader<>), | |||||
| [typeof(IConvertible)] = typeof(DefaultValueReader<>), | [typeof(IConvertible)] = typeof(DefaultValueReader<>), | ||||
| [typeof(Enum)] = typeof(EnumReader<>) | [typeof(Enum)] = typeof(EnumReader<>) | ||||
| }); | }); | ||||
| @@ -311,7 +311,7 @@ namespace Discord.Interactions | |||||
| public async Task<ModuleInfo> AddModuleAsync (Type type, IServiceProvider services) | public async Task<ModuleInfo> AddModuleAsync (Type type, IServiceProvider services) | ||||
| { | { | ||||
| if (!typeof(IInteractionModuleBase).IsAssignableFrom(type)) | if (!typeof(IInteractionModuleBase).IsAssignableFrom(type)) | ||||
| throw new ArgumentException("Type parameter must be a type of Slash Module", "T"); | |||||
| throw new ArgumentException("Type parameter must be a type of Slash Module", nameof(type)); | |||||
| services ??= EmptyServiceProvider.Instance; | services ??= EmptyServiceProvider.Instance; | ||||
| @@ -344,7 +344,7 @@ namespace Discord.Interactions | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Register Application Commands from <see cref="ContextCommands"/> and <see cref="SlashCommands"/> to a guild. | |||||
| /// Register Application Commands from <see cref="ContextCommands"/> and <see cref="SlashCommands"/> to a guild. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="guildId">Id of the target guild.</param> | /// <param name="guildId">Id of the target guild.</param> | ||||
| /// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param> | /// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param> | ||||
| @@ -440,7 +440,7 @@ namespace Discord.Interactions | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Register Application Commands from modules provided in <paramref name="modules"/> to a guild. | |||||
| /// Register Application Commands from modules provided in <paramref name="modules"/> to a guild. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="guild">The target guild.</param> | /// <param name="guild">The target guild.</param> | ||||
| /// <param name="modules">Modules to be registered to Discord.</param> | /// <param name="modules">Modules to be registered to Discord.</param> | ||||
| @@ -467,7 +467,7 @@ namespace Discord.Interactions | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Register Application Commands from modules provided in <paramref name="modules"/> as global commands. | |||||
| /// Register Application Commands from modules provided in <paramref name="modules"/> as global commands. | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="modules">Modules to be registered to Discord.</param> | /// <param name="modules">Modules to be registered to Discord.</param> | ||||
| /// <returns> | /// <returns> | ||||
| @@ -695,7 +695,7 @@ namespace Discord.Interactions | |||||
| public async Task<IResult> ExecuteCommandAsync (IInteractionContext context, IServiceProvider services) | public async Task<IResult> ExecuteCommandAsync (IInteractionContext context, IServiceProvider services) | ||||
| { | { | ||||
| var interaction = context.Interaction; | var interaction = context.Interaction; | ||||
| return interaction switch | return interaction switch | ||||
| { | { | ||||
| ISlashCommandInteraction slashCommand => await ExecuteSlashCommandAsync(context, slashCommand, services).ConfigureAwait(false), | ISlashCommandInteraction slashCommand => await ExecuteSlashCommandAsync(context, slashCommand, services).ConfigureAwait(false), | ||||
| @@ -865,7 +865,7 @@ namespace Discord.Interactions | |||||
| /// Add a concrete type <see cref="TypeReader"/>. | /// Add a concrete type <see cref="TypeReader"/>. | ||||
| /// </summary> | /// </summary> | ||||
| /// <typeparam name="T">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</typeparam> | /// <typeparam name="T">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</typeparam> | ||||
| /// <param name="converter">The <see cref="TypeReader"/> instance.</param> | |||||
| /// <param name="reader">The <see cref="TypeReader"/> instance.</param> | |||||
| public void AddTypeReader<T>(TypeReader reader) => | public void AddTypeReader<T>(TypeReader reader) => | ||||
| AddTypeReader(typeof(T), reader); | AddTypeReader(typeof(T), reader); | ||||
| @@ -873,7 +873,7 @@ namespace Discord.Interactions | |||||
| /// Add a concrete type <see cref="TypeReader"/>. | /// Add a concrete type <see cref="TypeReader"/>. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="type">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</param> | /// <param name="type">Primary target <see cref="Type"/> of the <see cref="TypeReader"/>.</param> | ||||
| /// <param name="converter">The <see cref="TypeReader"/> instance.</param> | |||||
| /// <param name="reader">The <see cref="TypeReader"/> instance.</param> | |||||
| public void AddTypeReader(Type type, TypeReader reader) => | public void AddTypeReader(Type type, TypeReader reader) => | ||||
| _typeReaderMap.AddConcrete(type, reader); | _typeReaderMap.AddConcrete(type, reader); | ||||
| @@ -1066,7 +1066,7 @@ namespace Discord.Interactions | |||||
| public ModuleInfo GetModuleInfo<TModule> ( ) where TModule : class | public ModuleInfo GetModuleInfo<TModule> ( ) where TModule : class | ||||
| { | { | ||||
| if (!typeof(IInteractionModuleBase).IsAssignableFrom(typeof(TModule))) | if (!typeof(IInteractionModuleBase).IsAssignableFrom(typeof(TModule))) | ||||
| throw new ArgumentException("Type parameter must be a type of Slash Module", "TModule"); | |||||
| throw new ArgumentException("Type parameter must be a type of Slash Module", nameof(TModule)); | |||||
| var module = _typedModuleDefs[typeof(TModule)]; | var module = _typedModuleDefs[typeof(TModule)]; | ||||
| @@ -25,8 +25,8 @@ namespace Discord.Interactions | |||||
| if (_concretes.TryGetValue(type, out var specific)) | if (_concretes.TryGetValue(type, out var specific)) | ||||
| return specific; | return specific; | ||||
| else if (_generics.Any(x => x.Key.IsAssignableFrom(type) | |||||
| || (x.Key.IsGenericTypeDefinition && type.IsGenericType && x.Key.GetGenericTypeDefinition() == type.GetGenericTypeDefinition()))) | |||||
| if (_generics.Any(x => x.Key.IsAssignableFrom(type) | |||||
| || x.Key.IsGenericTypeDefinition && type.IsGenericType && x.Key.GetGenericTypeDefinition() == type.GetGenericTypeDefinition())) | |||||
| { | { | ||||
| services ??= EmptyServiceProvider.Instance; | services ??= EmptyServiceProvider.Instance; | ||||
| @@ -36,10 +36,10 @@ namespace Discord.Interactions | |||||
| return converter; | return converter; | ||||
| } | } | ||||
| else if (_concretes.Any(x => x.Value.CanConvertTo(type))) | |||||
| if (_concretes.Any(x => x.Value.CanConvertTo(type))) | |||||
| return _concretes.First(x => x.Value.CanConvertTo(type)).Value; | return _concretes.First(x => x.Value.CanConvertTo(type)).Value; | ||||
| throw new ArgumentException($"No type {typeof(TConverter).Name} is defined for this {type.FullName}", "type"); | |||||
| throw new ArgumentException($"No type {typeof(TConverter).Name} is defined for this {type.FullName}", nameof(type)); | |||||
| } | } | ||||
| public void AddConcrete<TTarget>(TConverter converter) => | public void AddConcrete<TTarget>(TConverter converter) => | ||||
| @@ -63,7 +63,7 @@ namespace Discord.Interactions | |||||
| var genericArguments = converterType.GetGenericArguments(); | var genericArguments = converterType.GetGenericArguments(); | ||||
| if (genericArguments.Count() > 1) | |||||
| if (genericArguments.Length > 1) | |||||
| throw new InvalidOperationException($"Valid generic {converterType.FullName}s cannot have more than 1 generic type parameter"); | throw new InvalidOperationException($"Valid generic {converterType.FullName}s cannot have more than 1 generic type parameter"); | ||||
| var constraints = genericArguments.SelectMany(x => x.GetGenericParameterConstraints()); | var constraints = genericArguments.SelectMany(x => x.GetGenericParameterConstraints()); | ||||
| @@ -15,10 +15,8 @@ namespace Discord.Interactions | |||||
| var result = await GetEntity(snowflake, context).ConfigureAwait(false); | var result = await GetEntity(snowflake, context).ConfigureAwait(false); | ||||
| if (result is not null) | |||||
| return TypeConverterResult.FromSuccess(result); | |||||
| else | |||||
| return TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} must be a valid {typeof(T).Name} snowflake to be parsed."); | |||||
| return result is not null ? | |||||
| TypeConverterResult.FromSuccess(result) : TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option} must be a valid {typeof(T).Name} snowflake to be parsed."); | |||||
| } | } | ||||
| public override Task<string> SerializeAsync(object obj) => Task.FromResult((obj as ISnowflakeEntity)?.Id.ToString()); | public override Task<string> SerializeAsync(object obj) => Task.FromResult((obj as ISnowflakeEntity)?.Id.ToString()); | ||||
| @@ -8,10 +8,8 @@ namespace Discord.Interactions | |||||
| { | { | ||||
| public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services) | public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services) | ||||
| { | { | ||||
| if (Enum.TryParse<T>(option, out var result)) | |||||
| return Task.FromResult(TypeConverterResult.FromSuccess(result)); | |||||
| else | |||||
| return Task.FromResult(TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"Value {option} cannot be converted to {nameof(T)}")); | |||||
| return Task.FromResult(Enum.TryParse<T>(option, out var result) ? | |||||
| TypeConverterResult.FromSuccess(result) : TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"Value {option} cannot be converted to {nameof(T)}")); | |||||
| } | } | ||||
| public override Task<string> SerializeAsync(object obj) | public override Task<string> SerializeAsync(object obj) | ||||
| @@ -20,8 +20,8 @@ namespace Discord.Interactions | |||||
| /// <summary> | /// <summary> | ||||
| /// Will be used to read the incoming payload before executing the method body. | /// Will be used to read the incoming payload before executing the method body. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="context">Command exexution context.</param> | |||||
| /// <param name="option">Recieved option payload.</param> | |||||
| /// <param name="context">Command execution context.</param> | |||||
| /// <param name="option">Received option payload.</param> | |||||
| /// <param name="services">Service provider that will be used to initialize the command module.</param> | /// <param name="services">Service provider that will be used to initialize the command module.</param> | ||||
| /// <returns>The result of the read process.</returns> | /// <returns>The result of the read process.</returns> | ||||
| public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services); | public abstract Task<TypeConverterResult> ReadAsync(IInteractionContext context, string option, IServiceProvider services); | ||||
| @@ -31,7 +31,7 @@ namespace Discord.Interactions | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="obj">Object to be serialized.</param> | /// <param name="obj">Object to be serialized.</param> | ||||
| /// <returns> | /// <returns> | ||||
| /// A task represting the conversion process. The result of the task contains the conversion result. | |||||
| /// A task representing the conversion process. The result of the task contains the conversion result. | |||||
| /// </returns> | /// </returns> | ||||
| public virtual Task<string> SerializeAsync(object obj) => Task.FromResult(obj.ToString()); | public virtual Task<string> SerializeAsync(object obj) => Task.FromResult(obj.ToString()); | ||||
| } | } | ||||
| @@ -7,7 +7,7 @@ namespace Discord.Interactions | |||||
| { | { | ||||
| internal static class ModalUtils | internal static class ModalUtils | ||||
| { | { | ||||
| private static ConcurrentDictionary<Type, ModalInfo> _modalInfos = new(); | |||||
| private static readonly ConcurrentDictionary<Type, ModalInfo> _modalInfos = new(); | |||||
| public static IReadOnlyCollection<ModalInfo> Modals => _modalInfos.Values.ToReadOnlyCollection(); | public static IReadOnlyCollection<ModalInfo> Modals => _modalInfos.Values.ToReadOnlyCollection(); | ||||