Skip to content

Commit

Permalink
Implement voice stage channel
Browse files Browse the repository at this point in the history
  • Loading branch information
HopeBaron committed Apr 4, 2021
1 parent 876c89c commit a81e7c4
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 12 deletions.
3 changes: 3 additions & 0 deletions common/src/main/kotlin/entity/DiscordChannel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ sealed class ChannelType(val value: Int) {
/** A channel in which game developers can sell their game on Discord. */
object GuildStore : ChannelType(6)

object GuildStageVoice : ChannelType(13)

companion object;

internal object Serializer : KSerializer<ChannelType> {
Expand All @@ -107,6 +109,7 @@ sealed class ChannelType(val value: Int) {
4 -> GuildCategory
5 -> GuildNews
6 -> GuildStore
13 -> GuildStageVoice
else -> Unknown(code)
}

Expand Down
2 changes: 2 additions & 0 deletions common/src/main/kotlin/entity/DiscordGuild.kt
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ data class DiscordVoiceState(
@SerialName("self_stream")
val selfStream: OptionalBoolean = OptionalBoolean.Missing,
val suppress: Boolean,
@SerialName("request_to_speak_timestamp")
val requestToSpeakTimestamp: String?
)

/**
Expand Down
3 changes: 2 additions & 1 deletion common/src/main/kotlin/entity/Permission.kt
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,6 @@ sealed class Permission(val code: DiscordBitSet) {
object ManageWebhooks : Permission(0x20000000)
object ManageEmojis : Permission(0x40000000)
object UseSlashCommands : Permission(0x80000000)
object All : Permission(0x7FFFFDFF)
object RequestToSpeak: Permission(0x100000000)
object All : Permission(0x17FFFFDFF)
}
7 changes: 2 additions & 5 deletions core/src/main/kotlin/behavior/GuildBehavior.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ import dev.kord.rest.Image
import dev.kord.rest.builder.auditlog.AuditLogGetRequestBuilder
import dev.kord.rest.builder.ban.BanCreateBuilder
import dev.kord.rest.builder.channel.*
import dev.kord.rest.builder.guild.EmojiCreateBuilder
import dev.kord.rest.builder.guild.GuildModifyBuilder
import dev.kord.rest.builder.guild.GuildWidgetModifyBuilder
import dev.kord.rest.builder.guild.WelcomeScreenModifyBuilder
import dev.kord.rest.builder.guild.*
import dev.kord.rest.builder.interaction.ApplicationCommandCreateBuilder
import dev.kord.rest.builder.interaction.ApplicationCommandsCreateBuilder
import dev.kord.rest.builder.role.RoleCreateBuilder
Expand Down Expand Up @@ -509,7 +506,7 @@ interface GuildBehavior : KordEntity, Strategizable {
override fun withStrategy(strategy: EntitySupplyStrategy<*>): GuildBehavior = GuildBehavior(id, kord, strategy)
}

fun GuildBehavior(
fun GuildBehavior(
id: Snowflake,
kord: Kord,
strategy: EntitySupplyStrategy<*> = kord.resources.defaultStrategy,
Expand Down
44 changes: 44 additions & 0 deletions core/src/main/kotlin/behavior/VoiceStageChannelBehavior.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dev.kord.core.behavior

import dev.kord.common.entity.Snowflake
import dev.kord.core.Kord
import dev.kord.core.cache.data.ChannelData
import dev.kord.core.entity.channel.CategorizableChannel
import dev.kord.core.supplier.EntitySupplier
import dev.kord.core.supplier.EntitySupplyStrategy
import dev.kord.rest.builder.guild.CurrentVoiceStateModifyBuilder
import dev.kord.rest.builder.guild.VoiceStateModifyBuilder
import dev.kord.rest.service.modifyCurrentVoiceState
import dev.kord.rest.service.modifyVoiceState
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

interface VoiceStageChannelBehavior : CategorizableChannel {

override fun withStrategy(strategy: EntitySupplyStrategy<*>): VoiceStageChannelBehavior {
return VoiceStageChannelBehavior(data, kord, strategy.supply(kord))
}

fun VoiceStageChannelBehavior(
data: ChannelData,
kord: Kord,
supplier: EntitySupplier = kord.defaultSupplier
): VoiceStageChannelBehavior = object : VoiceStageChannelBehavior {
override val data: ChannelData = data
override val kord = kord
override val supplier = supplier
}
}

@OptIn(ExperimentalContracts::class)
suspend inline fun VoiceStageChannelBehavior.editCurrentVoiceState(builder: CurrentVoiceStateModifyBuilder.() -> Unit) {
contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) }
kord.rest.guild.modifyCurrentVoiceState(guildId, id, builder)
}

@OptIn(ExperimentalContracts::class)
suspend inline fun VoiceStageChannelBehavior.editVoiceState(userId: Snowflake, builder: VoiceStateModifyBuilder.() -> Unit) {
contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) }
kord.rest.guild.modifyVoiceState(guildId, userId, builder)
}
4 changes: 3 additions & 1 deletion core/src/main/kotlin/cache/data/VoiceStateData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ data class VoiceStateData(
val selfMute: Boolean,
val selfStream: OptionalBoolean = OptionalBoolean.Missing,
val suppress: Boolean,
val requestToSpeakTimestamp: String?
) {

companion object {
Expand All @@ -48,7 +49,8 @@ data class VoiceStateData(
selfDeaf = selfDeaf,
selfMute = selfMute,
selfStream = selfStream,
suppress = suppress
suppress = suppress,
requestToSpeakTimestamp = requestToSpeakTimestamp
)
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/kotlin/entity/VoiceState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class VoiceState(
*/
val isSelfSteaming: Boolean get() = data.selfStream.orElse(false)

val requestToSpeakTimestamp: String? get() = data.requestToSpeakTimestamp

/**
* Requests to get the voice channel of this voice state.
* Returns null if the [VoiceChannel] isn't present, or [channelId] is null.
Expand Down
36 changes: 36 additions & 0 deletions core/src/main/kotlin/entity/channel/StageChannel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package dev.kord.core.entity.channel

import dev.kord.common.entity.Snowflake
import dev.kord.common.entity.optional.getOrThrow
import dev.kord.core.Kord
import dev.kord.core.behavior.VoiceStageChannelBehavior
import dev.kord.core.cache.data.ChannelData
import dev.kord.core.supplier.EntitySupplier
import dev.kord.core.supplier.EntitySupplyStrategy

class VoiceStageChannel(
override val data: ChannelData,
override val kord: Kord,
override val supplier: EntitySupplier = kord.defaultSupplier
) : VoiceStageChannelBehavior {

override val id: Snowflake get() = data.id

override val guildId: Snowflake get() = data.guildId.value!!
override fun withStrategy(strategy: EntitySupplyStrategy<*>): VoiceStageChannelBehavior {
return VoiceStageChannelBehavior(data, kord, strategy.supply(kord))
}


/**
* The bitrate (in bits) of this channel.
*/
val bitrate: Int get() = data.bitrate.getOrThrow()

/**
* The user limit of the voice channel.
*/
val userLimit: Int get() = data.userLimit.getOrThrow()

val topic: String get() = data.topic.value!!
}
38 changes: 38 additions & 0 deletions rest/src/main/kotlin/builder/guild/VoiceStatePatchBuilder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dev.kord.rest.builder.guild

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.delegate.delegate
import dev.kord.rest.builder.RequestBuilder
import dev.kord.rest.json.request.CurrentVoiceStateModifyRequest
import dev.kord.rest.json.request.VoiceStateModifyRequest

class CurrentVoiceStateModifyBuilder(val channelId: Snowflake) : RequestBuilder<CurrentVoiceStateModifyRequest> {

private var _requestToSpeakTimestamp: Optional<String> = Optional.Missing()

private var _suppress: OptionalBoolean = OptionalBoolean.Missing

var requestToSpeakTimestamp: String? by ::_requestToSpeakTimestamp.delegate()

var suppress: Boolean? by ::_suppress.delegate()


override fun toRequest(): CurrentVoiceStateModifyRequest {
return CurrentVoiceStateModifyRequest(channelId, _suppress, _requestToSpeakTimestamp)
}
}


class VoiceStateModifyBuilder(val channelId: Snowflake) : RequestBuilder<VoiceStateModifyRequest> {

private var _suppress: OptionalBoolean = OptionalBoolean.Missing

var suppress: Boolean? by ::_suppress.delegate()

override fun toRequest(): VoiceStateModifyRequest {
return VoiceStateModifyRequest(channelId, _suppress)
}
}

24 changes: 24 additions & 0 deletions rest/src/main/kotlin/json/request/VoiceStateRequests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.kord.rest.json.request

import dev.kord.common.entity.Snowflake
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.OptionalBoolean
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CurrentVoiceStateModifyRequest(
@SerialName("channel_id")
val channelId: Snowflake,
val suppress: OptionalBoolean = OptionalBoolean.Missing,
@SerialName("request_to_speak_timestamp")
val requestToSpeakTimeStamp: Optional<String> = Optional.Missing()
)


@Serializable
data class VoiceStateModifyRequest(
@SerialName("channel_id")
val channelId: Snowflake,
val suppress: OptionalBoolean = OptionalBoolean.Missing
)
8 changes: 7 additions & 1 deletion rest/src/main/kotlin/route/Route.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import dev.kord.common.annotation.KordExperimental
import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.*
import dev.kord.rest.json.optional
import dev.kord.rest.json.request.MessageEditPatchRequest
import dev.kord.rest.json.response.*
import io.ktor.http.*
import kotlinx.serialization.DeserializationStrategy
Expand Down Expand Up @@ -545,6 +544,13 @@ sealed class Route<T>(
object FollowupMessageDelete :
Route<Unit>(HttpMethod.Delete, "/webhooks/${ApplicationId}/${InteractionToken}/messages/${MessageId}", NoStrategy)

object SelfVoiceStatePatch:
Route<Unit>(HttpMethod.Patch, "/guilds/${GuildId}/voice-states/@me", NoStrategy)


object OthersVoiceStatePatch:
Route<Unit>(HttpMethod.Patch, "/guilds/${GuildId}/voice-states/${UserId}", NoStrategy)

companion object {
val baseUrl = "https://discord.com/api/$restVersion"
}
Expand Down
32 changes: 28 additions & 4 deletions rest/src/main/kotlin/service/GuildService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import dev.kord.common.annotation.KordExperimental
import dev.kord.common.entity.*
import dev.kord.rest.builder.ban.BanCreateBuilder
import dev.kord.rest.builder.channel.*
import dev.kord.rest.builder.guild.GuildCreateBuilder
import dev.kord.rest.builder.guild.GuildModifyBuilder
import dev.kord.rest.builder.guild.GuildWidgetModifyBuilder
import dev.kord.rest.builder.guild.WelcomeScreenModifyBuilder
import dev.kord.rest.builder.guild.*
import dev.kord.rest.builder.integration.IntegrationModifyBuilder
import dev.kord.rest.builder.member.MemberAddBuilder
import dev.kord.rest.builder.member.MemberModifyBuilder
Expand Down Expand Up @@ -314,6 +311,18 @@ class GuildService(requestHandler: RequestHandler) : RestService(requestHandler)
body(GuildWelcomeScreenModifyRequest.serializer(), request)
}

suspend fun modifyCurrentVoiceState(guildId: Snowflake, request: CurrentVoiceStateModifyRequest) = call(Route.SelfVoiceStatePatch) {
keys[Route.GuildId] = guildId
body(CurrentVoiceStateModifyRequest.serializer(), request)
}


suspend fun modifyVoiceState(guildId: Snowflake, userId: Snowflake, request: VoiceStateModifyRequest) = call(Route.SelfVoiceStatePatch) {
keys[Route.GuildId] = guildId
keys[Route.UserId] = userId
body(VoiceStateModifyRequest.serializer(), request)
}

}
@OptIn(ExperimentalContracts::class)
suspend inline fun GuildService.modifyGuildWelcomeScreen(guildId: Snowflake, builder: WelcomeScreenModifyBuilder.() -> Unit): DiscordWelcomeScreen {
Expand All @@ -340,3 +349,18 @@ suspend inline fun GuildService.createCategory(guildId: Snowflake, name: String,
val createBuilder = CategoryCreateBuilder(name).apply(builder)
return createGuildChannel(guildId, createBuilder.toRequest(), createBuilder.reason)
}

@OptIn(ExperimentalContracts::class)
suspend inline fun GuildService.modifyCurrentVoiceState(guildId: Snowflake,channelId: Snowflake, builder: CurrentVoiceStateModifyBuilder.() -> Unit) {
contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) }
val modifyBuilder = CurrentVoiceStateModifyBuilder(channelId).apply(builder)
modifyCurrentVoiceState(guildId, modifyBuilder.toRequest())
}


@OptIn(ExperimentalContracts::class)
suspend inline fun GuildService.modifyVoiceState(guildId: Snowflake,channelId: Snowflake, builder: VoiceStateModifyBuilder.() -> Unit) {
contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) }
val modifyBuilder = VoiceStateModifyBuilder(guildId).apply(builder)
modifyVoiceState(guildId,channelId, modifyBuilder.toRequest())
}

0 comments on commit a81e7c4

Please sign in to comment.