diff --git a/src/Discord.Net.Interactions/Extensions/RestExtensions.cs b/src/Discord.Net.Interactions/Extensions/RestExtensions.cs
new file mode 100644
index 000000000..2641617e0
--- /dev/null
+++ b/src/Discord.Net.Interactions/Extensions/RestExtensions.cs
@@ -0,0 +1,25 @@
+using Discord.Interactions;
+using System;
+
+namespace Discord.Rest
+{
+ public static class RestExtensions
+ {
+ ///
+ /// Respond to an interaction with a .
+ ///
+ /// Type of the implementation.
+ /// The interaction to respond to.
+ /// The request options for this request.
+ /// Serialized payload to be used to create a HTTP response.
+ public static string RespondWithModal(this RestInteraction interaction, string customId, RequestOptions options = null, Action modifyModal = null)
+ where T : class, IModal
+ {
+ if (!ModalUtils.TryGet(out var modalInfo))
+ throw new ArgumentException($"{typeof(T).FullName} isn't referenced by any registered Modal Interaction Command and doesn't have a cached {typeof(ModalInfo)}");
+
+ var modal = modalInfo.ToModal(customId, modifyModal);
+ return interaction.RespondWithModal(modal, options);
+ }
+ }
+}
diff --git a/src/Discord.Net.Interactions/RestInteractionModuleBase.cs b/src/Discord.Net.Interactions/RestInteractionModuleBase.cs
index a07614f7f..e83c91fef 100644
--- a/src/Discord.Net.Interactions/RestInteractionModuleBase.cs
+++ b/src/Discord.Net.Interactions/RestInteractionModuleBase.cs
@@ -65,5 +65,39 @@ namespace Discord.Interactions
else
await InteractionService._restResponseCallback(Context, payload).ConfigureAwait(false);
}
+
+ ///
+ /// Responds to the interaction with a modal.
+ ///
+ /// The modal to respond with.
+ /// The request options for this request.
+ /// A string that contains json to write back to the incoming http request.
+ ///
+ ///
+ protected override async Task RespondWithModalAsync(Modal modal, RequestOptions options = null)
+ {
+ if (Context.Interaction is not RestInteraction restInteraction)
+ throw new InvalidOperationException($"Invalid interaction type. Interaction must be a type of {nameof(RestInteraction)} in order to execute this method");
+
+ var payload = restInteraction.RespondWithModal(modal, options);
+
+ if (Context is IRestInteractionContext restContext && restContext.InteractionResponseCallback != null)
+ await restContext.InteractionResponseCallback.Invoke(payload).ConfigureAwait(false);
+ else
+ await InteractionService._restResponseCallback(Context, payload).ConfigureAwait(false);
+ }
+
+ protected override async Task RespondWithModalAsync(string customId, RequestOptions options = null)
+ {
+ if (Context.Interaction is not RestInteraction restInteraction)
+ throw new InvalidOperationException($"Invalid interaction type. Interaction must be a type of {nameof(RestInteraction)} in order to execute this method");
+
+ var payload = restInteraction.RespondWithModal(customId, options);
+
+ if (Context is IRestInteractionContext restContext && restContext.InteractionResponseCallback != null)
+ await restContext.InteractionResponseCallback.Invoke(payload).ConfigureAwait(false);
+ else
+ await InteractionService._restResponseCallback(Context, payload).ConfigureAwait(false);
+ }
}
}
diff --git a/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs b/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
index 46f0f4a4a..c2052b7c7 100644
--- a/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
+++ b/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
@@ -196,5 +196,26 @@ namespace Discord.Interactions
}).ToList(),
Options = commandOption.Options?.Select(x => x.ToApplicationCommandOptionProps()).ToList()
};
+
+ public static Modal ToModal(this ModalInfo modalInfo, string customId, Action modifyModal = null)
+ {
+ var builder = new ModalBuilder(modalInfo.Title, customId);
+
+ foreach (var input in modalInfo.Components)
+ switch (input)
+ {
+ case TextInputComponentInfo textComponent:
+ builder.AddTextInput(textComponent.Label, textComponent.CustomId, textComponent.Style, textComponent.Placeholder, textComponent.IsRequired ? textComponent.MinLength : null,
+ textComponent.MaxLength, textComponent.IsRequired, textComponent.InitialValue);
+ break;
+ default:
+ throw new InvalidOperationException($"{input.GetType().FullName} isn't a valid component info class");
+ }
+
+ if(modifyModal is not null)
+ modifyModal(builder);
+
+ return builder.Build();
+ }
}
}