diff --git a/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/Plugin.kt b/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/Plugin.kt index 3c7e8cb..a354aa5 100644 --- a/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/Plugin.kt +++ b/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/Plugin.kt @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory abstract class Plugin { private val handlerContainer = mutableListOf() + val config = this.javaClass.getResourceAsStream("/plugin.json")!!.let { val raw = it.bufferedReader().readText() val obj = Json.decodeFromString(raw) diff --git a/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/command/CommandHandler.kt b/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/command/CommandHandler.kt index 5613e10..790d94c 100644 --- a/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/command/CommandHandler.kt +++ b/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/command/CommandHandler.kt @@ -1,5 +1,8 @@ package net.projecttl.p.x32.api.command +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent @@ -15,6 +18,7 @@ fun commandHandler(block: (CommandHandler) -> Unit): CommandHandler { return handler } +@OptIn(DelicateCoroutinesApi::class) class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() { private val commands = mutableListOf() @@ -24,7 +28,7 @@ class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() { commands.forEach { command -> if (command.data.name == name) { if (command is GlobalCommand) { - runBlocking { + GlobalScope.launch { command.execute(ev) } @@ -40,7 +44,7 @@ class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() { commands.forEach { command -> if (command.data.name == name) { if (command is UserContext) { - runBlocking { + GlobalScope.launch { command.execute(ev) } @@ -56,7 +60,7 @@ class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() { commands.forEach { command -> if (command.data.name == name) { if (command is MessageContext) { - runBlocking { + GlobalScope.launch { command.execute(ev) } diff --git a/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/util/Color.kt b/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/util/Color.kt deleted file mode 100644 index e1a7578..0000000 --- a/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/util/Color.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.projecttl.p.x32.api.util - -import net.dv8tion.jda.api.EmbedBuilder -import kotlin.random.Random - -fun EmbedBuilder.colour(): EmbedBuilder { - val rand = Random.nextInt(0x000001, 0xffffff) - return this.setColor(rand) -} diff --git a/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/util/Embed.kt b/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/util/Embed.kt new file mode 100644 index 0000000..58fabad --- /dev/null +++ b/px32-bot-api/src/main/kotlin/net/projecttl/p/x32/api/util/Embed.kt @@ -0,0 +1,24 @@ +package net.projecttl.p.x32.api.util + +import net.dv8tion.jda.api.EmbedBuilder +import net.dv8tion.jda.api.entities.User +import java.time.LocalDate +import java.time.LocalDateTime +import kotlin.random.Random + +fun EmbedBuilder.colour(): EmbedBuilder { + val rand = Random.nextInt(0x000001, 0xffffff) + return this.setColor(rand) +} + +fun EmbedBuilder.footer(user: User): EmbedBuilder { + val date = LocalDateTime.now() + val mon = if (date.month.value > 9) { + date.month.value + } else { + "0${date.month.value}" + } + val str = "${user.name} • ${date.year}-${mon}-${date.dayOfMonth} ${date.hour}:${date.minute}" + + return this.setFooter(str, "${user.avatarUrl}?size=1024") +} diff --git a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/Px32.kt b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/Px32.kt index f30144c..a747ae0 100644 --- a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/Px32.kt +++ b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/Px32.kt @@ -1,5 +1,8 @@ package net.projecttl.p.x32 +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import net.dv8tion.jda.api.JDA import net.projecttl.p.x32.command.Info import net.projecttl.p.x32.command.PluginCommand @@ -17,6 +20,7 @@ lateinit var database: Database val logger: Logger = LoggerFactory.getLogger(Px32::class.java) +@OptIn(DelicateCoroutinesApi::class) fun main() { println("Px32 version v${DefaultConfig.version}") if (Config.owner.isBlank() || Config.owner.isEmpty()) { @@ -31,6 +35,9 @@ fun main() { handler.addCommand(PluginCommand) jda = kernel.build() + GlobalScope.launch { + kernel.register(jda) + } } object Px32 diff --git a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/PluginCommand.kt b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/PluginCommand.kt index 66d3e8f..abafc93 100644 --- a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/PluginCommand.kt +++ b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/PluginCommand.kt @@ -7,6 +7,7 @@ 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.util.colour +import net.projecttl.p.x32.api.util.footer import net.projecttl.p.x32.kernel.PluginLoader object PluginCommand : GlobalCommand { @@ -18,6 +19,7 @@ object PluginCommand : GlobalCommand { setThumbnail(ev.jda.selfUser.avatarUrl) colour() + footer(ev.user) } val loader = PluginLoader.getPlugins() diff --git a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/Reload.kt b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/Reload.kt index 0d2af47..2d82c1f 100644 --- a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/Reload.kt +++ b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/command/Reload.kt @@ -11,7 +11,7 @@ object Reload : GlobalCommand { override val data = CommandData.fromData(CommandDataImpl("reload", "플러그인을 다시 불러 옵니다").toData()) override suspend fun execute(ev: SlashCommandInteractionEvent) { - if (kernel.memLock) { + if (kernel.memLock.isLocked) { return } @@ -20,13 +20,13 @@ object Reload : GlobalCommand { } try { - kernel.reload() + kernel.reload(ev.jda) } catch (ex: Exception) { ex.printStackTrace() ev.reply(":warning: 플러그인을 다시 불러오는 도중에 오류가 발생 했어요. 자세한 내용은 콘솔을 확인해 주세요!").queue() return } - ev.reply(":white_check_mark: 플러그인을 다시 불러 왔어요!\n불러온 플러그인 수: ${kernel.plugins().size}").queue() + ev.reply(":white_check_mark: 플러그인을 다시 불러 왔어요!\n불러온 플러그인 수: ${kernel.plugins.size}").queue() } } \ No newline at end of file diff --git a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/kernel/CoreKernel.kt b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/kernel/CoreKernel.kt index 35410b0..9fe6932 100644 --- a/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/kernel/CoreKernel.kt +++ b/px32-bot-core/src/main/kotlin/net/projecttl/p/x32/kernel/CoreKernel.kt @@ -1,19 +1,16 @@ package net.projecttl.p.x32.kernel +import kotlinx.coroutines.sync.Mutex import net.dv8tion.jda.api.JDA import net.dv8tion.jda.api.JDABuilder import net.dv8tion.jda.api.hooks.ListenerAdapter import net.dv8tion.jda.api.requests.GatewayIntent -import net.projecttl.p.x32.api.Plugin import net.projecttl.p.x32.api.command.CommandHandler import net.projecttl.p.x32.config.Config import net.projecttl.p.x32.func.BundleModule -import net.projecttl.p.x32.jda import net.projecttl.p.x32.logger class CoreKernel(token: String) { - var memLock = false - private set private val builder = JDABuilder.createDefault(token, listOf( GatewayIntent.GUILD_PRESENCES, GatewayIntent.GUILD_MEMBERS, @@ -25,6 +22,9 @@ class CoreKernel(token: String) { private val handlers = mutableListOf() private val commandContainer = CommandHandler() + val memLock = Mutex() + val plugins get() = PluginLoader.getPlugins().map { it.value } + private fun include() { if (Config.bundle) { val b = BundleModule() @@ -44,15 +44,11 @@ class CoreKernel(token: String) { handlers.remove(handler) } - fun plugins(): List { - return PluginLoader.getPlugins().map { it.value } - } - fun build(): JDA { include() PluginLoader.load() - plugins().forEach { plugin -> + plugins.forEach { plugin -> plugin.handlers.forEach { handler -> handlers.add(handler) } @@ -64,7 +60,10 @@ class CoreKernel(token: String) { } builder.addEventListeners(commandContainer) - val jda = builder.build() + return builder.build() + } + + fun register(jda: JDA) { commandContainer.register(jda) handlers.forEach { h -> if (h is CommandHandler) { @@ -75,18 +74,16 @@ class CoreKernel(token: String) { Runtime.getRuntime().addShutdownHook(Thread { PluginLoader.destroy() }) - - return jda } - fun reload() { - if (!memLock) { - memLock = true + suspend fun reload(jda: JDA) { + if (!memLock.isLocked) { + memLock.lock() } val newHandlers = mutableListOf() PluginLoader.destroy() - plugins().forEach { plugin -> + plugins.forEach { plugin -> plugin.handlers.forEach { handler -> if (handlers.contains(handler)) { jda.removeEventListener(handler) @@ -98,7 +95,7 @@ class CoreKernel(token: String) { include() PluginLoader.load() - plugins().forEach { plugin -> + plugins.forEach { plugin -> plugin.handlers.forEach { handler -> if (!handlers.contains(handler)) { handlers.add(handler) @@ -108,15 +105,15 @@ class CoreKernel(token: String) { } handlers.map { - builder.addEventListeners(it) + jda.addEventListener(it) } - newHandlers.forEach { h -> + handlers.forEach { h -> if (h is CommandHandler) { h.register(jda) } } - memLock = false + memLock.unlock() } } diff --git a/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Avatar.kt b/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Avatar.kt index 8fb2477..3da4daa 100644 --- a/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Avatar.kt +++ b/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Avatar.kt @@ -6,6 +6,7 @@ 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.util.colour +import net.projecttl.p.x32.api.util.footer object Avatar : UserContext { override val data = CommandData.fromData(CommandDataImpl("avatar", "유저의 프로필 이미지를 가져 옵니다").toData()) @@ -15,6 +16,7 @@ object Avatar : UserContext { embed.setTitle(":frame_photo: ${ev.target.name}'s Avatar") embed.setImage("${ev.target.effectiveAvatarUrl}?size=512") embed.colour() + embed.footer(ev.user) ev.replyEmbeds(embed.build()).queue() } diff --git a/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Ping.kt b/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Ping.kt index e617f04..cfe18e7 100644 --- a/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Ping.kt +++ b/px32-bot-module/src/main/kotlin/net/projecttl/p/x32/func/command/Ping.kt @@ -3,11 +3,13 @@ package net.projecttl.p.x32.func.command import net.dv8tion.jda.api.EmbedBuilder import net.dv8tion.jda.api.JDA 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.util.colour +import net.projecttl.p.x32.api.util.footer object Ping : GlobalCommand { override val data: CommandData = CommandData.fromData(CommandDataImpl( @@ -25,17 +27,18 @@ object Ping : GlobalCommand { }.build() ev.replyEmbeds(embed).queue { - embed = measure(started, ev.jda) + embed = measure(started, ev.user, ev.jda) it.editOriginalEmbeds(embed).queue() } } - private fun measure(started: Long, jda: JDA): MessageEmbed { + private fun measure(started: Long, user: User, jda: JDA): MessageEmbed { val embed = EmbedBuilder() embed.setTitle(":ping_pong: **Pong!**") embed.addField(":electric_plug: **API**", "`${jda.gatewayPing}ms`", true) embed.addField(":robot: **BOT**", "`${System.currentTimeMillis() - started}ms`", true) embed.colour() + embed.footer(user) return embed.build() } diff --git a/sample-plugin/src/main/groovy/net/projecttl/plugin/sample/CorePlugin.groovy b/sample-plugin/src/main/groovy/net/projecttl/plugin/sample/CorePlugin.groovy index 25b1177..df264ca 100644 --- a/sample-plugin/src/main/groovy/net/projecttl/plugin/sample/CorePlugin.groovy +++ b/sample-plugin/src/main/groovy/net/projecttl/plugin/sample/CorePlugin.groovy @@ -10,7 +10,6 @@ class CorePlugin extends Plugin { logger.info "Hello, World!" CommandHandler handler = new CommandHandler() handler.addCommand new Greeting() - addHandler handler }