From 931a8cdf199f96d7f3927b2788cf6e1a75e1d0ee Mon Sep 17 00:00:00 2001 From: kageru Date: Thu, 11 Jul 2019 20:14:22 +0200 Subject: [PATCH] Added !help feature --- .../moe/kageru/kagebot/command/Command.kt | 2 + .../moe/kageru/kagebot/config/RawConfig.kt | 7 ++- .../kageru/kagebot/features/DebugFeature.kt | 2 +- .../moe/kageru/kagebot/features/Features.kt | 11 ++-- .../kageru/kagebot/features/HelpFeature.kt | 26 ++++++++ .../kageru/kagebot/features/MessageFeature.kt | 10 ++-- .../kageru/kagebot/features/WelcomeFeature.kt | 2 +- src/main/resources/config.toml | 9 ++- .../kotlin/moe/kageru/kagebot/TestUtil.kt | 15 +++++ .../kagebot/features/HelpFeatureTest.kt | 60 +++++++++++++++++++ src/test/resources/testconfig.toml | 7 ++- 11 files changed, 132 insertions(+), 19 deletions(-) create mode 100644 src/main/kotlin/moe/kageru/kagebot/features/HelpFeature.kt create mode 100644 src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt diff --git a/src/main/kotlin/moe/kageru/kagebot/command/Command.kt b/src/main/kotlin/moe/kageru/kagebot/command/Command.kt index e10906e..48a6877 100644 --- a/src/main/kotlin/moe/kageru/kagebot/command/Command.kt +++ b/src/main/kotlin/moe/kageru/kagebot/command/Command.kt @@ -34,6 +34,8 @@ class Command(cmd: RawCommand) { embed = cmd.embed?.let(MessageUtil::mapToEmbed) } + fun isAllowed(message: MessageCreateEvent) = permissions?.isAllowed(message) ?: true + fun execute(message: MessageCreateEvent) { if (permissions?.isAllowed(message) == false) { if (config.localization.permissionDenied.isNotBlank()) { diff --git a/src/main/kotlin/moe/kageru/kagebot/config/RawConfig.kt b/src/main/kotlin/moe/kageru/kagebot/config/RawConfig.kt index 6687c8e..2c5fa05 100644 --- a/src/main/kotlin/moe/kageru/kagebot/config/RawConfig.kt +++ b/src/main/kotlin/moe/kageru/kagebot/config/RawConfig.kt @@ -43,12 +43,13 @@ class RawCommand( class RawPermissions(val hasOneOf: List?, val hasNoneOf: List?, val onlyDM: Boolean) class RawMessageActions(val delete: Boolean, val redirect: RawRedirect?) class RawRedirect(val target: String?, val anonymous: Boolean) -class RawFeatures(val welcome: RawWelcomeFeature?, val debug: RawDebugFeature?) +class RawFeatures(val welcome: RawWelcomeFeature?, val debug: RawDebugFeature?, val help: RawHelpFeature?) class RawWelcomeFeature( - val enabled: Boolean, + val enable: Boolean, val content: Map?, val fallbackChannel: String?, val fallbackMessage: String?, @SerializedName("command") val commandEnabled: Boolean ) -class RawDebugFeature(var enabled: Boolean) \ No newline at end of file +class RawDebugFeature(val enable: Boolean) +class RawHelpFeature(val enable: Boolean) \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/features/DebugFeature.kt b/src/main/kotlin/moe/kageru/kagebot/features/DebugFeature.kt index 09f4d1d..66e6d56 100644 --- a/src/main/kotlin/moe/kageru/kagebot/features/DebugFeature.kt +++ b/src/main/kotlin/moe/kageru/kagebot/features/DebugFeature.kt @@ -11,7 +11,7 @@ import java.time.Duration import java.time.temporal.ChronoUnit class DebugFeature(rawDebugFeatures: RawDebugFeature): MessageFeature() { - override val commandEnabled = rawDebugFeatures.enabled + override val commandEnabled = rawDebugFeatures.enable override fun handleInternal(message: MessageCreateEvent) { if (message.messageAuthor.isBotOwner) { diff --git a/src/main/kotlin/moe/kageru/kagebot/features/Features.kt b/src/main/kotlin/moe/kageru/kagebot/features/Features.kt index b6b1f59..1bddcda 100644 --- a/src/main/kotlin/moe/kageru/kagebot/features/Features.kt +++ b/src/main/kotlin/moe/kageru/kagebot/features/Features.kt @@ -2,17 +2,18 @@ package moe.kageru.kagebot.features import moe.kageru.kagebot.config.RawFeatures -class Features(val welcome: WelcomeFeature?, val debug: DebugFeature?) { +class Features(val welcome: WelcomeFeature?, val debug: DebugFeature?, val help: HelpFeature?) { constructor(rawFeatures: RawFeatures) : this( rawFeatures.welcome?.let(::WelcomeFeature), - rawFeatures.debug?.let(::DebugFeature) + rawFeatures.debug?.let(::DebugFeature), + rawFeatures.help?.let(::HelpFeature) ) - fun all() = listOfNotNull(this.welcome, this.debug) - fun allWithMessage() = listOfNotNull(this.welcome, this.debug).filterIsInstance() + fun all() = listOfNotNull(this.welcome, this.debug, this.help) + fun allWithMessage() = all().filterIsInstance() companion object { - val NONE = Features(null, null) + val NONE = Features(null, null, null) } } diff --git a/src/main/kotlin/moe/kageru/kagebot/features/HelpFeature.kt b/src/main/kotlin/moe/kageru/kagebot/features/HelpFeature.kt new file mode 100644 index 0000000..6d69b8c --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/features/HelpFeature.kt @@ -0,0 +1,26 @@ +package moe.kageru.kagebot.features + +import moe.kageru.kagebot.Globals +import moe.kageru.kagebot.MessageUtil +import moe.kageru.kagebot.command.MatchType +import moe.kageru.kagebot.config.RawHelpFeature +import org.javacord.api.event.message.MessageCreateEvent + +class HelpFeature(rawFeature: RawHelpFeature) : MessageFeature() { + override val commandEnabled = rawFeature.enable + + override fun handleInternal(message: MessageCreateEvent) { + if (message.readableMessageContent.startsWith("!help")) { + message.channel.sendMessage( + MessageUtil.getEmbedBuilder() + .addField("Commands:", listCommands(message)) + ) + } + } + + private fun listCommands(message: MessageCreateEvent) = + Globals.commands + .filter { it.matchType == MatchType.PREFIX && it.isAllowed(message) } + .map { it.trigger } + .joinToString("\n") +} \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/features/MessageFeature.kt b/src/main/kotlin/moe/kageru/kagebot/features/MessageFeature.kt index ba5737e..d0def66 100644 --- a/src/main/kotlin/moe/kageru/kagebot/features/MessageFeature.kt +++ b/src/main/kotlin/moe/kageru/kagebot/features/MessageFeature.kt @@ -3,13 +3,15 @@ package moe.kageru.kagebot.features import moe.kageru.kagebot.Globals import org.javacord.api.event.message.MessageCreateEvent -abstract class MessageFeature: Feature { +abstract class MessageFeature : Feature { abstract val commandEnabled: Boolean fun handle(message: MessageCreateEvent) { - Globals.commandCounter.incrementAndGet() - handleInternal(message) + if (commandEnabled) { + Globals.commandCounter.incrementAndGet() + handleInternal(message) + } } - abstract fun handleInternal(message: MessageCreateEvent) + internal abstract fun handleInternal(message: MessageCreateEvent) } \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/features/WelcomeFeature.kt b/src/main/kotlin/moe/kageru/kagebot/features/WelcomeFeature.kt index dbf2322..2501a0f 100644 --- a/src/main/kotlin/moe/kageru/kagebot/features/WelcomeFeature.kt +++ b/src/main/kotlin/moe/kageru/kagebot/features/WelcomeFeature.kt @@ -16,7 +16,7 @@ class WelcomeFeature(rawWelcome: RawWelcomeFeature) : MessageFeature() { } } - val enabled: Boolean = rawWelcome.enabled + val enabled: Boolean = rawWelcome.enable val embed: EmbedBuilder? by lazy { rawWelcome.content?.let(MessageUtil::mapToEmbed) } diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index 57b6e12..dd9e70a 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -9,11 +9,11 @@ permissionDenied = "You do not have permission to use this command." redirectedMessage = "says" messageDeleted = "Your message was deleted because it contained a banned word or phrase." -# If this is enabled, every new user will receive a welcome message. +# If this is enable, every new user will receive a welcome message. # If the user has disabled their DMs, the fallbackMessage will be sent in the fallbackChannel instead. # If no fallback channel or message is specified, no fallback will be sent. [feature.welcome] -enabled = true +enable = true # enable the !welcome command which will print the welcome embed command = true fallbackChannel = "555097559023222825" @@ -26,7 +26,10 @@ fallbackMessage = "@@ I would like to greet you, but I can’t. :(" # allow the bot owner to get debug stats [feature.debug] -enabled = true +enable = true + +[feature.help] +enable = true [[command]] trigger = "!ping" diff --git a/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt b/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt index d7b6660..7329fe0 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 io.kotlintest.matchers.string.shouldContain +import io.kotlintest.matchers.string.shouldNotContain import io.kotlintest.shouldBe import io.mockk.Runs import io.mockk.every @@ -100,4 +102,17 @@ object TestUtil { test() Globals.config.localization = oldLoc } + + fun assertEmbedContents(expected: List, unexpected: List, op: (MutableList) -> Unit) { + val replies = mutableListOf() + op(replies) + replies.size shouldBe 1 + val replyString = embedToString(replies[0]) + for (string in expected) { + replyString shouldContain string + } + for (string in unexpected) { + replyString shouldNotContain string + } + } } \ No newline at end of file diff --git a/src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt b/src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt new file mode 100644 index 0000000..e5735e0 --- /dev/null +++ b/src/test/kotlin/moe/kageru/kagebot/features/HelpFeatureTest.kt @@ -0,0 +1,60 @@ +package moe.kageru.kagebot.features + +import io.kotlintest.specs.StringSpec +import io.mockk.every +import io.mockk.mockk +import moe.kageru.kagebot.Globals +import moe.kageru.kagebot.TestUtil +import moe.kageru.kagebot.TestUtil.assertEmbedContents +import moe.kageru.kagebot.TestUtil.mockMessage +import moe.kageru.kagebot.TestUtil.withCommands +import moe.kageru.kagebot.config.RawHelpFeature +import org.javacord.api.entity.message.embed.EmbedBuilder +import java.util.Optional + +class HelpFeatureTest : StringSpec({ + val sentEmbeds = mutableListOf() + TestUtil.prepareTestEnvironment(sentEmbeds = sentEmbeds) + val commandConfig = """ + [[command]] + trigger = "!ping" + [[command]] + trigger = "!something" + [[command]] + trigger = "not a prefix" + matchType = "CONTAINS" + [[command]] + trigger = "!prison" + [command.permissions] + hasOneOf = ["testrole"] + """.trimIndent() + "should show prefix command" { + withCommands(commandConfig) { + val expected = listOf("!ping", "!something") + val unexpected = listOf("not a prefix", "!prison") + assertEmbedContents(expected = expected, unexpected = unexpected) { replies -> + HelpFeature(RawHelpFeature(true)) + .handle(message = mockMessage("!help", replyEmbeds = replies)) + //Kagebot.processMessage(TestUtil.mockMessage("!help", replyEmbeds = replies)) + } + } + } + "should show moderation commands for mod" { + withCommands(commandConfig) { + val expected = listOf("!ping", "!something", "!prison") + val unexpected = listOf("not a prefix") + assertEmbedContents(expected = expected, unexpected = unexpected) { replies -> + val message = mockMessage("!help", replyEmbeds = replies) + every { message.messageAuthor.asUser() } returns Optional.of(mockk { + every { getRoles(any()) } returns listOf( + Globals.server.getRolesByNameIgnoreCase("testrole")[0] + ) + }) + HelpFeature(RawHelpFeature(true)) + .handle(message = message) + //Kagebot.processMessage(TestUtil.mockMessage("!help", replyEmbeds = replies)) + } + } + } +}) + diff --git a/src/test/resources/testconfig.toml b/src/test/resources/testconfig.toml index db79fea..4c42ac4 100644 --- a/src/test/resources/testconfig.toml +++ b/src/test/resources/testconfig.toml @@ -8,11 +8,14 @@ redirectedMessage = "says" messageDeleted = "message dongered" [feature.welcome] -enabled = true +enable = true fallbackChannel = "123" fallbackMessage = "@@ welcome" # This is a list of pairs where the key is the title and the value the content of the paragraph. # Do not use empty strings to get empty headings or paragraphs. The discord API rejects those. [feature.welcome.content] "Welcome to the Server" = "This is the content of the first paragraph" -"Second paragraph heading" = "Second paragraph content" \ No newline at end of file +"Second paragraph heading" = "Second paragraph content" + +[feature.help] +enable = true \ No newline at end of file