Skip to content

Commit

Permalink
PapagoAnonTranslate - papago without api
Browse files Browse the repository at this point in the history
  • Loading branch information
jombidev committed Apr 23, 2023
1 parent f4f0dab commit 9e49f50
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 94 deletions.
3 changes: 0 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ dependencies {
val fatJar = task("fatJar", type = Jar::class) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveFileName.set("${project.name.toLowerCaseAsciiOnly()}.jar")
manifest {
attributes["Implementation-Title"] = "Gradle Jar File Example"
}
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
with(tasks.jar.get() as CopySpec)
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/dev/jombi/copytotrans/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fun main() {
GlobalScreen.unregisterNativeHook()
exitProcess(0)
}
val keybindings = arrayOf(google/*, googleOld*/, papago, exitKey)
val keybindings = arrayOf(google, papago, exitKey)
GlobalScreen.registerNativeHook()
GlobalScreen.addNativeKeyListener(object : NativeKeyListener {
override fun nativeKeyPressed(e: NativeKeyEvent) {
Expand Down
2 changes: 0 additions & 2 deletions src/main/kotlin/dev/jombi/copytotrans/TranslateStatus.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ class TranslateStatus : JDialog() {
try {
val screenSize = Toolkit.getDefaultToolkit().screenSize
setLocation(screenSize.width / 2 - lab.width, screenSize.height / 2 + 32)
// showOverlay()
pack()
} catch (e: AWTException) {
// error("Error while showing overlay", e)
exitProcess(-1)
}
}
Expand Down
83 changes: 8 additions & 75 deletions src/main/kotlin/dev/jombi/copytotrans/Translators.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package dev.jombi.copytotrans

import com.fasterxml.jackson.databind.ObjectMapper
import dev.jombi.copytotrans.translator.Translator
import dev.jombi.copytotrans.translator.impl.GoogleAnonTranslate
import dev.jombi.copytotrans.translator.impl.PapagoTranslate
import dev.jombi.copytotrans.translator.impl.newg.GoogleRPCTranslate
import java.net.HttpURLConnection
import java.net.URL
import dev.jombi.copytotrans.translator.impl.newp.PapagoAnonTranslate
import dev.jombi.copytotrans.translator.impl.old.GoogleAnonTranslate
import dev.jombi.copytotrans.translator.impl.old.PapagoTokenedTranslate
import java.net.URLEncoder

interface Translators {
Expand All @@ -19,79 +18,13 @@ interface Translators {
object GoogleOld : Translators {
override val translator = GoogleAnonTranslate()
}
object PapagoOld : Translators {
override val translator = PapagoTokenedTranslate()
}
object Papago : Translators {
override val translator = PapagoTranslate()
override val translator = PapagoAnonTranslate()
}
}

fun ObjectMapper.buildJson(vararg pairs: Pair<String, Any?>): String = writeValueAsString(mapOf(*pairs))
fun buildUrlEncoded(vararg pairs: Pair<String, Comparable<*>>) = pairs.joinToString("&") { "${URLEncoder.encode(it.first, "utf-8")}=${URLEncoder.encode("${it.second}", "utf-8")}" }
/*
export class Translator {
protected options: typeof defaults & TranslateOptions;
constructor(protected inputText: string, options?: TranslateOptions) {
this.options = Object.assign({}, defaults, options);
}
async translate() {
const url = this.buildUrl();
const fetchOptions = this.buildFetchOptions();
const res = await fetch(url, fetchOptions);
if (!res.ok) throw await this.buildError(res);
const raw = await res.json() as RawResponse;
const text = this.buildResText(raw);
return { text, raw };
}
protected buildUrl() {
const { host } = this.options;
return [
`https://${host}/translate_a/single`,
'?client=at',
'&dt=t', // return sentences
'&dt=rm', // add translit to sentences
'&dj=1', // result as pretty json instead of deep nested arrays
].join('');
}
protected buildBody() {
const { from, to } = this.options;
const params = {
sl: from,
tl: to,
q: this.inputText,
};
return new URLSearchParams(params).toString();
}
protected buildFetchOptions() {
const { fetchOptions } = this.options;
const res = Object.assign({}, fetchOptions);
res.method = 'POST';
res.headers = Object.assign({}, res.headers, {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
});
res.body = this.buildBody();
return res;
}
protected buildResText({ sentences }: RawResponse) {
return sentences
.filter((s): s is Sentence => 'trans' in s)
.map(s => s.trans)
.join('');
}
protected async buildError(res: Response) {
if (res.status === 429) {
const text = await res.text();
const { ip, time, url } = extractTooManyRequestsInfo(text);
const message = `${res.statusText} IP: ${ip}, Time: ${time}, Url: ${url}`;
return createHttpError(res.status, message);
} else {
return createHttpError(res.status, res.statusText);
}
}
}
*/
fun buildUrlEncoded(vararg pairs: Pair<String, Comparable<*>>) = pairs.joinToString("&") { "${it.first}=${URLEncoder.encode("${it.second}", "utf-8")}" }
8 changes: 4 additions & 4 deletions src/main/kotlin/dev/jombi/copytotrans/config/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ fun makeDefaultConfig() {
val cfg = getRawConfig()
val path = Path("config.json")
val p = Property()
if (cfg.hasNonNull("papagoKey")) p.papagoKey = cfg["papagoKey"].asText()
if (cfg.hasNonNull("papagoSecret")) p.papagoSecret = cfg["papagoSecret"].asText()
// if (cfg.hasNonNull("papagoKey")) p.papagoKey = cfg["papagoKey"].asText()
// if (cfg.hasNonNull("papagoSecret")) p.papagoSecret = cfg["papagoSecret"].asText()
if (cfg.hasNonNull("sound")) p.sound = cfg["sound"].asBoolean()
if (cfg.hasNonNull("overlay")) p.overlay = cfg["overlay"].asBoolean()
mapper.writerWithDefaultPrettyPrinter().writeValue(path.outputStream(), p)
}

fun getPapagoApiKey(): String = getConfig().papagoKey
fun getPapagoApiSecret(): String = getConfig().papagoSecret
//fun getPapagoApiKey(): String = getConfig().papagoKey
//fun getPapagoApiSecret(): String = getConfig().papagoSecret

fun shouldShowOverlay(): Boolean = getConfig().overlay
fun shouldPlaySound(): Boolean = getConfig().sound
4 changes: 2 additions & 2 deletions src/main/kotlin/dev/jombi/copytotrans/config/Property.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.jombi.copytotrans.config

data class Property(
var papagoKey: String = "",
var papagoSecret: String = "",
// var papagoKey: String = "",
// var papagoSecret: String = "",
var overlay: Boolean = true,
var sound: Boolean = true
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package dev.jombi.copytotrans.translator.impl.newp

import dev.jombi.copytotrans.buildUrlEncoded
import dev.jombi.copytotrans.config.mapper
import dev.jombi.copytotrans.translator.Translator
import dev.jombi.copytotrans.translator.impl.newg.FailedToTranslateException
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
import kotlin.random.Random

class PapagoAnonTranslate : Translator {
private val endpoint = "https://papago.naver.com"
val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0"
val headers = hashMapOf<String, String>()
private val TIMESTAMP_REGEX = Regex("var query= null,timestamp= ([0-9]+)")
private val timestampFromHtml: Long
private val delayedTimestamp: Long
private val trustAllCerts: Array<TrustManager> = arrayOf(object : X509TrustManager {
override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {
}

override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {
}

override fun getAcceptedIssuers(): Array<X509Certificate>? {
return null
}

})

init {
val sc = SSLContext.getInstance("SSL")
sc.init(null, trustAllCerts, SecureRandom())
HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
val con = URL(endpoint).openConnection() as HttpURLConnection
con.requestMethod = "GET"
con.addRequestProperty("User-Agent", userAgent)
con.connect()
val (jsess, value) = con.getHeaderField("set-cookie").split(';')[0].split('=')
headers[jsess] = value
headers["papago_skin_locale"] = "en"
delayedTimestamp = System.currentTimeMillis() + Random.nextInt(200, 800)
timestampFromHtml =
TIMESTAMP_REGEX.find(String(con.inputStream.readBytes()))?.groupValues?.get(1)?.toLong() ?: delayedTimestamp

// println(timestampFromHtml)
// println(delayedTimestamp)
// println(delayedTimestamp - timestampFromHtml)
PapagoUUIDGen.gen()
}

override fun translate(target: String): String {
val toTranslate = detect(target)
val offset = System.currentTimeMillis() + timestampFromHtml - delayedTimestamp // (new Date).getTime() + a - d
val url = "$endpoint/apis/n2mt/translate"
val uuid = PapagoUUIDGen.gen()
val hash = createAuthorization(offset, url)
val transHeader = arrayOf(
"Accept" to "application/json",
"Accept-Language" to "en",
"Authorization" to "PPG $uuid:$hash",
"Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8",
"Timestamp" to "$offset",
"device-type" to "pc",
"x-apigw-partnerid" to "papago",
"Cookie" to headers.toList().joinToString("; ") { "${it.first}=${URLEncoder.encode(it.second, "utf-8")}" },
"User-Agent" to userAgent,
)
val body = buildUrlEncoded(
"deviceId" to uuid,
"locale" to "en",
"dict" to "false",
"honorific" to "false",
"instant" to "false",
"paging" to "false",
"source" to toTranslate,
"target" to "ko",
"text" to target
)
val con = URL(url).openConnection() as HttpURLConnection
for ((key, value) in transHeader) con.setRequestProperty(key, value)
con.requestMethod = "POST"
con.doOutput = true
con.outputStream.use {
it.write(body.toByteArray(charset("utf-8")))
it.flush()
}
con.connect()
if (con.responseCode in 200..299) {
// return String(con.inputStream.readBytes())
return mapper.readTree(con.inputStream)["translatedText"].asText()
} else {
println(String(con.errorStream.readBytes()))
throw FailedToTranslateException(con.responseCode)
}
}

fun detect(string: String): String {
val offset = System.currentTimeMillis() + timestampFromHtml - delayedTimestamp // (new Date).getTime() + a - d
val url = "$endpoint/apis/langs/dect"
val uuid = PapagoUUIDGen.gen()
val hash = createAuthorization(offset, url)
val detectHeader = mapOf(
"Accept" to "application/json",
"Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8",
"device-type" to "pc",
"Cookie" to headers.toList().joinToString("; ") { "${it.first}=${URLEncoder.encode(it.second, "utf-8")}" },
"User-Agent" to userAgent,
"Timestamp" to "$offset",
"Authorization" to "PPG $uuid:$hash"
)
val con = URL(url).openConnection() as HttpURLConnection
for ((key, value) in detectHeader) con.setRequestProperty(key, value)
con.requestMethod = "POST"
con.doOutput = true
val body = buildUrlEncoded("query" to string)
con.outputStream.use {
it.write(body.toByteArray(charset("utf-8")))
it.flush()
}
con.connect()
if (con.responseCode in 200..299) {
return mapper.readTree(con.inputStream)["langCode"].asText()
} else {
println(String(con.errorStream.readBytes()))
throw FailedToTranslateException(con.responseCode)
}
}

private fun createAuthorization(time: Long, url: String): String {
val uuid = PapagoUUIDGen.gen()
val mac = Mac.getInstance("HmacMD5")
mac.init(SecretKeySpec("v1.7.3_de60216eaa".toByteArray(charset("utf-8")), "HmacMD5"))
val myMac = uuid + "\n" + url.split("?")[0] + "\n" + time
return Base64.getEncoder().encodeToString(mac.doFinal(myMac.toByteArray()))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.jombi.copytotrans.translator.impl.newp

object PapagoUUIDGen {
private var stored: String? = null
fun gen(): String {
if (stored != null) return stored!!
var a = System.currentTimeMillis()
val genned = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".mapIndexed { i, it ->
if (it in "xy") {
val rng = Math.random()
val thing = ((a + 16 * rng) % 16).toInt()
a /= 16
(if ('x' == it) thing else 3 and thing or 8).toString(16)
} else it
}.joinToString("")
stored = genned
return stored!!
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.jombi.copytotrans.translator.impl
package dev.jombi.copytotrans.translator.impl.old

import dev.jombi.copytotrans.buildUrlEncoded
import dev.jombi.copytotrans.config.mapper
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package dev.jombi.copytotrans.translator.impl
package dev.jombi.copytotrans.translator.impl.old

import dev.jombi.copytotrans.buildUrlEncoded
import dev.jombi.copytotrans.config.getPapagoApiKey
import dev.jombi.copytotrans.config.getPapagoApiSecret
import dev.jombi.copytotrans.config.mapper
import dev.jombi.copytotrans.translator.Translator
import java.net.HttpURLConnection
import java.net.URL

class PapagoTranslate : Translator {
class PapagoTokenedTranslate : Translator {
val from = "auto"
val to = "ko"
override fun translate(target: String): String {
val con = URL("https://openapi.naver.com/v1/papago/n2mt").openConnection() as HttpURLConnection
con.doOutput = true
con.doInput = true
con.requestMethod = "POST"
con.addRequestProperty("X-Naver-Client-Id", getPapagoApiKey())
con.addRequestProperty("X-Naver-Client-Secret", getPapagoApiSecret())
// con.addRequestProperty("X-Naver-Client-Id", getPapagoApiKey())
// con.addRequestProperty("X-Naver-Client-Secret", getPapagoApiSecret())
val body = buildUrlEncoded("source" to from, "target" to to, "text" to target)
con.outputStream.use {
it.write(body.toByteArray())
Expand Down
8 changes: 8 additions & 0 deletions src/test/kotlin/PapagoAnonTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.jombi.copytotrans

import dev.jombi.copytotrans.translator.impl.newp.PapagoAnonTranslate

fun main() {
val anon = PapagoAnonTranslate()
println(anon.translate("hello"))
}
7 changes: 7 additions & 0 deletions src/test/kotlin/UUIDTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.jombi.copytotrans

import dev.jombi.copytotrans.translator.impl.newp.PapagoUUIDGen

fun main() {
println(PapagoUUIDGen.gen())
}

0 comments on commit 9e49f50

Please sign in to comment.