All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.freya02.botcommands.internal.core.CooldownService.kt Maven / Gradle / Ivy

package com.freya02.botcommands.internal.core

import com.freya02.botcommands.api.commands.CommandPath
import com.freya02.botcommands.api.commands.CooldownScope
import com.freya02.botcommands.api.core.service.annotations.BService
import com.freya02.botcommands.internal.BContextImpl
import com.freya02.botcommands.internal.commands.Cooldownable
import com.freya02.botcommands.internal.throwInternal
import gnu.trove.TCollections
import gnu.trove.impl.Constants
import gnu.trove.map.hash.TObjectLongHashMap
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent
import net.dv8tion.jda.api.events.message.MessageReceivedEvent

@BService
internal class CooldownService(private val context: BContextImpl) {
    @JvmRecord
    private data class CooldownKey(private val commandPath: CommandPath, private val placeId: Long?, private val userId: Long)

    //Trove sets are not *always* the fastest, by a small margin, but are by far the most memory efficient
    private val cooldowns =
        TCollections.synchronizedMap(TObjectLongHashMap(Constants.DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, 0))

    fun applyCooldown(cooldownable: Cooldownable, event: MessageReceivedEvent) =
        applyCooldown(cooldownable, event.toCooldownKey(cooldownable))

    fun applyCooldown(cooldownable: Cooldownable, event: GenericCommandInteractionEvent) =
        applyCooldown(cooldownable, event.toCooldownKey(cooldownable))

    fun getCooldown(cooldownable: Cooldownable, event: MessageReceivedEvent) = cooldowns[event.toCooldownKey(cooldownable)] - System.currentTimeMillis()

    fun getCooldown(cooldownable: Cooldownable, event: GenericCommandInteractionEvent) = cooldowns[event.toCooldownKey(cooldownable)] - System.currentTimeMillis()

    private fun applyCooldown(cooldownable: Cooldownable, cooldownKey: CooldownKey) {
        cooldowns.put(cooldownKey, System.currentTimeMillis() + cooldownable.cooldownStrategy.cooldownMillis)
        context.coroutineScopesConfig.cooldownScope.launch {
            delay(cooldownable.cooldownStrategy.cooldownMillis)
            cooldowns.remove(cooldownKey)
        }
    }

    private fun MessageReceivedEvent.toCooldownKey(cooldownable: Cooldownable): CooldownKey {
        if (!isFromGuild) throwInternal("Invalid cooldown scope for text commands")
        return when (cooldownable.cooldownStrategy.scope) {
            CooldownScope.USER -> CooldownKey(cooldownable.path, guild.idLong, author.idLong)
            CooldownScope.GUILD -> CooldownKey(cooldownable.path, guild.idLong, author.idLong)
            CooldownScope.CHANNEL -> CooldownKey(cooldownable.path, channel.idLong, author.idLong)
        }
    }

    private fun GenericCommandInteractionEvent.toCooldownKey(cooldownable: Cooldownable) = when (cooldownable.cooldownStrategy.scope) {
        CooldownScope.USER -> CooldownKey(cooldownable.path, guild?.idLong, user.idLong)
        CooldownScope.GUILD -> {
            val guild = guild ?: throwInternal("Invalid cooldown scope for text commands")
            CooldownKey(cooldownable.path, guild.idLong, user.idLong)
        }
        CooldownScope.CHANNEL -> {
            if (!isFromGuild) throwInternal("Invalid cooldown scope for text commands")
            CooldownKey(cooldownable.path, guildChannel.idLong, user.idLong)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy