From f802445ccb02c3d275dc28c146a0f1979bf4ff7d Mon Sep 17 00:00:00 2001 From: limited_dev Date: Mon, 27 Mar 2023 14:06:25 +0200 Subject: [PATCH] feat: added dynamic registering of commands & options, added embed support, added sample command --- src/main/kotlin/de/limited_dev/botendo/Bot.kt | 83 ++++++++++++++++- .../commands/slash/component/SlashCommand.kt | 35 ++++++++ .../{ => component}/SlashCommandManager.kt | 14 ++- .../options/CommandOption.kt} | 7 +- .../slash/component/options/OptionType.kt} | 11 ++- .../commands/slash/util/InfoCommand.kt | 37 ++++++++ .../limited_dev/botendo/util/MessageUtil.kt | 84 +++++++++++++++++ .../de/limited_dev/botendo/util/TimeUtil.kt | 89 +++++++++++++++++++ 8 files changed, 339 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommand.kt rename src/main/kotlin/de/limited_dev/botendo/commands/slash/{ => component}/SlashCommandManager.kt (80%) rename src/main/kotlin/de/limited_dev/botendo/commands/slash/{SlashCommand.kt => component/options/CommandOption.kt} (80%) rename src/main/kotlin/de/limited_dev/botendo/{util/SlashCommandListener.kt => commands/slash/component/options/OptionType.kt} (86%) create mode 100644 src/main/kotlin/de/limited_dev/botendo/commands/slash/util/InfoCommand.kt create mode 100644 src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt create mode 100644 src/main/kotlin/de/limited_dev/botendo/util/TimeUtil.kt diff --git a/src/main/kotlin/de/limited_dev/botendo/Bot.kt b/src/main/kotlin/de/limited_dev/botendo/Bot.kt index c1088ee..616379b 100644 --- a/src/main/kotlin/de/limited_dev/botendo/Bot.kt +++ b/src/main/kotlin/de/limited_dev/botendo/Bot.kt @@ -19,28 +19,107 @@ package de.limited_dev.botendo +import de.limited_dev.botendo.commands.slash.component.SlashCommandManager +import de.limited_dev.botendo.commands.slash.component.options.CommandOption +import de.limited_dev.botendo.commands.slash.component.options.OptionType import de.limited_dev.botendo.util.Logger import de.limited_dev.botendo.util.TokenManager import dev.kord.core.Kord +import dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent +import dev.kord.core.on import dev.kord.gateway.Intent import dev.kord.gateway.PrivilegedIntent +import dev.kord.rest.builder.interaction.boolean +import dev.kord.rest.builder.interaction.integer +import dev.kord.rest.builder.interaction.string object Bot { //The kord object gets set at app launch var kord: Kord? = null suspend fun start() { + Logger.out("Starting Bot...") + //Load config TokenManager.load() - //Don't run the bot when there is no bot token in config if (TokenManager.token == "empty") { Logger.out("The config does not contain a bot token") return } - // start the bot + //Register slash commands to bot & Discord + SlashCommandManager.register() + + //Add Bot token to kord kord = Kord(TokenManager.token) + + //Register Command Listener + kord!!.on { + val response = interaction.deferPublicResponse() + val command = interaction.command + Logger.out("Command /${command.rootName}") + for (c in SlashCommandManager.commands) { + if (c.name != command.rootName) + continue + val opt = mutableMapOf() + if (c.options != null) { + for (o in c.options) { + when (o.type) { + OptionType.INT -> + if (command.integers[o.name] != null) + opt[o] = command.integers[o.name].toString() + + OptionType.STRING -> + if (command.strings[o.name] != null) + opt[o] = command.strings[o.name].toString() + + OptionType.BOOLEAN -> + if (command.booleans[o.name] != null) + opt[o] = command.booleans[o.name].toString() + } + } + } + c.onSlashCommand(interaction, response, opt) + //response.respond { } + } + } + + //Register Slash Commands at Discord + kord!!.createGlobalApplicationCommands { + for (sc in SlashCommandManager.commands) { + this.input(sc.name, sc.description, builder = { + if (sc.options != null) { + for (o in sc.options) { + when (o.type) { + + OptionType.INT -> { + integer(o.name, o.description) { + this.required = o.required + } + } + + OptionType.STRING -> { + string(o.name, o.description) { + this.required = o.required + } + } + + OptionType.BOOLEAN -> { + boolean(o.name, o.description) { + this.required = o.required + } + } + } + } + } + }) + } + } + + Logger.out("Logging in") + + //Start the bot kord!!.login { // Load gateway privileges @OptIn(PrivilegedIntent::class) diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommand.kt new file mode 100644 index 0000000..91ca187 --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommand.kt @@ -0,0 +1,35 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.commands.slash.component + +import de.limited_dev.botendo.commands.slash.component.options.CommandOption +import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior +import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction + +abstract class SlashCommand(val name: String, val description: String, val options: List? = null) { + + open suspend fun onSlashCommand( + interaction: GuildChatInputCommandInteraction, + response: DeferredPublicMessageInteractionResponseBehavior, + args: Map + ) { + } + +} diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/SlashCommandManager.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommandManager.kt similarity index 80% rename from src/main/kotlin/de/limited_dev/botendo/commands/slash/SlashCommandManager.kt rename to src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommandManager.kt index 47a5b20..62bde2f 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/SlashCommandManager.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommandManager.kt @@ -17,17 +17,15 @@ * */ -package de.limited_dev.botendo.commands.slash +package de.limited_dev.botendo.commands.slash.component + +import de.limited_dev.botendo.commands.slash.util.InfoCommand object SlashCommandManager { + val commands = mutableListOf() + fun register() { - - } - - fun registerAtDiscord(commands: List) { - //Bot.kord.createGlobalApplicationCommands { - - //} + commands.add(InfoCommand()) } } diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/SlashCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/CommandOption.kt similarity index 80% rename from src/main/kotlin/de/limited_dev/botendo/commands/slash/SlashCommand.kt rename to src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/CommandOption.kt index 03a032f..9d816ba 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/SlashCommand.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/CommandOption.kt @@ -17,10 +17,7 @@ * */ -package de.limited_dev.botendo.commands.slash - -abstract class SlashCommand(val name: String, val description: String, val options: Array? = null) { - - fun onSlashCommand() {} +package de.limited_dev.botendo.commands.slash.component.options +data class CommandOption(val name: String, val description: String, val required: Boolean, val type: OptionType) { } diff --git a/src/main/kotlin/de/limited_dev/botendo/util/SlashCommandListener.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/OptionType.kt similarity index 86% rename from src/main/kotlin/de/limited_dev/botendo/util/SlashCommandListener.kt rename to src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/OptionType.kt index aa65cbf..bce7067 100644 --- a/src/main/kotlin/de/limited_dev/botendo/util/SlashCommandListener.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/OptionType.kt @@ -17,11 +17,10 @@ * */ -package de.limited_dev.botendo.util +package de.limited_dev.botendo.commands.slash.component.options -object SlashCommandListener { - - fun check() { - - } +enum class OptionType { + INT, + STRING, + BOOLEAN } diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/util/InfoCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/util/InfoCommand.kt new file mode 100644 index 0000000..fb1c34f --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/util/InfoCommand.kt @@ -0,0 +1,37 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.commands.slash.util + +import de.limited_dev.botendo.build.BuildConstants +import de.limited_dev.botendo.commands.slash.component.SlashCommand +import de.limited_dev.botendo.commands.slash.component.options.CommandOption +import de.limited_dev.botendo.util.MessageUtil +import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior +import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction + +class InfoCommand : SlashCommand("info", "Shows infos about the bot", null) { + override suspend fun onSlashCommand( + interaction: GuildChatInputCommandInteraction, + response: DeferredPublicMessageInteractionResponseBehavior, + args: Map + ) { + MessageUtil.sendEmbedForInteraction(interaction, response, "Botendo v5", "ver." + BuildConstants.version) + } +} diff --git a/src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt b/src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt new file mode 100644 index 0000000..92313f6 --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt @@ -0,0 +1,84 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.util + +import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior +import dev.kord.core.behavior.interaction.response.respond +import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction +import dev.kord.rest.builder.message.EmbedBuilder +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +object MessageUtil { + private val dtf: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy @ HH:mm:ss") + + suspend fun sendEmbedForInteraction( + interaction: GuildChatInputCommandInteraction, + response: DeferredPublicMessageInteractionResponseBehavior, + title: String, + description: String + ) { + response.respond { + embeds = mutableListOf( + getEmbed( + title, + description, + interaction.user.username + "#" + interaction.user.discriminator + ) + ) + } + } + + suspend fun sendEmbedForInteractionWithImage( + interaction: GuildChatInputCommandInteraction, + response: DeferredPublicMessageInteractionResponseBehavior, + title: String, + description: String, + thumbnailUrl: String + ) { + response.respond { + embeds = mutableListOf( + getEmbedWithImage( + title, + description, + interaction.user.username + "#" + interaction.user.discriminator, + thumbnailUrl + ) + ) + } + } + + fun getEmbed(title: String, description: String, source: String): EmbedBuilder { + val now: LocalDateTime = LocalDateTime.now() + val ebb = EmbedBuilder() + ebb.title = title + ebb.description = description + ebb.footer = EmbedBuilder.Footer() + ebb.footer!!.text = ">" + dtf.format(now) + " - $source" + return ebb + } + + fun getEmbedWithImage(title: String, description: String, source: String, thumbnailUrl: String): EmbedBuilder { + val ebb = getEmbed(title, description, source) + ebb.thumbnail = EmbedBuilder.Thumbnail() + ebb.thumbnail!!.url = thumbnailUrl + return ebb + } +} diff --git a/src/main/kotlin/de/limited_dev/botendo/util/TimeUtil.kt b/src/main/kotlin/de/limited_dev/botendo/util/TimeUtil.kt new file mode 100644 index 0000000..7365f16 --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/util/TimeUtil.kt @@ -0,0 +1,89 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.util + +import java.util.concurrent.TimeUnit + +object TimeUtil { + fun getTimeFormatedShortend(time: Long): String? { + var time = time + val days = TimeUnit.MILLISECONDS + .toDays(time) + time -= TimeUnit.DAYS.toMillis(days) + val hours = TimeUnit.MILLISECONDS + .toHours(time) + time -= TimeUnit.HOURS.toMillis(hours) + val minutes = TimeUnit.MILLISECONDS + .toMinutes(time) + time -= TimeUnit.MINUTES.toMillis(minutes) + val seconds = TimeUnit.MILLISECONDS + .toSeconds(time) + var s = "" + if (days >= 1) { + s += days.toString() + "d " + } + if (hours >= 1) { + s += hours.toString() + "h " + } + if (minutes >= 1) { + s += minutes.toString() + "m " + } + if (seconds >= 1 && hours < 1) { + s += seconds.toString() + "s" + } + if (s.isEmpty() || s.isBlank()) { + s = "0s" + } + return s + } + + fun getTimeFormatedRaw(time: Long): String? { + var time = time + val days = TimeUnit.MILLISECONDS + .toDays(time) + time -= TimeUnit.DAYS.toMillis(days) + val hours = TimeUnit.MILLISECONDS + .toHours(time) + time -= TimeUnit.HOURS.toMillis(hours) + val minutes = TimeUnit.MILLISECONDS + .toMinutes(time) + time -= TimeUnit.MINUTES.toMillis(minutes) + val seconds = TimeUnit.MILLISECONDS + .toSeconds(time) + var s = "" + if (days >= 1) { + s += days.toString() + "d " + } + if (hours >= 1) { + s += hours.toString() + "h " + } + if (minutes >= 1) { + s += minutes.toString() + "m " + } + if (seconds >= 1) { + s += seconds.toString() + "s" + } + if (s.isEmpty() || s.isBlank()) { + s = "0s" + } + return s + } + +}