diff --git a/.idea/misc.xml b/.idea/misc.xml
index a7dd9f9b93..0de8ea0f30 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -4,7 +4,7 @@
-
+
diff --git a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/ApplicationCommandRegistry.kt b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/ApplicationCommandRegistry.kt
index d8169da064..577ed38e03 100644
--- a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/ApplicationCommandRegistry.kt
+++ b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/ApplicationCommandRegistry.kt
@@ -132,6 +132,8 @@ public abstract class ApplicationCommandRegistry : KordExKoinComponent {
*/
public abstract suspend fun register(command: UserCommand<*, *>): UserCommand<*, *>?
+ public abstract suspend fun register(command: PrimaryEntryPointCommand): Snowflake
+
/** Event handler for slash commands. **/
public abstract suspend fun handle(event: ChatInputCommandInteractionCreateEvent)
@@ -236,6 +238,7 @@ public abstract class ApplicationCommandRegistry : KordExKoinComponent {
is SlashCommand<*, *, *> -> createDiscordSlashCommand(command)
is UserCommand<*, *> -> createDiscordUserCommand(command)
is MessageCommand<*, *> -> createDiscordMessageCommand(command)
+ is PrimaryEntryPointCommand -> createDiscordEntryPointCommand(command)
else -> throw IllegalArgumentException("Unknown ApplicationCommand type")
}
@@ -356,9 +359,50 @@ public abstract class ApplicationCommandRegistry : KordExKoinComponent {
return response.id
}
+ public open suspend fun createDiscordEntryPointCommand(command: PrimaryEntryPointCommand): Snowflake {
+ val locale = bot.settings.i18nBuilder.defaultLocale
+
+ val (name, nameLocalizations) = command.localizedName
+ val (description, descriptionLocalizations) = command.localizedDescription
+
+ val guild = if (command.guildId != null) {
+ kord.getGuildOrNull(command.guildId!!)
+ } else {
+ null
+ }
+
+ val response = if (guild == null) {
+ kord.createGlobalEntryPointCommand(name, description, command.handler) {
+ this.nameLocalizations = nameLocalizations
+ this.descriptionLocalizations = descriptionLocalizations
+
+ this.register(locale, command)
+ }
+ } else {
+ kord.createGuildEntryPointCommand(guild.id, name, description, command.handler) {
+ this.nameLocalizations = nameLocalizations
+ this.descriptionLocalizations = descriptionLocalizations
+
+ this.register(locale, command)
+ }
+ }
+
+ return response.id
+ }
+
// endregion
// region: Extensions
+
+ /** Registration logic for entry points, extracted for clarity. **/
+ public open suspend fun EntryPointCreateBuilder.register(locale: Locale, command: PrimaryEntryPointCommand) {
+ if (this is GlobalEntryPointCreateBuilder) {
+ registerGlobalPermissions(locale, command)
+ } else {
+ registerGuildPermissions(locale, command)
+ }
+ }
+
/** Registration logic for slash commands, extracted for clarity. **/
public open suspend fun ChatInputCreateBuilder.register(locale: Locale, command: SlashCommand<*, *, *>) {
if (this is GlobalChatInputCreateBuilder) {
diff --git a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/DefaultApplicationCommandRegistry.kt b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/DefaultApplicationCommandRegistry.kt
index 884ee83d8b..9e75900104 100644
--- a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/DefaultApplicationCommandRegistry.kt
+++ b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/DefaultApplicationCommandRegistry.kt
@@ -25,6 +25,7 @@ import dev.kord.core.event.interaction.ChatInputCommandInteractionCreateEvent
import dev.kord.core.event.interaction.MessageCommandInteractionCreateEvent
import dev.kord.core.event.interaction.UserCommandInteractionCreateEvent
import dev.kord.rest.builder.interaction.MultiApplicationCommandBuilder
+import dev.kord.rest.builder.interaction.entryPoint
import dev.kord.rest.builder.interaction.input
import dev.kord.rest.builder.interaction.message
import dev.kord.rest.builder.interaction.user
@@ -209,6 +210,17 @@ public open class DefaultApplicationCommandRegistry : ApplicationCommandRegistry
this.register(locale, it)
}
}
+
+ is PrimaryEntryPointCommand -> {
+ val (description, descriptionLocalizations) = it.localizedDescription
+
+ entryPoint(name, description, it.handler) {
+ this.nameLocalizations = nameLocalizations
+ this.descriptionLocalizations = descriptionLocalizations
+
+ this.register(locale, it)
+ }
+ }
}
}
}
@@ -290,6 +302,8 @@ public open class DefaultApplicationCommandRegistry : ApplicationCommandRegistry
return command
}
+ override suspend fun register(command: PrimaryEntryPointCommand): Snowflake = createDiscordEntryPointCommand(command)
+
// endregion
// region: Unregistration functions
diff --git a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/PrimaryEntryPointCommand.kt b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/PrimaryEntryPointCommand.kt
new file mode 100644
index 0000000000..17287c740b
--- /dev/null
+++ b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/PrimaryEntryPointCommand.kt
@@ -0,0 +1,32 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package com.kotlindiscord.kord.extensions.commands.application
+
+import com.kotlindiscord.kord.extensions.extensions.Extension
+import com.kotlindiscord.kord.extensions.utils.MutableStringKeyedMap
+import dev.kord.common.entity.ApplicationCommandType
+import dev.kord.common.entity.EntryPointCommandHandlerType
+
+public open class PrimaryEntryPointCommand(extension: Extension) : ApplicationCommand(extension) {
+
+ public lateinit var handler: EntryPointCommandHandlerType
+
+ /** Command description, as displayed on Discord. **/
+ public open lateinit var description: String
+
+ /**
+ * A [Localized] version of [description].
+ */
+ public val localizedDescription: Localized by lazy { localize(description) }
+
+ override val type: ApplicationCommandType = ApplicationCommandType.PrimaryEntryPoint
+
+ override suspend fun call(
+ event: Nothing,
+ cache: MutableStringKeyedMap,
+ ): Nothing = error("Primary entry point commands can't be called")
+}
diff --git a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/StorageAwareApplicationCommandRegistry.kt b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/StorageAwareApplicationCommandRegistry.kt
index 9c2f3517a6..ea01a94bea 100644
--- a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/StorageAwareApplicationCommandRegistry.kt
+++ b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/commands/application/StorageAwareApplicationCommandRegistry.kt
@@ -65,6 +65,8 @@ public open class StorageAwareApplicationCommandRegistry(
return command
}
+ override suspend fun register(command: PrimaryEntryPointCommand): Snowflake = createDiscordEntryPointCommand(command)
+
override suspend fun register(command: UserCommand<*, *>): UserCommand<*, *>? {
val commandId = createDiscordCommand(command) ?: return null
diff --git a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/Extension.kt b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/Extension.kt
index 271d9ecde0..7680d3a787 100644
--- a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/Extension.kt
+++ b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/Extension.kt
@@ -14,6 +14,7 @@ import com.kotlindiscord.kord.extensions.checks.types.UserCommandCheck
import com.kotlindiscord.kord.extensions.commands.Arguments
import com.kotlindiscord.kord.extensions.commands.application.ApplicationCommand
import com.kotlindiscord.kord.extensions.commands.application.ApplicationCommandRegistry
+import com.kotlindiscord.kord.extensions.commands.application.PrimaryEntryPointCommand
import com.kotlindiscord.kord.extensions.commands.application.message.MessageCommand
import com.kotlindiscord.kord.extensions.commands.application.slash.SlashCommand
import com.kotlindiscord.kord.extensions.commands.application.user.UserCommand
@@ -107,6 +108,8 @@ public abstract class Extension : KordExKoinComponent {
*/
public open val userCommands: MutableList> = mutableListOf()
+ public open val entryPointCommands: MutableList = mutableListOf()
+
/**
* List of chat command checks.
*
diff --git a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/_Commands.kt b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/_Commands.kt
index d4fbfd2552..fef43d3b58 100644
--- a/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/_Commands.kt
+++ b/kord-extensions/src/main/kotlin/com/kotlindiscord/kord/extensions/extensions/_Commands.kt
@@ -16,6 +16,7 @@ import com.kotlindiscord.kord.extensions.checks.types.MessageCommandCheck
import com.kotlindiscord.kord.extensions.checks.types.SlashCommandCheck
import com.kotlindiscord.kord.extensions.checks.types.UserCommandCheck
import com.kotlindiscord.kord.extensions.commands.Arguments
+import com.kotlindiscord.kord.extensions.commands.application.PrimaryEntryPointCommand
import com.kotlindiscord.kord.extensions.commands.application.message.EphemeralMessageCommand
import com.kotlindiscord.kord.extensions.commands.application.message.PublicMessageCommand
import com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommand
@@ -478,6 +479,17 @@ public suspend fun Extension.ephemeralUserCommand(
return ephemeralUserCommand(commandObj)
}
+/** Register an ephemeral entry point, DSL-style. **/
+@ExtensionDSL
+public suspend fun Extension.primaryEntryPointCommand(
+ body: suspend PrimaryEntryPointCommand.() -> Unit,
+): PrimaryEntryPointCommand {
+ val commandObj = PrimaryEntryPointCommand(this)
+ body(commandObj)
+
+ return primaryEntryPointCommand(commandObj)
+}
+
/** Register an ephemeral user command, DSL-style. **/
@ExtensionDSL
public suspend fun Extension.ephemeralUserCommand(
@@ -511,6 +523,27 @@ public suspend fun Extension.ephemeralUserCommand(
return commandObj
}
+/** Register a custom instance of an ephemeral user command. **/
+@ExtensionDSL
+public suspend fun Extension.primaryEntryPointCommand(
+ commandObj: PrimaryEntryPointCommand,
+): PrimaryEntryPointCommand {
+ try {
+ commandObj.validate()
+ entryPointCommands.add(commandObj)
+ } catch (e: CommandRegistrationException) {
+ logger.error(e) { "Failed to register message command ${commandObj.name} - $e" }
+ } catch (e: InvalidCommandException) {
+ logger.error(e) { "Failed to register message command ${commandObj.name} - $e" }
+ }
+
+ if (applicationCommandRegistry.initialised) {
+ applicationCommandRegistry.register(commandObj)
+ }
+
+ return commandObj
+}
+
/** Register a public user command, DSL-style. **/
@ExtensionDSL
public suspend fun Extension.publicUserCommand(