Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AutoComplete #435

Merged
merged 14 commits into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 74 additions & 24 deletions common/src/main/kotlin/entity/Interactions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,34 @@ import dev.kord.common.annotation.KordExperimental
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.OptionalBoolean
import dev.kord.common.entity.optional.OptionalSnowflake
import kotlinx.serialization.*
import kotlinx.serialization.Contextual
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.encoding.encodeStructure
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.double
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import kotlinx.serialization.json.longOrNull
import mu.KotlinLogging

val kordLogger = KotlinLogging.logger { }
Expand All @@ -21,6 +43,9 @@ data class DiscordApplicationCommand(
@SerialName("application_id")
val applicationId: Snowflake,
val name: String,
/**
* Don't trust the docs: This is nullable on non chat input commands.
*/
val description: String?,
@SerialName("guild_id")
val guildId: OptionalSnowflake = OptionalSnowflake.Missing,
Expand Down Expand Up @@ -64,6 +89,7 @@ class ApplicationCommandOption(
val required: OptionalBoolean = OptionalBoolean.Missing,
@OptIn(KordExperimental::class)
val choices: Optional<List<Choice<@Serializable(NotSerializable::class) Any?>>> = Optional.Missing(),
val autocomplete: OptionalBoolean = OptionalBoolean.Missing,
val options: Optional<List<ApplicationCommandOption>> = Optional.Missing(),
)

Expand Down Expand Up @@ -247,12 +273,15 @@ sealed class InteractionType(val type: Int) {
* this type exists and is needed for components even though it's not documented
*/
object Component : InteractionType(3)

object AutoComplete : InteractionType(4)
class Unknown(type: Int) : InteractionType(type)

override fun toString(): String = when (this) {
Ping -> "InteractionType.Ping($type)"
ApplicationCommand -> "InteractionType.ApplicationCommand($type)"
Component -> "InteractionType.ComponentInvoke($type)"
AutoComplete -> "InteractionType.AutoComplete($type)"
is Unknown -> "InteractionType.Unknown($type)"
}

Expand All @@ -267,6 +296,7 @@ sealed class InteractionType(val type: Int) {
1 -> Ping
2 -> ApplicationCommand
3 -> Component
4 -> AutoComplete
else -> Unknown(type)
}
}
Expand Down Expand Up @@ -306,6 +336,7 @@ sealed class Option {
element("value", JsonElement.serializer().descriptor, isOptional = true)
element("options", JsonArray.serializer().descriptor, isOptional = true)
element("type", ApplicationCommandOptionType.serializer().descriptor, isOptional = false)
element("focused", String.serializer().descriptor, isOptional = true)
}

override fun deserialize(decoder: Decoder): Option {
Expand All @@ -316,6 +347,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 @@ -324,7 +356,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 @@ -359,7 +392,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 @@ -412,10 +445,12 @@ data class SubCommand(
sealed class CommandArgument<out T> : Option() {

abstract val value: T
abstract val focused: OptionalBoolean

class StringArgument(
override val name: String,
override val value: String
override val value: String,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<String>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.String
Expand All @@ -425,7 +460,8 @@ sealed class CommandArgument<out T> : Option() {

class IntegerArgument(
override val name: String,
override val value: Long
override val value: Long,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<Long>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.Integer
Expand All @@ -435,7 +471,8 @@ sealed class CommandArgument<out T> : Option() {

class NumberArgument(
override val name: String,
override val value: Double
override val value: Double,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<Double>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.Number
Expand All @@ -445,7 +482,8 @@ sealed class CommandArgument<out T> : Option() {

class BooleanArgument(
override val name: String,
override val value: Boolean
override val value: Boolean,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<Boolean>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.Boolean
Expand All @@ -455,7 +493,8 @@ sealed class CommandArgument<out T> : Option() {

class UserArgument(
override val name: String,
override val value: Snowflake
override val value: Snowflake,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<Snowflake>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.User
Expand All @@ -465,7 +504,8 @@ sealed class CommandArgument<out T> : Option() {

class ChannelArgument(
override val name: String,
override val value: Snowflake
override val value: Snowflake,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<Snowflake>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.Channel
Expand All @@ -475,7 +515,8 @@ sealed class CommandArgument<out T> : Option() {

class RoleArgument(
override val name: String,
override val value: Snowflake
override val value: Snowflake,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<Snowflake>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.Role
Expand All @@ -485,7 +526,8 @@ sealed class CommandArgument<out T> : Option() {

class MentionableArgument(
override val name: String,
override val value: Snowflake
override val value: Snowflake,
override val focused: OptionalBoolean = OptionalBoolean.Missing
) : CommandArgument<Snowflake>() {
override val type: ApplicationCommandOptionType
get() = ApplicationCommandOptionType.Mentionable
Expand Down Expand Up @@ -542,32 +584,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 @@ -598,7 +641,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 Expand Up @@ -640,6 +683,7 @@ sealed class InteractionResponseType(val type: Int) {
object DeferredChannelMessageWithSource : InteractionResponseType(5)
object DeferredUpdateMessage : InteractionResponseType(6)
object UpdateMessage : InteractionResponseType(7)
object ApplicationCommandAutoCompleteResult : InteractionResponseType(8)
class Unknown(type: Int) : InteractionResponseType(type)

companion object;
Expand All @@ -656,6 +700,7 @@ sealed class InteractionResponseType(val type: Int) {
5 -> DeferredChannelMessageWithSource
6 -> DeferredUpdateMessage
7 -> UpdateMessage
8 -> ApplicationCommandAutoCompleteResult
else -> Unknown(type)
}
}
Expand Down Expand Up @@ -712,3 +757,8 @@ data class DiscordGuildApplicationCommandPermission(
}
}
}

@Serializable
data class DiscordAutoComplete<T>(
val choices: List<Choice<T>>
)
10 changes: 4 additions & 6 deletions core/src/main/kotlin/behavior/GuildInteractionBehavior.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
package dev.kord.core.behavior

import dev.kord.core.behavior.interaction.InteractionBehavior
import dev.kord.common.entity.Snowflake
import dev.kord.core.behavior.interaction.InteractionBehavior
import dev.kord.core.entity.Guild
import dev.kord.core.entity.interaction.ActionInteraction

/**
* The behavior of a [dev.kord.core.entity.interaction.Interaction][Interaction] that was invoked in a [Guild]
* The behavior of a [ActionInteraction] that was invoked in a [Guild]
*/
public interface GuildInteractionBehavior : InteractionBehavior {

public val guildId: Snowflake


/**
* The [GuildBehavior] for the guild the command was executed in.
*/
public val guildBehavior: GuildBehavior get() = GuildBehavior(guildId, kord)


public suspend fun getGuildOrNull(): Guild? = supplier.getGuildOrNull(guildId)

public suspend fun getGuild(): Guild = supplier.getGuild(guildId)

public companion object;
public companion object

}
Loading