Skip to content

Commit

Permalink
Implement Role Icons (#402)
Browse files Browse the repository at this point in the history
* Add role icon data

* This triggers my OCD

* role icons supported in REST

* Member Avatars & Avatar->Icon refactor

* Default to webp instead

* Make user avatar optional, add defaultAvatar

* Move CDN urls to DiscordCDN

* Refactor Role#iconUrl to Role#icon with new Icon type

* Fix merge

* Make git happy
  • Loading branch information
ByteAlex authored Oct 1, 2021
1 parent cee2b6f commit 8ad4895
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 0 deletions.
6 changes: 6 additions & 0 deletions common/src/main/kotlin/entity/DiscordRole.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ data class DiscordRole(
val name: String,
val color: Int,
val hoist: Boolean,
val icon: Optional<String?>,
@SerialName("unicode_emoji")
val unicodeEmoji: Optional<String?>,
val position: Int,
val permissions: Permissions,
val managed: Boolean,
Expand All @@ -41,6 +44,9 @@ data class DiscordPartialRole(
val name: Optional<String> = Optional.Missing(),
val color: OptionalInt = OptionalInt.Missing,
val hoist: OptionalBoolean = OptionalBoolean.Missing,
val icon: Optional<String?> = Optional.Missing(),
@SerialName("unicode_emoji")
val unicodeEmoji: Optional<String?> = Optional.Missing(),
val position: OptionalInt = OptionalInt.Missing,
val permissions: Optional<Permissions> = Optional.Missing(),
val managed: OptionalBoolean = OptionalBoolean.Missing,
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/kotlin/cache/data/RoleData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ data class RoleData(
val name: String,
val color: Int,
val hoisted: Boolean,
val icon: Optional<String?>,
val unicodeEmoji: Optional<String?>,
val position: Int,
val permissions: Permissions,
val managed: Boolean,
Expand All @@ -32,6 +34,8 @@ data class RoleData(
name,
color,
hoist,
icon,
unicodeEmoji,
position,
permissions,
managed,
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/kotlin/entity/Icon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

}
4 changes: 4 additions & 0 deletions core/src/main/kotlin/entity/Role.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions core/src/test/kotlin/live/LiveGuildTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ class LiveGuildTest : AbstractLiveEntityTest<LiveGuild>() {
name = "",
color = 0,
hoist = false,
icon = Optional.Missing(),
unicodeEmoji = Optional.Missing(),
position = 0,
permissions = Permissions(Permission.BanMembers),
managed = false,
Expand Down Expand Up @@ -287,6 +289,8 @@ class LiveGuildTest : AbstractLiveEntityTest<LiveGuild>() {
name = "",
color = 0,
hoist = false,
icon = Optional.Missing(),
unicodeEmoji = Optional.Missing(),
position = 0,
permissions = Permissions(Permission.BanMembers),
managed = false,
Expand Down
5 changes: 5 additions & 0 deletions core/src/test/kotlin/live/LiveRoleTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -48,6 +49,8 @@ class LiveRoleTest : AbstractLiveEntityTest<LiveRole>() {
name = "test",
color = 0,
hoisted = false,
icon = Optional.Missing(),
unicodeEmoji = Optional.Missing(),
position = 0,
permissions = Permissions(Permission.CreateInstantInvite),
managed = false,
Expand All @@ -74,6 +77,8 @@ class LiveRoleTest : AbstractLiveEntityTest<LiveRole>() {
name = "",
color = 0,
hoist = false,
icon = Optional.Missing(),
unicodeEmoji = Optional.Missing(),
position = 0,
permissions = Permissions(Permission.BanMembers),
managed = false,
Expand Down
28 changes: 28 additions & 0 deletions core/src/test/kotlin/rest/RestTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
}
}
Binary file added core/src/test/resources/images/kord_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions rest/src/main/kotlin/builder/role/RoleCreateBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -19,6 +21,12 @@ class RoleCreateBuilder : AuditRequestBuilder<GuildRoleCreateRequest> {
private var _hoist: OptionalBoolean = OptionalBoolean.Missing
var hoist: Boolean? by ::_hoist.delegate()

private var _icon: Optional<Image> = Optional.Missing()
var icon: Image? by ::_icon.delegate()

private var _unicodeEmoji: Optional<String> = Optional.Missing()
var unicodeEmoji: String? by ::_unicodeEmoji.delegate()

private var _name: Optional<String> = Optional.Missing()
var name: String? by ::_name.delegate()

Expand All @@ -31,6 +39,8 @@ class RoleCreateBuilder : AuditRequestBuilder<GuildRoleCreateRequest> {
override fun toRequest(): GuildRoleCreateRequest = GuildRoleCreateRequest(
color = _color,
separate = _hoist,
icon = _icon.map { it.dataUri },
unicodeEmoji = _unicodeEmoji,
name = _name,
mentionable = _mentionable,
permissions = _permissions
Expand Down
10 changes: 10 additions & 0 deletions rest/src/main/kotlin/builder/role/RoleModifyBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -19,6 +21,12 @@ class RoleModifyBuilder : AuditRequestBuilder<GuildRoleModifyRequest> {
private var _hoist: OptionalBoolean? = OptionalBoolean.Missing
var hoist: Boolean? by ::_hoist.delegate()

private var _icon: Optional<Image> = Optional.Missing()
var icon: Image? by ::_icon.delegate()

private var _unicodeEmoji: Optional<String> = Optional.Missing()
var unicodeEmoji: String? by ::_unicodeEmoji.delegate()

private var _name: Optional<String?> = Optional.Missing()
var name: String? by ::_name.delegate()

Expand All @@ -32,6 +40,8 @@ class RoleModifyBuilder : AuditRequestBuilder<GuildRoleModifyRequest> {
name = _name,
color = _color,
separate = _hoist,
icon = _icon.map { it.dataUri },
unicodeEmoji = _unicodeEmoji,
mentionable = _mentionable,
permissions = _permissions
)
Expand Down
6 changes: 6 additions & 0 deletions rest/src/main/kotlin/json/request/GuildRequests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ data class GuildRoleCreateRequest(
val color: Optional<Color> = Optional.Missing(),
@SerialName("hoist")
val separate: OptionalBoolean = OptionalBoolean.Missing,
val icon: Optional<String> = Optional.Missing(),
@SerialName("unicode_emoji")
val unicodeEmoji: Optional<String> = Optional.Missing(),
val mentionable: OptionalBoolean = OptionalBoolean.Missing,
val id: OptionalSnowflake = OptionalSnowflake.Missing,
)
Expand Down Expand Up @@ -160,6 +163,9 @@ data class GuildRoleModifyRequest(
val color: Optional<Color?> = Optional.Missing(),
@SerialName("hoist")
val separate: OptionalBoolean? = OptionalBoolean.Missing,
val icon: Optional<String> = Optional.Missing(),
@SerialName("unicode_emoji")
val unicodeEmoji: Optional<String> = Optional.Missing(),
val mentionable: OptionalBoolean? = OptionalBoolean.Missing,
)

Expand Down
1 change: 1 addition & 0 deletions rest/src/main/kotlin/route/DiscordCdn.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

0 comments on commit 8ad4895

Please sign in to comment.