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.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.dv8tion.jda.api.JDA 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.Info
import net.projecttl.p.x32.command.PluginCommand import net.projecttl.p.x32.command.PluginCommand
import net.projecttl.p.x32.command.Reload 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.config.DefaultConfig
import net.projecttl.p.x32.kernel.CoreKernel import net.projecttl.p.x32.kernel.CoreKernel
import org.slf4j.Logger import org.slf4j.Logger
@ -21,7 +21,7 @@ lateinit var kernel: CoreKernel
val logger: Logger = LoggerFactory.getLogger(Px32::class.java) val logger: Logger = LoggerFactory.getLogger(Px32::class.java)
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
fun main() { fun main(args: Array<out String>) {
println("Px32 version v${DefaultConfig.version}") println("Px32 version v${DefaultConfig.version}")
if (BotConfig.owner.isBlank() || BotConfig.owner.isEmpty()) { if (BotConfig.owner.isBlank() || BotConfig.owner.isEmpty()) {
logger.warn("owner option is blank or empty!") logger.warn("owner option is blank or empty!")
@ -30,6 +30,27 @@ fun main() {
kernel = CoreKernel(BotConfig.token) kernel = CoreKernel(BotConfig.token)
val handler = kernel.commandContainer 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(Info)
handler.addCommand(Reload) handler.addCommand(Reload)
handler.addCommand(PluginCommand) 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.JDAInfo
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData 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.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.config.DefaultConfig import net.projecttl.p.x32.config.DefaultConfig
import net.projecttl.p.x32.kernel import net.projecttl.p.x32.kernel
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
object Info : GlobalCommand { object Info : GlobalCommand {
override val data: CommandData = CommandData.fromData( override val data: CommandData = useCommand {
CommandDataImpl("info","봇의 정보를 표시 합니다").toData() name = "info"
) description = "봇의 정보를 표시 합니다"
}
override suspend fun execute(ev: SlashCommandInteractionEvent) { override suspend fun execute(ev: SlashCommandInteractionEvent) {
val rb = ManagementFactory.getRuntimeMXBean() 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.EmbedBuilder
import net.dv8tion.jda.api.entities.MessageEmbed import net.dv8tion.jda.api.entities.MessageEmbed
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent 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.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer import net.projecttl.p.x32.api.util.footer
import net.projecttl.p.x32.kernel import net.projecttl.p.x32.kernel
object PluginCommand : GlobalCommand { object PluginCommand : GlobalCommand {
override val data = CommandData.fromData(CommandDataImpl("plugins", "봇에 불러온 플러그인을 확인 합니다").toData()) override val data = useCommand {
name = "plugins"
description = "봇에 불러온 플러그인을 확인 합니다"
}
override suspend fun execute(ev: SlashCommandInteractionEvent) { override suspend fun execute(ev: SlashCommandInteractionEvent) {
val embed = EmbedBuilder().apply { val embed = EmbedBuilder().apply {

View file

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

View file

@ -98,6 +98,11 @@ class CoreKernel(token: String) {
memLock.unlock() memLock.unlock()
} }
fun kill() {
destroy()
jda.shutdownNow()
}
private fun load() { private fun load() {
if (BotConfig.bundle) { if (BotConfig.bundle) {
val b = BundleModule() 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.EmbedBuilder
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent 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.UserContext
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer import net.projecttl.p.x32.api.util.footer
object Avatar : UserContext { object Avatar : UserContext {
override val data = CommandData.fromData(CommandDataImpl("avatar", "유저의 프로필 이미지를 가져 옵니다").toData()) override val data = useCommand {
name = "avatar"
description = "유저의 프로필 이미지를 가져 옵니다"
}
override suspend fun execute(ev: UserContextInteractionEvent) { override suspend fun execute(ev: UserContextInteractionEvent) {
val embed = EmbedBuilder() 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.EmbedBuilder
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.OptionType 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.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer import net.projecttl.p.x32.api.util.footer
import java.math.RoundingMode import java.math.RoundingMode
@ -13,10 +12,24 @@ import java.text.DecimalFormat
import kotlin.math.pow import kotlin.math.pow
object Bmi : GlobalCommand { object Bmi : GlobalCommand {
override val data = CommandData.fromData(CommandDataImpl("bmi", "키와 몸무게 기반으로 bmi지수를 계산할 수 있어요").apply { override val data = useCommand {
addOption(OptionType.NUMBER, "height", "신장 길이를 적어 주세요 (cm)", true) name = "bmi"
addOption(OptionType.NUMBER, "weight", "몸무게를 적어 주세요 (kg)", true) description = "키와 몸무게 기반으로 bmi 지수를 계산해요"
}.toData())
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) { override suspend fun execute(ev: SlashCommandInteractionEvent) {
val height = ev.getOption("height")!!.asDouble val height = ev.getOption("height")!!.asDouble
@ -30,9 +43,9 @@ object Bmi : GlobalCommand {
val bmi = weight / (height / 100).pow(2) val bmi = weight / (height / 100).pow(2)
fun result(bmi: Double): String { fun result(bmi: Double): String {
return when { return when {
bmi < 18.5 -> "**저체중**" bmi < 18.5 -> "**저체중**"
bmi in 18.5..24.9 -> "**정상 체중**" bmi in 18.5..24.9 -> "**정상 체중**"
bmi in 25.0.. 29.9 -> "**과체중**" bmi in 25.0..29.9 -> "**과체중**"
else -> "**비만**" else -> "**비만**"
} }
} }

View file

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

View file

@ -1,12 +1,14 @@
package net.projecttl.p.x32.func.command package net.projecttl.p.x32.func.command
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent 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.MessageContext
import net.projecttl.p.x32.api.command.useCommand
object MsgLength : MessageContext { object MsgLength : MessageContext {
override val data = CommandData.fromData(CommandDataImpl("length", "메시지의 길이를 확인 합니다.").toData()) override val data = useCommand {
name = "length"
description = "메시지의 길이를 확인 합니다."
}
override suspend fun execute(ev: MessageContextInteractionEvent) { override suspend fun execute(ev: MessageContextInteractionEvent) {
val target = ev.target 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.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.OptionType 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.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.func.Conf import net.projecttl.p.x32.func.Conf
object MsgPurge : GlobalCommand { object MsgPurge : GlobalCommand {
override val data = CommandData.fromData( override val data = useCommand {
CommandDataImpl("purge", "n개의 메시지를 채널에서 삭제해요").apply { name = "purge"
addOption(OptionType.INTEGER, "n", "n개만큼 메시지가 삭제 됩니다", true) description = "n개의 메시지를 채널에서 삭제해요"
}.toData() option {
) type = OptionType.INTEGER
name = "n"
description = "n개만큼 메시지가 삭제 됩니다"
required = true
}
}
override suspend fun execute(ev: SlashCommandInteractionEvent) { override suspend fun execute(ev: SlashCommandInteractionEvent) {
val n = ev.getOption("n")!!.asInt 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.entities.User
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData 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.GlobalCommand
import net.projecttl.p.x32.api.command.useCommand
import net.projecttl.p.x32.api.util.colour import net.projecttl.p.x32.api.util.colour
import net.projecttl.p.x32.api.util.footer import net.projecttl.p.x32.api.util.footer
object Ping : GlobalCommand { object Ping : GlobalCommand {
override val data: CommandData = CommandData.fromData(CommandDataImpl( override val data: CommandData = useCommand {
"ping", name = "ping"
"Discord API 레이턴시 확인 합니다" description = "Discord API 레이턴시 확인 합니다"
).toData()) }
override suspend fun execute(ev: SlashCommandInteractionEvent) { override suspend fun execute(ev: SlashCommandInteractionEvent) {
val started = System.currentTimeMillis() val started = System.currentTimeMillis()

Binary file not shown.