Skip to content

Commit

Permalink
Add New webhook endpoints (discord/discord-api-docs#2243)
Browse files Browse the repository at this point in the history
Relates to #121
  • Loading branch information
DRSchlaubi committed Mar 25, 2021
1 parent d17c312 commit 64c299d
Show file tree
Hide file tree
Showing 8 changed files with 499 additions and 124 deletions.
81 changes: 71 additions & 10 deletions core/src/main/kotlin/behavior/MessageBehavior.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import dev.kord.core.supplier.getChannelOf
import dev.kord.core.supplier.getChannelOfOrNull
import dev.kord.rest.builder.message.MessageCreateBuilder
import dev.kord.rest.builder.message.MessageModifyBuilder
import dev.kord.rest.builder.webhook.EditWebhookMessageBuilder
import dev.kord.rest.request.RestRequestException
import dev.kord.rest.service.RestClient
import kotlinx.coroutines.flow.Flow
Expand All @@ -39,6 +40,8 @@ interface MessageBehavior : KordEntity, Strategizable {
*/
val channel get() = MessageChannelBehavior(channelId, kord)

val webhookId: Snowflake?

/**
* Requests to get the channel this message was send in.
*/
Expand All @@ -60,7 +63,8 @@ interface MessageBehavior : KordEntity, Strategizable {
*
* @throws [RequestException] if anything went wrong during the request.
*/
suspend fun asMessageOrNull(): Message? = supplier.getMessageOrNull(channelId = channelId, messageId = id)
suspend fun asMessageOrNull(): Message? =
supplier.getMessageOrNull(channelId = channelId, messageId = id)


/**
Expand All @@ -82,15 +86,19 @@ interface MessageBehavior : KordEntity, Strategizable {
*/

fun getReactors(emoji: ReactionEmoji): Flow<User> =
kord.with(EntitySupplyStrategy.rest).getReactors(channelId, id, emoji)
kord.with(EntitySupplyStrategy.rest).getReactors(channelId, id, emoji)

/**
* Requests to add an [emoji] to this message.
*
* @throws [RestRequestException] if something went wrong during the request.
*/
suspend fun addReaction(emoji: ReactionEmoji) {
kord.rest.channel.createReaction(channelId = channelId, messageId = id, emoji = emoji.urlFormat)
kord.rest.channel.createReaction(
channelId = channelId,
messageId = id,
emoji = emoji.urlFormat
)
}

/**
Expand Down Expand Up @@ -126,7 +134,12 @@ interface MessageBehavior : KordEntity, Strategizable {
* @throws [RestRequestException] if something went wrong during the request.
*/
suspend fun deleteReaction(userId: Snowflake, emoji: ReactionEmoji) {
kord.rest.channel.deleteReaction(channelId = channelId, messageId = id, userId = userId, emoji = emoji.urlFormat)
kord.rest.channel.deleteReaction(
channelId = channelId,
messageId = id,
userId = userId,
emoji = emoji.urlFormat
)
}

/**
Expand All @@ -135,7 +148,11 @@ interface MessageBehavior : KordEntity, Strategizable {
* @throws [RestRequestException] if something went wrong during the request.
*/
suspend fun deleteOwnReaction(emoji: ReactionEmoji) {
kord.rest.channel.deleteOwnReaction(channelId = channelId, messageId = id, emoji = emoji.urlFormat)
kord.rest.channel.deleteOwnReaction(
channelId = channelId,
messageId = id,
emoji = emoji.urlFormat
)
}

/**
Expand All @@ -153,7 +170,11 @@ interface MessageBehavior : KordEntity, Strategizable {
* @throws [RestRequestException] if something went wrong during the request.
*/
suspend fun deleteReaction(emoji: ReactionEmoji) {
kord.rest.channel.deleteAllReactionsForEmoji(channelId = channelId, messageId = id, emoji = emoji.urlFormat)
kord.rest.channel.deleteAllReactionsForEmoji(
channelId = channelId,
messageId = id,
emoji = emoji.urlFormat
)
}

/**
Expand All @@ -178,20 +199,22 @@ interface MessageBehavior : KordEntity, Strategizable {
* Returns a new [MessageBehavior] with the given [strategy].
*/
override fun withStrategy(
strategy: EntitySupplyStrategy<*>,
): MessageBehavior = MessageBehavior(channelId, id, kord, strategy)
strategy: EntitySupplyStrategy<*>,
): MessageBehavior = MessageBehavior(channelId, id, kord, webhookId, strategy)

}

fun MessageBehavior(
fun MessageBehavior(
channelId: Snowflake,
messageId: Snowflake,
kord: Kord,
webhookId: Snowflake? = null,
strategy: EntitySupplyStrategy<*> = kord.resources.defaultStrategy,
) = object : MessageBehavior {
override val channelId: Snowflake = channelId
override val id: Snowflake = messageId
override val kord: Kord = kord
override val webhookId: Snowflake? = webhookId
override val supplier: EntitySupplier = strategy.supply(kord)

override fun hashCode(): Int = Objects.hash(id)
Expand All @@ -212,13 +235,51 @@ interface MessageBehavior : KordEntity, Strategizable {
* @return The edited [Message].
*
* @throws [RestRequestException] if something went wrong during the request.
* @throws [IllegalStateException] if this message is a webhook message (See [editWebhookMessage]).
* @see editWebhookMessage
*/
@OptIn(ExperimentalContracts::class)
suspend inline fun MessageBehavior.edit(builder: MessageModifyBuilder.() -> Unit): Message {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}
val response = kord.rest.channel.editMessage(channelId = channelId, messageId = id, builder = builder)

require(webhookId == null) { "Cannot perform edits on webhook messages use editWebhook() instead" }

val response =
kord.rest.channel.editMessage(channelId = channelId, messageId = id, builder = builder)
val data = MessageData.from(response)

return Message(data, kord)
}

/**
* Requests to edit this message.
*
* @return The edited [Message].
*
* @throws [RestRequestException] if something went wrong during the request.
* @throws [IllegalStateException] if this message is a normal message (see [edit]).
* @see edit
*/
@OptIn(ExperimentalContracts::class)
suspend inline fun MessageBehavior.editWebhookMessage(
token: String,
builder: EditWebhookMessageBuilder.() -> Unit
): Message {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}

require(webhookId != null) { "Cannot perform edits on non webhook messages use edit() instead" }

val response =
kord.rest.webhook.editWebhookMessage(
webhookId = webhookId!!,
messageId = id,
token = token,
builder = builder
)
val data = MessageData.from(response)

return Message(data, kord)
Expand Down
43 changes: 30 additions & 13 deletions core/src/main/kotlin/entity/Message.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import java.util.*
* An instance of a [Discord Message][https://discord.com/developers/docs/resources/channel#message-object].
*/
class Message(
val data: MessageData,
override val kord: Kord,
override val supplier: EntitySupplier = kord.defaultSupplier,
val data: MessageData,
override val kord: Kord,
override val supplier: EntitySupplier = kord.defaultSupplier,
) : MessageBehavior {

/**
Expand All @@ -47,7 +47,8 @@ class Message(
/**
* The files attached to this message.
*/
val attachments: Set<Attachment> get() = data.attachments.asSequence().map { Attachment(it, kord) }.toSet()
val attachments: Set<Attachment>
get() = data.attachments.asSequence().map { Attachment(it, kord) }.toSet()

/**
* The author of this message, if it was created by a [User].
Expand Down Expand Up @@ -86,20 +87,23 @@ class Message(
* This collection can only contain values on crossposted messages, channels
* mentioned inside the same guild will not be present.
*/
val mentionedChannelIds: Set<Snowflake> get() = data.mentionedChannels.orEmpty().map { it }.toSet()
val mentionedChannelIds: Set<Snowflake>
get() = data.mentionedChannels.orEmpty().map { it }.toSet()

/**
* The [Channels][ChannelBehavior] specifically mentioned in this message.
*
* This collection can only contain values on crossposted messages, channels
* mentioned inside the same guild will not be present.
*/
val mentionedChannelBehaviors: Set<ChannelBehavior> get() = data.mentionedChannels.orEmpty().map { ChannelBehavior(it, kord) }.toSet()
val mentionedChannelBehaviors: Set<ChannelBehavior>
get() = data.mentionedChannels.orEmpty().map { ChannelBehavior(it, kord) }.toSet()

/**
* The stickers sent with this message.
*/
val stickers: List<MessageSticker> get() = data.stickers.orEmpty().map { MessageSticker(it, kord) }
val stickers: List<MessageSticker>
get() = data.stickers.orEmpty().map { MessageSticker(it, kord) }

/**
* The message being replied to.
Expand Down Expand Up @@ -160,7 +164,13 @@ class Message(
/**
* The [Behaviors][UserBehavior] of users mentioned in this message.
*/
val mentionedUserBehaviors: Set<UserBehavior> get() = data.mentions.map { UserBehavior(it, kord) }.toSet()
val mentionedUserBehaviors: Set<UserBehavior>
get() = data.mentions.map {
UserBehavior(
it,
kord
)
}.toSet()

/**
* The [users][User] mentioned in this message.
Expand All @@ -182,12 +192,17 @@ class Message(
/**
* The reactions to this message.
*/
val reactions: Set<Reaction> get() = data.reactions.orEmpty().asSequence().map { Reaction(it, kord) }.toSet()
val reactions: Set<Reaction>
get() = data.reactions.orEmpty().asSequence().map { Reaction(it, kord) }.toSet()

/**
* The instant when this message was created.
*/
val timestamp: Instant get() = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(data.timestamp, Instant::from)
val timestamp: Instant
get() = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(
data.timestamp,
Instant::from
)

/**
* Whether this message was send using `\tts`.
Expand All @@ -204,7 +219,7 @@ class Message(
*
* Returns null if this message was not send using a webhook.
*/
val webhookId: Snowflake? get() = data.webhookId.value
override val webhookId: Snowflake? get() = data.webhookId.value

/**
* Returns itself.
Expand Down Expand Up @@ -237,12 +252,14 @@ class Message(
*
* @throws [RequestException] if anything went wrong during the request.
*/
suspend fun getGuildOrNull(): Guild? = supplier.getChannelOfOrNull<GuildChannel>(channelId)?.getGuildOrNull()
suspend fun getGuildOrNull(): Guild? =
supplier.getChannelOfOrNull<GuildChannel>(channelId)?.getGuildOrNull()

/**
* Returns a new [Message] with the given [strategy].
*/
override fun withStrategy(strategy: EntitySupplyStrategy<*>): Message = Message(data, kord, strategy.supply(kord))
override fun withStrategy(strategy: EntitySupplyStrategy<*>): Message =
Message(data, kord, strategy.supply(kord))

override fun hashCode(): Int = Objects.hash(id)

Expand Down
13 changes: 8 additions & 5 deletions rest/src/main/kotlin/builder/message/MessageModifyBuilder.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package dev.kord.rest.builder.message

import dev.kord.common.entity.UserFlags
import dev.kord.common.annotation.KordDsl
import dev.kord.common.entity.MessageFlags
import dev.kord.common.entity.UserFlags
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.delegate.delegate
import dev.kord.common.entity.optional.map
import dev.kord.common.entity.optional.mapNullable
import dev.kord.rest.builder.RequestBuilder
import dev.kord.rest.json.request.MessageEditPatchRequest
Expand All @@ -21,8 +21,8 @@ class MessageModifyBuilder : RequestBuilder<MessageEditPatchRequest> {
private var _embed: Optional<EmbedBuilder?> = Optional.Missing()
var embed: EmbedBuilder? by ::_embed.delegate()

private var _flags: Optional<UserFlags?> = Optional.Missing()
var flags: UserFlags? by ::_flags.delegate()
private var _flags: Optional<MessageFlags?> = Optional.Missing()
var flags: MessageFlags? by ::_flags.delegate()

private var _allowedMentions: Optional<AllowedMentionsBuilder?> = Optional.Missing()
var allowedMentions: AllowedMentionsBuilder? by ::_allowedMentions.delegate()
Expand Down Expand Up @@ -50,6 +50,9 @@ class MessageModifyBuilder : RequestBuilder<MessageEditPatchRequest> {


override fun toRequest(): MessageEditPatchRequest = MessageEditPatchRequest(
_content, _embed.mapNullable { it?.toRequest() }, _flags, _allowedMentions.mapNullable { it?.build() }
_content,
_embed.mapNullable { it?.toRequest() },
_flags,
_allowedMentions.mapNullable { it?.build() }
)
}
35 changes: 35 additions & 0 deletions rest/src/main/kotlin/builder/webhook/EditWebhookMessageBuilder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package dev.kord.rest.builder.webhook

import dev.kord.common.entity.AllowedMentions
import dev.kord.common.entity.optional.Optional
import dev.kord.common.entity.optional.delegate.delegate
import dev.kord.rest.builder.RequestBuilder
import dev.kord.rest.builder.message.EmbedBuilder
import dev.kord.rest.json.request.EmbedRequest
import dev.kord.rest.json.request.WebhookEditMessageRequest
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

class EditWebhookMessageBuilder : RequestBuilder<WebhookEditMessageRequest> {

private var _content: Optional<String> = Optional.Missing()
var content: String? by ::_content.delegate()

var embeds: MutableList<EmbedRequest> = mutableListOf()

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

@OptIn(ExperimentalContracts::class)
inline fun embed(builder: EmbedBuilder.() -> Unit) {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}
embeds.add(EmbedBuilder().apply(builder).toRequest())
}

override fun toRequest(): WebhookEditMessageRequest = WebhookEditMessageRequest(
_content, Optional.missingOnEmpty(embeds), _allowedMentions
)
}
2 changes: 1 addition & 1 deletion rest/src/main/kotlin/json/request/MessageRequests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ data class EmbedFieldRequest(
data class MessageEditPatchRequest(
val content: Optional<String?> = Optional.Missing(),
val embed: Optional<EmbedRequest?> = Optional.Missing(),
val flags: Optional<UserFlags?> = Optional.Missing(),
val flags: Optional<MessageFlags?> = Optional.Missing(),
@SerialName("allowed_mentions")
val allowedMentions: Optional<AllowedMentions?> = Optional.Missing(),
)
Expand Down
Loading

0 comments on commit 64c299d

Please sign in to comment.