feat: added dynamic registering of commands & options, added embed support, added sample command

This commit is contained in:
limited_dev 2023-03-27 14:06:25 +02:00
parent f821a06342
commit f802445ccb
8 changed files with 339 additions and 21 deletions

View file

@ -19,28 +19,107 @@
package de.limited_dev.botendo 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.Logger
import de.limited_dev.botendo.util.TokenManager import de.limited_dev.botendo.util.TokenManager
import dev.kord.core.Kord 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.Intent
import dev.kord.gateway.PrivilegedIntent 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 { object Bot {
//The kord object gets set at app launch //The kord object gets set at app launch
var kord: Kord? = null var kord: Kord? = null
suspend fun start() { suspend fun start() {
Logger.out("Starting Bot...")
//Load config //Load config
TokenManager.load() TokenManager.load()
//Don't run the bot when there is no bot token in config //Don't run the bot when there is no bot token in config
if (TokenManager.token == "empty") { if (TokenManager.token == "empty") {
Logger.out("The config does not contain a bot token") Logger.out("The config does not contain a bot token")
return return
} }
// start the bot //Register slash commands to bot & Discord
SlashCommandManager.register()
//Add Bot token to kord
kord = Kord(TokenManager.token) kord = Kord(TokenManager.token)
//Register Command Listener
kord!!.on<GuildChatInputCommandInteractionCreateEvent> {
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<CommandOption, String>()
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 { kord!!.login {
// Load gateway privileges // Load gateway privileges
@OptIn(PrivilegedIntent::class) @OptIn(PrivilegedIntent::class)

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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<CommandOption>? = null) {
open suspend fun onSlashCommand(
interaction: GuildChatInputCommandInteraction,
response: DeferredPublicMessageInteractionResponseBehavior,
args: Map<CommandOption, String>
) {
}
}

View file

@ -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 { object SlashCommandManager {
val commands = mutableListOf<SlashCommand>()
fun register() { fun register() {
commands.add(InfoCommand())
}
fun registerAtDiscord(commands: List<SlashCommand>) {
//Bot.kord.createGlobalApplicationCommands {
//}
} }
} }

View file

@ -17,10 +17,7 @@
* *
*/ */
package de.limited_dev.botendo.commands.slash package de.limited_dev.botendo.commands.slash.component.options
abstract class SlashCommand(val name: String, val description: String, val options: Array<String>? = null) {
fun onSlashCommand() {}
data class CommandOption(val name: String, val description: String, val required: Boolean, val type: OptionType) {
} }

View file

@ -17,11 +17,10 @@
* *
*/ */
package de.limited_dev.botendo.util package de.limited_dev.botendo.commands.slash.component.options
object SlashCommandListener { enum class OptionType {
INT,
fun check() { STRING,
BOOLEAN
}
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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<CommandOption, String>
) {
MessageUtil.sendEmbedForInteraction(interaction, response, "Botendo v5", "ver." + BuildConstants.version)
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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
}
}