diff --git a/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs b/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs
index 9751b613da..4c1a14268f 100644
--- a/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs
+++ b/src/Discord.Net.Interactions/Utilities/InteractionUtility.cs
@@ -65,13 +65,13 @@ Task HandleInteraction (SocketInteraction interaction)
/// A Task representing the asyncronous waiting operation with a result,
/// the result is null if the process timed out before receiving a valid Interaction.
///
- public static Task WaitForMessageComponentAsync(BaseSocketClient client, IUserMessage fromMessage, TimeSpan timeout,
+ public static async Task WaitForMessageComponentAsync(BaseSocketClient client, IUserMessage fromMessage, TimeSpan timeout,
CancellationToken cancellationToken = default)
{
bool Predicate (SocketInteraction interaction) => interaction is SocketMessageComponent component &&
component.Message.Id == fromMessage.Id;
- return WaitForInteractionAsync(client, timeout, Predicate, cancellationToken);
+ return await WaitForInteractionAsync(client, timeout, Predicate, cancellationToken) as SocketMessageComponent;
}
///
@@ -100,14 +100,80 @@ public static async Task ConfirmAsync (BaseSocketClient client, IMessageCh
var prompt = await channel.SendMessageAsync(message, components: component).ConfigureAwait(false);
- var response = await WaitForMessageComponentAsync(client, prompt, timeout, cancellationToken).ConfigureAwait(false) as SocketMessageComponent;
-
+ var response = await WaitForMessageComponentAsync(client, prompt, timeout, cancellationToken).ConfigureAwait(false);
await prompt.DeleteAsync().ConfigureAwait(false);
- if (response != null && response.Data.CustomId == confirmId)
- return true;
+ return response is not null && response.Data.CustomId == confirmId;
+ }
+
+ ///
+ /// Create a confirmation dialog and wait for user input asynchronously.
+ ///
+ /// Interaction to send the response/followup message to.
+ /// Timeout duration of this operation.
+ /// Optional custom prompt message.
+ /// Token for canceling the wait operation.
+ ///
+ /// A Task representing the asyncronous waiting operation with a result,
+ /// the result is if the user declined the prompt or didnt answer in time, if the user confirmed the prompt.
+ ///
+ public static async Task ConfirmAsync(SocketInteraction interaction, TimeSpan timeout, string message = null, Action updateMessage = null,
+ CancellationToken cancellationToken = default)
+ {
+ message ??= "Would you like to continue?";
+ var confirmId = $"confirm";
+ var declineId = $"decline";
+
+ var component = new ComponentBuilder()
+ .WithButton("Confirm", confirmId, ButtonStyle.Success)
+ .WithButton("Cancel", declineId, ButtonStyle.Danger)
+ .Build();
+
+ IUserMessage prompt;
+
+ if (!interaction.HasResponded)
+ {
+ await interaction.RespondAsync(message, components: component, ephemeral: true);
+ prompt = await interaction.GetOriginalResponseAsync();
+ }
else
- return false;
+ prompt = await interaction.FollowupAsync(message, components: component, ephemeral: true);
+
+ var response = await WaitForMessageComponentAsync(interaction.Discord, prompt, timeout, cancellationToken).ConfigureAwait(false);
+
+ if(updateMessage is not null)
+ await response.UpdateAsync(updateMessage);
+
+ return response is not null && response.Data.CustomId == confirmId;
+ }
+
+ ///
+ /// Responds to an interaction with a modal and asyncronously wait for the users response.
+ ///
+ /// The type of to respond with.
+ /// The interaction to respond to.
+ /// Timeout duration of this operation.
+ /// Delegate for creating s to be passed on to the s.
+ /// Service collection to be passed on to the s.
+ /// Token for canceling the wait operation.
+ ///
+ /// A Task representing the asyncronous waiting operation with a result,
+ /// the result is q if the process timed out before receiving a valid Interaction.
+ ///
+ public static async Task SendModalAsync(this SocketInteraction interaction, TimeSpan timeout,
+ Func contextFactory, IServiceProvider services = null, CancellationToken cancellationToken = default)
+ where TModal : class, IModal
+ {
+ var customId = Guid.NewGuid().ToString();
+ await interaction.RespondWithModalAsync(customId);
+ var response = await WaitForInteractionAsync(interaction.Discord, timeout, interaction =>
+ {
+ return interaction is SocketModal socketModal &&
+ socketModal.Data.CustomId == customId;
+ }, cancellationToken) as SocketModal;
+
+ var modal = await ModalUtils.CreateModalAsync(contextFactory(response, response.Discord), services).ConfigureAwait(false);
+ return modal;
}
}
}
diff --git a/src/Discord.Net.Interactions/Utilities/ModalUtils.cs b/src/Discord.Net.Interactions/Utilities/ModalUtils.cs
index e2d028e1ff..10bbc0df0f 100644
--- a/src/Discord.Net.Interactions/Utilities/ModalUtils.cs
+++ b/src/Discord.Net.Interactions/Utilities/ModalUtils.cs
@@ -2,15 +2,31 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Threading.Tasks;
namespace Discord.Interactions
{
- internal static class ModalUtils
+ ///
+ /// General utility class regarding implementations.
+ ///
+ public static class ModalUtils
{
private static readonly ConcurrentDictionary _modalInfos = new();
+ ///
+ /// Get a collection of built object of cached implementatios.
+ ///
public static IReadOnlyCollection Modals => _modalInfos.Values.ToReadOnlyCollection();
+ ///
+ /// Get or add a to the shared cache.
+ ///
+ /// Type of the implementation.
+ /// Instance of in use.
+ ///
+ /// The built instance of .
+ ///
+ /// Thrown when isn't an implementation of .
public static ModalInfo GetOrAdd(Type type, InteractionService interactionService)
{
if (!typeof(IModal).IsAssignableFrom(type))
@@ -19,9 +35,26 @@ public static ModalInfo GetOrAdd(Type type, InteractionService interactionServic
return _modalInfos.GetOrAdd(type, ModuleClassBuilder.BuildModalInfo(type, interactionService));
}
+ ///
+ /// Get or add a to the shared cache.
+ ///
+ /// Type of the implementation.
+ /// Instance of in use.
+ ///
+ /// The built instance of .
+ ///
public static ModalInfo GetOrAdd(InteractionService interactionService) where T : class, IModal
=> GetOrAdd(typeof(T), interactionService);
+ ///
+ /// Gets the associated with an implementation.
+ ///
+ /// Type of the implementation.
+ /// The built instance of .
+ ///
+ /// A bool representing whether the fetch operation was successful.
+ ///
+ /// Thrown when isn't an implementation of .
public static bool TryGet(Type type, out ModalInfo modalInfo)
{
if (!typeof(IModal).IsAssignableFrom(type))
@@ -30,9 +63,26 @@ public static bool TryGet(Type type, out ModalInfo modalInfo)
return _modalInfos.TryGetValue(type, out modalInfo);
}
+ ///
+ /// Gets the associated with an implementation.
+ ///
+ /// Type of the implementation.
+ /// The built instance of .
+ ///
+ /// A bool representing whether the fetch operation was successful.
+ ///
public static bool TryGet(out ModalInfo modalInfo) where T : class, IModal
=> TryGet(typeof(T), out modalInfo);
+ ///
+ /// Remove the entry from the cache associated with an implementation.
+ ///
+ /// Type of the implementation.
+ /// The instance of the removed entry.
+ ///
+ /// A bool representing whether the removal operation was successful.
+ ///
+ /// Thrown when isn't an implementation of .
public static bool TryRemove(Type type, out ModalInfo modalInfo)
{
if (!typeof(IModal).IsAssignableFrom(type))
@@ -41,11 +91,49 @@ public static bool TryRemove(Type type, out ModalInfo modalInfo)
return _modalInfos.TryRemove(type, out modalInfo);
}
+ ///
+ /// Remove the entry from the cache associated with an implementation.
+ ///
+ /// Type of the implementation.
+ /// The instance of the removed entry.
+ ///
+ /// A bool representing whether the removal operation was successful.
+ ///
public static bool TryRemove(out ModalInfo modalInfo) where T : class, IModal
=> TryRemove(typeof(T), out modalInfo);
+ ///
+ /// Initialize an implementation from a based .
+ ///
+ /// Type of the implementation.
+ /// Context of the .
+ /// Service provider to be passed on to the s.
+ ///
+ /// A Task representing the asyncronous initialization operation with a result,
+ /// the result is if the process was unsuccessful.
+ ///
+ public static async Task CreateModalAsync(IInteractionContext context, IServiceProvider services = null)
+ where TModal : class, IModal
+ {
+ if (!TryGet(out var modalInfo))
+ return null;
+
+ var result = await modalInfo.CreateModalAsync(context, services, true).ConfigureAwait(false);
+
+ if (!result.IsSuccess || result is not ParseResult parseResult)
+ return null;
+
+ return parseResult.Value as TModal;
+ }
+
+ ///
+ /// Clears the cache.
+ ///
public static void Clear() => _modalInfos.Clear();
- public static int Count() => _modalInfos.Count;
+ ///
+ /// Gets the count entries in the cache.
+ ///
+ public static int Count => _modalInfos.Count;
}
}
diff --git a/src/Discord.Net.WebSocket/AssemblyInfo.cs b/src/Discord.Net.WebSocket/AssemblyInfo.cs
index 442ec7dd8b..2f895af38c 100644
--- a/src/Discord.Net.WebSocket/AssemblyInfo.cs
+++ b/src/Discord.Net.WebSocket/AssemblyInfo.cs
@@ -3,3 +3,4 @@
[assembly: InternalsVisibleTo("Discord.Net.Relay")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]
[assembly: InternalsVisibleTo("Discord.Net.Tests.Unit")]
+[assembly: InternalsVisibleTo("Discord.Net.Interactions")]