Skip to content

Commit

Permalink
Implement slash command permissions (#253)
Browse files Browse the repository at this point in the history
* Implement slash command permissions

* Undo some formatting that should now have happened

* Undo some more formatting

* Implement some requested changes

* Add core representation

* Remove duplicated method

* Change name

* Implement requested changes
- Fix compilation error

* More requested changes

* Add Test
  • Loading branch information
DRSchlaubi authored Apr 18, 2021
1 parent b82cb7d commit ec02f48
Show file tree
Hide file tree
Showing 13 changed files with 406 additions and 20 deletions.
54 changes: 53 additions & 1 deletion common/src/main/kotlin/entity/Interactions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package dev.kord.common.entity

import dev.kord.common.annotation.KordExperimental
import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.optional.*
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.OptionalBoolean
import dev.kord.common.entity.optional.OptionalSnowflake
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand All @@ -26,6 +28,8 @@ data class DiscordApplicationCommand(
@SerialName("guild_id")
val guildId: OptionalSnowflake = OptionalSnowflake.Missing,
val options: Optional<List<ApplicationCommandOption>> = Optional.Missing(),
@SerialName("default_permission")
val defaultPermission: OptionalBoolean = OptionalBoolean.Missing
)

@Serializable
Expand Down Expand Up @@ -395,3 +399,51 @@ sealed class InteractionResponseType(val type: Int) {

}
}


@KordPreview
@Serializable
data class DiscordGuildApplicationCommandPermissions(
val id: Snowflake,
@SerialName("application_id")
val applicationId: Snowflake,
@SerialName("guild_id")
val guildId: Snowflake,
val permissions: List<DiscordGuildApplicationCommandPermission>
)

@KordPreview
@Serializable
data class PartialDiscordGuildApplicationCommandPermissions(
val id: Snowflake,
val permissions: List<DiscordGuildApplicationCommandPermission>
)

@KordPreview
@Serializable
data class DiscordGuildApplicationCommandPermission(
val id: Snowflake,
val type: Type,
val permission: Boolean
) {
@Serializable(with = Type.Serializer::class)
sealed class Type(val value: Int) {
object Role : Type(1)
object User : Type(2)
class Unknown(value: Int) : Type(value)

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

override fun deserialize(decoder: Decoder): Type =
when (val value = decoder.decodeInt()) {
1 -> Role
2 -> User
else -> Unknown(value)
}

override fun serialize(encoder: Encoder, value: Type) = encoder.encodeInt(value.value)
}
}
}
19 changes: 19 additions & 0 deletions common/src/test/kotlin/json/InteractionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,23 @@ class InteractionTest {
arg.value.string() shouldBe "1"
}
}

@Test
fun `slash command permissions can be serialized`() {
val text = file("slash_command_permissions_update")

val interaction = json.decodeFromString(DiscordGuildApplicationCommandPermissions.serializer(), text)

with(interaction) {
id shouldBe "833008574669651989"
applicationId shouldBe "535129406650318860"
guildId shouldBe "809471441719787602"

with(permissions.first()) {
id shouldBe "827126703301066792"
type shouldBe DiscordGuildApplicationCommandPermission.Type.Role
permission shouldBe true
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "833008574669651989",
"application_id": "535129406650318860",
"guild_id": "809471441719787602",
"permissions": [
{
"id": "827126703301066792",
"type": 1,
"permission": true
}
]
}
46 changes: 46 additions & 0 deletions core/src/main/kotlin/SlashCommands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package dev.kord.core
import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.Snowflake
import dev.kord.core.cache.data.ApplicationCommandData
import dev.kord.core.cache.data.GuildApplicationCommandPermissionsData
import dev.kord.core.entity.interaction.GlobalApplicationCommand
import dev.kord.core.entity.interaction.GuildApplicationCommand
import dev.kord.core.entity.interaction.ApplicationCommandPermissions
import dev.kord.rest.builder.interaction.ApplicationCommandCreateBuilder
import dev.kord.rest.builder.interaction.ApplicationCommandPermissionsBulkModifyBuilder
import dev.kord.rest.builder.interaction.ApplicationCommandPermissionsModifyBuilder
import dev.kord.rest.builder.interaction.ApplicationCommandsCreateBuilder
import dev.kord.rest.request.RequestHandler
import dev.kord.rest.service.InteractionService
Expand Down Expand Up @@ -112,6 +116,48 @@ class SlashCommands(
emit(GlobalApplicationCommand(data, service))
}
}

suspend fun getGuildApplicationCommandPermissions(
applicationId: Snowflake,
guildId: Snowflake,
): ApplicationCommandPermissions {
val permissions = service.getGuildApplicationCommandPermissions(applicationId, guildId)
val data = GuildApplicationCommandPermissionsData.from(permissions)

return ApplicationCommandPermissions(data)
}

suspend fun getApplicationCommandPermissions(
applicationId: Snowflake,
guildId: Snowflake,
commandId: Snowflake,
): ApplicationCommandPermissions {
val permissions = service.getApplicationCommandPermissions(applicationId, guildId, commandId)
val data = GuildApplicationCommandPermissionsData.from(permissions)

return ApplicationCommandPermissions(data)
}

suspend fun editApplicationCommandPermissions(
applicationId: Snowflake,
guildId: Snowflake,
commandId: Snowflake,
builder: ApplicationCommandPermissionsModifyBuilder.() -> Unit,
) {
val request = ApplicationCommandPermissionsModifyBuilder().apply(builder).toRequest()

service.editApplicationCommandPermissions(applicationId, guildId, commandId, request)
}

suspend fun bulkEditApplicationCommandPermissions(
applicationId: Snowflake,
guildId: Snowflake,
builder: ApplicationCommandPermissionsBulkModifyBuilder.() -> Unit,
) {
val request = ApplicationCommandPermissionsBulkModifyBuilder().apply(builder).toRequest()

service.bulkEditApplicationCommandPermissions(applicationId, guildId, request)
}
}

@KordPreview
Expand Down
19 changes: 15 additions & 4 deletions core/src/main/kotlin/behavior/GlobalApplicationCommandBehavior.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@ import dev.kord.core.entity.interaction.ApplicationCommand
import dev.kord.core.entity.interaction.GlobalApplicationCommand
import dev.kord.core.entity.interaction.GuildApplicationCommand
import dev.kord.rest.builder.interaction.ApplicationCommandModifyBuilder
import dev.kord.rest.builder.interaction.ApplicationCommandPermissionsModifyBuilder
import dev.kord.rest.request.RestRequestException
import dev.kord.rest.service.InteractionService
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.Throws

@KordPreview
interface ApplicationCommandBehavior : Entity {
Expand All @@ -37,6 +34,7 @@ interface ApplicationCommandBehavior : Entity {
* @throws [RestRequestException] when something goes wrong during the request.
*/
suspend fun delete()

}

@KordPreview
Expand All @@ -52,6 +50,19 @@ interface GlobalApplicationCommandBehavior : ApplicationCommandBehavior {
override suspend fun delete() {
service.deleteGlobalApplicationCommand(applicationId, id)
}

/**
* Updates the permissions for this command on the guild corresponding to [guildId].
*
* @throws [RestRequestException] when something goes wrong during the request.
*/
suspend fun editPermissions(
guildId: Snowflake,
builder: ApplicationCommandPermissionsModifyBuilder.() -> Unit
) {
val request = ApplicationCommandPermissionsModifyBuilder().apply(builder).toRequest()
service.editApplicationCommandPermissions(applicationId, guildId, id, request)
}
}

@KordPreview
Expand Down
15 changes: 15 additions & 0 deletions core/src/main/kotlin/behavior/GuildBehavior.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ 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.interaction.ApplicationCommandCreateBuilder
import dev.kord.rest.builder.interaction.ApplicationCommandPermissionsBulkModifyBuilder
import dev.kord.rest.builder.interaction.ApplicationCommandsCreateBuilder
import dev.kord.rest.builder.role.RoleCreateBuilder
import dev.kord.rest.builder.role.RolePositionsModifyBuilder
Expand Down Expand Up @@ -925,3 +926,17 @@ inline fun GuildBehavior.requestMembers(builder: RequestGuildMembersBuilder.() -
val request = RequestGuildMembersBuilder(id).apply(builder).toRequest()
return requestMembers(request)
}

@OptIn(ExperimentalContracts::class)
@KordPreview
suspend inline fun GuildBehavior.bulkEditSlashCommandPermissions(noinline builder: ApplicationCommandPermissionsBulkModifyBuilder.() -> Unit) {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}

kord.slashCommands.bulkEditApplicationCommandPermissions(
kord.selfId,
id,
builder
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.kord.core.cache.data

import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.DiscordGuildApplicationCommandPermission
import dev.kord.common.entity.Snowflake

@KordPreview
data class GuildApplicationCommandPermissionData(
val id: Snowflake,
val type: DiscordGuildApplicationCommandPermission.Type,
val permission: Boolean
) {
companion object {
fun from(permission: DiscordGuildApplicationCommandPermission) = with(permission) {
GuildApplicationCommandPermissionData(id, type, this.permission)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.kord.core.cache.data

import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.DiscordGuildApplicationCommandPermissions
import dev.kord.common.entity.Snowflake

@KordPreview
data class GuildApplicationCommandPermissionsData(
val id: Snowflake,
val applicationId: Snowflake,
val guildId: Snowflake,
val permissions: List<GuildApplicationCommandPermissionData>
) {

companion object {
fun from(permissions: DiscordGuildApplicationCommandPermissions) = with(permissions) {
GuildApplicationCommandPermissionsData(
id,
applicationId,
guildId,
this.permissions.map(GuildApplicationCommandPermissionData::from)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.kord.core.entity.interaction

import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.DiscordGuildApplicationCommandPermission
import dev.kord.common.entity.Snowflake
import dev.kord.core.cache.data.GuildApplicationCommandPermissionData
import dev.kord.core.cache.data.GuildApplicationCommandPermissionsData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

@KordPreview
class GuildApplicationCommandPermission(val data: GuildApplicationCommandPermissionData) {

val id: Snowflake get() = data.id

val type: DiscordGuildApplicationCommandPermission.Type get() = data.type

val permission: Boolean get() = data.permission
}

@KordPreview
class ApplicationCommandPermissions(val data: GuildApplicationCommandPermissionsData) {
val id: Snowflake get() = data.id

val applicationId: Snowflake get() = data.applicationId

val guildId: Snowflake get() = data.guildId

val permissions: Flow<GuildApplicationCommandPermission>
get() = flow {
data.permissions.forEach { emit(GuildApplicationCommandPermission(it)) }
}
}
Loading

0 comments on commit ec02f48

Please sign in to comment.