Newer
Older
Twilight / src / main / kotlin / net / taehui / twilight / system / PlatformSystem.kt
@Taehui Taehui on 5 Jul 16 KB v1.0-SNAPSHOT
package net.taehui.twilight.system

import com.fasterxml.jackson.databind.ObjectMapper
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDA.Status
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent
import net.dv8tion.jda.api.events.guild.member.GuildMemberUpdateEvent
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateAvatarEvent
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent
import net.dv8tion.jda.api.events.message.MessageDeleteEvent
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
import net.dv8tion.jda.api.events.message.MessageUpdateEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import net.dv8tion.jda.api.interactions.callbacks.IModalCallback
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback
import net.dv8tion.jda.api.interactions.components.ActionRow
import net.dv8tion.jda.api.interactions.components.buttons.Button
import net.dv8tion.jda.api.interactions.components.text.TextInput
import net.dv8tion.jda.api.interactions.components.text.TextInputStyle
import net.dv8tion.jda.api.interactions.modals.Modal
import net.dv8tion.jda.api.requests.GatewayIntent
import net.taehui.twilight.*
import org.apache.hc.client5.http.HttpResponseException
import org.apache.hc.client5.http.classic.methods.HttpGet
import org.apache.hc.client5.http.classic.methods.HttpPost
import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler
import org.apache.hc.client5.http.impl.classic.HttpClients
import org.apache.hc.core5.http.ContentType
import org.apache.hc.core5.http.io.entity.StringEntity
import java.text.NumberFormat
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap


object PlatformSystem : Logger {
    class Client {
        var platform = ""
        var qwilight = 0L
        var siteHi = 0L
        var siteNotify = 0L
        var siteComment = 0L
        var siteDefault = 0L
        var sitePlatform = 0L
        var siteAudio = 0L
        var siteYellDefault = ""
        var siteYellEnter = ""
        var siteYellQuit = ""
        var siteYellTaehui = ""
        var siteYellComment = ""
        var siteYellAbility = ""
        var siteYellAbilityMini = ""
        var siteYellLevel = ""
        var siteYellInvite = ""
        var siteYellTV = ""
    }

    private val drawingStore = ConcurrentHashMap<String, ByteArray>()
    private val platformClient: Client = ObjectMapper().readValue(
        Twilight::class.java.classLoader.getResourceAsStream("Client.json"), Client::class.java
    )
    private var platform: JDA? = null
    private var qwilightPlatform: Guild? = null
    var platformAvatars = emptyList<Member>()

    fun handleSystem() {
        if (platform != null) {
            logInfo("Platform is already running")
        } else if (Configure.mode.platform) {
            try {
                fun doCallPlatformAvatars() {
                    qwilightPlatform?.loadMembers()?.onSuccess { avatars ->
                        platformAvatars = avatars.filter { !it.user.isBot && !it.user.isSystem }
                        CompletableFuture.allOf(
                            *platformAvatars.map { putDrawing("$${it.id}", it.effectiveAvatarUrl) }.toTypedArray()
                        )
                    }?.onSuccess {
                        SiteHandler.doCallSiteAvatar(
                            SiteHandler.platformSiteID, platformAvatars
                        )
                    }
                }

                platform = JDABuilder.createDefault(platformClient.platform).enableIntents(
                    GatewayIntent.MESSAGE_CONTENT, GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES
                ).addEventListeners(object : ListenerAdapter() {
                    override fun onMessageReceived(event: MessageReceivedEvent) {
                        event.member?.let {
                            if (!event.author.isBot && !event.author.isSystem) {
                                if (event.channel.idLong == platformClient.siteDefault) {
                                    event.message.delete().queue()
                                    SiteHandler.putSiteYell(SiteHandler.defaultSiteID, it, event)
                                } else if (event.channel.idLong == platformClient.sitePlatform) {
                                    SiteHandler.putSiteYell(SiteHandler.platformSiteID, it, event)
                                }
                            }
                        }
                    }

                    override fun onMessageUpdate(event: MessageUpdateEvent) {
                        if (event.channel.idLong == platformClient.sitePlatform) {
                            event.member?.let {
                                SiteHandler.doModifySIteYell(SiteHandler.platformSiteID, it, event)
                            }
                        }
                    }

                    override fun onMessageDelete(event: MessageDeleteEvent) {
                        if (event.channel.idLong == platformClient.sitePlatform) {
                            SiteHandler.wipeSiteYell(SiteHandler.platformSiteID, event)
                        }
                    }

                    override fun onGuildMemberJoin(event: GuildMemberJoinEvent) {
                        doCallPlatformAvatars()

                        qwilightPlatform?.getTextChannelById(platformClient.siteHi)
                            ?.sendMessage("Hi, <@${event.user.id}>")?.addActionRow(
                                Button.primary("login", "Log in to Qwilight Channel")
                            )?.queue()
                    }

                    fun onLogin(replyEvent: IReplyCallback, modalEvent: IModalCallback) {
                        if (PlatformIDSystem.hasPlatformID(replyEvent.user.id)) {
                            replyEvent.reply("You are already logged in").setEphemeral(true).queue()
                        } else {
                            modalEvent.replyModal(
                                Modal.create("login", "Log in to Qwilight Channel").addComponents(
                                    ActionRow.of(
                                        TextInput.create("avatarID", "ID", TextInputStyle.SHORT).setMinLength(1).build()
                                    ), ActionRow.of(
                                        TextInput.create("avatarCipher", "PW", TextInputStyle.SHORT).setMinLength(1)
                                            .build()
                                    )
                                ).build()
                            ).queue()
                        }
                    }

                    override fun onButtonInteraction(event: ButtonInteractionEvent) {
                        when (event.componentId) {
                            "login" -> onLogin(event, event)
                        }
                    }

                    override fun onGuildMemberRemove(event: GuildMemberRemoveEvent) {
                        doCallPlatformAvatars()

                        PlatformIDSystem.wipePlatformID(event.user.id)
                    }

                    override fun onGuildMemberUpdateAvatar(event: GuildMemberUpdateAvatarEvent) {
                        doCallPlatformAvatars()
                    }

                    override fun onGuildMemberUpdate(event: GuildMemberUpdateEvent) {
                        doCallPlatformAvatars()
                    }

                    override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) {
                        when (event.name) {
                            "login" -> {
                                onLogin(event, event)
                            }
                        }
                    }

                    override fun onModalInteraction(event: ModalInteractionEvent) {
                        if (event.modalId == "login") {
                            val avatarID =
                                event.getValue("avatarID")?.asString ?: throw IllegalArgumentException("avatarID")
                            val avatarCipher = event.getValue("avatarCipher")?.asString
                                ?: throw IllegalArgumentException("avatarCipher")

                            HttpClients.createDefault().use {
                                val jm = ObjectMapper()
                                val dataPost = HttpPost(Configure.www.taehui + "/avatar/getTotem")
                                dataPost.setHeader("millis", System.currentTimeMillis())
                                dataPost.entity = StringEntity(
                                    jm.writeValueAsString(object {
                                        val avatarID = avatarID
                                        val avatarCipher = avatarCipher
                                    }), ContentType.APPLICATION_JSON
                                )
                                try {
                                    val taehuiGetTotem = jm.readValue(
                                        it.execute(dataPost, BasicHttpClientResponseHandler()),
                                        JSON.TaehuiGetTotem::class.java
                                    )

                                    PlatformIDSystem.putPlatformID(event.user.id, avatarID)
                                    event.reply("You logged in as ${taehuiGetTotem.avatarName} to Qwilight Channel")
                                        .setEphemeral(true).queue()

                                    setAbilityName(taehuiGetTotem.avatarID)
                                } catch (e: HttpResponseException) {
                                    event.reply("Failed to log in").setEphemeral(true).queue()
                                }
                            }
                        }
                    }
                }).build().awaitReady()

                qwilightPlatform = platform?.getGuildById(platformClient.qwilight)

                qwilightPlatform?.retrieveCommands()?.queue { data ->
                    data.forEach {
                        qwilightPlatform?.deleteCommandById(it.idLong)?.queue()
                    }
                    qwilightPlatform?.upsertCommand("login", "Log in to Qwilight Channel")?.queue()
                }

                doCallPlatformAvatars()

                qwilightPlatform?.audioManager?.openAudioConnection(qwilightPlatform?.getVoiceChannelById(platformClient.siteAudio))
            } catch (e: Exception) {
                logFault(e)
            }
        } else {
            logInfo("Platform is disabled")
        }
    }

    val platformStatus: String
        get() = (platform?.status ?: Status.SHUTDOWN).toString()

    fun dispose() {
        platform?.shutdown()
        platform = null
    }

    fun sendSiteYell(siteYellData: JSON.TwilightSiteYell) {
        try {
            var site: TextChannel? = null
            when (UUID.fromString(siteYellData.siteID)) {
                SiteHandler.toNotifySiteID -> site = qwilightPlatform?.getTextChannelById(platformClient.siteNotify)
                SiteHandler.commentSiteID -> site = qwilightPlatform?.getTextChannelById(platformClient.siteComment)
                SiteHandler.defaultSiteID -> site = qwilightPlatform?.getTextChannelById(platformClient.siteDefault)
            }

            when (siteYellData.avatarName) {
                "@Enter" -> site?.sendMessage(String.format(platformClient.siteYellEnter, siteYellData.siteYell))

                "@Quit" -> site?.sendMessage(String.format(platformClient.siteYellQuit, siteYellData.siteYell))

                "@Notify" -> site?.sendMessage(
                    String.format(
                        platformClient.siteYellTaehui, siteYellData.siteYell
                    )
                )

                "@Comment" -> {
                    val twilightCommentSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightCommentSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellComment,
                            twilightCommentSiteYell.avatarName,
                            twilightCommentSiteYell.levelText,
                            twilightCommentSiteYell.artist,
                            twilightCommentSiteYell.title,
                            if (twilightCommentSiteYell.genre.isEmpty()) "" else "#${twilightCommentSiteYell.genre}",
                            NumberFormat.getInstance().format(twilightCommentSiteYell.stand.toLong())
                        )
                    )
                }

                "@Ability" -> {
                    val twilightAbilitySiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightAbilitySiteYell::class.java)
                    val inputMode = when (twilightAbilitySiteYell.inputMode) {
                        Component.InputMode.INPUT_MODE_5_1 -> "⑤K"
                        Component.InputMode.INPUT_MODE_7_1 -> "⑦K"
                        Component.InputMode.INPUT_MODE_9 -> "9K"
                        else -> ""
                    }
                    val ability = twilightAbilitySiteYell.ability
                    site?.sendMessage(
                        if (ability < 0.01) String.format(
                            platformClient.siteYellAbilityMini, twilightAbilitySiteYell.avatarName, inputMode
                        ) else String.format(
                            platformClient.siteYellAbility, twilightAbilitySiteYell.avatarName, inputMode, ability
                        )
                    )
                }

                "@Level" -> {
                    val twilightLevelSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightLevelSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellLevel, twilightLevelSiteYell.avatarName, twilightLevelSiteYell.title
                        )
                    )
                }

                "@Invite" -> {
                    val twilightInviteSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightInviteSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellInvite,
                            twilightInviteSiteYell.avatarName,
                            twilightInviteSiteYell.siteName
                        )
                    )
                }

                "@TV" -> {
                    val twilightTVSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightTVSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellTV, twilightTVSiteYell.text, twilightTVSiteYell.title
                        )
                    )
                }

                else -> {
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellDefault, siteYellData.avatarName, siteYellData.siteYell
                        )
                    )
                }
            }?.queue()
        } catch (e: Exception) {
            logFault(e)
        }
    }

    fun putDrawing(avatarID: String, platformDrawing: String): CompletableFuture<Void> {
        return if (drawingStore.containsKey(avatarID)) {
            CompletableFuture.completedFuture(null)
        } else {
            logFuture {
                HttpClients.createDefault().use {
                    drawingStore[avatarID] = it.execute(HttpGet(platformDrawing), HCDataHandler())
                }
            }
        }
    }

    fun getDrawing(avatarID: String): ByteArray? {
        return drawingStore[avatarID]
    }

    fun setAbilityName(avatarID: String) {
        logFuture {
            val abilityName = DB.getMaxAbilityName(avatarID)
            PlatformIDSystem.getPlatformID(avatarID)?.let {
                qwilightPlatform?.retrieveMemberById(it)?.queue { avatar ->
                    qwilightPlatform?.modifyMemberRoles(
                        avatar,
                        *(qwilightPlatform?.getRolesByName(abilityName, true)?.toTypedArray() ?: emptyArray())
                    )?.queue()
                }
            }
        }
    }
}