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 siteHello = 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.siteHello) ?.sendMessage("<@${event.user.id}>")?.addActionRow( Button.primary("login", "Log in to Qwilight Channel") )?.queue() } fun onLogin(replyEvent: IReplyCallback, modalEvent: IModalCallback) { if (PlatformIDSystem.hasAvatarID(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() } } fun onSetAbility(platformID: String): String { val abilityName = DB.getMaxAbilityName( PlatformIDSystem.getAvatarID(platformID) ) qwilightPlatform?.retrieveMemberById(platformID)?.queue { avatar -> qwilightPlatform?.modifyMemberRoles( avatar, *(qwilightPlatform?.getRolesByName(abilityName, true)?.toTypedArray() ?: emptyArray()) )?.queue() } return abilityName } 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) { "role" -> { val platformID = event.user.id if (PlatformIDSystem.hasAvatarID(platformID)) { event.deferReply().queue() event.hook.sendMessage("Your role is ${onSetAbility(platformID)}") .setEphemeral(true).queue() } else { event.reply("You didn't log in").setEphemeral(true).queue() } } "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 ) val platformID = event.user.id PlatformIDSystem.putPlatformID(platformID, avatarID) event.reply("You logged in as ${taehuiGetTotem.avatarName} to Qwilight Channel") .setEphemeral(true).queue() onSetAbility(platformID) } catch (e: HttpResponseException) { event.reply("Failed to log in").setEphemeral(true).queue() } } } } }).build().awaitReady() qwilightPlatform = platform?.getGuildById(platformClient.qwilight) qwilightPlatform?.upsertCommand("role", "Set role to Qwilight tier")?.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] } }