Skip to content

Commit

Permalink
Merge pull request #9 from Goksi/cooldowns
Browse files Browse the repository at this point in the history
Implementing button cooldowns
  • Loading branch information
Goksi authored Nov 30, 2022
2 parents 04a5b22 + 2cbdb29 commit 30626f5
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/main/kotlin/tech/goksi/pterobot/PteroBot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class PteroBot {
private val logger by SLF4J
private val jda: JDA

/*TODO: DRY*/
init {
val tokenPair = Checks.checkInput(
ConfigManager.config.getString("BotInfo.Token"),
Expand Down
25 changes: 15 additions & 10 deletions src/main/kotlin/tech/goksi/pterobot/commands/Servers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import com.mattmalec.pterodactyl4j.exceptions.ServerException
import dev.minn.jda.ktx.events.listener
import dev.minn.jda.ktx.interactions.components.Modal
import dev.minn.jda.ktx.interactions.components.SelectMenu
import dev.minn.jda.ktx.interactions.components.button
import dev.minn.jda.ktx.interactions.components.option
import dev.minn.jda.ktx.util.SLF4J
import net.dv8tion.jda.api.JDA
Expand All @@ -22,6 +21,8 @@ import tech.goksi.pterobot.entities.ServerInfo
import tech.goksi.pterobot.manager.ConfigManager
import tech.goksi.pterobot.manager.EmbedManager
import tech.goksi.pterobot.manager.EmbedManager.toEmbed
import tech.goksi.pterobot.util.cooldown.CooldownManager.cooldownButton
import tech.goksi.pterobot.util.cooldown.CooldownType
import kotlin.time.Duration.Companion.minutes

private const val CONFIG_PREFIX = "Messages.Commands.Servers."
Expand Down Expand Up @@ -141,11 +142,12 @@ class Servers(jda: JDA) : SimpleCommand() {

/*START OR STOP BTN*/
val changeStateButton = when (serverInfo.status) {
"RUNNING", "STARTING" -> event.jda.button(
"RUNNING", "STARTING" -> event.jda.cooldownButton(
style = ButtonStyle.valueOf(getButtonSetting("StopType")),
user = event.user,
label = getButtonSetting("Stop"),
emoji = Emoji.fromUnicode(getButtonSetting("StopEmoji"))
emoji = Emoji.fromUnicode(getButtonSetting("StopEmoji")),
type = CooldownType.STATUS_BTN
) { buttonEvent ->
server.setPower(PowerAction.STOP).executeAsync({
buttonEvent.hook.sendMessageEmbeds(
Expand All @@ -167,11 +169,12 @@ class Servers(jda: JDA) : SimpleCommand() {
}
}

else -> event.jda.button(
else -> event.jda.cooldownButton(
style = ButtonStyle.valueOf(getButtonSetting("StartType")),
user = event.user,
label = getButtonSetting("Start"),
emoji = Emoji.fromUnicode(getButtonSetting("StartEmoji"))
emoji = Emoji.fromUnicode(getButtonSetting("StartEmoji")),
type = CooldownType.STATUS_BTN
) { buttonEvent ->
server.setPower(PowerAction.START).executeAsync({
buttonEvent.hook.sendMessageEmbeds(
Expand All @@ -195,11 +198,12 @@ class Servers(jda: JDA) : SimpleCommand() {
}

/*RESTART BTN*/
val restartButton = event.jda.button(
val restartButton = event.jda.cooldownButton(
style = ButtonStyle.valueOf(getButtonSetting("RestartType")),
user = event.user,
label = getButtonSetting("Restart"),
emoji = Emoji.fromUnicode(getButtonSetting("RestartEmoji"))
emoji = Emoji.fromUnicode(getButtonSetting("RestartEmoji")),
type = CooldownType.RESTART_BTN
) { buttonEvent ->
server.setPower(PowerAction.RESTART).executeAsync({
buttonEvent.hook.sendMessageEmbeds(
Expand All @@ -222,11 +226,12 @@ class Servers(jda: JDA) : SimpleCommand() {
}

/*COMMAND BTN*/
val commandButton = event.jda.button(
val commandButton = event.jda.cooldownButton(
style = ButtonStyle.valueOf(getButtonSetting("CommandType")),
user = event.user,
label = getButtonSetting("Command"),
emoji = Emoji.fromUnicode(getButtonSetting("CommandEmoji"))
emoji = Emoji.fromUnicode(getButtonSetting("CommandEmoji")),
type = CooldownType.COMMAND_BTN
) { buttonEvent ->
val commandModal = Modal(
id = "pterobot:command:${server.identifier}",
Expand All @@ -244,7 +249,7 @@ class Servers(jda: JDA) : SimpleCommand() {
}

/*CLOSE BTN*/
val closeButton = event.jda.button(
val closeButton = event.jda.cooldownButton(
style = ButtonStyle.valueOf(getButtonSetting("CloseType")),
user = event.user,
label = getButtonSetting("Close"),
Expand Down
11 changes: 0 additions & 11 deletions src/main/kotlin/tech/goksi/pterobot/database/impl/SQLiteImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ class SQLiteImpl : DataStorage {
Class.forName("org.sqlite.JDBC")
connection = DriverManager.getConnection("jdbc:sqlite:database.db")
val statement = connection.createStatement()
/*TODO: remove in next release*/
val migrateStatement = connection.prepareStatement(
"pragma table_info('Accounts')"
)
statement.addBatch(
"create table if not exists Members(id integer not null primary key autoincrement, discordID bigint not null unique, apiID integer, foreign key(apiID) references Keys(id) on delete set null)"
)
Expand All @@ -34,13 +30,6 @@ class SQLiteImpl : DataStorage {
)
statement.use {
try {
val resultSetMigrate = migrateStatement.executeQuery()
while (resultSetMigrate.next()) {
if (resultSetMigrate.getString("name") == "id") {
connection.prepareStatement("drop table Accounts").executeUpdate()
break
}
}
it.executeBatch()
} catch (exception: SQLException) {
logger.error("Failed to initialize SQLite database... exiting", exception)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package tech.goksi.pterobot.util.cooldown

import dev.minn.jda.ktx.events.onButton
import dev.minn.jda.ktx.interactions.components.button
import dev.minn.jda.ktx.jdabuilder.scope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.entities.User
import net.dv8tion.jda.api.entities.emoji.Emoji
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent
import net.dv8tion.jda.api.interactions.components.buttons.Button
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle
import tech.goksi.pterobot.manager.ConfigManager
import tech.goksi.pterobot.manager.EmbedManager
import tech.goksi.pterobot.manager.EmbedManager.replace
import tech.goksi.pterobot.manager.EmbedManager.toEmbed
import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

object CooldownManager {
private val cooldownMapping: MutableMap<CooldownNamespace, Long> = HashMap()

private fun applyCooldown(event: ButtonInteractionEvent) {
val cooldownType = CooldownType.fromEvent(event)
cooldownMapping[CooldownNamespace(event.user.idLong, cooldownType)] =
System.currentTimeMillis() + cooldownType.millis
}

private fun canInteract(event: ButtonInteractionEvent): Boolean = getRemaining(event) == 0L

private fun getRemaining(event: ButtonInteractionEvent): Long = max(
0,
(
cooldownMapping[CooldownNamespace(event.user.idLong, CooldownType.fromEvent(event))] ?: 0
) - System.currentTimeMillis()
)

private fun getRemainingSeconds(event: ButtonInteractionEvent): Long =
TimeUnit.MILLISECONDS.toSeconds(getRemaining(event))

fun JDA.cooldownButton(
style: ButtonStyle,
label: String? = null,
emoji: Emoji?,
disabled: Boolean = false,
expiration: Duration = 15.minutes,
user: User? = null,
type: CooldownType = CooldownType.UNDEFINED,
listener: suspend (ButtonInteractionEvent) -> Unit
): Button {
val id = type.name + ":${ThreadLocalRandom.current().nextLong()}"
val button = button(id, label, emoji, style, disabled)
val task = onButton(id, expiration) {
if (user == null || user == it.user) {
if (!canInteract(it)) {
it.replyEmbeds(
EmbedManager.getGenericFailure(
ConfigManager.config.getString("Messages.OnCooldown")
.replace("%time" to "${getRemainingSeconds(it)}")
).toEmbed(it.jda)
).setEphemeral(true).queue()
return@onButton
}
listener(it)
applyCooldown(it)
}
if (!it.isAcknowledged) it.deferEdit().queue()
}
if (expiration.isPositive() && expiration.isFinite()) {
scope.launch {
delay(expiration)
removeEventListener(task)
}
}
return button
}
}

private data class CooldownNamespace(val id: Long, val type: CooldownType)
29 changes: 29 additions & 0 deletions src/main/kotlin/tech/goksi/pterobot/util/cooldown/CooldownType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package tech.goksi.pterobot.util.cooldown

import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent
import tech.goksi.pterobot.manager.ConfigManager
import java.util.concurrent.TimeUnit

private const val BUTTON_CONFIG = "Cooldown.Button."

enum class CooldownType(coolDownConfig: String) {
STATUS_BTN("StatusChange"),
RESTART_BTN("RestartServer"),
COMMAND_BTN("SendCommand"),
REFRESH_BTN("RefreshEmbed"),
LOGS_BTN("RequestLogs"),
UNDEFINED("");

private val seconds: Long = ConfigManager.config.getLong("$BUTTON_CONFIG$coolDownConfig")
val millis
get() = TimeUnit.SECONDS.toMillis(seconds)

companion object {
fun fromEvent(event: ButtonInteractionEvent): CooldownType = valueOf(event.button.id!!.split(":")[0])

fun fromEvent(event: SlashCommandInteractionEvent): CooldownType {
TODO("Not impl")
}
}
}
8 changes: 8 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ BotInfo:
ActivityName: "Pterodactyl !"
Ephemeral: false # indicates if commands like servers should return ephemeral or normal messages (node info will always return normal one)
MaxRegisteredAccounts: 1 # indicates number of max accounts user can register with /register command (set to 0 for unlimited)
Cooldown: # those values are in seconds !
Button:
StatusChange: 3
RestartServer: 10
SendCommand: 3
RefreshEmbed: 10
RequestLogs: 10
Messages:
OnCooldown: "Sorry, this button is on cooldown for next %time seconds !"
Commands:
Link:
Description: "Connect your pterodactyl account using this command !"
Expand Down

0 comments on commit 30626f5

Please sign in to comment.