diff --git a/CHANGELOG.md b/CHANGELOG.md index 2abd4ec7547..27e5bba2ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ +# 0.5.1 + +## Additions +* Added `PartialGuild` in `Invite` +* Added `GuildBehavior.createEmoji` +* Added `GuildEmoji.delete` and `GuildEmoji.edit` +* Added `getInviteOrNull` method to `RestEntitySupplier` + +## Fixes + +* `DiscordInvite#targetUser` is now correctly nullable. +* `PermissionOverwriteEntity#getGuildOrNull` uses the correct supplier method. +## Changes +* `Invite` now uses a `Channel` instead of `GuildChannel`. # 0.5.0 + ## Additions -`mention` properties for `GuildEmoji` and `ReactionEmoji` + +* `mention` properties for `GuildEmoji` and `ReactionEmoji` # 0.5.0-rc2 diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/GuildBehavior.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/GuildBehavior.kt index ff39e577190..51828abd0f6 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/GuildBehavior.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/GuildBehavior.kt @@ -12,8 +12,11 @@ import com.gitlab.kordlib.core.exception.EntityNotFoundException import com.gitlab.kordlib.core.sorted import com.gitlab.kordlib.core.supplier.EntitySupplier import com.gitlab.kordlib.core.supplier.EntitySupplyStrategy +import com.gitlab.kordlib.core.supplier.EntitySupplyStrategy.Companion.rest import com.gitlab.kordlib.rest.builder.ban.BanCreateBuilder import com.gitlab.kordlib.rest.builder.channel.* +import com.gitlab.kordlib.rest.builder.guild.EmojiCreateBuilder +import com.gitlab.kordlib.rest.builder.guild.EmojiModifyBuilder import com.gitlab.kordlib.rest.builder.guild.GuildModifyBuilder import com.gitlab.kordlib.rest.builder.role.RoleCreateBuilder import com.gitlab.kordlib.rest.builder.role.RolePositionsModifyBuilder @@ -195,6 +198,30 @@ interface GuildBehavior : Entity, Strategizable { */ suspend fun getRoleOrNull(roleId: Snowflake): Role? = supplier.getRoleOrNull(guildId = id, roleId = roleId) + /** + * Requests to get the [Invite] represented by the [code]. + * + * + * This property is not resolvable through cache and will always use the [RestClient] instead. + * + * @throws [RequestException] if anything went wrong during the request. + * @throws [EntityNotFoundException] if the [Invite] wasn't present. + */ + suspend fun getInvite(code: String, withCounts: Boolean = true): Invite = + kord.with(rest).getInvite(code, withCounts) + + /** + * Requests to get the [Invite] represented by the [code], + * returns null if the [Invite] isn't present. + * + * This property is not resolvable through cache and will always use the [RestClient] instead. + * + * @throws [RequestException] if anything went wrong during the request. + */ + suspend fun getInviteOrNull(code: String, withCounts: Boolean = true): Invite? = + kord.with(rest).getInviteOrNull(code, withCounts) + + /** * Requests to change the nickname of the bot in this guild, passing `null` will remove it. * @@ -249,7 +276,7 @@ interface GuildBehavior : Entity, Strategizable { * @throws RequestException if the guild does not exist or is not public. * @throws [EntityNotFoundException] if the preview was not found. */ - suspend fun getPreview(): GuildPreview = kord.with(EntitySupplyStrategy.rest).getGuildPreview(id) + suspend fun getPreview(): GuildPreview = kord.with(rest).getGuildPreview(id) /** * Returns the preview of this guild. The bot does not need to present in this guild @@ -259,7 +286,7 @@ interface GuildBehavior : Entity, Strategizable { * * @throws RequestException if the guild does not exist or is not public. */ - suspend fun getPreviewOrNull(): GuildPreview? = kord.with(EntitySupplyStrategy.rest).getGuildPreviewOrNull(id) + suspend fun getPreviewOrNull(): GuildPreview? = kord.with(rest).getGuildPreviewOrNull(id) /** * Requests to get the amount of users that would be pruned in this guild. @@ -309,6 +336,7 @@ interface GuildBehavior : Entity, Strategizable { override fun equals(other: Any?): Boolean = when (other) { is GuildBehavior -> other.id == id + is PartialGuild -> other.id == id else -> false } } @@ -330,6 +358,10 @@ suspend inline fun GuildBehavior.edit(builder: GuildModifyBuilder.() -> Unit): G return Guild(data, kord) } +suspend inline fun GuildBehavior.createEmoji(builder: EmojiCreateBuilder.() -> Unit) { + kord.rest.emoji.createEmoji(guildId = id.value, builder = builder) +} + /** * Requests to create a new text channel. * diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/MessageBehavior.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/MessageBehavior.kt index a19b17f59c7..4e2c8db6676 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/MessageBehavior.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/behavior/MessageBehavior.kt @@ -65,16 +65,9 @@ interface MessageBehavior : Entity, Strategizable { * The returned flow is lazily executed, any [RequestException] will be thrown on * [terminal operators](https://kotlinlang.org/docs/reference/coroutines/flow.html#terminal-flow-operators) instead. */ + fun getReactors(emoji: ReactionEmoji): Flow = - paginateForwards(batchSize = 100, idSelector = { it.id }) { position -> - kord.rest.channel.getReactions( - channelId = channelId.value, - messageId = id.value, - emoji = emoji.urlFormat, - limit = 100, - position = position - ) - }.map { UserData.from(it) }.map { User(it, kord) } + kord.with(EntitySupplyStrategy.rest).getReactors(channelId, id, emoji) /** * Requests to add an [emoji] to this message. @@ -153,7 +146,7 @@ interface MessageBehavior : Entity, Strategizable { */ override fun withStrategy( strategy: EntitySupplyStrategy<*> - ) : MessageBehavior = MessageBehavior(channelId, id, kord, strategy) + ): MessageBehavior = MessageBehavior(channelId, id, kord, strategy) companion object { internal operator fun invoke( @@ -169,7 +162,7 @@ interface MessageBehavior : Entity, Strategizable { override fun hashCode(): Int = Objects.hash(id) - override fun equals(other: Any?): Boolean = when(other) { + override fun equals(other: Any?): Boolean = when (other) { is MessageBehavior -> other.id == id && other.channelId == channelId else -> false } diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/cache/DataCacheExtensions.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/cache/DataCacheExtensions.kt index 89de23924c9..196d04bb4ab 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/cache/DataCacheExtensions.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/cache/DataCacheExtensions.kt @@ -41,4 +41,4 @@ internal suspend fun DataCache.removeKordData() { * Creates a [DataCacheView] for this view, only removing elements that were added * directly to this instance. */ -suspend fun DataCache.createView() : DataCacheView = DataCacheView(this) \ No newline at end of file +suspend fun DataCache.createView(): DataCacheView = DataCacheView(this) \ No newline at end of file diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/cache/data/InviteData.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/cache/data/InviteData.kt index 3aeb22d940a..16f4e14394e 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/cache/data/InviteData.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/cache/data/InviteData.kt @@ -1,5 +1,6 @@ package com.gitlab.kordlib.core.cache.data +import com.gitlab.kordlib.cache.api.data.description import com.gitlab.kordlib.common.entity.TargetUserType import com.gitlab.kordlib.rest.json.response.InviteResponse import kotlinx.serialization.Serializable @@ -7,7 +8,7 @@ import kotlinx.serialization.Serializable @Serializable data class InviteData( val code: String, - val guildId: Long?, + val guild: PartialGuildData?, val channelId: Long, val targetUserId: Long?, val inviterId: Long?, @@ -15,12 +16,14 @@ data class InviteData( val approximateMemberCount: Int?, val targetUserType: TargetUserType? ) { + companion object { + fun from(entity: InviteResponse) = with(entity) { InviteData( - code!!, - guild!!.id.toLong(), - channel!!.id.toLong(), + code, + guild?.let { PartialGuildData.from(it) }, + channel.id.toLong(), targetUser?.id?.toLong(), inviter?.id?.toLong(), approximatePresenceCount, diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/cache/data/PartialGuildData.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/cache/data/PartialGuildData.kt new file mode 100644 index 00000000000..027c74f59e8 --- /dev/null +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/cache/data/PartialGuildData.kt @@ -0,0 +1,24 @@ +package com.gitlab.kordlib.core.cache.data + +import com.gitlab.kordlib.cache.api.data.description +import com.gitlab.kordlib.common.entity.DiscordPartialGuild +import com.gitlab.kordlib.common.entity.Permissions +import kotlinx.serialization.Serializable + +@Serializable +class PartialGuildData( + val id: Long, + val name: String, + val icon: String? = null, + val owner: Boolean? = null, + val permissions: Permissions? = null +) { + companion object { + + fun from(partialGuild: DiscordPartialGuild) = with(partialGuild) { + PartialGuildData(id.toLong(), name, icon, owner, permissions) + } + } + + +} diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/entity/GuildEmoji.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/GuildEmoji.kt index 6221de3bcab..c5f06deac2a 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/entity/GuildEmoji.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/GuildEmoji.kt @@ -10,6 +10,8 @@ import com.gitlab.kordlib.core.cache.data.EmojiData import com.gitlab.kordlib.core.supplier.EntitySupplier import com.gitlab.kordlib.core.supplier.EntitySupplyStrategy import com.gitlab.kordlib.core.toSnowflakeOrNull +import com.gitlab.kordlib.rest.builder.guild.EmojiModifyBuilder +import io.ktor.utils.io.bits.withMemory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter @@ -98,6 +100,24 @@ class GuildEmoji( */ val user: UserBehavior? get() = userId?.let { UserBehavior(it, kord) } + /** + * Requests to delete this emoji, with the given [reason]. + * + * @throws RequestException if anything went wrong during the request. + */ + suspend fun delete(reason: String? = null) { + kord.rest.emoji.deleteEmoji(guildId = guildId.value, emojiId = id.value, reason = reason) + } + + /** + * Requests to edit the emoji. + * + * @throws [RequestException] if anything went wrong during the request. + */ + suspend inline fun edit(builder: EmojiModifyBuilder.() -> Unit) { + kord.rest.emoji.modifyEmoji(guildId = guildId.value, emojiId = id.value, builder = builder) + } + /** * Requests to get the creator of the emoji as a [Member], * returns null if the [Member] isn't present or [userId] is null. diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/entity/Invite.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/Invite.kt index 78a26c46317..233bc97f73f 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/entity/Invite.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/Invite.kt @@ -5,11 +5,10 @@ import com.gitlab.kordlib.common.entity.TargetUserType import com.gitlab.kordlib.common.exception.RequestException import com.gitlab.kordlib.core.Kord import com.gitlab.kordlib.core.KordObject -import com.gitlab.kordlib.core.behavior.GuildBehavior import com.gitlab.kordlib.core.behavior.UserBehavior -import com.gitlab.kordlib.core.behavior.channel.GuildChannelBehavior +import com.gitlab.kordlib.core.behavior.channel.ChannelBehavior import com.gitlab.kordlib.core.cache.data.InviteData -import com.gitlab.kordlib.core.entity.channel.GuildChannel +import com.gitlab.kordlib.core.entity.channel.Channel import com.gitlab.kordlib.core.exception.EntityNotFoundException import com.gitlab.kordlib.core.supplier.EntitySupplier import com.gitlab.kordlib.core.supplier.EntitySupplyStrategy @@ -37,9 +36,9 @@ data class Invite( val channelId: Snowflake get() = Snowflake(data.channelId) /** - * The id of the guild this invite is associated to. + * Returns [PartialGuild] if the invite was made in a guild, or null if not. */ - val guildId: Snowflake get() = Snowflake(data.guildId!!) + val partialGuild: PartialGuild? get() = data.guild?.let { PartialGuild(it, kord) } /** * The id of the user who created this invite, if present. @@ -54,12 +53,8 @@ data class Invite( /** * The behavior of the channel this invite is associated to. */ - val channel: GuildChannelBehavior get() = GuildChannelBehavior(guildId, channelId, kord) + val channel: ChannelBehavior get() = ChannelBehavior(channelId, kord) - /** - * The behavior of the guild this invite is associated to. - */ - val guild: GuildBehavior get() = GuildBehavior(guildId, kord) /** * The user behavior of the user who created this invite, if present. @@ -90,40 +85,25 @@ data class Invite( * Requests to get the channel this invite is for. * * @throws [RequestException] if anything went wrong during the request. - * @throws [EntityNotFoundException] if the [GuildChannel] wasn't present. + * @throws [EntityNotFoundException] if the [Channel] wasn't present. */ - suspend fun getChannel(): GuildChannel = supplier.getChannelOf(channelId) + suspend fun getChannel(): Channel = supplier.getChannelOf(channelId) /** * Requests to get the channel this invite is for, - * returns null if the [GuildChannel] isn't present. - * - * @throws [RequestException] if anything went wrong during the request. - */ - suspend fun getChannelOrNull(): GuildChannel? = supplier.getChannelOfOrNull(channelId) - - /** - * Requests to get the [Guild] for this invite. + * returns null if the [Channel] isn't present. * * @throws [RequestException] if anything went wrong during the request. - * @throws [EntityNotFoundException] if the [Guild] wasn't present. */ - suspend fun getGuild(): Guild = supplier.getGuild(guildId) - - /** - * Requests to get the [Guild] for this invite, - * returns null if the [Guild] isn't present. - * - * @throws [RequestException] if anything went wrong during the request. - */ - suspend fun getGuildOrNull(): Guild? = supplier.getGuildOrNull(guildId) + suspend fun getChannelOrNull(): Channel? = supplier.getChannelOfOrNull(channelId) /** * Requests to get the creator of the invite for, * returns null if the [User] isn't present or [inviterId] is null. * * @throws [RequestException] if anything went wrong during the request. - */ suspend fun getInviter(): User? = inviterId?.let { supplier.getUserOrNull(it) } + */ + suspend fun getInviter(): User? = inviterId?.let { supplier.getUserOrNull(it) } /** * Requests to get the user this invite was created for, diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/entity/PartialGuild.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/PartialGuild.kt new file mode 100644 index 00000000000..6836e763643 --- /dev/null +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/PartialGuild.kt @@ -0,0 +1,92 @@ +package com.gitlab.kordlib.core.entity + +import com.gitlab.kordlib.common.entity.Permissions +import com.gitlab.kordlib.common.entity.Snowflake +import com.gitlab.kordlib.common.exception.RequestException +import com.gitlab.kordlib.core.Kord +import com.gitlab.kordlib.core.behavior.GuildBehavior +import com.gitlab.kordlib.core.cache.data.PartialGuildData +import com.gitlab.kordlib.core.entity.channel.Channel +import com.gitlab.kordlib.core.exception.EntityNotFoundException +import com.gitlab.kordlib.core.supplier.EntitySupplier +import com.gitlab.kordlib.core.supplier.EntitySupplyStrategy +import com.gitlab.kordlib.core.supplier.getChannelOf +import com.gitlab.kordlib.core.supplier.getChannelOfOrNull +import com.gitlab.kordlib.rest.Image +import java.util.* + +class PartialGuild( + val data: PartialGuildData, + override val kord: Kord, + override val supplier: EntitySupplier = kord.defaultSupplier +) : Entity, Strategizable { + + /** + * The name of this guild. + */ + val name: String get() = data.name + + + override val id: Snowflake get() = Snowflake(data.id) + + /** + * The icon hash, if present. + */ + val iconHash: String? get() = data.icon + + /** + * wither who created the invite is the owner or not. + */ + + val owner: Boolean? get() = data.owner + + /** + * permissions that the invite creator has, if present. + */ + + val permissions: Permissions? get() = data.permissions + + /** + * Gets the icon url, if present. + */ + fun getIconUrl(format: Image.Format): String? = data.icon?.let { "https://cdn.discordapp.com/icons/${id.value}/$it.${format.extension}" } + + /** + * Requests to get the icon image in the specified [format], if present. + */ + suspend fun getIcon(format: Image.Format): Image? { + val url = getIconUrl(format) ?: return null + + return Image.fromUrl(kord.resources.httpClient, url) + } + + /** + * Requests to get the full [Guild] entity for this [PartialGuild]. + * + * @throws [RequestException] if anything went wrong during the request. + * @throws [EntityNotFoundException] if the [Guild] wasn't present, or the bot is not a part of this [Guild]. + */ + suspend fun getGuild(): Guild = supplier.getGuild(id) + + /** + * Requests to get the [Guild] for this invite, + * returns null if the [Guild] isn't present. + * + * @throws [RequestException] if anything went wrong during the request. + */ + suspend fun getGuildOrNull(): Guild? = supplier.getGuildOrNull(id) + + + override fun hashCode(): Int = Objects.hash(id) + + override fun equals(other: Any?): Boolean = when (other) { + is GuildBehavior -> other.id == id + is PartialGuild -> other.id == id + else -> false + } + + + override fun withStrategy(strategy: EntitySupplyStrategy<*>): PartialGuild = + PartialGuild(data, kord, strategy.supply(kord)) + +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/entity/PermissionOverwriteEntity.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/PermissionOverwriteEntity.kt index c589669d1fa..1a6428b67f4 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/entity/PermissionOverwriteEntity.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/entity/PermissionOverwriteEntity.kt @@ -57,7 +57,7 @@ class PermissionOverwriteEntity( * * @throws [RequestException] if anything went wrong during the request. */ - suspend fun getGuildOrNull(): Guild? = supplier.getGuild(guildId) + suspend fun getGuildOrNull(): Guild? = supplier.getGuildOrNull(guildId) /** * Requests to delete this overwrite. diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/exception/EntityNotFoundException.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/exception/EntityNotFoundException.kt index fa181189617..5509f2d13ba 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/exception/EntityNotFoundException.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/exception/EntityNotFoundException.kt @@ -50,6 +50,9 @@ class EntityNotFoundException : Exception { fun webhookNotFound(webhookId: Snowflake): Nothing = entityNotFound("Webhook", webhookId) + fun inviteNotFound(code: String): Nothing = + throw EntityNotFoundException("Invite with code $code was not found.") + } } \ No newline at end of file diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt index 62b5a3af16d..ba56bd3d561 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt @@ -238,4 +238,6 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { return User(data, kord) } + + } \ No newline at end of file diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/EntitySupplier.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/EntitySupplier.kt index a4abac2e6ca..8a49235176d 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/EntitySupplier.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/EntitySupplier.kt @@ -343,7 +343,6 @@ interface EntitySupplier { */ suspend fun getWebhookWithToken(id: Snowflake, token: String): Webhook = getWebhookWithTokenOrNull(id, token) ?: EntityNotFoundException.webhookNotFound(id) - } diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/FallbackEntitySupplier.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/FallbackEntitySupplier.kt index 9ab95debb5f..02ef50eee4c 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/FallbackEntitySupplier.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/FallbackEntitySupplier.kt @@ -23,8 +23,6 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override suspend fun getChannelOrNull(id: Snowflake): Channel? = first.getChannelOrNull(id) ?: second.getChannelOrNull(id) - override suspend fun getChannel(id: Snowflake): Channel = getChannelOrNull(id)!! - override fun getGuildChannels(guildId: Snowflake): Flow = first.getGuildChannels(guildId).switchIfEmpty(second.getGuildChannels(guildId)) @@ -37,14 +35,9 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override suspend fun getMessageOrNull(channelId: Snowflake, messageId: Snowflake): Message? = first.getMessageOrNull(channelId, messageId) ?: second.getMessageOrNull(channelId, messageId) - override suspend fun getGuild(id: Snowflake): Guild = getGuildOrNull(id)!! - override suspend fun getMember(guildId: Snowflake, userId: Snowflake): Member = getMemberOrNull(guildId, userId)!! - override suspend fun getMessage(channelId: Snowflake, messageId: Snowflake): Message = - getMessageOrNull(channelId, messageId)!! - override fun getMessagesAfter(messageId: Snowflake, channelId: Snowflake, limit: Int): Flow = first.getMessagesAfter(messageId, channelId, limit).switchIfEmpty(second.getMessagesAfter(messageId, channelId, limit)) @@ -60,15 +53,9 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override suspend fun getUserOrNull(id: Snowflake): User? = first.getUserOrNull(id) ?: second.getUserOrNull(id) - override suspend fun getSelf(): User = getSelfOrNull()!! - - override suspend fun getUser(id: Snowflake): User = getUserOrNull(id)!! - override suspend fun getRoleOrNull(guildId: Snowflake, roleId: Snowflake): Role? = first.getRoleOrNull(guildId, roleId) ?: second.getRoleOrNull(guildId, roleId) - override suspend fun getRole(guildId: Snowflake, roleId: Snowflake): Role = - getRoleOrNull(guildId, roleId)!! override fun getGuildRoles(guildId: Snowflake): Flow = first.getGuildRoles(guildId).switchIfEmpty(second.getGuildRoles(guildId)) @@ -76,9 +63,6 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override suspend fun getGuildBanOrNull(guildId: Snowflake, userId: Snowflake): Ban? = first.getGuildBanOrNull(guildId, userId) ?: second.getGuildBanOrNull(guildId, userId) - override suspend fun getGuildBan(guildId: Snowflake, userId: Snowflake): Ban = - getGuildBanOrNull(guildId, userId)!! - override fun getGuildBans(guildId: Snowflake): Flow = first.getGuildBans(guildId).switchIfEmpty(second.getGuildBans(guildId)) @@ -91,9 +75,6 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override suspend fun getEmojiOrNull(guildId: Snowflake, emojiId: Snowflake): GuildEmoji? = first.getEmojiOrNull(guildId, emojiId) ?: second.getEmojiOrNull(guildId, emojiId) - override suspend fun getEmoji(guildId: Snowflake, emojiId: Snowflake): GuildEmoji = - getEmojiOrNull(guildId, emojiId)!! - override fun getEmojis(guildId: Snowflake): Flow = first.getEmojis(guildId).switchIfEmpty(second.getEmojis(guildId)) @@ -112,11 +93,5 @@ private class FallbackEntitySupplier(val first: EntitySupplier, val second: Enti override suspend fun getWebhookWithTokenOrNull(id: Snowflake, token: String): Webhook? = first.getWebhookWithTokenOrNull(id, token) ?: second.getWebhookWithTokenOrNull(id, token) - override suspend fun getWebhook(id: Snowflake): Webhook = - getWebhookOrNull(id)!! - - override suspend fun getWebhookWithToken(id: Snowflake, token: String): Webhook = - getWebhookWithTokenOrNull(id, token)!! - } diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt index a237454e029..6d4b3048916 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt @@ -73,8 +73,8 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier { * @throws [RestRequestException] if something went wrong during the request. * @throws [EntityNotFoundException] if the preview was not found. */ - suspend fun getGuildPreview(id: Snowflake): GuildPreview = getGuildPreviewOrNull(id) - ?: EntityNotFoundException.entityNotFound("Guild preview", id) + suspend fun getGuildPreview(id: Snowflake): GuildPreview = getGuildPreviewOrNull(id) + ?: EntityNotFoundException.entityNotFound("Guild preview", id) /** * Returns the preview of the guild matching the [id]. The bot does not need to present in this guild @@ -229,6 +229,14 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier { return Webhook(data, kord) } + suspend fun getInviteOrNull(code: String, withCounts: Boolean): Invite? = catchNotFound { + val response = invite.getInvite(code, withCounts) + return Invite(InviteData.from(response), kord) + } + + suspend fun getInvite(code: String, withCounts: Boolean = true): Invite = + getInviteOrNull(code, withCounts) ?: EntityNotFoundException.inviteNotFound(code) + /** * Requests to get the information of the current application. * diff --git a/core/src/test/kotlin/InviteTests.kt b/core/src/test/kotlin/InviteTests.kt new file mode 100644 index 00000000000..a9d802b9134 --- /dev/null +++ b/core/src/test/kotlin/InviteTests.kt @@ -0,0 +1,22 @@ +import com.gitlab.kordlib.cache.api.put +import com.gitlab.kordlib.core.Kord +import com.gitlab.kordlib.core.supplier.EntitySupplyStrategy +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable +import kotlin.test.assertEquals + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@EnabledIfEnvironmentVariable(named = "TARGET_BRANCH", matches = "master") +class InviteTests { + + @Test + fun `get invite from rest and cache`() = runBlocking { + val kord = Kord(System.getenv("KORD_TEST_TOKEN")) + val invite = kord.with(EntitySupplyStrategy.rest).getInvite("mpDQm5N") + assertEquals("mpDQm5N", invite.code) + } +} \ No newline at end of file diff --git a/gateway/src/main/kotlin/com/gitlab/kordlib/gateway/Event.kt b/gateway/src/main/kotlin/com/gitlab/kordlib/gateway/Event.kt index 5970ccd1248..8f199da2487 100644 --- a/gateway/src/main/kotlin/com/gitlab/kordlib/gateway/Event.kt +++ b/gateway/src/main/kotlin/com/gitlab/kordlib/gateway/Event.kt @@ -346,7 +346,7 @@ data class DiscordCreatedInvite( * The target user for this invite. */ @SerialName("target_user") - val targetUser: DiscordInviteUser, + val targetUser: DiscordInviteUser? = null, /** * The type of user target for this invite. diff --git a/rest/src/main/kotlin/com/gitlab/kordlib/rest/builder/guild/EmojiCreateBuilder.kt b/rest/src/main/kotlin/com/gitlab/kordlib/rest/builder/guild/EmojiCreateBuilder.kt new file mode 100644 index 00000000000..45b85b9b76a --- /dev/null +++ b/rest/src/main/kotlin/com/gitlab/kordlib/rest/builder/guild/EmojiCreateBuilder.kt @@ -0,0 +1,22 @@ +package com.gitlab.kordlib.rest.builder.guild + +import com.gitlab.kordlib.common.annotation.KordDsl +import com.gitlab.kordlib.common.entity.Snowflake +import com.gitlab.kordlib.rest.Image +import com.gitlab.kordlib.rest.builder.AuditRequestBuilder +import com.gitlab.kordlib.rest.json.request.EmojiCreateRequest +import com.gitlab.kordlib.rest.json.request.EmojiModifyRequest + +@KordDsl +class EmojiCreateBuilder : AuditRequestBuilder { + override var reason: String? = null + lateinit var name: String + lateinit var image: Image + var roles: Set = emptySet() + + override fun toRequest(): EmojiCreateRequest = EmojiCreateRequest( + name = name, + image = image.dataUri, + roles = roles.map { it.value } + ) +} diff --git a/rest/src/main/kotlin/com/gitlab/kordlib/rest/json/response/Invite.kt b/rest/src/main/kotlin/com/gitlab/kordlib/rest/json/response/Invite.kt index e1aea836995..4be601c2060 100644 --- a/rest/src/main/kotlin/com/gitlab/kordlib/rest/json/response/Invite.kt +++ b/rest/src/main/kotlin/com/gitlab/kordlib/rest/json/response/Invite.kt @@ -9,9 +9,9 @@ import kotlinx.serialization.internal.IntDescriptor @Serializable data class InviteResponse( - val code: String? = null, + val code: String, val guild: DiscordPartialGuild? = null, - val channel: DiscordChannel? = null, + val channel: DiscordChannel, val inviter: DiscordUser? = null, @SerialName("target_user") val targetUser: DiscordUser? = null, diff --git a/rest/src/main/kotlin/com/gitlab/kordlib/rest/service/EmojiService.kt b/rest/src/main/kotlin/com/gitlab/kordlib/rest/service/EmojiService.kt index fd772647e47..f010ddebe16 100644 --- a/rest/src/main/kotlin/com/gitlab/kordlib/rest/service/EmojiService.kt +++ b/rest/src/main/kotlin/com/gitlab/kordlib/rest/service/EmojiService.kt @@ -1,5 +1,6 @@ package com.gitlab.kordlib.rest.service +import com.gitlab.kordlib.rest.builder.guild.EmojiCreateBuilder import com.gitlab.kordlib.rest.builder.guild.EmojiModifyBuilder import com.gitlab.kordlib.rest.json.request.EmojiCreateRequest import com.gitlab.kordlib.rest.json.request.EmojiModifyRequest @@ -8,6 +9,16 @@ import com.gitlab.kordlib.rest.route.Route class EmojiService(requestHandler: RequestHandler) : RestService(requestHandler) { + + suspend inline fun createEmoji(guildId: String, builder: EmojiCreateBuilder.() -> Unit) = call(Route.GuildEmojiPost) { + keys[Route.GuildId] = guildId + val emoji = EmojiCreateBuilder().apply(builder) + + body(EmojiCreateRequest.serializer(), emoji.toRequest()) + emoji.reason?.let { header("X-Audit-Log-Reason", it) } + } + + @Deprecated("use the inline builder instead", ReplaceWith("createEmoji(guildId) { }") ,level = DeprecationLevel.WARNING) suspend fun createEmoji(guildId: String, emoji: EmojiCreateRequest, reason: String? = null) = call(Route.GuildEmojiPost) { keys[Route.GuildId] = guildId body(EmojiCreateRequest.serializer(), emoji) diff --git a/rest/src/test/kotlin/com/gitlab/kordlib/rest/services/RestServiceTest.kt b/rest/src/test/kotlin/com/gitlab/kordlib/rest/services/RestServiceTest.kt index 5358f61beaa..a4e2af859c8 100644 --- a/rest/src/test/kotlin/com/gitlab/kordlib/rest/services/RestServiceTest.kt +++ b/rest/src/test/kotlin/com/gitlab/kordlib/rest/services/RestServiceTest.kt @@ -1,6 +1,7 @@ package com.gitlab.kordlib.rest.services import com.gitlab.kordlib.common.entity.* +import com.gitlab.kordlib.rest.Image import com.gitlab.kordlib.rest.json.request.* import com.gitlab.kordlib.rest.ratelimit.ExclusionRequestRateLimiter import com.gitlab.kordlib.rest.request.KtorRequestHandler @@ -21,6 +22,14 @@ fun image(path: String): String { return "data:image/$imageType;base64, $encoded" } +fun imageBinary(path: String) : Image { + val loader = Unit::class.java.classLoader + val image = loader?.getResource(path)?.readBytes() + val imageType = path.split(".").last() + val format = Image.Format.fromContentType(imageType) + return Image.raw(image!!, format) +} + @TestMethodOrder(MethodOrderer.OrderAnnotation::class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @EnabledIfEnvironmentVariable(named = "TARGET_BRANCH", matches = "master") @@ -371,7 +380,11 @@ class RestServiceTest { fun `emojis in guilds`() = runBlocking { with(rest.emoji) { - val emoji = createEmoji(guildId, EmojiCreateRequest("kord", image("images/kord.png"), listOf(guildId))) + val emoji = createEmoji(guildId) { + name = "kord" + image = imageBinary("images/kord.png") + roles = setOf(Snowflake(guildId)) + } modifyEmoji(guildId, emoji.id!!) { name = "edited"