mirror of
https://github.com/devproje/px32-bot.git
synced 2025-04-20 07:09:53 +09:00
Compare commits
No commits in common. "master" and "0.1.0" have entirely different histories.
34 changed files with 342 additions and 936 deletions
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Project_IO
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
71
README.md
71
README.md
|
@ -1,71 +0,0 @@
|
||||||
# Px32 Bot
|
|
||||||
- [JDA](https://github.com/discord-jda/JDA)로 만든 Kotlin 디스코드 봇 구동기 입니다.
|
|
||||||
|
|
||||||
## Project Repo
|
|
||||||
- [Gitlab](https://gitlab.wh64.net/devproje/px32-bot.git)
|
|
||||||
- [Github](https://github.com/devproje/px32-bot.git)
|
|
||||||
|
|
||||||
## How to Use
|
|
||||||
봇 구동기는 [이곳](https://github.com/devproje/px32-bot/releases)에서 다운로드 받으실 수 있습니다.
|
|
||||||
|
|
||||||
## Developments
|
|
||||||
API를 사용하여 플러그인 개발을 원하신다면 아래의 코드로 라이브러리를 추가 해 주세요.
|
|
||||||
JDA의 버전은 [이곳](https://github.com/discord-jda/JDA/releases)에서 확인 하실 수 있습니다.
|
|
||||||
|
|
||||||
- Maven
|
|
||||||
```xml
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>ProjectCentral</id>
|
|
||||||
<url>https://repo.wh64.net/repository/maven-releases/</url>
|
|
||||||
</repository>
|
|
||||||
|
|
||||||
// If you want to snapshot version, please write this code.
|
|
||||||
<repository>
|
|
||||||
<id>ProjectCentral-SNAPSHOT</id>
|
|
||||||
<url>https://repo.wh64.net/repository/maven-snapshots/</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
```
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<dependencies>
|
|
||||||
...
|
|
||||||
<!-- JDA is required -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.dv8tion</groupId>
|
|
||||||
<artifactId>JDA</artifactId>
|
|
||||||
<version>VERSION</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.projecttl</groupId>
|
|
||||||
<artifactId>px32-bot-api</artifactId>
|
|
||||||
<version>VERSION</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Gradle
|
|
||||||
```kts
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven("https://repo.wh64.net/repository/maven-releases/")
|
|
||||||
|
|
||||||
// If you want to snapshot version, please write this code.
|
|
||||||
maven("https://repo.wh64.net/repository/maven-snapshots/")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```kts
|
|
||||||
dependencies {
|
|
||||||
...
|
|
||||||
// JDA is required
|
|
||||||
compileOnly("net.dv8tion:JDA:VERSION")
|
|
||||||
compileOnly("net.projecttl:px32-bot-api:VERSION")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
- 해당 프로젝트는 [MIT License](https://gitlab.wh64.net/devproje/px32-bot/-/blob/master/LICENSE)를 따릅니다.
|
|
|
@ -1,6 +1,5 @@
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.0.20"
|
kotlin("jvm") version "2.0.20"
|
||||||
id("org.jetbrains.dokka") version "1.9.20"
|
|
||||||
kotlin("plugin.serialization") version "2.0.20"
|
kotlin("plugin.serialization") version "2.0.20"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +14,6 @@ val sqlite_version: String by project
|
||||||
val postgres_version: String by project
|
val postgres_version: String by project
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
apply(plugin = "org.jetbrains.dokka")
|
|
||||||
apply(plugin = "org.jetbrains.kotlin.jvm")
|
apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||||
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
|
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ kotlin.code.style=official
|
||||||
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
group=net.projecttl
|
group=net.projecttl
|
||||||
version=1.0.0-beta.3
|
version=0.1.0-SNAPSHOT
|
||||||
|
|
||||||
ktor_version=2.3.12
|
ktor_version=2.3.12
|
||||||
log4j_version=2.23.1
|
log4j_version=2.23.1
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
package net.projecttl.p.x32.api
|
|
||||||
|
|
||||||
import org.jetbrains.exposed.sql.Database
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
object BotConfig {
|
|
||||||
private fun useBotConfig(): BotConfigDelegate {
|
|
||||||
return BotConfigDelegate()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun useDatabase(): Database {
|
|
||||||
if (db_username.isNotBlank() && db_password.isNotBlank()) {
|
|
||||||
return Database.connect(
|
|
||||||
driver = db_driver,
|
|
||||||
url = db_url,
|
|
||||||
user = db_username,
|
|
||||||
password = db_password
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Database.connect(
|
|
||||||
driver = db_driver,
|
|
||||||
url = db_url
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val token: String by useBotConfig()
|
|
||||||
val owner: String by useBotConfig()
|
|
||||||
|
|
||||||
private val bundle_func: String by useBotConfig()
|
|
||||||
val bundle = when (bundle_func) {
|
|
||||||
"1" -> true
|
|
||||||
"0" -> false
|
|
||||||
else -> {
|
|
||||||
throw IllegalArgumentException("bundle_func option must be 0 or 1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val db_driver: String by useBotConfig()
|
|
||||||
private val db_url: String by useBotConfig()
|
|
||||||
private val db_username: String by useBotConfig()
|
|
||||||
private val db_password: String by useBotConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BotConfigDelegate {
|
|
||||||
private val props = Properties()
|
|
||||||
|
|
||||||
init {
|
|
||||||
val file = File("config.properties")
|
|
||||||
if (!file.exists()) {
|
|
||||||
val default = this.javaClass.getResourceAsStream("/config.sample.properties")!!.readBytes()
|
|
||||||
file.outputStream().use { stream ->
|
|
||||||
stream.write(default)
|
|
||||||
}
|
|
||||||
|
|
||||||
println("config.properties is not found, create new one...")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
props.load(FileInputStream(file))
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
|
|
||||||
return props.getProperty(property.name).toString()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,24 +3,11 @@ package net.projecttl.p.x32.api
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
import net.projecttl.p.x32.api.model.PluginConfig
|
import net.projecttl.p.x32.api.model.PluginConfig
|
||||||
import net.projecttl.p.x32.api.util.AsyncTaskContainer
|
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class Plugin {
|
abstract class Plugin {
|
||||||
private val handlerContainer = mutableListOf<ListenerAdapter>()
|
private val handlerContainer = mutableListOf<ListenerAdapter>()
|
||||||
val taskContainer = AsyncTaskContainer()
|
|
||||||
val pluginDataDir: File
|
|
||||||
get() {
|
|
||||||
val f = File("plugins/${config.name}")
|
|
||||||
if (!f.exists()) {
|
|
||||||
f.mkdirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
val config = this.javaClass.getResourceAsStream("/plugin.json")!!.let {
|
val config = this.javaClass.getResourceAsStream("/plugin.json")!!.let {
|
||||||
val raw = it.bufferedReader().readText()
|
val raw = it.bufferedReader().readText()
|
||||||
val obj = Json.decodeFromString<PluginConfig>(raw)
|
val obj = Json.decodeFromString<PluginConfig>(raw)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package net.projecttl.p.x32.api.command
|
package net.projecttl.p.x32.api.command
|
||||||
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import net.dv8tion.jda.api.JDA
|
import net.dv8tion.jda.api.JDA
|
||||||
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent
|
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
||||||
|
@ -10,28 +8,21 @@ import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEven
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
import net.dv8tion.jda.api.interactions.commands.build.Commands
|
||||||
|
|
||||||
fun commandHandler(guildId: Long = 0L, block: (CommandHandler) -> Unit): CommandHandler {
|
|
||||||
val handler = CommandHandler(guildId)
|
|
||||||
block.invoke(handler)
|
|
||||||
|
|
||||||
return handler
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() {
|
class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() {
|
||||||
private val commands = mutableListOf<CommandExecutor>()
|
private val commands = mutableListOf<CommandExecutor>()
|
||||||
|
|
||||||
override fun onSlashCommandInteraction(ev: SlashCommandInteractionEvent) {
|
override fun onSlashCommandInteraction(ev: SlashCommandInteractionEvent) {
|
||||||
val name = ev.interaction.name
|
val name = ev.interaction.name
|
||||||
|
|
||||||
commands.singleOrNull { it.data.name == name }?.also { command ->
|
commands.forEach { command ->
|
||||||
|
if (command.data.name == name) {
|
||||||
if (command is GlobalCommand) {
|
if (command is GlobalCommand) {
|
||||||
GlobalScope.launch {
|
runBlocking {
|
||||||
command.execute(ev)
|
command.execute(ev)
|
||||||
println("${ev.user.id} is use command for: ${ev.interaction.name}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return@forEach
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,14 +30,15 @@ class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() {
|
||||||
override fun onUserContextInteraction(ev: UserContextInteractionEvent) {
|
override fun onUserContextInteraction(ev: UserContextInteractionEvent) {
|
||||||
val name = ev.interaction.name
|
val name = ev.interaction.name
|
||||||
|
|
||||||
commands.singleOrNull { it.data.name == name }?.also { command ->
|
commands.forEach { command ->
|
||||||
|
if (command.data.name == name) {
|
||||||
if (command is UserContext) {
|
if (command is UserContext) {
|
||||||
GlobalScope.launch {
|
runBlocking {
|
||||||
command.execute(ev)
|
command.execute(ev)
|
||||||
println("${ev.user.id} is use user context for: ${ev.interaction.name}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return@forEach
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,14 +46,15 @@ class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() {
|
||||||
override fun onMessageContextInteraction(ev: MessageContextInteractionEvent) {
|
override fun onMessageContextInteraction(ev: MessageContextInteractionEvent) {
|
||||||
val name = ev.interaction.name
|
val name = ev.interaction.name
|
||||||
|
|
||||||
commands.singleOrNull { it.data.name == name }?.also { command ->
|
commands.forEach { command ->
|
||||||
|
if (command.data.name == name) {
|
||||||
if (command is MessageContext) {
|
if (command is MessageContext) {
|
||||||
GlobalScope.launch {
|
runBlocking {
|
||||||
command.execute(ev)
|
command.execute(ev)
|
||||||
println("${ev.user.id} is use message context for: ${ev.interaction.name}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return@forEach
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,10 +106,10 @@ class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() {
|
||||||
|
|
||||||
if (command is MessageContext) {
|
if (command is MessageContext) {
|
||||||
if (guild == null) {
|
if (guild == null) {
|
||||||
jda.upsertCommand(Commands.message(data.name)).queue()
|
jda.upsertCommand(Commands.message(data.name))
|
||||||
println("Register Message Context Command: /${data.name}")
|
println("Register Message Context Command: /${data.name}")
|
||||||
} else {
|
} else {
|
||||||
guild.upsertCommand(Commands.message(data.name)).queue()
|
guild.upsertCommand(Commands.message(data.name))
|
||||||
println("Register '${guild.id}' Guild's Message Context Command: /${data.name}")
|
println("Register '${guild.id}' Guild's Message Context Command: /${data.name}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,135 +0,0 @@
|
||||||
package net.projecttl.p.x32.api.command
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.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
|
|
||||||
|
|
||||||
var choices = mutableListOf<Command.Choice>()
|
|
||||||
|
|
||||||
fun build(): OptionData {
|
|
||||||
return OptionData(type, name, description, required, autoComplete).apply {
|
|
||||||
this@OptionObj.choices.forEach {
|
|
||||||
addChoices(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
package net.projecttl.p.x32.api.util
|
|
||||||
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class only support by kotlin
|
|
||||||
* This feature is Experimental
|
|
||||||
*/
|
|
||||||
class AsyncTask(val loop: Boolean = true, val block: suspend () -> Unit) {
|
|
||||||
private val memLock = Mutex()
|
|
||||||
var isActive = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
suspend fun run() {
|
|
||||||
do {
|
|
||||||
if (memLock.isLocked) {
|
|
||||||
if (!isActive) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
block.invoke()
|
|
||||||
} while (loop)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun kill() {
|
|
||||||
memLock.lock()
|
|
||||||
isActive = false
|
|
||||||
|
|
||||||
memLock.unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class only support by kotlin
|
|
||||||
* This feature is Experimental
|
|
||||||
*/
|
|
||||||
class AsyncTaskContainer {
|
|
||||||
val tasks = mutableMapOf<Long, AsyncTask>()
|
|
||||||
private var tid = 0L
|
|
||||||
|
|
||||||
fun getTask(tid: Long): AsyncTask? {
|
|
||||||
return tasks[tid]
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createTask(task: AsyncTask) {
|
|
||||||
tasks[tid] = task
|
|
||||||
println("created task with id: $tid")
|
|
||||||
try {
|
|
||||||
task.run()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
tasks.remove(tid)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tid++
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun removeTask(tid: Long) {
|
|
||||||
val task = getTask(tid) ?: throw NullPointerException("task $tid is missing or not defined")
|
|
||||||
task.kill()
|
|
||||||
|
|
||||||
tasks.remove(tid)
|
|
||||||
println("removed task with id: $tid")
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun killAll() {
|
|
||||||
tasks.forEach { (k, t) ->
|
|
||||||
t.kill()
|
|
||||||
tasks.remove(k)
|
|
||||||
println("removed task with id: $tid")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -1,23 +0,0 @@
|
||||||
package net.projecttl.p.x32.api.util
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.EmbedBuilder
|
|
||||||
import net.dv8tion.jda.api.entities.User
|
|
||||||
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")
|
|
||||||
}
|
|
|
@ -14,7 +14,9 @@ repositories {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":${rootProject.name}-api"))
|
implementation(project(":${rootProject.name}-api"))
|
||||||
implementation(project(":${rootProject.name}-module"))
|
implementation(project(":px32-bot-module"))
|
||||||
|
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
|
|
@ -1,62 +1,34 @@
|
||||||
package net.projecttl.p.x32
|
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.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.PluginCommand
|
||||||
import net.projecttl.p.x32.command.Reload
|
import net.projecttl.p.x32.command.Reload
|
||||||
|
import net.projecttl.p.x32.config.Config
|
||||||
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.jetbrains.exposed.sql.Database
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
lateinit var jda: JDA
|
lateinit var jda: JDA
|
||||||
private set
|
|
||||||
lateinit var kernel: CoreKernel
|
lateinit var kernel: CoreKernel
|
||||||
private set
|
lateinit var database: Database
|
||||||
|
|
||||||
val logger: Logger = LoggerFactory.getLogger(Px32::class.java)
|
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}")
|
println("Px32 version v${DefaultConfig.version}")
|
||||||
if (BotConfig.owner.isBlank() || BotConfig.owner.isEmpty()) {
|
if (Config.owner.isBlank() || Config.owner.isEmpty()) {
|
||||||
logger.warn("owner option is blank or empty!")
|
logger.warn("owner option is blank or empty!")
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel = CoreKernel(BotConfig.token)
|
kernel = CoreKernel(Config.token)
|
||||||
val handler = kernel.commandContainer
|
val handler = kernel.getCommandContainer()
|
||||||
|
|
||||||
if (args.contains("--remove-cmd")) {
|
|
||||||
jda = kernel.build()
|
|
||||||
try {
|
|
||||||
val list = jda.retrieveCommands().complete()
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
list.forEach { command ->
|
|
||||||
logger.info("unregister command: /${command.name}")
|
|
||||||
command.jda.deleteCommandById(command.id).complete()
|
|
||||||
}
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.addCommand(Info)
|
|
||||||
handler.addCommand(Reload)
|
handler.addCommand(Reload)
|
||||||
handler.addCommand(PluginCommand)
|
handler.addCommand(PluginCommand)
|
||||||
|
|
||||||
jda = kernel.build()
|
jda = kernel.build()
|
||||||
GlobalScope.launch {
|
|
||||||
kernel.register(jda)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Px32
|
object Px32
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
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.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 = useCommand {
|
|
||||||
name = "info"
|
|
||||||
description = "봇의 정보를 표시 합니다"
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
|
||||||
val rb = ManagementFactory.getRuntimeMXBean()
|
|
||||||
val r = Runtime.getRuntime()
|
|
||||||
|
|
||||||
val size = kernel.plugins.size
|
|
||||||
val info = """
|
|
||||||
Px32Bot v${DefaultConfig.version}, JDA `v${JDAInfo.VERSION}`,
|
|
||||||
`Java ${System.getProperty("java.version")}` and `Kotlin ${KotlinVersion.CURRENT}` System on `${System.getProperty("os.name")}`
|
|
||||||
|
|
||||||
Process Started on <t:${(System.currentTimeMillis() - rb.uptime)/ 1000L}:R>
|
|
||||||
Bot Process Running on PID `${rb.pid}`
|
|
||||||
|
|
||||||
Assigned `${r.maxMemory() / 1048576}MB` of Max Memories at this Bot
|
|
||||||
Using `${(r.totalMemory() - r.freeMemory()) / 1048576}MB` at this Bot
|
|
||||||
|
|
||||||
Total $size plugin${if (size > 1) "s" else ""} loaded
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
ev.reply(info).queue()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,17 +3,14 @@ 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.kernel.PluginLoader
|
||||||
import net.projecttl.p.x32.kernel
|
|
||||||
|
|
||||||
object PluginCommand : GlobalCommand {
|
object PluginCommand : GlobalCommand {
|
||||||
override val data = useCommand {
|
override val data = CommandData.fromData(CommandDataImpl("plugin", "봇에 불러온 플러그인을 확인 합니다").toData())
|
||||||
name = "plugins"
|
|
||||||
description = "봇에 불러온 플러그인을 확인 합니다"
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
||||||
val embed = EmbedBuilder().apply {
|
val embed = EmbedBuilder().apply {
|
||||||
|
@ -21,10 +18,9 @@ object PluginCommand : GlobalCommand {
|
||||||
setThumbnail(ev.jda.selfUser.avatarUrl)
|
setThumbnail(ev.jda.selfUser.avatarUrl)
|
||||||
|
|
||||||
colour()
|
colour()
|
||||||
footer(ev.user)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val loader = kernel.plugins
|
val loader = PluginLoader.getPlugins()
|
||||||
val fields = loader.map { (c, _) ->
|
val fields = loader.map { (c, _) ->
|
||||||
MessageEmbed.Field(":electric_plug: **${c.name}**", "`${c.version}`", true)
|
MessageEmbed.Field(":electric_plug: **${c.name}**", "`${c.version}`", true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,32 @@
|
||||||
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.projecttl.p.x32.api.BotConfig
|
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.Config
|
||||||
import net.projecttl.p.x32.kernel
|
import net.projecttl.p.x32.kernel
|
||||||
|
|
||||||
object Reload : GlobalCommand {
|
object Reload : GlobalCommand {
|
||||||
override val data = useCommand {
|
override val data = CommandData.fromData(CommandDataImpl("reload", "플러그인을 다시 불러 옵니다").toData())
|
||||||
name = "reload"
|
|
||||||
description = "플러그인을 다시 불러 옵니다"
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
||||||
if (kernel.memLock.isLocked) {
|
if (kernel.memLock) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.user.id != BotConfig.owner) {
|
if (ev.user.id != Config.owner) {
|
||||||
return ev.reply(":warning: 권한을 가지고 있지 않아요").queue()
|
return ev.reply(":warning: 권한을 가지고 있지 않아요").queue()
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
kernel.reload(ev.jda)
|
kernel.reload()
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
ev.reply(":warning: 플러그인을 다시 불러오는 도중에 오류가 발생 했어요. 자세한 내용은 콘솔을 확인해 주세요!").queue()
|
ev.reply(":warning: 플러그인을 다시 불러오는 도중에 오류가 발생 했어요. 자세한 내용은 콘솔을 확인해 주세요!").queue()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ev.reply(":white_check_mark: 플러그인을 다시 불러 왔어요!\n불러온 플러그인 수: ${kernel.plugins.size}").queue()
|
ev.reply(":white_check_mark: 플러그인을 다시 불러 왔어요!\n불러온 플러그인 수: ${kernel.plugins().size}").queue()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package net.projecttl.p.x32.config
|
||||||
|
|
||||||
|
import net.projecttl.p.x32.logger
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
object Config {
|
||||||
|
private fun useConfig(): ConfigDelegate {
|
||||||
|
return ConfigDelegate()
|
||||||
|
}
|
||||||
|
|
||||||
|
val token: String by useConfig()
|
||||||
|
val owner: String by useConfig()
|
||||||
|
|
||||||
|
private val bundle_func: String by useConfig()
|
||||||
|
val bundle = if (bundle_func == "1") true else if (bundle_func == "0") false else throw IllegalArgumentException("bundle_func option must be 0 or 1")
|
||||||
|
|
||||||
|
val db_driver: String by useConfig()
|
||||||
|
val db_url: String by useConfig()
|
||||||
|
val db_username: String by useConfig()
|
||||||
|
val db_password: String by useConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConfigDelegate {
|
||||||
|
private val props = Properties()
|
||||||
|
|
||||||
|
init {
|
||||||
|
val file = File("config.properties")
|
||||||
|
if (!file.exists()) {
|
||||||
|
val default = this.javaClass.getResourceAsStream("/config.sample.properties")!!.readBytes()
|
||||||
|
file.outputStream().use { stream ->
|
||||||
|
stream.write(default)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error("config.properties is not found, create new one...")
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
props.load(FileInputStream(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
|
||||||
|
return props.getProperty(property.name).toString()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,221 +1,112 @@
|
||||||
package net.projecttl.p.x32.kernel
|
package net.projecttl.p.x32.kernel
|
||||||
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import net.dv8tion.jda.api.JDA
|
import net.dv8tion.jda.api.JDA
|
||||||
import net.dv8tion.jda.api.JDABuilder
|
import net.dv8tion.jda.api.JDABuilder
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
import net.dv8tion.jda.api.requests.GatewayIntent
|
|
||||||
import net.dv8tion.jda.api.utils.MemberCachePolicy
|
|
||||||
import net.projecttl.p.x32.api.BotConfig
|
|
||||||
import net.projecttl.p.x32.api.Plugin
|
import net.projecttl.p.x32.api.Plugin
|
||||||
import net.projecttl.p.x32.api.command.CommandHandler
|
import net.projecttl.p.x32.api.command.CommandHandler
|
||||||
import net.projecttl.p.x32.api.model.PluginConfig
|
import net.projecttl.p.x32.config.Config
|
||||||
import net.projecttl.p.x32.config.DefaultConfig
|
import net.projecttl.p.x32.func.General
|
||||||
import net.projecttl.p.x32.func.BundleModule
|
import net.projecttl.p.x32.jda
|
||||||
import net.projecttl.p.x32.logger
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
import java.util.jar.JarFile
|
|
||||||
|
|
||||||
class CoreKernel(token: String) {
|
class CoreKernel(token: String) {
|
||||||
lateinit var jda: JDA
|
var memLock = false
|
||||||
private set
|
private set
|
||||||
|
private val builder = JDABuilder.createDefault(token)
|
||||||
|
private val handlers = mutableListOf<ListenerAdapter>()
|
||||||
|
private val commandContainer = CommandHandler()
|
||||||
|
|
||||||
private val builder = JDABuilder.createDefault(token, listOf(
|
private fun include() {
|
||||||
GatewayIntent.GUILD_MEMBERS,
|
if (Config.bundle) {
|
||||||
GatewayIntent.GUILD_MESSAGES,
|
val b = General()
|
||||||
GatewayIntent.MESSAGE_CONTENT,
|
PluginLoader.putModule(b.config, b)
|
||||||
GatewayIntent.GUILD_PRESENCES,
|
|
||||||
GatewayIntent.SCHEDULED_EVENTS,
|
|
||||||
GatewayIntent.GUILD_VOICE_STATES,
|
|
||||||
GatewayIntent.GUILD_EMOJIS_AND_STICKERS
|
|
||||||
)).setMemberCachePolicy(MemberCachePolicy.ALL)
|
|
||||||
|
|
||||||
val memLock = Mutex()
|
|
||||||
val commandContainer = CommandHandler()
|
|
||||||
var plugins = mutableMapOf<PluginConfig, Plugin>()
|
|
||||||
private set
|
|
||||||
var isActive = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val parentDir = File("./plugins").apply {
|
|
||||||
if (!exists()) {
|
|
||||||
mkdirs()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val handlers: List<ListenerAdapter>
|
fun getCommandContainer(): CommandHandler {
|
||||||
get() {
|
return commandContainer
|
||||||
if (!isActive) {
|
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
return jda.eventManager.registeredListeners.map { it as ListenerAdapter }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addHandler(handler: ListenerAdapter) {
|
fun addHandler(handler: ListenerAdapter) {
|
||||||
if (isActive) {
|
handlers.add(handler)
|
||||||
jda.addEventListener(handler)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.addEventListeners(handler)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delHandler(handler: ListenerAdapter) {
|
fun delHandler(handler: ListenerAdapter) {
|
||||||
if (isActive) {
|
handlers.remove(handler)
|
||||||
jda.removeEventListener(handler)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.removeEventListeners(handler)
|
fun plugins(): List<Plugin> {
|
||||||
}
|
return PluginLoader.getPlugins().map { it.value }
|
||||||
|
|
||||||
fun register(jda: JDA) {
|
|
||||||
commandContainer.register(jda)
|
|
||||||
jda.eventManager.registeredListeners.filterIsInstance<CommandHandler>().forEach { h ->
|
|
||||||
h.register(jda)
|
|
||||||
}
|
|
||||||
|
|
||||||
Runtime.getRuntime().addShutdownHook(Thread {
|
|
||||||
destroy()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun reload(jda: JDA) {
|
|
||||||
if (!memLock.isLocked) {
|
|
||||||
memLock.lock()
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy()
|
|
||||||
load()
|
|
||||||
handlers.filterIsInstance<CommandHandler>().forEach { h ->
|
|
||||||
h.register(jda)
|
|
||||||
}
|
|
||||||
|
|
||||||
memLock.unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun kill() {
|
|
||||||
isActive = false
|
|
||||||
|
|
||||||
destroy()
|
|
||||||
logger.info("shutdown now Px32 kernel v${DefaultConfig.version}")
|
|
||||||
jda.shutdownNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun load() {
|
|
||||||
if (BotConfig.bundle) {
|
|
||||||
val b = BundleModule()
|
|
||||||
loadModule(b.config, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
parentDir.listFiles()?.forEach { file ->
|
|
||||||
try {
|
|
||||||
loadPlugin(file)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
logger.error("error occurred while to plugin loading: ${ex.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cnt = 0
|
|
||||||
plugins.forEach { (_, plugin) ->
|
|
||||||
plugin.handlers.forEach {
|
|
||||||
addHandler(it)
|
|
||||||
logger.info("Load event listener: ${it::class.simpleName}")
|
|
||||||
cnt++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Loaded ${plugins.size} plugin${if (plugins.size > 1) "s" else ""} and $cnt handler${if (cnt > 1) "s" else ""}.")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun destroy() {
|
|
||||||
plugins.forEach { (config, plugin) ->
|
|
||||||
logger.info("disable ${config.name} plugin...")
|
|
||||||
|
|
||||||
plugin.handlers.forEach {
|
|
||||||
delHandler(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
plugin.destroy()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
logger.error("failed to destroy ${config.name} plugin")
|
|
||||||
ex.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins = mutableMapOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadPlugin(file: File) {
|
|
||||||
if (file.name == "px32-bot-module") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.name.endsWith(".jar")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val jar = JarFile(file)
|
|
||||||
val cnf = jar.entries().toList().singleOrNull { jarEntry -> jarEntry.name == "plugin.json" }
|
|
||||||
if (cnf == null)
|
|
||||||
throw IllegalAccessException("${file.name} is not a plugin. aborted")
|
|
||||||
|
|
||||||
val stream = jar.getInputStream(cnf)
|
|
||||||
val raw = stream.use {
|
|
||||||
return@use it.readBytes().toString(Charset.forName("UTF-8"))
|
|
||||||
}
|
|
||||||
|
|
||||||
val config = Json.decodeFromString<PluginConfig>(raw)
|
|
||||||
val cl = URLClassLoader(arrayOf(file.toPath().toUri().toURL()))
|
|
||||||
val obj = cl.loadClass(config.main).getDeclaredConstructor().newInstance()
|
|
||||||
|
|
||||||
if (obj !is Plugin)
|
|
||||||
throw IllegalAccessException("${config.name} is not valid plugin class. aborted")
|
|
||||||
|
|
||||||
try {
|
|
||||||
loadModule(config, obj)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
throw ex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadModule(config: PluginConfig, plugin: Plugin) {
|
|
||||||
try {
|
|
||||||
logger.info("Load plugin ${config.name} v${config.version}")
|
|
||||||
plugin.onLoad()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
try {
|
|
||||||
plugin.destroy()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
throw ex
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ex
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins[config] = plugin
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(): JDA {
|
fun build(): JDA {
|
||||||
if (isActive) {
|
include()
|
||||||
logger.error("core kernel is already loaded! you cannot rebuild this kernel.")
|
|
||||||
return jda
|
PluginLoader.load()
|
||||||
|
plugins().forEach { plugin ->
|
||||||
|
plugin.handlers.forEach { handler ->
|
||||||
|
handlers.add(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
load()
|
handlers.map {
|
||||||
|
builder.addEventListeners(it)
|
||||||
|
}
|
||||||
builder.addEventListeners(commandContainer)
|
builder.addEventListeners(commandContainer)
|
||||||
jda = builder.build()
|
|
||||||
isActive = true
|
val jda = builder.build()
|
||||||
|
commandContainer.register(jda)
|
||||||
|
handlers.forEach { h ->
|
||||||
|
if (h is CommandHandler) {
|
||||||
|
h.register(jda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Runtime.getRuntime().addShutdownHook(Thread {
|
Runtime.getRuntime().addShutdownHook(Thread {
|
||||||
kill()
|
PluginLoader.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
return jda
|
return jda
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reload() {
|
||||||
|
if (!memLock) {
|
||||||
|
memLock = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val newHandlers = mutableListOf<ListenerAdapter>()
|
||||||
|
PluginLoader.destroy()
|
||||||
|
plugins().forEach { plugin ->
|
||||||
|
plugin.handlers.forEach { handler ->
|
||||||
|
if (handlers.contains(handler)) {
|
||||||
|
jda.removeEventListener(handler)
|
||||||
|
handlers.remove(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include()
|
||||||
|
PluginLoader.load()
|
||||||
|
|
||||||
|
plugins().forEach { plugin ->
|
||||||
|
plugin.handlers.forEach { handler ->
|
||||||
|
if (!handlers.contains(handler)) {
|
||||||
|
handlers.add(handler)
|
||||||
|
newHandlers.add(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers.map {
|
||||||
|
builder.addEventListeners(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
newHandlers.forEach { h ->
|
||||||
|
if (h is CommandHandler) {
|
||||||
|
h.register(jda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memLock = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package net.projecttl.p.x32.kernel
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import net.projecttl.p.x32.api.Plugin
|
||||||
|
import net.projecttl.p.x32.api.model.PluginConfig
|
||||||
|
import net.projecttl.p.x32.logger
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
|
||||||
|
object PluginLoader {
|
||||||
|
private val plugins = mutableMapOf<PluginConfig, Plugin>()
|
||||||
|
private val parentDir = File("./plugins").apply {
|
||||||
|
if (!exists()) {
|
||||||
|
mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPlugins(): Map<PluginConfig, Plugin> {
|
||||||
|
return plugins.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putModule(config: PluginConfig, plugin: Plugin) {
|
||||||
|
try {
|
||||||
|
logger.info("Load module ${config.name} v${config.version}")
|
||||||
|
plugin.onLoad()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
plugin.destroy()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins[config] = plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
parentDir.listFiles()?.forEach { file ->
|
||||||
|
loadPlugin(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Loaded ${plugins.size} plugins")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun destroy() {
|
||||||
|
plugins.forEach { (config, plugin) ->
|
||||||
|
logger.info("disable ${config.name} plugin...")
|
||||||
|
|
||||||
|
try {
|
||||||
|
plugin.destroy()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
logger.error("failed to destroy ${config.name} plugin")
|
||||||
|
ex.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadPlugin(file: File) {
|
||||||
|
if (file.name == "px32-bot-module") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.name.endsWith(".jar")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val jar = JarFile(file)
|
||||||
|
val cnf = jar.entries().toList().singleOrNull { jarEntry -> jarEntry.name == "plugin.json" }
|
||||||
|
if (cnf == null)
|
||||||
|
return logger.error("${file.name} is not a plugin. aborted")
|
||||||
|
|
||||||
|
val stream = jar.getInputStream(cnf)
|
||||||
|
val raw = stream.use {
|
||||||
|
return@use it.readBytes().toString(Charset.forName("UTF-8"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val config = Json.decodeFromString<PluginConfig>(raw)
|
||||||
|
val cl = URLClassLoader(arrayOf(file.toPath().toUri().toURL()))
|
||||||
|
val obj = cl.loadClass(config.main).getDeclaredConstructor().newInstance()
|
||||||
|
|
||||||
|
if (obj !is Plugin)
|
||||||
|
return logger.error("${config.name} is not valid main class. aborted")
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("Load plugin ${config.name} v${config.version}")
|
||||||
|
obj.onLoad()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
return logger.error("Failed to load plugin ${config.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins[config] = obj
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
token=<discord_bot_token>
|
token=<discord_bot_token>
|
||||||
owner=
|
owner=
|
||||||
|
|
||||||
# Px32's Default module activation
|
|
||||||
# setting value is 1 or 0
|
|
||||||
bundle_func=1
|
bundle_func=1
|
||||||
|
|
||||||
# Database connection settings
|
|
||||||
db_driver=org.sqlite.JDBC
|
db_driver=org.sqlite.JDBC
|
||||||
db_url=jdbc:sqlite:data.db
|
db_url=jdbc:sqlite:data.db
|
||||||
db_username=
|
db_username=
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
version=${rootProject.version}
|
version=${project.version}
|
||||||
|
|
|
@ -2,8 +2,8 @@ plugins {
|
||||||
id("java")
|
id("java")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = rootProject.group
|
group = "net.projecttl"
|
||||||
version = rootProject.version
|
version = "0.1.0-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
package net.projecttl.p.x32.func
|
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
object Conf {
|
object Conf {
|
||||||
private fun useDef(): DefDel {
|
|
||||||
return DefDel()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun useConf(): ConfDel {
|
private fun useConf(): ConfDel {
|
||||||
return ConfDel()
|
return ConfDel()
|
||||||
}
|
}
|
||||||
|
|
||||||
val owner: String by useConf()
|
val owner: String by useConf()
|
||||||
val version: String by useDef()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ConfDel {
|
private class ConfDel {
|
||||||
|
@ -30,15 +23,3 @@ private class ConfDel {
|
||||||
return props.getProperty(property.name).toString()
|
return props.getProperty(property.name).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefDel {
|
|
||||||
private val props = Properties()
|
|
||||||
|
|
||||||
init {
|
|
||||||
props.load(this.javaClass.getResourceAsStream("/default.properties"))
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
|
|
||||||
return props.getProperty(property.name).toString()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package net.projecttl.p.x32.func
|
|
||||||
|
|
||||||
import net.projecttl.p.x32.api.Plugin
|
|
||||||
import net.projecttl.p.x32.api.command.commandHandler
|
|
||||||
import net.projecttl.p.x32.func.command.*
|
|
||||||
import net.projecttl.p.x32.func.handler.Ready
|
|
||||||
|
|
||||||
lateinit var instance: BundleModule
|
|
||||||
|
|
||||||
class BundleModule : Plugin() {
|
|
||||||
override fun onLoad() {
|
|
||||||
instance = this
|
|
||||||
|
|
||||||
logger.info("Created by Project_IO")
|
|
||||||
logger.info("Hello! This is Px32's general module!")
|
|
||||||
|
|
||||||
addHandler(Ready)
|
|
||||||
addHandler(commandHandler { handler ->
|
|
||||||
handler.addCommand(Avatar)
|
|
||||||
handler.addCommand(Bmi)
|
|
||||||
handler.addCommand(Dice)
|
|
||||||
handler.addCommand(MsgLength)
|
|
||||||
handler.addCommand(MsgPurge)
|
|
||||||
handler.addCommand(Ping)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun destroy() {
|
|
||||||
logger.info("bye!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package net.projecttl.p.x32.func
|
||||||
|
|
||||||
|
import net.projecttl.p.x32.api.Plugin
|
||||||
|
import net.projecttl.p.x32.api.command.CommandHandler
|
||||||
|
import net.projecttl.p.x32.func.command.Avatar
|
||||||
|
import net.projecttl.p.x32.func.command.MsgLength
|
||||||
|
import net.projecttl.p.x32.func.command.MsgPurge
|
||||||
|
import net.projecttl.p.x32.func.command.Ping
|
||||||
|
import net.projecttl.p.x32.func.handler.Ready
|
||||||
|
|
||||||
|
|
||||||
|
class General : Plugin() {
|
||||||
|
override fun onLoad() {
|
||||||
|
logger.info("Created by Project_IO")
|
||||||
|
logger.info("Hello! This is Px32's general module!")
|
||||||
|
|
||||||
|
addHandler(Ready)
|
||||||
|
addHandler(with(CommandHandler()) {
|
||||||
|
addCommand(Avatar)
|
||||||
|
addCommand(MsgLength)
|
||||||
|
addCommand(Ping)
|
||||||
|
addCommand(MsgPurge)
|
||||||
|
|
||||||
|
this
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun destroy() {
|
||||||
|
logger.info("bye!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,23 +2,19 @@ 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
|
|
||||||
|
|
||||||
object Avatar : UserContext {
|
object Avatar : UserContext {
|
||||||
override val data = useCommand {
|
override val data = CommandData.fromData(CommandDataImpl("avatar", "유저의 프로필 이미지를 가져 옵니다").toData())
|
||||||
name = "avatar"
|
|
||||||
description = "유저의 프로필 이미지를 가져 옵니다"
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun execute(ev: UserContextInteractionEvent) {
|
override suspend fun execute(ev: UserContextInteractionEvent) {
|
||||||
val embed = EmbedBuilder()
|
val embed = EmbedBuilder()
|
||||||
embed.setTitle(":frame_photo: ${ev.target.name}'s Avatar")
|
embed.setTitle(":frame_photo: ${ev.target.name}'s Avatar")
|
||||||
embed.setImage("${ev.target.effectiveAvatarUrl}?size=512")
|
embed.setImage("${ev.target.effectiveAvatarUrl}?size=512")
|
||||||
embed.colour()
|
embed.colour()
|
||||||
embed.footer(ev.user)
|
|
||||||
|
|
||||||
ev.replyEmbeds(embed.build()).queue()
|
ev.replyEmbeds(embed.build()).queue()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
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.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
|
|
||||||
import java.text.DecimalFormat
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
object Bmi : GlobalCommand {
|
|
||||||
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
|
|
||||||
val weight = ev.getOption("weight")!!.asDouble
|
|
||||||
|
|
||||||
if (height <= 0.0 || weight <= 0.0) {
|
|
||||||
ev.reply(":warning: 키 또는 몸무게가 0 또는 음수일 수 없어요").setEphemeral(true).queue()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val bmi = weight / (height / 100).pow(2)
|
|
||||||
fun result(bmi: Double): String {
|
|
||||||
return when {
|
|
||||||
bmi < 18.5 -> "**저체중**"
|
|
||||||
bmi in 18.5..24.9 -> "**정상 체중**"
|
|
||||||
bmi in 25.0..29.9 -> "**과체중**"
|
|
||||||
else -> "**비만**"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val df = DecimalFormat("#.##")
|
|
||||||
df.roundingMode = RoundingMode.HALF_UP
|
|
||||||
|
|
||||||
ev.replyEmbeds(EmbedBuilder().apply {
|
|
||||||
setTitle(":pencil: BMI 지수가 나왔어요")
|
|
||||||
setDescription("${result(bmi)} 이에요")
|
|
||||||
addField(":straight_ruler: **신장 길이**", "`${height}cm`", true)
|
|
||||||
addField(":scales: **몸무게**", "`${weight}kg`", true)
|
|
||||||
addField(":chart_with_downwards_trend: **BMI 지수**", "`${df.format(bmi)}` ", true)
|
|
||||||
|
|
||||||
colour()
|
|
||||||
footer(ev.user)
|
|
||||||
}.build()).queue()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package net.projecttl.p.x32.func.command
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
|
|
||||||
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 = useCommand {
|
|
||||||
name = "dice"
|
|
||||||
description = "랜덤으로 주사위를 굴립니다"
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
|
||||||
val rand = Random.nextInt(1..6)
|
|
||||||
ev.reply(":game_die: **${rand}**").queue()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,15 @@
|
||||||
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 = useCommand {
|
override val data = CommandData.fromData(CommandDataImpl("length", "메시지의 길이를 확인 합니다.").toData())
|
||||||
name = "length"
|
|
||||||
description = "메시지의 길이를 확인 합니다."
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun execute(ev: MessageContextInteractionEvent) {
|
override suspend fun execute(ev: MessageContextInteractionEvent) {
|
||||||
val target = ev.target
|
val target = ev.target
|
||||||
ev.reply("${target.jumpUrl} 메시지의 길이: ${target.contentRaw.length}").queue()
|
ev.reply("${target.jumpUrl} 메시지의 길이:\n\t${target.contentRaw.split("\\s+").size}").queue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
package net.projecttl.p.x32.func.command
|
package net.projecttl.p.x32.func.command
|
||||||
|
|
||||||
|
import Conf
|
||||||
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
|
|
||||||
|
|
||||||
object MsgPurge : GlobalCommand {
|
object MsgPurge : GlobalCommand {
|
||||||
override val data = useCommand {
|
override val data = CommandData.fromData(
|
||||||
name = "purge"
|
CommandDataImpl("purge", "n개의 메시지를 채널에서 삭제해요").apply {
|
||||||
description = "n개의 메시지를 채널에서 삭제해요"
|
addOption(OptionType.INTEGER, "n", "n개만큼 메시지가 삭제 됩니다", true)
|
||||||
option {
|
}.toData()
|
||||||
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
|
||||||
|
|
|
@ -3,19 +3,17 @@ package net.projecttl.p.x32.func.command
|
||||||
import net.dv8tion.jda.api.EmbedBuilder
|
import net.dv8tion.jda.api.EmbedBuilder
|
||||||
import net.dv8tion.jda.api.JDA
|
import net.dv8tion.jda.api.JDA
|
||||||
import net.dv8tion.jda.api.entities.MessageEmbed
|
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.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
|
|
||||||
|
|
||||||
object Ping : GlobalCommand {
|
object Ping : GlobalCommand {
|
||||||
override val data: CommandData = useCommand {
|
override val data: CommandData = CommandData.fromData(CommandDataImpl(
|
||||||
name = "ping"
|
"ping",
|
||||||
description = "Discord API 레이턴시 확인 합니다"
|
"Discord API 레이턴시 확인 합니다"
|
||||||
}
|
).toData())
|
||||||
|
|
||||||
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
override suspend fun execute(ev: SlashCommandInteractionEvent) {
|
||||||
val started = System.currentTimeMillis()
|
val started = System.currentTimeMillis()
|
||||||
|
@ -27,18 +25,17 @@ object Ping : GlobalCommand {
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
ev.replyEmbeds(embed).queue {
|
ev.replyEmbeds(embed).queue {
|
||||||
embed = measure(started, ev.user, ev.jda)
|
embed = measure(started, ev.jda)
|
||||||
it.editOriginalEmbeds(embed).queue()
|
it.editOriginalEmbeds(embed).queue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun measure(started: Long, user: User, jda: JDA): MessageEmbed {
|
private fun measure(started: Long, jda: JDA): MessageEmbed {
|
||||||
val embed = EmbedBuilder()
|
val embed = EmbedBuilder()
|
||||||
embed.setTitle(":ping_pong: **Pong!**")
|
embed.setTitle(":ping_pong: **Pong!**")
|
||||||
embed.addField(":electric_plug: **API**", "`${jda.gatewayPing}ms`", true)
|
embed.addField(":electric_plug: **API**", "`${jda.gatewayPing}ms`", true)
|
||||||
embed.addField(":robot: **BOT**", "`${System.currentTimeMillis() - started}ms`", true)
|
embed.addField(":robot: **BOT**", "`${System.currentTimeMillis() - started}ms`", true)
|
||||||
embed.colour()
|
embed.colour()
|
||||||
embed.footer(user)
|
|
||||||
|
|
||||||
return embed.build()
|
return embed.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,10 @@
|
||||||
package net.projecttl.p.x32.func.handler
|
package net.projecttl.p.x32.func.handler
|
||||||
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import net.dv8tion.jda.api.OnlineStatus
|
|
||||||
import net.dv8tion.jda.api.entities.Activity
|
|
||||||
import net.dv8tion.jda.api.events.session.ReadyEvent
|
import net.dv8tion.jda.api.events.session.ReadyEvent
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
import net.projecttl.p.x32.func.Conf
|
|
||||||
import net.projecttl.p.x32.func.instance
|
|
||||||
|
|
||||||
object Ready : ListenerAdapter() {
|
object Ready : ListenerAdapter() {
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
override fun onReady(ev: ReadyEvent) {
|
override fun onReady(ev: ReadyEvent) {
|
||||||
val list = listOf(
|
|
||||||
Activity.playing("${ev.jda.selfUser.name} v${Conf.version}"),
|
|
||||||
Activity.listening("${ev.guildTotalCount}개의 서버와 함께 서비스 하는 중")
|
|
||||||
)
|
|
||||||
println("Logged in as ${ev.jda.selfUser.asTag}")
|
println("Logged in as ${ev.jda.selfUser.asTag}")
|
||||||
|
|
||||||
GlobalScope.launch {
|
|
||||||
do {
|
|
||||||
list.forEach { act ->
|
|
||||||
try {
|
|
||||||
ev.jda.presence.setPresence(OnlineStatus.ONLINE, act)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
instance.logger.error(ex.message)
|
|
||||||
}
|
|
||||||
delay(1000 * 10L)
|
|
||||||
}
|
|
||||||
} while (true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "bundle-module",
|
"name": "${project.name}",
|
||||||
"version": "${rootProject.version}",
|
"version": "${project.version}",
|
||||||
"main": "net.projecttl.p.x32.func.BundleModule"
|
"main": "net.projecttl.p.x32.func.General"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ class CorePlugin extends Plugin {
|
||||||
logger.info "Hello, World!"
|
logger.info "Hello, World!"
|
||||||
CommandHandler handler = new CommandHandler()
|
CommandHandler handler = new CommandHandler()
|
||||||
handler.addCommand new Greeting()
|
handler.addCommand new Greeting()
|
||||||
|
|
||||||
addHandler handler
|
addHandler handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue