Skip to content

Commit

Permalink
Working slash commands
Browse files Browse the repository at this point in the history
Signed-off-by: shedaniel <[email protected]>
  • Loading branch information
shedaniel committed Aug 5, 2021
1 parent f5b29e1 commit 1985ba4
Show file tree
Hide file tree
Showing 25 changed files with 411 additions and 146 deletions.
21 changes: 11 additions & 10 deletions src/main/kotlin/me/shedaniel/linkie/discord/Command.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ typealias CommandExecutor<T> = suspend (ctx: CommandContext, options: T) -> Unit

data class BuiltCommand(
val command: Command,
val slashCommand: SlashCommand,
val slash: Boolean,
val regularCommand: SlashCommand,
val slashCommand: SlashCommand?,
)

interface Command {
Expand All @@ -69,8 +69,8 @@ interface Command {
}
}

suspend fun buildCommandPrivate(builder: SlashCommandBuilderInterface) = builder.buildCommand()
suspend fun SlashCommandBuilderInterface.buildCommand()
suspend fun buildCommandPrivate(builder: SlashCommandBuilderInterface, slash: Boolean) = builder.buildCommand(slash)
suspend fun SlashCommandBuilderInterface.buildCommand(slash: Boolean)
suspend fun execute(ctx: CommandContext, command: SlashCommand, args: MutableList<String>): Boolean {
val isGroup = command.options.any { it is NestedSlashCommandOption }
val root = if (isGroup) SubGroupCommandOption(ctx.cmd, "", listOf()) else SubCommandOption(ctx.cmd, "", listOf())
Expand Down Expand Up @@ -101,17 +101,18 @@ interface SimpleCommand<T> : Command {
}

suspend fun Command.build(
cmds: List<String>,
regular: List<String>,
slashAlias: List<String>,
slash: Boolean,
description: (cmd: String) -> String,
): BuiltCommand = BuiltCommand(
command = this,
slashCommand = SlashCommandBuilder(description).cmd(*cmds.toTypedArray()).also { buildCommandPrivate(it) },
slash = slash,
regularCommand = SlashCommandBuilder(description).cmd(*regular.toTypedArray()).also { buildCommandPrivate(it, false) },
slashCommand = if (slash) SlashCommandBuilder(description).cmd(*slashAlias.toTypedArray()).also { buildCommandPrivate(it, true) } else null,
)

interface OptionlessCommand : Command {
override suspend fun SlashCommandBuilderInterface.buildCommand() =
override suspend fun SlashCommandBuilderInterface.buildCommand(slash: Boolean) =
executeCommandWithNothing { execute(it) }

override suspend fun execute(ctx: CommandContext, command: SlashCommand, args: MutableList<String>): Boolean {
Expand All @@ -130,7 +131,7 @@ interface LegacyCommand {

open class SubCommandHolder : Command {
private val subcommands = mutableMapOf<String, Command>()
override suspend fun SlashCommandBuilderInterface.buildCommand() {
override suspend fun SlashCommandBuilderInterface.buildCommand(slash: Boolean) {
this@SubCommandHolder.javaClass.declaredFields.forEach { field ->
if (field.type.isAssignableFrom(SubCommandEntry::class.java)) {
val name = field.name.toLowerCase(Locale.ROOT)
Expand All @@ -141,7 +142,7 @@ open class SubCommandHolder : Command {
}
subcommands.forEach { (name, command) ->
sub(name, "Sub command '$name'") {
command.buildCommandPrivate(this@sub)
command.buildCommandPrivate(this@sub, slash)
}
}
}
Expand Down
67 changes: 36 additions & 31 deletions src/main/kotlin/me/shedaniel/linkie/discord/CommandHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package me.shedaniel.linkie.discord

import com.soywiz.korio.async.runBlockingNoJs
import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel
import discord4j.core.event.domain.message.MessageCreateEvent
import me.shedaniel.linkie.InvalidUsageException
import me.shedaniel.linkie.discord.config.ConfigManager
Expand All @@ -28,54 +26,61 @@ import me.shedaniel.linkie.discord.scripting.LinkieScripting
import me.shedaniel.linkie.discord.scripting.push
import me.shedaniel.linkie.discord.tricks.TricksManager
import me.shedaniel.linkie.discord.utils.CommandContext
import me.shedaniel.linkie.discord.utils.MessageBasedCommandContext
import me.shedaniel.linkie.discord.utils.msgCreator

object CommandHandler : CommandAcceptor {
private val commandMap: MutableMap<String, Any> = mutableMapOf()
val slashCommands: Set<BuiltCommand>
get() = commandMap.mapNotNull { it.value as? BuiltCommand }.toSet()
private val slashCommandMap: MutableMap<String, BuiltCommand> = mutableMapOf()
private val regularCommandMap: MutableMap<String, Any> = mutableMapOf()
val slashCommands: MutableCollection<BuiltCommand>
get() = slashCommandMap.values

fun registerCommand(command: Command, vararg l: String): CommandHandler =
registerCommand(true, command, *l)
registerCommand(true, command, l.toList())

fun registerCommand(slash: Boolean, command: Command, vararg l: String): CommandHandler {
val list = l.toMutableList()
modifyDebug(list)
val builtCommand = runBlockingNoJs { command.build(list.toList(), slash) { "Command '$it'" } }
for (ll in list)
commandMap[ll.toLowerCase()] = builtCommand
fun registerCommand(slash: Boolean, command: Command, vararg l: String): CommandHandler =
registerCommand(slash, command, l.toList())

fun registerCommand(
command: Command,
regular: List<String>,
slashAlias: List<String> = regular
): CommandHandler =
registerCommand(true, command, regular, slashAlias)

fun registerCommand(
slash: Boolean,
command: Command,
regular: List<String>,
slashAlias: List<String> = regular
): CommandHandler {
val builtCommand = runBlockingNoJs { command.build(regular, slashAlias, slash) { "Command '$it'" } }
for (ll in regular)
regularCommandMap[ll.toLowerCase()] = builtCommand
for (ll in slashAlias)
slashCommandMap[ll.toLowerCase()] = builtCommand
command.postRegister()
return this
}

private fun modifyDebug(list: MutableList<String>) {
if (isDebug) {
list.replaceAll { "${it}_debug" }
}
}

fun registerCommand(command: LegacyCommand, vararg l: String): CommandHandler {
for (ll in l)
commandMap[ll.toLowerCase()] = command
fun registerCommand(command: LegacyCommand, vararg regular: String): CommandHandler {
for (ll in regular)
regularCommandMap[ll.toLowerCase()] = command
command.postRegister()
return this
}

override fun getPrefix(event: MessageCreateEvent): String? =
event.guildId.orElse(null)?.let { ConfigManager[it.asLong()].prefix }

override suspend fun execute(event: MessageCreateEvent, prefix: String, user: User, cmd: String, args: MutableList<String>, channel: MessageChannel) {
val ctx = MessageBasedCommandContext(event, channel.msgCreator(event.message), prefix, cmd, channel)
if (cmd in commandMap) {
val command = commandMap[cmd]!!
override suspend fun execute(event: MessageCreateEvent, ctx: CommandContext, args: MutableList<String>) {
if (ctx.cmd in regularCommandMap) {
val command = regularCommandMap[ctx.cmd]!!
if (command is LegacyCommand) {
command.execute(ctx, event.message, args)
} else if (command is BuiltCommand) {
executeCommand(command, args, ctx)
}
} else {
TricksManager.globalTricks[cmd]?.also { trick ->
TricksManager.globalTricks[ctx.cmd]?.also { trick ->
val evalContext = EvalContext(
ctx,
event.message,
Expand All @@ -85,16 +90,16 @@ object CommandHandler : CommandAcceptor {
)
LinkieScripting.evalTrick(evalContext, ctx.message, trick) {
LinkieScripting.simpleContext.push {
ContextExtensions.commandContexts(evalContext, user, channel, ctx.message, this)
ContextExtensions.commandContexts(evalContext, ctx.user, ctx.channel, ctx.message, this)
}
}
}
}
}

private suspend fun executeCommand(command: BuiltCommand, args: MutableList<String>, ctx: CommandContext) {
if (!command.command.execute(ctx, command.slashCommand, args)) {
throw InvalidUsageException("Invalid Usage:\n${command.slashCommand.usage(ctx)}")
if (!command.command.execute(ctx, command.regularCommand, args)) {
throw InvalidUsageException("Invalid Usage:\n${command.regularCommand.usage(ctx)}")
}
}
}
34 changes: 26 additions & 8 deletions src/main/kotlin/me/shedaniel/linkie/discord/CommandMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@
package me.shedaniel.linkie.discord

import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel
import discord4j.core.event.domain.message.MessageCreateEvent
import discord4j.core.spec.EmbedCreateSpec
import discord4j.rest.util.Color
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import me.shedaniel.linkie.discord.utils.CommandContext
import me.shedaniel.linkie.discord.utils.MessageBasedCommandContext
import me.shedaniel.linkie.discord.utils.basicEmbed
import me.shedaniel.linkie.discord.utils.buildReactions
import me.shedaniel.linkie.discord.utils.dismissButton
import me.shedaniel.linkie.discord.utils.msgCreator
import me.shedaniel.linkie.discord.utils.sendEmbedMessage
import java.time.Duration

Expand All @@ -35,18 +38,33 @@ class CommandMap(private val commandAcceptor: CommandAcceptor, private val defau
val message: String = event.message.content
val prefix = commandAcceptor.getPrefix(event) ?: defaultPrefix
GlobalScope.launch {
runCatching {
try {
if (message.toLowerCase().startsWith(prefix)) {
val content = message.substring(prefix.length)
val split = content.splitArgs().dropLastWhile(String::isEmpty)
val split = content.splitArgs()
if (split.isNotEmpty()) {
val cmd = split[0].toLowerCase()
val ctx = MessageBasedCommandContext(event, channel.msgCreator(event.message), prefix, cmd, channel)
val args = split.drop(1).toMutableList()
commandAcceptor.execute(event, prefix, user, cmd, args, channel)
try {
commandAcceptor.execute(event, ctx, args)
} catch (throwable: Throwable) {
if (throwable is SuppressedException) return@launch
try {
ctx.message.reply(ctx, {
dismissButton()
}) {
generateThrowable(throwable, user)
}
} catch (throwable2: Exception) {
throwable2.addSuppressed(throwable)
throwable2.printStackTrace()
}
}
}
}
}.exceptionOrNull()?.also { throwable ->
if (throwable is SuppressedException) return@also
} catch (throwable: Throwable) {
if (throwable is SuppressedException) return@launch
try {
channel.sendEmbedMessage { generateThrowable(throwable, user) }.subscribe { message ->
buildReactions(Duration.ofMinutes(2)) {
Expand Down Expand Up @@ -81,11 +99,11 @@ fun String.splitArgs(): MutableList<String> {
}
if (stringBuilder.isNotEmpty())
args.add(stringBuilder.toString())
return args
return args.dropLastWhile(String::isEmpty).toMutableList()
}

interface CommandAcceptor {
suspend fun execute(event: MessageCreateEvent, prefix: String, user: User, cmd: String, args: MutableList<String>, channel: MessageChannel)
suspend fun execute(event: MessageCreateEvent, ctx: CommandContext, args: MutableList<String>)
fun getPrefix(event: MessageCreateEvent): String?
}

Expand Down
44 changes: 25 additions & 19 deletions src/main/kotlin/me/shedaniel/linkie/discord/LinkieBot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ import me.shedaniel.linkie.discord.commands.ListTricksCommand
import me.shedaniel.linkie.discord.commands.QueryMappingsCommand
import me.shedaniel.linkie.discord.commands.QueryTranslateMappingsCommand
import me.shedaniel.linkie.discord.commands.RandomClassCommand
import me.shedaniel.linkie.discord.commands.RemapAWATCommand
import me.shedaniel.linkie.discord.commands.RemoveTrickCommand
import me.shedaniel.linkie.discord.commands.RunTrickCommand
import me.shedaniel.linkie.discord.commands.SetValueCommand
import me.shedaniel.linkie.discord.commands.TrickInfoCommand
import me.shedaniel.linkie.discord.commands.TricksCommand
import me.shedaniel.linkie.discord.commands.ValueCommand
import me.shedaniel.linkie.discord.commands.ValueListCommand
import me.shedaniel.linkie.discord.commands.legacy.RemapAWATCommand
import me.shedaniel.linkie.discord.config.ConfigManager
import me.shedaniel.linkie.discord.scommands.SlashCommands
import me.shedaniel.linkie.discord.scommands.sub
Expand All @@ -78,7 +78,7 @@ import me.shedaniel.linkie.utils.info
import java.io.File
import java.util.*

const val testingGuild = 792699517631594506L
const val testingGuild = 432055962233470986L

fun main() {
(File(System.getProperty("user.dir")) / ".properties").apply {
Expand Down Expand Up @@ -107,13 +107,13 @@ fun main() {
start(
LinkieConfig.DEFAULT.copy(
namespaces = listOf(
LegacyYarnNamespace,
YarrnNamespace,
YarnNamespace,
PlasmaNamespace,
MCPNamespace,
MojangNamespace,
MojangSrgNamespace,
MCPNamespace,
LegacyYarnNamespace,
YarrnNamespace,
PlasmaNamespace,
)
)
) {
Expand All @@ -122,8 +122,12 @@ fun main() {
registerCommands(CommandHandler)
registerSlashCommands(slashCommands)
CommandHandler.slashCommands.forEach { cmd ->
if (cmd.slash) {
slashCommands.guildCommand(testingGuild, cmd.slashCommand)
if (cmd.slashCommand != null) {
if (isDebug) {
slashCommands.guildCommand(testingGuild, cmd.slashCommand)
} else {
slashCommands.globalCommand(cmd.slashCommand)
}
}
}
slashCommands.register()
Expand Down Expand Up @@ -160,9 +164,9 @@ private operator fun File.div(s: String): File = File(this, s)

fun registerCommands(commands: CommandHandler) {
commands.registerCommand(QueryMappingsCommand(null, *MappingsEntryType.values()), "mapping")
commands.registerCommand(QueryMappingsCommand(null, MappingsEntryType.CLASS), "c", "class")
commands.registerCommand(QueryMappingsCommand(null, MappingsEntryType.METHOD), "m", "method")
commands.registerCommand(QueryMappingsCommand(null, MappingsEntryType.FIELD), "f", "field")
commands.registerCommand(false, QueryMappingsCommand(null, MappingsEntryType.CLASS), "c", "class")
commands.registerCommand(false, QueryMappingsCommand(null, MappingsEntryType.METHOD), "m", "method")
commands.registerCommand(false, QueryMappingsCommand(null, MappingsEntryType.FIELD), "f", "field")

commands.registerCommand(false, QueryMappingsCommand(Namespaces["yarn"], *MappingsEntryType.values()), "y", "yarn")
commands.registerCommand(false, QueryMappingsCommand(Namespaces["yarn"], MappingsEntryType.CLASS), "yc", "yarnc")
Expand Down Expand Up @@ -199,10 +203,10 @@ fun registerCommands(commands: CommandHandler) {
commands.registerCommand(false, QueryMappingsCommand(Namespaces["mojang_srg"], MappingsEntryType.METHOD), "mmsm", "mojmapsm")
commands.registerCommand(false, QueryMappingsCommand(Namespaces["mojang_srg"], MappingsEntryType.FIELD), "mmsf", "mojmapsm")

commands.registerCommand(QueryTranslateMappingsCommand(null, null, *MappingsEntryType.values()), "translate", "t")
commands.registerCommand(QueryTranslateMappingsCommand(null, null, MappingsEntryType.CLASS), "translatec", "tc")
commands.registerCommand(QueryTranslateMappingsCommand(null, null, MappingsEntryType.METHOD), "translatem", "tm")
commands.registerCommand(QueryTranslateMappingsCommand(null, null, MappingsEntryType.FIELD), "translatef", "tf")
commands.registerCommand(QueryTranslateMappingsCommand(null, null, *MappingsEntryType.values()), listOf("translate", "t"), listOf("translate"))
commands.registerCommand(false, QueryTranslateMappingsCommand(null, null, MappingsEntryType.CLASS), "translatec", "tc")
commands.registerCommand(false, QueryTranslateMappingsCommand(null, null, MappingsEntryType.METHOD), "translatem", "tm")
commands.registerCommand(false, QueryTranslateMappingsCommand(null, null, MappingsEntryType.FIELD), "translatef", "tf")

commands.registerCommand(false, QueryTranslateMappingsCommand(Namespaces["yarn"], Namespaces["mcp"], *MappingsEntryType.values()), "voldefy", "volde", "v", "ymcp")
commands.registerCommand(false, QueryTranslateMappingsCommand(Namespaces["yarn"], Namespaces["mcp"], MappingsEntryType.CLASS), "voldefyc", "voldec", "vc", "ymcpc")
Expand Down Expand Up @@ -231,13 +235,15 @@ fun registerCommands(commands: CommandHandler) {
commands.registerCommand(false, QueryTranslateMappingsCommand(Namespaces["mojang"], Namespaces["mcp"], MappingsEntryType.METHOD), "mmmcpm")
commands.registerCommand(false, QueryTranslateMappingsCommand(Namespaces["mojang"], Namespaces["mcp"], MappingsEntryType.FIELD), "mmmcpf")

// commands.registerCommand(RemapStackTraceCommand(MojangNamespace), "fabriccrash")
// commands.registerCommand(RemapStackTraceCommand(MojangSrgNamespace), "forgecrash")
commands.registerCommand(RemapAWATCommand, "remapaccess")

commands.registerCommand(FabricDramaCommand, "fabricdrama", "fdrama")
commands.registerCommand(FTBDramaCommand, "ftbdrama", "drama")
commands.registerCommand(FabricDramaCommand, listOf("fabricdrama", "fdrama"), listOf("fabricdrama"))
commands.registerCommand(FTBDramaCommand, listOf("ftbdrama", "drama"), listOf("ftbdrama"))
commands.registerCommand(AboutCommand, "about")
commands.registerCommand(RandomClassCommand, "randc")
commands.registerCommand(EvaluateCommand, "eval", "evaluate")
commands.registerCommand(RandomClassCommand, listOf("randc"), listOf("random_class"))
commands.registerCommand(EvaluateCommand, listOf("eval", "evaluate"), listOf("evaluate"))
commands.registerCommand(RunTrickCommand, "run")
commands.registerCommand(false, AddTrickCommand, "trickadd")
commands.registerCommand(false, RemoveTrickCommand, "trickremove")
Expand Down
Loading

0 comments on commit 1985ba4

Please sign in to comment.