From 60ab2120c6a13366562157aaccf68057f571dcfc Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Sat, 15 May 2021 21:02:29 +0200 Subject: [PATCH] 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 --- .../kotlin/entity/DiscordStageInstance.kt | 23 ++++++++++ core/src/main/kotlin/Unsafe.kt | 5 ++- .../kotlin/behavior/StageInstanceBehavior.kt | 45 +++++++++++++++++++ .../behavior/channel/StageChannelBehavior.kt | 16 ++++++- .../kotlin/cache/data/StageInstanceData.kt | 19 ++++++++ core/src/main/kotlin/entity/StageInstance.kt | 19 ++++++++ .../exception/EntityNotFoundException.kt | 3 ++ .../kotlin/supplier/CacheEntitySupplier.kt | 2 + .../main/kotlin/supplier/EntitySupplier.kt | 7 ++- .../kotlin/supplier/FallbackEntitySupplier.kt | 2 + .../kotlin/supplier/RestEntitySupplier.kt | 7 +++ core/src/samples/kotlin/PingBot.kt | 4 ++ .../kotlin/json/request/ChannelRequests.kt | 1 + .../json/request/StageInstanceRequests.kt | 15 +++++++ rest/src/main/kotlin/route/Route.kt | 12 +++++ rest/src/main/kotlin/service/RestClient.kt | 1 + .../kotlin/service/StageInstanceService.kt | 39 ++++++++++++++++ 17 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 common/src/main/kotlin/entity/DiscordStageInstance.kt create mode 100644 core/src/main/kotlin/behavior/StageInstanceBehavior.kt create mode 100644 core/src/main/kotlin/cache/data/StageInstanceData.kt create mode 100644 core/src/main/kotlin/entity/StageInstance.kt create mode 100644 rest/src/main/kotlin/json/request/StageInstanceRequests.kt create mode 100644 rest/src/main/kotlin/service/StageInstanceService.kt diff --git a/common/src/main/kotlin/entity/DiscordStageInstance.kt b/common/src/main/kotlin/entity/DiscordStageInstance.kt new file mode 100644 index 000000000000..826eb9a4f79c --- /dev/null +++ b/common/src/main/kotlin/entity/DiscordStageInstance.kt @@ -0,0 +1,23 @@ +package dev.kord.common.entity + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + + +/** + * A _Stage Instance_ holds information about a live stage. + * + * @property id The id of this Stage instance + * @property guildId The guild id of the associated Stage channel + * @property channelId The id of the associated Stage channel + * @property topic The topic of the Stage instance (1-120 characters) + */ +@Serializable +data class DiscordStageInstance( + val id: Snowflake, + @SerialName("guild_id") + val guildId: Snowflake, + @SerialName("channel_id") + val channelId: Snowflake, + val topic: String +) diff --git a/core/src/main/kotlin/Unsafe.kt b/core/src/main/kotlin/Unsafe.kt index b09c1e206ba3..c949920656d5 100644 --- a/core/src/main/kotlin/Unsafe.kt +++ b/core/src/main/kotlin/Unsafe.kt @@ -68,6 +68,10 @@ class Unsafe(private val kord: Kord) { fun webhook(id: Snowflake): WebhookBehavior = WebhookBehavior(id, kord) + fun stageInstance(id: Snowflake, channelId: Snowflake): StageInstanceBehavior = StageInstanceBehavior( + id, channelId, kord, kord.defaultSupplier + ) + override fun toString(): String { return "Unsafe" } @@ -87,5 +91,4 @@ class Unsafe(private val kord: Kord) { ): GlobalApplicationCommandBehavior = GlobalApplicationCommandBehavior(applicationId, commandId, service) - } \ No newline at end of file diff --git a/core/src/main/kotlin/behavior/StageInstanceBehavior.kt b/core/src/main/kotlin/behavior/StageInstanceBehavior.kt new file mode 100644 index 000000000000..bd8f4ed2c051 --- /dev/null +++ b/core/src/main/kotlin/behavior/StageInstanceBehavior.kt @@ -0,0 +1,45 @@ +package dev.kord.core.behavior + +import dev.kord.common.entity.Snowflake +import dev.kord.core.Kord +import dev.kord.core.cache.data.StageInstanceData +import dev.kord.core.entity.KordEntity +import dev.kord.core.entity.StageInstance +import dev.kord.core.entity.Strategizable +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy +import dev.kord.rest.json.request.StageInstanceUpdateRequest + +interface StageInstanceBehavior : KordEntity, Strategizable { + val channelId: Snowflake + + suspend fun delete(): Unit = kord.rest.stageInstance.deleteStageInstance(channelId) + + suspend fun update(topic: String): StageInstance { + val instance = kord.rest.stageInstance.updateStageInstance(channelId, StageInstanceUpdateRequest(topic)) + val data = StageInstanceData.from(instance) + + return StageInstance(data, kord, supplier) + } + + suspend fun asStageInstance(): StageInstance = supplier.getStageInstance(channelId) + + override fun withStrategy(strategy: EntitySupplyStrategy<*>): StageInstanceBehavior = + StageInstanceBehavior(id, channelId, kord, strategy.supply(kord)) +} + +internal fun StageInstanceBehavior(id: Snowflake, channelId: Snowflake, kord: Kord, supplier: EntitySupplier) = + object : StageInstanceBehavior { + override val channelId: Snowflake + get() = channelId + override val kord: Kord + get() = kord + override val id: Snowflake + get() = id + override val supplier: EntitySupplier + get() = supplier + + override fun toString(): String { + return "StageInstanceBehavior(id=$id, channelId=$id, kord=$kord, supplier=$supplier)" + } + } diff --git a/core/src/main/kotlin/behavior/channel/StageChannelBehavior.kt b/core/src/main/kotlin/behavior/channel/StageChannelBehavior.kt index 2d93dcc7bd79..85f818b6c29d 100644 --- a/core/src/main/kotlin/behavior/channel/StageChannelBehavior.kt +++ b/core/src/main/kotlin/behavior/channel/StageChannelBehavior.kt @@ -3,15 +3,17 @@ package dev.kord.core.behavior.channel import dev.kord.common.entity.Snowflake import dev.kord.core.Kord import dev.kord.core.cache.data.ChannelData +import dev.kord.core.cache.data.StageInstanceData +import dev.kord.core.entity.StageInstance import dev.kord.core.entity.channel.Channel import dev.kord.core.entity.channel.StageChannel -import dev.kord.core.entity.channel.VoiceChannel import dev.kord.core.supplier.EntitySupplier import dev.kord.core.supplier.EntitySupplyStrategy import dev.kord.rest.builder.channel.StageVoiceChannelModifyBuilder import dev.kord.rest.builder.guild.CurrentVoiceStateModifyBuilder import dev.kord.rest.builder.guild.VoiceStateModifyBuilder import dev.kord.rest.request.RestRequestException +import dev.kord.rest.service.createStageInstance import dev.kord.rest.service.modifyCurrentVoiceState import dev.kord.rest.service.modifyVoiceState import dev.kord.rest.service.patchStageVoiceChannel @@ -30,6 +32,18 @@ interface StageChannelBehavior : BaseVoiceChannelBehavior { return StageChannelBehavior(id, guildId, kord, strategy.supply(kord)) } + + suspend fun createStageInstance(topic: String): StageInstance { + val instance = kord.rest.stageInstance.createStageInstance(id, topic) + val data = StageInstanceData.from(instance) + + return StageInstance(data, kord, supplier) + } + + suspend fun getStageInstanceOrNull(): StageInstance? = supplier.getStageInstanceOrNull(id) + + suspend fun getStageInstance(): StageInstance = supplier.getStageInstance(id) + } /** diff --git a/core/src/main/kotlin/cache/data/StageInstanceData.kt b/core/src/main/kotlin/cache/data/StageInstanceData.kt new file mode 100644 index 000000000000..43e6af3ba954 --- /dev/null +++ b/core/src/main/kotlin/cache/data/StageInstanceData.kt @@ -0,0 +1,19 @@ +package dev.kord.core.cache.data + +import dev.kord.common.entity.DiscordStageInstance +import dev.kord.common.entity.Snowflake +import kotlinx.serialization.Serializable + +@Serializable +data class StageInstanceData( + val id: Snowflake, + val guildId: Snowflake, + val channelId: Snowflake, + val topic: String +) { + companion object { + fun from(stageInstance: DiscordStageInstance) = with(stageInstance) { + StageInstanceData(id, guildId, channelId, topic) + } + } +} diff --git a/core/src/main/kotlin/entity/StageInstance.kt b/core/src/main/kotlin/entity/StageInstance.kt new file mode 100644 index 000000000000..5987e4d95e09 --- /dev/null +++ b/core/src/main/kotlin/entity/StageInstance.kt @@ -0,0 +1,19 @@ +package dev.kord.core.entity + +import dev.kord.common.entity.Snowflake +import dev.kord.core.Kord +import dev.kord.core.behavior.StageInstanceBehavior +import dev.kord.core.cache.data.StageInstanceData +import dev.kord.core.supplier.EntitySupplier +import dev.kord.core.supplier.EntitySupplyStrategy + +class StageInstance(val data: StageInstanceData, override val kord: Kord, override val supplier: EntitySupplier) : StageInstanceBehavior { + override val id: Snowflake get() = data.id + val guildId: Snowflake get() = data.guildId + override val channelId: Snowflake get() = data.channelId + val topic: String get() = data.topic + + override fun withStrategy(strategy: EntitySupplyStrategy<*>): StageInstanceBehavior = + StageInstance(data, kord, strategy.supply(kord)) + +} diff --git a/core/src/main/kotlin/exception/EntityNotFoundException.kt b/core/src/main/kotlin/exception/EntityNotFoundException.kt index 4a6ca68aed23..39c45e34699b 100644 --- a/core/src/main/kotlin/exception/EntityNotFoundException.kt +++ b/core/src/main/kotlin/exception/EntityNotFoundException.kt @@ -63,6 +63,9 @@ class EntityNotFoundException : Exception { fun welcomeScreenNotFound(guildId: Snowflake): Nothing = throw EntityNotFoundException("Welcome screen for guild $guildId was not found.") + fun stageInstanceNotFound(channelId: Snowflake): Nothing = + throw EntityNotFoundException("Stage instance for channel $channelId was not found") + } } \ No newline at end of file diff --git a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt index 5f994729de54..351fd651fd3a 100644 --- a/core/src/main/kotlin/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/supplier/CacheEntitySupplier.kt @@ -272,6 +272,8 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { }.asFlow().map { Template(it, kord) } } + override suspend fun getStageInstanceOrNull(channelId: Snowflake): StageInstance? = null + override fun toString(): String { return "CacheEntitySupplier(cache=$cache)" } diff --git a/core/src/main/kotlin/supplier/EntitySupplier.kt b/core/src/main/kotlin/supplier/EntitySupplier.kt index 38dfae09b299..f059f4da729c 100644 --- a/core/src/main/kotlin/supplier/EntitySupplier.kt +++ b/core/src/main/kotlin/supplier/EntitySupplier.kt @@ -1,5 +1,6 @@ package dev.kord.core.supplier +import dev.kord.common.entity.ChannelType.Unknown import dev.kord.common.entity.Snowflake import dev.kord.common.exception.RequestException import dev.kord.core.entity.* @@ -8,7 +9,6 @@ import dev.kord.core.entity.channel.GuildChannel import dev.kord.core.entity.channel.MessageChannel import dev.kord.core.exception.EntityNotFoundException import kotlinx.coroutines.flow.Flow -import dev.kord.common.entity.ChannelType.Unknown /** * An abstraction that allows for requesting Discord entities. @@ -389,6 +389,11 @@ interface EntitySupplier { getTemplateOrNull(code) ?: EntityNotFoundException.templateNotFound(code) fun getTemplates(guildId: Snowflake): Flow