Skip to content

Commit

Permalink
Improve slash command API and add support for components (#310)
Browse files Browse the repository at this point in the history
* Make slash command creation eager

createGuildApplicationCommands and createGlobalApplicationCommands should eagerly create new commands to be more in line with the rest of the API

* Fix typo in InteractionBehavior

ackowledgePublic -> acknowledgePublic

* Don't compute supplier in InteractionBehavior

It's a waste of resources, and might result in unexpected behavior for non-Kord suppliers.

* Specify withStrategy for Interactions

Return types are important!

* Introduce type to command options

Also add Mentionable.

Seems like Discord consistently tells us what kind of option an object is via the type field, this allows us to more clearly represent the json data in the lower level APIs.

I ended up deleting DiscordOptionValue and replacing it with a more fleshed out CommandArgument.

* Add KordDsl to builders

* Add allowedMentions builder functions

* Add permission edits to guild commands

* Make full member available for guild contexts

* Support buttons/components (#303)

* Add new API models and properties

* Add new InteractionCallbackType

* Add ability to send components

* Update core entities

* Update tests

* Add @KordPreview annotations

* Convert don't trust doc comments to normal comments

* Fix compilation issue

* Optimize interaction serializing

* Add builder

* Fix tests

* Apply some requested changes

* Hopefully fix formatting

* Make builders enforce Discord restrictions

* Improve builders again

* Remove LinkButtonBuilder.style

* Improve builders
- Add components to more supported builders
- Make required fields parameters
- Make builders appending

* Update deprecated message (#280)

* Expose the creation of application commands behavior (#281)

* Fix GuildUpdate core handling (#284)

* Expose the creation of application commands behavior

* Fix type of emitted event

* Sealed message types (#282)

* Expose the creation of application commands behavior

* Make message types sealed

* make Unknown a class

* Add missing message types

* make MessageTypeSerializer internal

* Add buttons to Activity (#287)

* Add buttons to Activity

* Also pass buttons in constructor

* Add missing fields to Guild (#288)

* Add missing fields to Guild
- Add welcome_screen
- Add nsfw

* Fix failing tests

* Fix another failing tests

* Add Message.applicationId (#289)

* Message interaction (#283)

* Expose the creation of application commands behavior

* Add interaction message

* Apply suggestions

* reference the MessageInteraction in docs

* Implement Strategizable for MessageInteraction

* cache user from interaction message

* Fix compilation errors

* Fix withStrategy return type

Co-authored-by: Bart Arys <[email protected]>

Co-authored-by: Bart Arys <[email protected]>

* Implement Stage instances (#291)

* Add low-level implementation of stage instances

* Add helper functions

* Add core entities and api representations

* Expose creation of StageInstanceBehavior to unsafe
- Revert outdated change

* Final additions
- Add StageInstanceBehavior.asStageInstance
- Fix compiler issue
- Add StageChannelBehavior.getStageInstance()

* Add StageInstances to EntitySupplier.kt

* Add StageInstances to EntitySupplier.kt

* Fix typo

* Apply requested changes

* Fix return type of channel in VoiceState (#295)

* Fix return type of channel in VoiceState

* Fix GuildService#modifyVoiceState endpoint

* Fix MessageInteraction#user id

* Add missing session start limit field (#306)

- Adds the max_concurrency field to the session start limit data class

* Update to Kotlin 1.5 (#299)

* Update to 1.5.0-RC

* trigger on branch pushes

* Port to Kotlin 1.5 (#268)

* Port dependencies to Kotlin 1.5
- Convert AbstractRateLimiter.AbstractRequestToken to a static rather than an inner class due to a compiler bug
- Downgrade kx.ser-json to 1.0.0 to avoid a compiler bug
- Bump other Kotlin dependencies to latest

fixup! Port dependencies to Kotlin 1.5 - Convert AbstractRateLimiter.AbstractRequestToken to a static rather than an inner class due to a compiler bug - Downgrade kx.ser-json to 1.0.0 to avoid a compiler bug - Bump other Kotlin dependencies to latest

* Replace deprecated kotlin.time APIs

* Replace more deprecated APIs & inline classes

* Replace deprecated usage of time API in tests

* Possibly fix test

Co-authored-by: Hope <[email protected]>

* Port to Kotlin 1.5
- Bump dependencies to 1.5 recommended versions
- Remove inline classes in favor of value classes
- Add required opt-ins
- Migrate some more deprecated apis

* Fix some gradle issues

* Fix Gradle compilation issue

* Remove documentationFileName as the new dokka version doesn't support it anymore and there is no replacement yet

* Port kotlinx.serialization to 1.2.0 (#279)

* Port kotlinx.serialization to 1.2.0
- Convert local classes to top level classes (See Kotlin/kotlinx.serialization#1472)
- Improve handling of empty JSON bodies (See Kotlin/kotlinx.serialization#678)
- Fix Failing Command test

* Fix failing test

* Properly decode null in gateway events (#286)

* Properly decode null in gateway events

* Update gateway/src/main/kotlin/Event.kt

Co-authored-by: Bart Arys <[email protected]>

Co-authored-by: Bart Arys <[email protected]>

* Update Kotlinx.serialization (#290)

* Update Kotlin 1.5 branch to upstream (#292)

* Implement voice stage channel (#239)

* compute All

* implement rest endpoints

* JSON representation

* implement core representation

* handle stage channels

* Apply suggestions

Co-authored-by: BartArys <[email protected]>

* Remove duplicated factory function
Co-authored-by: BartArys <[email protected]>

* add documentation

* Document the requestToSpeak variable

Co-authored-by: BartArys <[email protected]>

* Fix CI triggers

* Add "Competing" activity type (Fix #270) (#272)

* Make Updatestatus activities not-null (#274)

As per Discord's documentation: discord/discord-api-docs#2789

* Fix memory issues related to Permission combining (#277)

* Do not octuple bitset size on copy

the pure plus and minus function create a new array to work with, this incorrectly created an array of a size equal to the amount of bits that were allocated, instead the amount of longs. Thus, octupling the internal size.

* Optimize Permission All

The All Permission folded each DiscordBitSet of each value into eachother, resulting in n + 1 bitsets being created. This commit changes that to use the internal `add` which instead, which only mutates the single bitset created.

* Add Stream permission

It was missing

* Add Permission All regression tests

* Update deprecated message (#280)

* Expose the creation of application commands behavior (#281)

* Fix GuildUpdate core handling (#284)

* Expose the creation of application commands behavior

* Fix type of emitted event

* Sealed message types (#282)

* Expose the creation of application commands behavior

* Make message types sealed

* make Unknown a class

* Add missing message types

* make MessageTypeSerializer internal

* Add buttons to Activity (#287)

* Add buttons to Activity

* Also pass buttons in constructor

* Add missing fields to Guild (#288)

* Add missing fields to Guild
- Add welcome_screen
- Add nsfw

* Fix failing tests

* Fix another failing tests

* Add Message.applicationId (#289)

Co-authored-by: Hope <[email protected]>
Co-authored-by: BartArys <[email protected]>
Co-authored-by: HopeBaron <[email protected]>
Co-authored-by: Bart Arys <[email protected]>
Co-authored-by: Noah Hendrickson <[email protected]>

* Fix broken CI (#293)

* Migrate to kotlinx-datetime (#297)

* Migrate API code to kotlinx-datetime

* Update tests

* Remove dead code

* Replace iso serializing with kx.dt

* Bump dependencies to Kotlin 1.5.10 (#305)

* Bump dependencies to Kotlin 1.5.10
- Fix sample code

* Update remaining dependencies

* Update ktor to 1.6.0

Co-authored-by: HopeBaron <[email protected]>
Co-authored-by: Michael Rittmeister <[email protected]>
Co-authored-by: Hope <[email protected]>
Co-authored-by: Noah Hendrickson <[email protected]>

* Filter out non-guilds when fetching rest guilds (#301)

This is a side-effect from the system we currently employ when it comes to creating channels. Unknown channels are instantiated as an anonymous channel without hierarchy. While we could assume it is a guild channel, it's not in actuality.

So the solution, as we did with cache, is to filter out channels that don't inherit the hierarchy.

* Improve builders again
- Merge in upstream

* Fix builder requirements
- Fix error when sending a emoji button

* Fix merge related syntax errors

* Restore default sample

* Fix formatting isssue in documentation

Co-authored-by: Bart Arys <[email protected]>

* Max component list a val

Co-authored-by: Bart Arys <[email protected]>

* Apply suggestions from code review

Co-authored-by: Bart Arys <[email protected]>

* Apply requested changes

* No longer make DiscordInteraction a sealed class

* Fix DiscordInteraction serialization

* Add some documentation for components

* Remove unused imports from builder

Co-authored-by: Noah Hendrickson <[email protected]>
Co-authored-by: Hope <[email protected]>
Co-authored-by: Bart Arys <[email protected]>
Co-authored-by: 2D <[email protected]>
Co-authored-by: HopeBaron <[email protected]>

* Add core versions of components

* Restructure and document ButtonBuilder

* Remove ActionRowContainerBuilder

* Make ComponentInteraction message nullable

ephemeral messages in the interaction contain a id, which is not a message id, and the flags.

We can't construct a behaviour from this (since the id isn't real), so I decided to drop the data if the message is ephemeral.

* Add missing components to interaction builders

* Add missing ComponentInteraction behavior

* Fix withStrategy for ComponentInteractionBehavior

* Implement ComponentInteractionBehavior

* Move component builders directory

We use singular for package names

* Fix interaction embeds optionality

* Make CommandInteraction#guildId optional

* Make MessageModifyBuilder components vals

Co-authored-by: Michael Rittmeister <[email protected]>
Co-authored-by: Noah Hendrickson <[email protected]>
Co-authored-by: Hope <[email protected]>
Co-authored-by: 2D <[email protected]>
Co-authored-by: HopeBaron <[email protected]>
  • Loading branch information
6 people authored Jun 12, 2021
1 parent 447ba66 commit 1d24fa8
Show file tree
Hide file tree
Showing 40 changed files with 1,615 additions and 259 deletions.
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ dependencies {
implementation(kotlin("gradle-plugin-api", version = "1.5.0"))
implementation(gradleApi())
implementation(localGroovy())
}
}
140 changes: 140 additions & 0 deletions common/src/main/kotlin/entity/DiscordComponent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package dev.kord.common.entity

import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.OptionalBoolean
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

/**
* Represent a [intractable component within a message sent in Discord](https://discord.com/developers/docs/interactions/message-components#what-are-components).
*
* @property type the [ComponentType] of the component
* @property style the [ButtonStyle] of the component (if it is a button)
* @property style the text that appears on the button (if the component is a button)
* @property emoji an [DiscordPartialEmoji] that appears on the button (if the component is a button)
* @property customId a developer-defined identifier for the button, max 100 characters
* @property url a url for link-style buttons
* @property disabled whether the button is disabled, default `false`
* @property components a list of child components (for action rows)
*/
@KordPreview
@Serializable
data class DiscordComponent(
val type: ComponentType,
val style: Optional<ButtonStyle> = Optional.Missing(),
val label: Optional<String> = Optional.Missing(),
val emoji: Optional<DiscordPartialEmoji> = Optional.Missing(),
@SerialName("custom_id")
val customId: Optional<String> = Optional.Missing(),
val url: Optional<String> = Optional.Missing(),
val disabled: OptionalBoolean = OptionalBoolean.Missing,
val components: Optional<List<DiscordComponent>> = Optional.Missing()
)

/**
* Representation of different [DiscordComponent] types.
*
* @property value the raw type value used by the Discord API
*/
@KordPreview
@Serializable(with = ComponentType.Serializer::class)
sealed class ComponentType(val value: Int) {

/**
* Fallback type used for types that haven't been added to Kord yet.
*/
class Unknown(value: Int) : ComponentType(value)

/**
* A container for other components.
*/
object ActionRow : ComponentType(1)

/**
* A clickable button.
*/
object Button : ComponentType(2)

companion object Serializer : KSerializer<ComponentType> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ComponentType", PrimitiveKind.INT)

override fun deserialize(decoder: Decoder): ComponentType =
when (val value = decoder.decodeInt()) {
1 -> ActionRow
2 -> Button
else -> Unknown(value)
}

override fun serialize(encoder: Encoder, value: ComponentType) = encoder.encodeInt(value.value)
}
}

/**
* Representation of different ButtonStyles.
*
* A cheat sheet on how the styles look like can be found [here](https://discord.com/assets/7bb017ce52cfd6575e21c058feb3883b.png)
*
* @see ComponentType.Button
*/
@KordPreview
@Serializable(with = ButtonStyle.Serializer::class)
sealed class ButtonStyle(val value: Int) {

/**
* A fallback style used for styles that haven't been added to Kord yet.
*/
class Unknown(value: Int) : ButtonStyle(value)

/**
* Blurple.
* Requires: [DiscordComponent.customId]
*/
object Primary : ButtonStyle(1)

/**
* Grey.
* Requires: [DiscordComponent.customId]
*/
object Secondary : ButtonStyle(2)

/**
* Green
* Requires: [DiscordComponent.customId]
*/
object Success : ButtonStyle(3)

/**
* Red.
* Requires: [DiscordComponent.customId]
*/
object Danger : ButtonStyle(4)

/**
* Grey, navigates to an URL.
* Requires: [DiscordComponent.url]
*/
object Link : ButtonStyle(5)

companion object Serializer : KSerializer<ButtonStyle> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ButtonStyle", PrimitiveKind.INT)

override fun deserialize(decoder: Decoder): ButtonStyle =
when (val value = decoder.decodeInt()) {
1 -> Primary
2 -> Secondary
3 -> Success
4 -> Danger
5 -> Link
else -> Unknown(value)
}

override fun serialize(encoder: Encoder, value: ButtonStyle) = encoder.encodeInt(value.value)
}
}
4 changes: 2 additions & 2 deletions common/src/main/kotlin/entity/DiscordEmoji.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ data class DiscordUpdatedEmojis(
*/
@Serializable
data class DiscordPartialEmoji(
val id: Snowflake?,
val name: String?,
val id: Snowflake? = null,
val name: String? = null,
val animated: OptionalBoolean = OptionalBoolean.Missing,
)
6 changes: 6 additions & 0 deletions common/src/main/kotlin/entity/DiscordMessage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import kotlin.contracts.contract
* @param stickers The stickers sent with the message (bots currently can only receive messages with stickers, not send).
* @param referencedMessage the message associated with [messageReference].
* @param applicationId if the message is a response to an [Interaction][DiscordInteraction], this is the id of the interaction's application
* @param components a list of [components][DiscordComponent] which have been added to this message
*/
@Serializable
data class DiscordMessage(
Expand Down Expand Up @@ -103,6 +104,11 @@ data class DiscordMessage(
val stickers: Optional<List<DiscordMessageSticker>> = Optional.Missing(),
@SerialName("referenced_message")
val referencedMessage: Optional<DiscordMessage?> = Optional.Missing(),
/*
* don't trust the docs:
* This is a list even though the docs say it's a component
*/
val components: Optional<List<DiscordComponent>> = Optional.Missing(),
val interaction: Optional<DiscordMessageInteraction> = Optional.Missing()
)

Expand Down
Loading

0 comments on commit 1d24fa8

Please sign in to comment.