refactor: Adjust how command is invoked

This commit is contained in:
Ahmad Ansori Palembani 2025-05-23 15:07:09 +07:00
parent 04472f8bfa
commit a62d86563a
6 changed files with 57 additions and 35 deletions

View file

@ -6,7 +6,7 @@ import io.github.null2264.tsukumogami.core.module.api.botModules
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
val generalModule = botModules("General") { 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() val startTime = Clock.System.now()
ctx.typing() ctx.typing()
val endTime = Clock.System.now() val endTime = Clock.System.now()

View file

@ -34,18 +34,28 @@ open class Bot internal constructor(): IGroup {
_prefixes.add(prefix) _prefixes.add(prefix)
} }
fun addCommand(command: Command) { private fun addCommand(name: String, command: Command) {
if (allCommands.containsKey(command.name)) { if (allCommands.containsKey(name)) {
throw IllegalStateException("Duplicate command: '${command.name}'") throw IllegalStateException("Duplicate command: '${command.name}'")
} }
allCommands[command.name] = command allCommands[command.name] = command
} }
fun removeCommand(command: Command) { fun addCommand(command: Command) {
if (!allCommands.containsKey(command.name)) { addCommand(command.name, command)
throw IllegalStateException("Command not found: '${command.name}'") 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() { open suspend fun start() {
@ -67,9 +77,10 @@ open class Bot internal constructor(): IGroup {
fun getCommand(name: String?) = allCommands[name] fun getCommand(name: String?) = allCommands[name]
private fun getContext(message: Message): Context { private fun getContext(message: Message): Context {
val candidate = message.content.parsePrefixCommandAndArguments() val parsed = message.content.parsePrefixAndCommand()
val context = Context(this, message, candidate?.first, candidate?.second?.toMutableList()) val context = Context(this, message, parsed?.first)
context.command = getCommand(context.pullCandidateOrNull()) context.command = getCommand(parsed?.second)
if (context.command != null) context.invokedWith = parsed?.second
return context return context
} }
@ -99,17 +110,18 @@ open class Bot internal constructor(): IGroup {
Logger.i { "Online! ${client.getSelf().username}" } Logger.i { "Online! ${client.getSelf().username}" }
} }
private fun String.parsePrefixCommandAndArguments(): Pair<String, List<String>>? { private fun String.parsePrefixAndCommand(): Pair<String, String>? {
if (this.isBlank()) return null if (this.isBlank()) return null
var ret: Pair<String, List<String>>? = null var ret: Pair<String, String>? = null
prefixes.forEach { run {
if (this.substring(0, it.length) == it) { prefixes.forEach { candidate ->
val prefix = this.substring(0, it.length) val prefix = this.substring(0, candidate.length)
val cleanPrompt = this.substring(it.length) if (prefix != candidate) { return@forEach }
ret = Pair(prefix, cleanPrompt.parseCommandAndArguments()) val command = this.drop(candidate.length).substringBefore(' ')
return@forEach ret = prefix to command
return@run
} }
} }

View file

@ -8,34 +8,30 @@ import io.github.null2264.tsukumogami.core.commands.Command
class Context( class Context(
val bot: Bot, val bot: Bot,
private val message: Message, val message: Message,
/** /**
* The prefix that used to invoke the command * The prefix that used to invoke the command
*/ */
val prefix: String?, val prefix: String?,
/**
* Potential command name and/or arguments
*/
val candidate: MutableList<String>?,
) { ) {
/** /**
* The user that invoked the command * The user that invoked the command
*
* Alias to [Message.author]
*/ */
val author get() = message.author val author get() = message.author
/**
* The text that invoked the command
*/
var invokedWith: String? = null
/** /**
* The command that currently being invoked * The command that currently being invoked
*/ */
var command: Command? = null 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 send(content: String) = message.channel.createMessage(content)
suspend fun reply(content: String, mentionsAuthor: Boolean = false) = message.channel.createMessage { suspend fun reply(content: String, mentionsAuthor: Boolean = false) = message.channel.createMessage {
@ -48,4 +44,15 @@ class Context(
} }
suspend fun typing() = message.channel.type() suspend fun typing() = message.channel.type()
fun parseArguments(): MutableList<String>? =
if (prefix != null && invokedWith != null) {
message.content
.substringAfter("$prefix$invokedWith")
.trim()
.split(' ')
.toMutableList()
} else {
null
}
} }

View file

@ -17,7 +17,7 @@ abstract class Arguments {
} }
suspend fun parse(context: Context) { suspend fun parse(context: Context) {
val currentValues = context.candidate?.toMutableList() val currentValues = context.parseArguments()
run { run {
args.forEach { arg -> args.forEach { arg ->

View file

@ -19,9 +19,13 @@ class Group(
override val allCommands: MutableMap<String, Command> = mutableMapOf() override val allCommands: MutableMap<String, Command> = mutableMapOf()
override suspend fun invoke(context: Context) { override suspend fun invoke(context: Context) {
val command = allCommands[context.getCandidateOrNull()] val subcommandName = context.message.content
context.pullCandidateIf { command != null } ?: return .substringAfter("${context.prefix}${context.invokedWith}")
context.command = command!! .trim()
.substringBefore(' ')
val command = allCommands[subcommandName] ?: return
context.invokedWith += " $subcommandName"
context.command = command
command.invoke(context) command.invoke(context)
} }
} }

View file

@ -9,7 +9,6 @@ interface IGroup {
private fun addCommand(command: Command) { private fun addCommand(command: Command) {
allCommands[command.name] = command allCommands[command.name] = command
command.alias.forEach { allCommands[it] = command }
} }
fun commands( fun commands(