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 0c3bd10..0591074 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 @@ -6,7 +6,7 @@ import io.github.null2264.tsukumogami.core.module.api.botModules import kotlinx.datetime.Clock val generalModule = botModules("General") { - commands("ping", description = "Ping the bot!") { ctx -> + commands("ping", alias = setOf("p"), description = "Ping the bot!") { ctx -> val startTime = Clock.System.now() ctx.typing() val endTime = Clock.System.now() diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt index 418f46e..a68e8f0 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt @@ -34,18 +34,28 @@ open class Bot internal constructor(): IGroup { _prefixes.add(prefix) } - fun addCommand(command: Command) { - if (allCommands.containsKey(command.name)) { + private fun addCommand(name: String, command: Command) { + if (allCommands.containsKey(name)) { throw IllegalStateException("Duplicate command: '${command.name}'") } allCommands[command.name] = command } - fun removeCommand(command: Command) { - if (!allCommands.containsKey(command.name)) { - throw IllegalStateException("Command not found: '${command.name}'") + fun addCommand(command: Command) { + addCommand(command.name, command) + command.alias.forEach { addCommand(it, command) } + } + + private fun removeCommand(name: String) { + if (!allCommands.containsKey(name)) { + throw IllegalStateException("Command not found: '${name}'") } - allCommands.remove(command.name) + allCommands.remove(name) + } + + fun removeCommand(command: Command) { + removeCommand(command.name) + command.alias.forEach { removeCommand(it) } } open suspend fun start() { @@ -67,9 +77,10 @@ open class Bot internal constructor(): IGroup { fun getCommand(name: String?) = allCommands[name] private fun getContext(message: Message): Context { - val candidate = message.content.parsePrefixCommandAndArguments() - val context = Context(this, message, candidate?.first, candidate?.second?.toMutableList()) - context.command = getCommand(context.pullCandidateOrNull()) + val parsed = message.content.parsePrefixAndCommand() + val context = Context(this, message, parsed?.first) + context.command = getCommand(parsed?.second) + if (context.command != null) context.invokedWith = parsed?.second return context } @@ -99,17 +110,18 @@ open class Bot internal constructor(): IGroup { Logger.i { "Online! ${client.getSelf().username}" } } - private fun String.parsePrefixCommandAndArguments(): Pair>? { + private fun String.parsePrefixAndCommand(): Pair? { if (this.isBlank()) return null - var ret: Pair>? = null + var ret: Pair? = null - prefixes.forEach { - if (this.substring(0, it.length) == it) { - val prefix = this.substring(0, it.length) - val cleanPrompt = this.substring(it.length) - ret = Pair(prefix, cleanPrompt.parseCommandAndArguments()) - return@forEach + run { + prefixes.forEach { candidate -> + val prefix = this.substring(0, candidate.length) + if (prefix != candidate) { return@forEach } + val command = this.drop(candidate.length).substringBefore(' ') + ret = prefix to command + return@run } } 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 7c8cac4..bb71633 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 @@ -8,34 +8,30 @@ import io.github.null2264.tsukumogami.core.commands.Command class Context( val bot: Bot, - private val message: Message, + val message: Message, /** * The prefix that used to invoke the command */ val prefix: String?, - /** - * Potential command name and/or arguments - */ - val candidate: MutableList?, ) { /** * The user that invoked the command + * + * Alias to [Message.author] */ val author get() = message.author + /** + * The text that invoked the command + */ + var invokedWith: String? = null + /** * The command that currently being invoked */ var command: Command? = null - fun getCandidateOrNull() = candidate?.getOrNull(0) - fun pullCandidateOrNull() = candidate?.removeFirstOrNull() - fun pullCandidateIf(predicate: () -> Boolean): String? { - if (predicate()) return candidate?.removeFirstOrNull() - return null - } - suspend fun send(content: String) = message.channel.createMessage(content) suspend fun reply(content: String, mentionsAuthor: Boolean = false) = message.channel.createMessage { @@ -48,4 +44,15 @@ class Context( } suspend fun typing() = message.channel.type() + + fun parseArguments(): MutableList? = + if (prefix != null && invokedWith != null) { + message.content + .substringAfter("$prefix$invokedWith") + .trim() + .split(' ') + .toMutableList() + } else { + null + } } 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 index d593c69..860e23e 100644 --- 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 @@ -17,7 +17,7 @@ abstract class Arguments { } suspend fun parse(context: Context) { - val currentValues = context.candidate?.toMutableList() + val currentValues = context.parseArguments() run { args.forEach { arg -> diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt index af7897d..0c41d48 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt @@ -19,9 +19,13 @@ class Group( override val allCommands: MutableMap = mutableMapOf() override suspend fun invoke(context: Context) { - val command = allCommands[context.getCandidateOrNull()] - context.pullCandidateIf { command != null } ?: return - context.command = command!! + val subcommandName = context.message.content + .substringAfter("${context.prefix}${context.invokedWith}") + .trim() + .substringBefore(' ') + val command = allCommands[subcommandName] ?: return + context.invokedWith += " $subcommandName" + context.command = command command.invoke(context) } } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/IGroup.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/IGroup.kt index 9f87233..dcc1640 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/IGroup.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/IGroup.kt @@ -9,7 +9,6 @@ interface IGroup { private fun addCommand(command: Command) { allCommands[command.name] = command - command.alias.forEach { allCommands[it] = command } } fun commands(