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

me.jakejmattson.kutils.api.services.ConversationService.kt Maven / Gradle / Ivy

There is a newer version: 0.18.1
Show newest version
@file:Suppress("unused")

package me.jakejmattson.kutils.api.services

import kotlinx.coroutines.runBlocking
import me.jakejmattson.kutils.api.Discord
import me.jakejmattson.kutils.api.dsl.conversation.*
import me.jakejmattson.kutils.api.extensions.stdlib.pluralize
import me.jakejmattson.kutils.internal.utils.*
import net.dv8tion.jda.api.entities.*
import java.lang.reflect.Method

enum class ConversationResult {
    INVALID_USER,
    CANNOT_DM,
    HAS_CONVO,
    COMPLETE,
    EXITED
}

data class ConversationContext(val userId: String, val channelId: String)

class ConversationService(val discord: Discord) {
    @PublishedApi
    internal val availableConversations = mutableMapOf, Pair>()

    @PublishedApi
    internal val activeConversations = mutableMapOf()

    internal fun registerConversations(path: String) {
        val startFunctions = ReflectionUtils.detectMethodsWith(path)

        ReflectionUtils.detectSubtypesOf(path)
            .forEach { conversationClass ->
                val relevantStartFunctions = startFunctions.filter { it.declaringClass == conversationClass }
                val conversationName = conversationClass.name.substringAfterLast(".")

                val starter = when (relevantStartFunctions.size) {
                    0 -> {
                        InternalLogger.error("$conversationName has no method annotated with @Start. It cannot be registered.")
                        return@forEach
                    }
                    else -> {
                        if (relevantStartFunctions.size != 1)
                            InternalLogger.error("$conversationName has multiple methods annotated with @Start. Searching for best fit.")

                        relevantStartFunctions.firstOrNull { it.returnType == ConversationBuilder::class.java }
                    }
                } ?: return@forEach InternalLogger.error("$conversationName @Start function does not build a conversation.")

                val instance = diService.invokeConstructor(conversationClass) as Conversation
                availableConversations[conversationClass as Class] = instance to starter
            }

        println(availableConversations.size.pluralize("Conversation"))
    }

    private fun getConversation(user: User, channel: MessageChannel) = activeConversations[ConversationContext(user.id, channel.id)]
    fun hasConversation(user: User, channel: MessageChannel) = getConversation(user, channel) != null

    @PublishedApi
    internal inline fun  startConversation(stateContainer: ConversationStateContainer, vararg arguments: Any): ConversationResult {
        val (_, user, channel) = stateContainer
        val context = ConversationContext(user.id, channel.id)

        val (instance, function) = availableConversations[T::class.java]!!
        val conversation = function.invoke(instance, *arguments) as ConversationBuilder

        activeConversations[context] = conversation

        return conversation.start(stateContainer) {
            activeConversations.remove(context)
        }
    }

    inline fun  startPrivateConversation(user: User, vararg arguments: Any): ConversationResult {
        if (user.mutualGuilds.isEmpty() || user.isBot)
            return ConversationResult.INVALID_USER

        val channel = user.openPrivateChannel().complete()

        if (hasConversation(user, channel))
            return ConversationResult.HAS_CONVO

        val state = ConversationStateContainer(discord, user, channel)

        return startConversation(state, *arguments)
    }

    inline fun  startPublicConversation(user: User, channel: MessageChannel, vararg arguments: Any): ConversationResult {
        if (user.mutualGuilds.isEmpty() || user.isBot)
            return ConversationResult.INVALID_USER

        if (hasConversation(user, channel))
            return ConversationResult.HAS_CONVO

        val state = ConversationStateContainer(discord, user, channel)

        return startConversation(state, *arguments)
    }

    internal fun handleResponse(message: Message) {
        runBlocking {
            val conversation = getConversation(message.author, message.channel) ?: return@runBlocking
            conversation.acceptMessage(message)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy