From 8ac593375b4d42ded860108a86b908abecfb804f Mon Sep 17 00:00:00 2001 From: Miu Date: Fri, 24 Feb 2023 16:38:39 +0800 Subject: [PATCH] Adds support for auto-deferring responses (#14) * fix: wrong function name * add: better and generalized response interface * add: auto-defer command responses * fix: readme * fix: missing delegate for NexusMiddlewareEventCore * fix: ephemeral for deferred in auto-defer not working * add: additional measure against random defers whilst scheduled defer --- README.md | 76 ++- src/main/java/pw/mihou/nexus/Nexus.kt | 3 + ...CommonsInterceptorsMessageConfiguration.kt | 10 +- .../modules/NexusGlobalConfiguration.kt | 7 + .../modules/NexusInterceptorsConfiguration.kt | 11 +- .../modules/NexusLaunchConfiguration.kt | 8 + .../command/core/NexusCommandDispatcher.kt | 18 +- .../command/core/NexusCommandEventCore.kt | 50 ++ .../command/core/NexusMiddlewareEventCore.kt | 7 +- .../command/facade/NexusCommandEvent.java | 629 +++++++++--------- .../command/facade/NexusMiddlewareEvent.java | 5 +- .../core/NexusMiddlewareGateCore.java | 2 +- .../interceptors/facades/NexusMiddleware.java | 2 +- .../facades/NexusMiddlewareGate.java | 2 +- .../command/responses/NexusAutoResponse.kt | 14 + .../nexus/features/messages/NexusMessage.kt | 21 + .../messages/core/NexusMessageCore.java | 111 ---- .../messages/facade/NexusMessage.java | 146 ---- 18 files changed, 523 insertions(+), 599 deletions(-) create mode 100644 src/main/java/pw/mihou/nexus/features/command/responses/NexusAutoResponse.kt create mode 100644 src/main/java/pw/mihou/nexus/features/messages/NexusMessage.kt delete mode 100755 src/main/java/pw/mihou/nexus/features/messages/core/NexusMessageCore.java delete mode 100755 src/main/java/pw/mihou/nexus/features/messages/facade/NexusMessage.java diff --git a/README.md b/README.md index ceccfe33..2b9fd51a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ one Discord bot per application. 4. [Deferred Middleware Responses](#-deferred-middleware-responses) 5. [Basic Subcommand Router](#-basic-subcommand-router) 6. [Option Validation](#-option-validation) -7. [Command Synchronizations](#-command-synchronizations) +7. [Auto-deferring Responses](#-auto-deferring-responses) +8. [Command Synchronizations](#-command-synchronizations) #### 💭 Preparation @@ -74,6 +75,8 @@ DiscordApiBuilder() > Before we start, a fair warning, it is never recommended to use the `event.interaction.respondLater()` methods of Javacord when using > Nexus because we have special handling for middlewares that requires coordination between the command and the middleware. It is better > to use `event.respondLater()` or `event.respondLaterAsEphemeral()` instead. +> +> You can even use auto-deferring instead which handles these cases for you, read more at [Auto-deferring Responses](#-auto-deferring-responses) Nexus offers a simple, and straightforward manner of designing commands, but before we can continue designing commands, let us first understand a few fundamental rules that Nexus enforces: @@ -107,7 +110,16 @@ object PingCommand: NexusHandler { override fun onEvent(event: NexusCommandEvent) { val server = event.server.orElseThrow() - event.respondNowWith("Hello ${server.name}!") + // There are two ways to respond in Nexus, one is auto-deferring and the other is manual response. + // the example shown here demonstrates auto-defer responses. + event.autoDefer(ephemeral = false) { + return@autoDefer NexusMessage.with { + setContent("Hello ${server.name}") + } + } + + // The example below demonstrates manual response wherein it is up to you to manually respond or not. + // event.respondNowWith("Hello ${server.name}!") } } ``` @@ -176,7 +188,7 @@ You can even configure more properties such as whether to make the deferred resp defer by setting either of the properties: ```kotlin // Default values -Nexus.configuration.interceptors.autoDeferMiddlewaresInMilliseconds = 2500 +Nexus.configuration.global.autoDeferAfterMilliseconds = 2350 Nexus.configuration.interceptors.autoDeferAsEphemeral = true ``` @@ -184,8 +196,9 @@ Nexus.configuration.interceptors.autoDeferAsEphemeral = true > > As stated above, it is your responsibility to use deferred responses in the commands after enabling this. Nexus > will not defer your command responses automatically, you should use methods such as `event.respondLater()` or `event.respondLaterAsEphemeral()` -> to handle these cases. Although, these methods may return non-ephemeral or ephemeral depending on the `autoDeferAsEphemeral` -> value or depending on how you deferred it manually. +> to handle these cases, otherwise, you can have Nexus auto-defer for you. +> +> To learn more about auto-deferring responses, you can read [Auto-deferring Responses](#-auto-deferring-responses). #### 💭 Interceptor Repositories @@ -269,6 +282,59 @@ but it suits most developers' needs and prevents a lot of potential code duplica To learn more about how to use the option validation, you can check our example: - [Option Validators](examples/option_validators) +#### 💭 Auto-deferring Responses + +Nexus supports auto-deferring of responses in both middlewares and commands, but before that, we have to understand a +thing with slash commands and Nexus, and that is the three-second response requirement before defer. In Nexus, there are two core +features that can haggle up that three-second response requirement and that are: +1. Middlewares +2. Your actual command itself + +And to solve an issue where the developer does not know which feature exactly causes an auto-defer, Nexus introduces auto-deferring, but +it requires you to enable the feature on both middlewares and the command itself. To enable auto-defer in middlewares, you can check +the [Deferred Middleware Responses](#-deferred-middleware-responses) section. + +To enable auto-deferring in commands themselves, you have to use the `event.autoDefer(ephemeral, function)` method instead of the +other related methods. It is recommended to actually use this especially when you have long-running middlewares because this will +also take care of handling when a middleware actually requests for deferred response. + +An example of how this looks is: +```kotlin +override fun onEvent(event: NexusCommandEvent) { + event.autoDefer(ephemeral = true) { + // ... imagine something long running task + return@autoDefer NexusMessage.with { + setContent("Hello!") + } + } +} +``` + +If you want to receive the response from Discord, it is possible by actually handling the response of the `autoDefer` method: +```kotlin +override fun onEvent(event: NexusCommandEvent) { + event.autoDefer(ephemeral = true) { + // ... imagine something long running task + return@autoDefer NexusMessage.with { + setContent("Hello!") + } + }.thenAccept { response -> + // Updater is only available if the message went through deferred response. + val updater = response.updater + // Using `getOrRequestMessage` actually calls `InteractionOriginalResponseUpdater.update()` if the interaction + // answered non-deferred since Javacord or Discord does not offer getting the actual message immediately from + // the response. + val message = response.getOrRequestMessage() + } +} +``` + +To configure when to defer, you can configure the following setting: +```kotlin +// Default values +Nexus.configuration.global.autoDeferAfterMilliseconds = 2350 +``` + #### 💭 Command Synchronizations Nexus brings together a simple and straightforward method of synchronization for commands. To synchronize commands, all you diff --git a/src/main/java/pw/mihou/nexus/Nexus.kt b/src/main/java/pw/mihou/nexus/Nexus.kt index e552d68a..e44d5cf3 100644 --- a/src/main/java/pw/mihou/nexus/Nexus.kt +++ b/src/main/java/pw/mihou/nexus/Nexus.kt @@ -97,6 +97,9 @@ object Nexus: SlashCommandCreateListener, ButtonClickListener { @JvmStatic val synchronizer = NexusSynchronizer() + @get:JvmSynthetic + internal val launch get() = configuration.launch + @JvmStatic internal val launcher get() = configuration.launch.launcher diff --git a/src/main/java/pw/mihou/nexus/configuration/modules/NexusCommonsInterceptorsMessageConfiguration.kt b/src/main/java/pw/mihou/nexus/configuration/modules/NexusCommonsInterceptorsMessageConfiguration.kt index 758a1367..4413fe9f 100644 --- a/src/main/java/pw/mihou/nexus/configuration/modules/NexusCommonsInterceptorsMessageConfiguration.kt +++ b/src/main/java/pw/mihou/nexus/configuration/modules/NexusCommonsInterceptorsMessageConfiguration.kt @@ -1,7 +1,7 @@ package pw.mihou.nexus.configuration.modules import pw.mihou.nexus.features.command.facade.NexusCommandEvent -import pw.mihou.nexus.features.messages.facade.NexusMessage +import pw.mihou.nexus.features.messages.NexusMessage class NexusCommonsInterceptorsMessageConfiguration internal constructor() { @@ -9,11 +9,9 @@ class NexusCommonsInterceptorsMessageConfiguration internal constructor() { @get:JvmName("getRatelimitedMessage") @Volatile var ratelimited: (event: NexusCommandEvent, remainingSeconds: Long) -> NexusMessage = { _, remainingSeconds -> - NexusMessage.fromEphemereal( - "**SLOW DOWN***!" - + "\n" - + "You are executing commands too fast, please try again in $remainingSeconds seconds." - ) + NexusMessage.with(true) { + this.setContent("***SLOW DOWN***!\nYou are executing commands too fast, please try again in $remainingSeconds seconds.") + } } } \ No newline at end of file diff --git a/src/main/java/pw/mihou/nexus/configuration/modules/NexusGlobalConfiguration.kt b/src/main/java/pw/mihou/nexus/configuration/modules/NexusGlobalConfiguration.kt index 09fbe2e1..1b82a188 100644 --- a/src/main/java/pw/mihou/nexus/configuration/modules/NexusGlobalConfiguration.kt +++ b/src/main/java/pw/mihou/nexus/configuration/modules/NexusGlobalConfiguration.kt @@ -40,4 +40,11 @@ class NexusGlobalConfiguration internal constructor() { * whether they have a local inheritance class. */ @JvmField @Volatile var inheritance: Any? = null + + /** + * When in an automatic defer situation, the framework will automatically defer the response when the time has + * surpassed the specified amount. You can specify this to any value less than 3,000 but the default value should + * be more than enough even when considering network latencies. + */ + @JvmField @Volatile var autoDeferAfterMilliseconds: Long = 2350 } \ No newline at end of file diff --git a/src/main/java/pw/mihou/nexus/configuration/modules/NexusInterceptorsConfiguration.kt b/src/main/java/pw/mihou/nexus/configuration/modules/NexusInterceptorsConfiguration.kt index c1a79e2d..297480e0 100644 --- a/src/main/java/pw/mihou/nexus/configuration/modules/NexusInterceptorsConfiguration.kt +++ b/src/main/java/pw/mihou/nexus/configuration/modules/NexusInterceptorsConfiguration.kt @@ -2,12 +2,12 @@ package pw.mihou.nexus.configuration.modules class NexusInterceptorsConfiguration internal constructor() { /** - * Sets whether to defer middleware responses when the middlewares have reached the - * [autoDeferMiddlewaresInMilliseconds] milliseconds. + * Sets whether to defer middleware responses when the middlewares have reached processing time beyond + * [NexusGlobalConfiguration.autoDeferAfterMilliseconds]. * This is only possible in middlewares because middlewares uses a custom method of responding. * * WARNING: You have to write your command to also use deferred responses. It is solely your responsibility to - * ensure that whichever commands uses middlewares that would take longer than the specified [autoDeferMiddlewaresInMilliseconds] + * ensure that whichever commands uses middlewares that would take longer than the specified [NexusGlobalConfiguration.autoDeferAfterMilliseconds] * has to use deferred responses. */ @Volatile @@ -15,11 +15,6 @@ class NexusInterceptorsConfiguration internal constructor() { @get:JvmName("autoDeferMiddlewareResponses") var autoDeferMiddlewareResponses = false - @Volatile - @set:JvmName("setAutoDeferMiddlewaresInMilliseconds") - @get:JvmName("autoDeferMiddlewaresInMilliseconds") - var autoDeferMiddlewaresInMilliseconds = 2500L - @Volatile @set:JvmName("setAutoDeferAsEphemeral") @get:JvmName("autoDeferAsEphemeral") diff --git a/src/main/java/pw/mihou/nexus/configuration/modules/NexusLaunchConfiguration.kt b/src/main/java/pw/mihou/nexus/configuration/modules/NexusLaunchConfiguration.kt index 03a04f8c..8d183938 100644 --- a/src/main/java/pw/mihou/nexus/configuration/modules/NexusLaunchConfiguration.kt +++ b/src/main/java/pw/mihou/nexus/configuration/modules/NexusLaunchConfiguration.kt @@ -1,17 +1,25 @@ package pw.mihou.nexus.configuration.modules import pw.mihou.nexus.core.threadpool.NexusThreadPool +import java.util.concurrent.TimeUnit class NexusLaunchConfiguration internal constructor() { @JvmField var launcher: NexusLaunchWrapper = NexusLaunchWrapper { task -> NexusThreadPool.executorService.submit { task.run() } } + @JvmField var scheduler: NexusScheduledLaunchWrapper = NexusScheduledLaunchWrapper { timeInMillis, task -> + NexusThreadPool.scheduledExecutorService.schedule(task::run, timeInMillis, TimeUnit.MILLISECONDS) + } } fun interface NexusLaunchWrapper { fun launch(task: NexusLaunchTask) } +fun interface NexusScheduledLaunchWrapper { + fun launch(timeInMillis: Long, task: NexusLaunchTask) +} + fun interface NexusLaunchTask { fun run() } \ No newline at end of file diff --git a/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandDispatcher.kt b/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandDispatcher.kt index 6efb9faf..1b06c3ec 100755 --- a/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandDispatcher.kt +++ b/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandDispatcher.kt @@ -13,7 +13,8 @@ import pw.mihou.nexus.features.command.interceptors.core.NexusCommandInterceptor import pw.mihou.nexus.features.command.interceptors.core.NexusMiddlewareGateCore import pw.mihou.nexus.features.command.validation.OptionValidation import pw.mihou.nexus.features.command.validation.result.ValidationResult -import pw.mihou.nexus.features.messages.core.NexusMessageCore +import pw.mihou.nexus.features.messages.NexusMessage +import java.time.Instant import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -41,28 +42,29 @@ object NexusCommandDispatcher { val future = CompletableFuture.supplyAsync { NexusCommandInterceptorCore.execute(nexusEvent, NexusCommandInterceptorCore.middlewares(middlewares)) } - NexusThreadPool.scheduledExecutorService.schedule({ + val timeUntil = Instant.now().toEpochMilli() - + event.interaction.creationTimestamp.minusMillis(Nexus.configuration.global.autoDeferAfterMilliseconds).toEpochMilli() + Nexus.launch.scheduler.launch(timeUntil) { if (future.isDone) { - return@schedule + return@launch } nexusEvent.respondLaterAsEphemeralIf(Nexus.configuration.interceptors.autoDeferAsEphemeral) .exceptionally(ExceptionLogger.get()) - }, Nexus.configuration.interceptors.autoDeferMiddlewaresInMilliseconds, TimeUnit.MILLISECONDS) + } future.join() } else { NexusCommandInterceptorCore.execute(nexusEvent, NexusCommandInterceptorCore.middlewares(middlewares)) } if (middlewareGate != null) { - val middlewareResponse = middlewareGate.response() as NexusMessageCore? + val middlewareResponse = middlewareGate.response() if (middlewareResponse != null) { val updaterFuture = nexusEvent.updater.get() if (updaterFuture != null) { val updater = updaterFuture.join() - (middlewareResponse.convertTo(updater) as InteractionOriginalResponseUpdater).update() - .exceptionally(ExceptionLogger.get()) + middlewareResponse.into(updater).update().exceptionally(ExceptionLogger.get()) } else { - middlewareResponse.convertTo(nexusEvent.respondNow()).respond().exceptionally(ExceptionLogger.get()) + middlewareResponse.into(nexusEvent.respondNow()).respond().exceptionally(ExceptionLogger.get()) } } return diff --git a/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandEventCore.kt b/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandEventCore.kt index fbcb356b..3d4f9d0d 100755 --- a/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandEventCore.kt +++ b/src/main/java/pw/mihou/nexus/features/command/core/NexusCommandEventCore.kt @@ -1,11 +1,18 @@ package pw.mihou.nexus.features.command.core +import org.javacord.api.entity.message.MessageFlag import org.javacord.api.event.interaction.SlashCommandCreateEvent import org.javacord.api.interaction.callback.InteractionOriginalResponseUpdater +import org.javacord.api.util.logging.ExceptionLogger +import pw.mihou.nexus.Nexus import pw.mihou.nexus.features.command.facade.NexusCommand import pw.mihou.nexus.features.command.facade.NexusCommandEvent +import pw.mihou.nexus.features.command.responses.NexusAutoResponse +import pw.mihou.nexus.features.messages.NexusMessage +import java.time.Instant import java.util.concurrent.CompletableFuture import java.util.concurrent.atomic.AtomicReference +import java.util.function.Function class NexusCommandEventCore(private val event: SlashCommandCreateEvent, private val command: NexusCommand) : NexusCommandEvent { private val store: Map = HashMap() @@ -14,6 +21,49 @@ class NexusCommandEventCore(private val event: SlashCommandCreateEvent, private override fun getBaseEvent() = event override fun getCommand() = command override fun store() = store + override fun autoDefer(ephemeral: Boolean, response: Function): CompletableFuture { + if (updater.get() == null) { + val timeUntil = Instant.now().toEpochMilli() - event.interaction.creationTimestamp + .minusMillis(Nexus.configuration.global.autoDeferAfterMilliseconds) + .toEpochMilli() + + Nexus.launch.scheduler.launch(timeUntil) { + if (updater.get() == null) { + respondLaterAsEphemeralIf(ephemeral).exceptionally(ExceptionLogger.get()) + } + } + } + val future = CompletableFuture() + Nexus.launcher.launch { + try { + val message = response.apply(null) + val updater = updater.get() + if (updater == null) { + val responder = respondNow() + if (ephemeral) { + responder.setFlags(MessageFlag.EPHEMERAL) + } + message.into(responder).respond() + .thenAccept { r -> future.complete(NexusAutoResponse(r, null)) } + .exceptionally { exception -> + future.completeExceptionally(exception) + return@exceptionally null + } + } else { + val completedUpdater = updater.join() + message.into(completedUpdater).update() + .thenAccept { r -> future.complete(NexusAutoResponse(null, r)) } + .exceptionally { exception -> + future.completeExceptionally(exception) + return@exceptionally null + } + } + } catch (exception: Exception) { + future.completeExceptionally(exception) + } + } + return future + } override fun respondLater(): CompletableFuture { return updater.updateAndGet { interaction.respondLater() }!! diff --git a/src/main/java/pw/mihou/nexus/features/command/core/NexusMiddlewareEventCore.kt b/src/main/java/pw/mihou/nexus/features/command/core/NexusMiddlewareEventCore.kt index 53a9cfdb..202d701e 100644 --- a/src/main/java/pw/mihou/nexus/features/command/core/NexusMiddlewareEventCore.kt +++ b/src/main/java/pw/mihou/nexus/features/command/core/NexusMiddlewareEventCore.kt @@ -6,8 +6,10 @@ import pw.mihou.nexus.features.command.facade.NexusCommand import pw.mihou.nexus.features.command.facade.NexusCommandEvent import pw.mihou.nexus.features.command.facade.NexusMiddlewareEvent import pw.mihou.nexus.features.command.interceptors.core.NexusMiddlewareGateCore -import pw.mihou.nexus.features.messages.facade.NexusMessage +import pw.mihou.nexus.features.command.responses.NexusAutoResponse +import pw.mihou.nexus.features.messages.NexusMessage import java.util.concurrent.CompletableFuture +import java.util.function.Function class NexusMiddlewareEventCore(val event: NexusCommandEvent, private val gate: NexusMiddlewareGateCore): NexusMiddlewareEvent { override fun getBaseEvent(): SlashCommandCreateEvent = event.baseEvent @@ -16,6 +18,9 @@ class NexusMiddlewareEventCore(val event: NexusCommandEvent, private val gate: N override fun respondLaterAsEphemeral(): CompletableFuture = event.respondLaterAsEphemeral() override fun store(): MutableMap = event.store() + override fun autoDefer(ephemeral: Boolean, response: Function): CompletableFuture { + return event.autoDefer(ephemeral, response) + } override fun next() { gate.next() diff --git a/src/main/java/pw/mihou/nexus/features/command/facade/NexusCommandEvent.java b/src/main/java/pw/mihou/nexus/features/command/facade/NexusCommandEvent.java index 4384938b..a9be65ae 100755 --- a/src/main/java/pw/mihou/nexus/features/command/facade/NexusCommandEvent.java +++ b/src/main/java/pw/mihou/nexus/features/command/facade/NexusCommandEvent.java @@ -1,308 +1,321 @@ -package pw.mihou.nexus.features.command.facade; - -import org.javacord.api.DiscordApi; -import org.javacord.api.entity.channel.ServerTextChannel; -import org.javacord.api.entity.channel.TextChannel; -import org.javacord.api.entity.message.MessageFlag; -import org.javacord.api.entity.message.embed.EmbedBuilder; -import org.javacord.api.entity.server.Server; -import org.javacord.api.entity.user.User; -import org.javacord.api.event.interaction.SlashCommandCreateEvent; -import org.javacord.api.interaction.SlashCommandInteraction; -import org.javacord.api.interaction.callback.InteractionImmediateResponseBuilder; -import org.javacord.api.interaction.callback.InteractionOriginalResponseUpdater; -import org.javacord.api.util.logging.ExceptionLogger; -import pw.mihou.nexus.Nexus; -import pw.mihou.nexus.features.command.interceptors.core.NexusCommandInterceptorCore; -import pw.mihou.nexus.features.command.interceptors.core.NexusMiddlewareGateCore; -import pw.mihou.nexus.features.messages.core.NexusMessageCore; -import pw.mihou.nexus.sharding.NexusShardingManager; - -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public interface NexusCommandEvent { - - /** - * Gets the base event that was received from Javacord. This is usually nothing - * of use to the end-user. - * - * @return The base event that was received. - */ - SlashCommandCreateEvent getBaseEvent(); - - /** - * Gets the interaction received from Javacord. - * - * @return The interaction that was received from Javacord. - */ - default SlashCommandInteraction getInteraction() { - return getBaseEvent().getSlashCommandInteraction(); - } - - /** - * Gets the user received from Javacord. - * - * @return The user that was received from Javacord. - */ - default User getUser() { - return getInteraction().getUser(); - } - - /** - * Gets the channel that isn't an optional. This exists primarily because there is no currently possible - * way for the text channel to be optional in Discord Slash Commands. - * - * @return The text channel that is being used. - */ - default TextChannel getChannel() { - return getInteraction().getChannel().orElseThrow(() -> new NoSuchElementException( - "It seems like the text channel is no longer always available. Please create an issue on https://github.com/ShindouMihou/Nexus" - )); - } - - /** - * Gets the server instance. - * - * @return The server instance. - */ - default Optional getServer() { - return getInteraction().getServer(); - } - - /** - * Gets the ID of the server. - * - * @return The ID of the server. - */ - default Optional getServerId() { - return getInteraction().getServer().map(Server::getId); - } - - /** - * Gets the ID of the text channel where the command was executed. - * - * @return The ID of the text channel where the command was executed. - */ - default long getChannelId() { - return getChannel().getId(); - } - - /** - * Gets the ID of the user that executed the command. - * - * @return The ID of the user who executed this command. - */ - default long getUserId() { - return getUser().getId(); - } - - /** - * Gets the text channel as a server text channel. - * - * @return The text channel as a server text channel. - */ - default Optional getServerTextChannel() { - return getChannel().asServerTextChannel(); - } - /** - * Gets the command that was executed. - * - * @return The command that was executed. - */ - NexusCommand getCommand(); - - /** - * Gets the Discord API shard that was in charge of this event. - * - * @return The Discord API shard that was in charge of this event. - */ - default DiscordApi getApi() { - return getBaseEvent().getApi(); - } - - /** - * Gets the {@link NexusShardingManager} that is associated with the {@link Nexus} instance. - * - * @return The Nexus Shard Manager associated with this Nexus instance. - */ - default NexusShardingManager getShardManager() { - return Nexus.getShardingManager(); - } - - /** - * Gets the immediate response for this command. - * - * @return The immediate response builder associated with this command. - */ - default InteractionImmediateResponseBuilder respondNow() { - return getInteraction().createImmediateResponder(); - } - - /** - * A short-hand expression of sending a non-ephemeral response to Discord with the given content. This does not - * handle the exceptions, please handle the exceptions accordingly. - * - * @param content the content to send as a response. - * @return A future that contains the updater to update the response when needed. - */ - default CompletableFuture respondNowWith(String content) { - return respondNow().setContent(content).respond(); - } - - /** - * A short-hand expression of sending a non-ephemeral response to Discord with the given embeds. This does not - * handle the exceptions, please handle the exceptions accordingly. - * - * @param embeds the embeds to send as a response. - * @return A future that contains the updater to update the response when needed. - */ - default CompletableFuture respondNowWith(EmbedBuilder... embeds) { - return respondNow().addEmbeds(embeds).respond(); - } - - /** - * A short-hand expression of sending an ephemeral response to Discord with the given content. This does not - * handle the exceptions, please handle the exceptions accordingly. - * - * @param content the content to send as a response. - * @return A future that contains the updater to update the response when needed. - */ - default CompletableFuture respondNowEphemerallyWith(String content) { - return respondNowAsEphemeral().setContent(content).respond(); - } - - /** - * A short-hand expression of sending an ephemeral response to Discord with the given embeds. This does not - * handle the exceptions, please handle the exceptions accordingly. - * - * @param embeds the embeds to send as a response. - * @return A future that contains the updater to update the response when needed. - */ - default CompletableFuture respondNowEphemerallyWith(EmbedBuilder... embeds) { - return respondNowAsEphemeral().addEmbeds(embeds).respond(); - } - - /** - * Gets the immediate response builder for this command and adds the - * {@link MessageFlag#EPHEMERAL} flag ahead of time. - * - * @return The immediate response builder associated with this command with the - * ephemeral flag added. - */ - default InteractionImmediateResponseBuilder respondNowAsEphemeral() { - return respondNow().setFlags(MessageFlag.EPHEMERAL); - } - - /** - * Gets the {@link InteractionOriginalResponseUpdater} associated with this command. - * - * @return The {@link InteractionOriginalResponseUpdater}. - */ - CompletableFuture respondLater(); - - /** - * Gets the {@link InteractionOriginalResponseUpdater} associated with this command with the - * ephemeral flag attached. - * - * @return The {@link InteractionOriginalResponseUpdater} with the ephemeral flag attached. - */ - CompletableFuture respondLaterAsEphemeral(); - - /** - * Gets the {@link InteractionOriginalResponseUpdater} associated with this command with the ephemeral flag - * attached if the predicate is true. - * - * @param predicate The predicate to determine whether to use ephemeral response or not. - * @return The {@link InteractionOriginalResponseUpdater} for this interaction. - */ - default CompletableFuture respondLaterAsEphemeralIf(boolean predicate) { - if (predicate) { - return respondLaterAsEphemeral(); - } - - return respondLater(); - } - - /** - * Gets the {@link InteractionOriginalResponseUpdater} associated with this command with the ephemeral flag - * attached if the predicate is true. - * - * @param predicate The predicate to determine whether to use ephemeral response or not. - * @return The {@link InteractionOriginalResponseUpdater} for this interaction. - */ - default CompletableFuture respondLaterAsEphemeralIf(Predicate predicate) { - return respondLaterAsEphemeralIf(predicate.test(null)); - } - - /** - * Gets the event local store that is used to contain shared fields accessible from middlewares, command and - * afterwares themselves. You can use this to store data such as whether the command was completed successfully - * or other related. - * - * @return The event-local store from this event. - */ - Map store(); - - /** - * Gets the value of the given key from the {@link NexusCommandEvent#store()} and maps it into the type given - * if possible, otherwise returns null. - * - * @param key The key to get from the {@link NexusCommandEvent#store()}. - * @param type The type expected of the value. - * @param The type expected of the value. - * - * @return The value mapped with the key in {@link NexusCommandEvent#store()} mapped to the type, otherwise null. - */ - default T get(String key, Class type) { - if (!store().containsKey(key)) - return null; - - Object object = store().get(key); - - if (type.isAssignableFrom(object.getClass())) { - return type.cast(object); - } - - return null; - } - - /** - * A short-hand expression for placing a key-value pair to {@link NexusCommandEvent#store()}. - * - * @param key The key to insert to the store. - * @param value The value to insert to the store. - */ - default void store(String key, Object value) { - store().put(key, value); - } - - /** - * Activates one or more middlewares and executes the success consumer once all middlewares succeed without - * throwing an issue. This does not support {@link InteractionOriginalResponseUpdater} or related, it will use - * {@link InteractionImmediateResponseBuilder} instead. - * - * @param middlewares the middlewares to activate. - * @param success what to do when all the middlewares succeed. - */ - default void middlewares(List middlewares, Consumer success) { - NexusMiddlewareGateCore middlewareGate = NexusCommandInterceptorCore.execute(this, - NexusCommandInterceptorCore.middlewares(middlewares)); - - if (middlewareGate == null) { - success.accept(null); - return; - } - - NexusMessageCore response = ((NexusMessageCore) middlewareGate.response()); - if (response != null) { - response.convertTo(respondNow()).respond().exceptionally(ExceptionLogger.get()); - } - } - -} +package pw.mihou.nexus.features.command.facade; + +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.ServerTextChannel; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.MessageFlag; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.server.Server; +import org.javacord.api.entity.user.User; +import org.javacord.api.event.interaction.SlashCommandCreateEvent; +import org.javacord.api.interaction.SlashCommandInteraction; +import org.javacord.api.interaction.callback.InteractionImmediateResponseBuilder; +import org.javacord.api.interaction.callback.InteractionOriginalResponseUpdater; +import org.javacord.api.util.logging.ExceptionLogger; +import pw.mihou.nexus.Nexus; +import pw.mihou.nexus.features.command.interceptors.core.NexusCommandInterceptorCore; +import pw.mihou.nexus.features.command.interceptors.core.NexusMiddlewareGateCore; +import pw.mihou.nexus.features.command.responses.NexusAutoResponse; +import pw.mihou.nexus.features.messages.NexusMessage; +import pw.mihou.nexus.sharding.NexusShardingManager; + +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public interface NexusCommandEvent { + + /** + * Gets the base event that was received from Javacord. This is usually nothing + * of use to the end-user. + * + * @return The base event that was received. + */ + SlashCommandCreateEvent getBaseEvent(); + + /** + * Gets the interaction received from Javacord. + * + * @return The interaction that was received from Javacord. + */ + default SlashCommandInteraction getInteraction() { + return getBaseEvent().getSlashCommandInteraction(); + } + + /** + * Gets the user received from Javacord. + * + * @return The user that was received from Javacord. + */ + default User getUser() { + return getInteraction().getUser(); + } + + /** + * Gets the channel that isn't an optional. This exists primarily because there is no currently possible + * way for the text channel to be optional in Discord Slash Commands. + * + * @return The text channel that is being used. + */ + default TextChannel getChannel() { + return getInteraction().getChannel().orElseThrow(() -> new NoSuchElementException( + "It seems like the text channel is no longer always available. Please create an issue on https://github.com/ShindouMihou/Nexus" + )); + } + + /** + * Gets the server instance. + * + * @return The server instance. + */ + default Optional getServer() { + return getInteraction().getServer(); + } + + /** + * Gets the ID of the server. + * + * @return The ID of the server. + */ + default Optional getServerId() { + return getInteraction().getServer().map(Server::getId); + } + + /** + * Gets the ID of the text channel where the command was executed. + * + * @return The ID of the text channel where the command was executed. + */ + default long getChannelId() { + return getChannel().getId(); + } + + /** + * Gets the ID of the user that executed the command. + * + * @return The ID of the user who executed this command. + */ + default long getUserId() { + return getUser().getId(); + } + + /** + * Gets the text channel as a server text channel. + * + * @return The text channel as a server text channel. + */ + default Optional getServerTextChannel() { + return getChannel().asServerTextChannel(); + } + /** + * Gets the command that was executed. + * + * @return The command that was executed. + */ + NexusCommand getCommand(); + + /** + * Gets the Discord API shard that was in charge of this event. + * + * @return The Discord API shard that was in charge of this event. + */ + default DiscordApi getApi() { + return getBaseEvent().getApi(); + } + + /** + * Gets the {@link NexusShardingManager} that is associated with the {@link Nexus} instance. + * + * @return The Nexus Shard Manager associated with this Nexus instance. + */ + default NexusShardingManager getShardManager() { + return Nexus.getShardingManager(); + } + + /** + * Gets the immediate response for this command. + * + * @return The immediate response builder associated with this command. + */ + default InteractionImmediateResponseBuilder respondNow() { + return getInteraction().createImmediateResponder(); + } + + /** + * A short-hand expression of sending a non-ephemeral response to Discord with the given content. This does not + * handle the exceptions, please handle the exceptions accordingly. + * + * @param content the content to send as a response. + * @return A future that contains the updater to update the response when needed. + */ + default CompletableFuture respondNowWith(String content) { + return respondNow().setContent(content).respond(); + } + + /** + * A short-hand expression of sending a non-ephemeral response to Discord with the given embeds. This does not + * handle the exceptions, please handle the exceptions accordingly. + * + * @param embeds the embeds to send as a response. + * @return A future that contains the updater to update the response when needed. + */ + default CompletableFuture respondNowWith(EmbedBuilder... embeds) { + return respondNow().addEmbeds(embeds).respond(); + } + + /** + * A short-hand expression of sending an ephemeral response to Discord with the given content. This does not + * handle the exceptions, please handle the exceptions accordingly. + * + * @param content the content to send as a response. + * @return A future that contains the updater to update the response when needed. + */ + default CompletableFuture respondNowEphemerallyWith(String content) { + return respondNowAsEphemeral().setContent(content).respond(); + } + + /** + * A short-hand expression of sending an ephemeral response to Discord with the given embeds. This does not + * handle the exceptions, please handle the exceptions accordingly. + * + * @param embeds the embeds to send as a response. + * @return A future that contains the updater to update the response when needed. + */ + default CompletableFuture respondNowEphemerallyWith(EmbedBuilder... embeds) { + return respondNowAsEphemeral().addEmbeds(embeds).respond(); + } + + /** + * Gets the immediate response builder for this command and adds the + * {@link MessageFlag#EPHEMERAL} flag ahead of time. + * + * @return The immediate response builder associated with this command with the + * ephemeral flag added. + */ + default InteractionImmediateResponseBuilder respondNowAsEphemeral() { + return respondNow().setFlags(MessageFlag.EPHEMERAL); + } + + /** + * Gets the {@link InteractionOriginalResponseUpdater} associated with this command. + * + * @return The {@link InteractionOriginalResponseUpdater}. + */ + CompletableFuture respondLater(); + + /** + * Gets the {@link InteractionOriginalResponseUpdater} associated with this command with the + * ephemeral flag attached. + * + * @return The {@link InteractionOriginalResponseUpdater} with the ephemeral flag attached. + */ + CompletableFuture respondLaterAsEphemeral(); + + /** + * Gets the {@link InteractionOriginalResponseUpdater} associated with this command with the ephemeral flag + * attached if the predicate is true. + * + * @param predicate The predicate to determine whether to use ephemeral response or not. + * @return The {@link InteractionOriginalResponseUpdater} for this interaction. + */ + default CompletableFuture respondLaterAsEphemeralIf(boolean predicate) { + if (predicate) { + return respondLaterAsEphemeral(); + } + + return respondLater(); + } + + /** + * Gets the {@link InteractionOriginalResponseUpdater} associated with this command with the ephemeral flag + * attached if the predicate is true. + * + * @param predicate The predicate to determine whether to use ephemeral response or not. + * @return The {@link InteractionOriginalResponseUpdater} for this interaction. + */ + default CompletableFuture respondLaterAsEphemeralIf(Predicate predicate) { + return respondLaterAsEphemeralIf(predicate.test(null)); + } + + /** + * Gets the event local store that is used to contain shared fields accessible from middlewares, command and + * afterwares themselves. You can use this to store data such as whether the command was completed successfully + * or other related. + * + * @return The event-local store from this event. + */ + Map store(); + + /** + * Gets the value of the given key from the {@link NexusCommandEvent#store()} and maps it into the type given + * if possible, otherwise returns null. + * + * @param key The key to get from the {@link NexusCommandEvent#store()}. + * @param type The type expected of the value. + * @param The type expected of the value. + * + * @return The value mapped with the key in {@link NexusCommandEvent#store()} mapped to the type, otherwise null. + */ + default T get(String key, Class type) { + if (!store().containsKey(key)) + return null; + + Object object = store().get(key); + + if (type.isAssignableFrom(object.getClass())) { + return type.cast(object); + } + + return null; + } + + /** + * A short-hand expression for placing a key-value pair to {@link NexusCommandEvent#store()}. + * + * @param key The key to insert to the store. + * @param value The value to insert to the store. + */ + default void store(String key, Object value) { + store().put(key, value); + } + + /** + * Activates one or more middlewares and executes the success consumer once all middlewares succeed without + * throwing an issue. This does not support {@link InteractionOriginalResponseUpdater} or related, it will use + * {@link InteractionImmediateResponseBuilder} instead. + * + * @param middlewares the middlewares to activate. + * @param success what to do when all the middlewares succeed. + */ + default void middlewares(List middlewares, Consumer success) { + NexusMiddlewareGateCore middlewareGate = NexusCommandInterceptorCore.execute(this, + NexusCommandInterceptorCore.middlewares(middlewares)); + + if (middlewareGate == null) { + success.accept(null); + return; + } + + NexusMessage response = middlewareGate.response(); + if (response != null) { + response.into(respondNow()).respond().exceptionally(ExceptionLogger.get()); + } + } + + /** + * Automatically answers either deferred or non-deferred based on circumstances, to configure the time that it should + * consider before deferring (this is based on time now - (interaction creation time - auto defer time)), you can + * modify {@link pw.mihou.nexus.configuration.modules.NexusGlobalConfiguration#autoDeferAfterMilliseconds}. + * + * @param ephemeral whether to respond ephemerally or not. + * @param response the response to send to Discord. + * @return the response from Discord. + */ + CompletableFuture autoDefer(boolean ephemeral, Function response); + +} diff --git a/src/main/java/pw/mihou/nexus/features/command/facade/NexusMiddlewareEvent.java b/src/main/java/pw/mihou/nexus/features/command/facade/NexusMiddlewareEvent.java index feca2661..ad6294eb 100644 --- a/src/main/java/pw/mihou/nexus/features/command/facade/NexusMiddlewareEvent.java +++ b/src/main/java/pw/mihou/nexus/features/command/facade/NexusMiddlewareEvent.java @@ -1,11 +1,10 @@ package pw.mihou.nexus.features.command.facade; -import pw.mihou.nexus.Nexus; -import pw.mihou.nexus.features.messages.facade.NexusMessage; + +import pw.mihou.nexus.features.messages.NexusMessage; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; public interface NexusMiddlewareEvent extends NexusCommandEvent { diff --git a/src/main/java/pw/mihou/nexus/features/command/interceptors/core/NexusMiddlewareGateCore.java b/src/main/java/pw/mihou/nexus/features/command/interceptors/core/NexusMiddlewareGateCore.java index 19771687..525244b7 100755 --- a/src/main/java/pw/mihou/nexus/features/command/interceptors/core/NexusMiddlewareGateCore.java +++ b/src/main/java/pw/mihou/nexus/features/command/interceptors/core/NexusMiddlewareGateCore.java @@ -1,7 +1,7 @@ package pw.mihou.nexus.features.command.interceptors.core; import pw.mihou.nexus.features.command.interceptors.facades.NexusMiddlewareGate; -import pw.mihou.nexus.features.messages.facade.NexusMessage; +import pw.mihou.nexus.features.messages.NexusMessage; import javax.annotation.Nullable; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddleware.java b/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddleware.java index 6c1ef46d..18e21836 100755 --- a/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddleware.java +++ b/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddleware.java @@ -1,7 +1,7 @@ package pw.mihou.nexus.features.command.interceptors.facades; import pw.mihou.nexus.features.command.facade.NexusMiddlewareEvent; -import pw.mihou.nexus.features.messages.facade.NexusMessage; +import pw.mihou.nexus.features.messages.NexusMessage; public interface NexusMiddleware extends NexusCommandInterceptor { diff --git a/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddlewareGate.java b/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddlewareGate.java index 801dceab..13a85c2f 100755 --- a/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddlewareGate.java +++ b/src/main/java/pw/mihou/nexus/features/command/interceptors/facades/NexusMiddlewareGate.java @@ -1,6 +1,6 @@ package pw.mihou.nexus.features.command.interceptors.facades; -import pw.mihou.nexus.features.messages.facade.NexusMessage; +import pw.mihou.nexus.features.messages.NexusMessage; import javax.annotation.Nullable; diff --git a/src/main/java/pw/mihou/nexus/features/command/responses/NexusAutoResponse.kt b/src/main/java/pw/mihou/nexus/features/command/responses/NexusAutoResponse.kt new file mode 100644 index 00000000..51815934 --- /dev/null +++ b/src/main/java/pw/mihou/nexus/features/command/responses/NexusAutoResponse.kt @@ -0,0 +1,14 @@ +package pw.mihou.nexus.features.command.responses + +import org.javacord.api.entity.message.Message +import org.javacord.api.interaction.callback.InteractionOriginalResponseUpdater +import java.util.concurrent.CompletableFuture + +data class NexusAutoResponse internal constructor( + val updater: InteractionOriginalResponseUpdater?, + val message: Message? +) { + fun getOrRequestMessage(): CompletableFuture = + if (message == null && updater != null) updater.update() + else CompletableFuture.completedFuture(message) +} diff --git a/src/main/java/pw/mihou/nexus/features/messages/NexusMessage.kt b/src/main/java/pw/mihou/nexus/features/messages/NexusMessage.kt new file mode 100644 index 00000000..5936db18 --- /dev/null +++ b/src/main/java/pw/mihou/nexus/features/messages/NexusMessage.kt @@ -0,0 +1,21 @@ +package pw.mihou.nexus.features.messages + +import org.javacord.api.interaction.callback.InteractionMessageBuilderBase + +class NexusMessage internal constructor( + val ephemeral: Boolean = false, + val builder: InteractionMessageBuilderBase<*>.() -> Unit = { } +) { + companion object { + fun with(ephemeral: Boolean, builder: InteractionMessageBuilderBase<*>.() -> Unit): NexusMessage { + return NexusMessage(ephemeral, builder) + } + fun with(builder: InteractionMessageBuilderBase<*>.() -> Unit): NexusMessage { + return with(false, builder) + } + } + fun > into(instance: T): T { + builder(instance) + return instance + } +} \ No newline at end of file diff --git a/src/main/java/pw/mihou/nexus/features/messages/core/NexusMessageCore.java b/src/main/java/pw/mihou/nexus/features/messages/core/NexusMessageCore.java deleted file mode 100755 index 4777c048..00000000 --- a/src/main/java/pw/mihou/nexus/features/messages/core/NexusMessageCore.java +++ /dev/null @@ -1,111 +0,0 @@ -package pw.mihou.nexus.features.messages.core; - -import org.javacord.api.entity.message.MessageFlag; -import org.javacord.api.entity.message.embed.EmbedBuilder; -import org.javacord.api.entity.message.mention.AllowedMentions; -import org.javacord.api.entity.message.mention.AllowedMentionsBuilder; -import org.javacord.api.interaction.callback.InteractionImmediateResponseBuilder; -import org.javacord.api.interaction.callback.InteractionMessageBuilderBase; -import pw.mihou.nexus.features.command.core.NexusCommandDispatcher; -import pw.mihou.nexus.features.messages.facade.NexusMessage; - -import java.util.Optional; -import java.util.function.Function; - -public class NexusMessageCore implements NexusMessage { - - private final String textVal; - private final EmbedBuilder embedVal; - private Function, InteractionMessageBuilderBase> builder = bob -> bob; - boolean ephemeral = false; - private static final AllowedMentions ALLOWED_MENTIONS = new AllowedMentionsBuilder() - .setMentionRoles(false) - .setMentionUsers(false) - .setMentionEveryoneAndHere(false) - .build(); - - private NexusMessageCore(String textVal, EmbedBuilder embedVal) { - this.textVal = textVal; - this.embedVal = embedVal; - } - - /** - * Creates a new {@link NexusMessageCore} instance that allows for a text - * response instead of an embed response. - * - * @param textValue The text value to send to the user. - */ - public NexusMessageCore(String textValue) { - this(textValue, null); - } - - /** - * Creates a new {@link NexusMessageCore} instance that allows for an embed - * response instead of a text response. - * - * @param embedValue The embed to send to the user. - */ - public NexusMessageCore(EmbedBuilder embedValue) { - this(null, embedValue); - } - - /** - * An internal method that is utilized by the likes of {@link NexusCommandDispatcher} to - * transform the {@link NexusMessage} into an {@link InteractionImmediateResponseBuilder} that can then be used to send the response - * to the end-user. - * - * @param instance The {@link InteractionImmediateResponseBuilder} provided by Javacord. - * @return The {@link InteractionImmediateResponseBuilder} with all the proper configuration. - */ - public InteractionImmediateResponseBuilder convertTo(InteractionImmediateResponseBuilder instance) { - InteractionImmediateResponseBuilder build = (InteractionImmediateResponseBuilder) builder.apply(instance); - - if (ephemeral) { - build.setFlags(MessageFlag.EPHEMERAL); - } - - asString().ifPresent(build::setContent); - asEmbed().ifPresent(build::addEmbed); - - build.setAllowedMentions(ALLOWED_MENTIONS); - return build; - } - - /** - * An internal method that is utilized by the likes of {@link NexusCommandDispatcher} to - * transform the {@link NexusMessage} into an {@link InteractionMessageBuilderBase} that can then be used to send the response - * to the end-user. - * - * @param instance The {@link InteractionImmediateResponseBuilder} provided by Javacord. - * @return The {@link InteractionMessageBuilderBase} with all the proper configuration. - */ - public InteractionMessageBuilderBase convertTo(InteractionMessageBuilderBase instance) { - asString().ifPresent(instance::setContent); - asEmbed().ifPresent(instance::addEmbed); - - instance.setAllowedMentions(ALLOWED_MENTIONS); - return instance; - } - - @Override - public NexusMessage setBuilder(Function, InteractionMessageBuilderBase> builder) { - this.builder = builder; - return this; - } - - @Override - public NexusMessage setEphemeral(boolean ephemeral) { - this.ephemeral = true; - return this; - } - - @Override - public Optional asString() { - return Optional.ofNullable(textVal); - } - - @Override - public Optional asEmbed() { - return Optional.ofNullable(embedVal); - } -} diff --git a/src/main/java/pw/mihou/nexus/features/messages/facade/NexusMessage.java b/src/main/java/pw/mihou/nexus/features/messages/facade/NexusMessage.java deleted file mode 100755 index 5ed188cd..00000000 --- a/src/main/java/pw/mihou/nexus/features/messages/facade/NexusMessage.java +++ /dev/null @@ -1,146 +0,0 @@ -package pw.mihou.nexus.features.messages.facade; - -import org.javacord.api.entity.message.MessageFlag; -import org.javacord.api.entity.message.embed.EmbedBuilder; -import org.javacord.api.interaction.callback.InteractionImmediateResponseBuilder; -import org.javacord.api.interaction.callback.InteractionMessageBuilderBase; -import pw.mihou.nexus.features.messages.core.NexusMessageCore; - -import java.util.Optional; -import java.util.function.Function; - -public interface NexusMessage { - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link EmbedBuilder} specified. - * - * @param builder The {@link EmbedBuilder} to send to the end-user. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage from(EmbedBuilder builder) { - return new NexusMessageCore(builder); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link EmbedBuilder} specified with ephemereal flag. - * - * @param builder The {@link EmbedBuilder} to send to the end-user. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage fromEphemeral(EmbedBuilder builder) { - return new NexusMessageCore(builder).setEphemeral(true); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link EmbedBuilder} specified. - * - * @param builder The {@link EmbedBuilder} to send to the end-user. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage from(Function builder) { - return new NexusMessageCore(builder.apply(new EmbedBuilder())); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link EmbedBuilder} specified with ephemereal flag. - * - * @param builder The {@link EmbedBuilder} to send to the end-user. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage fromEphemereal(Function builder) { - return fromEphemeral(builder.apply(new EmbedBuilder())); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link String} specified. - * - * @param text The {@link String} to send to the end-user. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage from(String text) { - return new NexusMessageCore(text); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link String} specified with ephemereal flag. - * - * @param text The {@link String} to send to the end-user. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage fromEphemereal(String text) { - return new NexusMessageCore(text).setEphemeral(true); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link EmbedBuilder} specified with a customized - * {@link InteractionMessageBuilderBase} configuration. - * - * @param builder The {@link EmbedBuilder} to send to the end-user. - * @param builderBaseFunction The {@link InteractionMessageBuilderBase} configuration to - * utilize when sending the message. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage fromWith(EmbedBuilder builder, - Function, InteractionMessageBuilderBase> - builderBaseFunction) { - return from(builder).setBuilder(builderBaseFunction); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link EmbedBuilder} specified with a customized - * {@link InteractionMessageBuilderBase} configuration. - * - * @param builder The {@link EmbedBuilder} to send to the end-user. - * @param builderBaseFunction The {@link InteractionMessageBuilderBase} configuration to - * utilize when sending the message. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage fromWith(Function builder, - Function, InteractionMessageBuilderBase> - builderBaseFunction) { - return from(builder).setBuilder(builderBaseFunction); - } - - /** - * Creates a new {@link NexusMessage} that utilizes the {@link String} specified with a customized - * {@link InteractionMessageBuilderBase} configuration. - * - * @param text The {@link String} to send to the end-user. - * @param builderBaseFunction The {@link InteractionMessageBuilderBase} configuration to - * utilize when sending the message. - * @return The new {@link NexusMessage} instance. - */ - static NexusMessage fromWith(String text, - Function, InteractionMessageBuilderBase> - interactionImmediateResponseBuilderFunction) { - return from(text).setBuilder(interactionImmediateResponseBuilderFunction); - } - - /** - * Modifies the {@link InteractionImmediateResponseBuilder} to suit the needs of the developer. You can customize - * this to your liking, although any text content or embed will be overriden at the end. - * - * @param builder The builder that is being utilized. - * @return {@link NexusMessageCore} for chain-calling methods. - */ - NexusMessage setBuilder(Function, InteractionMessageBuilderBase> builder); - - NexusMessage setEphemeral(boolean ephemeral); - - /** - * Retrieves the {@link String} version of this {@link NexusMessage} instance. - * This will return empty if it isn't configured to send a text message but an embed instead. - * - * @return The {@link String} value of this message. - */ - Optional asString(); - - /** - * Retrieves the {@link EmbedBuilder} version of this {@link NexusMessage} instance. - * This will erturn empty if it isn't configured to send an embed but a text message instead. - * - * @return The {@link EmbedBuilder} value of this message. - */ - Optional asEmbed(); - -}