Skip to content

Commit

Permalink
User commands actually work
Browse files Browse the repository at this point in the history
  • Loading branch information
duncte123 committed Mar 8, 2024
1 parent e6cdb28 commit ca15b8b
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 4 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ dependencies {
implementation(libs.jda)
implementation(libs.logger)
implementation(libs.jackson.core)
implementation(libs.jackson.kotlin)
implementation(libs.jackson.yaml)
implementation(kotlin("scripting-jsr223")) // Dangerous stuff!!
testImplementation("org.jetbrains.kotlin:kotlin-test")
}

Expand Down
9 changes: 7 additions & 2 deletions commands/example.yml → commands/advanced-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ input:
type: BOOLEAN
desc: is the user nice?
required: false
# fancy stuff
# Yeah, you can write full-blown kotlin here
# That is why I will be manually reviewing each command that is added
output: |-
I want to do something like ${options.user.asMention} is ${options["is-nice"]?.asBoolean ?: false}
import java.util.concurrent.ThreadLocalRandom
val randomBool = ThreadLocalRandom.current().nextBoolean()
"I want to do something like ${event.getOption("user")!!.asUser.asMention} is ${event.getOption("is-nice")?.asBoolean ?: randomBool}"
6 changes: 6 additions & 0 deletions commands/simple-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# name and description and output are required
name: simple-command
description: A simple example command
# Final line of output MUST be in quotes as that is what will be returned from the bot
output: |-
"Hi there! Wanna add your own commands? Submit a Pull Request on GitHub: <https://github.com/dunste123/io>"
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: "3.7"
services:
io:
container_name: io
volumes:
- './commands:/oracle-bot/commands'
build:
context: .
environment:
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencyResolutionManagement {
library("logger", "ch.qos.logback", "logback-classic").version("1.4.11")

library("jackson-core", "com.fasterxml.jackson.core", "jackson-core").versionRef("jackson")
library("jackson-kotlin", "com.fasterxml.jackson.module", "jackson-module-kotlin").versionRef("jackson")
library("jackson-yaml", "com.fasterxml.jackson.dataformat", "jackson-dataformat-yaml").versionRef("jackson")
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/main/kotlin/me/duncte123/io/CommandManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package me.duncte123.io
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.KotlinFeature
import com.fasterxml.jackson.module.kotlin.KotlinModule
import me.duncte123.io.commands.JjCommand
import me.duncte123.io.commands.ReloadCommand
import me.duncte123.io.commands.UserCommand
Expand All @@ -17,6 +19,17 @@ import java.io.File
class CommandManager {
private val log = LoggerFactory.getLogger(CommandManager::class.java)
private val jackson = ObjectMapper(YAMLFactory())
.registerModule(
KotlinModule.Builder()
.withReflectionCacheSize(512)
.configure(KotlinFeature.NullToEmptyCollection, false)
.configure(KotlinFeature.NullToEmptyMap, false)
.configure(KotlinFeature.NullIsSameAsDefault, false)
// .configure(KotlinFeature.SingletonSupport, SingletonSupport.DISABLED)
.configure(KotlinFeature.SingletonSupport, false)
.configure(KotlinFeature.StrictNullChecks, false)
.build()
)
.addMixIn(OptionData::class.java, OptionDataMixin::class.java)

private val commands = mutableMapOf<String, ICommand>()
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/me/duncte123/io/Listener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ class Listener : EventListener {
is ReadyEvent -> onReady(event)
is SlashCommandInteractionEvent -> onSlashCommandInteraction(event)
is MessageReceivedEvent -> {
if (event.isFromType(ChannelType.PRIVATE)) {
// ALWAYS IGNORE BOTS
if (event.isFromType(ChannelType.PRIVATE) && !event.author.isBot) {
event.message
.reply("Hey you! Wishlist jock studio now! <https://store.steampowered.com/app/2115310/Jock_Studio?utm_source=io_bot>")
.queue()
Expand Down
85 changes: 84 additions & 1 deletion src/main/kotlin/me/duncte123/io/commands/UserCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@ import com.fasterxml.jackson.annotation.JsonProperty
import me.duncte123.io.ICommand
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.OptionData
import net.dv8tion.jda.api.requests.RestAction
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
import javax.script.SimpleBindings
import kotlin.math.min

private val kotlinEngine: ScriptEngine by lazy {
ScriptEngineManager().getEngineByExtension("kts")!!.apply {
this.eval(
"""
import java.io.*
import java.lang.*
import java.math.*
import java.time.*
import java.util.*
import java.util.concurrent.*
import java.util.stream.*
import net.dv8tion.jda.api.*
import net.dv8tion.jda.api.entities.*
import net.dv8tion.jda.api.entities.channel.*
import net.dv8tion.jda.api.entities.channel.attribute.*
import net.dv8tion.jda.api.entities.channel.middleman.*
import net.dv8tion.jda.api.entities.channel.concrete.*
import net.dv8tion.jda.api.sharding.*
import net.dv8tion.jda.internal.entities.*
import net.dv8tion.jda.api.managers.*
import net.dv8tion.jda.internal.managers.*
import net.dv8tion.jda.api.utils.*
""".trimIndent()
)
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
data class UserCommand @JsonCreator constructor (
Expand All @@ -16,6 +48,57 @@ data class UserCommand @JsonCreator constructor (
@JsonProperty("output") val output: String,
) : ICommand {
override fun execute(event: SlashCommandInteractionEvent) {
event.reply(output).queue()
event.deferReply(false).queue()

val bindings = SimpleBindings()

bindings["event"] = event

val response = try {
kotlinEngine.eval(output, bindings)
} catch (e: Exception) {
e
}

parseEvalResponse(response, event)
}

private fun parseEvalResponse(out: Any?, event: SlashCommandInteractionEvent) {
val send: (message: String) -> Unit = { event.hook.sendMessage(it).queue() }

when (out) {
null -> send("(command produced no output)")

is Throwable -> {
send("ERROR: $out")
}

is RestAction<*> -> {
out.queue({
send("Rest action success: $it")
}) {
send("Rest action error: $it")
}
}

else -> {
val toString = out.toString()

if (toString.isEmpty() || toString.isBlank()) {
send("(command produced no output)")
return
}

send(toString.truncate(1900))
}
}
}

private fun String.truncate(length: Int): String {
if (this.length < length) {
return this
}

return this.substring(0, min(this.length, length - 3)) + "..."
}
}

0 comments on commit ca15b8b

Please sign in to comment.