From 69c3ae80b854ffc503de949636ce95969e75ccf1 Mon Sep 17 00:00:00 2001 From: kageru Date: Tue, 12 Nov 2019 22:02:32 +0100 Subject: [PATCH] Consistently use Javacord extensions --- src/main/kotlin/moe/kageru/kagebot/Util.kt | 32 +++---------------- .../kageru/kagebot/command/RoleAssignment.kt | 9 +++--- .../kageru/kagebot/extensions/Extensions.kt | 5 ++- .../kageru/kagebot/features/TempVCFeature.kt | 4 +-- .../kageru/kagebot/features/TimeoutFeature.kt | 2 +- .../kotlin/moe/kageru/kagebot/TestUtil.kt | 29 +++++++++-------- .../moe/kageru/kagebot/command/CommandTest.kt | 9 ++++-- .../kagebot/features/HelpFeatureTest.kt | 3 +- 8 files changed, 40 insertions(+), 53 deletions(-) diff --git a/src/main/kotlin/moe/kageru/kagebot/Util.kt b/src/main/kotlin/moe/kageru/kagebot/Util.kt index a30b82a..4b7979c 100644 --- a/src/main/kotlin/moe/kageru/kagebot/Util.kt +++ b/src/main/kotlin/moe/kageru/kagebot/Util.kt @@ -3,16 +3,13 @@ package moe.kageru.kagebot import arrow.core.* import arrow.core.extensions.either.monad.flatMap import arrow.core.extensions.list.foldable.find -import arrow.typeclasses.Monad -import moe.kageru.kagebot.config.Config -import moe.kageru.kagebot.extensions.* import moe.kageru.kagebot.config.Config.server +import moe.kageru.kagebot.extensions.* import org.javacord.api.entity.channel.TextChannel import org.javacord.api.entity.message.MessageAuthor import org.javacord.api.entity.message.embed.EmbedBuilder import org.javacord.api.entity.permission.Role import org.javacord.api.entity.user.User -import org.javacord.api.event.message.MessageCreateEvent import java.awt.Color import java.util.* import java.util.concurrent.CompletableFuture @@ -23,13 +20,6 @@ object Util { return if (condition) op(this) else this } - /** - * Mimics the behavior of [Optional.ifPresent], but returns null if the optional is empty, - * allowing easier fallback behavior via Kotlin’s ?: operator. - */ - internal inline fun Optional.ifNotEmpty(op: (T) -> R): R? = - if (this.isPresent) op(this.get()) else null - fun hasOneOf(messageAuthor: MessageAuthor, roles: Set): Boolean { return messageAuthor.asUser().asOption().flatMap { user -> user.roles().find { it in roles } @@ -53,17 +43,13 @@ object Util { } } - private fun Optional.toNullable(): T? { - return orElse(null) - } - fun findUser(idOrName: String): Option { return when { idOrName.isEntityId() -> server.getMemberById(idOrName).asOption() else -> { when { idOrName.contains('#') -> server.getMemberByDiscriminatedNameIgnoreCase(idOrName).asOption() - else -> server.getMembersByName(idOrName).firstOrNull().toOption() + else -> server.membersByName(idOrName).firstOrNone() } } } @@ -97,13 +83,13 @@ object Util { fun findChannel(idOrName: String): Either { return when { - idOrName.isEntityId() -> server.getTextChannelById(idOrName).asOption().toEither { "Channel $idOrName not found" } + idOrName.isEntityId() -> server.channelById(idOrName).toEither { "Channel $idOrName not found" } idOrName.startsWith('@') -> Globals.api.getCachedUserByDiscriminatedName(idOrName.removePrefix("@")).asOption() .toEither { "User $idOrName not found" } .flatMap { user -> user.openPrivateChannel().asOption().toEither { "Can’t DM user $idOrName" } } - else -> server.channelByName(idOrName).getOnly().mapLeft { "Found $it channels for $idOrName, expected 1" } + else -> server.channelsByName(idOrName).getOnly().mapLeft { "Found $it channels for $idOrName, expected 1" } } } @@ -129,8 +115,6 @@ object Util { } } - fun MessageCreateEvent.getUser(): User? = Config.server.getMemberById(messageAuthor.id).toNullable() - /** * Convert a list of elements to pairs, retaining order. * The last element is dropped if the input size is odd. @@ -141,12 +125,4 @@ object Util { Pair(next(), next()) } } - - private inline fun CompletableFuture.joinOr(op: () -> Nothing): T { - val value = join() - if (isCompletedExceptionally) { - op() - } - return value - } } diff --git a/src/main/kotlin/moe/kageru/kagebot/command/RoleAssignment.kt b/src/main/kotlin/moe/kageru/kagebot/command/RoleAssignment.kt index 8c85902..3aa0d4a 100644 --- a/src/main/kotlin/moe/kageru/kagebot/command/RoleAssignment.kt +++ b/src/main/kotlin/moe/kageru/kagebot/command/RoleAssignment.kt @@ -3,14 +3,15 @@ package moe.kageru.kagebot.command import com.fasterxml.jackson.annotation.JsonProperty import moe.kageru.kagebot.Log import moe.kageru.kagebot.Util -import moe.kageru.kagebot.Util.getUser import moe.kageru.kagebot.Util.unwrap +import moe.kageru.kagebot.extensions.getUser import org.javacord.api.event.message.MessageCreateEvent class RoleAssignment(@JsonProperty("role") role: String) { private val role = Util.findRole(role).unwrap() - fun assign(message: MessageCreateEvent) = - message.getUser()?.addRole(role, "Requested via command.") - ?: Log.warn("Could not find user ${message.messageAuthor.name} for role assign") + fun assign(message: MessageCreateEvent) = message.getUser().fold( + { Log.warn("Could not find user ${message.messageAuthor.name} for role assign") }, + { it.addRole(role, "Requested via command.") } + ) } diff --git a/src/main/kotlin/moe/kageru/kagebot/extensions/Extensions.kt b/src/main/kotlin/moe/kageru/kagebot/extensions/Extensions.kt index 593862d..6771348 100644 --- a/src/main/kotlin/moe/kageru/kagebot/extensions/Extensions.kt +++ b/src/main/kotlin/moe/kageru/kagebot/extensions/Extensions.kt @@ -10,12 +10,15 @@ import org.javacord.api.entity.channel.ChannelCategory import org.javacord.api.entity.permission.Role import org.javacord.api.entity.server.Server import org.javacord.api.entity.user.User +import org.javacord.api.event.message.MessageCreateEvent fun Server.channelById(id: String): Option = getTextChannelById(id).asOption() -fun Server.channelByName(name: String): ListK = getTextChannelsByName(name).k() +fun Server.channelsByName(name: String): ListK = getTextChannelsByName(name).k() fun Server.rolesByName(name: String): ListK = getRolesByNameIgnoreCase(name).k() fun Server.membersByName(name: String): ListK = getMembersByName(name).toList().k() fun Server.memberById(name: Long): Option = getMemberById(name).asOption() fun Server.categoriesByName(name: String): ListK = getChannelCategoriesByNameIgnoreCase(name).k() +fun MessageCreateEvent.getUser(): Option = Config.server.memberById(messageAuthor.id) + fun User.roles(): ListK = getRoles(Config.server).k() diff --git a/src/main/kotlin/moe/kageru/kagebot/features/TempVCFeature.kt b/src/main/kotlin/moe/kageru/kagebot/features/TempVCFeature.kt index 965b375..32ea8ee 100644 --- a/src/main/kotlin/moe/kageru/kagebot/features/TempVCFeature.kt +++ b/src/main/kotlin/moe/kageru/kagebot/features/TempVCFeature.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import moe.kageru.kagebot.Log import moe.kageru.kagebot.Util.failed import moe.kageru.kagebot.config.Config +import moe.kageru.kagebot.extensions.categoriesByName import moe.kageru.kagebot.persistence.Dao import org.javacord.api.DiscordApi import org.javacord.api.entity.channel.ChannelCategory @@ -12,8 +13,7 @@ import org.javacord.api.event.message.MessageCreateEvent import java.util.concurrent.CompletionException class TempVCFeature(@JsonProperty("category") category: String? = null) : EventFeature, MessageFeature { - private val category: ChannelCategory? = - category?.let { Config.server.getChannelCategoriesByNameIgnoreCase(it).first() } + private val category: ChannelCategory? = category?.let { Config.server.categoriesByName(it).first() } override fun handle(message: MessageCreateEvent) { if (" " !in message.readableMessageContent) { diff --git a/src/main/kotlin/moe/kageru/kagebot/features/TimeoutFeature.kt b/src/main/kotlin/moe/kageru/kagebot/features/TimeoutFeature.kt index 8bc3139..f5f6434 100644 --- a/src/main/kotlin/moe/kageru/kagebot/features/TimeoutFeature.kt +++ b/src/main/kotlin/moe/kageru/kagebot/features/TimeoutFeature.kt @@ -35,7 +35,7 @@ class TimeoutFeature(@JsonProperty("role") role: String) : MessageFeature { Tuple3( findUser(it.a).orNull() ?: return@flatMap "Error: User ${it.a} not found, consider using the user ID".left(), - it.b.toLongOrNull() ?: return@flatMap "Error: malformed time".left(), + it.b.toLongOrNull() ?: return@flatMap "Error: malformed time “${it.b}”".left(), it.c ).right() }.on { (user, time, _) -> diff --git a/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt b/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt index e7405fe..f2b1bff 100644 --- a/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt +++ b/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt @@ -1,5 +1,7 @@ package moe.kageru.kagebot +import arrow.core.ListK +import arrow.core.Option import io.kotlintest.matchers.string.shouldContain import io.kotlintest.matchers.string.shouldNotContain import io.kotlintest.shouldBe @@ -10,6 +12,7 @@ import io.mockk.mockk import moe.kageru.kagebot.Kagebot.process import moe.kageru.kagebot.config.Config import moe.kageru.kagebot.config.ConfigParser +import moe.kageru.kagebot.extensions.* import org.javacord.api.entity.channel.ServerTextChannel import org.javacord.api.entity.message.embed.EmbedBuilder import org.javacord.api.entity.permission.Role @@ -63,7 +66,7 @@ object TestUtil { fun messageableAuthor(messages: MutableList = mutableListOf()): User { return mockk { - every { getRoles(any()) } returns emptyList() + every { roles() } returns ListK.empty() every { sendMessage(capture(messages)) } returns mockk(relaxed = true) } } @@ -74,29 +77,29 @@ object TestUtil { dmEmbeds: MutableList = mutableListOf() ) { val channel = mockk(relaxed = true) { - every { sendMessage(capture(sentEmbeds)) } returns mockk(relaxed = true) { - every { join() } returns mockk { - every { isCompletedExceptionally } returns false - } + every { sendMessage(capture(sentEmbeds)) } returns mockk(relaxed = true) { + every { join() } returns mockk { every { isCompletedExceptionally } returns false } - every { sendMessage(capture(sentMessages)) } returns mockk(relaxed = true) + every { isCompletedExceptionally } returns false } + every { sendMessage(capture(sentMessages)) } returns mockk(relaxed = true) + } Globals.api = mockk(relaxed = true) { every { getServerById(any()) } returns Optional.of(mockk(relaxed = true) { every { icon.ifPresent(any()) } just Runs - every { getTextChannelById(any()) } returns Optional.of(channel) - every { getTextChannelsByName(any()) } returns listOf(channel) - every { getRolesByNameIgnoreCase("testrole") } returns listOf(TEST_ROLE) - every { getRolesByNameIgnoreCase("timeout") } returns listOf(TIMEOUT_ROLE) - every { getChannelCategoriesByNameIgnoreCase(any()) } returns listOf(mockk()) + every { channelById(any()) } returns Option.just(channel) + every { channelsByName(any()) } returns ListK.just(channel) + every { rolesByName("testrole") } returns ListK.just(TEST_ROLE) + every { rolesByName("timeout") } returns ListK.just(TIMEOUT_ROLE) + every { categoriesByName(any()) } returns ListK.just(mockk()) every { createVoiceChannelBuilder().create() } returns mockk { every { isCompletedExceptionally } returns false every { join().idAsString } returns "12345" } - every { getMembersByName(any()) } returns listOf(mockk(relaxed = true) { + every { membersByName(any()) } returns ListK.just(mockk(relaxed = true) { every { id } returns 123 - every { getRoles(any()) } returns listOf(TEST_ROLE) + every { roles() } returns ListK.just(TEST_ROLE) every { sendMessage(capture(dmEmbeds)) } returns mockk(relaxed = true) { every { isCompletedExceptionally } returns false } diff --git a/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt b/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt index 81c95fa..1af632f 100644 --- a/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt +++ b/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt @@ -1,5 +1,6 @@ package moe.kageru.kagebot.command +import arrow.core.ListK import io.kotlintest.matchers.string.shouldContain import io.kotlintest.shouldBe import io.kotlintest.specs.StringSpec @@ -17,6 +18,8 @@ import moe.kageru.kagebot.TestUtil.withCommands import moe.kageru.kagebot.Util import moe.kageru.kagebot.Util.unwrap import moe.kageru.kagebot.config.Config +import moe.kageru.kagebot.extensions.roles +import moe.kageru.kagebot.extensions.rolesByName import moe.kageru.kagebot.persistence.Dao import org.javacord.api.entity.message.embed.EmbedBuilder import org.javacord.api.entity.permission.Role @@ -184,8 +187,8 @@ class CommandTest : StringSpec({ val calls = mutableListOf() val mockMessage = mockMessage("!restricted", replies = calls) every { mockMessage.messageAuthor.asUser() } returns Optional.of(mockk { - every { getRoles(any()) } returns listOf( - Config.server.getRolesByNameIgnoreCase("testrole")[0] + every { roles() } returns ListK.just( + Config.server.rolesByName("testrole").first() ) }) mockMessage.process() @@ -208,7 +211,7 @@ class CommandTest : StringSpec({ every { mockMessage.messageAuthor.asUser() } returns mockk { every { isPresent } returns true every { get().getRoles(any()) } returns listOf( - Config.server.getRolesByNameIgnoreCase("testrole")[0] + Config.server.rolesByName("testrole").first() ) } mockMessage.process() diff --git a/src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt b/src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt index 0686b85..23b52cf 100644 --- a/src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt +++ b/src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt @@ -9,6 +9,7 @@ import moe.kageru.kagebot.TestUtil.mockMessage import moe.kageru.kagebot.TestUtil.withCommands import moe.kageru.kagebot.TestUtil.withReplyContents import moe.kageru.kagebot.config.Config +import moe.kageru.kagebot.extensions.rolesByName import org.javacord.api.entity.message.embed.EmbedBuilder import java.util.* @@ -48,7 +49,7 @@ class HelpFeatureTest : StringSpec({ val message = mockMessage("!help", replyEmbeds = replies) every { message.messageAuthor.asUser() } returns Optional.of(mockk { every { getRoles(any()) } returns listOf( - Config.server.getRolesByNameIgnoreCase("testrole")[0] + Config.server.rolesByName("testrole").first() ) }) message.process()