diff --git a/common/src/main/kotlin/entity/DiscordRole.kt b/common/src/main/kotlin/entity/DiscordRole.kt index f823ea91a269..57f3523dbe6e 100644 --- a/common/src/main/kotlin/entity/DiscordRole.kt +++ b/common/src/main/kotlin/entity/DiscordRole.kt @@ -19,6 +19,9 @@ data class DiscordRole( val name: String, val color: Int, val hoist: Boolean, + val icon: Optional, + @SerialName("unicode_emoji") + val unicodeEmoji: Optional, val position: Int, val permissions: Permissions, val managed: Boolean, @@ -41,6 +44,9 @@ data class DiscordPartialRole( val name: Optional = Optional.Missing(), val color: OptionalInt = OptionalInt.Missing, val hoist: OptionalBoolean = OptionalBoolean.Missing, + val icon: Optional = Optional.Missing(), + @SerialName("unicode_emoji") + val unicodeEmoji: Optional = Optional.Missing(), val position: OptionalInt = OptionalInt.Missing, val permissions: Optional = Optional.Missing(), val managed: OptionalBoolean = OptionalBoolean.Missing, diff --git a/core/src/main/kotlin/cache/data/GuildWdigetData.kt b/core/src/main/kotlin/cache/data/GuildWidgetData.kt similarity index 100% rename from core/src/main/kotlin/cache/data/GuildWdigetData.kt rename to core/src/main/kotlin/cache/data/GuildWidgetData.kt diff --git a/core/src/main/kotlin/cache/data/RoleData.kt b/core/src/main/kotlin/cache/data/RoleData.kt index 2d04e85e9f71..0dab0173b03e 100644 --- a/core/src/main/kotlin/cache/data/RoleData.kt +++ b/core/src/main/kotlin/cache/data/RoleData.kt @@ -16,6 +16,8 @@ data class RoleData( val name: String, val color: Int, val hoisted: Boolean, + val icon: Optional, + val unicodeEmoji: Optional, val position: Int, val permissions: Permissions, val managed: Boolean, @@ -32,6 +34,8 @@ data class RoleData( name, color, hoist, + icon, + unicodeEmoji, position, permissions, managed, diff --git a/core/src/main/kotlin/entity/Icon.kt b/core/src/main/kotlin/entity/Icon.kt index 6b638dda82ee..bdf625bb87ae 100644 --- a/core/src/main/kotlin/entity/Icon.kt +++ b/core/src/main/kotlin/entity/Icon.kt @@ -51,4 +51,8 @@ sealed class Icon(val animated: Boolean, val cdnUrl: CdnUrl, override val kord: class MemberAvatar(guildId: Snowflake, userId: Snowflake, avatarHash: String, kord: Kord) : Icon(avatarHash.startsWith("a_"), DiscordCdn.memberAvatar(guildId, userId, avatarHash), kord) + + class RoleIcon(roleId: Snowflake, iconHash: String, kord: Kord) : + Icon(iconHash.startsWith("a_"), DiscordCdn.roleIcon(roleId, iconHash), kord) + } \ No newline at end of file diff --git a/core/src/main/kotlin/entity/Role.kt b/core/src/main/kotlin/entity/Role.kt index 23c7a90c2780..c2fab024c5d9 100644 --- a/core/src/main/kotlin/entity/Role.kt +++ b/core/src/main/kotlin/entity/Role.kt @@ -27,6 +27,10 @@ data class Role( val hoisted: Boolean get() = data.hoisted + val icon: Icon? get() = data.icon.value?.let { Icon.RoleIcon(data.id, it, kord) } + + val unicodeEmoji: String? = data.unicodeEmoji.value + val managed: Boolean get() = data.managed val mentionable: Boolean get() = data.mentionable diff --git a/core/src/test/kotlin/live/LiveGuildTest.kt b/core/src/test/kotlin/live/LiveGuildTest.kt index 6db7ebe8a7e2..9a2b2cfd0688 100644 --- a/core/src/test/kotlin/live/LiveGuildTest.kt +++ b/core/src/test/kotlin/live/LiveGuildTest.kt @@ -256,6 +256,8 @@ class LiveGuildTest : AbstractLiveEntityTest() { name = "", color = 0, hoist = false, + icon = Optional.Missing(), + unicodeEmoji = Optional.Missing(), position = 0, permissions = Permissions(Permission.BanMembers), managed = false, @@ -285,6 +287,8 @@ class LiveGuildTest : AbstractLiveEntityTest() { name = "", color = 0, hoist = false, + icon = Optional.Missing(), + unicodeEmoji = Optional.Missing(), position = 0, permissions = Permissions(Permission.BanMembers), managed = false, diff --git a/core/src/test/kotlin/live/LiveRoleTest.kt b/core/src/test/kotlin/live/LiveRoleTest.kt index 65bbb3bf07be..5afb714ae0cb 100644 --- a/core/src/test/kotlin/live/LiveRoleTest.kt +++ b/core/src/test/kotlin/live/LiveRoleTest.kt @@ -2,6 +2,7 @@ package live import dev.kord.common.annotation.KordPreview import dev.kord.common.entity.* +import dev.kord.common.entity.optional.Optional import dev.kord.core.cache.data.RoleData import dev.kord.core.entity.Role import dev.kord.core.event.guild.GuildDeleteEvent @@ -46,6 +47,8 @@ class LiveRoleTest : AbstractLiveEntityTest() { name = "test", color = 0, hoisted = false, + icon = Optional.Missing(), + unicodeEmoji = Optional.Missing(), position = 0, permissions = Permissions(Permission.CreateInstantInvite), managed = false, @@ -72,6 +75,8 @@ class LiveRoleTest : AbstractLiveEntityTest() { name = "", color = 0, hoist = false, + icon = Optional.Missing(), + unicodeEmoji = Optional.Missing(), position = 0, permissions = Permissions(Permission.BanMembers), managed = false, diff --git a/core/src/test/kotlin/rest/RestTest.kt b/core/src/test/kotlin/rest/RestTest.kt index 0f0371da03f4..44ac9798f58d 100644 --- a/core/src/test/kotlin/rest/RestTest.kt +++ b/core/src/test/kotlin/rest/RestTest.kt @@ -40,6 +40,9 @@ fun imageBinary(path: String): Image { @EnabledIfEnvironmentVariable(named = "KORD_TEST_TOKEN", matches = ".+") class RestServiceTest { + // should we run checks which require boosts on the public guild? + private val boostEnabled = System.getenv("KORD_BOOST_ENABLED")?.equals("true", ignoreCase = true) ?: false + private val publicGuildId = Snowflake(322850917248663552u) private val token = System.getenv("KORD_TEST_TOKEN") @@ -545,4 +548,29 @@ class RestServiceTest { Unit } + @Test + fun `create role with image icon`() = runBlocking { + if (!boostEnabled) + return@runBlocking Unit + val guild = kord.getGuild(publicGuildId)!! + guild.createRole { + name = "Test Image Icon" + hoist = true + icon = imageBinary("images/kord_icon.png") + } + return@runBlocking Unit + } + + @Test + fun `create role with unicode icon`() = runBlocking { + if (!boostEnabled) + return@runBlocking Unit + val guild = kord.getGuild(publicGuildId)!! + guild.createRole { + name = "Test Unicode Icon" + hoist = true + unicodeEmoji = "\uD83D\uDE04" + } + return@runBlocking Unit + } } diff --git a/core/src/test/resources/images/kord_icon.png b/core/src/test/resources/images/kord_icon.png new file mode 100644 index 000000000000..045f9247564f Binary files /dev/null and b/core/src/test/resources/images/kord_icon.png differ diff --git a/rest/src/main/kotlin/builder/role/RoleCreateBuilder.kt b/rest/src/main/kotlin/builder/role/RoleCreateBuilder.kt index b8cebef5eee7..8f2647848106 100644 --- a/rest/src/main/kotlin/builder/role/RoleCreateBuilder.kt +++ b/rest/src/main/kotlin/builder/role/RoleCreateBuilder.kt @@ -6,6 +6,8 @@ import dev.kord.common.entity.Permissions 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.common.entity.optional.map +import dev.kord.rest.Image import dev.kord.rest.builder.AuditRequestBuilder import dev.kord.rest.json.request.GuildRoleCreateRequest @@ -19,6 +21,12 @@ class RoleCreateBuilder : AuditRequestBuilder { private var _hoist: OptionalBoolean = OptionalBoolean.Missing var hoist: Boolean? by ::_hoist.delegate() + private var _icon: Optional = Optional.Missing() + var icon: Image? by ::_icon.delegate() + + private var _unicodeEmoji: Optional = Optional.Missing() + var unicodeEmoji: String? by ::_unicodeEmoji.delegate() + private var _name: Optional = Optional.Missing() var name: String? by ::_name.delegate() @@ -31,6 +39,8 @@ class RoleCreateBuilder : AuditRequestBuilder { override fun toRequest(): GuildRoleCreateRequest = GuildRoleCreateRequest( color = _color, separate = _hoist, + icon = _icon.map { it.dataUri }, + unicodeEmoji = _unicodeEmoji, name = _name, mentionable = _mentionable, permissions = _permissions diff --git a/rest/src/main/kotlin/builder/role/RoleModifyBuilder.kt b/rest/src/main/kotlin/builder/role/RoleModifyBuilder.kt index 9b12acad0936..293178f77340 100644 --- a/rest/src/main/kotlin/builder/role/RoleModifyBuilder.kt +++ b/rest/src/main/kotlin/builder/role/RoleModifyBuilder.kt @@ -6,6 +6,8 @@ import dev.kord.common.entity.Permissions 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.common.entity.optional.map +import dev.kord.rest.Image import dev.kord.rest.builder.AuditRequestBuilder import dev.kord.rest.json.request.GuildRoleModifyRequest @@ -19,6 +21,12 @@ class RoleModifyBuilder : AuditRequestBuilder { private var _hoist: OptionalBoolean? = OptionalBoolean.Missing var hoist: Boolean? by ::_hoist.delegate() + private var _icon: Optional = Optional.Missing() + var icon: Image? by ::_icon.delegate() + + private var _unicodeEmoji: Optional = Optional.Missing() + var unicodeEmoji: String? by ::_unicodeEmoji.delegate() + private var _name: Optional = Optional.Missing() var name: String? by ::_name.delegate() @@ -32,6 +40,8 @@ class RoleModifyBuilder : AuditRequestBuilder { name = _name, color = _color, separate = _hoist, + icon = _icon.map { it.dataUri }, + unicodeEmoji = _unicodeEmoji, mentionable = _mentionable, permissions = _permissions ) diff --git a/rest/src/main/kotlin/json/request/GuildRequests.kt b/rest/src/main/kotlin/json/request/GuildRequests.kt index 65beb605b5ae..3592e13b2f6f 100644 --- a/rest/src/main/kotlin/json/request/GuildRequests.kt +++ b/rest/src/main/kotlin/json/request/GuildRequests.kt @@ -123,6 +123,9 @@ data class GuildRoleCreateRequest( val color: Optional = Optional.Missing(), @SerialName("hoist") val separate: OptionalBoolean = OptionalBoolean.Missing, + val icon: Optional = Optional.Missing(), + @SerialName("unicode_emoji") + val unicodeEmoji: Optional = Optional.Missing(), val mentionable: OptionalBoolean = OptionalBoolean.Missing, val id: OptionalSnowflake = OptionalSnowflake.Missing, ) @@ -160,6 +163,9 @@ data class GuildRoleModifyRequest( val color: Optional = Optional.Missing(), @SerialName("hoist") val separate: OptionalBoolean? = OptionalBoolean.Missing, + val icon: Optional = Optional.Missing(), + @SerialName("unicode_emoji") + val unicodeEmoji: Optional = Optional.Missing(), val mentionable: OptionalBoolean? = OptionalBoolean.Missing, ) diff --git a/rest/src/main/kotlin/route/DiscordCdn.kt b/rest/src/main/kotlin/route/DiscordCdn.kt index dbc8df49f1fd..3eff065bc952 100644 --- a/rest/src/main/kotlin/route/DiscordCdn.kt +++ b/rest/src/main/kotlin/route/DiscordCdn.kt @@ -15,4 +15,5 @@ object DiscordCdn { fun memberAvatar(guildId: Snowflake, userId: Snowflake, hash: String) = CdnUrl("$BASE_URL/guilds/${guildId.asString}/users/${userId.asString}/avatars/$hash") + fun roleIcon(roleId: Snowflake, hash: String) = CdnUrl("$BASE_URL/role-icons/${roleId.asString}/$hash") } \ No newline at end of file