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

Add New webhook endpoints #217

Merged
merged 10 commits into from
Apr 16, 2021
72 changes: 62 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 Down Expand Up @@ -60,7 +61,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 +84,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 +132,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 +146,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 +168,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,12 +197,12 @@ 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)

}

fun MessageBehavior(
fun MessageBehavior(
channelId: Snowflake,
messageId: Snowflake,
kord: Kord,
Expand Down Expand Up @@ -212,13 +231,46 @@ interface MessageBehavior : KordEntity, Strategizable {
* @return The edited [Message].
*
* @throws [RestRequestException] if something went wrong during the request.
* @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)

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.
* @see edit
*/
@OptIn(ExperimentalContracts::class)
suspend inline fun MessageBehavior.editWebhookMessage(
webhookId: Snowflake,
token: String,
builder: EditWebhookMessageBuilder.() -> Unit
): Message {
contract {
callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
}

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
41 changes: 29 additions & 12 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 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
1 change: 0 additions & 1 deletion core/src/main/kotlin/entity/channel/MessageChannel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import dev.kord.core.behavior.channel.MessageChannelBehavior
import dev.kord.core.entity.Message
import dev.kord.core.supplier.EntitySupplyStrategy
import dev.kord.core.toInstant
import dev.kord.core.toSnowflakeOrNull
import java.time.Instant

/**
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<EmbedBuilder> = 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))
}

override fun toRequest(): WebhookEditMessageRequest = WebhookEditMessageRequest(
_content, Optional.missingOnEmpty(embeds.map(EmbedBuilder::toRequest)), _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
35 changes: 21 additions & 14 deletions rest/src/main/kotlin/json/request/WebhookRequests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,31 @@ data class WebhookCreateRequest(val name: String, val avatar: Optional<String> =

@Serializable
data class WebhookModifyRequest(
val name: Optional<String> = Optional.Missing(),
val avatar: Optional<String?> = Optional.Missing(),
@SerialName("channel_id")
val channelId: OptionalSnowflake = OptionalSnowflake.Missing
val name: Optional<String> = Optional.Missing(),
val avatar: Optional<String?> = Optional.Missing(),
@SerialName("channel_id")
val channelId: OptionalSnowflake = OptionalSnowflake.Missing
)

@Serializable
data class WebhookExecuteRequest(
val content: Optional<String> = Optional.Missing(),
val username: Optional<String> = Optional.Missing(),
@SerialName("avatar_url")
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 content: Optional<String> = Optional.Missing(),
val username: Optional<String> = Optional.Missing(),
@SerialName("avatar_url")
val avatar: Optional<String> = Optional.Missing(),
val tts: OptionalBoolean = OptionalBoolean.Missing,
val embeds: Optional<List<EmbedRequest>> = Optional.Missing(),
val allowedMentions: Optional<AllowedMentions> = Optional.Missing()
)

data class MultiPartWebhookExecuteRequest(
val request: WebhookExecuteRequest,
val file: Pair<String, java.io.InputStream>?
)
val request: WebhookExecuteRequest,
val file: Pair<String, java.io.InputStream>?
)

@Serializable
data class WebhookEditMessageRequest(
val content: Optional<String> = Optional.Missing(),
val embeds: Optional<List<EmbedRequest>> = Optional.Missing(),
val allowedMentions: Optional<AllowedMentions> = Optional.Missing()
)
Loading