diff --git a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/DeveloperModule.kt b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/DeveloperModule.kt index 95f82f9..17a07d9 100644 --- a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/DeveloperModule.kt +++ b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/DeveloperModule.kt @@ -1,6 +1,6 @@ package io.github.null2264.tsukumogami.bot.core.module -import io.github.null2264.tsukumogami.core.module.annotation.Command +import io.github.null2264.tsukumogami.core.commands.annotation.Command import io.github.null2264.tsukumogami.core.Context import io.github.null2264.tsukumogami.core.module.BotModule diff --git a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt index ce39762..b3adb11 100644 --- a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt +++ b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt @@ -1,7 +1,7 @@ package io.github.null2264.tsukumogami.bot.core.module import dev.kord.core.entity.effectiveName -import io.github.null2264.tsukumogami.core.module.annotation.Command +import io.github.null2264.tsukumogami.core.commands.annotation.Command import io.github.null2264.tsukumogami.core.Context import io.github.null2264.tsukumogami.core.module.BotModule import kotlinx.datetime.Clock diff --git a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/arguments/TestArguments.kt b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/arguments/TestArguments.kt new file mode 100644 index 0000000..e252549 --- /dev/null +++ b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/arguments/TestArguments.kt @@ -0,0 +1,8 @@ +package io.github.null2264.tsukumogami.bot.core.module.arguments + +import io.github.null2264.tsukumogami.core.commands.Arguments +import io.github.null2264.tsukumogami.core.commands.ext.string + +class TestArguments : Arguments() { + val test by string("Test") +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/AbstractBot.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/AbstractBot.kt index 594bd09..e04126f 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/AbstractBot.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/AbstractBot.kt @@ -11,7 +11,8 @@ import dev.kord.gateway.PrivilegedIntent import io.github.null2264.tsukumogami.core.exceptions.CommandException import io.github.null2264.tsukumogami.core.exceptions.CommandNotFound import io.github.null2264.tsukumogami.core.module.BotModule -import io.github.null2264.tsukumogami.core.module.CommandHolder +import io.github.null2264.tsukumogami.core.commands.CommandHolder +import io.github.null2264.tsukumogami.core.utils.parseCommandAndArguments import kotlin.reflect.full.callSuspend import kotlinx.coroutines.runBlocking @@ -22,13 +23,13 @@ abstract class AbstractBot(configurator: BotConfigurator.() -> Unit) { private val prefixes: List private val client: Kord + // TODO: Bind Bot and Kord to Koin init { val currentConfig = BotConfigurator() currentConfig.apply(configurator) extensions = mutableMapOf() - currentConfig.extensions.forEach { - val module = it.call() + currentConfig.extensions.forEach { module -> module.setup() module.install(this, currentConfig) extensions[module.name] = module @@ -59,7 +60,7 @@ abstract class AbstractBot(configurator: BotConfigurator.() -> Unit) { fun getCommand(name: String) = commands[name] private fun getContext(message: Message): Context { - val candidate = message.content.hasPrefix() + val candidate = message.content.parsePrefixCommandAndArguments() return Context(this, message, candidate?.first, candidate?.second) } @@ -91,14 +92,16 @@ abstract class AbstractBot(configurator: BotConfigurator.() -> Unit) { Logger.i { "Online! ${client.getSelf().username}" } } - fun String.hasPrefix(): Pair? { + private fun String.parsePrefixCommandAndArguments(): Pair>? { if (this.isBlank()) return null - var ret: Pair? = null + var ret: Pair>? = null prefixes.forEach { if (this.substring(0, it.length) == it) { - ret = Pair(this.substring(0, it.length), this.substring(it.length).split(" ").first()) + val prefix = this.substring(0, it.length) + val cleanPrompt = this.substring(it.length) + ret = Pair(prefix, cleanPrompt.parseCommandAndArguments()) return@forEach } } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/BotConfigurator.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/BotConfigurator.kt index dbdfc8a..ce46057 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/BotConfigurator.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/BotConfigurator.kt @@ -1,15 +1,14 @@ package io.github.null2264.tsukumogami.core +import io.github.null2264.tsukumogami.core.module.internal.Modules import io.github.null2264.tsukumogami.core.module.BotModule -import io.github.null2264.tsukumogami.core.module.CommandHolder -import kotlin.reflect.KClass +import io.github.null2264.tsukumogami.core.commands.CommandHolder import kotlin.reflect.KFunction -import kotlin.reflect.full.isSubclassOf class BotConfigurator internal constructor() { internal val commands = mutableMapOf() - internal val extensions = mutableListOf>() + internal val extensions = Modules() internal val prefixes = mutableListOf() var token: String = "" @@ -20,13 +19,7 @@ class BotConfigurator internal constructor() { } fun extensions(vararg extensions: KFunction) { - extensions.forEach { - val kClass = it.returnType.classifier as KClass<*> - if (!kClass.isSubclassOf(BotModule::class)) - return - - this.extensions.add(it) - } + this.extensions.initializeAndAddAll(extensions.toList()) } fun prefixes(vararg prefixes: String) { diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt index baed03d..6f2ad19 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt @@ -5,10 +5,10 @@ import dev.kord.core.entity.Message import dev.kord.rest.builder.message.AllowedMentionsBuilder import dev.kord.rest.builder.message.allowedMentions -class Context(private val bot: AbstractBot, private val message: Message, val prefix: String?, private val commandName: String?) { +class Context(private val bot: AbstractBot, private val message: Message, val prefix: String?, private val commandAndArguments: List?) { val author get() = message.author - val command get() = commandName?.let { bot.getCommand(it) } + val command get() = commandAndArguments?.get(0)?.let { bot.getCommand(it) } suspend fun send(content: String) = message.channel.createMessage(content) diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Argument.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Argument.kt new file mode 100644 index 0000000..bc85b9b --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Argument.kt @@ -0,0 +1,12 @@ +package io.github.null2264.tsukumogami.core.commands + +import io.github.null2264.tsukumogami.core.commands.converters.Converter + +data class Argument( + val name: String, + val converter: Converter, +) { + init { + converter.argumentObj = this + } +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Arguments.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Arguments.kt new file mode 100644 index 0000000..686be91 --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Arguments.kt @@ -0,0 +1,16 @@ +package io.github.null2264.tsukumogami.core.commands + +import io.github.null2264.tsukumogami.core.commands.converters.Converter + +open class Arguments { + val args = mutableListOf>() + + fun args( + name: String, + converter: Converter + ): Converter { + args.add(Argument(name, converter)) + + return converter + } +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/CommandHolder.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/CommandHolder.kt similarity index 67% rename from core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/CommandHolder.kt rename to core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/CommandHolder.kt index f2a56dc..25c9a41 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/CommandHolder.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/CommandHolder.kt @@ -1,11 +1,11 @@ -package io.github.null2264.tsukumogami.core.module +package io.github.null2264.tsukumogami.core.commands import kotlin.reflect.KFunction /** * Class holding information about a command */ -internal data class CommandHolder( +data class CommandHolder internal constructor( val name: String, val extension: String, val description: String? = null, diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/annotation/Command.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/annotation/Command.kt similarity index 85% rename from core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/annotation/Command.kt rename to core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/annotation/Command.kt index 9d6bf50..9756178 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/annotation/Command.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/annotation/Command.kt @@ -1,5 +1,4 @@ -package io.github.null2264.tsukumogami.core.module.annotation - +package io.github.null2264.tsukumogami.core.commands.annotation /** * Annotation to tag a function as command diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/Converter.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/Converter.kt new file mode 100644 index 0000000..cc9ed82 --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/Converter.kt @@ -0,0 +1,17 @@ +package io.github.null2264.tsukumogami.core.commands.converters + +import io.github.null2264.tsukumogami.core.commands.Argument +import io.github.null2264.tsukumogami.core.commands.Arguments +import kotlin.reflect.KProperty + +abstract class Converter { + lateinit var argumentObj: Argument + + abstract var parsed: OutputType + + abstract suspend fun parse(input: String): OutputType + + operator fun getValue(thisRef: Arguments, property: KProperty<*>): OutputType { + return parsed + } +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/StringConverter.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/StringConverter.kt new file mode 100644 index 0000000..723c52a --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/StringConverter.kt @@ -0,0 +1,11 @@ +package io.github.null2264.tsukumogami.core.commands.converters.impl + +import io.github.null2264.tsukumogami.core.commands.converters.Converter + +class StringConverter : Converter() { + override var parsed: String = "" + override suspend fun parse(input: String): String { + this.parsed = input + return input + } +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/ext/ArgumentsExtensions.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/ext/ArgumentsExtensions.kt new file mode 100644 index 0000000..c48824b --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/ext/ArgumentsExtensions.kt @@ -0,0 +1,6 @@ +package io.github.null2264.tsukumogami.core.commands.ext + +import io.github.null2264.tsukumogami.core.commands.Arguments +import io.github.null2264.tsukumogami.core.commands.converters.impl.StringConverter + +fun Arguments.string(name: String) = args(name, StringConverter()) diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt index 1cb21a6..ef96d0e 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt @@ -2,8 +2,9 @@ package io.github.null2264.tsukumogami.core.module import co.touchlab.kermit.Logger import io.github.null2264.tsukumogami.core.AbstractBot -import io.github.null2264.tsukumogami.core.module.annotation.Command +import io.github.null2264.tsukumogami.core.commands.annotation.Command import io.github.null2264.tsukumogami.core.BotConfigurator +import io.github.null2264.tsukumogami.core.commands.CommandHolder import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.kotlinFunction diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/internal/Modules.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/internal/Modules.kt new file mode 100644 index 0000000..01ae3e2 --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/internal/Modules.kt @@ -0,0 +1,40 @@ +package io.github.null2264.tsukumogami.core.module.internal + +import io.github.null2264.tsukumogami.core.module.BotModule +import java.util.function.Consumer +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.full.isSubclassOf + +class Modules { + + private val list = mutableListOf() + + fun get(index: Int) = list[index] + + private fun KFunction.tryInitialize(): BotModule? { + val kClass = this.returnType.classifier as KClass<*> + if (!kClass.isSubclassOf(BotModule::class)) + return null + + return this.call() + } + + fun initializeAndAddAll(modules: List>) { + addAll(modules.mapNotNull { it.tryInitialize() }) + } + + fun addAll(modules: List) { + list.addAll(modules) + } + + fun add(module: KFunction) { + module.tryInitialize()?.let { add(it) } + } + + fun add(module: BotModule) { + list.add(module) + } + + fun forEach(consumer: Consumer) = list.forEach(consumer) +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/utils/StringExtensions.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/utils/StringExtensions.kt new file mode 100644 index 0000000..0ed11fe --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/utils/StringExtensions.kt @@ -0,0 +1,56 @@ +package io.github.null2264.tsukumogami.core.utils + +fun String.parseCommandAndArguments(): List { + if (isBlank()) { + return emptyList() + } + + val result = mutableListOf() + val currentToken = StringBuilder() + var inQuotes = false + + for (char in this) { + when (char) { + '"' -> { + if (inQuotes) { + // Closing quote: add the accumulated token + result.add(currentToken.toString()) + currentToken.clear() + inQuotes = false + } else { + // Opening quote: + // If there's an existing token (e.g., word"another"), add it first + if (currentToken.isNotEmpty()) { + result.add(currentToken.toString()) + currentToken.clear() + } + inQuotes = true + } + } + ' ' -> { + if (inQuotes) { + // Space inside quotes: append it + currentToken.append(char) + } else { + // Space outside quotes: separator + if (currentToken.isNotEmpty()) { + result.add(currentToken.toString()) + currentToken.clear() + } + // Ignore multiple spaces between tokens + } + } + else -> { + // Any other character: append it + currentToken.append(char) + } + } + } + + // Add any remaining token after the loop (e.g., if the string doesn't end with a quote or space) + if (currentToken.isNotEmpty()) { + result.add(currentToken.toString()) + } + + return result +}