Skip to content

Commit

Permalink
Update Core representation
Browse files Browse the repository at this point in the history
  • Loading branch information
DRSchlaubi committed Oct 28, 2021
1 parent 3e9bf6a commit 0585983
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 36 deletions.
27 changes: 15 additions & 12 deletions common/src/main/kotlin/entity/Interactions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ sealed class Option {
var jsonValue: JsonElement? = null
var jsonOptions: JsonArray? = null
var type: ApplicationCommandOptionType? = null
var focused: OptionalBoolean = OptionalBoolean.Missing
decoder.decodeStructure(descriptor) {
while (true) {
when (val index = decodeElementIndex(descriptor)) {
Expand All @@ -352,7 +353,8 @@ sealed class Option {
2 -> jsonOptions = decodeSerializableElement(descriptor, index, JsonArray.serializer())
3 -> type =
decodeSerializableElement(descriptor, index, ApplicationCommandOptionType.serializer())

4 -> focused =
decodeSerializableElement(descriptor, index, OptionalBoolean.serializer())
CompositeDecoder.DECODE_DONE -> return@decodeStructure
else -> throw SerializationException("unknown index: $index")
}
Expand Down Expand Up @@ -387,7 +389,7 @@ sealed class Option {
ApplicationCommandOptionType.Role,
ApplicationCommandOptionType.String,
ApplicationCommandOptionType.User -> CommandArgument.Serializer.deserialize(
json, jsonValue!!, name, type!!
json, jsonValue!!, name, type!!, focused
)
else -> error("unknown ApplicationCommandOptionType $type")
}
Expand Down Expand Up @@ -579,32 +581,33 @@ sealed class CommandArgument<out T> : Option() {
json: Json,
element: JsonElement,
name: String,
type: ApplicationCommandOptionType
type: ApplicationCommandOptionType,
focused: OptionalBoolean
): CommandArgument<*> = when (type) {
ApplicationCommandOptionType.Boolean -> BooleanArgument(
name, json.decodeFromJsonElement(Boolean.serializer(), element)
name, json.decodeFromJsonElement(Boolean.serializer(), element), focused
)
ApplicationCommandOptionType.String -> StringArgument(
name, json.decodeFromJsonElement(String.serializer(), element)
name, json.decodeFromJsonElement(String.serializer(), element), focused
)
ApplicationCommandOptionType.Integer -> IntegerArgument(
name, json.decodeFromJsonElement(Long.serializer(), element)
name, json.decodeFromJsonElement(Long.serializer(), element), focused
)

ApplicationCommandOptionType.Number -> NumberArgument(
name, json.decodeFromJsonElement(Double.serializer(), element)
name, json.decodeFromJsonElement(Double.serializer(), element), focused
)
ApplicationCommandOptionType.Channel -> ChannelArgument(
name, json.decodeFromJsonElement(Snowflake.serializer(), element)
name, json.decodeFromJsonElement(Snowflake.serializer(), element), focused
)
ApplicationCommandOptionType.Mentionable -> MentionableArgument(
name, json.decodeFromJsonElement(Snowflake.serializer(), element)
name, json.decodeFromJsonElement(Snowflake.serializer(), element), focused
)
ApplicationCommandOptionType.Role -> RoleArgument(
name, json.decodeFromJsonElement(Snowflake.serializer(), element)
name, json.decodeFromJsonElement(Snowflake.serializer(), element), focused
)
ApplicationCommandOptionType.User -> UserArgument(
name, json.decodeFromJsonElement(Snowflake.serializer(), element)
name, json.decodeFromJsonElement(Snowflake.serializer(), element), focused
)
ApplicationCommandOptionType.SubCommand,
ApplicationCommandOptionType.SubCommandGroup,
Expand Down Expand Up @@ -635,7 +638,7 @@ sealed class CommandArgument<out T> : Option() {

requireNotNull(element)
requireNotNull(type)
return deserialize(json, element, name, type)
return deserialize(json, element, name, type, OptionalBoolean.Missing)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package dev.kord.core.behavior.interaction

import dev.kord.common.entity.Choice
import dev.kord.common.entity.DiscordAutoComplete
import dev.kord.rest.builder.interaction.IntChoiceBuilder
import dev.kord.rest.builder.interaction.NumberChoiceBuilder
import dev.kord.rest.builder.interaction.StringChoiceBuilder
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

/**
* Behavior of an AutoComplete interaction.
*
* @see respondNumber
* @see respondString
* @see respondInt
* @see respond
*/
public interface AutoCompleteInteractionBehavior : InteractionBehavior

/**
* Responds with the int choices specified by [builder].
*
* @see IntChoiceBuilder
*/
@OptIn(ExperimentalContracts::class)
public suspend inline fun AutoCompleteInteractionBehavior.respondInt(builder: IntChoiceBuilder.() -> Unit) {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}

kord.rest.interaction.createIntAutoCompleteInteractionResponse(id, token, builder)
}

/**
* Responds with the number choices specified by [builder].
*
* @see NumberChoiceBuilder
*/
@OptIn(ExperimentalContracts::class)
public suspend inline fun AutoCompleteInteractionBehavior.respondNumber(builder: NumberChoiceBuilder.() -> Unit) {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}

kord.rest.interaction.createNumberAutoCompleteInteractionResponse(id, token, builder)
}

/**
* Responds with the string choices specified by [builder].
*
* @see StringChoiceBuilder
*/
@OptIn(ExperimentalContracts::class)
public suspend inline fun AutoCompleteInteractionBehavior.respondString(builder: StringChoiceBuilder.() -> Unit) {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}

kord.rest.interaction.createStringAutoCompleteInteractionResponse(id, token, builder)
}

/**
* Responds with [choices] to this auto-complete request.
*/
public suspend inline fun <reified T> AutoCompleteInteractionBehavior.respond(choices: List<Choice<T>>) {
kord.rest.interaction.createAutoCompleteInteractionResponse(
id,
token,
DiscordAutoComplete(choices)
)
}
20 changes: 19 additions & 1 deletion core/src/main/kotlin/entity/interaction/ContextInteraction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import dev.kord.common.entity.optional.unwrap
import dev.kord.core.Kord
import dev.kord.core.behavior.UserBehavior
import dev.kord.core.behavior.interaction.ApplicationCommandInteractionBehavior
import dev.kord.core.behavior.interaction.AutoCompleteInteractionBehavior
import dev.kord.core.cache.data.InteractionData
import dev.kord.core.entity.Message
import dev.kord.core.entity.User
import dev.kord.core.supplier.EntitySupplier
import dev.kord.core.supplier.EntitySupplyStrategy
import java.util.*
import java.util.Objects

/**
* Represents an interaction of type [ApplicationCommand][dev.kord.common.entity.InteractionType.ApplicationCommand]
Expand Down Expand Up @@ -204,3 +205,20 @@ public class UnknownApplicationCommandInteraction(
return UnknownApplicationCommandInteraction(data, kord, strategy.supply(kord))
}
}

/**
* Interaction indicating an auto-complete request from Discord.
*
* **Follow-ups and normals responses don't work on this type**
*
* Check [AutoCompleteInteractionBehavior] for response options
*/
public class AutoCompleteInteraction(
override val data: InteractionData,
override val user: UserBehavior,
override val kord: Kord,
override val supplier: EntitySupplier = kord.defaultSupplier
) : AutoCompleteInteractionBehavior, ChatInputCommandInteraction {
override fun withStrategy(strategy: EntitySupplyStrategy<*>): Interaction =
AutoCompleteInteraction(data, user, kord, strategy.supply(kord))
}
4 changes: 4 additions & 0 deletions core/src/main/kotlin/entity/interaction/Interaction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public sealed interface Interaction : InteractionBehavior {
): Interaction {
return when {
data.type == InteractionType.Component -> ComponentInteraction(data, kord, strategy.supply(kord))
data.type == InteractionType.AutoComplete -> {
val user = User(data.user.value!!, kord, strategy.supply(kord))
AutoCompleteInteraction(data, user, kord, strategy.supply(kord))
}
data.guildId !is OptionalSnowflake.Missing -> GuildApplicationCommandInteraction(
data,
kord,
Expand Down
22 changes: 17 additions & 5 deletions core/src/main/kotlin/event/interaction/ApplicationCreate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import dev.kord.core.entity.application.*
import dev.kord.core.entity.interaction.*
import dev.kord.core.event.kordCoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlin.coroutines.CoroutineContext

/**
* This event fires when an interaction is created.
Expand Down Expand Up @@ -46,7 +45,7 @@ public sealed interface GuildApplicationInteractionCreateEvent : ApplicationInte
override val interaction: GuildApplicationCommandInteraction
}

public sealed interface UserCommandInteractionCreateEvent : ApplicationInteractionCreateEvent {
public sealed interface UserCommandInteractionCreateEvent : ApplicationInteractionCreateEvent {
override val interaction: UserCommandInteraction
}

Expand All @@ -65,7 +64,7 @@ public class GlobalUserCommandInteractionCreateEvent(
) : GlobalApplicationInteractionCreateEvent, UserCommandInteractionCreateEvent, CoroutineScope by coroutineScope


public sealed interface MessageCommandInteractionCreateEvent : ApplicationInteractionCreateEvent {
public sealed interface MessageCommandInteractionCreateEvent : ApplicationInteractionCreateEvent {
override val interaction: MessageCommandInteraction
}

Expand All @@ -84,8 +83,7 @@ public class GlobalMessageCommandInteractionCreateEvent(
) : GlobalApplicationInteractionCreateEvent, MessageCommandInteractionCreateEvent, CoroutineScope by coroutineScope



public sealed interface ChatInputCommandInteractionCreateEvent : ApplicationInteractionCreateEvent {
public sealed interface ChatInputCommandInteractionCreateEvent : ApplicationInteractionCreateEvent {
override val interaction: ChatInputCommandInteraction
}

Expand All @@ -102,3 +100,17 @@ public class GlobalChatInputCommandInteractionCreateEvent(
override val shard: Int,
public val coroutineScope: CoroutineScope = kordCoroutineScope(kord)
) : GlobalApplicationInteractionCreateEvent, ChatInputCommandInteractionCreateEvent, CoroutineScope by coroutineScope

/**
* Interaction received when a users types into an auto-completed option.
*
* Check [AutoCompleteInteractionBehavior] on how to reply.
*
* @see AutoCompleteInteraction
*/
public class AutoCompleteInteractionCreateEvent(
override val interaction: AutoCompleteInteraction,
override val kord: Kord,
override val shard: Int,
public val coroutineScope: CoroutineScope = kordCoroutineScope(kord)
) : InteractionCreateEvent, CoroutineScope by coroutineScope
47 changes: 43 additions & 4 deletions core/src/main/kotlin/gateway/handler/InteractionEventHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,48 @@ import dev.kord.core.Kord
import dev.kord.core.cache.data.ApplicationCommandData
import dev.kord.core.cache.data.InteractionData
import dev.kord.core.cache.idEq
import dev.kord.core.entity.application.*
import dev.kord.core.entity.interaction.*
import dev.kord.core.event.interaction.*
import dev.kord.core.entity.application.GuildApplicationCommand
import dev.kord.core.entity.application.GuildChatInputCommand
import dev.kord.core.entity.application.GuildMessageCommand
import dev.kord.core.entity.application.GuildUserCommand
import dev.kord.core.entity.application.UnknownGuildApplicationCommand
import dev.kord.core.entity.interaction.AutoCompleteInteraction
import dev.kord.core.entity.interaction.GlobalButtonInteraction
import dev.kord.core.entity.interaction.GlobalChatInputCommandInteraction
import dev.kord.core.entity.interaction.GlobalMessageCommandInteraction
import dev.kord.core.entity.interaction.GlobalSelectMenuInteraction
import dev.kord.core.entity.interaction.GlobalUserCommandInteraction
import dev.kord.core.entity.interaction.GuildButtonInteraction
import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction
import dev.kord.core.entity.interaction.GuildMessageCommandInteraction
import dev.kord.core.entity.interaction.GuildSelectMenuInteraction
import dev.kord.core.entity.interaction.GuildUserCommandInteraction
import dev.kord.core.entity.interaction.Interaction
import dev.kord.core.entity.interaction.UnknownApplicationCommandInteraction
import dev.kord.core.entity.interaction.UnknownComponentInteraction
import dev.kord.core.event.interaction.AutoCompleteInteractionCreateEvent
import dev.kord.core.event.interaction.ChatInputCommandCreateEvent
import dev.kord.core.event.interaction.ChatInputCommandDeleteEvent
import dev.kord.core.event.interaction.ChatInputCommandUpdateEvent
import dev.kord.core.event.interaction.GlobalButtonInteractionCreateEvent
import dev.kord.core.event.interaction.GlobalChatInputCommandInteractionCreateEvent
import dev.kord.core.event.interaction.GlobalMessageCommandInteractionCreateEvent
import dev.kord.core.event.interaction.GlobalSelectMenuInteractionCreateEvent
import dev.kord.core.event.interaction.GlobalUserCommandInteractionCreateEvent
import dev.kord.core.event.interaction.GuildButtonInteractionCreateEvent
import dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent
import dev.kord.core.event.interaction.GuildMessageCommandInteractionCreateEvent
import dev.kord.core.event.interaction.GuildSelectMenuInteractionCreateEvent
import dev.kord.core.event.interaction.GuildUserCommandInteractionCreateEvent
import dev.kord.core.event.interaction.MessageCommandCreateEvent
import dev.kord.core.event.interaction.MessageCommandDeleteEvent
import dev.kord.core.event.interaction.MessageCommandUpdateEvent
import dev.kord.core.event.interaction.UnknownApplicationCommandCreateEvent
import dev.kord.core.event.interaction.UnknownApplicationCommandDeleteEvent
import dev.kord.core.event.interaction.UnknownApplicationCommandUpdateEvent
import dev.kord.core.event.interaction.UserCommandCreateEvent
import dev.kord.core.event.interaction.UserCommandDeleteEvent
import dev.kord.core.event.interaction.UserCommandUpdateEvent
import dev.kord.gateway.*
import kotlinx.coroutines.CoroutineScope
import dev.kord.core.event.Event as CoreEvent
Expand All @@ -32,6 +71,7 @@ public class InteractionEventHandler(
val data = InteractionData.from(event.interaction)
val interaction = Interaction.from(data, kord)
val coreEvent = when(interaction) {
is AutoCompleteInteraction -> AutoCompleteInteractionCreateEvent(interaction, kord, shard, coroutineScope)
is GlobalChatInputCommandInteraction -> GlobalChatInputCommandInteractionCreateEvent(interaction, kord, shard, coroutineScope)
is GlobalUserCommandInteraction -> GlobalUserCommandInteractionCreateEvent(interaction, kord, shard, coroutineScope)
is GlobalMessageCommandInteraction -> GlobalMessageCommandInteractionCreateEvent(interaction, kord, shard, coroutineScope)
Expand Down Expand Up @@ -74,7 +114,6 @@ public class InteractionEventHandler(
): CoreEvent {
val data = ApplicationCommandData.from(event.application)
cache.put(data)
val application = GuildApplicationCommand(data, kord.rest.interaction)

val coreEvent = when (val application = GuildApplicationCommand(data, kord.rest.interaction)) {
is GuildChatInputCommand -> ChatInputCommandUpdateEvent(application, kord, shard, coroutineScope)
Expand Down
25 changes: 19 additions & 6 deletions rest/src/main/kotlin/builder/interaction/OptionsBuilder.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package dev.kord.rest.builder.interaction

import dev.kord.common.annotation.KordDsl
import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.ApplicationCommandOption
import dev.kord.common.entity.ApplicationCommandOptionType
import dev.kord.common.entity.Choice
Expand All @@ -27,12 +26,26 @@ sealed class OptionsBuilder(
internal var _required: OptionalBoolean = OptionalBoolean.Missing
var required: Boolean? by ::_required.delegate()

internal var _autocomplete: OptionalBoolean = OptionalBoolean.Missing

/**
* Setting this to `true` allows you to dynamically respond with your choices, depending on the user input.
*
* **Warning:** This disables all input validation, users can submit values before responding to the AutoComplete request
*
* **Note:** If you set this to `true` you can't add any other choice
*
* Core users can check the `AutoCompleteInteractionCreateEvent` and `AutoCompleteInteraction` classes.
*/
var autocomplete: Boolean? by ::_autocomplete.delegate()

override fun toRequest() = ApplicationCommandOption(
type,
name,
description,
_default,
_required
_required,
autocomplete = _autocomplete
)
}

Expand All @@ -41,8 +54,7 @@ sealed class BaseChoiceBuilder<T>(
name: String,
description: String,
type: ApplicationCommandOptionType
) :
OptionsBuilder(name, description, type) {
) : OptionsBuilder(name, description, type) {
private var _choices: Optional<MutableList<Choice<*>>> = Optional.Missing()
var choices: MutableList<Choice<*>>? by ::_choices.delegate()

Expand All @@ -54,7 +66,8 @@ sealed class BaseChoiceBuilder<T>(
description,
choices = _choices,
required = _required,
default = _default
default = _default,
autocomplete = _autocomplete
)
}

Expand Down Expand Up @@ -150,4 +163,4 @@ class GroupCommandBuilder(name: String, description: String) :
if (options == null) options = mutableListOf()
options!!.add(SubCommandBuilder(name, description).apply(builder))
}
}
}
Loading

0 comments on commit 0585983

Please sign in to comment.