Add features as configurable command output

This commit is contained in:
kageru 2019-07-11 21:05:35 +02:00
parent 731867f59e
commit 344148cd03
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
16 changed files with 73 additions and 108 deletions

@ -3,6 +3,7 @@ package moe.kageru.kagebot
import moe.kageru.kagebot.command.Command
import moe.kageru.kagebot.config.Config
import moe.kageru.kagebot.config.SystemConfig
import moe.kageru.kagebot.features.Features
import org.javacord.api.DiscordApi
import org.javacord.api.entity.server.Server
import java.util.concurrent.atomic.AtomicInteger
@ -13,5 +14,6 @@ object Globals {
lateinit var config: Config
lateinit var commands: List<Command>
lateinit var systemConfig: SystemConfig
lateinit var features: Features
var commandCounter: AtomicInteger = AtomicInteger(0)
}

@ -4,7 +4,6 @@ import moe.kageru.kagebot.Log.log
import moe.kageru.kagebot.Util.checked
import moe.kageru.kagebot.config.Config
import moe.kageru.kagebot.config.RawConfig
import moe.kageru.kagebot.features.MessageFeature
import org.javacord.api.DiscordApiBuilder
import org.javacord.api.event.message.MessageCreateEvent
import org.javacord.api.event.server.member.ServerMemberJoinEvent
@ -65,18 +64,9 @@ object Kagebot {
})
log.info("kagebot Mk II running")
Globals.api.addMessageCreateListener { checked { processMessage(it) } }
Globals.config.features.welcome?.let { welcome ->
if (welcome.enabled) {
Globals.api.addServerMemberJoinListener {
checked { welcomeUser(it) }
}
}
}
for (feature in Globals.config.features.allWithMessage()) {
if (feature.commandEnabled) {
Globals.api.addMessageCreateListener {
checked { feature.handle(it) }
}
Globals.config.features.welcome?.let {
Globals.api.addServerMemberJoinListener {
checked { welcomeUser(it) }
}
}
}

@ -6,6 +6,7 @@ import moe.kageru.kagebot.Log.log
import moe.kageru.kagebot.MessageUtil
import moe.kageru.kagebot.Util.doIf
import moe.kageru.kagebot.config.RawCommand
import moe.kageru.kagebot.features.MessageFeature
import org.javacord.api.entity.message.MessageAuthor
import org.javacord.api.entity.message.embed.EmbedBuilder
import org.javacord.api.event.message.MessageCreateEvent
@ -20,6 +21,7 @@ class Command(cmd: RawCommand) {
private val actions: MessageActions?
val regex: Regex?
val embed: EmbedBuilder?
val feature: MessageFeature?
init {
trigger = cmd.trigger ?: throw IllegalArgumentException("Every command must have a trigger.")
@ -32,6 +34,7 @@ class Command(cmd: RawCommand) {
actions = cmd.actions?.let { MessageActions(it) }
regex = if (matchType == MatchType.REGEX) Regex(trigger) else null
embed = cmd.embed?.let(MessageUtil::mapToEmbed)
feature = cmd.feature?.let { Globals.features.findByString(it) }
}
fun isAllowed(message: MessageCreateEvent) = permissions?.isAllowed(message) ?: true
@ -53,6 +56,7 @@ class Command(cmd: RawCommand) {
this.embed?.let {
message.channel.sendMessage(embed)
}
this.feature?.handle(message)
}
fun matches(msg: String) = this.matchType.matches(msg, this)

@ -16,9 +16,11 @@ class Config(rawConfig: RawConfig) {
init {
Globals.systemConfig = system
Globals.server = api.getServerById(system.serverId).orElseThrow()
Globals.features = rawConfig.features?.let(::Features) ?: Features(RawFeatures(null))
// TODO: remove this
this.features = Globals.features
Globals.commands = rawConfig.commands?.map(::Command) ?: emptyList()
Globals.config = this
this.features = rawConfig.features?.let(::Features) ?: Features.NONE
}
fun reloadLocalization(rawLocalization: RawLocalization) {

@ -8,9 +8,10 @@ class RawCommand(
val matchType: String?,
val permissions: RawPermissions?,
@SerializedName("action") val actions: RawMessageActions?,
val embed: Map<String, String>?
val embed: Map<String, String>?,
val feature: String?
)
class RawPermissions(val hasOneOf: List<String>?, val hasNoneOf: List<String>?, val onlyDM: Boolean)
class RawMessageActions(val delete: Boolean, val redirect: RawRedirect?)
class RawRedirect(val target: String?, val anonymous: Boolean)
class RawRedirect(val target: String?, val anonymous: Boolean)

@ -1,16 +1,4 @@
package moe.kageru.kagebot.config
import com.google.gson.annotations.SerializedName
class RawFeatures(val welcome: RawWelcomeFeature?, val debug: RawDebugFeature?, val help: RawHelpFeature?)
class RawWelcomeFeature(
val enable: Boolean,
val content: Map<String, String>?,
val fallbackChannel: String?,
val fallbackMessage: String?,
@SerializedName("command") val commandEnabled: Boolean
)
class RawDebugFeature(val enable: Boolean)
class RawHelpFeature(val enable: Boolean)
class RawFeatures(val welcome: RawWelcomeFeature?)
class RawWelcomeFeature(val content: Map<String, String>?, val fallbackChannel: String?, val fallbackMessage: String?)

@ -3,21 +3,17 @@ package moe.kageru.kagebot.features
import com.sun.management.OperatingSystemMXBean
import moe.kageru.kagebot.Globals
import moe.kageru.kagebot.MessageUtil
import moe.kageru.kagebot.config.RawDebugFeature
import org.javacord.api.entity.message.embed.EmbedBuilder
import org.javacord.api.event.message.MessageCreateEvent
import java.lang.management.ManagementFactory
import java.time.Duration
import java.time.temporal.ChronoUnit
class DebugFeature(rawDebugFeatures: RawDebugFeature): MessageFeature() {
override val commandEnabled = rawDebugFeatures.enable
class DebugFeature : MessageFeature() {
override fun handleInternal(message: MessageCreateEvent) {
if (message.messageAuthor.isBotOwner) {
if (message.readableMessageContent.startsWith("!debugstats")) {
message.channel.sendMessage(getPerformanceStats())
}
message.channel.sendMessage(getPerformanceStats())
}
}

@ -2,19 +2,14 @@ package moe.kageru.kagebot.features
import moe.kageru.kagebot.config.RawFeatures
class Features(val welcome: WelcomeFeature?, val debug: DebugFeature?, val help: HelpFeature?) {
class Features(val welcome: WelcomeFeature?, debug: DebugFeature, help: HelpFeature) {
constructor(rawFeatures: RawFeatures) : this(
rawFeatures.welcome?.let(::WelcomeFeature),
rawFeatures.debug?.let(::DebugFeature),
rawFeatures.help?.let(::HelpFeature)
DebugFeature(),
HelpFeature()
)
fun all() = listOfNotNull(this.welcome, this.debug, this.help)
fun allWithMessage() = all().filterIsInstance<MessageFeature>()
private val featureMap = mapOf("help" to help, "debug" to debug, "welcome" to welcome)
companion object {
val NONE = Features(null, null, null)
}
}
interface Feature
fun findByString(feature: String) = featureMap[feature]
}

@ -3,24 +3,18 @@ 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
class HelpFeature : MessageFeature() {
override fun handleInternal(message: MessageCreateEvent) {
if (message.readableMessageContent.startsWith("!help")) {
message.channel.sendMessage(
MessageUtil.getEmbedBuilder()
.addField("Commands:", listCommands(message))
)
}
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")
}
private fun listCommands(message: MessageCreateEvent) = Globals.commands
.filter { it.matchType == MatchType.PREFIX && it.isAllowed(message) }
.map { it.trigger }
.joinToString("\n")

@ -1,16 +1,10 @@
package moe.kageru.kagebot.features
import moe.kageru.kagebot.Globals
import org.javacord.api.event.message.MessageCreateEvent
abstract class MessageFeature : Feature {
abstract val commandEnabled: Boolean
abstract class MessageFeature {
fun handle(message: MessageCreateEvent) {
if (commandEnabled) {
Globals.commandCounter.incrementAndGet()
handleInternal(message)
}
handleInternal(message)
}
internal abstract fun handleInternal(message: MessageCreateEvent)

@ -8,15 +8,10 @@ import org.javacord.api.entity.message.embed.EmbedBuilder
import org.javacord.api.event.message.MessageCreateEvent
class WelcomeFeature(rawWelcome: RawWelcomeFeature) : MessageFeature() {
override val commandEnabled = rawWelcome.commandEnabled
override fun handleInternal(message: MessageCreateEvent) {
if (message.readableMessageContent == "!welcome") {
message.channel.sendMessage(embed)
}
message.channel.sendMessage(embed)
}
val enabled: Boolean = rawWelcome.enable
val embed: EmbedBuilder? by lazy {
rawWelcome.content?.let(MessageUtil::mapToEmbed)
}

@ -13,9 +13,6 @@ messageDeleted = "Your message was deleted because it contained a banned word or
# 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]
enable = true
# enable the !welcome command which will print the welcome embed
command = true
fallbackChannel = "555097559023222825"
fallbackMessage = "@@ I would like to greet you, but I can’t. :("
# This is a list of pairs where the key is the title and the value the content of the paragraph.
@ -24,13 +21,6 @@ fallbackMessage = "@@ I would like to greet you, but I can’t. :("
"Welcome to the Server" = "This is the content of the first paragraph"
"Second paragraph heading" = "Second paragraph content"
# allow the bot owner to get debug stats
[feature.debug]
enable = true
[feature.help]
enable = true
[[command]]
trigger = "!ping"
response = "pong"
@ -95,3 +85,15 @@ response = "redirected"
[command.action.redirect]
target = "555097559023222825"
anonymous = true
[[command]]
trigger = "!debug"
feature = "debug"
[[command]]
trigger = "!welcome"
feature = "welcome"
[[command]]
trigger = "!help"
feature = "help"

@ -9,6 +9,6 @@ class ConfigTest : StringSpec({
"should properly parse test config" {
Globals.config shouldNotBe null
Globals.systemConfig shouldNotBe null
Globals.commands shouldBe emptyList()
Globals.commands.size shouldBe 2
}
})

@ -4,8 +4,8 @@ import io.kotlintest.specs.StringSpec
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import moe.kageru.kagebot.Kagebot
import moe.kageru.kagebot.TestUtil
import moe.kageru.kagebot.config.RawDebugFeature
import org.javacord.api.entity.message.embed.EmbedBuilder
import org.javacord.api.event.message.MessageCreateEvent
@ -14,19 +14,19 @@ class DebugFeatureTest : StringSpec({
// this will fail if the bot tries to execute more than it should
// because the mock does not provide the necessary methods
"should ignore regular users" {
val message = mockk<MessageCreateEvent> {
every { messageAuthor.isBotOwner } returns false
}
DebugFeature(RawDebugFeature(true)).handle(message)
val message = TestUtil.mockMessage("!debug")
every { message.messageAuthor.isBotOwner } returns false
Kagebot.processMessage(message)
DebugFeature().handle(message)
verify(exactly = 0) { message.channel.sendMessage(any<EmbedBuilder>()) }
}
"should return something" {
val message = mockk<MessageCreateEvent> {
every { messageAuthor.isBotOwner } returns true
every { readableMessageContent } returns "!debugstats something"
every { readableMessageContent } returns "!debug"
every { channel.sendMessage(any<EmbedBuilder>()) } returns mockk()
}
DebugFeature(RawDebugFeature(true)).handle(message)
DebugFeature().handle(message)
verify(exactly = 1) { message.channel.sendMessage(any<EmbedBuilder>()) }
}
})

@ -4,18 +4,21 @@ import io.kotlintest.specs.StringSpec
import io.mockk.every
import io.mockk.mockk
import moe.kageru.kagebot.Globals
import moe.kageru.kagebot.Kagebot
import moe.kageru.kagebot.TestUtil
import moe.kageru.kagebot.TestUtil.withReplyContents
import moe.kageru.kagebot.TestUtil.mockMessage
import moe.kageru.kagebot.TestUtil.withCommands
import moe.kageru.kagebot.config.RawHelpFeature
import moe.kageru.kagebot.TestUtil.withReplyContents
import org.javacord.api.entity.message.embed.EmbedBuilder
import java.util.Optional
import java.util.*
class HelpFeatureTest : StringSpec({
val sentEmbeds = mutableListOf<EmbedBuilder>()
TestUtil.prepareTestEnvironment(sentEmbeds = sentEmbeds)
val commandConfig = """
[[command]]
trigger = "!help"
feature = "help"
[[command]]
trigger = "!ping"
[[command]]
@ -33,9 +36,7 @@ class HelpFeatureTest : StringSpec({
val expected = listOf("!ping", "!something")
val unexpected = listOf("not a prefix", "!prison")
withReplyContents(expected = expected, unexpected = unexpected) { replies ->
HelpFeature(RawHelpFeature(true))
.handle(message = mockMessage("!help", replyEmbeds = replies))
//Kagebot.processMessage(TestUtil.mockMessage("!help", replyEmbeds = replies))
Kagebot.processMessage(mockMessage("!help", replyEmbeds = replies))
}
}
}
@ -50,11 +51,8 @@ class HelpFeatureTest : StringSpec({
Globals.server.getRolesByNameIgnoreCase("testrole")[0]
)
})
HelpFeature(RawHelpFeature(true))
.handle(message = message)
//Kagebot.processMessage(TestUtil.mockMessage("!help", replyEmbeds = replies))
Kagebot.processMessage(message)
}
}
}
})

@ -8,7 +8,6 @@ redirectedMessage = "says"
messageDeleted = "message dongered"
[feature.welcome]
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.
@ -17,5 +16,10 @@ fallbackMessage = "@@ welcome"
"Welcome to the Server" = "This is the content of the first paragraph"
"Second paragraph heading" = "Second paragraph content"
[feature.help]
enable = true
[[command]]
trigger = "!debug"
feature = "debug"
[[command]]
trigger = "!welcome"
feature = "welcome"