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

io.github.freya022.botcommands.internal.commands.application.ApplicationCommandsCache.kt Maven / Gradle / Ivy

Go to download

A Kotlin-first (and Java) framework that makes creating Discord bots a piece of cake, using the JDA library.

There is a newer version: 3.0.0-alpha.18
Show newest version
package io.github.freya022.botcommands.internal.commands.application

import io.github.freya022.botcommands.api.core.service.ServiceContainer
import io.github.freya022.botcommands.api.core.service.ServiceStart
import io.github.freya022.botcommands.api.core.service.annotations.BService
import io.github.freya022.botcommands.api.core.service.getService
import io.github.freya022.botcommands.api.core.utils.DefaultObjectMapper
import io.github.freya022.botcommands.internal.application.diff.DiffLogger
import io.github.freya022.botcommands.internal.core.BContextImpl
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.interactions.commands.build.CommandData
import net.dv8tion.jda.api.utils.data.DataArray
import java.nio.file.Files
import java.nio.file.Path

@BService(ServiceStart.LAZY)
internal class ApplicationCommandsCache(serviceContainer: ServiceContainer) {
    //Cannot put JDA in the constructor as it is injected later
    private val cachePath = Path.of(System.getProperty("java.io.tmpdir"), "${serviceContainer.getService().selfUser.id}slashcommands")
    val globalCommandsPath: Path = cachePath.resolve("globalCommands.json")

    init {
        Files.createDirectories(cachePath)
    }

    fun getGuildCommandsPath(guild: Guild): Path {
        return cachePath.resolve(guild.id).resolve("commands.json")
    }

    companion object {
        fun Collection.toJsonBytes(): ByteArray = DataArray.empty().addAll(this).toJson()

        fun isJsonContentSame(context: BContextImpl, oldContentBytes: ByteArray, newContentBytes: ByteArray): Boolean {
            val oldMap = DefaultObjectMapper.readList(oldContentBytes)
            val newMap = DefaultObjectMapper.readList(newContentBytes)

            val isSame = DiffLogger.getLogger(context).let { diffLogger ->
                checkDiff(oldMap, newMap, diffLogger, 0).also {
                    diffLogger.printLogs()
                }
            }

            return isSame
        }

        private fun checkDiff(oldObj: Any?, newObj: Any?, logger: DiffLogger, indent: Int): Boolean {
            if (oldObj == null && newObj == null) {
                return true
            }

            if (oldObj == null) {
                logger.trace(indent, "oldObj is null")
                return false
            } else if (newObj == null) {
                logger.trace(indent, "newObj is null")
                return false
            }

            if (oldObj.javaClass != newObj.javaClass) {
                logger.trace(indent, "Class type not equal: %s to %s", oldObj.javaClass.simpleName, newObj.javaClass.simpleName)
                return false
            }

            if (oldObj is Map<*, *> && newObj is Map<*, *>) {
                if (!checkMap(oldObj, newObj, logger, indent)) return false
            } else if (oldObj is List<*> && newObj is List<*>) {
                if (!checkList(oldObj, newObj, logger, indent)) return false
            } else {
                return (oldObj == newObj).also { equals ->
                    if (!equals) logger.trace(indent, "Not same object: %s to %s", oldObj, newObj)
                }
            }

            return true
        }

        private fun checkList(oldList: List<*>, newList: List<*>, logger: DiffLogger, indent: Int): Boolean {
            if (oldList.size != newList.size) return false

            for (i in oldList.indices) {
                var found = false
                var index = -1
                for (o in newList) {
                    index++
                    if (checkDiff(oldList[i], o, logger, indent + 1)) {
                        found = true
                        break
                    }
                }

                if (found) {
                    //If command options (parameters, not subcommands, not groups) are moved
                    // then it means the command data changed
                    if (i != index) {
                        //Check if any final command property is here,
                        // such as autocomplete, or required
                        if (oldList[index] is Map<*, *>) {
                            val map = oldList[index] as Map<*, *>
                            if (map["autocomplete"] != null) {
                                //We found a real command option that has **changed index**,
                                // this is NOT equal under different indexes
                                logger.trace(
                                    indent,
                                    "Final command option has changed place from index %s to %s : %s",
                                    i,
                                    index,
                                    oldList[i]
                                )
                                return false
                            }
                        }
                    }

                    logger.trace(indent, "Found exact object at index %s (original object at %s) : %s", index, i, oldList[i])
                    continue
                }

                if (!checkDiff(oldList[i], newList[i], logger, indent + 1)) {
                    logger.trace(indent, "List item not equal: %s to %s", oldList[i], newList[i])
                    return false
                }
            }

            return true
        }

        private fun checkMap(oldMap: Map<*, *>, newMap: Map<*, *>, logger: DiffLogger, indent: Int): Boolean {
            if (!oldMap.keys.containsAll(newMap.keys)) return false

            for (key in oldMap.keys) {
                if (!checkDiff(oldMap[key], newMap[key], logger, indent + 1)) {
                    logger.trace(indent, "Map value not equal for key '%s': %s to %s", key, oldMap[key], newMap[key])
                    return false
                }
            }

            return true
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy