diff --git a/.dockerfile b/.dockerfile deleted file mode 100644 index a0edd36..0000000 --- a/.dockerfile +++ /dev/null @@ -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 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 3afbdd6..0000000 --- a/Dockerfile +++ /dev/null @@ -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"] diff --git a/build.gradle.kts b/build.gradle.kts index 016d403..3b861ba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,11 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { id("java") kotlin("jvm") version "2.0.20" id("com.gradleup.shadow") version "8.3.0" } -group = "net.wh64" -version = "1.0-SNAPSHOT" +group = property("group")!! +version = property("version")!! val ktor_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.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version") implementation("io.ktor:ktor-client-okhttp-jvm:2.3.12") -// testImplementation(platform("org.junit:junit-bom:5.10.0")) -// testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") } tasks { test { useJUnitPlatform() } - - withType { - options.encoding = "UTF-8" - } - - withType { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_21) - } - } - - shadowJar { - archiveBaseName.set(project.name) - archiveClassifier.set("") - archiveVersion.set("") - - manifest { - attributes["Main-Class"] = "net.projecttl.p.x32.Px32" - } - } } diff --git a/compose.yml b/compose.yml deleted file mode 100644 index 1a035ff..0000000 --- a/compose.yml +++ /dev/null @@ -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: {} diff --git a/gradle.properties b/gradle.properties index b3b29dc..787bb7d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 log4j_version=2.23.1 exposed_version=0.54.0 diff --git a/settings.gradle.kts b/settings.gradle.kts index eb6c344..7d276bd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,2 +1 @@ rootProject.name = "px32-bot" - diff --git a/src/main/java/net/projecttl/p/x32/Px32.java b/src/main/java/net/projecttl/p/x32/Px32.java deleted file mode 100644 index 555995d..0000000 --- a/src/main/java/net/projecttl/p/x32/Px32.java +++ /dev/null @@ -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 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(); - } -} diff --git a/src/main/java/net/projecttl/p/x32/command/Ping.java b/src/main/java/net/projecttl/p/x32/command/Ping.java deleted file mode 100644 index 6fe25e2..0000000 --- a/src/main/java/net/projecttl/p/x32/command/Ping.java +++ /dev/null @@ -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 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(); - }); - } -} diff --git a/src/main/java/net/projecttl/p/x32/handler/CommandHandler.java b/src/main/java/net/projecttl/p/x32/handler/CommandHandler.java deleted file mode 100644 index aab83c7..0000000 --- a/src/main/java/net/projecttl/p/x32/handler/CommandHandler.java +++ /dev/null @@ -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 commands; - - public CommandHandler(List 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()); - } -} diff --git a/src/main/java/net/projecttl/p/x32/handler/Ready.java b/src/main/java/net/projecttl/p/x32/handler/Ready.java deleted file mode 100644 index 2d2cc2c..0000000 --- a/src/main/java/net/projecttl/p/x32/handler/Ready.java +++ /dev/null @@ -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()); - } -} diff --git a/src/main/kotlin/net/projecttl/p/x32/Database.kt b/src/main/kotlin/net/projecttl/p/x32/Database.kt deleted file mode 100644 index 9cff878..0000000 --- a/src/main/kotlin/net/projecttl/p/x32/Database.kt +++ /dev/null @@ -1,4 +0,0 @@ -package net.projecttl.p.x32 - -class Database { -} diff --git a/src/main/kotlin/net/projecttl/p/x32/DefaultConfig.kt b/src/main/kotlin/net/projecttl/p/x32/DefaultConfig.kt new file mode 100644 index 0000000..99ec715 --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/DefaultConfig.kt @@ -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() + } +} diff --git a/src/main/kotlin/net/projecttl/p/x32/Px32.kt b/src/main/kotlin/net/projecttl/p/x32/Px32.kt new file mode 100644 index 0000000..0316b9d --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/Px32.kt @@ -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 diff --git a/src/main/kotlin/net/projecttl/p/x32/command/Avatar.kt b/src/main/kotlin/net/projecttl/p/x32/command/Avatar.kt new file mode 100644 index 0000000..621438a --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/command/Avatar.kt @@ -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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/projecttl/p/x32/command/Ping.kt b/src/main/kotlin/net/projecttl/p/x32/command/Ping.kt new file mode 100644 index 0000000..cdd75e2 --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/command/Ping.kt @@ -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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/projecttl/p/x32/config/Config.kt b/src/main/kotlin/net/projecttl/p/x32/config/Config.kt new file mode 100644 index 0000000..6565d54 --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/config/Config.kt @@ -0,0 +1,7 @@ +package net.projecttl.p.x32.config + +object Config { +} + +class ConfigDelegate { +} \ No newline at end of file diff --git a/src/main/kotlin/net/projecttl/p/x32/config/DefaultConfig.kt b/src/main/kotlin/net/projecttl/p/x32/config/DefaultConfig.kt new file mode 100644 index 0000000..3b19c27 --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/config/DefaultConfig.kt @@ -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() + } +} diff --git a/src/main/kotlin/net/projecttl/p/x32/handler/CommandExecutor.kt b/src/main/kotlin/net/projecttl/p/x32/handler/CommandExecutor.kt index 2403160..b9de5e8 100644 --- a/src/main/kotlin/net/projecttl/p/x32/handler/CommandExecutor.kt +++ b/src/main/kotlin/net/projecttl/p/x32/handler/CommandExecutor.kt @@ -1,9 +1,25 @@ 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.UserContextInteractionEvent import net.dv8tion.jda.api.interactions.commands.build.CommandData interface CommandExecutor { val data: CommandData - fun execute(ev: SlashCommandInteractionEvent) -} \ No newline at end of file +} + +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) +} diff --git a/src/main/kotlin/net/projecttl/p/x32/handler/CommandHandler.kt b/src/main/kotlin/net/projecttl/p/x32/handler/CommandHandler.kt new file mode 100644 index 0000000..51dd23d --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/handler/CommandHandler.kt @@ -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() + + 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}") + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/projecttl/p/x32/handler/Ready.kt b/src/main/kotlin/net/projecttl/p/x32/handler/Ready.kt new file mode 100644 index 0000000..33d7699 --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/handler/Ready.kt @@ -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}") + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/projecttl/p/x32/kernel/CoreKernel.kt b/src/main/kotlin/net/projecttl/p/x32/kernel/CoreKernel.kt new file mode 100644 index 0000000..8e4a18d --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/kernel/CoreKernel.kt @@ -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() + + 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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/projecttl/p/x32/service/ServiceProvider.kt b/src/main/kotlin/net/projecttl/p/x32/service/ServiceProvider.kt deleted file mode 100644 index c631815..0000000 --- a/src/main/kotlin/net/projecttl/p/x32/service/ServiceProvider.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.projecttl.p.x32.service - -import org.jetbrains.exposed.sql.transactions.transaction - -interface ServiceProvider { - fun 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) {} -} \ No newline at end of file diff --git a/src/main/kotlin/net/projecttl/p/x32/util/Color.kt b/src/main/kotlin/net/projecttl/p/x32/util/Color.kt new file mode 100644 index 0000000..537821a --- /dev/null +++ b/src/main/kotlin/net/projecttl/p/x32/util/Color.kt @@ -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) +} diff --git a/src/main/resources/default.properties b/src/main/resources/default.properties new file mode 100644 index 0000000..a50bf5c --- /dev/null +++ b/src/main/resources/default.properties @@ -0,0 +1 @@ +version=${version}