Skip to content

Commit

Permalink
Ephemeral embed support (#296)
Browse files Browse the repository at this point in the history
* Fix return type of channel in VoiceState

* support embeds in ephemeral messages

* supply modify request with embeds

* Common builders for interaction response

needs more work

* Common builders for interaction response

needs more work

* Seal base builders for interaction response

* make embeds list final

* make allowedMentions additive

* Fix tests

* revert tests

* Fix stream closed exception

I still haven't managed to know the root of the problem but changing the append process fixed it...

* use readBytes for Java 8 compatibility
  • Loading branch information
HopeBaron committed Jun 24, 2021
1 parent 75705dd commit 0e795d3
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 135 deletions.
66 changes: 6 additions & 60 deletions rest/src/main/kotlin/builder/interaction/Base.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package dev.kord.rest.builder.interaction

import dev.kord.common.annotation.KordPreview
import dev.kord.rest.builder.RequestBuilder
import dev.kord.rest.builder.component.ActionRowBuilder
import dev.kord.rest.builder.component.MessageComponentBuilder
import dev.kord.rest.builder.message.AllowedMentionsBuilder
import dev.kord.rest.builder.message.EmbedBuilder
import dev.kord.rest.json.request.MultipartInteractionResponseCreateRequest
Expand All @@ -13,86 +11,34 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

sealed interface BaseInteractionResponseBuilder<T> : RequestBuilder<T> {

var content: String?

val embeds: MutableList<EmbedBuilder>?

val components: MutableList<MessageComponentBuilder>?
val embeds: MutableList<EmbedBuilder>

var allowedMentions: AllowedMentionsBuilder?


}

@KordPreview
@OptIn(ExperimentalContracts::class, KordPreview::class)
@OptIn(ExperimentalContracts::class)
inline fun <T> BaseInteractionResponseBuilder<T>.embed(builder: EmbedBuilder.() -> Unit) {
contract { callsInPlace(builder, InvocationKind.EXACTLY_ONCE) }

when(this){
is BaseInteractionResponseCreateBuilder -> {
embeds.add(EmbedBuilder().apply(builder))
}
is BaseInteractionResponseModifyBuilder -> {
embeds = (embeds ?: mutableListOf()).also {
it.add(EmbedBuilder().apply(builder))
}
}
}
embeds += EmbedBuilder().apply(builder)
}

/**
* Configures the mentions that should trigger a mention (aka ping). Not calling this function will result in the default behavior
* (ping everything), calling this function but not configuring it before the request is build will result in all
* pings being ignored.
*/
@KordPreview
@OptIn(ExperimentalContracts::class)
inline fun <T> BaseInteractionResponseBuilder<T>.allowedMentions(block: AllowedMentionsBuilder.() -> Unit = {}) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
if (allowedMentions == null) allowedMentions = AllowedMentionsBuilder()
if(allowedMentions == null) allowedMentions = AllowedMentionsBuilder()
allowedMentions!!.apply(block)
}


@OptIn(ExperimentalContracts::class)
@KordPreview
inline fun <T> BaseInteractionResponseBuilder<T>.actionRow(builder: ActionRowBuilder.() -> Unit) {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}

when (this) {
is BaseInteractionResponseCreateBuilder -> {
components.add(ActionRowBuilder().apply(builder))
}
is BaseInteractionResponseModifyBuilder -> {
components = (components ?: mutableListOf()).also {
it.add(ActionRowBuilder().apply(builder))
}
}
}

}


interface BaseInteractionResponseCreateBuilder : BaseInteractionResponseBuilder<MultipartInteractionResponseCreateRequest>
@KordPreview
interface BaseInteractionResponseCreateBuilder :
BaseInteractionResponseBuilder<MultipartInteractionResponseCreateRequest> {

override val components: MutableList<MessageComponentBuilder>

override val embeds: MutableList<EmbedBuilder>

}

@KordPreview
interface BaseInteractionResponseModifyBuilder :
BaseInteractionResponseBuilder<MultipartInteractionResponseModifyRequest> {

override var components: MutableList<MessageComponentBuilder>?

override var embeds: MutableList<EmbedBuilder>?

}
interface BaseInteractionResponseModifyBuilder : BaseInteractionResponseBuilder<MultipartInteractionResponseModifyRequest>
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
package dev.kord.rest.builder.interaction

import dev.kord.common.annotation.KordDsl
import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.InteractionResponseType
import dev.kord.common.entity.MessageFlag
import dev.kord.common.entity.MessageFlags
import dev.kord.common.entity.optional.*
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.delegate.delegate
import dev.kord.rest.builder.component.ComponentBuilder
import dev.kord.rest.builder.component.MessageComponentBuilder
import dev.kord.common.entity.optional.map
import dev.kord.common.entity.optional.mapList
import dev.kord.common.entity.optional.optional
import dev.kord.rest.builder.message.AllowedMentionsBuilder
import dev.kord.rest.builder.message.EmbedBuilder
import dev.kord.rest.json.request.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

@KordDsl
@KordPreview
class EphemeralInteractionResponseModifyBuilder : BaseInteractionResponseModifyBuilder {
private var _content: Optional<String?> = Optional.Missing()
private var _content: Optional<String> = Optional.Missing()
override var content: String? by ::_content.delegate()

private var _embeds: Optional<MutableList<EmbedBuilder>> = Optional.Missing()
override var embeds: MutableList<EmbedBuilder>? by ::_embeds.delegate()
override val embeds: MutableList<EmbedBuilder> = mutableListOf()

private var _allowedMentions: Optional<AllowedMentionsBuilder?> = Optional.Missing()
override var allowedMentions: AllowedMentionsBuilder? by ::_allowedMentions.delegate()

private var _components: Optional<MutableList<MessageComponentBuilder>> = Optional.Missing()
override var components: MutableList<MessageComponentBuilder>? by ::_components.delegate()
private var _allowedMentions: Optional<AllowedMentionsBuilder> = Optional.Missing()
override var allowedMentions: AllowedMentionsBuilder? by ::_allowedMentions.delegate()

override fun toRequest(): MultipartInteractionResponseModifyRequest {
return MultipartInteractionResponseModifyRequest(
InteractionResponseModifyRequest(
content = _content,
allowedMentions = _allowedMentions.map { it.build() },
components = _components.mapList { it.build() },
embeds = _embeds.mapList { it.toRequest() },
embeds = embeds.map { it.toRequest() }
)
)

}
}

@KordDsl

@KordPreview
class EphemeralInteractionResponseCreateBuilder : BaseInteractionResponseCreateBuilder {
private var _content: Optional<String> = Optional.Missing()
Expand All @@ -49,8 +48,6 @@ class EphemeralInteractionResponseCreateBuilder : BaseInteractionResponseCreateB

override val embeds: MutableList<EmbedBuilder> = mutableListOf()

override val components: MutableList<MessageComponentBuilder> = mutableListOf()


private var _allowedMentions: Optional<AllowedMentionsBuilder> = Optional.Missing()
override var allowedMentions: AllowedMentionsBuilder? by ::_allowedMentions.delegate()
Expand All @@ -64,13 +61,11 @@ class EphemeralInteractionResponseCreateBuilder : BaseInteractionResponseCreateB
val data = InteractionApplicationCommandCallbackData(
content = _content,
flags = flags,
embeds = Optional.missingOnEmpty(embeds.map(EmbedBuilder::toRequest)),
components = Optional.missingOnEmpty(components.map(ComponentBuilder::build))
embeds = embeds.map { it.toRequest() }
)

return MultipartInteractionResponseCreateRequest(
InteractionResponseCreateRequest(type, data.optional())
)

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package dev.kord.rest.builder.interaction
import dev.kord.common.annotation.KordDsl
import dev.kord.common.annotation.KordPreview
import dev.kord.common.entity.InteractionResponseType
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.delegate.delegate
import dev.kord.rest.builder.component.MessageComponentBuilder
import dev.kord.common.entity.optional.map
import dev.kord.common.entity.optional.optional
import dev.kord.rest.builder.message.AllowedMentionsBuilder
import dev.kord.rest.builder.message.EmbedBuilder
import dev.kord.rest.json.request.*
Expand All @@ -18,7 +20,7 @@ import java.nio.file.Path
@KordPreview
@KordDsl
class PublicInteractionResponseCreateBuilder :
BaseInteractionResponseCreateBuilder {
BaseInteractionResponseCreateBuilder {
private var _content: Optional<String> = Optional.Missing()
override var content: String? by ::_content.delegate()

Expand All @@ -31,8 +33,6 @@ class PublicInteractionResponseCreateBuilder :
private var _tts: OptionalBoolean = OptionalBoolean.Missing
var tts: Boolean? by ::_tts.delegate()

override val components: MutableList<MessageComponentBuilder> = mutableListOf()

val files: MutableList<Pair<String, InputStream>> = mutableListOf()


Expand All @@ -51,13 +51,12 @@ class PublicInteractionResponseCreateBuilder :

return MultipartInteractionResponseCreateRequest(
InteractionResponseCreateRequest(
type,
type,
InteractionApplicationCommandCallbackData(
content = _content,
embeds = Optional.missingOnEmpty(embeds.map { it.toRequest() }),
embeds = embeds.map { it.toRequest() },
allowedMentions = _allowedMentions.map { it.build() },
tts = _tts,
components = Optional.missingOnEmpty(components.map { it.build() })
tts = _tts
).optional()
),
files
Expand All @@ -70,20 +69,16 @@ class PublicInteractionResponseCreateBuilder :
@KordDsl
class PublicInteractionResponseModifyBuilder :
BaseInteractionResponseModifyBuilder {
private var _content: Optional<String?> = Optional.Missing()
private var _content: Optional<String> = Optional.Missing()
override var content: String? by ::_content.delegate()

private var _embeds: Optional<MutableList<EmbedBuilder>> = Optional.Missing()
override var embeds: MutableList<EmbedBuilder>? by ::_embeds.delegate()
override var embeds: MutableList<EmbedBuilder> = mutableListOf()

private var _allowedMentions: Optional<AllowedMentionsBuilder?> = Optional.Missing()
private var _allowedMentions: Optional<AllowedMentionsBuilder> = Optional.Missing()
override var allowedMentions: AllowedMentionsBuilder? by ::_allowedMentions.delegate()

val files: MutableList<Pair<String, InputStream>> = mutableListOf()

private var _components: Optional<MutableList<MessageComponentBuilder>> = Optional.Missing()
override var components: MutableList<MessageComponentBuilder>? by ::_components.delegate()

fun addFile(name: String, content: InputStream) {
files += name to content
}
Expand All @@ -96,9 +91,8 @@ class PublicInteractionResponseModifyBuilder :
return MultipartInteractionResponseModifyRequest(
InteractionResponseModifyRequest(
content = _content,
embeds = Optional(embeds).coerceToMissing().mapList { it.toRequest() },
embeds = embeds.map { it.toRequest() },
allowedMentions = _allowedMentions.map { it.build() },
components = Optional(components).coerceToMissing().mapList { it.build() },
),
files
)
Expand Down
25 changes: 11 additions & 14 deletions rest/src/main/kotlin/json/request/InteractionsRequests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ data class ApplicationCommandModifyRequest(
@Serializable
@KordPreview
data class InteractionResponseModifyRequest(
val content: Optional<String?> = Optional.Missing(),
val embeds: Optional<List<EmbedRequest>?> = Optional.Missing(),
val content: Optional<String> = Optional.Missing(),
val embeds: List<EmbedRequest> = emptyList(),
@SerialName("allowed_mentions")
val allowedMentions: Optional<AllowedMentions?> = Optional.Missing(),
val flags: Optional<MessageFlags?> = Optional.Missing(),
val components: Optional<List<DiscordComponent>?> = Optional.Missing()
val allowedMentions: Optional<AllowedMentions> = Optional.Missing(),
val flags: Optional<MessageFlags> = Optional.Missing()
)

@KordPreview
Expand All @@ -63,11 +62,11 @@ data class MultipartInteractionResponseCreateRequest(
class InteractionApplicationCommandCallbackData(
val tts: OptionalBoolean = OptionalBoolean.Missing,
val content: Optional<String> = Optional.Missing(),
val embeds: Optional<List<EmbedRequest>> = Optional.Missing(),
val embeds: List<EmbedRequest> = emptyList(),
@SerialName("allowed_mentions")
val allowedMentions: Optional<AllowedMentions> = Optional.Missing(),
val flags: Optional<MessageFlags> = Optional.Missing(),
val components: Optional<List<DiscordComponent>> = Optional.Missing()
val flags: Optional<MessageFlags> = Optional.Missing()

)

@KordPreview
Expand All @@ -85,18 +84,16 @@ class FollowupMessageCreateRequest(
val avatar: Optional<String> = Optional.Missing(),
val tts: OptionalBoolean = OptionalBoolean.Missing,
val embeds: Optional<List<EmbedRequest>> = Optional.Missing(),
val allowedMentions: Optional<AllowedMentions> = Optional.Missing(),
val components: Optional<List<DiscordComponent>> = Optional.Missing()
val allowedMentions: Optional<AllowedMentions> = Optional.Missing()
)

@Serializable
@KordPreview
data class FollowupMessageModifyRequest(
val content: Optional<String?> = Optional.Missing(),
val embeds: Optional<List<EmbedRequest>?> = Optional.Missing(),
val content: Optional<String> = Optional.Missing(),
val embeds: Optional<List<EmbedRequest>> = Optional.Missing(),
@SerialName("allowed_mentions")
val allowedMentions: Optional<AllowedMentions?> = Optional.Missing(),
val components: Optional<List<DiscordComponent>?> = Optional.Missing()
val allowedMentions: Optional<AllowedMentions> = Optional.Missing(),
)

@KordPreview
Expand Down
21 changes: 11 additions & 10 deletions rest/src/main/kotlin/request/Request.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package dev.kord.rest.request

import dev.kord.rest.route.Route
import io.ktor.client.request.forms.append
import io.ktor.client.request.forms.formData
import io.ktor.http.encodeURLQueryComponent
import io.ktor.util.StringValues
import io.ktor.utils.io.streams.outputStream
import io.ktor.client.request.forms.*
import io.ktor.http.*
import io.ktor.util.*
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.json.Json

Expand Down Expand Up @@ -83,16 +81,19 @@ class MultipartRequest<B : Any, R>(
append("payload_json", Json.encodeToString(it.strategy, it.body))
}
try {
if (files.size == 1) append("file", filename = files[0].first) {
files[0].second.copyTo(outputStream())
} else files.forEachIndexed { index, pair ->

files.forEachIndexed { index, pair ->
val name = pair.first
val inputStream = pair.second
append("file$index", name) { inputStream.copyTo(outputStream()) }
append(
"file$index",
inputStream.readBytes(),
Headers.build { append(HttpHeaders.ContentDisposition, "filename=$name") }
)
}
} finally {
files.forEach { it.second.close() }
}
}

}

Loading

0 comments on commit 0e795d3

Please sign in to comment.