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

Implement Stage instances #291

Merged
merged 9 commits into from
May 15, 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
23 changes: 23 additions & 0 deletions common/src/main/kotlin/entity/DiscordStageInstance.kt
Original file line number Diff line number Diff line change
@@ -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
)
5 changes: 4 additions & 1 deletion core/src/main/kotlin/Unsafe.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand All @@ -87,5 +91,4 @@ class Unsafe(private val kord: Kord) {
): GlobalApplicationCommandBehavior =
GlobalApplicationCommandBehavior(applicationId, commandId, service)


}
45 changes: 45 additions & 0 deletions core/src/main/kotlin/behavior/StageInstanceBehavior.kt
Original file line number Diff line number Diff line change
@@ -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)"
}
}
16 changes: 15 additions & 1 deletion core/src/main/kotlin/behavior/channel/StageChannelBehavior.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

}

/**
Expand Down
19 changes: 19 additions & 0 deletions core/src/main/kotlin/cache/data/StageInstanceData.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
19 changes: 19 additions & 0 deletions core/src/main/kotlin/entity/StageInstance.kt
Original file line number Diff line number Diff line change
@@ -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))

}
3 changes: 3 additions & 0 deletions core/src/main/kotlin/exception/EntityNotFoundException.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")

}

}
2 changes: 2 additions & 0 deletions core/src/main/kotlin/supplier/CacheEntitySupplier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
}
Expand Down
7 changes: 6 additions & 1 deletion core/src/main/kotlin/supplier/EntitySupplier.kt
Original file line number Diff line number Diff line change
@@ -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.*
Expand All @@ -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.
Expand Down Expand Up @@ -389,6 +389,11 @@ interface EntitySupplier {
getTemplateOrNull(code) ?: EntityNotFoundException.templateNotFound(code)

fun getTemplates(guildId: Snowflake): Flow<Template>

suspend fun getStageInstanceOrNull(channelId: Snowflake): StageInstance?

suspend fun getStageInstance(channelId: Snowflake): StageInstance =
getStageInstanceOrNull(channelId) ?: EntityNotFoundException.stageInstanceNotFound(channelId)
}


Expand Down
2 changes: 2 additions & 0 deletions core/src/main/kotlin/supplier/FallbackEntitySupplier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti
override fun getTemplates(guildId: Snowflake): Flow<Template> =
first.getTemplates(guildId).switchIfEmpty(second.getTemplates(guildId))

override suspend fun getStageInstanceOrNull(channelId: Snowflake): StageInstance? = first.getStageInstanceOrNull(channelId) ?: second.getStageInstanceOrNull(channelId)


override fun toString(): String {
return "FallbackEntitySupplier(first=$first, second=$second)"
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/kotlin/supplier/RestEntitySupplier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,13 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier {
auditLog.getAuditLogs(guildId, request.copy(before = it.value)).auditLogEntries
}

override suspend fun getStageInstanceOrNull(channelId: Snowflake): StageInstance? = catchNotFound {
val instance = kord.rest.stageInstance.getStageInstance(channelId)
val data = StageInstanceData.from(instance)

return StageInstance(data, kord, this)
}

override fun toString(): String {
return "RestEntitySupplier(rest=${kord.rest})"
}
Expand Down
4 changes: 4 additions & 0 deletions core/src/samples/kotlin/PingBot.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import dev.kord.core.Kord
import dev.kord.core.behavior.channel.createStageInstance
import dev.kord.core.behavior.channel.getStageInstance
import dev.kord.core.behavior.getChannelOf
import dev.kord.core.entity.channel.StageChannel
import dev.kord.core.event.message.MessageCreateEvent
import dev.kord.core.on

Expand Down
1 change: 1 addition & 0 deletions rest/src/main/kotlin/json/request/ChannelRequests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dev.kord.rest.json.request
import dev.kord.common.entity.Overwrite
import dev.kord.common.entity.OverwriteType
import dev.kord.common.entity.Permissions
import dev.kord.common.entity.Snowflake
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.OptionalBoolean
import dev.kord.common.entity.optional.OptionalInt
Expand Down
15 changes: 15 additions & 0 deletions rest/src/main/kotlin/json/request/StageInstanceRequests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.kord.rest.json.request

import dev.kord.common.entity.Snowflake
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class StageInstanceCreateRequest(
@SerialName("channel_id")
val channelId: Snowflake,
val topic: String
)

@Serializable
data class StageInstanceUpdateRequest(val topic: String)
12 changes: 12 additions & 0 deletions rest/src/main/kotlin/route/Route.kt
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,18 @@ sealed class Route<T>(
object OthersVoiceStatePatch:
Route<Unit>(HttpMethod.Patch, "/guilds/${GuildId}/voice-states/${UserId}", NoStrategy)

object StageInstanceGet :
Route<DiscordStageInstance>(HttpMethod.Get, "/stage-instances/$ChannelId", DiscordStageInstance.serializer())

object StageInstancePost :
Route<DiscordStageInstance>(HttpMethod.Post, "/stage-instances", DiscordStageInstance.serializer())

object StageInstancePatch :
Route<DiscordStageInstance>(HttpMethod.Patch, "/stage-instances/$ChannelId", DiscordStageInstance.serializer())

object StageInstanceDelete :
Route<Unit>(HttpMethod.Delete, "/stage-instances/$ChannelId", NoStrategy)

companion object {
val baseUrl = "https://discord.com/api/$restVersion"
}
Expand Down
1 change: 1 addition & 0 deletions rest/src/main/kotlin/service/RestClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class RestClient(requestHandler: RequestHandler) : RestService(requestHandler) {
val application: ApplicationService = ApplicationService(requestHandler)
val template: TemplateService = TemplateService(requestHandler)
val interaction: InteractionService = InteractionService(requestHandler)
val stageInstance: StageInstanceService = StageInstanceService(requestHandler)

/**
* Sends a request to the given [route]. This function exposes a direct call to the Discord api and allows
Expand Down
39 changes: 39 additions & 0 deletions rest/src/main/kotlin/service/StageInstanceService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package dev.kord.rest.service

import dev.kord.common.entity.DiscordStageInstance
import dev.kord.common.entity.Snowflake
import dev.kord.rest.json.request.StageInstanceCreateRequest
import dev.kord.rest.json.request.StageInstanceUpdateRequest
import dev.kord.rest.request.RequestHandler
import dev.kord.rest.route.Route

class StageInstanceService(requestHandler: RequestHandler) : RestService(requestHandler) {
suspend fun getStageInstance(channelId: Snowflake): DiscordStageInstance = call(Route.StageInstanceGet) {
keys[Route.ChannelId] = channelId
}

suspend fun createStageInstance(request: StageInstanceCreateRequest): DiscordStageInstance =
call(Route.StageInstancePost) {
body(StageInstanceCreateRequest.serializer(), request)
}

suspend fun updateStageInstance(channelId: Snowflake, request: StageInstanceUpdateRequest): DiscordStageInstance =
call(Route.StageInstancePost) {
keys[Route.ChannelId] = channelId

body(StageInstanceUpdateRequest.serializer(), request)
}

suspend fun deleteStageInstance(channelId: Snowflake): Unit = call(Route.StageInstanceDelete) {
keys[Route.ChannelId] = channelId
}
}

suspend fun StageInstanceService.createStageInstance(channelId: Snowflake, topic: String) = createStageInstance(
StageInstanceCreateRequest(channelId, topic)
)

suspend fun StageInstanceService.updateStageInstance(channelId: Snowflake, topic: String) = updateStageInstance(
channelId,
StageInstanceUpdateRequest(topic)
)