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

io.github.freya022.botcommands.internal.parameters.resolvers.MentionsStringResolverFactory.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.parameters.resolvers

import io.github.freya022.botcommands.api.commands.application.checkGuildOnly
import io.github.freya022.botcommands.api.commands.application.slash.annotations.MentionsString
import io.github.freya022.botcommands.api.commands.application.slash.options.SlashCommandOption
import io.github.freya022.botcommands.api.core.entities.InputUser
import io.github.freya022.botcommands.api.core.reflect.ParameterWrapper
import io.github.freya022.botcommands.api.core.reflect.hasAnnotation
import io.github.freya022.botcommands.api.core.reflect.requireUser
import io.github.freya022.botcommands.api.core.service.annotations.ResolverFactory
import io.github.freya022.botcommands.api.core.utils.*
import io.github.freya022.botcommands.api.parameters.ParameterResolverFactory
import io.github.freya022.botcommands.api.parameters.ResolverRequest
import io.github.freya022.botcommands.api.parameters.TypedParameterResolver
import io.github.freya022.botcommands.api.parameters.resolvers.SlashParameterResolver
import io.github.freya022.botcommands.internal.core.entities.InputUserImpl
import io.github.freya022.botcommands.internal.utils.annotationRef
import io.github.freya022.botcommands.internal.utils.classRef
import io.github.freya022.botcommands.internal.utils.findErasureOfAt
import io.github.freya022.botcommands.internal.utils.throwArgument
import net.dv8tion.jda.api.entities.IMentionable
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.Message.MentionType
import net.dv8tion.jda.api.entities.Role
import net.dv8tion.jda.api.entities.User
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel
import net.dv8tion.jda.api.entities.emoji.CustomEmoji
import net.dv8tion.jda.api.interactions.commands.CommandInteractionPayload
import net.dv8tion.jda.api.interactions.commands.OptionMapping
import net.dv8tion.jda.api.interactions.commands.OptionType
import net.dv8tion.jda.api.interactions.commands.SlashCommandReference
import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.safeCast
import kotlin.reflect.typeOf

@ResolverFactory
internal object MentionsStringResolverFactory : ParameterResolverFactory(MentionsStringResolver::class) {
    internal class MentionsStringResolver private constructor(
        private val mentionTypes: Array,
        private val transform: (IMentionable) -> IMentionable?
    ) : TypedParameterResolver>(typeOf>()),
        SlashParameterResolver> {

        override val optionType: OptionType = OptionType.STRING

        override suspend fun resolveSuspend(
            option: SlashCommandOption,
            event: CommandInteractionPayload,
            optionMapping: OptionMapping
        ): List = optionMapping.mentions.getMentions(*mentionTypes).mapNotNull(transform)

        internal companion object {
            internal fun ofMentionable(mentionTypes: Array): MentionsStringResolver {
                return MentionsStringResolver(mentionTypes) { it }
            }

            internal fun ofEntity(entityType: KClass, mentionType: MentionType) : MentionsStringResolver {
                return ofEntity(mentionType) { entityType.safeCast(it) }
            }

            internal fun ofEntity(mentionType: MentionType, mapper: (IMentionable) -> IMentionable?) : MentionsStringResolver {
                return MentionsStringResolver(arrayOf(mentionType), mapper)
            }
        }
    }

    override val supportedTypesStr: List = listOf(IMentionable::class.shortQualifiedName)

    override fun isResolvable(request: ResolverRequest): Boolean {
        val parameter = request.parameter
        if (!parameter.hasAnnotation())
            return false

        // Must be a List
        parameter.requireUser(parameter.erasure.isSubclassOf>()) {
            "Parameter '${parameter.name}' annotated with ${annotationRef()} must be a ${classRef>()} subtype"
        }

        // Elements must be mentionable
        val elementType = parameter.type.findErasureOfAt>(0).jvmErasure
        require(IMentionable::class.isAssignableFrom(elementType)) {
            "Parameter '${parameter.name}' annotated with ${annotationRef()} must be a ${classRef>()} containing ${classRef()} or any subtype"
        }

        // If they are not mentionable, the annotation must not have any mention type
        if (elementType != IMentionable::class) {
            // If this is a concrete type, do not allow types
            parameter.requireUser(parameter.getMentionTypes().isEmpty()) {
                "Parameter '${parameter.name}' annotated with ${annotationRef()} cannot have mention types on a concrete list element type (${elementType.simpleNestedName})"
            }
        }

        return true
    }

    @Suppress("UNCHECKED_CAST")
    override fun get(request: ResolverRequest): MentionsStringResolver {
        val parameter = request.parameter
        val mentionTypes = parameter.getMentionTypes()

        val elementErasure = parameter.type.findErasureOfAt>(0).jvmErasure as KClass
        return if (elementErasure == IMentionable::class) {
            MentionsStringResolver.ofMentionable(mentionTypes.ifEmpty { enumSetOfAll() }.toTypedArray())
        } else if (elementErasure == User::class) {
            MentionsStringResolver.ofEntity(MentionType.USER) {
                when (it) {
                    is Member -> it.user
                    is User -> it
                    else -> null
                }
            }
        } else if (elementErasure == Member::class) {
            request.checkGuildOnly(Member::class)
            MentionsStringResolver.ofEntity(elementErasure, MentionType.USER)
        } else if (elementErasure == InputUser::class) {
            MentionsStringResolver.ofEntity(MentionType.USER) {
                when (it) {
                    is Member -> InputUserImpl(it)
                    is User -> InputUserImpl(it)
                    else -> null
                }
            }
        } else if (elementErasure.isSubclassOf()) {
            request.checkGuildOnly(elementErasure)
            MentionsStringResolver.ofEntity(elementErasure, MentionType.CHANNEL)
        } else if (elementErasure == Role::class) {
            request.checkGuildOnly(Role::class)
            MentionsStringResolver.ofEntity(elementErasure, MentionType.ROLE)
        } else if (elementErasure == CustomEmoji::class) {
            MentionsStringResolver.ofEntity(elementErasure, MentionType.EMOJI)
        } else if (elementErasure == SlashCommandReference::class) {
            MentionsStringResolver.ofEntity(elementErasure, MentionType.SLASH_COMMAND)
        } else {
            throwArgument("Unsupported element type: ${elementErasure.shortQualifiedName}")
        }
    }

    private fun ParameterWrapper.getMentionTypes(): EnumSet {
        return parameter.findAllAnnotations().flatMapTo(enumSetOf()) { it.types }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy