From ca6f77276e111633146dce39ebff1b00376629ea Mon Sep 17 00:00:00 2001
From: Cenk Ergen <57065323+Cenngo@users.noreply.github.com>
Date: Fri, 11 Feb 2022 00:45:50 +0300
Subject: [PATCH] add component typeconverters to modals
---
.../Modals/Inputs/IInputComponentBuilder.cs | 2 +
.../Modals/Inputs/InputComponentBuilder.cs | 3 ++
.../Builders/Modals/ModalBuilder.cs | 6 ++-
.../Builders/ModuleClassBuilder.cs | 4 +-
.../Info/Commands/ModalCommandInfo.cs | 11 +++++-
.../InputComponents/InputComponentInfo.cs | 3 ++
.../Info/ModalInfo.cs | 37 +++++++++++++++++++
.../DefaultValueComponentConverter.cs | 26 +++++++++++++
.../Utilities/ModalUtils.cs | 8 ++--
9 files changed, 90 insertions(+), 10 deletions(-)
create mode 100644 src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs
diff --git a/src/Discord.Net.Interactions/Builders/Modals/Inputs/IInputComponentBuilder.cs b/src/Discord.Net.Interactions/Builders/Modals/Inputs/IInputComponentBuilder.cs
index 37cd861c4..77569718a 100644
--- a/src/Discord.Net.Interactions/Builders/Modals/Inputs/IInputComponentBuilder.cs
+++ b/src/Discord.Net.Interactions/Builders/Modals/Inputs/IInputComponentBuilder.cs
@@ -38,6 +38,8 @@ namespace Discord.Interactions.Builders
///
Type Type { get; }
+ ComponentTypeConverter TypeConverter { get; }
+
///
/// Gets the default value of this input component.
///
diff --git a/src/Discord.Net.Interactions/Builders/Modals/Inputs/InputComponentBuilder.cs b/src/Discord.Net.Interactions/Builders/Modals/Inputs/InputComponentBuilder.cs
index c2b9b0645..19674ac4d 100644
--- a/src/Discord.Net.Interactions/Builders/Modals/Inputs/InputComponentBuilder.cs
+++ b/src/Discord.Net.Interactions/Builders/Modals/Inputs/InputComponentBuilder.cs
@@ -33,6 +33,8 @@ namespace Discord.Interactions.Builders
///
public Type Type { get; private set; }
+ public ComponentTypeConverter TypeConverter { get; private set; }
+
///
public object DefaultValue { get; set; }
@@ -111,6 +113,7 @@ namespace Discord.Interactions.Builders
public TBuilder WithType(Type type)
{
Type = type;
+ TypeConverter = Modal._interactionService.GetComponentTypeConverter(type);
return Instance;
}
diff --git a/src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs b/src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs
index e120e78be..fc1dbdc0e 100644
--- a/src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs
+++ b/src/Discord.Net.Interactions/Builders/Modals/ModalBuilder.cs
@@ -9,6 +9,7 @@ namespace Discord.Interactions.Builders
///
public class ModalBuilder
{
+ internal readonly InteractionService _interactionService;
internal readonly List _components;
///
@@ -31,11 +32,12 @@ namespace Discord.Interactions.Builders
///
public IReadOnlyCollection Components => _components;
- internal ModalBuilder(Type type)
+ internal ModalBuilder(Type type, InteractionService interactionService)
{
if (!typeof(IModal).IsAssignableFrom(type))
throw new ArgumentException($"Must be an implementation of {nameof(IModal)}", nameof(type));
+ _interactionService = interactionService;
_components = new();
}
@@ -43,7 +45,7 @@ namespace Discord.Interactions.Builders
/// Initializes a new
///
/// The initialization delegate for this modal.
- public ModalBuilder(Type type, ModalInitializer modalInitializer) : this(type)
+ public ModalBuilder(Type type, ModalInitializer modalInitializer, InteractionService interactionService) : this(type, interactionService)
{
ModalInitializer = modalInitializer;
}
diff --git a/src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs
index c163ec83d..ac727496c 100644
--- a/src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs
+++ b/src/Discord.Net.Interactions/Builders/ModuleClassBuilder.cs
@@ -482,7 +482,7 @@ namespace Discord.Interactions.Builders
#endregion
#region Modals
- public static ModalInfo BuildModalInfo(Type modalType)
+ public static ModalInfo BuildModalInfo(Type modalType, InteractionService interactionService)
{
if (!typeof(IModal).IsAssignableFrom(modalType))
throw new InvalidOperationException($"{modalType.FullName} isn't an implementation of {typeof(IModal).FullName}");
@@ -491,7 +491,7 @@ namespace Discord.Interactions.Builders
try
{
- var builder = new ModalBuilder(modalType)
+ var builder = new ModalBuilder(modalType, interactionService)
{
Title = instance.Title
};
diff --git a/src/Discord.Net.Interactions/Info/Commands/ModalCommandInfo.cs b/src/Discord.Net.Interactions/Info/Commands/ModalCommandInfo.cs
index a750603fc..40f93ebfe 100644
--- a/src/Discord.Net.Interactions/Info/Commands/ModalCommandInfo.cs
+++ b/src/Discord.Net.Interactions/Info/Commands/ModalCommandInfo.cs
@@ -52,8 +52,15 @@ namespace Discord.Interactions
if (additionalArgs is not null)
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);
}
diff --git a/src/Discord.Net.Interactions/Info/InputComponents/InputComponentInfo.cs b/src/Discord.Net.Interactions/Info/InputComponents/InputComponentInfo.cs
index 790838ad9..e457562a7 100644
--- a/src/Discord.Net.Interactions/Info/InputComponents/InputComponentInfo.cs
+++ b/src/Discord.Net.Interactions/Info/InputComponents/InputComponentInfo.cs
@@ -39,6 +39,8 @@ namespace Discord.Interactions
///
public Type Type { get; }
+ public ComponentTypeConverter TypeConverter { get; }
+
///
/// Gets the default value of this component.
///
@@ -57,6 +59,7 @@ namespace Discord.Interactions
IsRequired = builder.IsRequired;
ComponentType = builder.ComponentType;
Type = builder.Type;
+ TypeConverter = builder.TypeConverter;
DefaultValue = builder.DefaultValue;
Attributes = builder.Attributes.ToImmutableArray();
}
diff --git a/src/Discord.Net.Interactions/Info/ModalInfo.cs b/src/Discord.Net.Interactions/Info/ModalInfo.cs
index edc31373e..1b1b198ac 100644
--- a/src/Discord.Net.Interactions/Info/ModalInfo.cs
+++ b/src/Discord.Net.Interactions/Info/ModalInfo.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
+using System.Threading.Tasks;
namespace Discord.Interactions
{
@@ -86,5 +87,41 @@ namespace Discord.Interactions
return _initializer(args);
}
+
+ internal async Task 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));
+ }
}
}
diff --git a/src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs b/src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs
new file mode 100644
index 000000000..ed2ee951b
--- /dev/null
+++ b/src/Discord.Net.Interactions/TypeConverters/ComponentInteractions/DefaultValueComponentConverter.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Discord.Interactions
+{
+ internal sealed class DefaultValueComponentConverter : ComponentTypeConverter
+ where T : IConvertible
+ {
+ public override Task 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));
+ }
+ }
+ }
+}
diff --git a/src/Discord.Net.Interactions/Utilities/ModalUtils.cs b/src/Discord.Net.Interactions/Utilities/ModalUtils.cs
index d42cc2fe9..9fdd195c2 100644
--- a/src/Discord.Net.Interactions/Utilities/ModalUtils.cs
+++ b/src/Discord.Net.Interactions/Utilities/ModalUtils.cs
@@ -11,16 +11,16 @@ namespace Discord.Interactions
public static IReadOnlyCollection Modals => _modalInfos.Values.ToReadOnlyCollection();
- public static ModalInfo GetOrAdd(Type type)
+ public static ModalInfo GetOrAdd(Type type, InteractionService interactionService)
{
if (!typeof(IModal).IsAssignableFrom(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() where T : class, IModal
- => GetOrAdd(typeof(T));
+ public static ModalInfo GetOrAdd(InteractionService interactionService) where T : class, IModal
+ => GetOrAdd(typeof(T), interactionService);
public static bool TryGet(Type type, out ModalInfo modalInfo)
{