Merge pull request #1 from devproje/module

Module
This commit is contained in:
Project_IO 2024-09-19 00:52:09 +09:00 committed by GitHub
commit dfd1934b5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 344 additions and 278 deletions

View file

@ -1,45 +0,0 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
.fleet/
.kotlin/
.env
!.env.example
### IntelliJ IDEA ###
.idea/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View file

@ -1,17 +0,0 @@
FROM amazoncorretto:21-alpine3.20
ARG BOT_TOKEN
ENV BOT_TOKEN=${BOT_TOKEN}
WORKDIR /opt/bot/build
COPY . .
RUN chmod a+x ./gradlew
RUN ./gradlew shadowJar
RUN cp ./build/libs/px32-bot.jar ../
WORKDIR /opt/bot
RUN rm -rf ./build
RUN export BOT_TOKEN=${BOT_TOKEN}
ENTRYPOINT ["java", "-Xmx4096M", "-jar", "/opt/bot/px32-bot.jar"]

View file

@ -1,14 +1,11 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
id("java") id("java")
kotlin("jvm") version "2.0.20" kotlin("jvm") version "2.0.20"
id("com.gradleup.shadow") version "8.3.0" id("com.gradleup.shadow") version "8.3.0"
} }
group = "net.wh64" group = property("group")!!
version = "1.0-SNAPSHOT" version = property("version")!!
val ktor_version: String by project val ktor_version: String by project
val log4j_version: String by project val log4j_version: String by project
@ -39,32 +36,12 @@ dependencies {
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version") implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
implementation("org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version") implementation("org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version")
implementation("io.ktor:ktor-client-okhttp-jvm:2.3.12") implementation("io.ktor:ktor-client-okhttp-jvm:2.3.12")
// testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation(platform("org.junit:junit-bom:5.10.0"))
// testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.junit.jupiter:junit-jupiter")
} }
tasks { tasks {
test { test {
useJUnitPlatform() useJUnitPlatform()
} }
withType<JavaCompile> {
options.encoding = "UTF-8"
}
withType<KotlinCompile> {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
shadowJar {
archiveBaseName.set(project.name)
archiveClassifier.set("")
archiveVersion.set("")
manifest {
attributes["Main-Class"] = "net.projecttl.p.x32.Px32"
}
}
} }

View file

@ -1,17 +0,0 @@
services:
bot:
container_name: "P_x32-bot"
build:
context: .
dockerfile: "./Dockerfile"
env_file:
- ".env"
environment:
- BOT_TOKEN=${BOT_TOKEN}
networks:
- "bot"
volumes:
- "/etc/localtime:/etc/localtime:ro"
networks:
bot: {}

View file

@ -1,3 +1,9 @@
kotlin.code.style=official
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8
group=net.projecttl
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
exposed_version=0.54.0 exposed_version=0.54.0

View file

@ -1,2 +1 @@
rootProject.name = "px32-bot" rootProject.name = "px32-bot"

View file

@ -1,47 +0,0 @@
package net.projecttl.p.x32;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.projecttl.p.x32.command.Ping;
import net.projecttl.p.x32.handler.CommandExecutor;
import net.projecttl.p.x32.handler.CommandHandler;
import net.projecttl.p.x32.handler.Ready;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
public class Px32 {
public static final Logger log = LoggerFactory.getLogger(Px32.class);
private JDA jda;
private final ArrayList<CommandExecutor> commands = new ArrayList<>();
private void register() {
commands.forEach(command -> {
jda.upsertCommand(command.getData()).queue();
jda.updateCommands().addCommands(
Commands.context(Command.Type.USER, command.getData().getName())
).queue();
log.info("registered command: {}", command.getData().getName());
});
}
public static void main(String[] args) {
Px32 core = new Px32();
JDABuilder builder = JDABuilder.createDefault(System.getenv("BOT_TOKEN"));
core.commands.add(new Ping());
CommandHandler handler = new CommandHandler(core.commands);
builder.addEventListeners(
handler,
new Ready()
);
builder.setAutoReconnect(true);
core.jda = builder.build();
core.register();
}
}

View file

@ -1,45 +0,0 @@
package net.projecttl.p.x32.command;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.internal.interactions.CommandDataImpl;
import net.projecttl.p.x32.handler.CommandExecutor;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import static java.lang.String.format;
public class Ping implements CommandExecutor {
@NotNull
@Override
public CommandData getData() {
return CommandData.fromData(new CommandDataImpl(
"ping",
"Discord API 레이턴시를 확인 합니다."
).toData());
}
@Override
public void execute(SlashCommandInteractionEvent ev) {
long current = System.currentTimeMillis();
AtomicReference<MessageEmbed> embed = new AtomicReference<>(new EmbedBuilder()
.setDescription(":hourglass: Just wait a seconds...")
.build());
ev.replyEmbeds(embed.get()).queue(hook -> {
Random r = new Random();
embed.set(new EmbedBuilder()
.setTitle(":ping_pong: Pong!")
.addField("\uD83E\uDD16", format("**%d**ms", System.currentTimeMillis() - current), true)
.addField("\uD83D\uDD0C", format("**%d**ms", ev.getJDA().getGatewayPing()), true)
.setColor(r.nextInt(0x000001, 0xffffff))
.build());
hook.editOriginalEmbeds(embed.get()).queue();
});
}
}

View file

@ -1,42 +0,0 @@
package net.projecttl.p.x32.handler;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.projecttl.p.x32.Px32;
import java.util.List;
public class CommandHandler extends ListenerAdapter {
private final List<CommandExecutor> commands;
public CommandHandler(List<CommandExecutor> commands) {
this.commands = commands;
}
@Override
public void onSlashCommandInteraction(SlashCommandInteractionEvent ev) {
if (ev.getUser().isBot()) {
return;
}
for (CommandExecutor command : commands) {
if (!command.getData().getName().equals(ev.getName())) {
continue;
}
try {
command.execute(ev);
Px32.log.info("user {} execute command: {}", ev.getUser().getId(), ev.getName());
} catch (Exception ex) {
Px32.log.error("user {} execute command {} failed", ev.getUser().getId(), ev.getName(), ex);
}
break;
}
}
@Override
public void onUserContextInteraction(UserContextInteractionEvent ev) {
Px32.log.info("user {} execute context: {}", ev.getUser().getId(), ev.getName());
}
}

View file

@ -1,15 +0,0 @@
package net.projecttl.p.x32.handler;
import net.dv8tion.jda.api.events.session.ReadyEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Ready extends ListenerAdapter {
private static final Logger log = LoggerFactory.getLogger(Ready.class);
@Override
public void onReady(ReadyEvent ev) {
log.info("Logged in as {}", ev.getJDA().getSelfUser().getAsTag());
}
}

View file

@ -1,4 +0,0 @@
package net.projecttl.p.x32
class Database {
}

View file

@ -0,0 +1,16 @@
package net.projecttl.p.x32
import java.util.*
import kotlin.reflect.KProperty
private class DefaultConfigDelegate {
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()
}
}

View file

@ -0,0 +1,23 @@
package net.projecttl.p.x32
import net.projecttl.p.x32.command.Avatar
import net.projecttl.p.x32.command.Ping
import net.projecttl.p.x32.handler.Ready
import net.projecttl.p.x32.kernel.CoreKernel
import org.slf4j.Logger
import org.slf4j.LoggerFactory
val logger: Logger = LoggerFactory.getLogger(Px32::class.java)
fun main() {
val kernel = CoreKernel(System.getenv("TOKEN"))
kernel.addHandler(Ready)
val handler = kernel.getGlobalCommandHandler()
handler.addCommand(Avatar)
handler.addCommand(Ping)
kernel.build()
}
class Px32

View file

@ -0,0 +1,21 @@
package net.projecttl.p.x32.command
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.internal.interactions.CommandDataImpl
import net.projecttl.p.x32.handler.UserContext
import net.projecttl.p.x32.util.colour
object Avatar : UserContext {
override val data = CommandData.fromData(CommandDataImpl("avatar", "유저의 프로필 이미지를 가져 옵니다").toData())
override suspend fun execute(ev: UserContextInteractionEvent) {
val embed = EmbedBuilder()
embed.setTitle(":frame_photo: ${ev.name} Avatar")
embed.setImage(ev.target.effectiveAvatarUrl)
embed.colour()
ev.replyEmbeds(embed.build()).queue()
}
}

View file

@ -0,0 +1,32 @@
package net.projecttl.p.x32.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.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.handler.GlobalCommand
import net.projecttl.p.x32.util.colour
import kotlin.random.Random
object Ping : GlobalCommand {
override val data: CommandData = CommandData.fromData(CommandDataImpl(
"ping",
"Discord API 레이턴시 확인 합니다"
).toData())
override suspend fun execute(ev: SlashCommandInteractionEvent) {
val embed = measure(ev.jda)
ev.replyEmbeds(embed).queue()
}
private fun measure(jda: JDA): MessageEmbed {
val embed = EmbedBuilder()
embed.setTitle(":ping_pong: **Pong!**")
embed.addField(":electric_plug: **API**", "`${jda.gatewayPing}ms`", true)
embed.colour()
return embed.build()
}
}

View file

@ -0,0 +1,7 @@
package net.projecttl.p.x32.config
object Config {
}
class ConfigDelegate {
}

View file

@ -0,0 +1,24 @@
package net.projecttl.p.x32.config
import java.util.*
import kotlin.reflect.KProperty
object DefaultConfig {
private fun useConfig(): DefaultConfigDelegate {
return DefaultConfigDelegate()
}
val version: String by useConfig()
}
private class DefaultConfigDelegate {
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()
}
}

View file

@ -1,9 +1,25 @@
package net.projecttl.p.x32.handler package net.projecttl.p.x32.handler
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
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent
import net.dv8tion.jda.api.interactions.commands.build.CommandData import net.dv8tion.jda.api.interactions.commands.build.CommandData
interface CommandExecutor { interface CommandExecutor {
val data: CommandData val data: CommandData
fun execute(ev: SlashCommandInteractionEvent) }
interface GlobalCommand : CommandExecutor {
override val data: CommandData
suspend fun execute(ev: SlashCommandInteractionEvent)
}
interface UserContext : CommandExecutor {
override val data: CommandData
suspend fun execute(ev: UserContextInteractionEvent)
}
interface MessageContext : CommandExecutor {
override val data: CommandData
suspend fun execute(ev: MessageContextInteractionEvent)
} }

View file

@ -0,0 +1,130 @@
package net.projecttl.p.x32.handler
import kotlinx.coroutines.runBlocking
import net.dv8tion.jda.api.JDA
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.UserContextInteractionEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import net.dv8tion.jda.api.interactions.commands.Command
import net.dv8tion.jda.api.interactions.commands.build.Commands
import net.projecttl.p.x32.logger
class CommandHandler(val guildId: Long = 0L) : ListenerAdapter() {
private val commands = mutableListOf<CommandExecutor>()
override fun onSlashCommandInteraction(ev: SlashCommandInteractionEvent) {
val name = ev.interaction.name
commands.forEach { command ->
if (command.data.name == name) {
if (command is GlobalCommand) {
runBlocking {
command.execute(ev)
}
return@forEach
}
}
}
}
override fun onUserContextInteraction(ev: UserContextInteractionEvent) {
val name = ev.interaction.name
commands.forEach { command ->
if (command.data.name == name) {
if (command is UserContext) {
runBlocking {
command.execute(ev)
}
return@forEach
}
}
}
}
override fun onMessageContextInteraction(ev: MessageContextInteractionEvent) {
val name = ev.interaction.name
commands.forEach { command ->
if (command.data.name == name) {
if (command is MessageContext) {
runBlocking {
command.execute(ev)
}
return@forEach
}
}
}
}
fun addCommand(command: CommandExecutor) {
commands.add(command)
}
fun delCommand(command: CommandExecutor) {
commands.remove(command)
}
fun register(jda: JDA) {
val guild = jda.getGuildById(guildId)
if (guildId != 0L) {
if (guild == null) {
logger.info("'${guildId}' guild is not exists!")
return
}
}
commands.forEach { command ->
val data = command.data
if (command is GlobalCommand) {
if (guild == null) {
jda.upsertCommand(data).queue()
logger.info("Register Global Command: /${data.name}")
} else {
guild.upsertCommand(data).queue()
logger.info("Register '${guild.id}' Guild's Command: /${data.name}")
}
}
if (command is UserContext) {
if (guild == null) {
jda.updateCommands().addCommands(
Commands.context(Command.Type.USER, data.name),
Commands.message(data.name)
).queue()
logger.info("Register User Context Command: /${data.name}")
} else {
guild.updateCommands().addCommands(
Commands.context(Command.Type.USER, data.name),
Commands.message(data.name)
).queue()
logger.info("Register '${guild.id}' Guild's User Context Command: /${data.name}")
}
}
if (command is MessageContext) {
if (guild == null) {
jda.updateCommands().addCommands(
Commands.context(Command.Type.MESSAGE, data.name),
Commands.message(data.name)
)
logger.info("Register Message Context Command: /${data.name}")
} else {
guild.updateCommands().addCommands(
Commands.context(Command.Type.MESSAGE, data.name),
Commands.message(data.name)
)
logger.info("Register '${guild.id}' Guild's Message Context Command: /${data.name}")
}
}
}
}
}

View file

@ -0,0 +1,11 @@
package net.projecttl.p.x32.handler
import net.dv8tion.jda.api.events.session.ReadyEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import net.projecttl.p.x32.logger
object Ready : ListenerAdapter() {
override fun onReady(ev: ReadyEvent) {
logger.info("Logged in as ${ev.jda.selfUser.asTag}")
}
}

View file

@ -0,0 +1,42 @@
package net.projecttl.p.x32.kernel
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.hooks.ListenerAdapter
import net.projecttl.p.x32.handler.CommandHandler
class CoreKernel(token: String) {
private val builder = JDABuilder.createDefault(token)
private val handlers = mutableListOf<ListenerAdapter>()
private val handler = CommandHandler()
fun getGlobalCommandHandler(): CommandHandler {
return handler
}
fun addHandler(handler: ListenerAdapter) {
handlers.add(handler)
}
fun delHandler(handler: ListenerAdapter) {
handlers.remove(handler)
}
fun build(): JDA {
handlers.map {
builder.addEventListeners(it)
}
builder.addEventListeners(handler)
val jda = builder.build()
handler.register(jda)
handlers.forEach { h ->
if (h is CommandHandler) {
h.register(jda)
}
}
return jda
}
}

View file

@ -1,16 +0,0 @@
package net.projecttl.p.x32.service
import org.jetbrains.exposed.sql.transactions.transaction
interface ServiceProvider<P, T> {
fun <T> dbQuery(block: () -> T): T =
transaction { block() }
fun create(data: T)
fun read(id: P): T?
fun update(id: P, data: T) {}
fun delete(id: P) {}
}

View file

@ -0,0 +1,9 @@
package net.projecttl.p.x32.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)
}

View file

@ -0,0 +1 @@
version=${version}