Browse Source

add component typeconverters to modals

pull/2169/head
Cenk Ergen 3 years ago
parent
commit
ca6f77276e
9 changed files with 90 additions and 10 deletions
  1. +2
    -0
      src/Discord.Net.Interactions/Builders/Modals/Inputs/IInputComponentBuilder.cs
  2. +3
    -0
      src/Discord.Net.Interactions/Builders/Modals/Inputs/InputComponentBuilder.cs
  3. +4
    -2
      src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs
  4. +2
    -2
      src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs
  5. +9
    -2
      src/Discord.Net.Interactions/Info/Commands/ModalCommandInfo.cs
  6. +3
    -0
      src/Discord.Net.Interactions/Info/InputComponents/InputComponentInfo.cs
  7. +37
    -0
      src/Discord.Net.Interactions/Info/ModalInfo.cs
  8. +26
    -0
      src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs
  9. +4
    -4
      src/Discord.Net.Interactions/Utilities/ModalUtils.cs

+ 2
- 0
src/Discord.Net.Interactions/Builders/Modals/Inputs/IInputComponentBuilder.cs View File

@@ -38,6 +38,8 @@ namespace Discord.Interactions.Builders
/// </summary> /// </summary>
Type Type { get; } Type Type { get; }


ComponentTypeConverter TypeConverter { get; }

/// <summary> /// <summary>
/// Gets the default value of this input component. /// Gets the default value of this input component.
/// </summary> /// </summary>


+ 3
- 0
src/Discord.Net.Interactions/Builders/Modals/Inputs/InputComponentBuilder.cs View File

@@ -33,6 +33,8 @@ namespace Discord.Interactions.Builders
/// <inheritdoc/> /// <inheritdoc/>
public Type Type { get; private set; } public Type Type { get; private set; }


public ComponentTypeConverter TypeConverter { get; private set; }

/// <inheritdoc/> /// <inheritdoc/>
public object DefaultValue { get; set; } public object DefaultValue { get; set; }


@@ -111,6 +113,7 @@ namespace Discord.Interactions.Builders
public TBuilder WithType(Type type) public TBuilder WithType(Type type)
{ {
Type = type; Type = type;
TypeConverter = Modal._interactionService.GetComponentTypeConverter(type);
return Instance; return Instance;
} }




+ 4
- 2
src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs View File

@@ -9,6 +9,7 @@ namespace Discord.Interactions.Builders
/// </summary> /// </summary>
public class ModalBuilder public class ModalBuilder
{ {
internal readonly InteractionService _interactionService;
internal readonly List<IInputComponentBuilder> _components; internal readonly List<IInputComponentBuilder> _components;


/// <summary> /// <summary>
@@ -31,11 +32,12 @@ namespace Discord.Interactions.Builders
/// </summary> /// </summary>
public IReadOnlyCollection<IInputComponentBuilder> Components => _components; public IReadOnlyCollection<IInputComponentBuilder> Components => _components;


internal ModalBuilder(Type type)
internal ModalBuilder(Type type, InteractionService interactionService)
{ {
if (!typeof(IModal).IsAssignableFrom(type)) if (!typeof(IModal).IsAssignableFrom(type))
throw new ArgumentException($"Must be an implementation of {nameof(IModal)}", nameof(type)); throw new ArgumentException($"Must be an implementation of {nameof(IModal)}", nameof(type));


_interactionService = interactionService;
_components = new(); _components = new();
} }


@@ -43,7 +45,7 @@ namespace Discord.Interactions.Builders
/// Initializes a new <see cref="ModalBuilder"/> /// Initializes a new <see cref="ModalBuilder"/>
/// </summary> /// </summary>
/// <param name="modalInitializer">The initialization delegate for this modal.</param> /// <param name="modalInitializer">The initialization delegate for this modal.</param>
public ModalBuilder(Type type, ModalInitializer modalInitializer) : this(type)
public ModalBuilder(Type type, ModalInitializer modalInitializer, InteractionService interactionService) : this(type, interactionService)
{ {
ModalInitializer = modalInitializer; ModalInitializer = modalInitializer;
} }


+ 2
- 2
src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs View File

@@ -482,7 +482,7 @@ namespace Discord.Interactions.Builders
#endregion #endregion


#region Modals #region Modals
public static ModalInfo BuildModalInfo(Type modalType)
public static ModalInfo BuildModalInfo(Type modalType, InteractionService interactionService)
{ {
if (!typeof(IModal).IsAssignableFrom(modalType)) if (!typeof(IModal).IsAssignableFrom(modalType))
throw new InvalidOperationException($"{modalType.FullName} isn't an implementation of {typeof(IModal).FullName}"); throw new InvalidOperationException($"{modalType.FullName} isn't an implementation of {typeof(IModal).FullName}");
@@ -491,7 +491,7 @@ namespace Discord.Interactions.Builders


try try
{ {
var builder = new ModalBuilder(modalType)
var builder = new ModalBuilder(modalType, interactionService)
{ {
Title = instance.Title Title = instance.Title
}; };


+ 9
- 2
src/Discord.Net.Interactions/Info/Commands/ModalCommandInfo.cs View File

@@ -52,8 +52,15 @@ namespace Discord.Interactions
if (additionalArgs is not null) if (additionalArgs is not null)
args.AddRange(additionalArgs); args.AddRange(additionalArgs);


var modal = Modal.CreateModal(modalInteraction, Module.CommandService._exitOnMissingModalField);
args.Add(modal);
var modalResult = await Modal.ParseModalAsync(context, services, Module.CommandService._exitOnMissingModalField).ConfigureAwait(false);

if(!modalResult.IsSuccess || modalResult is not ParseResult parseResult)
{
await InvokeModuleEvent(context, modalResult).ConfigureAwait(false);
return modalResult;
}

args.Add(parseResult.Value);


return await RunAsync(context, args.ToArray(), services); return await RunAsync(context, args.ToArray(), services);
} }


+ 3
- 0
src/Discord.Net.Interactions/Info/InputComponents/InputComponentInfo.cs View File

@@ -39,6 +39,8 @@ namespace Discord.Interactions
/// </summary> /// </summary>
public Type Type { get; } public Type Type { get; }


public ComponentTypeConverter TypeConverter { get; }

/// <summary> /// <summary>
/// Gets the default value of this component. /// Gets the default value of this component.
/// </summary> /// </summary>
@@ -57,6 +59,7 @@ namespace Discord.Interactions
IsRequired = builder.IsRequired; IsRequired = builder.IsRequired;
ComponentType = builder.ComponentType; ComponentType = builder.ComponentType;
Type = builder.Type; Type = builder.Type;
TypeConverter = builder.TypeConverter;
DefaultValue = builder.DefaultValue; DefaultValue = builder.DefaultValue;
Attributes = builder.Attributes.ToImmutableArray(); Attributes = builder.Attributes.ToImmutableArray();
} }


+ 37
- 0
src/Discord.Net.Interactions/Info/ModalInfo.cs View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks;


namespace Discord.Interactions namespace Discord.Interactions
{ {
@@ -86,5 +87,41 @@ namespace Discord.Interactions


return _initializer(args); return _initializer(args);
} }

internal async Task<IResult> ParseModalAsync(IInteractionContext context, IServiceProvider services = null, bool throwOnMissingField = false)
{
if (context.Interaction is not IModalInteraction modalInteraction)
throw new InvalidOperationException("Provided context doesn't belong to a Modal Interaction.");

services ??= EmptyServiceProvider.Instance;

var args = new object[Components.Count];
var components = modalInteraction.Data.Components.ToList();

for (var i = 0; i < Components.Count; i++)
{
var input = Components.ElementAt(i);
var component = components.Find(x => x.CustomId == input.CustomId);

if (component is null)
{
if (!throwOnMissingField)
args[i] = input.DefaultValue;
else
throw new InvalidOperationException($"Modal interaction is missing the required field: {input.CustomId}");
}
else
{
var readResult = await input.TypeConverter.ReadAsync(context, component, services).ConfigureAwait(false);

if (!readResult.IsSuccess)
return readResult;

args[i] = readResult.Value;
}
}

return ParseResult.FromSuccess(_initializer(args));
}
} }
} }

+ 26
- 0
src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs View File

@@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;

namespace Discord.Interactions
{
internal sealed class DefaultValueComponentConverter<T> : ComponentTypeConverter<T>
where T : IConvertible
{
public override Task<TypeConverterResult> ReadAsync(IInteractionContext context, IComponentInteractionData option, IServiceProvider services)
{
try
{
return option.Type switch
{
ComponentType.SelectMenu => Task.FromResult(TypeConverterResult.FromSuccess(Convert.ChangeType(string.Join(',', option.Values), typeof(T)))),
ComponentType.TextInput => Task.FromResult(TypeConverterResult.FromSuccess(Convert.ChangeType(option.Value, typeof(T)))),
_ => Task.FromResult(TypeConverterResult.FromError(InteractionCommandError.ConvertFailed, $"{option.Type} doesn't have a convertible value."))
};
}
catch (InvalidCastException castEx)
{
return Task.FromResult(TypeConverterResult.FromError(castEx));
}
}
}
}

+ 4
- 4
src/Discord.Net.Interactions/Utilities/ModalUtils.cs View File

@@ -11,16 +11,16 @@ namespace Discord.Interactions


public static IReadOnlyCollection<ModalInfo> Modals => _modalInfos.Values.ToReadOnlyCollection(); public static IReadOnlyCollection<ModalInfo> Modals => _modalInfos.Values.ToReadOnlyCollection();


public static ModalInfo GetOrAdd(Type type)
public static ModalInfo GetOrAdd(Type type, InteractionService interactionService)
{ {
if (!typeof(IModal).IsAssignableFrom(type)) if (!typeof(IModal).IsAssignableFrom(type))
throw new ArgumentException($"Must be an implementation of {nameof(IModal)}", nameof(type)); throw new ArgumentException($"Must be an implementation of {nameof(IModal)}", nameof(type));


return _modalInfos.GetOrAdd(type, ModuleClassBuilder.BuildModalInfo(type));
return _modalInfos.GetOrAdd(type, ModuleClassBuilder.BuildModalInfo(type, interactionService));
} }


public static ModalInfo GetOrAdd<T>() where T : class, IModal
=> GetOrAdd(typeof(T));
public static ModalInfo GetOrAdd<T>(InteractionService interactionService) where T : class, IModal
=> GetOrAdd(typeof(T), interactionService);


public static bool TryGet(Type type, out ModalInfo modalInfo) public static bool TryGet(Type type, out ModalInfo modalInfo)
{ {


Loading…
Cancel
Save