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

com.freya02.botcommands.internal.modals.ModalMaps.kt Maven / Gradle / Ivy

package com.freya02.botcommands.internal.modals

import com.freya02.botcommands.api.core.config.BConfig
import com.freya02.botcommands.api.core.service.annotations.BService
import com.freya02.botcommands.internal.throwUser
import com.freya02.botcommands.internal.utils.Utils
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent
import java.util.concurrent.ThreadLocalRandom
import kotlin.math.floor
import kotlin.math.log10
import kotlin.math.pow

private const val MAX_ID = Long.MAX_VALUE
//Same amount of digits except every digit is 0 but the first one is 1
private val MIN_ID = 10.0.pow(floor(log10(MAX_ID.toDouble()))).toLong()

@BService
internal class ModalMaps(private val config: BConfig) {
    private val modalMap: MutableMap = hashMapOf()

    //Modals input IDs are temporarily stored here while it waits for its ModalBuilder owner to be built, and it's InputData to be associated with it
    private val inputMap: MutableMap = hashMapOf()

    fun insertModal(partialModalData: PartialModalData, id: String): String {
        synchronized(modalMap) {
            if (id == "0") { //If not a user supplied ID
                return insertModal(partialModalData, nextModalId())
            }

            val job = partialModalData.timeoutInfo?.let { timeoutInfo ->
                config.coroutineScopesConfig.cooldownScope.launch {
                    delay(timeoutInfo.unit.toMillis(timeoutInfo.timeout))

                    synchronized(modalMap) {
                        val data = modalMap.remove(id)
                        if (data != null) { //If the timeout was reached without the modal being used
                            timeoutInfo.onTimeout.run()
                            for (continuation in data.continuations) {
                                continuation.cancel(Utils.createModalTimeoutException())
                            }
                        }
                    }
                }
            }

            modalMap[id] = ModalData(partialModalData, job)
        }

        return id
    }

    fun insertInput(inputData: InputData, id: String): String {
        synchronized(inputMap) {
            if (id == "0") {
                return insertInput(inputData, nextInputId())
            }

            inputMap.put(id, inputData)
        }

        return id
    }

    fun insertContinuation(id: String, continuation: CancellableContinuation) {
        val data = modalMap[id] ?: throwUser("Unable to find a modal with id '$id' ; Is the modal created with the framework ?")
        data.continuations.add(continuation)
    }

    fun removeContinuation(id: String, continuation: CancellableContinuation) {
        val data = modalMap[id]
        data?.continuations?.remove(continuation)
    }

    fun consumeModal(modalId: String): ModalData? {
        synchronized(modalMap) {
            return modalMap.remove(modalId)?.also { it.cancelTimeout() }
        }
    }

    fun consumeInput(inputId: String): InputData? {
        synchronized(inputMap) { return inputMap.remove(inputId) }
    }

    private fun nextModalId(): String {
        val random = ThreadLocalRandom.current()
        synchronized(modalMap) {
            return generateId(random, modalMap)
        }
    }

    private fun nextInputId(): String {
        val random = ThreadLocalRandom.current()
        synchronized(inputMap) {
            return generateId(random, inputMap)
        }
    }

    private fun generateId(random: ThreadLocalRandom, map: Map): String {
        while (true) {
            val id = random.nextLong(MIN_ID, MAX_ID).toString()

            if (!map.containsKey(id)) {
                return id
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy