feat: add application command dsl builder and migrate commands

This commit is contained in:
Project_IO 2024-10-07 13:35:06 +09:00
parent 208084b617
commit 83319facb2
13 changed files with 224 additions and 42 deletions

View file

@ -0,0 +1,128 @@
package net.projecttl.p.x32.api.command
import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.api.interactions.commands.build.OptionData
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
fun useCommand(init: CommandObj.() -> Unit): CommandData {
val obj = CommandObj()
init.invoke(obj)
return obj.build()
}
class CommandObj {
var name: String = ""
var description: String = ""
val subcommands = mutableListOf<SubcommandData>()
val subcommandGroups = mutableListOf<SubcommandGroupData>()
val options = mutableListOf<OptionData>()
fun subcommand(sub: SubcommandObj.() -> Unit) {
val obj = SubcommandObj()
sub.invoke(obj)
subcommands += obj.build()
}
fun subcommandGroup(group: SubcommandGroupObj.() -> Unit) {
val obj = SubcommandGroupObj()
group.invoke(obj)
subcommandGroups += obj.build()
}
fun option(opt: OptionObj.() -> Unit) {
val obj = OptionObj()
opt.invoke(obj)
options += obj.build()
}
fun build(): CommandData {
val obj = CommandDataImpl(name, description)
if (subcommands.isNotEmpty()) {
subcommands.forEach {
obj.addSubcommands(it)
}
}
if (subcommandGroups.isNotEmpty()) {
subcommandGroups.forEach {
obj.addSubcommandGroups(it)
}
}
if (options.isNotEmpty()) {
options.forEach {
obj.addOptions(it)
}
}
return CommandData.fromData(obj.toData())
}
}
class SubcommandObj {
var name: String = ""
var description: String = ""
val options = mutableListOf<OptionData>()
fun option(opt: OptionObj.() -> Unit) {
val obj = OptionObj()
opt.invoke(obj)
options += obj.build()
}
fun build(): SubcommandData {
val obj = SubcommandData(name, description)
if (options.isNotEmpty()) {
options.forEach {
obj.addOptions(it)
}
}
return obj
}
}
class SubcommandGroupObj {
var name: String = ""
var description: String = ""
val subcommands = mutableListOf<SubcommandData>()
fun subcommand(opt: SubcommandObj.() -> Unit) {
val obj = SubcommandObj()
opt.invoke(obj)
subcommands += obj.build()
}
fun build(): SubcommandGroupData {
val obj = SubcommandGroupData(name, description)
if (subcommands.isNotEmpty()) {
subcommands.forEach {
obj.addSubcommands(it)
}
}
return obj
}
}
class OptionObj {
lateinit var type: OptionType
var name: String = ""
var description: String = ""
var required: Boolean = false
var autoComplete: Boolean = false
fun build(): OptionData {
return OptionData(type, name, description, required, autoComplete)
}
}

View file

@ -4,10 +4,10 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.dv8tion.jda.api.JDA
import net.projecttl.p.x32.api.BotConfig
import net.projecttl.p.x32.command.Info
import net.projecttl.p.x32.command.PluginCommand
import net.projecttl.p.x32.command.Reload
import net.projecttl.p.x32.api.BotConfig
import net.projecttl.p.x32.config.DefaultConfig
import net.projecttl.p.x32.kernel.CoreKernel
import org.slf4j.Logger
@ -21,7 +21,7 @@ lateinit var kernel: CoreKernel
val logger: Logger = LoggerFactory.getLogger(Px32::class.java)
@OptIn(DelicateCoroutinesApi::class)
fun main() {
fun main(args: Array<out String>) {
println("Px32 version v${DefaultConfig.version}")
if (BotConfig.owner.isBlank() || BotConfig.owner.isEmpty()) {
logger.warn("owner option is blank or empty!")
@ -30,6 +30,27 @@ fun main() {
kernel = CoreKernel(BotConfig.token)
val handler = kernel.commandContainer
if (args.contains("--remove-cmd")) {
jda = kernel.build()
try {
jda.retrieveCommands().queue {
if (it == null) {
return@queue
}
it.forEach { command ->
logger.info("unregister command: /${command.name}")
command.jda.deleteCommandById(command.id).queue()
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
kernel.kill()
return
}
handler.addCommand(Info)
handler.addCommand(Reload)
handler.addCommand(PluginCommand)

View file

@ -3,16 +3,17 @@ package net.projecttl.p.x32.command
import net.dv8tion.jda.api.JDAInfo
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.config.DefaultConfig
import net.projecttl.p.x32.kernel
import java.lang.management.ManagementFactory
object Info : GlobalCommand {
override val data: CommandData = CommandData.fromData(
CommandDataImpl("info","봇의 정보를 표시 합니다").toData()
)
override val data: CommandData = useCommand {
name = "info"
description = "봇의 정보를 표시 합니다"
}
override suspend fun execute(ev: SlashCommandInteractionEvent) {
val rb = ManagementFactory.getRuntimeMXBean()

View file

@ -3,15 +3,17 @@ package net.projecttl.p.x32.command
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.entities.MessageEmbed
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer
import net.projecttl.p.x32.kernel
object PluginCommand : GlobalCommand {
override val data = CommandData.fromData(CommandDataImpl("plugins", "봇에 불러온 플러그인을 확인 합니다").toData())
override val data = useCommand {
name = "plugins"
description = "봇에 불러온 플러그인을 확인 합니다"
}
override suspend fun execute(ev: SlashCommandInteractionEvent) {
val embed = EmbedBuilder().apply {

View file

@ -1,14 +1,16 @@
package net.projecttl.p.x32.command
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.BotConfig
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.kernel
object Reload : GlobalCommand {
override val data = CommandData.fromData(CommandDataImpl("reload", "플러그인을 다시 불러 옵니다").toData())
override val data = useCommand {
name = "reload"
description = "플러그인을 다시 불러 옵니다"
}
override suspend fun execute(ev: SlashCommandInteractionEvent) {
if (kernel.memLock.isLocked) {

View file

@ -98,6 +98,11 @@ class CoreKernel(token: String) {
memLock.unlock()
}
fun kill() {
destroy()
jda.shutdownNow()
}
private fun load() {
if (BotConfig.bundle) {
val b = BundleModule()

View file

@ -2,14 +2,16 @@ package net.projecttl.p.x32.func.command
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.UserContext
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer
object Avatar : UserContext {
override val data = CommandData.fromData(CommandDataImpl("avatar", "유저의 프로필 이미지를 가져 옵니다").toData())
override val data = useCommand {
name = "avatar"
description = "유저의 프로필 이미지를 가져 옵니다"
}
override suspend fun execute(ev: UserContextInteractionEvent) {
val embed = EmbedBuilder()

View file

@ -3,9 +3,8 @@ package net.projecttl.p.x32.func.command
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer
import java.math.RoundingMode
@ -13,10 +12,24 @@ import java.text.DecimalFormat
import kotlin.math.pow
object Bmi : GlobalCommand {
override val data = CommandData.fromData(CommandDataImpl("bmi", "키와 몸무게 기반으로 bmi지수를 계산할 수 있어요").apply {
addOption(OptionType.NUMBER, "height", "신장 길이를 적어 주세요 (cm)", true)
addOption(OptionType.NUMBER, "weight", "몸무게를 적어 주세요 (kg)", true)
}.toData())
override val data = useCommand {
name = "bmi"
description = "키와 몸무게 기반으로 bmi 지수를 계산해요"
option {
type = OptionType.NUMBER
name = "height"
description = "신장 길이를 적어 주세요 (cm)"
required = true
}
option {
type = OptionType.NUMBER
name = "weight"
description = "몸무게를 적어 주세요 (kg)"
required = true
}
}
override suspend fun execute(ev: SlashCommandInteractionEvent) {
val height = ev.getOption("height")!!.asDouble
@ -32,7 +45,7 @@ object Bmi : GlobalCommand {
return when {
bmi < 18.5 -> "**저체중**"
bmi in 18.5..24.9 -> "**정상 체중**"
bmi in 25.0.. 29.9 -> "**과체중**"
bmi in 25.0..29.9 -> "**과체중**"
else -> "**비만**"
}
}

View file

@ -1,14 +1,16 @@
package net.projecttl.p.x32.func.command
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import kotlin.random.Random
import kotlin.random.nextInt
object Dice : GlobalCommand {
override val data = CommandData.fromData(CommandDataImpl("dice", "랜덤으로 주사위를 굴립니다").toData())
override val data = useCommand {
name = "dice"
description = "랜덤으로 주사위를 굴립니다"
}
override suspend fun execute(ev: SlashCommandInteractionEvent) {
val rand = Random.nextInt(1..6)

View file

@ -1,12 +1,14 @@
package net.projecttl.p.x32.func.command
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.MessageContext
import net.projecttl.p.x32.api.command.useCommand
object MsgLength : MessageContext {
override val data = CommandData.fromData(CommandDataImpl("length", "메시지의 길이를 확인 합니다.").toData())
override val data = useCommand {
name = "length"
description = "메시지의 길이를 확인 합니다."
}
override suspend fun execute(ev: MessageContextInteractionEvent) {
val target = ev.target

View file

@ -2,17 +2,21 @@ package net.projecttl.p.x32.func.command
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.func.Conf
object MsgPurge : GlobalCommand {
override val data = CommandData.fromData(
CommandDataImpl("purge", "n개의 메시지를 채널에서 삭제해요").apply {
addOption(OptionType.INTEGER, "n", "n개만큼 메시지가 삭제 됩니다", true)
}.toData()
)
override val data = useCommand {
name = "purge"
description = "n개의 메시지를 채널에서 삭제해요"
option {
type = OptionType.INTEGER
name = "n"
description = "n개만큼 메시지가 삭제 됩니다"
required = true
}
}
override suspend fun execute(ev: SlashCommandInteractionEvent) {
val n = ev.getOption("n")!!.asInt

View file

@ -6,16 +6,16 @@ import net.dv8tion.jda.api.entities.MessageEmbed
import net.dv8tion.jda.api.entities.User
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.api.command.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer
object Ping : GlobalCommand {
override val data: CommandData = CommandData.fromData(CommandDataImpl(
"ping",
"Discord API 레이턴시 확인 합니다"
).toData())
override val data: CommandData = useCommand {
name = "ping"
description = "Discord API 레이턴시 확인 합니다"
}
override suspend fun execute(ev: SlashCommandInteractionEvent) {
val started = System.currentTimeMillis()

Binary file not shown.