diff --git a/build.gradle.kts b/build.gradle.kts index beb7fd8..5e708ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,7 @@ implementation("com.fasterxml.jackson.core:jackson-databind:2.16.1") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.16.1") + implementation("com.github.pemistahl:lingua:1.2.2") implementation("com.google.protobuf:protobuf-java:3.25.3") implementation("com.google.protobuf:protobuf-kotlin:3.25.3") implementation("com.ibm.icu:icu4j:74.2") @@ -55,6 +56,10 @@ useJUnitPlatform() } +tasks.shadowJar { + isZip64 = true +} + tasks.withType { kotlinOptions.jvmTarget = "21" } diff --git a/src/main/kotlin/net/taehui/twilight/awilight/DefaultCompute.kt b/src/main/kotlin/net/taehui/twilight/awilight/DefaultCompute.kt index 989e6f3..f4618c7 100644 --- a/src/main/kotlin/net/taehui/twilight/awilight/DefaultCompute.kt +++ b/src/main/kotlin/net/taehui/twilight/awilight/DefaultCompute.kt @@ -270,13 +270,17 @@ this.handlerID = this@DefaultCompute.handlerID this.hitPointsMode = modeComponentValue.hitPointsMode this.avatarNetStatus = avatarNetStatus - this.stand = stand - this.band = band - this.point = point - this.hitPoints = hitPoints + this.stand = this@DefaultCompute.stand + this.point = this@DefaultCompute.point + this.hitPoints = this@DefaultCompute.hitPoints this.isFailed = this@DefaultCompute.isFailed this.lastJudged = this@DefaultCompute.lastJudged.value - if (avatarNetStatus != EventOuterClass.Event.AvatarNetStatus.Default) { + if (avatarNetStatus == EventOuterClass.Event.AvatarNetStatus.Default) { + this.band = this@DefaultCompute.band + this.drawingComponent = drawingComponent + this.addAllDrawings(this@DefaultCompute.netDrawings) + } else { + this.highestBand = this@DefaultCompute.highestBand this.title = this@DefaultCompute.title this.artist = this@DefaultCompute.artist this.genre = this@DefaultCompute.genre @@ -307,9 +311,6 @@ this.multiplier = comment.levyingMultiplier this.inputMode = this@DefaultCompute.inputMode.value addData(comment.build().toByteString()) - } else { - this.drawingComponent = drawingComponent - this.addAllDrawings(this@DefaultCompute.netDrawings) } }.build() }) diff --git a/src/main/kotlin/net/taehui/twilight/system/DB.kt b/src/main/kotlin/net/taehui/twilight/system/DB.kt index c8818cd..28d0d72 100644 --- a/src/main/kotlin/net/taehui/twilight/system/DB.kt +++ b/src/main/kotlin/net/taehui/twilight/system/DB.kt @@ -248,15 +248,6 @@ ) dbStatement.executeUpdate( """ - CREATE TABLE IF NOT EXISTS tw_language ( - Text VARCHAR(766) NOT NULL, - Language VARCHAR(5) NOT NULL, - PRIMARY KEY (Text) - ) COLLATE=utf8mb4_general_ci ENGINE=InnoDB - """.trimIndent() - ) - dbStatement.executeUpdate( - """ CREATE TABLE IF NOT EXISTS tw_favor ( Note_ID VARCHAR(139) NOT NULL, Avatar VARCHAR(20) NOT NULL, @@ -348,60 +339,26 @@ } } - fun loadLanguage(text: String): CompletableFuture { + fun loadTranslatedText(text: String, targetLanguage: String): CompletableFuture { return logValueFuture { pool.connection.use { it.prepareStatement( """ - SELECT Language - FROM tw_language - WHERE Text = ? + SELECT Translated_Text + FROM tw_translated + WHERE Text = ? AND Target_Language = ? """.trimIndent() ).use { dbStatement -> dbStatement.setString(1, text) + dbStatement.setString(2, targetLanguage) dbStatement.executeQuery().use { rows -> - if (rows.next()) rows.getString("Language") else "" + if (rows.next()) rows.getString("Translated_Text") else "" } } } } } - fun saveLanguage(text: String, language: String): CompletableFuture { - return logFuture { - pool.connection.use { - it.prepareStatement( - """ - REPLACE INTO tw_language - VALUES(?, ?) - """.trimIndent() - ).use { dbStatement -> - dbStatement.setString(1, text) - dbStatement.setString(2, language) - dbStatement.execute() - } - } - } - } - - fun loadTranslatedText(text: String, targetLanguage: String): String { - return pool.connection.use { - it.prepareStatement( - """ - SELECT Translated_Text - FROM tw_translated - WHERE Text = ? AND Target_Language = ? - """.trimIndent() - ).use { dbStatement -> - dbStatement.setString(1, text) - dbStatement.setString(2, targetLanguage) - dbStatement.executeQuery().use { rows -> - if (rows.next()) rows.getString("Translated_Text") else "" - } - } - } - } - fun saveTranslatedText(text: String, targetLanguage: String, translatedText: String): CompletableFuture { return logFuture { pool.connection.use { @@ -554,10 +511,10 @@ val pmsNoteID = "${Utility.getNoteID512(noteID)}:1" it.prepareStatement( """ - DELETE - FROM tw_note - WHERE Note_ID = ? - """.trimIndent() + DELETE + FROM tw_note + WHERE Note_ID = ? + """.trimIndent() ).use { dbStatement -> dbStatement.setString(1, noteID) if (dbStatement.executeUpdate() > 0) { @@ -566,10 +523,10 @@ } it.prepareStatement( """ - UPDATE tw_comment - SET Note_ID = ? - WHERE Note_ID = ? - """.trimIndent() + UPDATE tw_comment + SET Note_ID = ? + WHERE Note_ID = ? + """.trimIndent() ).use { dbStatement -> dbStatement.setString(1, pmsNoteID) dbStatement.setString(2, noteID) @@ -612,6 +569,75 @@ } } + fun setCommentMax(noteID: String): CompletableFuture { + data class Comment( + val stand: Int, + val date: Timestamp, + val commentID: String + ) + + return logFuture { + pool.connection.use { + it.autoCommit = false + + it.prepareStatement( + """ + UPDATE tw_comment + SET Is_Max = false + WHERE Note_ID = ? + """.trimIndent() + ).use { dbStatement -> + dbStatement.setString(1, noteID) + dbStatement.executeUpdate() + } + + val comments = mutableMapOf() + it.prepareStatement( + """ + SELECT Avatar, Stand, Date, Comment_ID + FROM tw_comment + WHERE Note_ID = ? + """.trimIndent() + ).use { dbStatement -> + dbStatement.setString(1, noteID) + dbStatement.executeQuery().use { rows -> + while (rows.next()) { + val avatar = rows.getString("Avatar") + val stand = rows.getInt("Stand") + val date = rows.getTimestamp("Date") + val commentID = rows.getString("Comment_ID") + + val comment = comments.getOrPut(avatar) { + Comment(stand, date, commentID) + } + + if (stand > comment.stand || (stand == comment.stand && date > comment.date)) { + comments[avatar] = Comment(stand, date, commentID) + } + } + } + } + + comments.forEach { (avatar, comment) -> + it.prepareStatement( + """ + UPDATE tw_comment + SET Is_Max = true + WHERE Note_ID = ? AND Avatar = ? AND Comment_ID = ? + """.trimIndent() + ).use { dbStatement -> + dbStatement.setString(1, noteID) + dbStatement.setString(2, avatar) + dbStatement.setString(3, comment.commentID) + dbStatement.executeUpdate() + } + } + + it.commit() + } + } + } + fun getWipeComments(): CompletableFuture> { return logValueFuture { val commentIDs = mutableSetOf() @@ -1903,10 +1929,10 @@ try { it.prepareStatement( """ - SELECT Stand, Comment_ID - FROM tw_comment - WHERE Note_ID = ? AND Avatar = ? AND Is_Max = true - """.trimIndent() + SELECT Stand, Comment_ID + FROM tw_comment + WHERE Note_ID = ? AND Avatar = ? AND Is_Max = true + """.trimIndent() ).use { dbStatement -> dbStatement.setString(1, noteID) dbStatement.setString(2, avatarID) @@ -1923,10 +1949,10 @@ if (!commentID.isNullOrEmpty()) { it.prepareStatement( """ - UPDATE tw_comment - SET Is_Max = false - WHERE Comment_ID = ? - """.trimIndent() + UPDATE tw_comment + SET Is_Max = false + WHERE Comment_ID = ? + """.trimIndent() ).use { dbStatement -> dbStatement.setString(1, commentID) dbStatement.execute() @@ -1934,9 +1960,9 @@ } it.prepareStatement( """ - INSERT INTO tw_comment - VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() + INSERT INTO tw_comment + VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """.trimIndent() ).use { dbStatement -> dbStatement.setTimestamp(1, Timestamp(System.currentTimeMillis())) dbStatement.setString(2, noteID) diff --git a/src/main/kotlin/net/taehui/twilight/system/IO.kt b/src/main/kotlin/net/taehui/twilight/system/IO.kt index 78aecb0..0faabda 100644 --- a/src/main/kotlin/net/taehui/twilight/system/IO.kt +++ b/src/main/kotlin/net/taehui/twilight/system/IO.kt @@ -194,18 +194,22 @@ if (w.size > 1) { when (w[1]) { "learn" -> { - if (futureLearnComments?.isDone != false) { - futureLearnCommentsStatus.set(0) - futureLearnComments = - DB.learnComments(futureLearnCommentsStatus).thenAccept { - logInfo( - "Learned ${futureLearnCommentsStatus.get()} Comments" - ) - } + if (w.size > 2) { + DB.setCommentMax(w[2]) } else { - logInfo( - "Learning ${futureLearnCommentsStatus.get()} Comments" - ) + if (futureLearnComments?.isDone != false) { + futureLearnCommentsStatus.set(0) + futureLearnComments = + DB.learnComments(futureLearnCommentsStatus).thenAccept { + logInfo( + "Learned ${futureLearnCommentsStatus.get()} Comments" + ) + } + } else { + logInfo( + "Learning ${futureLearnCommentsStatus.get()} Comments" + ) + } } continue } @@ -222,7 +226,7 @@ } } - logInfo("comment [learn] Learn Comments") + logInfo("comment [learn] Learn Comments") logInfo("comment [wipe] Clean Comment Files") } @@ -385,7 +389,7 @@ } } logInfo("note [learn] Learn Notes") - logInfo("note [ban] Ban Note") + logInfo("note [ban] Ban Note") logInfo("note [allow] Allow Note") logInfo("note [wipe] Clean or Wipe Note File") logInfo("note [pms] PMS Note File") diff --git a/src/main/kotlin/net/taehui/twilight/system/Translator.kt b/src/main/kotlin/net/taehui/twilight/system/Translator.kt index 678ade4..ec7a973 100644 --- a/src/main/kotlin/net/taehui/twilight/system/Translator.kt +++ b/src/main/kotlin/net/taehui/twilight/system/Translator.kt @@ -1,6 +1,8 @@ package net.taehui.twilight.system import com.fasterxml.jackson.databind.ObjectMapper +import com.github.pemistahl.lingua.api.Language +import com.github.pemistahl.lingua.api.LanguageDetectorBuilder import net.taehui.twilight.JSON import net.taehui.twilight.Logger import org.apache.hc.client5.http.HttpResponseException @@ -8,11 +10,18 @@ import org.apache.hc.client5.http.entity.UrlEncodedFormEntity 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.Header +import org.apache.hc.core5.http.HttpHeaders import org.apache.hc.core5.http.message.BasicNameValuePair +import org.apache.hc.core5.http.message.HeaderGroup import java.nio.charset.StandardCharsets import java.util.concurrent.CompletableFuture object Translator { + private val languageSystem = + LanguageDetectorBuilder.fromLanguages(Language.KOREAN, Language.ENGLISH, Language.JAPANESE).build() + fun translate( language: String, text: String, @@ -21,70 +30,47 @@ return if (text.length > 766) CompletableFuture.completedFuture( text ) else { - DB.loadLanguage(text).thenApply { loadedLanguage -> - var textLanguage = loadedLanguage - if (textLanguage.isEmpty()) { - textLanguage = try { + val textLanguage = when (languageSystem.detectLanguageOf(text)) { + Language.KOREAN -> "ko" + Language.ENGLISH -> "en" + Language.JAPANESE -> "ja" + else -> "" + } + val targetLanguage = language.split('-')[0] + if (textLanguage.isEmpty() || textLanguage == "en" || textLanguage == targetLanguage) CompletableFuture.completedFuture( + text + ) else DB.loadTranslatedText( + text, + targetLanguage + ).thenApply { loadedTranslatedText -> + try { + loadedTranslatedText.ifEmpty { HttpClients.createDefault().use { - val dataPost = HttpPost("https://openapi.naver.com/v1/papago/detectLangs") - dataPost.setHeader("X-Naver-Client-Id", Configure.nhn.nhnID) - dataPost.setHeader("X-Naver-Client-Secret", Configure.nhn.nhnPw) + val dataPost = HttpPost("https://naveropenapi.apigw.ntruss.com/nmt/v1/translation") + dataPost.setHeader("X-NCP-APIGW-API-KEY-ID", Configure.nhn.nhnID) + dataPost.setHeader("X-NCP-APIGW-API-KEY", Configure.nhn.nhnPw) dataPost.entity = UrlEncodedFormEntity( listOf( - BasicNameValuePair("query", text) + BasicNameValuePair("source", textLanguage), + BasicNameValuePair("target", targetLanguage), + BasicNameValuePair("text", text) ), StandardCharsets.UTF_8 ) - ObjectMapper().readValue( + val translatedText = ObjectMapper().readValue( it.execute(dataPost, BasicHttpClientResponseHandler()), - JSON.N2MTLanguage::class.java - ).langCode.apply { - DB.saveLanguage(text, this) - } + JSON.N2MT::class.java + ).message.result.translatedText + DB.saveTranslatedText(text, targetLanguage, translatedText) + translatedText } - } catch (e: HttpResponseException) { - if (e.statusCode != 429) { - logger.logFault(e) - } - "" } - } - - val targetLanguage = language.split('-')[0] - if ((textLanguage != "ko" && textLanguage != "ja") || textLanguage == targetLanguage) { + } catch (e: HttpResponseException) { + if (e.statusCode != 429) { + logger.logFault(e) + } text - } else { - val loadedTranslatedText = DB.loadTranslatedText(text, targetLanguage) - var translatedText = loadedTranslatedText - if (translatedText.isEmpty()) { - translatedText = try { - HttpClients.createDefault().use { - val dataPost = HttpPost("https://openapi.naver.com/v1/papago/n2mt") - dataPost.setHeader("X-Naver-Client-Id", Configure.nhn.nhnID) - dataPost.setHeader("X-Naver-Client-Secret", Configure.nhn.nhnPw) - dataPost.entity = UrlEncodedFormEntity( - listOf( - BasicNameValuePair("source", textLanguage), - BasicNameValuePair("target", targetLanguage), - BasicNameValuePair("text", text) - ), StandardCharsets.UTF_8 - ) - ObjectMapper().readValue( - it.execute(dataPost, BasicHttpClientResponseHandler()), - JSON.N2MT::class.java - ).message.result.translatedText.apply { - DB.saveTranslatedText(text, targetLanguage, this) - } - } - } catch (e: HttpResponseException) { - if (e.statusCode != 429) { - logger.logFault(e) - } - text - } - } - translatedText } } } } -} \ No newline at end of file +}